Sterling has too many projects Blogging about programming, microcontrollers & electronics, 3D printing, and whatever else...

Supply Back Pressure

| 349 words | 2 minutes | raku advent-2019
Person receiving a back massage

In Raku, a Supply is one of the primary tools for sending messages between threads. From the way a Supply is structured, it is obvious that is provides a means for one ore more tasks to send events to multiple recipient tasks. What is less obvious, however, is that a Supply imposes a cost on the sender.

Consider this program:

my $counter = Supplier.new;
start react whenever $counter.Supply {
    say "A pre-whenever $_";
    sleep rand;
    say "A post-whenever $_";
}
start react whenever $counter.Supply {
    say "B pre-whenever $_";
    sleep rand;
    say "B post-whenever $_";
}
start for 1...* {
    say "pre-emit $_";
    $counter.emit($_);
    say "post-emit $_";
}
sleep 10;

Here we have three tasks running, each in a separate thread. We let the main program quit after 10 seconds. The first threads two receive messages from the $counter.Supply. The third thread feeds a sequence of integers to this supply. You might be tempted to think that the final task will race through the delivery of events, but if so, you’d be wrong.

Consider the output of this program:

pre-emit 1
A pre-whenever 1
A post-whenever 1
B pre-whenever 1
B post-whenever 1
post-emit 1
pre-emit 2
A pre-whenever 2
A post-whenever 2
B pre-whenever 2
B post-whenever 2
post-emit 2

Notice a pattern? Even though there’s a random wait in the first two threads and no wait at all in the third, the third thread is blocked until both of the other threads complete. This behavior is the same regardless of how the Supply is tapped, i.e., it does not matter if you use whenever blocks or call .tap.

Therefore, if you want your emitter to blast through events as quickly as possible, you need to make sure the taps are written to finish as soon as possible or consider a different solution, such as using a Channel which will queue tasks in memory and they will get processed whenever the thread listening to that channel has time to process them.

Just be aware of this back pressure cost whenever using a Supply. The sender always pays.

Cheers.

The content of this site is licensed under Attribution 4.0 International (CC BY 4.0).

Image credit: unsplash-logoToa Heftiba