check_recency: refactor some more, + include Promises
[c11tester.git] / model.cc
index 7c9b4c07296bf05e57280c4eb4f33198f62dbe38..b0ca05ea3c9009734d1196fad234b2aceaa8b001 100644 (file)
--- a/model.cc
+++ b/model.cc
@@ -263,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();
@@ -282,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();
 
@@ -313,14 +311,41 @@ void ModelChecker::execute_sleep_set()
        }
 }
 
+/**
+ * @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);
                }
        }
@@ -458,8 +483,16 @@ void ModelChecker::record_stats()
                stats.num_buggy_executions++;
        else if (is_complete_execution())
                stats.num_complete++;
-       else
+       else {
                stats.num_redundant++;
+
+               /**
+                * @todo We can violate this ASSERT() when fairness/sleep sets
+                * conflict to cause an execution to terminate, e.g. with:
+                * Scheduler: [0: disabled][1: disabled][2: sleep][3: current, enabled]
+                */
+               //ASSERT(scheduler->all_threads_sleeping());
+       }
 }
 
 /** @brief Print execution stats */
@@ -545,25 +578,118 @@ bool ModelChecker::next_execution()
        return true;
 }
 
+/**
+ * @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: {
@@ -720,42 +846,54 @@ 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();
+               bool updated = false;
+               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;
+                       mo_graph->startChanges();
 
-                       if (!second_part_of_rmw) {
-                               check_recency(curr, reads_from);
-                               r_status = r_modification_order(curr, reads_from);
-                       }
+                       ASSERT(!is_infeasible());
+                       if (!check_recency(curr, rf))
+                               priv->too_many_reads = true;
+                       updated = 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);
+                       value = rf->get_value();
+                       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);
@@ -763,6 +901,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;
@@ -888,7 +1030,7 @@ void ModelChecker::add_future_value(const ModelAction *writer, ModelAction *read
                        write_thread = write_thread->get_parent();
 
                struct future_value fv = {
-                       writer->get_value(),
+                       writer->get_write_value(),
                        writer->get_seq_number() + params.maxfuturedelay,
                        write_thread->get_id(),
                };
@@ -904,8 +1046,26 @@ void ModelChecker::add_future_value(const ModelAction *writer, ModelAction *read
  */
 bool ModelChecker::process_write(ModelAction *curr)
 {
-       bool updated_mod_order = w_modification_order(curr);
-       bool updated_promises = resolve_promises(curr);
+       /* Readers to which we may send our future value */
+       std::vector< ModelAction *, ModelAlloc<ModelAction *> > send_fv;
+
+       bool updated_mod_order = w_modification_order(curr, &send_fv);
+       int promise_idx = get_promise_to_resolve(curr);
+       const ModelAction *earliest_promise_reader;
+       bool updated_promises = false;
+
+       if (promise_idx >= 0) {
+               earliest_promise_reader = (*promises)[promise_idx]->get_reader(0);
+               updated_promises = resolve_promise(curr, promise_idx);
+       } else
+               earliest_promise_reader = NULL;
+
+       /* Don't send future values to reads after the Promise we resolve */
+       for (unsigned int i = 0; i < send_fv.size(); i++) {
+               ModelAction *read = send_fv[i];
+               if (!earliest_promise_reader || *read < *earliest_promise_reader)
+                       futurevalues->push_back(PendingFutureValue(curr, read));
+       }
 
        if (promises->size() == 0) {
                for (unsigned int i = 0; i < futurevalues->size(); i++) {
@@ -991,7 +1151,7 @@ bool ModelChecker::process_thread_action(ModelAction *curr)
        case THREAD_CREATE: {
                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);
+               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 */
@@ -1190,8 +1350,9 @@ bool ModelChecker::initialize_curr_action(ModelAction **curr)
  */
 bool ModelChecker::read_from(ModelAction *act, const ModelAction *rf)
 {
+       ASSERT(rf);
        act->set_read_from(rf);
-       if (rf != NULL && act->is_acquire()) {
+       if (act->is_acquire()) {
                rel_heads_list_t release_heads;
                get_release_seq_heads(act, act, &release_heads);
                int num_heads = release_heads.size();
@@ -1205,6 +1366,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.
  *
@@ -1228,6 +1418,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;
                }
        }
@@ -1273,7 +1464,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));
@@ -1290,7 +1481,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))
@@ -1331,7 +1522,7 @@ ModelAction * ModelChecker::check_current_action(ModelAction *curr)
                                }
                        }
                        if (act->is_write()) {
-                               if (w_modification_order(act))
+                               if (w_modification_order(act, NULL))
                                        updated = true;
                        }
                        mo_graph->commitChanges();
