Sterling has too many projects

Blogging about Raku programming, microcontrollers & electronics, 3D printing, and whatever else...

Asynchronous Locking

Raku actually provides two different locking classes. A Lock object provides a very standard locking mechanism. When .lock and .unlock are used or .protect is called, you get a section of code that pauses until the lock frees up, runs while holding the lock, and then frees the lock so other code that might be waiting on the lock can run.

However, the Lock class works in such a way that blocks the current thread. As I’ve pointed out earlier in this advent calendar, the purpose of threads is to do stuff, so blocking them from running is preventing them from fulfilling their purpose. Luckily, there is a solution.

In cases where you want locking, but don’t want to burn your threads waiting for the lock to free up, consider using Lock::Async instead of Lock. It works very similarly to Lock, but the .lock method does not block. Instead, it returns a Promise which will be kept when the lock is free. Code that awaits that Promise will pause in a way that allows Raku to reuse the current thread for another task:

class SafeQueue {
    has @!queue;
    has Lock::Async $!lock .= new;

    method enqueue($value) {
        await $!lock.lock;
        push @!queue, $value;

    method dequeue(--> Any) {
        $!lock.protect: { shift @!queue }

The code above demonstrates both the use of .lock and .unlock as well as .protect. You should always prefer .protect as the code in enqueue above might leave the lock forever held if an exception gets thrown after the lock is acquired. From the point of view of your program, a .protect will behave similarly between Lock and Lock::Async, but internally performs an await on the .lock method. This means that the thread the code is running on will be freed to be used by another task that is waiting to be scheduled.