Skip to content

Commit 6f0e3a9

Browse files
authored
Merge pull request #43 from jmid/add-stm-wsdeque-tests
Add STM ws_deque tests from multicoretests repo
2 parents e164bfa + 45fbfc1 commit 6f0e3a9

File tree

3 files changed

+126
-0
lines changed

3 files changed

+126
-0
lines changed

lockfree.opam

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ depends: [
1313
"dune" {>= "3.0"}
1414
"domain_shims" {>= "0.1.0"}
1515
"qcheck" {with-test & >= "0.18.1"}
16+
"qcheck-stm" {with-test & >= "0.1"}
1617
"qcheck-alcotest" {with-test & >= "0.18.1"}
1718
"alcotest" {>= "1.6.0"}
1819
"yojson" {>= "2.0.2"}

test/ws_deque/dune

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,10 @@
1818
(name qcheck_ws_deque)
1919
(libraries lockfree qcheck "qcheck-alcotest")
2020
(modules qcheck_ws_deque))
21+
22+
(test
23+
(name stm_ws_deque)
24+
(modules stm_ws_deque)
25+
(libraries lockfree qcheck-stm.sequential qcheck-stm.domain)
26+
(action
27+
(run %{test} --verbose)))

test/ws_deque/stm_ws_deque.ml

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
(** Sequential and Parallel model-based tests of ws_deque *)
2+
3+
open QCheck
4+
open STM
5+
open Util
6+
module Ws_deque = Lockfree.Ws_deque
7+
8+
module WSDConf = struct
9+
type cmd = Push of int | Pop | Steal
10+
11+
let show_cmd c =
12+
match c with
13+
| Push i -> "Push " ^ string_of_int i
14+
| Pop -> "Pop"
15+
| Steal -> "Steal"
16+
17+
type state = int list
18+
type sut = int Ws_deque.M.t
19+
20+
let arb_cmd _s =
21+
let int_gen = Gen.nat in
22+
QCheck.make ~print:show_cmd
23+
(Gen.oneof
24+
[
25+
Gen.map (fun i -> Push i) int_gen;
26+
Gen.return Pop;
27+
(*Gen.return Steal;*)
28+
(* No point in stealing from yourself :-D *)
29+
])
30+
31+
let stealer_cmd _s = QCheck.make ~print:show_cmd (Gen.return Steal)
32+
let init_state = []
33+
let init_sut () = Ws_deque.M.create ()
34+
let cleanup _ = ()
35+
36+
let next_state c s =
37+
match c with
38+
| Push i ->
39+
i :: s
40+
(*if i<>1213 then i::s else s*)
41+
(* an artificial fault *)
42+
| Pop -> ( match s with [] -> s | _ :: s' -> s')
43+
| Steal -> ( match List.rev s with [] -> s | _ :: s' -> List.rev s')
44+
45+
let precond _ _ = true
46+
47+
let run c d =
48+
match c with
49+
| Push i -> Res (unit, Ws_deque.M.push d i)
50+
| Pop -> Res (result int exn, protect Ws_deque.M.pop d)
51+
| Steal -> Res (result int exn, protect Ws_deque.M.steal d)
52+
53+
let postcond c (s : state) res =
54+
match (c, res) with
55+
| Push _, Res ((Unit, _), _) -> true
56+
| Pop, Res ((Result (Int, Exn), _), res) -> (
57+
match s with [] -> res = Error Exit | j :: _ -> res = Ok j)
58+
| Steal, Res ((Result (Int, Exn), _), res) -> (
59+
match List.rev s with [] -> Result.is_error res | j :: _ -> res = Ok j)
60+
| _, _ -> false
61+
end
62+
63+
module WSDT_seq = STM_sequential.Make (WSDConf)
64+
module WSDT_dom = STM_domain.Make (WSDConf)
65+
66+
(* The following definitions differ slightly from those in multicoretests:lib/STM.ml.
67+
This has to do with how work-stealing deques are supposed to be used according to spec:
68+
- [agree_prop_par] differs in that it only spawns one domain ("a stealer domain")
69+
in parallel with the original "owner domain" (it also uses [Semaphore.Binary]) *)
70+
let agree_prop_par (seq_pref, owner, stealer) =
71+
assume (WSDT_seq.cmds_ok WSDConf.init_state (seq_pref @ owner));
72+
assume (WSDT_seq.cmds_ok WSDConf.init_state (seq_pref @ stealer));
73+
let sut = WSDConf.init_sut () in
74+
let pref_obs = WSDT_dom.interp_sut_res sut seq_pref in
75+
let sema = Semaphore.Binary.make false in
76+
let stealer_dom =
77+
Domain.spawn (fun () ->
78+
Semaphore.Binary.release sema;
79+
WSDT_dom.interp_sut_res sut stealer)
80+
in
81+
while not (Semaphore.Binary.try_acquire sema) do
82+
Domain.cpu_relax ()
83+
done;
84+
let own_obs = WSDT_dom.interp_sut_res sut owner in
85+
let stealer_obs = Domain.join stealer_dom in
86+
let res =
87+
WSDT_dom.check_obs pref_obs own_obs stealer_obs WSDConf.init_state
88+
in
89+
let () = WSDConf.cleanup sut in
90+
res
91+
|| Test.fail_reportf " Results incompatible with linearized model:\n\n%s"
92+
@@ Util.print_triple_vertical ~center_prefix:false STM.show_res
93+
(List.map snd pref_obs, List.map snd own_obs, List.map snd stealer_obs)
94+
95+
(* [arb_cmds_par] differs in what each triple component generates:
96+
"Owner domain" cmds can't be [Steal], "stealer domain" cmds can only be [Steal]. *)
97+
let arb_cmds_par =
98+
WSDT_dom.arb_triple 20 15 WSDConf.arb_cmd WSDConf.arb_cmd WSDConf.stealer_cmd
99+
100+
(* A parallel agreement test - w/repeat and retries combined *)
101+
let agree_test_par ~count ~name =
102+
let rep_count = 50 in
103+
Test.make ~retries:10 ~count ~name arb_cmds_par
104+
(repeat rep_count agree_prop_par)
105+
106+
(* Note: this can generate, e.g., pop commands/actions in different threads, thus violating the spec. *)
107+
let agree_test_par_negative ~count ~name =
108+
WSDT_dom.neg_agree_test_par ~count ~name
109+
110+
let () =
111+
let count = 1000 in
112+
QCheck_base_runner.run_tests_main
113+
[
114+
WSDT_seq.agree_test ~count ~name:"STM Lockfree.Ws_deque test sequential";
115+
agree_test_par ~count ~name:"STM Lockfree.Ws_deque test parallel";
116+
agree_test_par_negative ~count
117+
~name:"STM Lockfree.Ws_deque test parallel, negative";
118+
]

0 commit comments

Comments
 (0)