Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Add explicit ODPOR clean-up phase to DFSExplorer
authorMaxwell Pirtle <maxwellpirtle@gmail.com>
Mon, 15 May 2023 13:31:31 +0000 (15:31 +0200)
committerMaxwell Pirtle <maxwellpirtle@gmail.com>
Tue, 16 May 2023 07:51:21 +0000 (09:51 +0200)
ODPOR requires that we remove elements from the
wakeup tree and add elements to the sleep sets
at state in the execution only AFTER we've
completed fully the recursive exploration of the
space pointed towards by the wakeup trees; i.e.,
we only remove subtrees and add to the sleep sets
when we're walking back UP the state stack looking
for a point to recontinue the execution.

src/mc/api/State.cpp
src/mc/api/State.hpp
src/mc/explo/DFSExplorer.cpp
src/mc/explo/DFSExplorer.hpp
src/mc/explo/odpor/Execution.cpp
src/mc/explo/odpor/Execution.hpp

index 1c40e31..174e1b9 100644 (file)
@@ -262,4 +262,12 @@ void State::mark_path_interesting_for_odpor(const odpor::PartialExecution& pe, c
   this->wakeup_tree_.insert(E, pe);
 }
 
+void State::do_odpor_backtrack_cleanup()
+{
+  if (auto out_transition = get_transition_out(); out_transition != nullptr) {
+    remove_subtree_starting_with(out_transition->aid_);
+    add_sleep_set(std::move(out_transition));
+  }
+}
+
 } // namespace simgrid::mc
index d951ed9..aa04094 100644 (file)
@@ -151,7 +151,6 @@ public:
    * `N` running actor `p` of this state's wakeup tree
    */
   void remove_subtree_starting_with(aid_t p);
-
   bool has_empty_tree() const { return this->wakeup_tree_.empty(); }
 
   /**
@@ -159,6 +158,9 @@ public:
    */
   void mark_path_interesting_for_odpor(const odpor::PartialExecution&, const odpor::Execution&);
 
+  /** */
+  void do_odpor_backtrack_cleanup();
+
   /* Returns the total amount of states created so far (for statistics) */
   static long get_expanded_states() { return expended_states_; }
 };
index 27df977..198b2d6 100644 (file)
@@ -220,29 +220,28 @@ void DFSExplorer::run()
     auto next_state = std::make_shared<State>(get_remote_app(), state);
     on_state_creation_signal(next_state.get(), get_remote_app());
 
-    /* Sleep set procedure:
-     * adding the taken transition to the sleep set of the original state.
-     * <!> Since the parent sleep set is used to compute the child sleep set, this need to be
-     * 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 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
+      // With ODPOR, after taking a step forward, we must
+      // assign a copy of that subtree to the next state.
       //
-      // The latter evidently must be done BEFORE the former
+      // NOTE: We only add actions to the sleep set AFTER
+      // we've regenerated states. We must perform the search
+      // fully down a single path before we consider adding
+      // any elements to the sleep set according to the pseudocode
       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_)
+    } else {
+      /* Sleep set procedure:
+       * adding the taken transition to the sleep set of the original state.
+       * <!> Since the parent sleep set is used to compute the child sleep set, this need to be
+       * 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 considered in ActorState
     }
 
     /* DPOR persistent set procedure:
@@ -380,12 +379,6 @@ void DFSExplorer::run()
 
 std::shared_ptr<State> DFSExplorer::best_opened_state()
 {
-  if (reduction_mode_ == ReductionMode::odpor) {
-    const auto first =
-        std::find_if(stack_.rbegin(), stack_.rend(), [](const auto& state) { return !state->has_empty_tree(); });
-    return *first;
-  }
-
   int best_prio = 0; // cache the value for the best priority found so far (initialized to silence gcc)
   auto best     = end(opened_states_);   // iterator to the state to explore having the best priority
   auto valid    = begin(opened_states_); // iterator marking the limit between states still to explore, and already
@@ -419,6 +412,19 @@ std::shared_ptr<State> DFSExplorer::best_opened_state()
   return best_state;
 }
 
+std::shared_ptr<State> DFSExplorer::next_odpor_state()
+{
+  for (auto iter = stack_.rbegin(); iter != stack_.rend(); ++iter) {
+    const auto& state = *iter;
+    state->do_odpor_backtrack_cleanup();
+    if (!state->has_empty_tree()) {
+      return state;
+    }
+  }
+  xbt_die("There are no more states for ODPOR");
+  return nullptr;
+}
+
 void DFSExplorer::backtrack()
 {
   if (const auto last_event = execution_seq_.get_latest_event_handle();
@@ -434,7 +440,7 @@ void DFSExplorer::backtrack()
      * 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 (auto e_prime = static_cast<odpor::Execution::EventHandle>(0); e_prime <= last_event.value(); ++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
@@ -468,7 +474,7 @@ void DFSExplorer::backtrack()
   get_remote_app().check_deadlock();
 
   // Take the point with smallest distance
-  auto backtracking_point = best_opened_state();
+  auto backtracking_point = reduction_mode_ == ReductionMode::odpor ? next_odpor_state() : best_opened_state();
 
   // if no backtracking point, then set the stack_ to empty so we can end the exploration
   if (not backtracking_point) {
index 0c33d84..a0dc478 100644 (file)
@@ -119,6 +119,11 @@ private:
   std::vector<std::shared_ptr<State>> opened_states_;
   std::shared_ptr<State> best_opened_state();
 
+  /** If we're running ODPOR, picks the corresponding state in the stack
+   * (opened_states_ are ignored)
+   */
+  std::shared_ptr<State> next_odpor_state();
+
   /** Change current stack_ value to correspond to the one we would have
    *  had if we executed transition to get to state. This is required when
    *  backtracking, and achieved thanks to the fact states save their parent.*/
index 568b233..6b8e535 100644 (file)
@@ -242,6 +242,11 @@ std::optional<PartialExecution> Execution::get_odpor_extension_from(EventHandle
   // Now we add `e_prime := <q, i>` to `E'.v` and repeat the same work
   {
     E_prime_v.push_transition(get_event_with_handle(e_prime).get_transition());
+    v.push_back(get_event_with_handle(e_prime).get_transition());
+
+    const EventHandle e_prime_in_E_prime_v = E_prime_v.get_latest_event_handle().value();
+    v_handles.push_back(e_prime_in_E_prime_v);
+
     if (not located_actor_in_initial) {
       // It's possible `proc(e_prime)` is an initial
       const EventHandle e_prime_in_E_prime_v = E_prime_v.get_latest_event_handle().value();
index 0131b19..7903f61 100644 (file)
@@ -9,6 +9,7 @@
 #include "src/mc/api/ClockVector.hpp"
 #include "src/mc/explo/odpor/odpor_forward.hpp"
 #include "src/mc/mc_forward.hpp"
+#include "src/mc/mc_record.hpp"
 #include "src/mc/transition/Transition.hpp"
 
 #include <list>