1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
open! Base
open! Import
module State : sig @@ portable
type t : immediate
val max_parties : int
val from_parties : int -> t
val decr_awaiting : t Awaitable.t @ local -> t
val is_awaiting_one_or_more : t -> bool
val is_awaiting_none : t -> bool
val is_poisoned : t -> bool
val parties : t -> int
module Sense : sig
type t : immediate
val equal : t -> t -> bool
end
(** [sense_of t] masks out all the bits of [t] except the sense bit. The sense bit is a
flip-flop changed whenever threads awaiting on the barrier need to wake up. *)
val sense_of : t -> Sense.t
val and_decr_awaiting : t -> t
val and_incr_awaiting : t -> t
val and_poison : t -> t
val and_reset : t -> t
end = struct
type t = int
let sense_bit = 1
let parties_shift = 1
let parties_bits = (Int.num_bits - 2) / 2
let max_parties = (1 lsl parties_bits) - 1
let parties_mask = max_parties lsl parties_shift
let awaiting_shift = parties_shift + parties_bits
let awaiting_one = 1 lsl awaiting_shift
let poisoned_bit = Int.min_value
let[@inline] is_poisoned state = state <= 0
let[@inline] is_awaiting_one_or_more state =
awaiting_one < state
;;
let[@inline] is_awaiting_none state =
state < awaiting_one
;;
module Sense = struct
type t = int
let equal = ( = )
end
let[@inline] sense_of state = state land sense_bit
let[@inline] parties state = (state land parties_mask) lsr parties_shift
let[@inline] from_parties parties =
(parties lsl parties_shift)
lor (
parties lsl awaiting_shift)
;;
let[@inline] decr_awaiting t = Awaitable.fetch_and_add t (-awaiting_one)
let[@inline] and_decr_awaiting state = state - awaiting_one
let[@inline] and_incr_awaiting state = state + awaiting_one
let[@inline] and_poison state =
let after_sense = sense_of (lnot state) in
let parties_shifted = state land parties_mask in
let awaiting_shifted =
max_parties lsl awaiting_shift
in
after_sense lor parties_shifted lor awaiting_shifted lor poisoned_bit
;;
let[@inline] and_reset state =
let after_sense = sense_of (lnot state) in
let parties_shifted = state land parties_mask in
let awaiting_shifted = parties_shifted lsl (awaiting_shift - parties_shift) in
after_sense lor parties_shifted lor awaiting_shifted
;;
end
type t = State.t Awaitable.t
let max_parties = State.max_parties
let create ?padded parties =
if 0 < parties && parties <= max_parties
then Awaitable.make ?padded (State.from_parties parties)
else invalid_arg "Barrier.create: invalid number of parties"
;;
let[@inline never] rec poison t =
let before = Awaitable.get t in
let after = before |> State.and_poison in
if not (phys_equal before after)
then (
match Awaitable.compare_and_set t ~if_phys_equal_to:before ~replace_with:after with
| Set_here -> Awaitable.broadcast t
| Compare_failed -> poison t)
;;
type ('a, _) result =
| Value : ('a, 'a) result
| Canceled : ('a, 'a Or_canceled.t) result
let await_as (type r) ~awt ~ct t (r : (unit, r) result) : r =
let[@inline] completed () : r =
match r with
| Value -> ()
| Canceled -> Completed ()
in
let before_decr = State.decr_awaiting t in
if State.is_awaiting_one_or_more before_decr
then (
let after_decr = before_decr |> State.and_decr_awaiting in
if State.is_awaiting_none after_decr
then (
let after_decr_and_reset = after_decr |> State.and_reset in
match
Awaitable.compare_and_set
t
~if_phys_equal_to:after_decr
~replace_with:after_decr_and_reset
with
| Set_here ->
Awaitable.broadcast t;
completed ()
| Compare_failed ->
poison t;
raise Poisoned)
else (
let sense_before = State.sense_of after_decr in
let rec await_others_to_arrive ~awt ~ct t state_before : r =
if State.Sense.equal (State.sense_of state_before) sense_before
then (
match r with
| Value ->
(match Awaitable.await awt t ~until_phys_unequal_to:state_before with
| Signaled -> await_others_to_arrive ~awt ~ct t (Awaitable.get t)
| Terminated ->
poison t;
raise Await.Terminated)
| Canceled ->
(match
Awaitable.await_or_cancel awt ct t ~until_phys_unequal_to:state_before
with
| Signaled -> await_others_to_arrive ~awt ~ct t (Awaitable.get t)
| Canceled ->
let rec try_to_cancel t =
let before = Awaitable.get t in
if State.Sense.equal (State.sense_of before) sense_before
&& State.is_awaiting_one_or_more before
then (
let after = before |> State.and_incr_awaiting in
match
Awaitable.compare_and_set
t
~if_phys_equal_to:before
~replace_with:after
with
| Set_here -> (Canceled : r)
| Compare_failed -> try_to_cancel t)
else if not (State.is_poisoned before)
then Completed ()
else raise Poisoned
in
try_to_cancel t
| Terminated ->
poison t;
raise Await.Terminated))
else if not (State.is_poisoned state_before)
then completed ()
else raise Poisoned
in
await_others_to_arrive ~awt ~ct t after_decr))
else (
poison t;
raise Poisoned)
;;
let await awt t = await_as ~awt ~ct:Cancellation.never t Value
let await_or_cancel awt ct t = await_as ~awt ~ct t Canceled
let parties t = State.parties (Awaitable.get t)