-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PROXY_TO_PTHREAD support for Embind #9847
Comments
This is surprising - I don't think we proxy embind calls to the main thread. Perhaps the full call stack leading to that error shows some proxying code, that could explain things? (Or maybe I've forgotten something here...) |
Here's the stacktrace:
Notice that it will call the C function pointer from JS on the main browser thread. I had a quick go with patching the concept9847.js// concept9847.js
// build with: --js-library concept9847.js
// --js-library must go after --bind
if (!LibraryManager.library['$embind__requireFunction']) {
throw Error("embind.js didn't run yet");
}
mergeInto(LibraryManager.library, {
$embind__requireFunction: function (signature, rawFunction) {
signature = readLatin1String(signature);
function makeDynCallerProxy() {
var args = [];
for (var i = 1; i < signature.length; ++i) {
args.push('a' + i);
}
// TODO: Make it work for other signatures as well. Perhaps something like this?:
/*var sig;
switch (signature) {
case 'v':
sig = {{{ cDefine('EM_FUNC_SIG_V') }}};
break;
case 'vi':
sig = {{{ cDefine('EM_FUNC_SIG_VI') }}};
break;
case 'vii':
sig = {{{ cDefine('EM_FUNC_SIG_VII') }}};
break;
case 'viii':
sig = {{{ cDefine('EM_FUNC_SIG_VIII') }}};
break;
case 'i':
sig = {{{ cDefine('EM_FUNC_SIG_I') }}};
break;
case 'ii':
sig = {{{ cDefine('EM_FUNC_SIG_II') }}};
break;
case 'iii':
sig = {{{ cDefine('EM_FUNC_SIG_III') }}};
break;
case 'iiii':
sig = {{{ cDefine('EM_FUNC_SIG_IIII') }}};
break;
default:
throwBindingError("No dynCall invoker for signature: " + signature);
}*/
var name = 'dynCall_' + signature + '_' + rawFunction;
var body = 'return function ' + name + '(' + args.join(', ') + ') {\n';
// body += ' var sig = '+ sig + ';\n';
body += ' var stackTop = stackSave();\n';
// TODO: These parameters are hardcoded, obviously we want something smarter here.
body += ' var varargs = stackAlloc(4);\n';
body += ' {{{ makeSetValue('varargs', 0, 'a1', 'i32')}}};\n';
// TODO: 5414848 = is the result of pthread_self() in the proxied main (`__emscripten_thread_main`)
body += ' _emscripten_async_queue_on_thread_(5414848, {{{ cDefine('EM_FUNC_SIG_II') }}}, rawFunction, 0, varargs);\n';
body += ' stackRestore(stackTop);\n';
// TODO: Wait with `emscripten_wait_for_call_v` and return result.
body += '};\n';
return (new Function('rawFunction', body))(rawFunction);
}
var fp;
if (Module['FUNCTION_TABLE_' + signature] !== undefined) {
fp = Module['FUNCTION_TABLE_' + signature][rawFunction];
} else if (typeof FUNCTION_TABLE !== "undefined") {
fp = FUNCTION_TABLE[rawFunction];
} else {
fp = makeDynCallerProxy();
}
if (typeof fp !== "function") {
throwBindingError("unknown function pointer with signature " + signature + ": " + rawFunction);
}
return fp;
},
}); With this patch, Unfortunately, I couldn't use the handy This could be a bit of a niche issue. Hopefully I've clarified a couple of things. |
Here is an improved workaround, which seems to work with this particular example:
|
Interesting. I'm not sure what's going on here, as I see no proxying in that stack trace, weird... So this may be an embind bug, but I don't see any proxying in the embind code. Can you perhaps add some debug logging in the various functions, to see where it proxies from one thread to the other? |
I updated a few comments and the issue title, since I mixed up the
Sure, which functions should I debug specifically? If you want, I can also provide the output of |
The goal would be to get a stack trace showing some embind code that calls proxying code, or at least the proxying code itself, with maybe something else calling it. Alternatively, if you can reduce this to a simple testcase with instructions, that would help too - I find it hard to follow the "workaround" sample you provide. However, perhaps @jgravelle-google who knows more about embind can find time to look at this, as the answer may be something trivial I am missing. |
I've created a minimal reproducible testcase. See commit kleisauke@240b43b. To reproduce: git clone --single-branch --branch testcase-9847 https://github.com/kleisauke/emscripten.git testcase-9847
cd testcase-9847
python tests/runner.py browser.test_pthread_main_thread_blocking_embind Hope this helps. |
I just came across #9910, which seems to be a much more convenient solution than moving all embind |
The issue I had is that the error produced by the Ideally, the error in this test case should only occur if the |
Upstream-Status: Inappropriate [Emscripten specific]
I'm creating JavaScript bindings (using Embind) for a C/C++ library that uses a lot of threading features. The bindings are compiled with these Emscripten compiler flags:
Which moves the application’s
main()
to a pthread and disallows to block the main browser thread.I noticed that when an Embind wrapped function (that will eventually use
pthread_join
orpthread_cond_wait
) is invoked on the main browser thread, an error is immediately thrown. This indicates that the wrapped function is executed on the calling thread, and not on the pthread running the originalmain()
.Is it possible to execute these functions on the pthread running the original
main()
?Minimal reproduction
Compile with:
Run
Module.answer()
in the developer tools console and notice that theBlocking on the main thread is not allowed by default
error is thrown.The text was updated successfully, but these errors were encountered: