Perl 6 Concurrency development

A perhaps naive idea on how to get rid of nested runloops. AFAIK there are two cases where nested runloops are used:

  • native code called via NCI calling back into Parrot
  • exception handlers throwing exceptions

NCI/custom ops

For the NCI case, stackless Python gave me an idea:

Instead of NCI calling the native function directly, it schedules a special task immediately which does the call. This way the scheduler is getting involved and the call is done from outside any runloop (but perhaps with prepared jump buffers). If this native function is calling back into Parrot, the same happens. The PIR function is not called directly, but again scheduled immediately and the NCI task suspended. Now the nested PIR call uses the only active runloop making suspend trivial.

Suspending the NCI task is probably the trickiest part here, since it would have to retain the C-stack of the native function. A possible way could be to replace current nested runloops by nested scheduler taskloops. Sounds useless at first, but there is a crucial difference between the two: it does not matter if one resumes a task in the outermost taskloop or in a nested one while resuming a task from within another runloop is impossible due to invalid jump buffers.

Exceptions

Idea pending due to need for more information about what exactly happens when a handler throws an exception. But probably something similiar to the NCI case could be used.

Performance considerations

The critical point in this system is calling C code from PIR. At this point, the PIR task has to be suspended, a native task to be created and scheduled to be called by the scheduler. This may prove to be prohibitive depending on how many situations there can be where this native code would need to call back into PIR.

The call back into PIR would hardly be more expensive than is is already. Currently the pseudo-callstack of calling back into parrot looks like this:

NCI code -> Parrot_ext_call -> Parrot_pcc_invoke_from_sig_object ->  runops -> runops_int

runops would set up a new runloop (needs to allocate memory)

In the non-nested runloop implementation it would look like:

NCI code -> Parrot_ext_call -> Parrot_cx_taskloop -> Parrot_pcc_invoke_from_sig_object -> runops -> runops_int

Parrot_ext_call would set up the PIR task (allocating memory). runops on the other hand would not have to bother with a new runloop anymore.

Powered by CiderCMS

Edit this page