Make ReactionCtx::spawn_physical_thread return a vanilla JoinHandle instead of a ScopedJoinHandle<'x>. This is better for usability as the join handle can be stored in a reactor as a state var, and eg joined on shutdown. This allows more programs to be written: currently some tests like AsyncCallback (link below) are not implementable.
To enable this, physical actions do not send Event<'x> directly, because these contain a reference to the internal data structure of the scheduler. Instead, they just send the ID of the action that has been triggered, and let the scheduler fetch that reference itself. It's a real cleanup for the internals of the scheduler.
Rationale
The LF-Rust runtime currently forces the user to use [[scoped threads]] if they want to create a thread that can communicate with the schedule. The reason it is so is to be able to capture references to the internals of the scheduler. It's not a good reason because it could actually be avoided, although it's the historical reason.
There is one debatably good thing that this design gives us, it's that those async threads are guaranteed to be joined by the scheduler when it shuts down the program. I think it's nice because it gives strong semantics to the [[shutdown]] procedure, but it's also maybe not the semantics that we want - maybe it's convenient to let those async threads die alone when the scheduler shuts down.
Using scoped threads actually is otherwise very cumbersome. We have to carry around [[lifetime annotations]] everywhere in the scheduler, which is a source of great pain. It also prevents users from storing threads in reactors and joining them whenever they require it (because otherwise reactors would have to carry lifetime annotations themselves - my attempt at allowing that didn't go well). For instance a program like AsyncCallback cannot be implemented. Ultimately it doesn't net the user more flexibility because the scope of the thread is fixed and only allows taking references to scheduler internals, which LF users don't have access to. It's only an implementation artefact that gets exposed to the user through the interface.
Summary
ReactionCtx::spawn_physical_thread
return a vanillaJoinHandle
instead of aScopedJoinHandle<'x>
. This is better for usability as the join handle can be stored in a reactor as a state var, and eg joined on shutdown. This allows more programs to be written: currently some tests like AsyncCallback (link below) are not implementable.Event<'x>
directly, because these contain a reference to the internal data structure of the scheduler. Instead, they just send the ID of the action that has been triggered, and let the scheduler fetch that reference itself. It's a real cleanup for the internals of the scheduler.Rationale
The LF-Rust runtime currently forces the user to use [[scoped threads]] if they want to create a thread that can communicate with the schedule. The reason it is so is to be able to capture references to the internals of the scheduler. It's not a good reason because it could actually be avoided, although it's the historical reason.
There is one debatably good thing that this design gives us, it's that those async threads are guaranteed to be joined by the scheduler when it shuts down the program. I think it's nice because it gives strong semantics to the [[shutdown]] procedure, but it's also maybe not the semantics that we want - maybe it's convenient to let those async threads die alone when the scheduler shuts down.
Using scoped threads actually is otherwise very cumbersome. We have to carry around [[lifetime annotations]] everywhere in the scheduler, which is a source of great pain. It also prevents users from storing threads in reactors and joining them whenever they require it (because otherwise reactors would have to carry lifetime annotations themselves - my attempt at allowing that didn't go well). For instance a program like AsyncCallback cannot be implemented. Ultimately it doesn't net the user more flexibility because the scope of the thread is fixed and only allows taking references to scheduler internals, which LF users don't have access to. It's only an implementation artefact that gets exposed to the user through the interface.