finish removing threads

This commit is contained in:
Simon Cruanes 2023-12-05 13:45:48 -05:00
parent bf2375f042
commit 33053a1f96
No known key found for this signature in database
GPG key ID: EBFFF6F283F3A2B4
10 changed files with 4 additions and 555 deletions

View file

@ -1,4 +1,4 @@
PACKAGES=containers,containers-data,containers-thread
PACKAGES=containers,containers-data
all: build test

View file

@ -39,13 +39,12 @@ Containers is:
data structures that don't have an equivalent in the standard library,
typically not as thoroughly maintained. This is now in its own package
since 3.0.
- A separate library for threaded programming in `containers-thread`,
including a blocking queue, semaphores, an extension of `Mutex`, and
thread-pool based futures. This is in its own package since 3.0.
Some of the modules have been moved to their own repository (e.g. `sequence` (now `iter`),
`gen`, `qcheck`) and are on opam for great fun and profit.
Containers-thread has been removed in favor of [Moonpool](https://github.com/c-cube/moonpool/).
## Migration Guide
### To 3.0
@ -54,7 +53,7 @@ The [changelog's breaking section](CHANGELOG.md) contains a list of the breaking
changes in this release.
1. The biggest change is that some sub-libraries have been either turned into
their own packages (`containers-thread`, `containers-data`),
their own packages (`containers-data`),
deleted (`containers.iter`),or merged elsewhere (`containers.sexp`).
This means that if use these libraries you will have to edit your
`dune`/`_oasis`/`opam` files.

View file

@ -1,6 +0,0 @@
(test
(name t)
(flags :standard -strict-sequence -warn-error -a+8)
(modes (best exe))
(package containers-thread)
(libraries containers containers-thread containers_testlib iter threads))

View file

@ -1,9 +0,0 @@
Containers_testlib.run_all ~descr:"containers-thread"
[
T_bq.Test.get ();
T_lock.Test.get ();
T_pool.Test.get ();
T_semaphore.Test.get ();
T_thread.Test.get ();
T_timer.Test.get ();
]

View file

@ -1,64 +0,0 @@
module Test = (val Containers_testlib.make ~__FILE__ ())
open Test
open CCBlockingQueue;;
t @@ fun () ->
let q = create 1 in
let t1 =
CCThread.spawn (fun () ->
push q 1;
push q 2)
in
let t2 =
CCThread.spawn (fun () ->
push q 3;
push q 4)
in
let l = CCLock.create [] in
let t3 =
CCThread.spawn (fun () ->
for _i = 1 to 4 do
let x = take q in
CCLock.update l (fun l -> x :: l)
done)
in
Thread.join t1;
Thread.join t2;
Thread.join t3;
assert_equal [ 1; 2; 3; 4 ] (List.sort Stdlib.compare (CCLock.get l));
true
;;
t @@ fun () ->
let n = 1000 in
let lists =
[|
CCList.(1 -- n); CCList.(n + 1 -- (2 * n)); CCList.((2 * n) + 1 -- (3 * n));
|]
in
let q = create 2 in
let senders =
CCThread.Arr.spawn 3 (fun i ->
if i = 1 then
push_list q lists.(i)
(* test push_list *)
else
List.iter (push q) lists.(i))
in
let res = CCLock.create [] in
let receivers =
CCThread.Arr.spawn 3 (fun i ->
if i = 1 then (
let l = take_list q n in
CCLock.update res (fun acc -> l @ acc)
) else
for _j = 1 to n do
let x = take q in
CCLock.update res (fun acc -> x :: acc)
done)
in
CCThread.Arr.join senders;
CCThread.Arr.join receivers;
let l = CCLock.get res |> List.sort Stdlib.compare in
assert_equal CCList.(1 -- (3 * n)) l;
true

View file

@ -1,101 +0,0 @@
module Test = (val Containers_testlib.make ~__FILE__ ())
open Test
open CCLock;;
t @@ fun () ->
let l = create 0 in
let try_incr l =
update l (fun x ->
Thread.yield ();
x + 1)
in
for _i = 1 to 10 do
ignore (Thread.create try_incr l)
done;
Thread.delay 0.10;
assert_equal 10 (get l);
true
;;
t @@ fun () ->
let l = create 0 in
let test_it l =
with_lock_as_ref l ~f:(fun r ->
(* increment and decrement *)
for j = 0 to 100 do
let x = LockRef.get r in
LockRef.set r (x + 10);
if j mod 5 = 0 then Thread.yield ();
let y = LockRef.get r in
LockRef.set r (y - 10)
done)
in
for _i = 1 to 100 do
ignore (Thread.create test_it l)
done;
Thread.delay 0.10;
0 = get l
;;
t @@ fun () ->
let l = create 5 in
update l (fun x -> x + 1);
get l = 6
;;
t @@ fun () ->
let l = create 5 in
update_map l (fun x -> x + 1, string_of_int x) = "5" && get l = 6
;;
t @@ fun () ->
let l = create 0 in
set l 4;
get l = 4
;;
t @@ fun () ->
let l = create 0 in
set l 4;
set l 5;
get l = 5
;;
t @@ fun () ->
let l = create 0 in
let a = Array.init 100 (fun _ -> Thread.create (fun _ -> incr l) ()) in
Array.iter Thread.join a;
assert_equal ~printer:CCInt.to_string 100 (get l);
true
;;
t @@ fun () ->
let l = create 0 in
incr l;
get l = 1
;;
t @@ fun () ->
let l = create 0 in
decr l;
get l = ~-1
;;
t @@ fun () ->
let l = create 0 in
1 = incr_then_get l && 1 = get l
;;
t @@ fun () ->
let l = create 0 in
0 = get_then_incr l && 1 = get l
;;
t @@ fun () ->
let l = create 10 in
9 = decr_then_get l && 9 = get l
;;
t @@ fun () ->
let l = create 10 in
10 = get_then_decr l && 9 = get l

View file

@ -1,220 +0,0 @@
module Test = (val Containers_testlib.make ~__FILE__ ())
open Test
open CCPool
module P = Make (struct
let max_size = 30
end)
module P2 = Make (struct
let max_size = 15
end)
module Fut = P.Fut
module Fut2 = P2.Fut;;
t @@ fun () ->
List.iter
(fun n ->
let l = Iter.(1 -- n) |> Iter.to_list in
let l =
List.rev_map
(fun _i ->
Fut.make (fun () ->
Thread.delay 0.01;
1))
l
in
let l' = List.map Fut.get l in
assert_equal n (List.fold_left ( + ) 0 l'))
[ 10; 300 ];
true
;;
t @@ fun () ->
List.iter
(fun n ->
let l = Iter.(1 -- n) |> Iter.to_list in
let l =
List.rev_map
(fun _i ->
Fut2.make (fun () ->
Thread.delay 0.01;
1))
l
in
let l' = List.map Fut2.get l in
assert_equal n (List.fold_left ( + ) 0 l'))
[ 10; 300 ];
true
;;
t @@ fun () ->
let a = Fut.make (fun () -> 1) in
let b = Fut.return 42 in
let c = Fut.monoid_product CCPair.make a b in
assert_equal (1, 42) (Fut.get c);
true
;;
t @@ fun () ->
let a = Fut.make (fun () -> 1) in
let b = Fut.make (fun () -> 42) in
let c = Fut.monoid_product CCPair.make a b in
assert_equal (1, 42) (Fut.get c);
true
;;
t @@ fun () ->
let a = Fut.make (fun () -> 1) in
let b = Fut.map succ @@ Fut.make (fun () -> 41) in
let c = Fut.monoid_product CCPair.make a b in
assert_equal (1, 42) (Fut.get c);
true
;;
eq [ 2; 3 ] (Fut.get @@ Fut.map_l (fun x -> Fut.return (x + 1)) [ 1; 2 ]);;
eq [] (Fut.get @@ Fut.map_l (fun x -> Fut.return (x + 1)) []);;
t @@ fun () ->
let l = CCList.(1 -- 50) in
let l' =
l
|> List.map (fun x ->
Fut.make (fun () ->
Thread.delay 0.1;
x * 10))
|> Fut.sequence_l
|> Fut.map (List.fold_left ( + ) 0)
in
let expected = List.fold_left (fun acc x -> acc + (10 * x)) 0 l in
assert_equal expected (Fut.get l');
true
;;
t @@ fun () ->
let l = CCList.(1 -- 100_000) in
let l' =
l
|> CCList.map (fun _x -> Fut.make (fun () -> 1))
|> Fut.sequence_l
|> Fut.map (List.fold_left ( + ) 0)
in
let expected = 100_000 in
assert_equal expected (Fut.get l');
true
;;
t @@ fun () ->
let l = CCList.(1 -- 50) in
let l' =
l
|> List.map (fun x ->
Fut.make (fun () ->
Thread.delay 0.1;
if x = 5 then raise Exit;
x))
|> Fut.sequence_l
|> Fut.map (List.fold_left ( + ) 0)
in
assert_raises (( = ) Exit) (fun () -> Fut.get l');
true
;;
t @@ fun () ->
let rec fib x =
if x < 2 then
1
else
fib (x - 1) + fib (x - 2)
in
let l =
CCList.(1 -- 10_000)
|> List.rev_map (fun x ->
Fut.make (fun () ->
Thread.yield ();
fib (x mod 20)))
|> Fut.(map_l (fun x -> x >|= fun x -> x + 1))
in
assert (Fut.state l = Waiting);
let l' = Fut.get l in
assert_equal 10_000 (List.length l');
true
;;
t @@ fun () ->
let l = CCList.(1 -- 50) in
let l' =
l
|> List.map (fun x ->
Fut2.make (fun () ->
Thread.delay 0.1;
x * 10))
|> Fut2.sequence_l
|> Fut2.map (List.fold_left ( + ) 0)
in
let expected = List.fold_left (fun acc x -> acc + (10 * x)) 0 l in
assert_equal expected (Fut2.get l');
true
;;
t @@ fun () ->
let l = CCList.(1 -- 50) in
let l' =
l
|> List.map (fun x ->
Fut2.make (fun () ->
Thread.delay 0.1;
if x = 5 then raise Exit;
x))
|> Fut2.sequence_l
|> Fut2.map (List.fold_left ( + ) 0)
in
assert_raises (( = ) Exit) (fun () -> Fut2.get l');
true
;;
t @@ fun () ->
let rec fib x =
if x < 2 then
1
else
fib (x - 1) + fib (x - 2)
in
let l =
CCList.(1 -- 10_000)
|> List.rev_map (fun x ->
Fut2.make (fun () ->
Thread.yield ();
fib (x mod 20)))
|> Fut2.(map_l (fun x -> x >|= fun x -> x + 1))
in
assert (Fut2.state l = Waiting);
let l' = Fut2.get l in
assert_equal 10_000 (List.length l');
true
;;
t @@ fun () ->
let start = Unix.gettimeofday () in
let pause = 0.2 and n = 10 in
let l =
CCList.(1 -- n) |> List.map (fun _ -> Fut.make (fun () -> Thread.delay pause))
in
List.iter Fut.get l;
let stop = Unix.gettimeofday () in
assert (stop -. start < float_of_int n *. pause);
true
;;
t @@ fun () ->
let start = Unix.gettimeofday () in
let pause = 0.2 and n = 10 in
let l =
CCList.(1 -- n)
|> List.map (fun _ -> Fut2.make (fun () -> Thread.delay pause))
in
List.iter Fut2.get l;
let stop = Unix.gettimeofday () in
assert (stop -. start < float_of_int n *. pause);
true

View file

@ -1,75 +0,0 @@
module Test = (val Containers_testlib.make ~__FILE__ ())
open Test
open CCSemaphore;;
t @@ fun () ->
let s = create 1 in
let r = CCLock.create false in
let _ =
Thread.create
(fun s ->
acquire 5 s;
CCLock.set r true)
s
in
Thread.yield ();
assert_equal false (CCLock.get r);
release 4 s;
Thread.delay 0.2;
assert_equal true (CCLock.get r);
assert_equal 0 (get s);
true
;;
t @@ fun () ->
let s = create 5 in
let n = CCLock.create 0 in
let a =
Array.init 100 (fun i ->
Thread.create
(fun _ ->
for _i = 1 to 100 do
with_acquire
~n:(1 + (i mod 5))
s
~f:(fun () ->
Thread.yield ();
CCLock.incr n)
done)
())
in
Array.iter Thread.join a;
assert_equal ~printer:CCInt.to_string 5 (get s);
assert_equal ~printer:CCInt.to_string 10_000 (CCLock.get n);
true
;;
t @@ fun () ->
let output _s = () in
let s = create 2 in
let res = CCLock.create false in
let id =
Thread.create
(fun () ->
output "start";
wait_until_at_least ~n:5 s ~f:(fun () ->
assert (get s >= 5);
output "modify now";
CCLock.set res true))
()
in
output "launched thread";
Thread.yield ();
assert (not (CCLock.get res));
output "release 2";
release 2 s;
Thread.yield ();
assert (not (CCLock.get res));
output "release 1";
release 1 s;
(* should work now *)
Thread.delay 0.2;
Thread.join id;
output "check";
assert (CCLock.get res);
true

View file

@ -1,32 +0,0 @@
module Test = (val Containers_testlib.make ~__FILE__ ())
open Test
open CCThread;;
t @@ fun () ->
let l = CCLock.create 0 in
let a = Arr.spawn 101 (fun i -> CCLock.update l (( + ) i)) in
Arr.join a;
let n = Iter.(1 -- 100 |> fold ( + ) 0) in
assert_equal ~printer:CCInt.to_string n (CCLock.get l);
true
;;
t @@ fun () ->
let b = Barrier.create () in
let res = CCLock.create 0 in
let t1 =
spawn (fun _ ->
Barrier.wait b;
CCLock.incr res)
and t2 =
spawn (fun _ ->
Barrier.wait b;
CCLock.incr res)
in
Thread.delay 0.2;
assert_equal 0 (CCLock.get res);
Barrier.activate b;
Thread.join t1;
Thread.join t2;
assert_equal 2 (CCLock.get res);
true

View file

@ -1,43 +0,0 @@
module Test = (val Containers_testlib.make ~__FILE__ ())
open Test
open CCTimer;;
(* NOTE: could be tighter bounds, but travis' mac OS seems to be dog slow. *)
t @@ fun () ->
let start = Unix.gettimeofday () in
let timer = create () in
let res = CCLock.create 0 in
let sem = CCSemaphore.create 1 in
CCSemaphore.acquire 1 sem;
let stop = ref 0. in
every timer 0.1 ~f:(fun () ->
if CCLock.incr_then_get res > 5 then (
stop := Unix.gettimeofday ();
CCSemaphore.release 1 sem;
raise ExitEvery
));
CCSemaphore.acquire 1 sem;
(* wait *)
assert_equal ~printer:CCInt.to_string 6 (CCLock.get res);
assert (!stop -. start >= 0.49999);
assert (!stop -. start < 2.);
true
;;
t @@ fun () ->
(* scenario: n := 1; n := n*4 ; n := n+2; res := n *)
let timer = create () in
let n = CCLock.create 1 in
let res = CCLock.create 0 in
after timer 0.3 ~f:(fun () -> CCLock.update n (fun x -> x + 2));
ignore
(Thread.create
(fun _ ->
Thread.delay 0.4;
CCLock.set res (CCLock.get n))
());
after timer 0.1 ~f:(fun () -> CCLock.update n (fun x -> x * 4));
Thread.delay 0.6;
assert_equal ~printer:Q.Print.int 6 (CCLock.get res);
true