janet-lang / janet

A dynamic language and bytecode vm
https://janet-lang.org
MIT License
3.45k stars 223 forks source link

Remove thread-local state from GC #1199

Closed iacore closed 1 year ago

iacore commented 1 year ago

I hope this is correct.

If janetvm is removed from thread-local, then Janet can be embedded better (multiple VM in a thread).

bakpakin commented 1 year ago

Besides the number of CodeQL warnings, I don't think this accomplishes anything. Janet's VM can be used with multiple interpreters on the same thread, you just need to switch between them with janet_vm_save() and janet_vm_load(). Since all functions calls to the JanetVM don't actually take all of the implicit "global" interpreter state, there is the concept of having one interpreter "active" per thread at any given time. Switching between the interpreters is fairly lightweight, but simply doing this in the GC does not actually accomplish anything afaict. This is pretty similar to passing global state to all calls into the interpreter, but does give a tiny increase in the cost of an interpreter switch.

See the below definitions from janet.h:

JANET_API JanetVM *janet_vm_alloc(void);
JANET_API JanetVM *janet_local_vm(void);
JANET_API void janet_vm_free(JanetVM *vm);
JANET_API void janet_vm_save(JanetVM *into);
JANET_API void janet_vm_load(JanetVM *from);

To have multiple VMs on a single thread, you would first call JanetVM *my_subinterpreter = janet_vm_alloc();, and then when you wanted to switch to the new interpreter you would first save the main interpreter with JanetVM old_state; janet_vm_save(&old_state); janet_vm_load(my_subinterpreter); and then probably call janet_loop() or similar to run the new interpreter.

Do you have a motivating example/program?

iacore commented 1 year ago

To have multiple VMs on a single thread, you would first call JanetVM *my_subinterpreter = janet_vm_alloc();, and then when you wanted to switch to the new interpreter you would first save the main interpreter with JanetVM old_state; janet_vm_save(&old_state); janet_vm_load(my_subinterpreter); and then probably call janet_loop() or similar to run the new interpreter. Do you have a motivating example/program?

I don't have a motivating example. I simply copied Lua's design for having VM be a C variable.

For callback from Janet to C, and moving VM between threads, what's the safe way to do it?

For example, during a call to janet_dostring, the control might be passed back to C. The stack is then captured by minicoro, and then later resumed on another OS thread.

Rust async runtimes also like to move stuff between OS threads.

I don't know Janet implementation enough to understand if this is safe or not.