action: print promise number, not just ?
[model-checker.git] / model.cc
index 7c6b90d7093e88d0452c14f2ceaa2bab633a58c1..591d42a1ab3dbbc9aae2b8ea152e252c58379fd5 100644 (file)
--- a/model.cc
+++ b/model.cc
@@ -39,7 +39,6 @@ struct bug_message {
  */
 struct model_snapshot_members {
        model_snapshot_members() :
-               current_action(NULL),
                /* First thread created will have id INITIAL_THREAD_ID */
                next_thread_id(INITIAL_THREAD_ID),
                used_sequence_numbers(0),
@@ -48,6 +47,7 @@ struct model_snapshot_members {
                stats(),
                failed_promise(false),
                too_many_reads(false),
+               no_valid_reads(false),
                bad_synchronization(false),
                asserted(false)
        { }
@@ -58,7 +58,6 @@ struct model_snapshot_members {
                bugs.clear();
        }
 
-       ModelAction *current_action;
        unsigned int next_thread_id;
        modelclock_t used_sequence_numbers;
        ModelAction *next_backtrack;
@@ -66,6 +65,7 @@ struct model_snapshot_members {
        struct execution_stats stats;
        bool failed_promise;
        bool too_many_reads;
+       bool no_valid_reads;
        /** @brief Incorrectly-ordered synchronization was made */
        bool bad_synchronization;
        bool asserted;
@@ -159,6 +159,14 @@ void ModelChecker::reset_to_initial_state()
        /* Print all model-checker output before rollback */
        fflush(model_out);
 
+       /**
+        * FIXME: if we utilize partial rollback, we will need to free only
+        * those pending actions which were NOT pending before the rollback
+        * point
+        */
+       for (unsigned int i = 0; i < get_num_threads(); i++)
+               delete get_thread(int_to_id(i))->get_pending();
+
        snapshot_backtrack_before(0);
 }
 
@@ -203,11 +211,14 @@ Node * ModelChecker::get_curr_node() const
  * adjacency of read/write portions of a RMW action, force THREAD_CREATE to be
  * followed by a THREAD_START, or it can enforce execution replay/backtracking.
  * The model-checker may have no preference regarding the next thread (i.e.,
- * when exploring a new execution ordering), in which case this will return
- * NULL.
- * @param curr The current ModelAction. This action might guide the choice of
- * next thread.
- * @return The next thread to run. If the model-checker has no preference, NULL.
+ * when exploring a new execution ordering), in which case we defer to the
+ * scheduler.
+ *
+ * @param curr Optional: The current ModelAction. Only used if non-NULL and it
+ * might guide the choice of next thread (i.e., THREAD_CREATE should be
+ * followed by THREAD_START, or ATOMIC_RMWR followed by ATOMIC_{RMW,RMWC})
+ * @return The next chosen thread to run, if any exist. Or else if no threads
+ * remain to be executed, return NULL.
  */
 Thread * ModelChecker::get_next_thread(ModelAction *curr)
 {
@@ -216,14 +227,17 @@ Thread * ModelChecker::get_next_thread(ModelAction *curr)
        if (curr != NULL) {
                /* Do not split atomic actions. */
                if (curr->is_rmwr())
-                       return thread_current();
+                       return get_thread(curr);
                else if (curr->get_type() == THREAD_CREATE)
                        return curr->get_thread_operand();
        }
 
-       /* Have we completed exploring the preselected path? */
+       /*
+        * Have we completed exploring the preselected path? Then let the
+        * scheduler decide
+        */
        if (diverge == NULL)
-               return NULL;
+               return scheduler->select_next_thread();
 
        /* Else, we are trying to replay an execution */
        ModelAction *next = node_stack->get_next()->get_action();
@@ -249,10 +263,6 @@ Thread * ModelChecker::get_next_thread(ModelAction *curr)
                        /* The next node will read from a different value. */
                        tid = next->get_tid();
                        node_stack->pop_restofstack(2);
-               } else if (nextnode->increment_future_value()) {
-                       /* The next node will try to read from a different future value. */
-                       tid = next->get_tid();
-                       node_stack->pop_restofstack(2);
                } else if (nextnode->increment_relseq_break()) {
                        /* The next node will try to resolve a release sequence differently */
                        tid = next->get_tid();
@@ -268,6 +278,8 @@ Thread * ModelChecker::get_next_thread(ModelAction *curr)
                                earliest_diverge = prevnode->get_action();
                        }
                }
+               /* Start the round robin scheduler from this thread id */
+               scheduler->set_scheduler_thread(tid);
                /* The correct sleep set is in the parent node. */
                execute_sleep_set();
 
@@ -293,24 +305,47 @@ void ModelChecker::execute_sleep_set()
        for (unsigned int i = 0; i < get_num_threads(); i++) {
                thread_id_t tid = int_to_id(i);
                Thread *thr = get_thread(tid);
-               if (scheduler->is_sleep_set(thr) && thr->get_pending() == NULL) {
-                       thr->set_state(THREAD_RUNNING);
-                       scheduler->next_thread(thr);
-                       Thread::swap(&system_context, thr);
-                       priv->current_action->set_sleep_flag();
-                       thr->set_pending(priv->current_action);
+               if (scheduler->is_sleep_set(thr) && thr->get_pending()) {
+                       thr->get_pending()->set_sleep_flag();
                }
        }
 }
 
+/**
+ * @brief Should the current action wake up a given thread?
+ *
+ * @param curr The current action
+ * @param thread The thread that we might wake up
+ * @return True, if we should wake up the sleeping thread; false otherwise
+ */
+bool ModelChecker::should_wake_up(const ModelAction *curr, const Thread *thread) const
+{
+       const ModelAction *asleep = thread->get_pending();
+       /* Don't allow partial RMW to wake anyone up */
+       if (curr->is_rmwr())
+               return false;
+       /* Synchronizing actions may have been backtracked */
+       if (asleep->could_synchronize_with(curr))
+               return true;
+       /* All acquire/release fences and fence-acquire/store-release */
+       if (asleep->is_fence() && asleep->is_acquire() && curr->is_release())
+               return true;
+       /* Fence-release + store can awake load-acquire on the same location */
+       if (asleep->is_read() && asleep->is_acquire() && curr->same_var(asleep) && curr->is_write()) {
+               ModelAction *fence_release = get_last_fence_release(curr->get_tid());
+               if (fence_release && *(get_last_action(thread->get_id())) < *fence_release)
+                       return true;
+       }
+       return false;
+}
+
 void ModelChecker::wake_up_sleeping_actions(ModelAction *curr)
 {
        for (unsigned int i = 0; i < get_num_threads(); i++) {
                Thread *thr = get_thread(int_to_id(i));
                if (scheduler->is_sleep_set(thr)) {
-                       ModelAction *pending_act = thr->get_pending();
-                       if ((!curr->is_rmwr()) && pending_act->could_synchronize_with(curr))
-                               //Remove this thread from sleep set
+                       if (should_wake_up(curr, thr))
+                               /* Remove this thread from sleep set */
                                scheduler->remove_sleep(thr);
                }
        }
@@ -323,11 +358,22 @@ void ModelChecker::set_bad_synchronization()
        priv->bad_synchronization = true;
 }
 
+/**
+ * Check whether the current trace has triggered an assertion which should halt
+ * its execution.
+ *
+ * @return True, if the execution should be aborted; false otherwise
+ */
 bool ModelChecker::has_asserted() const
 {
        return priv->asserted;
 }
 
+/**
+ * Trigger a trace assertion which should cause this execution to be halted.
+ * This can be due to a detected bug or due to an infeasibility that should
+ * halt ASAP.
+ */
 void ModelChecker::set_assert()
 {
        priv->asserted = true;
@@ -524,25 +570,118 @@ bool ModelChecker::next_execution()
        return true;
 }
 
-ModelAction * ModelChecker::get_last_conflict(ModelAction *act)
+/**
+ * @brief Find the last fence-related backtracking conflict for a ModelAction
+ *
+ * This function performs the search for the most recent conflicting action
+ * against which we should perform backtracking, as affected by fence
+ * operations. This includes pairs of potentially-synchronizing actions which
+ * occur due to fence-acquire or fence-release, and hence should be explored in
+ * the opposite execution order.
+ *
+ * @param act The current action
+ * @return The most recent action which conflicts with act due to fences
+ */
+ModelAction * ModelChecker::get_last_fence_conflict(ModelAction *act) const
+{
+       /* Only perform release/acquire fence backtracking for stores */
+       if (!act->is_write())
+               return NULL;
+
+       /* Find a fence-release (or, act is a release) */
+       ModelAction *last_release;
+       if (act->is_release())
+               last_release = act;
+       else
+               last_release = get_last_fence_release(act->get_tid());
+       if (!last_release)
+               return NULL;
+
+       /* Skip past the release */
+       action_list_t *list = action_trace;
+       action_list_t::reverse_iterator rit;
+       for (rit = list->rbegin(); rit != list->rend(); rit++)
+               if (*rit == last_release)
+                       break;
+       ASSERT(rit != list->rend());
+
+       /* Find a prior:
+        *   load-acquire
+        * or
+        *   load --sb-> fence-acquire */
+       std::vector< ModelAction *, ModelAlloc<ModelAction *> > acquire_fences(get_num_threads(), NULL);
+       std::vector< ModelAction *, ModelAlloc<ModelAction *> > prior_loads(get_num_threads(), NULL);
+       bool found_acquire_fences = false;
+       for ( ; rit != list->rend(); rit++) {
+               ModelAction *prev = *rit;
+               if (act->same_thread(prev))
+                       continue;
+
+               int tid = id_to_int(prev->get_tid());
+
+               if (prev->is_read() && act->same_var(prev)) {
+                       if (prev->is_acquire()) {
+                               /* Found most recent load-acquire, don't need
+                                * to search for more fences */
+                               if (!found_acquire_fences)
+                                       return NULL;
+                       } else {
+                               prior_loads[tid] = prev;
+                       }
+               }
+               if (prev->is_acquire() && prev->is_fence() && !acquire_fences[tid]) {
+                       found_acquire_fences = true;
+                       acquire_fences[tid] = prev;
+               }
+       }
+
+       ModelAction *latest_backtrack = NULL;
+       for (unsigned int i = 0; i < acquire_fences.size(); i++)
+               if (acquire_fences[i] && prior_loads[i])
+                       if (!latest_backtrack || *latest_backtrack < *acquire_fences[i])
+                               latest_backtrack = acquire_fences[i];
+       return latest_backtrack;
+}
+
+/**
+ * @brief Find the last backtracking conflict for a ModelAction
+ *
+ * This function performs the search for the most recent conflicting action
+ * against which we should perform backtracking. This primary includes pairs of
+ * synchronizing actions which should be explored in the opposite execution
+ * order.
+ *
+ * @param act The current action
+ * @return The most recent action which conflicts with act
+ */
+ModelAction * ModelChecker::get_last_conflict(ModelAction *act) const
 {
        switch (act->get_type()) {
-       case ATOMIC_FENCE:
+       /* case ATOMIC_FENCE: fences don't directly cause backtracking */
        case ATOMIC_READ:
        case ATOMIC_WRITE:
        case ATOMIC_RMW: {
-               /* Optimization: relaxed operations don't need backtracking */
-               if (act->is_relaxed())
-                       return NULL;
+               ModelAction *ret = NULL;
+
                /* linear search: from most recent to oldest */
                action_list_t *list = get_safe_ptr_action(obj_map, act->get_location());
                action_list_t::reverse_iterator rit;
                for (rit = list->rbegin(); rit != list->rend(); rit++) {
                        ModelAction *prev = *rit;
-                       if (prev->could_synchronize_with(act))
-                               return prev;
+                       if (prev->could_synchronize_with(act)) {
+                               ret = prev;
+                               break;
+                       }
                }
-               break;
+
+               ModelAction *ret2 = get_last_fence_conflict(act);
+               if (!ret2)
+                       return ret;
+               if (!ret)
+                       return ret2;
+               if (*ret < *ret2)
+                       return ret2;
+               return ret;
        }
        case ATOMIC_LOCK:
        case ATOMIC_TRYLOCK: {
@@ -699,42 +838,52 @@ ModelAction * ModelChecker::get_next_backtrack()
 }
 
 /**
- * Processes a read or rmw model action.
+ * Processes a read model action.
  * @param curr is the read model action to process.
- * @param second_part_of_rmw is boolean that is true is this is the second action of a rmw.
  * @return True if processing this read updates the mo_graph.
  */
-bool ModelChecker::process_read(ModelAction *curr, bool second_part_of_rmw)
+bool ModelChecker::process_read(ModelAction *curr)
 {
+       Node *node = curr->get_node();
        uint64_t value = VALUE_NONE;
        bool updated = false;
        while (true) {
-               const ModelAction *reads_from = curr->get_node()->get_read_from();
-               if (reads_from != NULL) {
-                       mo_graph->startChanges();
+               switch (node->get_read_from_status()) {
+               case READ_FROM_PAST: {
+                       const ModelAction *rf = node->get_read_from_past();
+                       ASSERT(rf);
 
-                       value = reads_from->get_value();
-                       bool r_status = false;
-
-                       if (!second_part_of_rmw) {
-                               check_recency(curr, reads_from);
-                               r_status = r_modification_order(curr, reads_from);
-                       }
+                       mo_graph->startChanges();
+                       value = rf->get_value();
+                       check_recency(curr, rf);
+                       bool r_status = r_modification_order(curr, rf);
 
-                       if (!second_part_of_rmw && is_infeasible() && (curr->get_node()->increment_read_from() || curr->get_node()->increment_future_value())) {
+                       if (is_infeasible() && node->increment_read_from()) {
                                mo_graph->rollbackChanges();
                                priv->too_many_reads = false;
                                continue;
                        }
 
-                       read_from(curr, reads_from);
+                       read_from(curr, rf);
                        mo_graph->commitChanges();
                        mo_check_promises(curr, true);
 
                        updated |= r_status;
-               } else if (!second_part_of_rmw) {
+                       break;
+               }
+               case READ_FROM_PROMISE: {
+                       Promise *promise = curr->get_node()->get_read_from_promise();
+                       promise->add_reader(curr);
+                       value = promise->get_value();
+                       curr->set_read_from_promise(promise);
+                       mo_graph->startChanges();
+                       updated = r_modification_order(curr, promise);
+                       mo_graph->commitChanges();
+                       break;
+               }
+               case READ_FROM_FUTURE: {
                        /* Read from future value */
-                       struct future_value fv = curr->get_node()->get_future_value();
+                       struct future_value fv = node->get_future_value();
                        Promise *promise = new Promise(curr, fv);
                        value = fv.value;
                        curr->set_read_from_promise(promise);
@@ -742,6 +891,10 @@ bool ModelChecker::process_read(ModelAction *curr, bool second_part_of_rmw)
                        mo_graph->startChanges();
                        updated = r_modification_order(curr, promise);
                        mo_graph->commitChanges();
+                       break;
+               }
+               default:
+                       ASSERT(false);
                }
                get_thread(curr)->set_return_value(value);
                return updated;
@@ -968,7 +1121,10 @@ bool ModelChecker::process_thread_action(ModelAction *curr)
 
        switch (curr->get_type()) {
        case THREAD_CREATE: {
-               Thread *th = curr->get_thread_operand();
+               thrd_t *thrd = (thrd_t *)curr->get_location();
+               struct thread_params *params = (struct thread_params *)curr->get_value();
+               Thread *th = new Thread(thrd, params->func, params->arg, get_thread(curr));
+               add_thread(th);
                th->set_creation(curr);
                /* Promises can be satisfied by children */
                for (unsigned int i = 0; i < promises->size(); i++) {
@@ -1181,6 +1337,35 @@ bool ModelChecker::read_from(ModelAction *act, const ModelAction *rf)
        return false;
 }
 
+/**
+ * Check promises and eliminate potentially-satisfying threads when a thread is
+ * blocked (e.g., join, lock). A thread which is waiting on another thread can
+ * no longer satisfy a promise generated from that thread.
+ *
+ * @param blocker The thread on which a thread is waiting
+ * @param waiting The waiting thread
+ */
+void ModelChecker::thread_blocking_check_promises(Thread *blocker, Thread *waiting)
+{
+       for (unsigned int i = 0; i < promises->size(); i++) {
+               Promise *promise = (*promises)[i];
+               if (!promise->thread_is_available(waiting->get_id()))
+                       continue;
+               for (unsigned int j = 0; j < promise->get_num_readers(); j++) {
+                       ModelAction *reader = promise->get_reader(j);
+                       if (reader->get_tid() != blocker->get_id())
+                               continue;
+                       if (promise->eliminate_thread(waiting->get_id())) {
+                               /* Promise has failed */
+                               priv->failed_promise = true;
+                       } else {
+                               /* Only eliminate the 'waiting' thread once */
+                               return;
+                       }
+               }
+       }
+}
+
 /**
  * @brief Check whether a model action is enabled.
  *
@@ -1204,6 +1389,7 @@ bool ModelChecker::check_action_enabled(ModelAction *curr) {
                Thread *blocking = (Thread *)curr->get_location();
                if (!blocking->is_complete()) {
                        blocking->push_wait_list(curr);
+                       thread_blocking_check_promises(blocking, get_thread(curr));
                        return false;
                }
        }
@@ -1211,16 +1397,6 @@ bool ModelChecker::check_action_enabled(ModelAction *curr) {
        return true;
 }
 
-/**
- * Stores the ModelAction for the current thread action.  Call this
- * immediately before switching from user- to system-context to pass
- * data between them.
- * @param act The ModelAction created by the user-thread action
- */
-void ModelChecker::set_current_action(ModelAction *act) {
-       priv->current_action = act;
-}
-
 /**
  * This is the heart of the model checker routine. It performs model-checking
  * actions corresponding to a given "current action." Among other processes, it
@@ -1259,7 +1435,7 @@ ModelAction * ModelChecker::check_current_action(ModelAction *curr)
 
        /* Build may_read_from set for newly-created actions */
        if (newly_explored && curr->is_read())
-               build_reads_from_past(curr);
+               build_may_read_from(curr);
 
        /* Initialize work_queue with the "current action" work */
        work_queue_t work_queue(1, CheckCurrWorkEntry(curr));
@@ -1276,7 +1452,7 @@ ModelAction * ModelChecker::check_current_action(ModelAction *curr)
                        if (process_thread_action(curr))
                                update_all = true;
 
-                       if (act->is_read() && process_read(act, second_part_of_rmw))
+                       if (act->is_read() && !second_part_of_rmw && process_read(act))
                                update = true;
 
                        if (act->is_write() && process_write(act))
@@ -1345,7 +1521,6 @@ void ModelChecker::check_curr_backtracking(ModelAction *curr)
        if ((parnode && !parnode->backtrack_empty()) ||
                         !currnode->misc_empty() ||
                         !currnode->read_from_empty() ||
-                        !currnode->future_value_empty() ||
                         !currnode->promise_empty() ||
                         !currnode->relseq_break_empty()) {
                set_latest_backtrack(curr);
@@ -1387,6 +1562,8 @@ void ModelChecker::print_infeasibility(const char *prefix) const
                ptr += sprintf(ptr, "[failed promise]");
        if (priv->too_many_reads)
                ptr += sprintf(ptr, "[too many reads]");
+       if (priv->no_valid_reads)
+               ptr += sprintf(ptr, "[no valid reads-from]");
        if (priv->bad_synchronization)
                ptr += sprintf(ptr, "[bad sw ordering]");
        if (promises_expired())
@@ -1415,6 +1592,7 @@ bool ModelChecker::is_feasible_prefix_ignore_relseq() const
 bool ModelChecker::is_infeasible() const
 {
        return mo_graph->checkForCycles() ||
+               priv->no_valid_reads ||
                priv->failed_promise ||
                priv->too_many_reads ||
                priv->bad_synchronization ||
@@ -1449,7 +1627,7 @@ ModelAction * ModelChecker::process_rmw(ModelAction *act) {
 void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf)
 {
        if (params.maxreads != 0) {
-               if (curr->get_node()->get_read_from_size() <= 1)
+               if (curr->get_node()->get_read_from_past_size() <= 1)
                        return;
                //Must make sure that execution is currently feasible...  We could
                //accidentally clear by rolling back
@@ -1482,25 +1660,21 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf)
 
                        if (act->get_reads_from() != rf)
                                return;
-                       if (act->get_node()->get_read_from_size() <= 1)
+                       if (act->get_node()->get_read_from_past_size() <= 1)
                                return;
                }
-               for (int i = 0; i < curr->get_node()->get_read_from_size(); i++) {
+               for (int i = 0; i < curr->get_node()->get_read_from_past_size(); i++) {
                        /* Get write */
-                       const ModelAction *write = curr->get_node()->get_read_from_at(i);
+                       const ModelAction *write = curr->get_node()->get_read_from_past(i);
 
                        /* Need a different write */
                        if (write == rf)
                                continue;
 
                        /* Test to see whether this is a feasible write to read from */
-                       mo_graph->startChanges();
-                       r_modification_order(curr, write);
-                       bool feasiblereadfrom = !is_infeasible();
-                       mo_graph->rollbackChanges();
+                       /** NOTE: all members of read-from set should be
+                        *  feasible, so we no longer check it here **/
 
-                       if (!feasiblereadfrom)
-                               continue;
                        rit = ritcopy;
 
                        bool feasiblewrite = true;
@@ -1509,8 +1683,8 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf)
                        for (int loop = count; loop > 0; loop--, rit++) {
                                ModelAction *act = *rit;
                                bool foundvalue = false;
-                               for (int j = 0; j < act->get_node()->get_read_from_size(); j++) {
-                                       if (act->get_node()->get_read_from_at(j) == write) {
+                               for (int j = 0; j < act->get_node()->get_read_from_past_size(); j++) {
+                                       if (act->get_node()->get_read_from_past(j) == write) {
                                                foundvalue = true;
                                                break;
                                        }
@@ -1605,13 +1779,13 @@ bool ModelChecker::r_modification_order(ModelAction *curr, const rf_type *rf)
                                                added = mo_graph->addEdge(act, rf) || added;
                                        }
                                } else {
-                                       const ModelAction *prevreadfrom = act->get_reads_from();
-                                       //if the previous read is unresolved, keep going...
-                                       if (prevreadfrom == NULL)
-                                               continue;
-
-                                       if (!prevreadfrom->equals(rf)) {
-                                               added = mo_graph->addEdge(prevreadfrom, rf) || added;
+                                       const ModelAction *prevrf = act->get_reads_from();
+                                       const Promise *prevrf_promise = act->get_reads_from_promise();
+                                       if (prevrf) {
+                                               if (!prevrf->equals(rf))
+                                                       added = mo_graph->addEdge(prevrf, rf) || added;
+                                       } else if (!prevrf_promise->equals(rf)) {
+                                               added = mo_graph->addEdge(prevrf_promise, rf) || added;
                                        }
                                }
                                break;
@@ -1857,7 +2031,7 @@ bool ModelChecker::release_seq_heads(const ModelAction *rf,
        if (mo_graph->checkForCycles())
                return false;
 
-       while (rf) {
+       for ( ; rf != NULL; rf = rf->get_reads_from()) {
                ASSERT(rf->is_write());
 
                if (rf->is_release())
@@ -1877,8 +2051,6 @@ bool ModelChecker::release_seq_heads(const ModelAction *rf,
                /* acq_rel RMW is a sufficient stopping condition */
                if (rf->is_acquire() && rf->is_release())
                        return true; /* complete */
-
-               rf = rf->get_reads_from();
        };
        if (!rf) {
                /* read from future: need to settle this later */
@@ -2285,15 +2457,17 @@ bool ModelChecker::resolve_promises(ModelAction *write)
        for (unsigned int i = 0, promise_index = 0; promise_index < promises->size(); i++) {
                Promise *promise = (*promises)[promise_index];
                if (write->get_node()->get_promise(i)) {
-                       ModelAction *read = promise->get_action();
-                       read_from(read, write);
+                       for (unsigned int j = 0; j < promise->get_num_readers(); j++) {
+                               ModelAction *read = promise->get_reader(j);
+                               read_from(read, write);
+                               actions_to_check.push_back(read);
+                       }
                        //Make sure the promise's value matches the write's value
                        ASSERT(promise->is_compatible(write));
-                       mo_graph->resolvePromise(read, write, &mustResolve);
+                       mo_graph->resolvePromise(promise, write, &mustResolve);
 
                        resolved.push_back(promise);
                        promises->erase(promises->begin() + promise_index);
-                       actions_to_check.push_back(read);
 
                        haveResolved = true;
                } else
@@ -2328,15 +2502,20 @@ void ModelChecker::compute_promises(ModelAction *curr)
 {
        for (unsigned int i = 0; i < promises->size(); i++) {
                Promise *promise = (*promises)[i];
-               const ModelAction *act = promise->get_action();
-               if (!act->happens_before(curr) &&
-                               act->is_read() &&
-                               !act->could_synchronize_with(curr) &&
-                               !act->same_thread(curr) &&
-                               act->get_location() == curr->get_location() &&
-                               promise->get_value() == curr->get_value()) {
-                       curr->get_node()->set_promise(i, act->is_rmw());
+               if (!promise->is_compatible(curr) || promise->get_value() != curr->get_value())
+                       continue;
+
+               bool satisfy = true;
+               for (unsigned int j = 0; j < promise->get_num_readers(); j++) {
+                       const ModelAction *act = promise->get_reader(j);
+                       if (act->happens_before(curr) ||
+                                       act->could_synchronize_with(curr)) {
+                               satisfy = false;
+                               break;
+                       }
                }
+               if (satisfy)
+                       curr->get_node()->set_promise(i);
        }
 }
 
@@ -2345,13 +2524,17 @@ void ModelChecker::check_promises(thread_id_t tid, ClockVector *old_cv, ClockVec
 {
        for (unsigned int i = 0; i < promises->size(); i++) {
                Promise *promise = (*promises)[i];
-               const ModelAction *act = promise->get_action();
-               if ((old_cv == NULL || !old_cv->synchronized_since(act)) &&
-                               merge_cv->synchronized_since(act)) {
-                       if (promise->eliminate_thread(tid)) {
-                               //Promise has failed
-                               priv->failed_promise = true;
-                               return;
+               if (!promise->thread_is_available(tid))
+                       continue;
+               for (unsigned int j = 0; j < promise->get_num_readers(); j++) {
+                       const ModelAction *act = promise->get_reader(j);
+                       if ((!old_cv || !old_cv->synchronized_since(act)) &&
+                                       merge_cv->synchronized_since(act)) {
+                               if (promise->eliminate_thread(tid)) {
+                                       /* Promise has failed */
+                                       priv->failed_promise = true;
+                                       return;
+                               }
                        }
                }
        }
@@ -2372,74 +2555,44 @@ void ModelChecker::check_promises_thread_disabled()
  * @brief Checks promises in response to addition to modification order for
  * threads.
  *
- * Definitions:
- *
- * pthread is the thread that performed the read that created the promise
- *
- * pread is the read that created the promise
- *
- * pwrite is either the first write to same location as pread by
- * pthread that is sequenced after pread or the write read by the
- * first read to the same location as pread by pthread that is
- * sequenced after pread.
- *
- * 1. If tid=pthread, then we check what other threads are reachable
- * through the mod order starting with pwrite.  Those threads cannot
- * perform a write that will resolve the promise due to modification
- * order constraints.
+ * We test whether threads are still available for satisfying promises after an
+ * addition to our modification order constraints. Those that are unavailable
+ * are "eliminated". Once all threads are eliminated from satisfying a promise,
+ * that promise has failed.
  *
- * 2. If the tid is not pthread, we check whether pwrite can reach the
- * action write through the modification order.  If so, that thread
- * cannot perform a future write that will resolve the promise due to
- * modificatin order constraints.
- *
- * @param tid The thread that either read from the model action write, or
- * actually did the model action write.
- *
- * @param write The ModelAction representing the relevant write.
- * @param read  The ModelAction that reads a promised write, or NULL otherwise.
+ * @param act The ModelAction which updated the modification order
+ * @param is_read_check Should be true if act is a read and we must check for
+ * updates to the store from which it read (there is a distinction here for
+ * RMW's, which are both a load and a store)
  */
 void ModelChecker::mo_check_promises(const ModelAction *act, bool is_read_check)
 {
-       thread_id_t tid = act->get_tid();
        const ModelAction *write = is_read_check ? act->get_reads_from() : act;
 
        for (unsigned int i = 0; i < promises->size(); i++) {
                Promise *promise = (*promises)[i];
-               const ModelAction *pread = promise->get_action();
 
                // Is this promise on the same location?
-               if (!pread->same_var(write))
+               if (promise->get_value() != write->get_value())
                        continue;
 
-               // same thread as pread
-               if (pread->get_tid() == tid) {
-                       // make sure that the reader of this write happens after the promise
-                       if (!is_read_check || (pread->happens_before(act))) {
-                               // do we have a pwrite for the promise, if not, set it
-                               if (promise->get_write() == NULL) {
-                                       promise->set_write(write);
-                                       // The pwrite cannot happen before pread
-                                       if (write->happens_before(pread) && (write != pread)) {
-                                               priv->failed_promise = true;
-                                               return;
-                                       }
-                               }
-
-                               if (mo_graph->checkPromise(write, promise)) {
-                                       priv->failed_promise = true;
-                                       return;
-                               }
+               for (unsigned int j = 0; j < promise->get_num_readers(); j++) {
+                       const ModelAction *pread = promise->get_reader(j);
+                       if (!pread->happens_before(act))
+                              continue;
+                       if (mo_graph->checkPromise(write, promise)) {
+                               priv->failed_promise = true;
+                               return;
                        }
+                       break;
                }
 
                // Don't do any lookups twice for the same thread
-               if (!promise->thread_is_available(tid))
+               if (!promise->thread_is_available(act->get_tid()))
                        continue;
 
-               const ModelAction *pwrite = promise->get_write();
-               if (pwrite && mo_graph->checkReachable(pwrite, write)) {
-                       if (promise->eliminate_thread(tid)) {
+               if (mo_graph->checkReachable(promise, write)) {
+                       if (mo_graph->checkPromise(write, promise)) {
                                priv->failed_promise = true;
                                return;
                        }
@@ -2472,12 +2625,12 @@ void ModelChecker::compute_relseq_breakwrites(ModelAction *curr)
 
 /**
  * Build up an initial set of all past writes that this 'read' action may read
- * from. This set is determined by the clock vector's "happens before"
- * relationship.
+ * from, as well as any previously-observed future values that must still be valid.
+ *
  * @param curr is the current ModelAction that we are exploring; it must be a
  * 'read' operation.
  */
-void ModelChecker::build_reads_from_past(ModelAction *curr)
+void ModelChecker::build_may_read_from(ModelAction *curr)
 {
        std::vector<action_list_t> *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, curr->get_location());
        unsigned int i;
@@ -2508,8 +2661,14 @@ void ModelChecker::build_reads_from_past(ModelAction *curr)
                        else if (curr->get_sleep_flag() && !curr->is_seqcst() && !sleep_can_read_from(curr, act))
                                allow_read = false;
 
-                       if (allow_read)
-                               curr->get_node()->add_read_from(act);
+                       if (allow_read) {
+                               /* Only add feasible reads */
+                               mo_graph->startChanges();
+                               r_modification_order(curr, act);
+                               if (!is_infeasible())
+                                       curr->get_node()->add_read_from_past(act);
+                               mo_graph->rollbackChanges();
+                       }
 
                        /* Include at most one act per-thread that "happens before" curr */
                        if (act->happens_before(curr))
@@ -2517,18 +2676,38 @@ void ModelChecker::build_reads_from_past(ModelAction *curr)
                }
        }
 
+       /* Inherit existing, promised future values */
+       for (i = 0; i < promises->size(); i++) {
+               const Promise *promise = (*promises)[i];
+               const ModelAction *promise_read = promise->get_reader(0);
+               if (promise_read->same_var(curr)) {
+                       /* Only add feasible future-values */
+                       mo_graph->startChanges();
+                       r_modification_order(curr, promise);
+                       if (!is_infeasible())
+                               curr->get_node()->add_read_from_promise(promise_read);
+                       mo_graph->rollbackChanges();
+               }
+       }
+
+       /* We may find no valid may-read-from only if the execution is doomed */
+       if (!curr->get_node()->read_from_size()) {
+               priv->no_valid_reads = true;
+               set_assert();
+       }
+
        if (DBG_ENABLED()) {
                model_print("Reached read action:\n");
                curr->print();
-               model_print("Printing may_read_from\n");
-               curr->get_node()->print_may_read_from();
-               model_print("End printing may_read_from\n");
+               model_print("Printing read_from_past\n");
+               curr->get_node()->print_read_from_past();
+               model_print("End printing read_from_past\n");
        }
 }
 
 bool ModelChecker::sleep_can_read_from(ModelAction *curr, const ModelAction *write)
 {
-       while (true) {
+       for ( ; write != NULL; write = write->get_reads_from()) {
                /* UNINIT actions don't have a Node, and they never sleep */
                if (write->is_uninitialized())
                        return true;
@@ -2537,13 +2716,10 @@ bool ModelChecker::sleep_can_read_from(ModelAction *curr, const ModelAction *wri
                bool thread_sleep = prevnode->enabled_status(curr->get_tid()) == THREAD_SLEEP_SET;
                if (write->is_release() && thread_sleep)
                        return true;
-               if (!write->is_rmw()) {
+               if (!write->is_rmw())
                        return false;
-               }
-               if (write->get_reads_from() == NULL)
-                       return true;
-               write = write->get_reads_from();
        }
+       return true;
 }
 
 /**
@@ -2664,6 +2840,26 @@ Thread * ModelChecker::get_thread(const ModelAction *act) const
        return get_thread(act->get_tid());
 }
 
+/**
+ * @brief Get a Promise's "promise number"
+ *
+ * A "promise number" is an index number that is unique to a promise, valid
+ * only for a specific snapshot of an execution trace. Promises may come and go
+ * as they are generated an resolved, so an index only retains meaning for the
+ * current snapshot.
+ *
+ * @param promise The Promise to check
+ * @return The promise index, if the promise still is valid; otherwise -1
+ */
+int ModelChecker::get_promise_number(const Promise *promise) const
+{
+       for (unsigned int i = 0; i < promises->size(); i++)
+               if ((*promises)[i] == promise)
+                       return i;
+       /* Not found */
+       return -1;
+}
+
 /**
  * @brief Check if a Thread is currently enabled
  * @param t The Thread to check
@@ -2684,6 +2880,19 @@ bool ModelChecker::is_enabled(thread_id_t tid) const
        return scheduler->is_enabled(tid);
 }
 
+/**
+ * Switch from a model-checker context to a user-thread context. This is the
+ * complement of ModelChecker::switch_to_master and must be called from the
+ * model-checker context
+ *
+ * @param thread The user-thread to switch to
+ */
+void ModelChecker::switch_from_master(Thread *thread)
+{
+       scheduler->set_current_thread(thread);
+       Thread::swap(&system_context, thread);
+}
+
 /**
  * Switch from a user-context to the "master thread" context (a.k.a. system
  * context). This switch is made with the intention of exploring a particular
@@ -2699,8 +2908,8 @@ uint64_t ModelChecker::switch_to_master(ModelAction *act)
 {
        DBG();
        Thread *old = thread_current();
-       set_current_action(act);
-       old->set_state(THREAD_READY);
+       ASSERT(!old->get_pending());
+       old->set_pending(act);
        if (Thread::swap(old, &system_context) < 0) {
                perror("swap threads");
                exit(EXIT_FAILURE);
@@ -2711,13 +2920,11 @@ uint64_t ModelChecker::switch_to_master(ModelAction *act)
 /**
  * Takes the next step in the execution, if possible.
  * @param curr The current step to take
- * @return Returns true (success) if a step was taken and false otherwise.
+ * @return Returns the next Thread to run, if any; NULL if this execution
+ * should terminate
  */
-bool ModelChecker::take_step(ModelAction *curr)
+Thread * ModelChecker::take_step(ModelAction *curr)
 {
-       if (has_asserted())
-               return false;
-
        Thread *curr_thrd = get_thread(curr);
        ASSERT(curr_thrd->get_state() == THREAD_READY);
 
@@ -2725,61 +2932,24 @@ bool ModelChecker::take_step(ModelAction *curr)
 
        /* Infeasible -> don't take any more steps */
        if (is_infeasible())
-               return false;
+               return NULL;
        else if (isfeasibleprefix() && have_bug_reports()) {
                set_assert();
-               return false;
+               return NULL;
        }
 
-       if (params.bound != 0)
-               if (priv->used_sequence_numbers > params.bound)
-                       return false;
+       if (params.bound != 0 && priv->used_sequence_numbers > params.bound)
+               return NULL;
 
        if (curr_thrd->is_blocked() || curr_thrd->is_complete())
                scheduler->remove_thread(curr_thrd);
 
        Thread *next_thrd = get_next_thread(curr);
-       next_thrd = scheduler->next_thread(next_thrd);
 
        DEBUG("(%d, %d)\n", curr_thrd ? id_to_int(curr_thrd->get_id()) : -1,
                        next_thrd ? id_to_int(next_thrd->get_id()) : -1);
 
-       /*
-        * Launch end-of-execution release sequence fixups only when there are:
-        *
-        * (1) no more user threads to run (or when execution replay chooses
-        *     the 'model_thread')
-        * (2) pending release sequences
-        * (3) pending assertions (i.e., data races)
-        * (4) no pending promises
-        */
-       if (!pending_rel_seqs->empty() && (!next_thrd || next_thrd->is_model_thread()) &&
-                       is_feasible_prefix_ignore_relseq() && !unrealizedraces.empty()) {
-               model_print("*** WARNING: release sequence fixup action (%zu pending release seuqences) ***\n",
-                               pending_rel_seqs->size());
-               ModelAction *fixup = new ModelAction(MODEL_FIXUP_RELSEQ,
-                               std::memory_order_seq_cst, NULL, VALUE_NONE,
-                               model_thread);
-               set_current_action(fixup);
-               return true;
-       }
-
-       /* next_thrd == NULL -> don't take any more steps */
-       if (!next_thrd)
-               return false;
-
-       next_thrd->set_state(THREAD_RUNNING);
-
-       if (next_thrd->get_pending() != NULL) {
-               /* restart a pending action */
-               set_current_action(next_thrd->get_pending());
-               next_thrd->set_pending(NULL);
-               next_thrd->set_state(THREAD_READY);
-               return true;
-       }
-
-       /* Return false only if swap fails with an error */
-       return (Thread::swap(&system_context, next_thrd) == 0);
+       return next_thrd;
 }
 
 /** Wrapper to run the user's main function, with appropriate arguments */
@@ -2793,17 +2963,57 @@ void ModelChecker::run()
 {
        do {
                thrd_t user_thread;
-               Thread *t = new Thread(&user_thread, &user_main_wrapper, NULL);
-
+               Thread *t = new Thread(&user_thread, &user_main_wrapper, NULL, NULL);
                add_thread(t);
 
-               /* Run user thread up to its first action */
-               scheduler->next_thread(t);
-               Thread::swap(&system_context, t);
+               do {
+                       /*
+                        * Stash next pending action(s) for thread(s). There
+                        * should only need to stash one thread's action--the
+                        * thread which just took a step--plus the first step
+                        * for any newly-created thread
+                        */
+                       for (unsigned int i = 0; i < get_num_threads(); i++) {
+                               thread_id_t tid = int_to_id(i);
+                               Thread *thr = get_thread(tid);
+                               if (!thr->is_model_thread() && !thr->is_complete() && !thr->get_pending()) {
+                                       switch_from_master(thr);
+                               }
+                       }
 
-               /* Wait for all threads to complete */
-               while (take_step(priv->current_action));
+                       /* Catch assertions from prior take_step or from
+                        * between-ModelAction bugs (e.g., data races) */
+                       if (has_asserted())
+                               break;
+
+                       /* Consume the next action for a Thread */
+                       ModelAction *curr = t->get_pending();
+                       t->set_pending(NULL);
+                       t = take_step(curr);
+               } while (t && !t->is_model_thread());
+
+               /*
+                * Launch end-of-execution release sequence fixups only when
+                * the execution is otherwise feasible AND there are:
+                *
+                * (1) pending release sequences
+                * (2) pending assertions that could be invalidated by a change
+                * in clock vectors (i.e., data races)
+                * (3) no pending promises
+                */
+               while (!pending_rel_seqs->empty() &&
+                               is_feasible_prefix_ignore_relseq() &&
+                               !unrealizedraces.empty()) {
+                       model_print("*** WARNING: release sequence fixup action "
+                                       "(%zu pending release seuqence(s)) ***\n",
+                                       pending_rel_seqs->size());
+                       ModelAction *fixup = new ModelAction(MODEL_FIXUP_RELSEQ,
+                                       std::memory_order_seq_cst, NULL, VALUE_NONE,
+                                       model_thread);
+                       take_step(fixup);
+               };
        } while (next_execution());
 
+       model_print("******* Model-checking complete: *******\n");
        print_stats();
 }