@@ -1359,7 +1550,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);
@@ -1452,6 +1642,33 @@ ModelAction * ModelChecker::process_rmw(ModelAction *act) {
        return lastread;
 }
 
+template <typename T>
+bool ModelChecker::should_read_instead(const ModelAction *curr, const ModelAction *rf, const T *other_rf) const
+{
+       /* Need a different write/promise */
+       if (other_rf->equals(rf))
+               return false;
+
+       /* Only look for "newer" writes/promises */
+       if (!mo_graph->checkReachable(rf, other_rf))
+               return false;
+
+       std::vector<action_list_t> *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, curr->get_location());
+       action_list_t *list = &(*thrd_lists)[id_to_int(curr->get_tid())];
+       action_list_t::reverse_iterator rit = list->rbegin();
+       ASSERT((*rit) == curr);
+       /* Skip past curr */
+       rit++;
+
+       /* Does this write/promise work for everyone? */
+       for (int i = 0; i < params.maxreads; i++, rit++) {
+               ModelAction *act = *rit;
+               if (!act->may_read_from(other_rf))
+                       return false;
+       }
+       return true;
+}
+
 /**
  * Checks whether a thread has read from the same write for too many times
  * without seeing the effects of a later write.
@@ -1462,83 +1679,57 @@ ModelAction * ModelChecker::process_rmw(ModelAction *act) {
  * 3) that other write must have been in the reads_from set for maxreads times.
  *
  * If so, we decide that the execution is no longer feasible.
+ *
+ * @param curr The current action. Must be a read.
+ * @param rf The store from which we might read.
+ * @return True if the read should succeed; false otherwise
  */
-void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf)
+bool ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) const
 {
-       if (params.maxreads != 0) {
-               if (curr->get_node()->get_read_from_size() <= 1)
-                       return;
-               //Must make sure that execution is currently feasible...  We could
-               //accidentally clear by rolling back
-               if (is_infeasible())
-                       return;
-               std::vector<action_list_t> *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, curr->get_location());
-               int tid = id_to_int(curr->get_tid());
-
-               /* Skip checks */
-               if ((int)thrd_lists->size() <= tid)
-                       return;
-               action_list_t *list = &(*thrd_lists)[tid];
-
-               action_list_t::reverse_iterator rit = list->rbegin();
-               /* Skip past curr */
-               for (; (*rit) != curr; rit++)
-                       ;
-               /* go past curr now */
-               rit++;
-
-               action_list_t::reverse_iterator ritcopy = rit;
-               //See if we have enough reads from the same value
-               int count = 0;
-               for (; count < params.maxreads; rit++, count++) {
-                       if (rit == list->rend())
-                               return;
-                       ModelAction *act = *rit;
-                       if (!act->is_read())
-                               return;
-
-                       if (act->get_reads_from() != rf)
-                               return;
-                       if (act->get_node()->get_read_from_size() <= 1)
-                               return;
-               }
-               for (int i = 0; i < curr->get_node()->get_read_from_size(); i++) {
-                       /* Get write */
-                       const ModelAction *write = curr->get_node()->get_read_from_at(i);
-
-                       /* Need a different write */
-                       if (write == rf)
-                               continue;
-
-                       /* Test to see whether this is a feasible write to read from */
-                       /** NOTE: all members of read-from set should be
-                        *  feasible, so we no longer check it here **/
-
-                       rit = ritcopy;
+       if (!params.maxreads)
+               return true;
 
-                       bool feasiblewrite = true;
-                       //new we need to see if this write works for everyone
+       //NOTE: Next check is just optimization, not really necessary....
+       if (curr->get_node()->get_read_from_past_size() +
+                       curr->get_node()->get_read_from_promise_size() <= 1)
+               return true;
 
-                       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) {
-                                               foundvalue = true;
-                                               break;
-                                       }
-                               }
-                               if (!foundvalue) {
-                                       feasiblewrite = false;
-                                       break;
-                               }
-                       }
-                       if (feasiblewrite) {
-                               priv->too_many_reads = true;
-                               return;
-                       }
-               }
+       std::vector<action_list_t> *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, curr->get_location());
+       int tid = id_to_int(curr->get_tid());
+       ASSERT(tid < (int)thrd_lists->size());
+       action_list_t *list = &(*thrd_lists)[tid];
+       action_list_t::reverse_iterator rit = list->rbegin();
+       ASSERT((*rit) == curr);
+       /* Skip past curr */
+       rit++;
+
+       action_list_t::reverse_iterator ritcopy = rit;
+       /* See if we have enough reads from the same value */
+       for (int count = 0; count < params.maxreads; ritcopy++, count++) {
+               if (ritcopy == list->rend())
+                       return true;
+               ModelAction *act = *ritcopy;
+               if (!act->is_read())
+                       return true;
+               if (act->get_reads_from_promise() && !act->get_reads_from_promise()->equals(rf))
+                       return true;
+               if (act->get_reads_from() && !act->get_reads_from()->equals(rf))
+                       return true;
+               if (act->get_node()->get_read_from_past_size() +
+                               act->get_node()->get_read_from_promise_size() <= 1)
+                       return true;
+       }
+       for (int i = 0; i < curr->get_node()->get_read_from_past_size(); i++) {
+               const ModelAction *write = curr->get_node()->get_read_from_past(i);
+               if (should_read_instead(curr, rf, write))
+                       return false; /* liveness failure */
+       }
+       for (int i = 0; i < curr->get_node()->get_read_from_promise_size(); i++) {
+               const Promise *promise = curr->get_node()->get_read_from_promise(i);
+               if (should_read_instead(curr, rf, promise))
+                       return false; /* liveness failure */
        }
