Merge pull request #64 from sidi762/master
Web: fixed timeout not working
This commit is contained in:
commit
e405c3e6d4
|
@ -3,6 +3,7 @@ const path = require('path');
|
|||
const yargs = require('yargs/yargs');
|
||||
const { hideBin } = require('yargs/helpers');
|
||||
const koffi = require('koffi');
|
||||
require('expose-gc');
|
||||
|
||||
// Parse command line arguments
|
||||
const argv = yargs(hideBin(process.argv))
|
||||
|
@ -37,7 +38,7 @@ let nasalLib;
|
|||
try {
|
||||
// First load the library
|
||||
const lib = koffi.load(path.join(__dirname, '../module/libnasal-web.dylib'));
|
||||
|
||||
|
||||
// Then declare the functions explicitly
|
||||
nasalLib = {
|
||||
nasal_init: lib.func('nasal_init', 'void*', []),
|
||||
|
@ -45,7 +46,7 @@ try {
|
|||
nasal_eval: lib.func('nasal_eval', 'const char*', ['void*', 'const char*', 'int']),
|
||||
nasal_get_error: lib.func('nasal_get_error', 'const char*', ['void*'])
|
||||
};
|
||||
|
||||
|
||||
} catch (err) {
|
||||
console.error('Failed to load nasal library:', err);
|
||||
process.exit(1);
|
||||
|
@ -66,7 +67,7 @@ app.post('/eval', (req, res) => {
|
|||
try {
|
||||
const result = nasalLib.nasal_eval(ctx, code, showTime ? 1 : 0);
|
||||
const error = nasalLib.nasal_get_error(ctx);
|
||||
|
||||
|
||||
if (error && error !== 'null') {
|
||||
if (argv.verbose) console.log('Nasal error:', error);
|
||||
res.json({ error: error });
|
||||
|
@ -83,6 +84,7 @@ app.post('/eval', (req, res) => {
|
|||
} finally {
|
||||
if (argv.verbose) console.log('Cleaning up Nasal context');
|
||||
nasalLib.nasal_cleanup(ctx);
|
||||
global.gc()
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -91,4 +93,4 @@ app.listen(PORT, () => {
|
|||
console.log(`Server running on port ${PORT}`);
|
||||
console.log(`Visit http://localhost:${PORT} to use the Nasal interpreter`);
|
||||
if (argv.verbose) console.log('Verbose logging enabled');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -191,7 +191,7 @@ void vm::value_info(var& val) {
|
|||
void vm::function_detail_info(const nas_func& func) {
|
||||
std::clog << "func@";
|
||||
std::clog << std::hex << reinterpret_cast<u64>(&func) << std::dec;
|
||||
|
||||
|
||||
std::vector<std::string> argument_list = {};
|
||||
argument_list.resize(func.keys.size());
|
||||
for(const auto& key : func.keys) {
|
||||
|
@ -273,7 +273,7 @@ void vm::function_call_trace() {
|
|||
std::clog << " `--> " << same_count << " same call(s)\n";
|
||||
same_count = 0;
|
||||
}
|
||||
|
||||
|
||||
last = func;
|
||||
last_callsite = place;
|
||||
|
||||
|
@ -376,7 +376,7 @@ void vm::global_state() {
|
|||
if (name.length()>=10) {
|
||||
name = name.substr(0, 7) + "...";
|
||||
} else {
|
||||
|
||||
|
||||
}
|
||||
std::clog << "| " << std::left << std::setw(10)
|
||||
<< std::setfill(' ') << name << " ";
|
||||
|
@ -570,6 +570,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,
|
||||
|
@ -665,6 +673,7 @@ void vm::run(const codegen& gen,
|
|||
code.push_back(oprs[i.op]);
|
||||
imm.push_back(i.num);
|
||||
}
|
||||
CHECK_INTERRUPT;
|
||||
// goto the first operand
|
||||
goto *code[ctx.pc];
|
||||
#else
|
||||
|
@ -674,6 +683,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");
|
||||
|
@ -704,6 +716,7 @@ vmexit:
|
|||
// do not cause stackoverflow
|
||||
#define exec_nodie(op) {\
|
||||
op();\
|
||||
CHECK_INTERRUPT;\
|
||||
goto *code[++ctx.pc];\
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <stack>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <atomic>
|
||||
|
||||
#include "nasal_import.h"
|
||||
#include "nasal_gc.h"
|
||||
|
@ -31,7 +32,7 @@ protected:
|
|||
|
||||
/* nasal native functions */
|
||||
std::vector<nasal_builtin_table> native_function;
|
||||
|
||||
|
||||
/* garbage collector */
|
||||
gc ngc;
|
||||
|
||||
|
@ -323,6 +324,13 @@ public:
|
|||
auto get_gc_time_ms() const {
|
||||
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) {
|
||||
|
@ -977,7 +985,7 @@ inline void vm::o_callfh() {
|
|||
for(u32 i = 0; i<func.local_size; ++i) {
|
||||
local[i] = func.local[i];
|
||||
}
|
||||
|
||||
|
||||
bool lack_arguments_flag = false;
|
||||
for(const auto& i : func.keys) {
|
||||
const auto& key = i.first;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <chrono>
|
||||
#include <vector>
|
||||
#include <future>
|
||||
#include <atomic>
|
||||
|
||||
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<bool> interrupted{false};
|
||||
|
||||
NasalContext() {
|
||||
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) {
|
||||
delete static_cast<NasalContext*>(context);
|
||||
auto* ctx = static_cast<NasalContext*>(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");
|
||||
|
|
Loading…
Reference in New Issue