Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Use `std::shared_ptr<Transition>` for Execution
[simgrid.git] / src / mc / explo / DFSExplorer.cpp
index ee62988..e654056 100644 (file)
@@ -93,8 +93,8 @@ void DFSExplorer::restore_stack(std::shared_ptr<State> state)
   }
   XBT_DEBUG("Replaced stack by %s", get_record_trace().to_string().c_str());
 
-  if (reduction_mode_ == ReductionMode::sdpor) {
-    execution_seq_ = sdpor::Execution();
+  if (reduction_mode_ == ReductionMode::sdpor || reduction_mode_ == ReductionMode::odpor) {
+    execution_seq_ = odpor::Execution();
 
     // NOTE: The outgoing transition for the top-most
     // state of the  stack refers to that which was taken
@@ -104,7 +104,7 @@ void DFSExplorer::restore_stack(std::shared_ptr<State> state)
     // when reconstructing the Exploration for SDPOR.
     for (auto iter = stack_.begin(); iter != stack_.end() - 1 and iter != stack_.end(); ++iter) {
       const auto& state = *(iter);
-      execution_seq_.push_transition(state->get_transition_out().get());
+      execution_seq_.push_transition(state->get_transition_out());
     }
   }
   XBT_DEBUG("Additionally replaced corresponding SDPOR execution stack");
@@ -143,8 +143,14 @@ void DFSExplorer::run()
         XBT_ERROR("/!\\ Max depth of %d reached! THIS WILL PROBABLY BREAK the dpor reduction /!\\",
                   _sg_mc_max_depth.get());
         XBT_ERROR("/!\\ If bad things happen, disable dpor with --cfg=model-check/reduction:none /!\\");
-      } else
+      } else if (reduction_mode_ == ReductionMode::sdpor || reduction_mode_ == ReductionMode::odpor) {
+        XBT_ERROR("/!\\ Max depth of %d reached! THIS **WILL** BREAK the reduction, which is not sound "
+                  "when stopping at a fixed depth /!\\",
+                  _sg_mc_max_depth.get());
+        XBT_ERROR("/!\\ If bad things happen, disable dpor with --cfg=model-check/reduction:none /!\\");
+      } else {
         XBT_WARN("/!\\ Max depth reached ! /!\\ ");
+      }
       this->backtrack();
       continue;
     }
@@ -161,9 +167,20 @@ void DFSExplorer::run()
     }
 #endif
 
+    if (reduction_mode_ == ReductionMode::odpor) {
+      // In the case of ODPOR, the wakeup tree for this
+      // state may be empty if we're exploring new territory
+      // (rather than following the partial execution of a
+      // wakeup tree). This corresponds to lines 9 to 13 of
+      // the ODPOR pseudocode
+      state->seed_wakeup_tree_if_needed(execution_seq_);
+    }
+
     // Search for the next transition
