Rules / Types, classes & roles / domain-specific

Lock Reference

A low-level, re-entrant, mutual exclusion lock

What it is #

Routines #

Signatures are reproduced from the documentation, including their declared return types. A routine listed here is part of the type's published interface; whether Raku++ implements it is a separate question, answered by the examples below.

method protect #

multi method protect(Lock:D: &code)

Obtains the lock, runs &code, and releases the lock afterwards. Care is taken to make sure the lock is released even if the code is left through an exception. Note that the Lock itself needs to be created outside the portion of the code that gets threaded and it needs to protect. In the first example below, Lock is first created and assigned to $lock, which is then used inside the Promises to protect

method lock #

method lock(Lock:D:)

Acquires the lock. If it is currently not available, waits for it. Since a Lock is implemented using OS-provided facilities, a thread waiting for the lock will not be scheduled until the lock is available for it. Since Lock is re-entrant, if the current thread already holds the lock, calling lock will simply bump a recursion count. While it's easy enough to use the lock method, it's more difficult to

method unlock #

method unlock(Lock:D:)

Releases the lock. It is important to make sure the Lock is always released, even if an exception is thrown. The safest way to ensure this is to use the protect method, instead of explicitly calling lock and unlock. Failing that, use a LEAVE phaser.

method condition #

method condition(Lock:D: )

Returns a condition variable as a Lock::ConditionVariable object. Check this article or the Wikipedia for background on condition variables and how they relate to locks and mutexes. You should use a condition over a lock when you want an interaction with it

Examples, run three ways #

Every example below comes from the official documentation, together with the output that documentation asserts. Each was then executed by Rakudo and by Raku++ when this page was built. Where the three agree, one result is shown; where they do not, all three are — because which of them is wrong is exactly the information worth having.

1 all-differ · 7 no-output · 1 ok

class Lock {}

Not executed: the documentation states no expected output for this example.

my $x = 0;
my $l = Lock.new;
await (^10).map: {
    start {
        $l.protect({ $x++ });
    }
}
say $x;         # OUTPUT: «10␤»
Output
10

Documentation, Rakudo and Raku++ all agree.

# Right: $lock is instantiated outside the portion of the
# code that will get threaded and be in need of protection
my $lock = Lock.new;
await ^20 .map: {
    start {
        $lock.protect: {
            print "Foo";
            sleep rand;
            say "Bar";
        }
    }
}

Not executed: the documentation states no expected output for this example.

# !!! WRONG !!! Lock is created inside threaded area!
await ^20 .map: {
    start {
        Lock.new.protect: {
            print "Foo"; sleep rand; say "Bar";
        }
    }
}

Not executed: the documentation states no expected output for this example.

my $l = Lock.new;
$l.lock;

Not executed: the documentation states no expected output for this example.

my $l = Lock.new;
$l.lock;
$l.unlock;

Not executed: the documentation states no expected output for this example.

my $l = Lock.new;
{
    $l.lock;
    LEAVE $l.unlock;
}

Not executed: the documentation states no expected output for this example.

my $l = Lock.new;
$l.condition;

Not executed: the documentation states no expected output for this example.

constant ITEMS = 100;
my $lock = Lock.new;
my $cond = $lock.condition;
my $todo = 0;
my $done = 0;
my @in = 1..ITEMS;
my @out = 0 xx ITEMS;

loop ( my $i = 0; $i < @in; $i++ ) {
my $in := @in[$i];
my $out := @out[$i];
Thread.start( {
  my $partial = $in² +1;
  if $partial.is-prime {
      $out = $partial but "Prime";
  } else {
      $out = $partial;
  }
  $lock.protect( {
     $done++;
     $cond.signal if $done == $todo;
  } );
} );
$todo++;
}
$lock.protect( {
$cond.wait({  $done == $todo } );
});

say @out.map: { $_.^roles > 2 ?? $_.Num ~ "*" !! $_ };
# OUTPUT: «2* 5* 10 17* 26 37* 50 65 82 101* … »
Documentation
2* 5* 10 17* 26 37* 50 65 82 101* … 
Rakudo
(2* 5* 10 17* 26 37* 50 65 82 101* 122 145 170 197* 226 257* 290 325 362 401* 442 485 530 577* 626 677* 730 785 842 901 962 1025 1090 1157 1226 1297* 1370 1445 1522 1601* 1682 1765 1850 1937 2026 2117 2210 2305 2402 2501 2602 2705 2810 2917* 3026 3137* 3250 3365 3482 3601 3722 3845 3970 4097 4226 4357* 4490 4625 4762 4901 5042 5185 5330 5477* 5626 5777 5930 6085 6242 6401 6562 6725 6890 7057* 7226 7397 7570 7745 7922 8101* 8282 8465 8650 8837* 9026 9217 9410 9605 9802 10001)
Raku++

All three differ. Needs a human.

Not yet examined. Which of these is correct has not been established — do not treat either engine as settled here.