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
Loading multiple emscripten modules in one page #3167
Comments
Being on the same page without sharing the HEAP is possible, you just give each its own Module object. For example, by putting each compiled codebase in a function scope where Module is defined. That's how ammo, box2d, etc. work, they are fine on pages with other emscripten-compiled projects. Sharing the HEAP is, on the one hand, simple. We just need to tell emscripten which range in the heap to actually use. This would be incompatible with memory growth, but otherwise ok. However, the more I think about this, the more worried I am ;) Is this safe? They will each have their own malloc and free, for example. Allocating in one and freeing in another will lead to corruption and errors. Calling a method on an object you pass between them will also fail (separate function tables). It seems like this would only work with simple data, ints and floats, arrays, pointers, etc., and without manipulating it. Very fragile. |
Cross-module malloc/free is already broken in most dynamic linking scenarios, though, unless you're compiling all the modules yourself with a single compiler configuration. (I'm coming from a Win32 background, where the odds are good that you've got a bunch of libraries that came from different compilers and have different C runtimes.) For my scenarios I mostly care about large-scale stuff, where it becomes unreasonable to copy between heaps - like I've got 2mb of vertex data here, or a texture there, and I want to pass it from one emscripten library to another. If I could just pass a 2MB Float32Array into an asm.js function I'd do that instead :-) Cases like calling methods across module boundaries or invoking destructors seem hopelessly complex in comparison, and the returns are much smaller - you can just copy most of those bytes around and it'll be quite fast. EDIT: If it helps, I'm thinking in terms of address space here, mostly. Being able to access the bytes at a pointer from moduleA in moduleB. So it's probably intended/best for each module to have their own heap, using a separate reservation of the single shared HEAP memory buffer. Though since emscripten provides its own malloc/free, there could always be one shared between all modules. |
I see. So, another option here is to add support for non-asm.js-validating passing of typed arrays. Not sure how practical this would be. All access to the typed array would need to be through a special interface, imagine something like
where
and |
I think that solves the problem pretty well in a general sense but not for existing code. The problem I'm looking at is passing external data into existing libraries (like say zlib or sdl), and the external data isn't easy to get into the heap of individual Modules without copies. That's why I want to share a single buffer between them, since I can just pass the offset between the modules, and then pass that final buffer to WebGL. If I have to change all the code to use FloatVec and then compile in a non-asm-validating mode, it's probably not going to end up being useful for the cases I care about. It seems like a reasonable solution though, if other people have a use for it in similar scenarios. The C type system really hurts us here since all we have are addresses, instead of ranges like if we were dealing with known-length arrays and buffers in another language. :/ |
Got it. Ok, sounds like we want to experiment with sharing a single ArrayBuffer over multiple programs. That commit should provide enough to start experimenting with this. As i suspected, it's pretty easy: emscripten can already put the program's "start of memory" anywhere it wants, using I don't have an idea for a good test for this, but it might work... |
That's perfect! I can add it to my set of basic test cases, at least. |
An obstacle here is that because emscripten output files aren't wrapped in an IIFE, it defines Module at global scope and uses it at global scope. So if I capture the Module object from one library then load another one, they are both using the current value of Module instead of the appropriate one. They're going to end up all using a single arbitrarily selected _malloc, printErr, etc. |
|
Does that work fully? It looks like you're intending for it to be possible to make multiple instances of an emscripten application, but unless I'm missing something, two instances of the same application will still quarrel over the global exported name: Maybe something similar to PR #3008 would help in that case? My changes to shell.js could be merged with |
I agree it would be good to do something like that pull. However, I don't think there is currently a problem as if MODULARIZE is used with multiple modules on the same page, it must be used with different EXPORT_NAME values anyhow (because we do But, I wonder if we can't just remove the assignment to |
Running test suite now. |
Well, the test suite seems to not complain, so I pushed this. If it sticks and no one finds fault, I think we can close that pull (or was it doing something more)? |
It looks like it's possible to create multiple instances of the module by
|
Well, I don't think anyone was actually reading from There are definitely use cases where the method is called more than once, I've done it when the app ported was too "messy" to provide a nice interface, so by calling the method with everything in it, you get a new heap, as if you started up the program from scratch. I am sure there are better uses too ;) |
MODULARIZE seems to be working well in our test cases so far. I'll let you know once I have something shippable that's actually putting it to use with two modules. |
Hooray! I'm happy now too. We ship a JS SDK which hides the fact that it's MODULARIZE doesn't add much really, you could achieve the same with some |
Yeah, it's mainly just to have a stable option, with tests in the test suite, so there is a standard safe way to do this. |
I think this has been completed. |
which version was this added at? I get this
with 1.29.0 (which is what downloads link to). |
git says 1.29.9. edit: fix typo |
never the less MODULARIZE is not found in src/settings.js there. was it removed later? |
Sorry, I had a typo but I guess you saw an email from github which was before. It was added in 1.29.9, which is later. |
When using both flags, for example -s MODULARIZE=1 -s EXPORT_NAME=\"'ModuleIMGJS'\" How is it possible to access the FS library? |
The FS is not exported by default. You can add it to Module manually (e.g. in a
Then you can access |
If the FS module is exported as suggested. Do libraries share the FS space? I saw this post and I want to confirm https://groups.google.com/forum/#!topic/emscripten-discuss/_K61fo-9oKY As an example:
I would like to have a module that writes some data using FS and then, many different modules can read this data to do some processing. |
What do you mean by libraries? Shared modules as in https://github.com/kripken/emscripten/wiki/Linking ? Or just separately compiled projects, that are logically libraries? Shared modules definitely share the FS of the main module (they also share all the system libraries). But separately-compiled programs don't have a way to share the FS, they each define one. But things like BrowserFS might help. There might also be a simple way to just make them reference the same FS object, however, that has references to things in memory (like the stdin stream in C), so memory would likely need to be shared, too. But that is feasible as well. However, that would take some hacking, probably. |
Yes, I want to have separately compiled libraries. Thank you.
|
Hi This is how I'm calling emcc: And I'm getting this runtime error: This is how the c function is declared: __attribute__((used))
ResamplerState *resampler_init(int nb_channels, int in_rate, int out_rate, int quality, int *err)
{
return resampler_init_frac(nb_channels, in_rate, out_rate, in_rate, out_rate, quality, err);
}
When I don't MODULARIZE all is ok, Thanks, |
Are you instantiating the module? When we modularize, you must create an instance (and can create multiple, which is a benefit here, etc.). If it's not that, perhaps look in the test suite for how modularize is used. Also looking in your build output might show the problem. Or, if you make a standalone testcase showing the issue, I can take a look at that. |
Yup, this was the problem, should have done: var DIGI = ModuleDIGI();
DIGI._resampler_init Thanks! |
I got another question: eval(require("fs").readFileSync("./digi-resampler.js").toString());
var DIGI = ModuleDIGI(); What happens here is that anything on the Regarding modularizations again, if I have two different modules I compiled with emcc that I want to use in the same page, is it enough to modularize one of them or do I need to modularize both? |
I don't know node that well, but i'm not sure i follow this. Who is writing to |
It would be great to have (at least basic) support for loading multiple emscripten modules in one webpage. This is important for scenarios where a larger application wants to pull in existing native libraries without itself being written in C.
Right now doing this will produce multiple files that blow away Module and potentially shadow each others' names in the global namespace (real__ or whatever), and they don't share an address space.
Sharing an address space is really valuable here because without it, you have to associate each pointer (
void*
, etc) with the emscripten module it came from, and carry that association around. Carrying it through C++ call boundaries is really tough. I think it's not necessary for anything more than shared address space to happen though - it's fine for the modules to still have their own stack, and even have their own function pointer table and malloc/free. (Though a shared function pointer table would be handy, it's easier to carry annotations around for FPs since they're already more magical than regular data pointers.)(For reference, my use case is porting C# applications that call native libraries - like SDL2 or sqlite - to the browser by compiling the native libraries with emscripten individually. It seems like at present the only way to do this would be some sort of merged library, assuming merging them is viable without conflicts.)
The text was updated successfully, but these errors were encountered: