Shared Object Support?
I doubt this is the case, but is there by chance a way to compile mojo to linux shared objects? If not is there any plan to include the ability in the future? Even if they cant cross operate with other executables coded in languages other than mojo, they'd be super useful for a project I have in mind.
17 Replies
Shared objects cause a lot of issues for languages with generics. C++ has made a lot of language-level sacrifices to support them. Right now Mojo does not have a stable ABI, which means shared objects would need to expose a C ABI, which removes most of the good features of Mojo.
I ran into this issue with Rust and found that using shared memory IPC or unix sockets and having one program per language was sufficient for almost all cases.
If you want a plugin system, the way this is typically done is to round-trip via the C ABI. Mojo will eventually get clean interop with C/C++, and I think Rust interop may be doable.
Ah damn. My project in mind was to reimplement Erlang's BEAM engine in MOJO but in native compiled MOJO rather than on a VM. The shared objects idea was to make hot swap-ability of code viable. Back to the drawing board to figure out something else then XD. Thanks @Owen Hilyard
Congrats @Jabadahut50, you just advanced to level 2!
Shared memory IPC could work but that'd be a LOT of individual processes. I feel like it'd eat a lot of RAM. May just have to wait for some updates or something if I can't figure out a different way to do it.
@Owen Hilyard follow up question... is there per chance a way to compile mojo to MLIR bytecode? The bytecode format is fairly well documented and I know MOJO gets compiled down to MLIR before machine language anyway... and while i'd prefer compiled machine code, creating a bytecode interpreter could be another way of getting the functionality I want without too much performance sacrifice?
I don't think the current compiler allows that. Mojo has a JIT mode (used in the REPL and notebooks), so you may be able to use that once it comes out, it should still be faster than BEAM.
That could work... guess I'll be waiting for that then. Appreciate it.
By the way, if you are doing that wait until I finish writing async IO to implement IO. The interface is going to look a little weird but should give you some options that are better than what production BEAM does right now for message passing.
oooh now i'm even more impatiantley waiting XD
so update... there IS a way to do shared objects... it's just an undocumented feature (aka they may make breaking changes to it so they don't want anyone relying on it incase it breaks later as Mojo is taking the rust approach to backwards compatibility)
I've approached something similar in the past, the best solution I could come up with was something along the line of having orchestrator and runners be separate processes. Version the runners, and define a mechanism for determining what flow is allowed through versions for a given entry
It's clunkier in some ways, but gets you most of the way there
yeah Owen mentioned the idea of Shared IPC's which is a possibility but I worry how much ram it might eat doing all the runners as individual processes
It would be per deployment, which is an important distinction.
The runners don't have to be per-actor is my thought
That spares you from having to slim things down quite as much as you would have to for something as flexible as beam
Or, you can have one central "router" which does nothing but pass messages around and possibly do networking.
With a good ring buffer implementation, a single core can bounce hundreds of millions of messages per second between buffers.
I planned on having a dedicated thread for scheduling.
I think that there are a few routes for scheduling.
1. You can do a single scheduler thread and use signals to preempt tasks.
2. Thread per core, tasks never move between cores. (Easy to implement, removes Send/Sync requirements, etc).
3. Thread per core with work stealing. This handles unpredictable workloads better, but can cause issues since it requires than all tasks are Send.
Using signals isn't always great and can require some very fiddly unsafe, so I think that cooperative scheduling may be a better idea, especially since large servers tend to not like when a core is sending signals over the place (except for sapphire rapids with the userspace interrupts work, which is VERY specific to that hardware).
a fair point... it'll require extra work but I think a work stealing scheduler is probably the better option
the thing i've always wondered is everytime I read about the actor model everyone says "It eliminates race conditions" but if you're doing a multicore work stealing architecture wouldn't you potentially run into message appending as a race condition?
Allowing work stealing is not that much of a lift, though I would avoid preemption--just relax any requirements that a task may run at most once and make it so you can roughly detect when tasks have gone off the rails
In those cases, isolate them so that they trap themselves and quit at the next reasonable boundary, cut off their ability to commit their work, and spawn a new instance of the task
You could potentially set up a way to kill that entire runner if it gets consumed by some accidental
while(true)
bugs, but you can't really stop things at the level of a single thread (and if you could you'd be opening yourself up to some very broken partial state)