Merge pull request #64 from sidi762/master

Web: fixed timeout not working
This commit is contained in:
ValK 2025-02-05 17:48:52 +08:00 committed by GitHub
commit e405c3e6d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 43 additions and 10 deletions

View File

@ -3,6 +3,7 @@ const path = require('path');
const yargs = require('yargs/yargs'); const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers'); const { hideBin } = require('yargs/helpers');
const koffi = require('koffi'); const koffi = require('koffi');
require('expose-gc');
// Parse command line arguments // Parse command line arguments
const argv = yargs(hideBin(process.argv)) const argv = yargs(hideBin(process.argv))
@ -83,6 +84,7 @@ app.post('/eval', (req, res) => {
} finally { } finally {
if (argv.verbose) console.log('Cleaning up Nasal context'); if (argv.verbose) console.log('Cleaning up Nasal context');
nasalLib.nasal_cleanup(ctx); nasalLib.nasal_cleanup(ctx);
global.gc()
} }
}); });

View File

@ -570,6 +570,14 @@ void vm::run(const codegen& gen,
#ifndef _MSC_VER #ifndef _MSC_VER
// using labels as values/computed goto // using labels as values/computed goto
// Define an interrupt check macro for computed goto mode.
#define CHECK_INTERRUPT { \
if (interrupt_ptr && interrupt_ptr->load()) { \
throw std::runtime_error("VM execution interrupted by timeout"); \
} \
}
const void* oprs[] = { const void* oprs[] = {
&&vmexit, &&vmexit,
&&repl, &&repl,
@ -665,6 +673,7 @@ void vm::run(const codegen& gen,
code.push_back(oprs[i.op]); code.push_back(oprs[i.op]);
imm.push_back(i.num); imm.push_back(i.num);
} }
CHECK_INTERRUPT;
// goto the first operand // goto the first operand
goto *code[ctx.pc]; goto *code[ctx.pc];
#else #else
@ -674,6 +683,9 @@ void vm::run(const codegen& gen,
imm.push_back(i.num); imm.push_back(i.num);
} }
while(code[ctx.pc]) { while(code[ctx.pc]) {
if (interrupt_ptr && interrupt_ptr->load()) {
throw std::runtime_error("VM execution interrupted by timeout");
}
(this->*code[ctx.pc])(); (this->*code[ctx.pc])();
if (ctx.top>=ctx.canary) { if (ctx.top>=ctx.canary) {
die("stack overflow"); die("stack overflow");
@ -704,6 +716,7 @@ vmexit:
// do not cause stackoverflow // do not cause stackoverflow
#define exec_nodie(op) {\ #define exec_nodie(op) {\
op();\ op();\
CHECK_INTERRUPT;\
goto *code[++ctx.pc];\ goto *code[++ctx.pc];\
} }

View File

@ -4,6 +4,7 @@
#include <stack> #include <stack>
#include <cstring> #include <cstring>
#include <sstream> #include <sstream>
#include <atomic>
#include "nasal_import.h" #include "nasal_import.h"
#include "nasal_gc.h" #include "nasal_gc.h"
@ -323,6 +324,13 @@ public:
auto get_gc_time_ms() const { auto get_gc_time_ms() const {
return ngc.get_gc_time_ms(); return ngc.get_gc_time_ms();
} }
void set_interrupt_ptr(std::atomic<bool>* p) {
interrupt_ptr = p;
}
private:
std::atomic<bool>* interrupt_ptr = nullptr;
}; };
inline bool vm::boolify(const var& val) { inline bool vm::boolify(const var& val) {

View File

@ -17,6 +17,7 @@
#include <chrono> #include <chrono>
#include <vector> #include <vector>
#include <future> #include <future>
#include <atomic>
namespace { namespace {
// Helper function implementations inside anonymous namespace // Helper function implementations inside anonymous namespace
@ -46,9 +47,15 @@ struct NasalContext {
std::string last_result; std::string last_result;
std::string last_error; std::string last_error;
std::chrono::seconds timeout{5}; // Default 5 second timeout std::chrono::seconds timeout{5}; // Default 5 second timeout
std::atomic<bool> interrupted{false};
NasalContext() { NasalContext() {
vm_instance = std::make_unique<nasal::vm>(); vm_instance = std::make_unique<nasal::vm>();
vm_instance->set_interrupt_ptr(&interrupted);
}
~NasalContext() {
vm_instance.reset(); // Reset explicitly
} }
}; };
@ -71,7 +78,9 @@ void* nasal_init() {
} }
void nasal_cleanup(void* context) { void nasal_cleanup(void* context) {
delete static_cast<NasalContext*>(context); auto* ctx = static_cast<NasalContext*>(context);
ctx->vm_instance.reset();
delete ctx;
} }
// Add new function to set timeout // Add new function to set timeout
@ -148,6 +157,7 @@ const char* nasal_eval(void* context, const char* code, int show_time) {
// Wait for completion or timeout // Wait for completion or timeout
auto status = future.wait_for(ctx->timeout); auto status = future.wait_for(ctx->timeout);
if (status == std::future_status::timeout) { if (status == std::future_status::timeout) {
ctx->interrupted.store(true);
std::remove(temp_filename); std::remove(temp_filename);
throw std::runtime_error("Execution timed out after " + throw std::runtime_error("Execution timed out after " +
std::to_string(ctx->timeout.count()) + " seconds"); std::to_string(ctx->timeout.count()) + " seconds");