Skip to content

Commit 813a8cb

Browse files
checkpoint
1 parent 8a518dd commit 813a8cb

File tree

6 files changed

+265
-18
lines changed

6 files changed

+265
-18
lines changed

.ocamlformat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
profile = default
2-
version = 0.24.1
2+
version = 0.25.1

src/tracedAtomic.ml

Lines changed: 205 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ type _ Effect.t +=
1212
| FetchAndAdd : (int t * int) -> int Effect.t
1313

1414
module IntSet = Set.Make (Int)
15-
1615
module IntMap = Map.Make (Int)
1716

1817
let _string_of_set s = IntSet.fold (fun y x -> string_of_int y ^ "," ^ x) s ""
@@ -194,10 +193,36 @@ type state_cell = {
194193
}
195194

196195
let num_runs = ref 0
196+
let num_traces = ref 0
197197

198198
(* we stash the current state in case a check fails and we need to log it *)
199199
let schedule_for_checks = ref []
200200

201+
let print_trace () =
202+
Printf.printf "-----------------------\n";
203+
List.iter
204+
(fun s ->
205+
match s with
206+
| last_run_proc, last_run_op, last_run_ptr ->
207+
let var_name i =
208+
let c = Char.chr (i + 97) in
209+
let seq = Seq.cons c Seq.empty in
210+
String.of_seq seq
211+
in
212+
213+
let last_run_ptr =
214+
Option.map (* string_of_int *) var_name last_run_ptr
215+
|> Option.value ~default:""
216+
in
217+
let tabs =
218+
List.init last_run_proc (fun _ -> "\t\t") |> String.concat ""
219+
in
220+
Printf.printf "%sP%d: %s %s\n" tabs last_run_proc
221+
(atomic_op_str last_run_op)
222+
last_run_ptr)
223+
!schedule_for_checks;
224+
Printf.printf "-----------------------\n%!"
225+
201226
let do_run init_func init_schedule =
202227
init_func ();
203228
(*set up run *)
@@ -214,6 +239,8 @@ let do_run init_func init_schedule =
214239
| [] ->
215240
if !finished_processes == num_processes then (
216241
tracing := false;
242+
print_trace ();
243+
num_traces := !num_traces + 1;
217244
!final_func ();
218245
tracing := true)
219246
| (process_id_to_run, next_op, next_ptr) :: schedule ->
@@ -259,6 +286,172 @@ let do_run init_func init_schedule =
259286
backtrack = IntSet.empty;
260287
}
261288

289+
let _set_to_str s =
290+
IntSet.to_seq s |> List.of_seq |> List.map Int.to_string |> String.concat ", "
291+
292+
let rec explore_source depth func state sleep =
293+
(* sleep := IntSet.empty; *)
294+
let s = last_element state in
295+
(* Printf.printf "[depth=%d] explore (backtrack=[%s], sleep=[%s], enabled=[%s])\n%!" depth
296+
(set_to_str s.backtrack) (set_to_str !sleep) (set_to_str s.enabled); *)
297+
let p_maybe = IntSet.min_elt_opt (IntSet.diff s.enabled !sleep) in
298+
match p_maybe with
299+
| None -> () (* Printf.printf "[depth=%d] rtn\n%!" depth *)
300+
| Some p ->
301+
s.backtrack <- IntSet.singleton p;
302+
303+
while IntSet.(cardinal (diff s.backtrack !sleep)) > 0 do
304+
(* Printf.printf "[depth=%d] loop (backtrack=[%s], sleep=[%s])\n%!" depth
305+
(set_to_str s.backtrack) (set_to_str !sleep); *)
306+
let p = IntSet.min_elt (IntSet.diff s.backtrack !sleep) in
307+
let proc = List.nth s.procs p in
308+
309+
(* Printf.printf "[depth=%d] p=%d, %s\n%!" depth p (atomic_op_str proc.op); *)
310+
(* run *)
311+
let state_top =
312+
let schedule =
313+
List.map (fun s -> (s.run_proc, s.run_op, s.run_ptr)) state
314+
@ [ (p, proc.op, proc.obj_ptr) ]
315+
in
316+
do_run func schedule
317+
in
318+
assert (state_top.run_proc = p);
319+
let new_state = state @ [ state_top ] in
320+
321+
let obj_ptr = Option.value proc.obj_ptr ~default:(-1) in
322+
(* find reversible races *)
323+
let reversible_races =
324+
List.filter
325+
(fun proc' ->
326+
let run_ptr = Option.value proc'.run_ptr ~default:(-2) in
327+
obj_ptr = run_ptr && proc'.run_proc <> p)
328+
new_state
329+
in
330+
let reversible_races =
331+
match reversible_races with
332+
| [] -> []
333+
| _ -> [ last_element reversible_races ]
334+
in
335+
336+
(* Printf.printf "[depth=%d] reversible races sz: %d\n%!" depth
337+
(List.length reversible_races); *)
338+
for i = 0 to List.length reversible_races - 1 do
339+
let e = List.nth reversible_races i in
340+
(* Printf.printf "[depth=%d] inner loop, racing op: e=(q=%d, %s)\n%!"
341+
depth e.run_proc (atomic_op_str e.run_op); *)
342+
let found_e, prefix_rev, suffix_rev =
343+
List.fold_left
344+
(fun (seen_e, prefix, suffix) proc' ->
345+
if seen_e then (seen_e, prefix, proc' :: suffix)
346+
else if proc' == e then (true, prefix, suffix)
347+
else (false, proc' :: prefix, suffix))
348+
(false, [], []) state
349+
in
350+
assert (
351+
List.length prefix_rev + List.length suffix_rev
352+
= List.length state - 1);
353+
assert found_e;
354+
let prefix (* E' *) = List.rev prefix_rev in
355+
356+
if List.length prefix > 0 then
357+
(* List.iter
358+
(fun proc ->
359+
Printf.printf "[depth=%d] ::prefix:: (p=%d, %s)\n%!" depth
360+
proc.run_proc
361+
(atomic_op_str proc.run_op))
362+
prefix; *)
363+
let suffix = List.rev suffix_rev in
364+
365+
(* List.iter
366+
(fun proc ->
367+
Printf.printf "[depth=%d] ::suffix:: (p=%d, %s)\n%!" depth
368+
proc.run_proc
369+
(atomic_op_str proc.run_op))
370+
suffix; *)
371+
let prefix_top = last_element prefix in
372+
373+
(* Printf.printf
374+
"[depth=%d] nonempty prefix, prefix_top: (p=%d, %s)\n%!" depth
375+
prefix_top.run_proc
376+
(atomic_op_str prefix_top.run_op); *)
377+
let v_E (* occurs after e but independent of e *) =
378+
List.filter
379+
(fun proc' ->
380+
proc'.run_proc <> e.run_proc
381+
&& Option.value proc'.run_ptr ~default:(-1)
382+
<> Option.value e.run_ptr ~default:(-2))
383+
suffix
384+
in
385+
386+
(* List.iter
387+
(fun proc ->
388+
Printf.printf
389+
"[depth=%d] ::occurs_after_e_but_indep:: (p=%d, %s)\n%!"
390+
depth proc.run_proc
391+
(atomic_op_str proc.run_op))
392+
v_E; *)
393+
let v = v_E @ [ state_top ] in
394+
395+
(* List.iter
396+
(fun proc ->
397+
Printf.printf "[depth=%d] ::v:: (p=%d, %s)\n%!" depth
398+
proc.run_proc
399+
(atomic_op_str proc.run_op))
400+
v; *)
401+
let initials =
402+
let initials_map, initials_spawns =
403+
List.fold_left
404+
(fun (first_accesses, spawns) (state_cell : state_cell) ->
405+
match state_cell.run_ptr with
406+
| None ->
407+
let new_spawns =
408+
IntSet.add state_cell.run_proc spawns
409+
in
410+
(first_accesses, new_spawns)
411+
| Some obj_ptr ->
412+
let new_first_accesses =
413+
IntMap.update obj_ptr
414+
(function
415+
| Some v -> Some v
416+
| None -> Some state_cell.run_proc)
417+
first_accesses
418+
in
419+
(new_first_accesses, spawns))
420+
(IntMap.empty, IntSet.empty)
421+
v
422+
in
423+
IntMap.fold
424+
(fun _ run_proc initials_set ->
425+
IntSet.add run_proc initials_set)
426+
initials_map initials_spawns
427+
in
428+
429+
(* Printf.printf "[depth=%d] initials=[%s], backtrack=[%s]\n%!"
430+
depth (set_to_str initials)
431+
(set_to_str prefix_top.backtrack); *)
432+
if IntSet.(cardinal (inter prefix_top.backtrack initials)) = 0 then
433+
let _initial = IntSet.min_elt initials in
434+
(* Printf.printf "[depth=%d] adding initial to backtrack\n%!"
435+
depth; *)
436+
prefix_top.backtrack <- IntSet.add _initial prefix_top.backtrack
437+
done;
438+
439+
let sleep' =
440+
IntSet.filter
441+
(fun q ->
442+
(* only keep q that are independent with p: must be other thread of execution and act on a different object (or none) *)
443+
if q == p then false
444+
else
445+
let proc' = List.nth s.procs q in
446+
match proc'.obj_ptr with
447+
| None -> true
448+
| Some obj_ptr' -> obj_ptr' <> obj_ptr)
449+
!sleep
450+
in
451+
explore_source (depth + 1) func new_state (ref sleep');
452+
sleep := IntSet.add p !sleep
453+
done
454+
262455
let rec explore func state clock last_access =
263456
let s = last_element state in
264457
List.iter
@@ -305,17 +498,7 @@ let check f =
305498
tracing := false;
306499
if not (f ()) then (
307500
Printf.printf "Found assertion violation at run %d:\n" !num_runs;
308-
List.iter
309-
(fun s ->
310-
match s with
311-
| last_run_proc, last_run_op, last_run_ptr ->
312-
let last_run_ptr =
313-
Option.map string_of_int last_run_ptr |> Option.value ~default:""
314-
in
315-
Printf.printf "Process %d: %s %s\n" last_run_proc
316-
(atomic_op_str last_run_op)
317-
last_run_ptr)
318-
!schedule_for_checks;
501+
print_trace ();
319502
assert false);
320503
tracing := tracing_at_start
321504

@@ -326,9 +509,18 @@ let reset_state () =
326509
schedule_for_checks := [];
327510
CCVector.clear processes
328511

329-
let trace func =
512+
let _trace func =
330513
reset_state ();
331514
let empty_state = do_run func [ (0, Start, None) ] :: [] in
332515
let empty_clock = IntMap.empty in
333516
let empty_last_access = IntMap.empty in
334517
explore func empty_state empty_clock empty_last_access
518+
519+
let _trace_source func =
520+
reset_state ();
521+
let empty_state = do_run func [ (0, Start, None) ] in
522+
explore_source 0 func [ empty_state ] (ref IntSet.empty)
523+
524+
let trace func =
525+
_trace_source func;
526+
Printf.printf "\nnum_traces: %d\n%!" !num_traces

tests/dune

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,13 @@
1212
(name test_michael_scott_queue)
1313
(libraries dscheck alcotest)
1414
(modules test_michael_scott_queue michael_scott_queue))
15+
16+
(test
17+
(name test_simple)
18+
(libraries dscheck alcotest)
19+
(modules test_simple))
20+
21+
(test
22+
(name test_simple2)
23+
(libraries dscheck)
24+
(modules test_simple2))

tests/test_michael_scott_queue.ml

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,39 @@ let drain queue =
88
done;
99
!remaining
1010

11+
let list_to_str s =
12+
List.map (function Some i -> Int.to_string i | None -> "_") s
13+
|> String.concat ", "
14+
1115
let producer_consumer () =
1216
Atomic.trace (fun () ->
1317
let queue = Michael_scott_queue.create () in
1418
let items_total = 4 in
1519

1620
Atomic.spawn (fun () ->
17-
for _ = 1 to items_total do
18-
Michael_scott_queue.push queue 0
21+
for i = 1 to items_total do
22+
Michael_scott_queue.push queue i
1923
done);
2024

25+
let result = ref [] in
26+
2127
(* consumer *)
2228
let popped = ref 0 in
2329
Atomic.spawn (fun () ->
2430
for _ = 1 to items_total do
2531
match Michael_scott_queue.pop queue with
26-
| None -> ()
27-
| Some _ -> popped := !popped + 1
32+
| None -> result := None :: !result
33+
| Some v ->
34+
(result := Some v :: !result;
35+
popped := !popped + 1)
2836
done);
2937

3038
(* checks*)
3139
Atomic.final (fun () ->
40+
Printf.printf "pop sequence: %s\n" (list_to_str !result);
3241
Atomic.check (fun () ->
3342
let remaining = drain queue in
43+
Printf.printf "popped=%d, remaining=%d\n%!" !popped remaining;
3444
!popped + remaining = items_total)))
3545

3646
let () =

tests/test_simple.ml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module Atomic = Dscheck.TracedAtomic
2+
3+
let counter () =
4+
let a = Atomic.make 0 in
5+
let b = Atomic.make 0 in
6+
Atomic.spawn (fun () ->
7+
Atomic.set a 1;
8+
Atomic.set b 2);
9+
Atomic.spawn (fun () ->
10+
Atomic.set a 3;
11+
Atomic.set b 4);
12+
Atomic.final (fun () ->
13+
Printf.printf "a=%d, b=%d\n%!" (Atomic.get a) (Atomic.get b))
14+
15+
let test_safe_counter () = Atomic.trace counter
16+
let _ = test_safe_counter ()

tests/test_simple2.ml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module Atomic = Dscheck.TracedAtomic
2+
3+
let test () =
4+
let c = Atomic.make 0 in
5+
let b = Atomic.make 0 in
6+
let ok = Atomic.make false in
7+
8+
Atomic.spawn (fun () -> Atomic.set b 1);
9+
Atomic.spawn (fun () ->
10+
Atomic.set c 1;
11+
Atomic.set b 2);
12+
Atomic.spawn (fun () ->
13+
if Atomic.get c = 0 then if Atomic.get b = 0 then Atomic.set ok true);
14+
15+
Atomic.final (fun () ->
16+
Format.printf "b=%i c=%i ok=%b@." (Atomic.get b) (Atomic.get c)
17+
(Atomic.get ok))
18+
19+
let () = Atomic.trace test

0 commit comments

Comments
 (0)