Module kernel::services::forth_spawnulator

source ·
Expand description

Service for spawning new Forth tasks.

This is a channel producer that communicates with the background task created by SpawnulatorServer::register.

§The Unfortunate Necessity of the Spawnulator

Forth tasks may spawn other, child Forth tasks. This is currently accomplished by sending the forked child Forth VM over a channel to a background task, which actually spawns its Forth::run() method. At a glance, this indirection seems unnecessary (and inefficient): why can’t the parent task simply call kernel.spawn(child.run()).await in the implementation of its spawn builtin?

The answer is that this is, unfortunately, not possible. The function implementing the spawn builtin, spawn_forth_task(), must be async, as it needs to perform allocations for the child task’s dictionary, stacks, etc Therefore, calling spawn_forth_task() returns an impl Future which is awaited inside the Dispatcher::dispatch_async() future, which is itself awaited inside Forth::process_line() in the parent VM’s Forth::run() async method. This means the layout of the future generated for spawn_forth_task() must be known in order to determine the layout of the future generated for Forth::run(). In order to spawn a new child task, we must call Forth::run() and then pass the returned impl Future to Kernel::spawn(). This means that the generated impl Future for Forth::run() becomes a local variable in Forth::run() — meaning that, in order to compute the layout for Forth::run(), the compiler must first compute the layout for Forth::run()…which is, naturally, impossible.

We can solve this problem by moving the actual kernel.spawn(forth.run()).await into a separate task (the spawnulator), to which we send new child Forth VMs to over a channel, without having called their run() methods. Now, the Forth::run() call does not occur inside of Forth::run(), and its layout is no longer cyclical. I don’t feel great about the fact that this requires us to, essentially, place child tasks in a queue in order to wait for the priveliege of being put in a different queue (the scheduler’s run queue), but I couldn’t easily come up with another solution…

Structs§