Skip to content

Commit a5b15c1

Browse files
committed
Use a GADT to express desired result type
This avoids unnecessarily raising and catching an exception in case an option result. The cost is a sigle cheap conditional.
1 parent a648ffd commit a5b15c1

File tree

1 file changed

+35
-23
lines changed

1 file changed

+35
-23
lines changed

src_lockfree/ws_deque.ml

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -125,64 +125,76 @@ module M : S = struct
125125
let a = Atomic.get q.tab in
126126
let size = b - t in
127127
let a =
128-
if size = CArray.size a then (
128+
if size = CArray.size a then begin
129129
grow q t b;
130-
Atomic.get q.tab)
130+
Atomic.get q.tab
131+
end
131132
else a
132133
in
133134
CArray.put a b v';
134135
Atomic.set q.bottom (b + 1)
135136

136-
let[@inline] release ptr =
137+
type ('a, _) poly = Option : ('a, 'a option) poly | Value : ('a, 'a) poly
138+
139+
let[@inline] release : type a r. a ref -> (a, r) poly -> r =
140+
fun ptr poly ->
137141
let res = !ptr in
138142
(* we know this ptr will never be dereferenced, but want to
139143
break the reference to ensure that the contents of the
140144
deque array get garbage collected *)
141145
ptr := Obj.magic ();
142-
res
146+
match poly with Option -> Some res | Value -> res
143147

144-
let pop q =
145-
if size q = 0 then raise Exit
148+
let pop_as : type a r. a t -> (a, r) poly -> r =
149+
fun q poly ->
150+
if size q = 0 then match poly with Option -> None | Value -> raise Exit
146151
else
147152
let b = Atomic.get q.bottom - 1 in
148153
Atomic.set q.bottom b;
149154
let t = Atomic.get q.top in
150155
let a = Atomic.get q.tab in
151156
let size = b - t in
152-
if size < 0 then (
157+
if size < 0 then begin
153158
(* empty queue *)
154159
Atomic.set q.bottom (b + 1);
155-
raise Exit)
160+
match poly with Option -> None | Value -> raise Exit
161+
end
156162
else
157163
let out = CArray.get a b in
158164
if b = t then
159165
(* single last element *)
160-
if Atomic.compare_and_set q.top t (t + 1) then (
166+
if Atomic.compare_and_set q.top t (t + 1) then begin
161167
Atomic.set q.bottom (b + 1);
162-
release out)
163-
else (
168+
release out poly
169+
end
170+
else begin
164171
Atomic.set q.bottom (b + 1);
165-
raise Exit)
166-
else (
172+
match poly with Option -> None | Value -> raise Exit
173+
end
174+
else begin
167175
(* non-empty queue *)
168-
if q.next_shrink > size then (
176+
if q.next_shrink > size then begin
169177
Atomic.set q.tab (CArray.shrink a t b);
170-
set_next_shrink q);
171-
release out)
178+
set_next_shrink q
179+
end;
180+
release out poly
181+
end
172182

173-
let pop_opt q = try Some (pop q) with Exit -> None
183+
let pop q = pop_as q Value
184+
let pop_opt q = pop_as q Option
174185

175-
let rec steal backoff q =
186+
let rec steal_as : type a r. a t -> Backoff.t -> (a, r) poly -> r =
187+
fun q backoff poly ->
176188
let t = Atomic.get q.top in
177189
let b = Atomic.get q.bottom in
178190
let size = b - t in
179-
if size <= 0 then raise Exit
191+
if size <= 0 then match poly with Option -> None | Value -> raise Exit
180192
else
181193
let a = Atomic.get q.tab in
182194
let out = CArray.get a t in
183-
if Atomic.compare_and_set q.top t (t + 1) then release out
184-
else steal (Backoff.once backoff) q
195+
if Atomic.compare_and_set q.top t (t + 1) then release out poly
196+
else steal_as q (Backoff.once backoff) poly
185197

186-
let steal q = steal Backoff.default q
187-
let steal_opt q = try Some (steal q) with Exit -> None
198+
let steal q = steal_as q Backoff.default Value
199+
let steal_opt q = steal_as q Backoff.default Option
188200
end

0 commit comments

Comments
 (0)