Parataxis in the Near Future #7
sanko
announced in
Announcements
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
I've been looking at other concurrency systems as I tinker and I am determined to bring what I've found to this project. No more introduction, let me toss my notes here.
Nursery pattern
I'm imagining a system like Python's TaskGroup or StructuredTaskScope from Java 21. Right now, fibers can be spawned detached. If one fails, throws an exception, or leaks, it's orphaned. Structured concurrency would enforce lifetimes onto fibers, binding them to the lexical block that created them. Perl's ref counter should make this an elegant solution.
Here's the API in my head:
The nursery spawns child fibers and will not exit until all fibers are complete. If any child fails with an unhandled exception, all sibling fibers in this particular nursery are cancelled and the nursery throws an error.
Cooperative Cancellation and Deadlines
This is straight from tokio.1
Right now, there's no way to interrupt a parataxis fiber that's blocked by
await_sleep,await, or a::Channel->getcall. What I need looks like this:$tok->is_cancelledor raise an interrupt at suspension points.CSP Channel Multiplexing
Communicating Sequential Processes (CSP) is just a fancy way to say
select( ... )over channels. I'm borrowing this directly from Rust'scrossbeam_channel.Currently, ::Channel supports
getandput(which are blocking) but CSP requires non-deterministic multiplexing, the ability to wait on multiple channels at the same time without busy-waiting.This could eventually look like this:
Implementing it might be tricky. A fiber will register a synthetic waiter across multiple channels and whichever channel satisfies the condition first unblocks the fiber and unregisters it from the other channels.
Hypothetical snippet once I get this working:
Stackful iterators
This is common across a lot of languages... Python, Ruby, Javascript, etc...
It might look like this:
class Acme::Parataxis::Generator { field $fiber; method next () { ... } } # Example usage: my $gen = Acme::Parataxis::Generator->new(sub ($yield) { for my $item (@large_tree) { $yield->($item); # Suspends fiber and yields value } }); while (defined(my $val = $gen->next)) { say 'Got ' . $val; }This would be amazing for batched network access.
Actor pattern
I kept this away for a long time because it's a mess when trying to insert into a language that's already unfriendly to concurrency but fibers lend themselves to it.
New
askandsend(ortell) methods that opens up the status so a supervisor can automatically manage children. If an actor terminates on an error, its supervisor can automatically restart it according to OTP restart strategy (one-for-one, one-for-all, etc.).More Primitives
Mutextes, WaitGroups (like Go's
sync.WaitGroup), RwLocks, etc.While a binary semaphore functions as a mutex, a true Mutex tracks fiber ownership, prevents release from non-owners, and enables read-heavy concurrency with a shared or exclusive read/write lock.
Barriers that holds a group of fibers to synchronize at a phase boundary before any are allowed to proceed.
Deadlock tracing
The most important missing feature. It's almost impossible to debug Parataxis because I never got around to this. I should be able to do
Acme::Parataxis->dump_fibers();and it prints all living fibers, their state (WAITING,RUNNING,SUSPENDED), what resources are they parked on (Semaphore, Cannel, etc), and the perl-level stack trace where they calledyield.My only idea to make this work is with weak references to the bottom layer.
Output might look like this on error:
But it would return structured data by default.
Footnotes
Go and C# also have similar systems ↩
All reactions