See Original text in context
multi sub cas(Mu is rw, Mu \expected, Mu \value)multi sub cas(Mu is rw, )
Performs an atomic compare and swap of the value in the Scalar
$target
. The first form has semantics like:
my = ;if <> =:= <>return ;
Except it is performed as a single hardware-supported atomic instruction, as if all memory access to $target
were blocked while it took place. Therefore it is safe to attempt the operation from multiple threads without any other synchronization. Since it is a reference comparison, this operation is usually not sensible on value types.
For example:
constant NOT_STARTED = Any.new;constant STARTED = Any.new;my = NOT_STARTED;await startxx 4
Will reliably only ever print Master!
one time, as only one of the threads will be successful in changing the Scalar
from NOT_STARTED
to STARTED
.
The second form, taking a code object, will first do an atomic fetch of the current value and invoke the code object with it. It will then try to do an atomic compare and swap of the target, using the value passed to the code object as expected
and the result of the code object as value
. If this fails, it will read the latest value, and retry, until a CAS operation succeeds.
Therefore, an item could be added to the head of a linked list in a lock free manner as follows:
my Node = Node;await startxx 4;
This will reliably build up a linked list of 4000 items, with 4 nodes with each value ranging from 0 up to 999.
Note: Before Rakudo version 2020.12, $target
, expected
and value
had an Any
type constraint.
See Original text in context
multi sub cas(atomicint is rw, int , int )multi sub cas(atomicint is rw, Int() , Int() )multi sub cas(atomicint is rw, )
Performs an atomic compare and swap of the native integer value in location $target
. The first two forms have semantics like:
my int = ;if ==return ;
Except it is performed as a single hardware-supported atomic instruction, as if all memory access to $target
were blocked while it took place. Therefore it is safe to attempt the operation from multiple threads without any other synchronization. For example:
my atomicint = 0;await startxx 4
Will reliably only ever print Master!
one time, as only one of the threads will be successful in changing the 0 into a 1.
Both $expected
and $value
will be coerced to Int
and unboxed if needed. An exception will be thrown if the value cannot be represented as a 64-bit integer. If the size of atomicint
is only 32 bits then the values will be silently truncated to this size.
The third form, taking a code object, will first do an atomic fetch of the current value and invoke the code object with it. It will then try to do an atomic compare and swap of the target, using the value passed to the code object as $expected
and the result of the code object as $value
. If this fails, it will read the latest value, and retry, until a CAS operation succeeds. Therefore, an atomic multiply of an atomicint
$i
by 2 could be implemented as:
cas , -> int
If another thread changed the value while $current * 2
was being calculated then the block would be called again with the latest value for a further attempt, and this would be repeated until success.