1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162open!Baseopen!Portabletypet={capacity:int(** The number of domains which can [await] on the barrier *);passed:intAtomic.t(** The number of domains which have entered, but not exited [await] *);waiters:intAtomic.t(** The number of domains which are ready to exit a call to [await] *)}letcreatecapacity={capacity;passed=Atomic.make0;waiters=(* Between rounds, [waiters] is set to [capacity]; we reset it to [0] once all of
the domains have entered [await], and let it count back up to [capacity] before
the domains leave [await]. *)Atomic.makecapacity};;letcapacityt=t.capacity(* NOTE: we assume no more than [capacity] domains call [await] at a time. This is
documented in the mli file. *)letawait{waiters;capacity;passed}=(* Awaiting a barrier progresses in two stages:
1. Each waiter increments [passed], until the last waiter increments it to equal
[capacity]
2. Each waiter synchronizes the exit of [await] using a semaphore on [waiters], by
each incrementing it in lockstep until [waiters] is equal to [capacity]
This ensures that we only return from [await] once [capacity] waiters have called
[await], as simultaneously as possible.
*)ifAtomic.fetch_and_addpassed1=capacity-1then((* If [capacity] waiters have called [await] on the barrier, reset [passed] to 0, and
reset [waiters] to prepare for synchronizing the return from [await] *)Atomic.setpassed0;Atomic.setwaiters0)else(* Otherwise, if we were /not/ the [capacity]th waiter, wait for the [capacity]th
waiter to reset the barrier. *)whileAtomic.getwaiters=capacitydoBasement.Stdlib_shim.Domain.cpu_relax()done;(* Finally, each waiter increments [waiters] up to [capacity] *)Atomic.incrwaiters;whileAtomic.getwaiters<capacitydoBasement.Stdlib_shim.Domain.cpu_relax()(* When each domain now exits from [await], we're back to the initial state setup by
[create]:
- [passed] is 0
- [waiters] is equal to [capacity]
*)done;;