+       return true;
 }
 
 /**
@@ -1618,13 +1809,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;
@@ -1663,9 +1854,11 @@ bool ModelChecker::r_modification_order(ModelAction *curr, const rf_type *rf)
  * (II) Sending the write back to non-synchronizing reads.
  *
  * @param curr The current action. Must be a write.
+ * @param send_fv A vector for stashing reads to which we may pass our future
+ * value. If NULL, then don't record any future values.
  * @return True if modification order edges were added; false otherwise
  */
-bool ModelChecker::w_modification_order(ModelAction *curr)
+bool ModelChecker::w_modification_order(ModelAction *curr, std::vector< ModelAction *, ModelAlloc<ModelAction *> > *send_fv)
 {
        std::vector<action_list_t> *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, curr->get_location());
        unsigned int i;
@@ -1758,9 +1951,9 @@ bool ModelChecker::w_modification_order(ModelAction *curr)
                                   pendingfuturevalue.
 
                                 */
-                               if (thin_air_constraint_may_allow(curr, act)) {
+                               if (send_fv && thin_air_constraint_may_allow(curr, act)) {
                                        if (!is_infeasible())
-                                               futurevalues->push_back(PendingFutureValue(curr, act));
+                                               send_fv->push_back(act);
                                        else if (curr->is_rmw() && act->is_rmw() && curr->get_reads_from() && curr->get_reads_from() == act->get_reads_from())
                                                add_future_value(curr, act);
                                }
@@ -1870,7 +2063,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())
@@ -1890,8 +2083,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 */
@@ -2284,42 +2475,49 @@ ClockVector * ModelChecker::get_cv(thread_id_t tid) const
 }
 
 /**
- * Resolve a set of Promises with a current write. The set is provided in the
- * Node corresponding to @a write.
+ * @brief Find the promise, if any to resolve for the current action
+ * @param curr The current ModelAction. Should be a write.
+ * @return The (non-negative) index for the Promise to resolve, if any;
+ * otherwise -1
+ */
+int ModelChecker::get_promise_to_resolve(const ModelAction *curr) const
+{
+       for (unsigned int i = 0; i < promises->size(); i++)
+               if (curr->get_node()->get_promise(i))
+                       return i;
+       return -1;
+}
+
+/**
+ * Resolve a Promise with a current write.
  * @param write The ModelAction that is fulfilling Promises
- * @return True if promises were resolved; false otherwise
+ * @param promise_idx The index corresponding to the promise
+ * @return True if the Promise was successfully resolved; false otherwise
  */
-bool ModelChecker::resolve_promises(ModelAction *write)
+bool ModelChecker::resolve_promise(ModelAction *write, unsigned int promise_idx)
 {
-       bool haveResolved = false;
        std::vector< ModelAction *, ModelAlloc<ModelAction *> > actions_to_check;
-       promise_list_t mustResolve, resolved;
+       promise_list_t mustResolve;
+       Promise *promise = (*promises)[promise_idx];
 
-       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);
-                       //Make sure the promise's value matches the write's value
-                       ASSERT(promise->is_compatible(write));
-                       mo_graph->resolvePromise(read, write, &mustResolve);
+       for (unsigned int i = 0; i < promise->get_num_readers(); i++) {
+               ModelAction *read = promise->get_reader(i);
+               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) && promise->same_value(write));
+       mo_graph->resolvePromise(promise, write, &mustResolve);
 
-                       resolved.push_back(promise);
-                       promises->erase(promises->begin() + promise_index);
-                       actions_to_check.push_back(read);
+       promises->erase(promises->begin() + promise_idx);
 
