Skip to content

Commit 8ae5409

Browse files
committed
Use GADT to avoid closures
1 parent fc24f99 commit 8ae5409

File tree

6 files changed

+112
-87
lines changed

6 files changed

+112
-87
lines changed

src/kcas/kcas.ml

+96-58
Original file line numberDiff line numberDiff line change
@@ -671,97 +671,135 @@ module Xt = struct
671671
validate_one which node_r.loc node_r.state;
672672
validate_all_rec which node_r.gt
673673

674-
let[@inline] maybe_validate_log (Xt xt_r as xt : _ t) =
675-
let c0 = xt_r.validate_counter in
676-
let c1 = c0 + 1 in
677-
xt_r.validate_counter <- c1;
678-
(* Validate whenever counter reaches next power of 2. *)
679-
if c0 land c1 = 0 then begin
680-
Timeout.check xt_r.timeout;
681-
validate_all_rec xt !(tree_as_ref xt)
682-
end
683-
684674
let[@inline] is_obstruction_free (Xt xt_r : _ t) loc =
685675
(* Fenceless is safe as we are accessing a private location. *)
686676
xt_r.mode == `Obstruction_free && 0 <= loc.id
687677

688-
let[@inline] update_new loc f xt lt gt =
689-
(* Fenceless is safe inside transactions as each log update has a fence. *)
678+
type (_, _) up =
679+
| Compare_and_swap : ('a * 'a, 'a) up
680+
| Fetch_and_add : (int, int) up
681+
| Fn : ('a -> 'a, 'a) up
682+
| Exchange : ('a, 'a) up
683+
| Get : (unit, 'a) up
684+
685+
let update_new : type c a. _ -> a loc -> c -> (c, a) up -> _ -> _ -> a =
686+
fun xt loc c up lt gt ->
690687
let state = fenceless_get (as_atomic loc) in
691688
let before = eval state in
692-
match f before with
693-
| after ->
694-
let state =
695-
if before == after && is_obstruction_free xt loc then state
696-
else { before; after; which = W xt; awaiters = [] }
697-
in
698-
tree_as_ref xt := T (Node { loc; state; lt; gt; awaiters = [] });
699-
before
700-
| exception exn ->
701-
tree_as_ref xt := T (Node { loc; state; lt; gt; awaiters = [] });
702-
raise exn
689+
let after : a =
690+
match up with
691+
| Compare_and_swap -> if fst c == before then snd c else before
692+
| Fetch_and_add -> before + c
693+
| Fn -> begin
694+
let rot = !(tree_as_ref xt) in
695+
match c before with
696+
| after ->
697+
assert (rot == !(tree_as_ref xt));
698+
after
699+
| exception exn ->
700+
assert (rot == !(tree_as_ref xt));
701+
tree_as_ref xt := T (Node { loc; state; lt; gt; awaiters = [] });
702+
raise exn
703+
end
704+
| Exchange -> c
705+
| Get -> before
706+
in
707+
let state =
708+
if before == after && is_obstruction_free xt loc then state
709+
else { before; after; which = W xt; awaiters = [] }
710+
in
711+
tree_as_ref xt := T (Node { loc; state; lt; gt; awaiters = [] });
712+
before
713+
714+
let update_old : type c a. _ -> a loc -> c -> (c, a) up -> _ -> _ -> _ -> a =
715+
fun (Xt xt_r as xt : _ t) loc c up lt gt state' ->
716+
let c0 = xt_r.validate_counter in
717+
let c1 = c0 + 1 in
718+
xt_r.validate_counter <- c1;
719+
(* Validate whenever counter reaches next power of 2.
703720
704-
let[@inline] update_top loc f xt state' lt gt =
705-
let state = Obj.magic state' in
721+
The assumption is that potentially infinite loops will repeatedly access
722+
the same locations. *)
723+
if c0 land c1 = 0 then begin
724+
Timeout.check xt_r.timeout;
725+
validate_all_rec xt !(tree_as_ref xt)
726+
end;
727+
let state : a state = Obj.magic state' in
706728
if is_cmp xt state then begin
707-
let before = eval state in
708-
let after = f before in
729+
let current = eval state in
730+
let after : a =
731+
match up with
732+
| Compare_and_swap -> if fst c == current then snd c else current
733+
| Fetch_and_add -> current + c
734+
| Fn ->
735+
let rot = !(tree_as_ref xt) in
736+
let after = c current in
737+
assert (rot == !(tree_as_ref xt));
738+
after
739+
| Exchange -> c
740+
| Get -> current
741+
in
709742
let state =
710-
if before == after then state
711-
else { before; after; which = W xt; awaiters = [] }
743+
if current == after then state
744+
else { before = current; after; which = W xt; awaiters = [] }
712745
in
713746
tree_as_ref xt := T (Node { loc; state; lt; gt; awaiters = [] });
714-
before
747+
current
715748
end
716749
else
717750
let current = state.after in
718-
let state = { state with after = f current } in
751+
let after : a =
752+
match up with
753+
| Compare_and_swap -> if fst c == current then snd c else current
754+
| Fetch_and_add -> current + c
755+
| Fn ->
756+
let rot = !(tree_as_ref xt) in
757+
let after = c current in
758+
assert (rot == !(tree_as_ref xt));
759+
after
760+
| Exchange -> c
761+
| Get -> current
762+
in
763+
let state =
764+
if current == after then state
765+
else { before = state.before; after; which = W xt; awaiters = [] }
766+
in
719767
tree_as_ref xt := T (Node { loc; state; lt; gt; awaiters = [] });
720768
current
721769

722-
let[@inline] unsafe_update ~xt loc f =
770+
let update_as ~xt loc c up =
723771
let loc = Loc.to_loc loc in
724-
maybe_validate_log xt;
725772
let x = loc.id in
726773
match !(tree_as_ref xt) with
727-
| T Leaf -> update_new loc f xt (T Leaf) (T Leaf)
774+
| T Leaf -> update_new xt loc c up (T Leaf) (T Leaf)
728775
| T (Node { loc = a; lt = T Leaf; _ }) as tree when x < a.id ->
729-
update_new loc f xt (T Leaf) tree
776+
update_new xt loc c up (T Leaf) tree
730777
| T (Node { loc = a; gt = T Leaf; _ }) as tree when a.id < x ->
731-
update_new loc f xt tree (T Leaf)
778+
update_new xt loc c up tree (T Leaf)
732779
| T (Node { loc = a; state; lt; gt; _ }) when Obj.magic a == loc ->
733-
update_top loc f xt state lt gt
780+
update_old xt loc c up lt gt state
734781
| tree -> begin
735782
match splay ~hit_parent:false x tree with
736-
| l, T Leaf, r -> update_new loc f xt l r
737-
| l, T (Node node_r), r -> update_top loc f xt node_r.state l r
783+
| l, T Leaf, r -> update_new xt loc c up l r
784+
| l, T (Node node_r), r -> update_old xt loc c up l r node_r.state
738785
end
739786

740-
let[@inline] protect xt f x =
741-
let tree = !(tree_as_ref xt) in
742-
let y = f x in
743-
assert (!(tree_as_ref xt) == tree);
744-
y
745-
746-
let get ~xt loc = unsafe_update ~xt loc Fun.id
747-
let set ~xt loc after = unsafe_update ~xt loc (fun _ -> after) |> ignore
748-
let modify ~xt loc f = unsafe_update ~xt loc (protect xt f) |> ignore
787+
let get ~xt loc = update_as ~xt loc () Get
788+
let set ~xt loc after = update_as ~xt loc after Exchange |> ignore
789+
let modify ~xt loc f = update_as ~xt loc f Fn |> ignore
749790

750791
let compare_and_swap ~xt loc before after =
751-
unsafe_update ~xt loc (fun actual ->
752-
if actual == before then after else actual)
792+
update_as ~xt loc (before, after) Compare_and_swap
753793

754794
let compare_and_set ~xt loc before after =
755795
compare_and_swap ~xt loc before after == before
756796

757-
let exchange ~xt loc after = unsafe_update ~xt loc (fun _ -> after)
758-
let fetch_and_add ~xt loc n = unsafe_update ~xt loc (( + ) n)
759-
let incr ~xt loc = unsafe_update ~xt loc inc |> ignore
760-
let decr ~xt loc = unsafe_update ~xt loc dec |> ignore
761-
let update ~xt loc f = unsafe_update ~xt loc (protect xt f)
797+
let exchange ~xt loc after = update_as ~xt loc after Exchange
798+
let fetch_and_add ~xt loc n = update_as ~xt loc n Fetch_and_add
799+
let incr ~xt loc = update_as ~xt loc 1 Fetch_and_add |> ignore
800+
let decr ~xt loc = update_as ~xt loc (-1) Fetch_and_add |> ignore
801+
let update ~xt loc f = update_as ~xt loc f Fn
762802
let swap ~xt l1 l2 = set ~xt l1 @@ exchange ~xt l2 @@ get ~xt l1
763-
let unsafe_modify ~xt loc f = unsafe_update ~xt loc f |> ignore
764-
let unsafe_update ~xt loc f = unsafe_update ~xt loc f
765803

766804
let[@inline] to_blocking ~xt tx =
767805
match tx ~xt with None -> Retry.later () | Some value -> value
@@ -889,7 +927,7 @@ module Xt = struct
889927
else stop
890928
| T (Node _) as stop -> stop
891929

892-
let initial_validate_period = 16
930+
let initial_validate_period = 4
893931

894932
let success (Xt xt_r : _ t) result =
895933
Timeout.cancel xt_r.timeout;

src/kcas/kcas.mli

+5-14
Original file line numberDiff line numberDiff line change
@@ -415,10 +415,11 @@ module Xt : sig
415415
416416
To mitigate potential issues due to this read skew anomaly and due to very
417417
long running transactions, all of the access recording operations in this
418-
section periodically validate the entire transaction log. An important
419-
guideline for writing transactions is that loops inside a transaction
420-
should always include an access of some shared memory location through the
421-
transaction log or should otherwise be guaranteed to be bounded. *)
418+
section periodically validate the entire transaction log when a previously
419+
accessed location is accessed again. An important guideline for writing
420+
transactions is that loops inside a transaction should always include an
421+
access of some shared memory location through the transaction log or
422+
should otherwise be guaranteed to be bounded. *)
422423

423424
val get : xt:'x t -> 'a Loc.t -> 'a
424425
(** [get ~xt r] returns the current value of the shared memory location [r] in
@@ -558,14 +559,4 @@ module Xt : sig
558559
The default {{!Mode.t} [mode]} for [commit] is [`Obstruction_free].
559560
However, after enough attempts have failed during the verification step,
560561
[commit] switches to [`Lock_free]. *)
561-
562-
(**/**)
563-
564-
val unsafe_modify : xt:'x t -> 'a Loc.t -> ('a -> 'a) -> unit
565-
(** [unsafe_modify ~xt r f] is equivalent to [modify ~xt r f], but does not
566-
assert against misuse. *)
567-
568-
val unsafe_update : xt:'x t -> 'a Loc.t -> ('a -> 'a) -> 'a
569-
(** [unsafe_update ~xt r f] is equivalent to [update ~xt r f], but does not
570-
assert against misuse. *)
571562
end

src/kcas_data/hashtbl.ml

+4-4
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ module Xt = struct
219219
Array.unsafe_get old_buckets i
220220
|> Xt.get ~xt
221221
|> Assoc.iter_rev @@ fun k v ->
222-
Xt.unsafe_modify ~xt
222+
Xt.modify ~xt
223223
(Array.unsafe_get new_buckets (hash k land mask))
224224
(Assoc.cons k v)
225225
done
@@ -337,7 +337,7 @@ module Xt = struct
337337
let buckets = r.buckets in
338338
let mask = Array.length buckets - 1 in
339339
let bucket = Array.unsafe_get buckets (r.hash k land mask) in
340-
match Xt.unsafe_modify ~xt bucket (Assoc.remove r.equal k) with
340+
match Xt.modify ~xt bucket (Assoc.remove r.equal k) with
341341
| () ->
342342
Accumulator.Xt.decr ~xt r.length;
343343
if r.min_buckets <= mask && Random.bits () land mask = 0 then
@@ -353,7 +353,7 @@ module Xt = struct
353353
let buckets = r.buckets in
354354
let mask = Array.length buckets - 1 in
355355
let bucket = Array.unsafe_get buckets (r.hash k land mask) in
356-
Xt.unsafe_modify ~xt bucket (Assoc.cons k v);
356+
Xt.modify ~xt bucket (Assoc.cons k v);
357357
Accumulator.Xt.incr ~xt r.length;
358358
if mask + 1 < r.max_buckets && Random.bits () land mask = 0 then
359359
let capacity = mask + 1 in
@@ -367,7 +367,7 @@ module Xt = struct
367367
let mask = Array.length buckets - 1 in
368368
let bucket = Array.unsafe_get buckets (r.hash k land mask) in
369369
let change = ref Assoc.Nop in
370-
Xt.unsafe_modify ~xt bucket (fun kvs ->
370+
Xt.modify ~xt bucket (fun kvs ->
371371
let kvs' = Assoc.replace r.equal change k v kvs in
372372
if !change != Assoc.Nop then kvs' else kvs);
373373
if !change == Assoc.Added then begin

src/kcas_data/mvar.ml

+2-3
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,13 @@ module Xt = struct
1111
Magic_option.is_none
1212
(Xt.compare_and_swap ~xt mv Magic_option.none (Magic_option.some value))
1313

14-
let put ~xt mv value =
15-
Xt.unsafe_modify ~xt mv (Magic_option.put_or_retry value)
14+
let put ~xt mv value = Xt.modify ~xt mv (Magic_option.put_or_retry value)
1615

1716
let take_opt ~xt mv =
1817
Magic_option.to_option (Xt.exchange ~xt mv Magic_option.none)
1918

2019
let take ~xt mv =
21-
Magic_option.get_unsafe (Xt.unsafe_update ~xt mv Magic_option.take_or_retry)
20+
Magic_option.get_unsafe (Xt.update ~xt mv Magic_option.take_or_retry)
2221

2322
let peek ~xt mv = Magic_option.get_or_retry (Xt.get ~xt mv)
2423
let peek_opt ~xt mv = Magic_option.to_option (Xt.get ~xt mv)

src/kcas_data/queue.ml

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ module Xt = struct
3434
+ Elems.length (Xt.get ~xt middle)
3535
+ Elems.length (Xt.get ~xt back)
3636

37-
let add ~xt x q = Xt.unsafe_modify ~xt q.back @@ Elems.cons x
37+
let add ~xt x q = Xt.modify ~xt q.back @@ Elems.cons x
3838
let push = add
3939

4040
(** Cooperative helper to move elems from back to middle. *)
@@ -53,7 +53,7 @@ module Xt = struct
5353

5454
let take_opt ~xt t =
5555
let front = t.front in
56-
let elems = Xt.unsafe_update ~xt front Elems.tl_safe in
56+
let elems = Xt.update ~xt front Elems.tl_safe in
5757
if elems != Elems.empty then Elems.hd_opt elems
5858
else
5959
let middle = t.middle and back = t.back in

src/kcas_data/stack.ml

+3-6
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,10 @@ let of_seq xs = Loc.make ~padded:true (Elems.of_seq_rev xs)
99
module Xt = struct
1010
let length ~xt s = Xt.get ~xt s |> Elems.length
1111
let is_empty ~xt s = Xt.get ~xt s == Elems.empty
12-
let push ~xt x s = Xt.unsafe_modify ~xt s @@ Elems.cons x
13-
let pop_opt ~xt s = Xt.unsafe_update ~xt s Elems.tl_safe |> Elems.hd_opt
12+
let push ~xt x s = Xt.modify ~xt s @@ Elems.cons x
13+
let pop_opt ~xt s = Xt.update ~xt s Elems.tl_safe |> Elems.hd_opt
1414
let pop_all ~xt s = Elems.to_seq @@ Xt.exchange ~xt s Elems.empty
15-
16-
let pop_blocking ~xt s =
17-
Xt.unsafe_update ~xt s Elems.tl_safe |> Elems.hd_or_retry
18-
15+
let pop_blocking ~xt s = Xt.update ~xt s Elems.tl_safe |> Elems.hd_or_retry
1916
let top_opt ~xt s = Xt.get ~xt s |> Elems.hd_opt
2017
let top_blocking ~xt s = Xt.get ~xt s |> Elems.hd_or_retry
2118
let clear ~xt s = Xt.set ~xt s Elems.empty

0 commit comments

Comments
 (0)