Suppose you have two code fragments, which may end up executed in parallel, and both of which access a global structure
*gs*
. The first fragment is a setter, and you can be sure that it is not executed in parallel to itself (normally because it actually runs inside a lock):
(setf
(my-structure-value-slot *gs*) ; store1
some-value)
(setf
(my-structure-counter-slot *gs*) ; store2
counter)
The second fragment is the reader. You want to guarantee that it gets a value that was stored after the counter reached some value (the counter value always increases). You may think that this will suffice:
(if (>=
(my-structure-counter-slot *gs*) ; load1
counter)
(my-structure-value-slot *gs*) ; load2
(.. something else ...))
Programmatically, if the
>=
is true then
store2
already occurred before
load1
, therefore
store1
also occurred before
load1
, and
load2
which happens after
load1
must happen after
store1
.
On a single CPU that is true. On a computer with multiple CPU cores it can go wrong (that is, load2 can happen before store1 ) because of two possible reasons:
To guarantee that load2 happens after store1 , both of these possibilities need to be dealt with. Thus the setter has to be:
(setf (my-structure-value-slot *gs*) ; store1
some-value)
(sys:ensure-stores-after-stores) ; ensure store order
(setf (my-structure-counter-slot *gs*) ; store2
(incf-counter))
(if (> (my-structure-counter-slot *gs*) ; load1
my-counter)
(progn
(sys:ensure-loads-after-loads) ; ensure load order
(my-structure-value-slot *gs*)) ; load2
(.. something else ...))
Note that somehow both threads know about
counter
, and normally will have to synchronize the getting of its value too.