-
Notifications
You must be signed in to change notification settings - Fork 3.4k
4.0.0 regression: Module.postRun callbacks not being called #23420
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
Comments
I believe this is known consequence of our transition to usage of async/await. See #23157 and the corresponding ChangeLog entry: Lines 69 to 74 in 9aa4df6
Basically, because we can now use async/await inside the module constructor factor function we can now The Having said all that my understanding is that this applies to Are you using |
That looks to fit the symptoms precisely. This breaks the late-stage init timing for us, but it's easy to work around now that we know about it.
We rely heavily on both --post-js and --extern-post-js. Our post-js code is in fact what adds the $ cat post-js-header.js
<docs snipped>
if(!Module.postRun) Module.postRun = [];
Module.postRun.push(function(Module/*the Emscripten-style module object*/){
'use strict';
<EOF> Our build process emits that, followed by a dozen-odd files, then the following block: /* The current function scope was opened via post-js-header.js, which
gets prepended to this at build-time. This file closes that
scope. */
})/*postRun.push(...)*/; That bundle then gets passed to --post-run. Thank you for explaining the issue. |
BTW: that's likely why we're seeing this. We have to, from extern-post, replace the module-provided init function with our own (see #18071) and call the original init func ourself. |
Would it be possible to switch to |
Good question. Next question? ;) IIRC (which i won't guaranty - it's been 2 years since i went down this rabbit hole), #18071 is the reason we cannot(?) do what we need to do in --pre-js. In short: we need to store some global state which is relevant for when the module init function (defined via EXPORT_NAME) is called by the client, but we cannot access the module init function from --pre-js because the EXPORT_NAME function is anonymous at that point:
To do what we want to do, we have to have access to the var xyz = (()=>{xyz.a = 1})() // --> Uncaught TypeError: xyz is undefined If that function had a well-defined name (as #18071 requests), we could (IIRC) move our extern-JS code into --pre-js, but it doesn't, so we have nowhere to attach our required state to. So we delay that part of our init until --extern-post-js, where we can see That said: i'm going from memories 2+ years old and may well be misremembering. |
While the code in the pre-js file is included before the module is fully constructed the
Not sure if this helps in your case. |
The Module object is, but we need to completely replace the function specified by EXPORT_NAME (see below). AFAIK, extern-js is the only place we have access to the function named by EXPORT_NAME. The --post-js code is run inside of the being-initialized EXPORT_NAME function, so does not have access to that function (because of #18071, hint, hint ;). The reason we want the EXPORT_NAME object is so that we can replace it so that clients cannot see the Emscripten module object: the public API has no Emscripten visibility and we want the init function to work identically for hypthetical non-Emscripten frameworks. We achieve that by replacing the EXPORT_NAME function to call the Emscripten-defined EXPORT_NAME and then return the sqlite3 API object instead of the Emscripten module. Relevant code from our --extern-js, noting that (A) /**
In order to hide the sqlite3InitModule()'s resulting
Emscripten module from downstream clients (and simplify our
documentation by being able to elide those details), we hide that
function and expose a hand-written sqlite3InitModule() to return
the sqlite3 object (most of the time).
*/
const originalInit = sqlite3InitModule;
if(!originalInit){
throw new Error("Expecting globalThis.sqlite3InitModule to be defined by the Emscripten build.");
}
/**
We need to add some state which our custom Module.locateFile()
can see, but an Emscripten limitation currently prevents us from
attaching it to the sqlite3InitModule function object:
https://github.com/emscripten-core/emscripten/issues/18071
The only(?) current workaround is to temporarily stash this state
into the global scope and delete it when sqlite3InitModule()
is called.
*/
const initModuleState = globalThis.sqlite3InitModuleState = Object.assign(Object.create(null),{
<...big snip...> });
globalThis.sqlite3InitModule = function ff(...args){
return originalInit(...args).then((EmscriptenModule)=>{
if( EmscriptenModule.postRun && EmscriptenModule.postRun.length ){
/* Emscripten 4.0.0 changes the order in which our Module.postRun handler
runs. In 3.x postRun would have run by now, and our code relies
heavily on that order, so we'll work around that difference here.
https://github.com/emscripten-core/emscripten/issues/23420 */
EmscriptenModule.postRun.shift()(EmscriptenModule);
}
//#if wasmfs
if('undefined'!==typeof WorkerGlobalScope &&
EmscriptenModule['ENVIRONMENT_IS_PTHREAD']){
/** Workaround for wasmfs-generated worker, which calls this
routine from each individual thread and requires that its
argument be returned. The conditional criteria above are
fragile, based solely on inspection of the offending code,
not public Emscripten details. */
//console.warn("sqlite3InitModule() returning E-module.",EmscriptenModule);
return EmscriptenModule;
}
//#endif
const s = EmscriptenModule.sqlite3;
s.scriptInfo = initModuleState;
//console.warn("sqlite3.scriptInfo =",s.scriptInfo);
if(ff.__isUnderTest) s.__isUnderTest = true;
const f = s.asyncPostInit;
delete s.asyncPostInit;
return f();
}).catch((e)=>{
console.error("Exception loading sqlite3 module:",e);
throw e;
});
};
globalThis.sqlite3InitModule.ready = originalInit.ready; |
Indeed if you want somehow replace the But I though we were talking about |
It's worked around sufficiently, IMO: if our replacement init function sees that there are still postRun items in the queue, it runs them and removes them from the queue so Emscripten doesn't run them again. We only have one postRun entry, but it contains and initializes the whole library. |
Add to Lines 267 to 282 in 4c14f1f
Module.postRun in your --post-js file.
A better solution here might just be execute the code directly since #23157 ensures (in most cases) that
Or maybe even simpler just do:
|
One reason I'm suggesting you avoid using |
i wasn't aware that postRun() had already run at that point. It's always "just worked" for us (until 4.0.0). Now that you mention it, though... we can probably remove the usage of
We can only do that from post-js, not extern-post-js, and we can't access the EXPORT_NAME symbol from post-js (#18071 again!), so we have to defer the final init of our module until extern-post, where we can override the EXPORT_NAME function. Even so... we use i'll need to tinker with that and figure out the ramifications.
Since the If we move our main lib-level init call into our overridden EXPORT_NAME(), that should give us the same effect as manually triggering postRun(), so i'll look into making that change. That would, in any case, |
The problem I'd like to solve is folks adding to |
We had some bug reports recently when we switched to using async/await in MODULUARIZE mode. One of the side effects was the `--post-js` code then runs after module instantiation and startup, which means assignment of properties like `onRuntimeInitialized` is too late if it happens in `--post-js`. This was always the case for sync instantiation too. We now detect this too-late-assignment at abort with a useful error. See emscripten-core#23626 and emscripten-core#23420
We had some bug reports recently when we switched to using async/await in MODULUARIZE mode. One of the side effects was the `--post-js` code then runs after module instantiation and startup, which means assignment of properties like `onRuntimeInitialized` is too late if it happens in `--post-js`. This was always the case for sync instantiation too. We now detect this too-late-assignment at abort with a useful error. See emscripten-core#23626 and emscripten-core#23420
We had some bug reports recently when we switched to using async/await in MODULUARIZE mode. One of the side effects was the `--post-js` code then runs after module instantiation and startup, which means assignment of properties like `onRuntimeInitialized` is too late if it happens in `--post-js`. This was always the case for sync instantiation too. We now detect this too-late-assignment at abort with a useful error. See emscripten-core#23626 and emscripten-core#23420
We had some bug reports recently when we switched to using async/await in `MODULUARIZE` mode. One of the side effects was the `--post-js` code then runs after module instantiation and startup, which means assignment of properties like `onRuntimeInitialized` is too late if it happens in `--post-js`. This was always the case for sync instantiation mode. When assertions are enabled, we now detect this too-late-assignment and abort with a useful error. Code size regression in this PR, as expected, only for debug builds. See #23626 and #23420
While attempting to get SQLite building with Emscripten 4.0.0 we've discovered that our --extern-post-js code is failing because initialization which has formerly been done via
Module.postRun
is no longer being run (or is perhaps being run later than it historically did and we can't get to that point because our --extern-post-js requires the prior ordering).This is most easily demonstrated with some console debug output comparing 3.1.74 (the last 3.1 release) and 4.0.0:
The relevant part is the 3rd yellow line on the left. That line, from the start of a Module.postRun handler, is never hit in 4.0.0.
i've not found any relevant change mentioned in recent change logs.
Adding this silly workaround to our extern-post-js code, from the then() handler of calling the EXPORT_NAME-defined module init function, works around the problem:
Which makes it now behave like 3.1.74 except for the later timing of postRun():
The text was updated successfully, but these errors were encountered: