Skip to content

Commit fefa925

Browse files
Source sets
1 parent d924d2a commit fefa925

File tree

6 files changed

+188
-22
lines changed

6 files changed

+188
-22
lines changed

src/tracedAtomic.ml

Lines changed: 178 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,174 @@ let rec explore_random func state =
326326
let statedash = state @ [ do_run func schedule ] in
327327
explore_random func statedash
328328

329+
let filter_out_happen_after operation sequence =
330+
let dependent_proc = ref (IntSet.singleton operation.run_proc) in
331+
let dependent_vars =
332+
ref
333+
(Option.map IntSet.singleton operation.run_ptr
334+
|> Option.value ~default:IntSet.empty)
335+
in
336+
List.filter_map
337+
(fun (state_cell : state_cell) ->
338+
let happen_after =
339+
IntSet.mem state_cell.run_proc !dependent_proc
340+
||
341+
match state_cell.run_ptr with
342+
| None -> false
343+
| Some run_ptr -> IntSet.mem run_ptr !dependent_vars
344+
in
345+
if happen_after then (
346+
dependent_proc := IntSet.add state_cell.run_proc !dependent_proc;
347+
match state_cell.run_ptr with
348+
| None -> ()
349+
| Some run_ptr -> dependent_vars := IntSet.add run_ptr !dependent_vars);
350+
351+
if happen_after then None else Some state_cell)
352+
sequence
353+
354+
let rec explore_source func state sleep_sets =
355+
let sleep = ref (last_element sleep_sets) in
356+
let s = last_element state in
357+
let p_maybe = IntSet.min_elt_opt (IntSet.diff s.enabled !sleep) in
358+
match p_maybe with
359+
| None -> ()
360+
| Some p ->
361+
s.backtrack <- IntSet.singleton p;
362+
363+
while IntSet.(cardinal (diff s.backtrack !sleep)) > 0 do
364+
let p = IntSet.min_elt (IntSet.diff s.backtrack !sleep) in
365+
let proc = List.nth s.procs p in
366+
367+
let state_top =
368+
let schedule =
369+
List.map (fun s -> (s.run_proc, s.run_op, s.run_ptr)) state
370+
@ [ (p, proc.op, proc.obj_ptr) ]
371+
in
372+
do_run func schedule
373+
in
374+
assert (state_top.run_proc = p);
375+
let new_state = state @ [ state_top ] in
376+
377+
(* Find the most recent race. Technically, this is the only one
378+
that fullfils the definition of race as defined per source set
379+
paper (as long as our atomic operations access one variable at a time).
380+
*)
381+
let reversible_race =
382+
Option.bind proc.obj_ptr (fun obj_ptr ->
383+
let dependent_ops =
384+
List.filter
385+
(fun proc' ->
386+
match proc'.run_ptr with
387+
| None -> false
388+
| Some run_ptr -> obj_ptr = run_ptr && proc'.run_proc <> p)
389+
new_state
390+
in
391+
match List.rev dependent_ops with [] -> None | v :: _ -> Some v)
392+
in
393+
394+
(match reversible_race with
395+
| None -> ()
396+
| Some e ->
397+
let prefix, suffix =
398+
(* We need the last operation before the first operation of the race
399+
occured because that's where alternative (reversal) is scheduled.
400+
We need the suffix to compute how to schedule the reversal. *)
401+
let found_e, prefix_rev, suffix_rev =
402+
List.fold_left
403+
(fun (seen_e, prefix, suffix) proc' ->
404+
if seen_e then (seen_e, prefix, proc' :: suffix)
405+
else if proc' == e then (true, prefix, suffix)
406+
else (false, proc' :: prefix, suffix))
407+
(false, [], []) state
408+
in
409+
410+
assert found_e;
411+
(* Out first operation is always a spawn, which cannot
412+
race with anything. Thus, any race has a prefix.
413+
*)
414+
assert (List.length prefix_rev > 0);
415+
assert (
416+
List.length suffix_rev
417+
= List.length state - List.length prefix_rev - 1);
418+
(List.rev prefix_rev, List.rev suffix_rev)
419+
in
420+
421+
(* Filter out operations that are dependent on the first operation
422+
of the race (e.g. successive operations of e.run_proc). We definitely
423+
don't want to schedule them.
424+
*)
425+
let indep_and_p =
426+
let indep = filter_out_happen_after e suffix in
427+
indep @ [ state_top ]
428+
in
429+
430+
(* Compute the set of operations, that lead to reversal of the race.
431+
The main premise here is that there may be a number of independent
432+
sequences that lead to reversal.
433+
434+
For example, suppose two racing operations t, t' and some sequences
435+
E, w, u. Suppose the current sequence is E.t.w.u.t', t' happens
436+
after u and w is independent of everything.
437+
438+
There's at least two ways to reverse t and t':
439+
- E.u.t'.(t,w in any order)
440+
- E.w.u.t'.t
441+
442+
Thus, initials consist of the first operations of u and w, since
443+
these are the operations that lead to exploration of the above
444+
sequences from E.
445+
*)
446+
let initials =
447+
let rec f = function
448+
| [] -> []
449+
| initial :: sequence ->
450+
initial.run_proc
451+
:: f (filter_out_happen_after initial sequence)
452+
in
453+
f indep_and_p
454+
in
455+
456+
(* Exploring one of the initials guarantees that reversal has been
457+
visited. Thus, schedule one of the initials only if none of them
458+
is in backtrack. *)
459+
let prefix_top = last_element prefix in
460+
if
461+
IntSet.(cardinal (inter prefix_top.backtrack (of_list initials)))
462+
= 0
463+
then
464+
(* We can add any initial*)
465+
let initial = last_element initials in
466+
prefix_top.backtrack <- IntSet.add initial prefix_top.backtrack);
467+
468+
let sleep' =
469+
(* Keep q that are independent with p only. Must be other thread of execution and act on a different object (or none).
470+
471+
The key idea here is as follows. Suppose we have processed some execution sequence E and there are
472+
just two enabled transitions left. Namely, t=(read a), t'=(read b). Crucially, they are independent.
473+
We begin the exploration from E with E.t and descend into E.t.t' afterwards. Since no more transitions
474+
are enabled, we return back to E and execute E.t'. But there's no point in executing E.t'.t. Since t and
475+
t' are independent, there's a guarantee that E.t.t' and E.t'.t belong to the same trace.
476+
477+
Therefore, at E, t is put into sleep set, and we explore with E.t' with sleep=[t]. Then E.t'.t gets
478+
sleep-set blocked and we save an execution sequence. Naturally, if there's some w such that it's dependent
479+
with t, then before we explore E.t'.w, we have to "wake" t up.
480+
*)
481+
IntSet.filter
482+
(fun q ->
483+
if q == p then false
484+
else
485+
let proc' = List.nth s.procs q in
486+
match proc'.obj_ptr with
487+
| None -> true
488+
| Some obj_ptr' ->
489+
Option.map (fun obj_ptr -> obj_ptr <> obj_ptr') proc.obj_ptr
490+
|> Option.value ~default:true)
491+
!sleep
492+
in
493+
explore_source func new_state (sleep_sets @ [ sleep' ]);
494+
sleep := IntSet.add p !sleep
495+
done
496+
329497
let rec explore func state clock last_access =
330498
let s = last_element state in
331499
List.iter
@@ -401,11 +569,19 @@ let dpor func =
401569
let empty_last_access = IntMap.empty in
402570
explore func empty_state empty_clock empty_last_access
403571

404-
let trace ?(impl = `Dpor) ?interleavings ?(record_traces = false) func =
572+
let dpor_source func =
573+
reset_state ();
574+
let empty_state = do_run func [ (0, Start, None) ] in
575+
explore_source func [ empty_state ] [ IntSet.empty ]
576+
577+
let trace ?(impl = `Dpor_source) ?interleavings ?(record_traces = false) func =
405578
record_traces_flag := record_traces || Option.is_some dscheck_trace_file_env;
406579
interleavings_chan := interleavings;
407580

408-
(match impl with `Dpor -> dpor func | `Random iters -> random func iters);
581+
(match impl with
582+
| `Dpor -> dpor func
583+
| `Random iters -> random func iters
584+
| `Dpor_source -> dpor_source func);
409585

410586
(* print reports *)
411587
(match !interleavings_chan with

src/tracedAtomic.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ val decr : int t -> unit
4747
(** [decr r] atomically decrements the value of [r] by [1]. *)
4848

4949
val trace :
50-
?impl:[ `Random of int | `Dpor ] ->
50+
?impl:[ `Random of int | `Dpor | `Dpor_source ] ->
5151
?interleavings:out_channel ->
5252
?record_traces:bool ->
5353
(unit -> unit) ->

tests/gen_program.ml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,13 +246,14 @@ let run_random config () =
246246
let threads = List.init config.domain_count (fun _ -> thread_f ()) in
247247
let program = ({ globals; threads } : Program.t) in
248248
if config.print_tests then Program.print program;
249-
let random = Program.run ~impl:(`Random 100) program in
249+
250+
let dpor_source = Program.run ~impl:`Dpor_source program in
250251
let dpor = Program.run ~impl:`Dpor program in
251-
if not (Dscheck.Trace_tracker.subset random dpor) then (
252+
if not (Dscheck.Trace_tracker.equal dpor_source dpor) then (
252253
Printf.printf "found mismatch\n\n%!";
253254
Program.print program;
254255
Dscheck.Trace_tracker.print dpor stdout;
255-
Dscheck.Trace_tracker.print random stdout;
256+
Dscheck.Trace_tracker.print dpor_source stdout;
256257
assert false)
257258

258259
let run config test_count =

tests/report_trace.expected

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,14 @@ sequence 2
1414
----------------------------------------
1515
P0 P1
1616
----------------------------------------
17-
start
18-
start
19-
fetch_and_add a
20-
fetch_and_add a
21-
fetch_and_add b
22-
----------------------------------------
23-
24-
sequence 3
25-
----------------------------------------
26-
P0 P1
27-
----------------------------------------
2817
start
2918
start
3019
fetch_and_add a
3120
fetch_and_add a
3221
fetch_and_add b
3322
----------------------------------------
3423

35-
explored 3 interleavings and 12 states
24+
explored 2 interleavings and 9 states
3625
----
3726
(1,a),(0,a),(1,b)
3827
(0,a),(1,a),(1,b)

tests/traces/conditional2

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
----
22
(0,a),(3,c),(3,b),(2,b),(1,b),(2,c)
3-
(0,a),(2,b),(3,c),(3,b),(1,b),(2,c)
4-
(2,b),(2,c),(3,c),(3,b),(3,a),(0,a),(1,b)
3+
(0,a),(2,b),(3,c),(2,c),(3,b),(1,b)
4+
(2,b),(2,c),(3,c),(3,b),(1,b),(3,a),(0,a)
55
(0,a),(3,c),(3,b),(1,b),(2,b)
66
(0,a),(1,b),(2,b),(3,c),(3,b)
77
(0,a),(1,b),(3,c),(3,b),(2,b)

tests/traces/conditional_ssb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
----
22
(0,a),(3,c),(3,b),(2,b),(1,b),(2,c)
3-
(0,a),(2,b),(3,c),(3,b),(1,b),(2,c)
4-
(2,b),(2,c),(3,c),(3,b),(3,a),(0,a),(1,b)
3+
(0,a),(2,b),(3,c),(2,c),(3,b),(1,b)
4+
(2,b),(2,c),(3,c),(3,b),(1,b),(3,a),(0,a)
55
(0,a),(3,c),(3,b),(1,b),(2,b)
66
(0,a),(1,b),(2,b),(3,c),(3,b)
77
(0,a),(1,b),(3,c),(3,b),(2,b)

0 commit comments

Comments
 (0)