Sterling has too many projects Blogging about programming, microcontrollers & electronics, 3D printing, and whatever else...
Posts with the tag Raku:

Supply Back Pressure

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.

» read more

Comparing react with tap

In Raku we have a couple basic ways of getting at the events emitted from a Supply, which begs the question, what’s the difference between each? I want to answer that question by creating a react block with a couple intervals and then emulate the same basic functionality using tap.

Let’s start with our base react block:

sub seconds { state $base = now; now - $base }
react {
    say "REACT 1: {seconds}";

    whenever Supply.interval(1) {
        say "INTERVAL 1-$_: {seconds}";
        done if $_ > 3;
    }

    say "REACT 2: {seconds}";

    whenever Supply.interval(0.5) {
        say "INTERVAL 2-$_: {seconds}";
    }

    say "REACT 3: {seconds}";
}

The seconds routine is just a helper to give us time in seconds from the start of the block to work from. The output from this block will typically be similar to this:

» read more

Semaphores

A semaphore is a system of sending messages using flags. Oh wait, that’s what a semaphore is outside of computing. Among computers, a semaphore is like a kind of lock that locks after being acquired N times. This is useful for situations where you have a resource of N items, want to quickly distribute them when you know they are available, and then immediately block until a resource has been released. Raku provides a built-in Semaphore class for this reason:

» read more

Breaking Down Async Problems

One challenge I’ve often faced when writing asynchronous code is trying to figure out how to break the problem down in a reasonable way. How far do I go in breaking up my code? How many steps do I want to take? How do I deal with tasks that branch or fork? How do I deal with the interdependencies I’ve created. My hope in this article is to give some guidelines I’ve learned and some tools for answering these questions.

» read more

Compare-and-swap Your Scalars

Previously, I discussed the compare-and-swap operation as an operation to perform on atomicint variables. That’s just the tip of the iceberg. While most of the atomic emoji ⚛️ operators are only for atomicints, the cas function, atomic-fetch (or prefix ⚛️ operator), and atomic-assign (or ⚛️= operator) can all be used on any kind of Scalar variable.

First, we need to make sure we know what a Scalar is. In Raku, every variable name is associated with a container. If you want the full description of how that works, I recommend reading the language docs on Containers. For our purpose it will suffice to say that almost every regular variable starting with $ sigil is contained by a Scalar. If you do something special to initialize such a variable, it may not have a Scalar container.

» read more

The Monitor Pattern

Today I want to discuss the use of locks to make an object thread safe. That is, by employing a simple pattern for locking access to your object, you can effectively guarantee that only a single thread has access to any part of the object at a time. This, therefore, guarantees that the state of the object can never be corrupted, even when multiple threads attempt to access it concurrently.

» read more

Keep Your Thread or Don't

In Raku, there are different ways to pause your code. The simplest and most obvious way to do so is to use sleep:

my $before = now;
sleep 1;
my $after = now - $before;
say $after;

Assuming your system is not bogged down at the moment, the output of $after should be a number pretty close to 1. Not very exciting: you didn’t do anything for a second. Woo. Woo.

In real code, however, you do sometimes need to pause. For example, you might be trying to send an email and it fails. When sending email, it is nice if it arrives quickly, but essential that it arrives eventually. Thus, when queueing up a message, you want to watch for errors. When they occur, you want to keep trying for a long time before giving up. However, you don’t want to keep trying constantly. You need to pause between retries:

» read more

Parallel Map-Reduce Pattern

Map-reduce is a common way of solving problems in functional programming. You have a list of items, you iterate through them to process them, and then you take the set and summarize them. We call this map-reduce because the iteration step is mapping values to new values and the summarize step reduces the number of values.

In Raku, map-reduce is a common programming pattern:

my $fibonacci = (1, 1, * + * ... *);
my $double-sum = $fibonacci[^100].grep(*.is-prime).map(* * 2).reduce(* + *);

This creates a Seq with the sequence operator. This is the classic Fibonacci sequence. We then take the first 100 elements of Fibonacci, filter out any non-primes, double the primes, and the sum the values.

» read more

The Divide and Conquer Pattern

Let’s consider now how to solve a big problem with concurrency. If you have an algorithmic problem with lots of data that needs to be processed, you want to maximize the amount of load you can place on the available CPU cores to process it as quickly as possible. To demonstrate how we can go about this in Raku, we will consider Conway’s Game of Life, played on an effectively infinite game board.1

» read more

Atomic Integers

What’s faster than locks? Compare-and-swap. Modern CPUs have multiple cores. As such, all modern CPUs must have tools for performing absolutely atomic operations to allow those multiple cores to work together. One of those operations is the compare-and-swap or cas operation.

In the abstract, a cas operation takes three arguments, a variable to modify, a given value, and a new value. The variable is set to the new value modified only if the current value held by the variable is equal to the given value. And this is performed as a single operation guaranteed to be safe from interference by any other concurrent operation. Along the way, the system sets a flag marking the success or failure of the operation. If the variable has a different value than expected, the variable is left unchanged and the operation fails. To complete an atomic change, you repeatedly run a calculation and end with a cas operation until it succeeds. That may not sound very efficient, but it turns out that in most cases, it is faster than locks.

» read more