-    // next_transition returns a pair<aid_t, int> in case we want to consider multiple state (eg. during backtrack)
-    auto [next, _] = state->next_transition_guided();
+    // next_transition returns a pair<aid_t, int>
+    // in case we want to consider multiple states (eg. during backtrack)
+    const aid_t next = reduction_mode_ == ReductionMode::odpor ? state->next_odpor_transition()
+                                                               : std::get<0>(state->next_transition_guided());
 
     if (next < 0) { // If there is no more transition in the current state, backtrack.
       XBT_VERB("%lu actors remain, but none of them need to be interleaved (depth %zu).", state->get_actor_count(),
@@ -204,7 +221,24 @@ void DFSExplorer::run()
      * done after next_state creation */
     XBT_DEBUG("Marking Transition >>%s<< of process %ld done and adding it to the sleep set",
               state->get_transition_out()->to_string().c_str(), state->get_transition_out()->aid_);
-    state->add_sleep_set(state->get_transition_out()); // Actors are marked done when they are considerd in ActorState
+    state->add_sleep_set(state->get_transition_out()); // Actors are marked done when they are considered in ActorState
+
+    if (reduction_mode_ == ReductionMode::odpor) {
+      // With ODPOR, after taking a step forward, we must:
+      // 1. remove the subtree whose root is a single-process
+      // node of actor `next` (viz. the action we took) from
+      // the wakeup tree of `state`
+      //
+      // 2. assign a copy of that subtree to the next state
+      //
+      // The latter evidently must be done BEFORE the former
+      next_state->sprout_tree_from_parent_state();
+      state->remove_subtree_starting_with(next);
+
+      // TODO: Consider what we have to do to handle transitions
+      // with multiple possible executions. We probably have to re-insert
+      // something into `state` and make note of that for later (opened_states_)
+    }
 
     /* DPOR persistent set procedure:
      * for each new transition considered, check if it depends on any other previous transition executed before it
@@ -251,20 +285,23 @@ void DFSExplorer::run()
       /**
        * SDPOR Source Set Procedure:
        *
-       * Find "reversible races" in the current execution with respect
+       * Find "reversible races" in the current execution `E` with respect
        * to the latest action `p`. For each such race, determine one thread
        * not contained in the backtrack set at the "race point" `r` which
        * "represents" the trace formed by first executing everything after
-       * `r` and then `p` to flip the race
+       * `r` that doesn't depend on it (`v := notdep(r, E)`) and then `p` to
+       * flip the race.
+       *
+       * The intuition is that some subsequence of `v` may enable `p`, so
+       * we want to be sure that search "in that direction"
        */
-      execution_seq_.push_transition(executed_transition.get());
-
+      const aid_t p = executed_transition->aid_;
+      execution_seq_.push_transition(std::move(executed_transition));
       xbt_assert(execution_seq_.get_latest_event_handle().has_value(),
                  "No events are contained in the SDPOR/OPDPOR execution "
                  "even though one was just added");
-      const aid_t p       = executed_transition->aid_;
-      const auto next_E_p = execution_seq_.get_latest_event_handle().value();
 
+      const auto next_E_p = execution_seq_.get_latest_event_handle().value();
       for (const auto racing_event_handle : execution_seq_.get_racing_events_of(next_E_p)) {
         // To determine if the race is reversible, we have to ensure
         // that actor `p` running `next_E_p` (viz. the event such that
@@ -282,15 +319,19 @@ void DFSExplorer::run()
         if (prev_state->is_actor_enabled(p)) {
           // NOTE: To incorporate the idea of attempting to select the "best"
           // backtrack point into SDPOR, instead of selecting the `first` initial,
-          // we should instead compute all choices and decide which is bes
+          // we should instead compute all choices and decide which is best
           const std::optional<aid_t> q =
-              execution_seq_.get_first_ssdpor_initial_from(racing_event_handle, prev_state->get_backtrack_set());
+              execution_seq_.get_first_sdpor_initial_from(racing_event_handle, prev_state->get_backtrack_set());
           if (q.has_value()) {
             prev_state->consider_one(q.value());
             opened_states_.emplace_back(std::move(prev_state));
           }
         }
       }
+    } else if (reduction_mode_ == ReductionMode::odpor) {
+      // In the case of ODPOR, we simply observe the transition that was executed
+      // until we've reached a maximal trace
+      execution_seq_.push_transition(std::move(executed_transition));
     }
 
     // Before leaving that state, if the transition we just took can be taken multiple times, we
@@ -369,6 +410,46 @@ std::shared_ptr<State> DFSExplorer::best_opened_state()
 
 void DFSExplorer::backtrack()
 {
+  if (const auto last_event = execution_seq_.get_latest_event_handle();
+      reduction_mode_ == ReductionMode::odpor and last_event.has_value()) {
+    /**
+     * ODPOR Race Detection Procedure:
+     *
+     * For each reversible race in the current execution, we
+     * note if there are any continuations `C` equivalent to that which
+     * would reverse the race that have already either a) been searched by ODPOR or
+     * b) been *noted* to be searched by the wakeup tree at the
+     * appropriate reversal point, either as `C` directly or
+     * an as equivalent to `C` ("eventually looks like C", viz. the `~_E`
+     * relation)
+     */
+    for (auto e_prime = static_cast<odpor::Execution::EventHandle>(0); e_prime <= last_event; e_prime++) {
+      for (const auto e : execution_seq_.get_racing_events_of(e_prime)) {
+        // To determine if the race is reversible, we have to ensure
+        // that actor `p` running `e_i` (viz. the event such that
+        // `racing_event -> (E_p) e_i` and no other event
+        // "happens-between" the two) is enabled in any equivalent
+        // execution where `racing_event` happens before `e_i`.
+        //
+        // Importantly, it is equivalent to checking if in ANY
+        // such equivalent execution sequence where `racing_event`
+        // happens-before `next_E_p` that `p` is enabled in `pre(racing_event, E.p)`.
+        // Thus it suffices to check THIS execution
+        //
+        // If the actor `p` is not enabled at s_[E'], it is not a *reversible* race
+        const aid_t p                           = execution_seq_.get_actor_with_handle(e_prime);
+        const std::shared_ptr<State> prev_state = stack_[e];
+        if (prev_state->is_actor_enabled(p)) {
+          const std::optional<odpor::PartialExecution> v = execution_seq_.get_odpor_extension_from(
+              e, e_prime, prev_state->get_sleeping_set(), prev_state->get_enabled_actors());
+          if (v.has_value()) {
+            prev_state->mark_path_interesting_for_odpor(v.value(), execution_seq_.get_prefix_before(e));
+          }
+        }
+      }
+    }
+  }
+
   XBT_VERB("Backtracking from %s", get_record_trace().to_string().c_str());
   XBT_DEBUG("%lu alternatives are yet to be explored:", opened_states_.size());
 
@@ -435,20 +516,16 @@ void DFSExplorer::backtrack()
   this->restore_stack(backtracking_point);
 }
 
-DFSExplorer::DFSExplorer(const std::vector<char*>& args, bool with_dpor, bool need_memory_info)
+DFSExplorer::DFSExplorer(const std::vector<char*>& args, ReductionMode mode, bool need_memory_info)
     : Exploration(args, need_memory_info || _sg_mc_termination
 #if SIMGRID_HAVE_STATEFUL_MC
                             || _sg_mc_checkpoint > 0
 #endif
-      )
+                  )
+    , reduction_mode_(mode)
 {
-  if (with_dpor)
-    reduction_mode_ = ReductionMode::dpor;
-  else
-    reduction_mode_ = ReductionMode::none;
-
   if (_sg_mc_termination) {
-    if (with_dpor) {
+    if (mode != ReductionMode::none) {
       XBT_INFO("Check non progressive cycles (turning DPOR off)");
       reduction_mode_ = ReductionMode::none;
     } else {
@@ -474,9 +551,9 @@ DFSExplorer::DFSExplorer(const std::vector<char*>& args, bool with_dpor, bool ne
     opened_states_.emplace_back(stack_.back());
 }
 
-Exploration* create_dfs_exploration(const std::vector<char*>& args, bool with_dpor)
+Exploration* create_dfs_exploration(const std::vector<char*>& args, ReductionMode mode)
 {
-  return new DFSExplorer(args, with_dpor);
+  return new DFSExplorer(args, mode);
 }
 
 } // namespace simgrid::mc