jnthn / oo-monitors

Monitors with condition variables for Raku
12 stars 7 forks source link

OO::Monitors

Build Status

A monitor provides per-instance mutual exclusion for objects. This means that for a given object instance, only one thread can ever be inside its methods at a time. This is achieved by a lock being associated with each object. The lock is acquired automatically at the entry to each method in the monitor. Condition variables are also supported.

Basic Usage

A monitor looks like a normal class, but declared with the monitor keyword.

use OO::Monitors;

monitor IPFilter {
    has %!active;
    has %!blacklist; 
    has $.limit = 10;
    has $.blocked = 0;

    method should-start-request($ip) {
        if %!blacklist{$ip} || (%!active{$ip} // 0) == $.limit {
            $!blocked++;
            return False;
        }
        else {
            %!active{$ip}++;
            return True;
        }
    }

    method end-request($ip) {
        %!active{$ip}--;
    }
}

That's about all there is to it. The monitor meta-object enforces mutual exclusion.

Conditions

Condition variables are declared with the conditioned trait on the monitor. To wait on a condition, use wait-condition. To signal that a condition has been met, use meet-condition. Here is an example of a bounded queue.

monitor BoundedQueue is conditioned(< not-full not-empty >) {
    has @!tasks;
    has $.limit = die "Must specify a limit";

    method add-task($task) {
        while @!tasks.elems == $!limit {
            wait-condition <not-full>;
        }
        @!tasks.push($task);
        meet-condition <not-empty>;
    }

    method take-task() {
        until @!tasks {
            wait-condition <not-empty>;
        }
        meet-condition <not-full>;
        return @!tasks.shift;
    }
}

When wait-conditon is used, the lock is released and the thread blocks until the condition is met by some other thread. By contrast, meet-condition just marks a waiting thread as unblocked, but retains the lock until the method is over.

Circular waiting

Monitors are vulnerable to deadlock, if you set up a circular dependency. Keep object graphs involving monitors simple and cycle-free, so far as is possible.