@@ -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+
329502let 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
0 commit comments