Skip to content

Commit 9f12efe

Browse files
committed
Add Picos_aux_htbl.copy
1 parent 4dc9fbf commit 9f12efe

File tree

3 files changed

+62
-14
lines changed

3 files changed

+62
-14
lines changed

lib/picos_aux.htbl/picos_aux_htbl.ml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,43 @@ let rec try_dissoc : type v c r. (_, v) t -> _ -> c -> (v, c, r) op -> _ -> r =
554554

555555
(* *)
556556

557+
let rec copy t backoff =
558+
let r = get t in
559+
if try_resize t r (Atomic_array.length r.buckets) ~clear:false then begin
560+
(* We need to create and count a new size as the previous one is being used
561+
by the original. *)
562+
let non_linearizable_size =
563+
Array.init (Array.length r.non_linearizable_size) @@ fun _ ->
564+
Atomic.make_contended 0
565+
in
566+
let buckets = r.buckets in
567+
(* We need to copy the array, because other threads might actually still be
568+
trying to resize it. *)
569+
let buckets =
570+
Atomic_array.init (Atomic_array.length buckets) @@ fun i ->
571+
match Atomic_array.unsafe_fenceless_get buckets i with
572+
| B (Resize spine_r) ->
573+
let spine = spine_r.spine in
574+
let rec count n = function
575+
| Nil -> n
576+
| Cons r -> count (n + 1) r.rest
577+
in
578+
let _ : int =
579+
Atomic.fetch_and_add
580+
(Array.unsafe_get non_linearizable_size 0)
581+
(count 0 spine)
582+
in
583+
B spine
584+
| B (Nil | Cons _) ->
585+
(* After resize only [Resize] values should be left in the old
586+
buckets. *)
587+
assert false
588+
in
589+
let r = { r with non_linearizable_size; buckets } in
590+
Atomic.make r |> Multicore_magic.copy_as_padded
591+
end
592+
else copy t (Backoff.once backoff)
593+
557594
let rec snapshot t ~clear backoff =
558595
let r = get t in
559596
if try_resize t r (Atomic_array.length r.buckets) ~clear then begin
@@ -580,6 +617,7 @@ let rec snapshot t ~clear backoff =
580617
end
581618
else snapshot t ~clear (Backoff.once backoff)
582619

620+
let copy t = copy t Backoff.default
583621
let to_seq t = snapshot t ~clear:false Backoff.default
584622
let remove_all t = snapshot t ~clear:true Backoff.default
585623

lib/picos_aux.htbl/picos_aux_htbl.mli

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ val max_buckets_of : ('k, 'v) t -> int
5454
5555
ℹ️ The returned value may not be the same as given to {!create}. *)
5656

57+
val copy : ('k, 'v) t -> ('k, 'v) t
58+
(** [copy htbl] creates an independent copy of the hash table.
59+
60+
🐌 This is a linear time operation. *)
61+
5762
(** {2 Looking up bindings} *)
5863

5964
val find_exn : ('k, 'v) t -> 'k -> 'v

test/test_htbl.ml

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,25 @@ let () =
88
let t = Htbl.create () in
99
assert (Htbl.try_add t "Basics" 101);
1010
assert (Htbl.try_add t "Answer" 42);
11-
assert (Htbl.non_linearizable_length t = 2);
12-
assert (101 = Htbl.remove_exn t "Basics");
13-
assert (not (Htbl.try_remove t "Basics"));
14-
assert (Htbl.non_linearizable_length t = 1);
15-
assert (Htbl.remove_all t |> List.of_seq = [ ("Answer", 42) ]);
16-
assert (Htbl.non_linearizable_length t = 0);
17-
assert (Htbl.to_seq t |> List.of_seq = []);
18-
[ "One"; "Two"; "Three" ]
19-
|> List.iteri (fun v k -> assert (Htbl.try_add t k v));
20-
assert (Htbl.non_linearizable_length t = 3);
21-
assert (
22-
Htbl.to_seq t |> List.of_seq
23-
|> List.sort (fun l r -> String.compare (fst l) (fst r))
24-
= [ ("One", 0); ("Three", 2); ("Two", 1) ])
11+
let h = Htbl.copy t in
12+
let test t =
13+
assert (Htbl.non_linearizable_length t = 2);
14+
assert (101 = Htbl.remove_exn t "Basics");
15+
assert (not (Htbl.try_remove t "Basics"));
16+
assert (Htbl.non_linearizable_length t = 1);
17+
assert (Htbl.remove_all t |> List.of_seq = [ ("Answer", 42) ]);
18+
assert (Htbl.non_linearizable_length t = 0);
19+
assert (Htbl.to_seq t |> List.of_seq = []);
20+
[ "One"; "Two"; "Three" ]
21+
|> List.iteri (fun v k -> assert (Htbl.try_add t k v));
22+
assert (Htbl.non_linearizable_length t = 3);
23+
assert (
24+
Htbl.to_seq t |> List.of_seq
25+
|> List.sort (fun l r -> String.compare (fst l) (fst r))
26+
= [ ("One", 0); ("Three", 2); ("Two", 1) ])
27+
in
28+
test t;
29+
test h
2530

2631
module Int = struct
2732
include Int

0 commit comments

Comments
 (0)