Comments (11)
@yar2001 I've started a PR to add an ASYNCIFY build variant and a new API surface for dealing with asyncified QuickJS interactions here: #54
I'm still figuring out the best way to offer both sync and async APIs, how to support existing code that expects everything to be sync, etc.
from quickjs-emscripten.
Yeah, I’ve thought about this before as well. You’re right to bring up ASYNCIFY, which is a possible path forward. We could do a second build of QuickJS with -O3 and an asyncify wrapper function around this function:
quickjs-emscripten/c/interface.c
Line 93 in 8b17755
But, ASYNCIFY is a very big hammer for this problem, meant to work around C-level issues. We might be able to find a way around the issue in QuickJS’s C api.
from quickjs-emscripten.
I’ve checked, it seems impossible to use the C API to suspend the VM execution state. So, the options are ASYNCIFY, or do something strange: you can write your own suspense logic, because you control the script execution.
Whenever the user code would need to suspend to await a promise, throw the promise instead. Catch the promise at the top level, and await it. Once it’s ready, re-run the user code, but return the unwrapped promise result when the user code calls the async function again. Continue until you reach completion or hit a CPU or time budget.
For that idea to work, you need to ensure that your users’ code is as pure as possible, so that when you re-run it, it doesn’t diverge on subsequent runs, and so it doesn’t trigger effects multiple times.
from quickjs-emscripten.
Thanks for the advice! Actually, I have considered this method. However, user may use Math.rand
or Date
. Overriding it by static rand seed or other solution seems will solve the problem, but I think it is not general. I want to design an API for users of many levels and I can't make sure that this won't conflict with future plans.
I'm new to wasm, and after a whole day of exploration, I finally got what your "ASYNCIFY is a very big hammer" means. Perhaps I will try C++ with Embind. If I get anything good, I'll let you know.
from quickjs-emscripten.
I was unable to get an ideal way to achieve that. However, I found a way to execute async function by "polling". It's not elegant but it works.
Here is the prototype:
//async.cpp
#include <emscripten.h>
#include <emscripten/bind.h>
#include <map>
#include <iostream>
using namespace emscripten;
std::map<int, bool> fnWaitMap;
std::map<int, bool> fnRetMap;
void exeucteAsync(int fnId)
{
float (*f)(int) = reinterpret_cast<float (*)(int)>(fnId);
f(7);
fnWaitMap[fnId] = false;
while (!fnWaitMap[fnId])
{
emscripten_sleep(4);
}
EM_ASM_(Module.removeFunction($0), f);
std::cout << "Output " << fnRetMap[fnId] << std::endl;
}
void asyncResolve(int fnId, int ret)
{
fnWaitMap[fnId] = true;
fnRetMap[fnId] = ret;
}
EMSCRIPTEN_BINDINGS(my_module)
{
function("execute", &exeucteAsync);
function("asyncResolve", &asyncResolve);
}
(it can also work in c, not limited to the cpp)
//async.try.js
const Module = require('./async.js');
function executeAsync(fn) {
const fnId = Module.addFunction((parm) => {
fn(parm).then((ret) => {
Module.asyncResolve(fnId, ret);
});
}, 'fi');
return Module.execute(fnId);
}
Module.onRuntimeInitialized = () => {
executeAsync(async (a) => {
await new Promise((resolve) => setTimeout(() => resolve(), 1000));
return a ** 2;
});
};
setInterval(() => {
console.log('thread is not block!');
}, 1000);
emcc -o async.js --bind async.cpp -s ALLOW_TABLE_GROWTH -s EXPORTED_RUNTIME_METHODS='["cwrap", "addFunction", "removeFunction", "stringToUTF8", "lengthBytesUTF8"]' -O3 -s ASYNCIFY
node async.js
And the result should be:
The key point is emscripten_sleep. It can let wasm VM wait without blocking the thread.
from quickjs-emscripten.
The key point is emscripten_sleep. It can let wasm wait without block the thread.
true, but any function that calls down to emscripten_sleep is going to be suspended - it’s stack unwound and stored by ASYNCIFY. That means that even using polling, the compiler will need to ASYNCIFY all of quickjs, which might lead to some issues. So, I don’t see much advantage to using this polling method over a direct async call of your APIs.
After reading your issue the other day, I found someone else worked on ASYNCIFYing quickjs-emscripten in a fork and asked them about it on Twitter: https://twitter.com/louroboros/status/1481689244603072513?s=21
it seems like they originally tried using ASYNCIFY but encountered some challenges so they removed it in this commit. But, maybe you could continue their work from the parent commit? It looks like a good start: namuol@29c816b
from quickjs-emscripten.
That means that even using polling, the compiler will need to ASYNCIFY all of quickjs, which might lead to some issues.
Sorry, I don't get it. Won't ASYNCIFY all of quickjs is an expected behavior? Because from quickjs perspective, the thread of sync function is blocked until the async function in host releases it. Will there exist a solution to achieve ASYNCIFY without freezing all the quickjs? Do I miss something?
Or are you indicating that it will influence multiple QuickJSVm instances?
The reason I use polling method is because I fail to find a way to execute async function outside VM directly. All the pages of asyncify in manual is about asyncifying Web APIs. I'm not familiar with emscripten yet. But after your reply, now I know that I can directly call function outside the VM by EM_ASYNC_JS
.
const Module = require('./async2');
async function waitForSecond() {
await new Promise((resolve) => setTimeout(() => resolve(), 1000));
}
Module.waitForSecond = waitForSecond;
setInterval(() => {
console.log('thread is not block!');
}, 1000);
#include <emscripten.h>
#include <emscripten/bind.h>
using namespace emscripten;
EM_ASYNC_JS(void, waitSecond, (),
{
out("before waiting");
await Module.waitForSecond();
out("after waiting");
});
int main()
{
waitSecond();
}
from quickjs-emscripten.
Besides using an ASYNCIFY build, a strategy that works now is to run quickjs-emscripten inside of a separate worker thread, have the worker thread and the main thread share a SharedArrayBuffer, and then at any time your code in the worker thread can communicate to the main thread through the SharedArrayBuffer and use Atomics.wait()
to block the worker until the main thread responds. There's some discussion of the technique here: pyodide/pyodide#1219
from quickjs-emscripten.
I got the asyncified function API passing a test this morning in #54. There’s still a bit of work left on async module loading, though.
from quickjs-emscripten.
OMG thank you so much for your amazing work, this may be the first elegant solution in the entire community for asynchronous to synchronous.
I'm really sorry that I can't give you a hand. I'm busy preparing for my undergraduate internship interview recently, but my project and my users still need it very much. I will try this scheme when I have time later. Thanks again!
from quickjs-emscripten.
Nice, I migrated from vm2 to quickjs-emscripten, and it works great! The old temporary solution is to fetch all the data user codes needed which is really slow, and the new asyncify solution gives me a lot of room to optimize performance.
Besides, quickjs-emscripten has better memory and resource control, and be able to control many of the security vulnerabilities I know so far such as unceasing promise and thread blocking. (Surely don't need to care about prototype chain pollution, global scope leakage since the VM is absolutely isolated from the host)
There are also spillover effects, that I can use the sandbox in both browser and server. The sharing of code and the consistent code behavior greatly enhances the development and debugging efficiency. In the near future, I will develop an interactive teaching program for my users and it will be very useful for sandbox execution in the browser.
For anyone who is investigating the JS sandbox technology stack, quickjs-emscripten would be a good choice. I would recommend this framework to anyone around who needs it.
from quickjs-emscripten.
Related Issues (20)
- Module "fs", "path", and "crypto" have been externalized for browser compatibility HOT 2
- Tests fail on s390x machines HOT 5
- Proxy log receiver blocking error HOT 1
- Async Regression "TypeError: Object not disposable" in v0.26.0 HOT 4
- wish to add a new api (QuickJSHandle.keepAlive) HOT 2
- `getQuickJS()` callback never resolves? HOT 3
- [vite - MIME type Error]It's cannot work in vite HOT 2
- How to access wasm module's Memory? HOT 3
- Feature request: Expose GetModuleExport APIs HOT 4
- Timeout not working HOT 6
- [feature request] Upgrade to v2024-01-13 HOT 3
- TypeError: Failed to construct 'URL': Invalid URL HOT 7
- Loading WasmMemory into module HOT 11
- Determinism of the library HOT 2
- Distributed packages have no license HOT 2
- `evalCode` won't return error in ES module mode HOT 3
- Can't run cloudflare example HOT 1
- `Out of bounds memory access` Error only on ARM Safari HOT 10
- Having troubles with ArrayBuffer HOT 1
- Inconsistent behavior when running in browser (and web worker) HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from quickjs-emscripten.