Source file ortac_runtime_qcheck_stm_domain.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
open Ortac_runtime_qcheck_stm_util
open STM
include Ortac_runtime
module Report = Report
module Model = Stores.Model
module SUT = Stores.SUT
module type SpecExtOrtac = sig
include SpecExt
val arb_cmd_seq : state -> cmd QCheck.arbitrary
val arb_cmd_dom0 : state -> cmd QCheck.arbitrary
val arb_cmd_dom1 : state -> cmd QCheck.arbitrary
end
module type SpecOrtac = sig
include Spec
val arb_cmd_seq : state -> cmd QCheck.arbitrary
val arb_cmd_dom0 : state -> cmd QCheck.arbitrary
val arb_cmd_dom1 : state -> cmd QCheck.arbitrary
end
module MakeExt (Spec : SpecExtOrtac) = struct
open Util
open QCheck
open Report
open Internal.Make (Spec) [@alert "-internal"]
type pos = Prefix | Tail1 | Tail2
type traces = {
where_it_failed : pos;
trace_prefix : trace list;
trace_tail_1 : trace list;
trace_tail_2 : trace list;
}
let empty where_it_failed =
{ where_it_failed; trace_prefix = []; trace_tail_1 = []; trace_tail_2 = [] }
let start_traces pos call res =
let traces = empty pos in
match pos with
| Prefix -> { traces with trace_prefix = [ { call; res } ] }
| Tail1 -> { traces with trace_tail_1 = [ { call; res } ] }
| Tail2 -> { traces with trace_tail_2 = [ { call; res } ] }
let add_trace (pos, trace) traces =
match pos with
| Prefix -> { traces with trace_prefix = trace :: traces.trace_prefix }
| Tail1 -> { traces with trace_tail_1 = trace :: traces.trace_tail_1 }
| Tail2 -> { traces with trace_tail_2 = trace :: traces.trace_tail_2 }
let append_traces traces (pos, xs) =
match pos with
| Prefix -> { traces with trace_prefix = traces.trace_prefix @ xs }
| Tail1 -> { traces with trace_tail_1 = traces.trace_tail_1 @ xs }
| Tail2 -> { traces with trace_tail_2 = traces.trace_tail_2 @ xs }
let get_traces pos traces =
match pos with
| Prefix -> traces.trace_prefix
| Tail1 -> traces.trace_tail_1
| Tail2 -> traces.trace_tail_2
let ( <+> ) marked_trace =
Option.map (fun (traces, report) -> (add_trace marked_trace traces, report))
let ( <++> ) o marked_traces =
Option.map
(fun (traces, report) -> (append_traces traces marked_traces, report))
o
let ( &&& ) o1 o2 = match o1 with None -> Lazy.force o2 | _ -> o1
let ( ||| ) o1 o2 = match o1 with None -> None | Some _ -> Lazy.force o2
let check_obs ortac_show_cmd postcond =
let postcond pos cmd state res =
let f report =
let call =
lazy (ortac_show_cmd cmd (Spec.next_state cmd state) true res)
in
(start_traces pos call res, report)
in
Option.map f @@ postcond cmd state res
in
let mk_trace pos last cmd state res =
let call = lazy (ortac_show_cmd cmd state last res) in
(pos, { call; res })
in
let trace_suffix pos state cs =
let rec aux state = function
| [] -> []
| (cmd, res) :: tail ->
let state' = Spec.next_state cmd state in
let call = lazy (ortac_show_cmd cmd state' (tail = []) res) in
{ call; res } :: aux state' tail
in
(pos, aux state cs)
in
let rec aux pref cs1 cs2 s =
match pref with
| (c, res) :: pref' ->
postcond Prefix c s res
&&& lazy
(let s' = Spec.next_state c s in
mk_trace Prefix (pref' = []) c s' res <+> aux pref' cs1 cs2 s')
| [] -> (
match (cs1, cs2) with
| [], [] -> None
| [], (c2, res2) :: cs2' ->
postcond Tail2 c2 s res2
&&& lazy
(let s' = Spec.next_state c2 s in
mk_trace Tail2 (cs2' = []) c2 s' res2
<+> aux pref cs1 cs2' s')
| (c1, res1) :: cs1', [] ->
postcond Tail1 c1 s res1
&&& lazy
(let s' = Spec.next_state c1 s in
mk_trace Tail1 (cs1' = []) c1 s' res1
<+> aux pref cs1' cs2 s')
| (c1, res1) :: cs1', (c2, res2) :: cs2' ->
postcond Tail1 c1 s res1
<++> trace_suffix Tail2 s cs2
&&& lazy
(let s' = Spec.next_state c1 s in
mk_trace Tail1 (cs1' = []) c1 s' res1
<+> aux pref cs1' cs2 s')
||| lazy
(postcond Tail2 c2 s res2
<++> trace_suffix Tail1 s cs1
&&& lazy
(let s' = Spec.next_state c2 s in
mk_trace Tail2 (cs2' = []) c2 s' res2
<+> aux pref cs1 cs2' (Spec.next_state c2 s))))
in
aux
let all_interleavings_ok (seq_pref, cmds1, cmds2) =
all_interleavings_ok seq_pref cmds1 cmds2 Spec.init_state
let arb_cmds_triple = arb_cmds_triple
let arb_triple = arb_triple
let rep_count = 25
let retries = 10
let seq_len = 20
let par_len = 12
let interp_sut_res sut cs =
let cs_arr = Array.of_list cs in
let res_arr =
Array.map
(fun c ->
Domain.cpu_relax ();
Spec.run c sut)
cs_arr
in
List.combine cs (Array.to_list res_arr)
let run_par seq_pref cmds1 cmds2 =
let sut = Spec.init_sut () in
let pref_obs = Spec.wrap_cmd_seq @@ fun () -> interp_sut_res sut seq_pref in
let barrier = Atomic.make 2 in
let main cmds () =
Spec.wrap_cmd_seq @@ fun () ->
Atomic.decr barrier;
while Atomic.get barrier <> 0 do
Domain.cpu_relax ()
done;
try Ok (interp_sut_res sut cmds) with exn -> Error exn
in
let dom1 = Domain.spawn (main cmds1) in
let dom2 = Domain.spawn (main cmds2) in
let obs1 = Domain.join dom1 in
let obs2 = Domain.join dom2 in
let () = Spec.cleanup sut in
let obs1 = match obs1 with Ok v -> v | Error exn -> raise exn in
let obs2 = match obs2 with Ok v -> v | Error exn -> raise exn in
(pref_obs, obs1, obs2)
let pp_prefix exp_res ppf traces =
let assert_flag = traces.where_it_failed = Prefix in
pp_traces assert_flag exp_res ppf @@ get_traces Prefix traces
let pp_spawned pos ppf traces =
let open Fmt in
let rec aux ppf = function
| [ { call; res } ] ->
pf ppf "%s in@\n(* returned %s *)@\n r" (Lazy.force call)
(show_res res)
| { call; res } :: xs ->
pf ppf "%s in@\n(* returned %s *)@\n" (Lazy.force call) (show_res res);
aux ppf xs
| _ -> ()
in
match get_traces pos traces with [] -> pf ppf " ()" | xs -> aux ppf xs
let pp_program max_suts ppf (traces, report) =
let open Fmt in
let inits =
List.init max_suts (fun i ->
Format.asprintf "let sut%d = %s" i report.init_sut)
in
let join1 =
match traces.where_it_failed with
| Tail1 -> "let r = Domain.join dom1"
| _ -> "let _ = Domain.join dom1"
and join2 =
match traces.where_it_failed with
| Tail2 -> "let r = Domain.join dom2"
| _ -> "let _ = Domain.join dom2"
in
pf ppf
"@[%s@\n\
open %s@\n\
let protect f = try Ok (f ()) with e -> Error e@\n\
%a@\n\
%a@\n\
let main1 () =@\n\
\ @[%a@]@\n\
let main2 () =@\n\
\ @[%a@]@\n\
@\n\
let dom1 = Domain.spawn main1@\n\
let dom2 = Domain.spawn main2@\n\
%s@\n\
%s@\n\
%a@\n"
"[@@@ocaml.warning \"-8\"]" report.mod_name
Format.(
pp_print_list ~pp_sep:(fun pf _ -> fprintf pf "@\n") pp_print_string)
inits (pp_prefix report.exp_res) traces (pp_spawned Tail1) traces
(pp_spawned Tail2) traces join1 join2 pp_expected_result report.exp_res
let agree_prop max_suts wrapped_init_state ortac_show_cmd postcond
(seq_pref, cmds1, cmds2) =
wrapped_init_state ();
let pref_obs, obs1, obs2 = run_par seq_pref cmds1 cmds2 in
match
check_obs ortac_show_cmd postcond pref_obs obs1 obs2 Spec.init_state
with
| None -> true
| Some (traces, report) ->
Report.message (pp_program max_suts) traces report
let agree_test ~count ~name max_suts wrapped_init_state ortac_show_cmd
postcond =
let max_gen = 3 * count in
let test_prop =
agree_prop max_suts wrapped_init_state ortac_show_cmd postcond
in
Test.make ~retries ~max_gen ~count ~name
(arb_triple seq_len par_len Spec.arb_cmd_seq Spec.arb_cmd_dom0
Spec.arb_cmd_dom1) (fun triple ->
assume (all_interleavings_ok triple);
repeat rep_count test_prop triple)
end
module Make (Spec : SpecOrtac) = MakeExt (struct
include SpecDefaults
include Spec
end)