From 699c5f7af4630ae0580ad9b33bb312d0893604d4 Mon Sep 17 00:00:00 2001 From: Sidi Liang <1467329765@qq.com> Date: Wed, 5 Feb 2025 14:46:07 +0800 Subject: [PATCH] Web: Fix timeout --- src/nasal_vm.cpp | 14 +++++++++++++- src/nasal_vm.h | 7 +++++++ src/nasal_web.cpp | 12 +++++++++++- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/nasal_vm.cpp b/src/nasal_vm.cpp index 70bc2cc..1e5d1b3 100644 --- a/src/nasal_vm.cpp +++ b/src/nasal_vm.cpp @@ -559,6 +559,14 @@ void vm::run(const codegen& gen, #ifndef _MSC_VER // 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[] = { &&vmexit, &&repl, @@ -654,7 +662,7 @@ void vm::run(const codegen& gen, code.push_back(oprs[i.op]); imm.push_back(i.num); } - // goto the first operand + CHECK_INTERRUPT; goto *code[ctx.pc]; #else std::vector code; @@ -663,6 +671,9 @@ void vm::run(const codegen& gen, imm.push_back(i.num); } while(code[ctx.pc]) { + if (interrupt_ptr && interrupt_ptr->load()) { + throw std::runtime_error("VM execution interrupted by timeout"); + } (this->*code[ctx.pc])(); if (ctx.top>=ctx.canary) { die("stack overflow"); @@ -693,6 +704,7 @@ vmexit: // do not cause stackoverflow #define exec_nodie(op) {\ op();\ + CHECK_INTERRUPT;\ goto *code[++ctx.pc];\ } diff --git a/src/nasal_vm.h b/src/nasal_vm.h index 0829bce..0dae0a6 100644 --- a/src/nasal_vm.h +++ b/src/nasal_vm.h @@ -318,6 +318,13 @@ public: void set_limit_mode_flag(bool flag) { flag_limited_mode = flag; } + + void set_interrupt_ptr(std::atomic* p) { + interrupt_ptr = p; + } + +private: + std::atomic* interrupt_ptr = nullptr; }; inline bool vm::boolify(const var& val) { diff --git a/src/nasal_web.cpp b/src/nasal_web.cpp index 67b5c2f..b2070f2 100644 --- a/src/nasal_web.cpp +++ b/src/nasal_web.cpp @@ -17,6 +17,7 @@ #include #include #include +#include namespace { // Helper function implementations inside anonymous namespace @@ -46,9 +47,15 @@ struct NasalContext { std::string last_result; std::string last_error; std::chrono::seconds timeout{5}; // Default 5 second timeout + std::atomic interrupted{false}; NasalContext() { vm_instance = std::make_unique(); + vm_instance->set_interrupt_ptr(&interrupted); + } + + ~NasalContext() { + vm_instance.reset(); // Reset explicitly } }; @@ -71,7 +78,9 @@ void* nasal_init() { } void nasal_cleanup(void* context) { - delete static_cast(context); + auto* ctx = static_cast(context); + ctx->vm_instance.reset(); + delete ctx; } // 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 auto status = future.wait_for(ctx->timeout); if (status == std::future_status::timeout) { + ctx->interrupted.store(true); std::remove(temp_filename); throw std::runtime_error("Execution timed out after " + std::to_string(ctx->timeout.count()) + " seconds");