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