-                       haveResolved = true;
-               } else
-                       promise_index++;
-       }
+       /** @todo simplify the 'mustResolve' stuff */
+       ASSERT(mustResolve.size() <= 1);
+
+       if (!mustResolve.empty() && mustResolve[0] != promise)
+               priv->failed_promise = true;
+       delete promise;
 
-       for (unsigned int i = 0; i < mustResolve.size(); i++) {
-               if (std::find(resolved.begin(), resolved.end(), mustResolve[i])
-                               == resolved.end())
-                       priv->failed_promise = true;
-       }
-       for (unsigned int i = 0; i < resolved.size(); i++)
-               delete resolved[i];
        //Check whether reading these writes has made threads unable to
        //resolve promises
 
@@ -2328,7 +2526,7 @@ bool ModelChecker::resolve_promises(ModelAction *write)
                mo_check_promises(read, true);
        }
 
-       return haveResolved;
+       return true;
 }
 
 /**
@@ -2341,15 +2539,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->same_value(curr))
+                       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);
        }
 }
 
@@ -2358,13 +2561,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;
+                               }
                        }
                }
        }
@@ -2401,15 +2608,20 @@ void ModelChecker::mo_check_promises(const ModelAction *act, bool is_read_check)
 
        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->same_location(write))
                        continue;
 
-               if (pread->happens_before(act) && 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
@@ -2450,12 +2662,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;
@@ -2491,7 +2703,7 @@ void ModelChecker::build_reads_from_past(ModelAction *curr)
                                mo_graph->startChanges();
                                r_modification_order(curr, act);
                                if (!is_infeasible())
-                                       curr->get_node()->add_read_from(act);
+                                       curr->get_node()->add_read_from_past(act);
                                mo_graph->rollbackChanges();
                        }
 
@@ -2500,8 +2712,23 @@ void ModelChecker::build_reads_from_past(ModelAction *curr)
                                break;
                }
        }
+
+       /* 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()->get_read_from_size()) {
+       if (!curr->get_node()->read_from_size()) {
                priv->no_valid_reads = true;
                set_assert();
        }
@@ -2509,15 +2736,15 @@ void ModelChecker::build_reads_from_past(ModelAction *curr)
        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;
@@ -2526,13 +2753,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;
 }
 
 /**
@@ -2575,17 +2799,28 @@ void ModelChecker::dumpGraph(char *filename) const
        ModelAction **thread_array = (ModelAction **)model_calloc(1, sizeof(ModelAction *) * get_num_threads());
 
        for (action_list_t::iterator it = action_trace->begin(); it != action_trace->end(); it++) {
-               ModelAction *action = *it;
-               if (action->is_read()) {
-                       fprintf(file, "N%u [label=\"N%u, T%u\"];\n", action->get_seq_number(), action->get_seq_number(), action->get_tid());
-                       if (action->get_reads_from() != NULL)
-                               fprintf(file, "N%u -> N%u[label=\"rf\", color=red];\n", action->get_seq_number(), action->get_reads_from()->get_seq_number());
+               ModelAction *act = *it;
+               if (act->is_read()) {
+                       mo_graph->dot_print_node(file, act);
+                       if (act->get_reads_from())
+                               mo_graph->dot_print_edge(file,
+                                               act->get_reads_from(),
+                                               act,
+                                               "label=\"rf\", color=red, weight=2");
+                       else
+                               mo_graph->dot_print_edge(file,
+                                               act->get_reads_from_promise(),
+                                               act,
+                                               "label=\"rf\", color=red");
                }
-               if (thread_array[action->get_tid()] != NULL) {
-                       fprintf(file, "N%u -> N%u[label=\"sb\", color=blue];\n", thread_array[action->get_tid()]->get_seq_number(), action->get_seq_number());
+               if (thread_array[act->get_tid()]) {
+                       mo_graph->dot_print_edge(file,
+                                       thread_array[id_to_int(act->get_tid())],
+                                       act,
+                                       "label=\"sb\", color=blue, weight=400");
                }
 
-               thread_array[action->get_tid()] = action;
+               thread_array[act->get_tid()] = act;
        }
        fprintf(file, "}\n");
        model_free(thread_array);
@@ -2605,9 +2840,11 @@ void ModelChecker::print_summary() const
 #endif
 
        model_print("Execution %d:", stats.num_total);
-       if (isfeasibleprefix())
+       if (isfeasibleprefix()) {
+               if (scheduler->all_threads_sleeping())
+                       model_print(" SLEEP-SET REDUNDANT");
                model_print("\n");
-       else
+       else
                print_infeasibility(" INFEASIBLE");
        print_list(action_trace);
        model_print("\n");
@@ -2653,6 +2890,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
@@ -2756,7 +3013,7 @@ 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);
 
                do {