Skip to content

Commit 40073b7

Browse files
Source sets (#18)
Source sets
1 parent d924d2a commit 40073b7

File tree

6 files changed

+193
-22
lines changed

6 files changed

+193
-22
lines changed

src/tracedAtomic.ml

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

404-
let trace ?(impl = `Dpor) ?interleavings ?(record_traces = false) func =
577+
let dpor_source func =
578+
reset_state ();
579+
let empty_state = do_run func [ (0, Start, None) ] in
580+
explore_source func [ empty_state ] [ IntSet.empty ]
581+
582+
let trace ?(impl = `Dpor_source) ?interleavings ?(record_traces = false) func =
405583
record_traces_flag := record_traces || Option.is_some dscheck_trace_file_env;
406584
interleavings_chan := interleavings;
407585

408-
(match impl with `Dpor -> dpor func | `Random iters -> random func iters);
586+
(match impl with
587+
| `Dpor -> dpor func
588+
| `Random iters -> random func iters
589+
| `Dpor_source -> dpor_source func);
409590

410591
(* print reports *)
411592
(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)