Remove std::process.poll
Polling is currently only used in std::test
, and is needed so we can present test progress as tests finish, instead of waiting until all tests in a batch finish. Polling however isn't efficient, requiring at least a single pass over all the futures that are polled. The larger this list gets, the more expensive it becomes. Polling also involves mutating the input array, and returning a new array every time the call returns.
The core problem is the following: library code may wish to spawn a bunch of processes and wait for all of them to complete, but display progress as these processes finish. This requires the parent process to block, but be woken up every time progress is made, without the sub processes knowing anything about the parent process (i.e. what messages it supports).
To solve this I think we need to do the following:
First we have to allow passing mut SomeProcess
/ref SomeProcess
to SomeProcess
, which is currently disallowed. Basically we treat them the same way as Int
. This makes passing references to processes around possible, and in turn allows for bi-directional communication between processes.
Next we'd need some kind of Parker
type. This type would be used to park an arbitrary process, then later unpark it. In case of std::test
we'd have a intermediate/coordinator process that is tasked with spawning test runner processes, and has access to the Parker
of whatever process is using std::test
. The parent process parks itself, and the intermediate process unparks it once all tests are complete. In this setup no polling is necessary, and we retain a structured form of concurrency.
This could also be used to implement a blocking queue and related types: the queue itself would be a process, and support at least a pop
, push
and wait
message. Users would first try to pop()
using the usual messaging approach. If this doesn't return anything the user would create a Parker
, send a clone using wait
, then park itself. The queue in turn would keep these Parker
values around, notifying them as new values are added. The popping side them simply retries the operation.
Channels are another option, but I find them to be a bit of an odd choice in a language that uses actors. In addition, if one wants to communicate both input and output over the same channel each value has to be wrapped in e.g. an enum. In contrast, a Parker
is a much more generic and flexible approach.