W6
This commit is contained in:
+139
-36
@@ -27,6 +27,7 @@
|
||||
#include <wasmtime.hh>
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
@@ -352,6 +353,27 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
// The guest calls a sized hostcall twice (buf=0 to learn the length, then
|
||||
// to fetch). For side-effecting ops (sqlite) re-executing on the fetch is
|
||||
// wrong, so the result is staged on the first call (keyed on the exact
|
||||
// input bytes) and replayed on the second without re-running the op.
|
||||
String staged_hostcall_input;
|
||||
String staged_hostcall_result;
|
||||
bool hostcall_staged(const String& input, String& out)
|
||||
{
|
||||
if(!staged_hostcall_input.empty() && input == staged_hostcall_input)
|
||||
{
|
||||
out = staged_hostcall_result;
|
||||
return(true);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
void hostcall_stage(const String& input, const String& out)
|
||||
{
|
||||
staged_hostcall_input = input;
|
||||
staged_hostcall_result = out;
|
||||
}
|
||||
|
||||
// resolve-kind values shared with the guest core (src/wasm/core.cpp)
|
||||
// must match WasmResolveKind in src/wasm/core.cpp
|
||||
enum ResolveKind { RESOLVE_COMPONENT = 0, RESOLVE_RENDER = 1, RESOLVE_EXISTS = 2, RESOLVE_ONCE = 3 };
|
||||
@@ -1054,6 +1076,17 @@ private:
|
||||
return((int32_t)slot);
|
||||
}
|
||||
|
||||
String run_task_callback(u64 callback_id)
|
||||
{
|
||||
auto runner = core_func("uce_wasm_task_run");
|
||||
if(!runner)
|
||||
return("core does not export uce_wasm_task_run");
|
||||
auto result = runner->call(ctx(), { wasmtime::Val((int64_t)callback_id) });
|
||||
if(!result)
|
||||
return(trap_text(result.err()));
|
||||
return("");
|
||||
}
|
||||
|
||||
// ---- host imports for the core -----------------------------------------
|
||||
|
||||
wasmtime::Extern make_host_import(wasmtime::Store::Context cx, const String& mod, const String& name, const wasmtime::FuncType& func_type)
|
||||
@@ -1168,52 +1201,58 @@ private:
|
||||
// handle table (handle = 1-based index).
|
||||
String encoded;
|
||||
self->hostcall_read(args[0].i32(), args[1].i32(), encoded);
|
||||
DValue request, response, decode_err_unused;
|
||||
String decode_error;
|
||||
if(ucb_decode(encoded, request, &decode_error))
|
||||
String out;
|
||||
// run the op once across the length-query + fetch pair
|
||||
if(!self->hostcall_staged(encoded, out))
|
||||
{
|
||||
String op = request["op"].to_string();
|
||||
if(op == "connect")
|
||||
DValue request, response;
|
||||
String decode_error;
|
||||
if(ucb_decode(encoded, request, &decode_error))
|
||||
{
|
||||
SQLite* db = new SQLite();
|
||||
db->connect(request["path"].to_string());
|
||||
u64 handle = 0;
|
||||
if(db->connection)
|
||||
String op = request["op"].to_string();
|
||||
if(op == "connect")
|
||||
{
|
||||
self->sqlite_handles.push_back(db);
|
||||
handle = self->sqlite_handles.size();
|
||||
}
|
||||
response["handle"] = (f64)handle;
|
||||
response["error_code"] = (f64)db->error_code;
|
||||
response["statement_info"] = db->error();
|
||||
if(handle == 0)
|
||||
delete db;
|
||||
}
|
||||
else
|
||||
{
|
||||
u64 handle = request["handle"].to_u64();
|
||||
SQLite* db = (handle >= 1 && handle <= self->sqlite_handles.size())
|
||||
? self->sqlite_handles[(size_t)handle - 1] : 0;
|
||||
if(op == "query" && db)
|
||||
{
|
||||
StringMap params;
|
||||
DValue* p = request.key("params");
|
||||
if(p)
|
||||
p->each([&](const DValue& value, String key) { params[key] = value.to_string(); });
|
||||
response["result"] = db->query(request["query"].to_string(), params);
|
||||
response["insert_id"] = (f64)db->insert_id;
|
||||
response["affected"] = (f64)db->affected_rows;
|
||||
SQLite* db = new SQLite();
|
||||
db->connect(request["path"].to_string());
|
||||
u64 handle = 0;
|
||||
if(db->connection)
|
||||
{
|
||||
self->sqlite_handles.push_back(db);
|
||||
handle = self->sqlite_handles.size();
|
||||
}
|
||||
response["handle"] = (f64)handle;
|
||||
response["error_code"] = (f64)db->error_code;
|
||||
response["statement_info"] = db->error();
|
||||
if(handle == 0)
|
||||
delete db;
|
||||
}
|
||||
else if(op == "disconnect" && db)
|
||||
else
|
||||
{
|
||||
delete db;
|
||||
self->sqlite_handles[(size_t)handle - 1] = 0;
|
||||
u64 handle = request["handle"].to_u64();
|
||||
SQLite* db = (handle >= 1 && handle <= self->sqlite_handles.size())
|
||||
? self->sqlite_handles[(size_t)handle - 1] : 0;
|
||||
if(op == "query" && db)
|
||||
{
|
||||
StringMap params;
|
||||
DValue* p = request.key("params");
|
||||
if(p)
|
||||
p->each([&](const DValue& value, String key) { params[key] = value.to_string(); });
|
||||
response["result"] = db->query(request["query"].to_string(), params);
|
||||
response["insert_id"] = (f64)db->insert_id;
|
||||
response["affected"] = (f64)db->affected_rows;
|
||||
response["error_code"] = (f64)db->error_code;
|
||||
response["statement_info"] = db->error();
|
||||
}
|
||||
else if(op == "disconnect" && db)
|
||||
{
|
||||
delete db;
|
||||
self->sqlite_handles[(size_t)handle - 1] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
out = ucb_encode(response);
|
||||
self->hostcall_stage(encoded, out);
|
||||
}
|
||||
String out = ucb_encode(response);
|
||||
u32 cap = (u32)args[3].i32();
|
||||
int32_t buf = args[2].i32();
|
||||
if(buf != 0 && cap >= out.size())
|
||||
@@ -1232,6 +1271,70 @@ private:
|
||||
::unlink(resolved.c_str());
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_task_spawn")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
String key;
|
||||
self->hostcall_read(args[0].i32(), args[1].i32(), key);
|
||||
u64 callback_id = (u64)args[2].i64();
|
||||
f64 interval = args[3].f64();
|
||||
u64 timeout = (u64)args[4].i64();
|
||||
bool repeat = args[5].i32() != 0;
|
||||
auto run_callback = [self, callback_id]() {
|
||||
String error = self->run_task_callback(callback_id);
|
||||
if(error != "")
|
||||
fprintf(stderr, "[wasm task] callback failed: %s\n", error.c_str());
|
||||
};
|
||||
pid_t pid = 0;
|
||||
try
|
||||
{
|
||||
if(!repeat || (interval > 0 && std::isfinite(interval)))
|
||||
pid = repeat
|
||||
? ::task_repeat(key, interval, run_callback, timeout)
|
||||
: ::task(key, run_callback, timeout);
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
fprintf(stderr, "[wasm task] spawn failed for key '%s': %s\n", key.c_str(), e.what());
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
fprintf(stderr, "[wasm task] spawn failed for key '%s'\n", key.c_str());
|
||||
}
|
||||
results[0] = Val((int32_t)pid);
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_task_pid")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
String key;
|
||||
self->hostcall_read(args[0].i32(), args[1].i32(), key);
|
||||
results[0] = Val((int32_t)::task_pid(key));
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_task_kill")
|
||||
return(add([](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
results[0] = Val((int32_t)::task_kill((pid_t)args[0].i32(), args[1].i32()));
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_sleep_us")
|
||||
return(add([self](Caller caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
u64 usec = (u64)args[0].i64();
|
||||
while(usec >= 1000000ull)
|
||||
{
|
||||
unsigned int remaining = ::sleep((unsigned int)(usec / 1000000ull));
|
||||
if(remaining != 0)
|
||||
{
|
||||
caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks);
|
||||
results[0] = Val((int32_t)remaining);
|
||||
return(std::monostate());
|
||||
}
|
||||
usec %= 1000000ull;
|
||||
}
|
||||
if(usec > 0)
|
||||
::usleep((useconds_t)usec);
|
||||
caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks);
|
||||
results[0] = Val((int32_t)0);
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_regex")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
// {op,pattern,subject,flags,replacement} in (UCEB1) → result out.
|
||||
|
||||
Reference in New Issue
Block a user