model: add process_fence()
[c11tester.git] / model.cc
index d829582ef49ac6c9efe6053b2291a370bf9ff80e..6120f4d4d688d908fb66c47b848b3db472c9081b 100644 (file)
--- a/model.cc
+++ b/model.cc
@@ -91,6 +91,7 @@ ModelChecker::ModelChecker(struct model_params params) :
        futurevalues(new std::vector< struct PendingFutureValue, SnapshotAlloc<struct PendingFutureValue> >()),
        pending_rel_seqs(new std::vector< struct release_seq *, SnapshotAlloc<struct release_seq *> >()),
        thrd_last_action(new std::vector< ModelAction *, SnapshotAlloc<ModelAction *> >(1)),
+       thrd_last_fence_release(new std::vector< ModelAction *, SnapshotAlloc<ModelAction *> >()),
        node_stack(new NodeStack()),
        priv(new struct model_snapshot_members()),
        mo_graph(new CycleGraph())
@@ -120,6 +121,7 @@ ModelChecker::~ModelChecker()
        delete pending_rel_seqs;
 
        delete thrd_last_action;
+       delete thrd_last_fence_release;
        delete node_stack;
        delete scheduler;
        delete mo_graph;
@@ -172,7 +174,7 @@ unsigned int ModelChecker::get_num_threads() const
 }
 
 /** @return The currently executing Thread. */
-Thread * ModelChecker::get_current_thread()
+Thread * ModelChecker::get_current_thread() const
 {
        return scheduler->get_current_thread();
 }
@@ -183,7 +185,8 @@ modelclock_t ModelChecker::get_next_seq_num()
        return ++priv->used_sequence_numbers;
 }
 
-Node * ModelChecker::get_curr_node() {
+Node * ModelChecker::get_curr_node() const
+{
        return node_stack->get_head();
 }
 
@@ -520,6 +523,7 @@ bool ModelChecker::next_execution()
 ModelAction * ModelChecker::get_last_conflict(ModelAction *act)
 {
        switch (act->get_type()) {
+       case ATOMIC_FENCE:
        case ATOMIC_READ:
        case ATOMIC_WRITE:
        case ATOMIC_RMW: {
@@ -701,7 +705,7 @@ bool ModelChecker::process_read(ModelAction *curr, bool second_part_of_rmw)
                                continue;
                        }
 
-                       curr->read_from(reads_from);
+                       read_from(curr, reads_from);
                        mo_graph->commitChanges();
                        mo_check_promises(curr->get_tid(), reads_from);
 
@@ -710,7 +714,7 @@ bool ModelChecker::process_read(ModelAction *curr, bool second_part_of_rmw)
                        /* Read from future value */
                        value = curr->get_node()->get_future_value();
                        modelclock_t expiration = curr->get_node()->get_future_value_expiration();
-                       curr->read_from(NULL);
+                       read_from(curr, NULL);
                        Promise *valuepromise = new Promise(curr, value, expiration);
                        promises->push_back(valuepromise);
                }
@@ -854,6 +858,54 @@ bool ModelChecker::process_write(ModelAction *curr)
        return updated_mod_order || updated_promises;
 }
 
+/**
+ * Process a fence ModelAction
+ * @param curr The ModelAction to process
+ * @return True if synchronization was updated
+ */
+bool ModelChecker::process_fence(ModelAction *curr)
+{
+       /*
+        * fence-relaxed: no-op
+        * fence-release: only log the occurence (not in this function), for
+        *   use in later synchronization
+        * fence-acquire (this function): search for hypothetical release
+        *   sequences
+        */
+       bool updated = false;
+       if (curr->is_acquire()) {
+               action_list_t *list = action_trace;
+               action_list_t::reverse_iterator rit;
+               /* Find X : is_read(X) && X --sb-> curr */
+               for (rit = list->rbegin(); rit != list->rend(); rit++) {
+                       ModelAction *act = *rit;
+                       if (act->get_tid() != curr->get_tid())
+                               continue;
+                       /* Stop at the beginning of the thread */
+                       if (act->is_thread_start())
+                               break;
+                       /* Stop once we reach a prior fence-acquire */
+                       if (act->is_fence() && act->is_acquire())
+                               break;
+                       if (!act->is_read())
+                               continue;
+                       /* read-acquire will find its own release sequences */
+                       if (act->is_acquire())
+                               continue;
+
+                       /* Establish hypothetical release sequences */
+                       rel_heads_list_t release_heads;
+                       get_release_seq_heads(curr, act, &release_heads);
+                       for (unsigned int i = 0; i < release_heads.size(); i++)
+                               if (!act->synchronize_with(release_heads[i]))
+                                       set_bad_synchronization();
+                       if (release_heads.size() != 0)
+                               updated = true;
+               }
+       }
+       return updated;
+}
+
 /**
  * @brief Process the current action for thread-related activity
  *
@@ -1000,7 +1052,7 @@ bool ModelChecker::initialize_curr_action(ModelAction **curr)
 
        (*curr)->set_seq_number(get_next_seq_num());
 
-       newcurr = node_stack->explore_action(*curr, scheduler->get_enabled());
+       newcurr = node_stack->explore_action(*curr, scheduler->get_enabled_array());
        if (newcurr) {
                /* First restore type and order in case of RMW operation */
                if ((*curr)->is_rmwr())
@@ -1022,6 +1074,10 @@ bool ModelChecker::initialize_curr_action(ModelAction **curr)
 
                /* Always compute new clock vector */
                newcurr->create_cv(get_parent_action(newcurr->get_tid()));
+
+               /* Assign most recent release fence */
+               newcurr->set_last_fence_release(get_last_fence_release(newcurr->get_tid()));
+
                /*
                 * Perform one-time actions when pushing new ModelAction onto
                 * NodeStack
@@ -1039,6 +1095,34 @@ bool ModelChecker::initialize_curr_action(ModelAction **curr)
        }
 }
 
+/**
+ * @brief Establish reads-from relation between two actions
+ *
+ * Perform basic operations involved with establishing a concrete rf relation,
+ * including setting the ModelAction data and checking for release sequences.
+ *
+ * @param act The action that is reading (must be a read)
+ * @param rf The action from which we are reading (must be a write)
+ *
+ * @return True if this read established synchronization
+ */
+bool ModelChecker::read_from(ModelAction *act, const ModelAction *rf)
+{
+       act->set_read_from(rf);
+       if (rf != NULL && act->is_acquire()) {
+               rel_heads_list_t release_heads;
+               get_release_seq_heads(act, act, &release_heads);
+               int num_heads = release_heads.size();
+               for (unsigned int i = 0; i < release_heads.size(); i++)
+                       if (!act->synchronize_with(release_heads[i])) {
+                               set_bad_synchronization();
+                               num_heads--;
+                       }
+               return num_heads > 0;
+       }
+       return false;
+}
+
 /**
  * @brief Check whether a model action is enabled.
  *
@@ -1137,6 +1221,9 @@ Thread * ModelChecker::check_current_action(ModelAction *curr)
                        if (act->is_write() && process_write(act))
                                update = true;
 
+                       if (act->is_fence() && process_fence(act))
+                               update_all = true;
+
                        if (act->is_mutex_op() && process_mutex(act))
                                update_all = true;
 
@@ -1214,14 +1301,17 @@ bool ModelChecker::promises_expired() const
  * This is the strongest feasibility check available.
  * @return whether the current trace (partial or complete) must be a prefix of
  * a feasible trace.
- * */
+ */
 bool ModelChecker::isfeasibleprefix() const
 {
-       return promises->size() == 0 && pending_rel_seqs->size() == 0 && !is_infeasible();
+       return pending_rel_seqs->size() == 0 && is_feasible_prefix_ignore_relseq();
 }
 
-/** Returns whether the current completed trace is feasible. */
-bool ModelChecker::isfinalfeasible() const
+/**
+ * Returns whether the current completed trace is feasible, except for pending
+ * release sequences.
+ */
+bool ModelChecker::is_feasible_prefix_ignore_relseq() const
 {
        if (DBG_ENABLED() && promises->size() != 0)
                DEBUG("Infeasible: unrevolved promises\n");
@@ -1398,14 +1488,48 @@ bool ModelChecker::r_modification_order(ModelAction *curr, const ModelAction *rf
        bool added = false;
        ASSERT(curr->is_read());
 
+       /* Last SC fence in the current thread */
+       ModelAction *last_sc_fence_local = get_last_seq_cst_fence(curr->get_tid(), NULL);
+
        /* Iterate over all threads */
        for (i = 0; i < thrd_lists->size(); i++) {
+               /* Last SC fence in thread i */
+               ModelAction *last_sc_fence_thread_local = NULL;
+               if (int_to_id((int)i) != curr->get_tid())
+                       last_sc_fence_thread_local = get_last_seq_cst_fence(int_to_id(i), NULL);
+
+               /* Last SC fence in thread i, before last SC fence in current thread */
+               ModelAction *last_sc_fence_thread_before = NULL;
+               if (last_sc_fence_local)
+                       last_sc_fence_thread_before = get_last_seq_cst_fence(int_to_id(i), last_sc_fence_local);
+
                /* Iterate over actions in thread, starting from most recent */
                action_list_t *list = &(*thrd_lists)[i];
                action_list_t::reverse_iterator rit;
                for (rit = list->rbegin(); rit != list->rend(); rit++) {
                        ModelAction *act = *rit;
 
+                       if (act->is_write() && act != rf && act != curr) {
+                               /* C++, Section 29.3 statement 5 */
+                               if (curr->is_seqcst() && last_sc_fence_thread_local &&
+                                               *act < *last_sc_fence_thread_local) {
+                                       mo_graph->addEdge(act, rf);
+                                       added = true;
+                               }
+                               /* C++, Section 29.3 statement 4 */
+                               else if (act->is_seqcst() && last_sc_fence_local &&
+                                               *act < *last_sc_fence_local) {
+                                       mo_graph->addEdge(act, rf);
+                                       added = true;
+                               }
+                               /* C++, Section 29.3 statement 6 */
+                               else if (last_sc_fence_thread_before &&
+                                               *act < *last_sc_fence_thread_before) {
+                                       mo_graph->addEdge(act, rf);
+                                       added = true;
+                               }
+                       }
+
                        /*
                         * Include at most one act per-thread that "happens
                         * before" curr. Don't consider reflexively.
@@ -1531,15 +1655,23 @@ bool ModelChecker::w_modification_order(ModelAction *curr)
        if (curr->is_seqcst()) {
                /* We have to at least see the last sequentially consistent write,
                         so we are initialized. */
-               ModelAction *last_seq_cst = get_last_seq_cst(curr);
+               ModelAction *last_seq_cst = get_last_seq_cst_write(curr);
                if (last_seq_cst != NULL) {
                        mo_graph->addEdge(last_seq_cst, curr);
                        added = true;
                }
        }
 
+       /* Last SC fence in the current thread */
+       ModelAction *last_sc_fence_local = get_last_seq_cst_fence(curr->get_tid(), NULL);
+
        /* Iterate over all threads */
        for (i = 0; i < thrd_lists->size(); i++) {
+               /* Last SC fence in thread i, before last SC fence in current thread */
+               ModelAction *last_sc_fence_thread_before = NULL;
+               if (last_sc_fence_local && int_to_id((int)i) != curr->get_tid())
+                       last_sc_fence_thread_before = get_last_seq_cst_fence(int_to_id(i), last_sc_fence_local);
+
                /* Iterate over actions in thread, starting from most recent */
                action_list_t *list = &(*thrd_lists)[i];
                action_list_t::reverse_iterator rit;
@@ -1566,6 +1698,13 @@ bool ModelChecker::w_modification_order(ModelAction *curr)
                                        continue;
                        }
 
+                       /* C++, Section 29.3 statement 7 */
+                       if (last_sc_fence_thread_before && act->is_write() &&
+                                       *act < *last_sc_fence_thread_before) {
+                               mo_graph->addEdge(act, curr);
+                               added = true;
+                       }
+
                        /*
                         * Include at most one act per-thread that "happens
                         * before" curr
@@ -1709,6 +1848,8 @@ bool ModelChecker::release_seq_heads(const ModelAction *rf,
 
                if (rf->is_release())
                        release_heads->push_back(rf);
+               else if (rf->get_last_fence_release())
+                       release_heads->push_back(rf->get_last_fence_release());
                if (!rf->is_rmw())
                        break; /* End of RMW chain */
 
@@ -1734,8 +1875,17 @@ bool ModelChecker::release_seq_heads(const ModelAction *rf,
        if (rf->is_release())
                return true; /* complete */
 
-       /* else relaxed write; check modification order for contiguous subsequence
-        * -> rf must be same thread as release */
+       /* else relaxed write
+        * - check for fence-release in the same thread (29.8, stmt. 3)
+        * - check modification order for contiguous subsequence
+        *   -> rf must be same thread as release */
+
+       const ModelAction *fence_release = rf->get_last_fence_release();
+       /* Synchronize with a fence-release unconditionally; we don't need to
+        * find any more "contiguous subsequence..." for it */
+       if (fence_release)
+               release_heads->push_back(fence_release);
+
        int tid = id_to_int(rf->get_tid());
        std::vector<action_list_t> *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, rf->get_location());
        action_list_t *list = &(*thrd_lists)[tid];
@@ -1745,14 +1895,21 @@ bool ModelChecker::release_seq_heads(const ModelAction *rf,
        rit = std::find(list->rbegin(), list->rend(), rf);
        ASSERT(rit != list->rend());
 
-       /* Find the last write/release */
-       for (; rit != list->rend(); rit++)
+       /* Find the last {write,fence}-release */
+       for (; rit != list->rend(); rit++) {
+               if (fence_release && *(*rit) < *fence_release)
+                       break;
                if ((*rit)->is_release())
                        break;
+       }
        if (rit == list->rend()) {
                /* No write-release in this thread */
                return true; /* complete */
-       }
+       } else if (fence_release && *(*rit) < *fence_release) {
+               /* The fence-release is more recent (and so, "stronger") than
+                * the most recent write-release */
+               return true; /* complete */
+       } /* else, need to establish contiguous release sequence */
        ModelAction *release = *rit;
 
        ASSERT(rf->same_thread(release));
@@ -1825,22 +1982,29 @@ bool ModelChecker::release_seq_heads(const ModelAction *rf,
 }
 
 /**
- * A public interface for getting the release sequence head(s) with which a
+ * An interface for getting the release sequence head(s) with which a
  * given ModelAction must synchronize. This function only returns a non-empty
  * result when it can locate a release sequence head with certainty. Otherwise,
  * it may mark the internal state of the ModelChecker so that it will handle
- * the release sequence at a later time, causing @a act to update its
+ * the release sequence at a later time, causing @a acquire to update its
  * synchronization at some later point in execution.
- * @param act The 'acquire' action that may read from a release sequence
+ *
+ * @param acquire The 'acquire' action that may synchronize with a release
+ * sequence
+ * @param read The read action that may read from a release sequence; this may
+ * be the same as acquire, or else an earlier action in the same thread (i.e.,
+ * when 'acquire' is a fence-acquire)
  * @param release_heads A pass-by-reference return parameter. Will be filled
  * with the head(s) of the release sequence(s), if they exists with certainty.
  * @see ModelChecker::release_seq_heads
  */
-void ModelChecker::get_release_seq_heads(ModelAction *act, rel_heads_list_t *release_heads)
+void ModelChecker::get_release_seq_heads(ModelAction *acquire,
+               ModelAction *read, rel_heads_list_t *release_heads)
 {
-       const ModelAction *rf = act->get_reads_from();
+       const ModelAction *rf = read->get_reads_from();
        struct release_seq *sequence = (struct release_seq *)snapshot_calloc(1, sizeof(struct release_seq));
-       sequence->acquire = act;
+       sequence->acquire = acquire;
+       sequence->read = read;
 
        if (!release_seq_heads(rf, release_heads, sequence)) {
                /* add act to 'lazy checking' list */
@@ -1869,21 +2033,22 @@ bool ModelChecker::resolve_release_sequences(void *location, work_queue_t *work_
        std::vector< struct release_seq *, SnapshotAlloc<struct release_seq *> >::iterator it = pending_rel_seqs->begin();
        while (it != pending_rel_seqs->end()) {
                struct release_seq *pending = *it;
-               ModelAction *act = pending->acquire;
+               ModelAction *acquire = pending->acquire;
+               const ModelAction *read = pending->read;
 
                /* Only resolve sequences on the given location, if provided */
-               if (location && act->get_location() != location) {
+               if (location && read->get_location() != location) {
                        it++;
                        continue;
                }
 
-               const ModelAction *rf = act->get_reads_from();
+               const ModelAction *rf = read->get_reads_from();
                rel_heads_list_t release_heads;
                bool complete;
                complete = release_seq_heads(rf, &release_heads, pending);
                for (unsigned int i = 0; i < release_heads.size(); i++) {
-                       if (!act->has_synchronized_with(release_heads[i])) {
-                               if (act->synchronize_with(release_heads[i]))
+                       if (!acquire->has_synchronized_with(release_heads[i])) {
+                               if (acquire->synchronize_with(release_heads[i]))
                                        updated = true;
                                else
                                        set_bad_synchronization();
@@ -1893,15 +2058,16 @@ bool ModelChecker::resolve_release_sequences(void *location, work_queue_t *work_
                if (updated) {
                        /* Re-check all pending release sequences */
                        work_queue->push_back(CheckRelSeqWorkEntry(NULL));
-                       /* Re-check act for mo_graph edges */
-                       work_queue->push_back(MOEdgeWorkEntry(act));
+                       /* Re-check read-acquire for mo_graph edges */
+                       if (acquire->is_read())
+                               work_queue->push_back(MOEdgeWorkEntry(acquire));
 
                        /* propagate synchronization to later actions */
                        action_list_t::reverse_iterator rit = action_trace->rbegin();
-                       for (; (*rit) != act; rit++) {
+                       for (; (*rit) != acquire; rit++) {
                                ModelAction *propagate = *rit;
-                               if (act->happens_before(propagate)) {
-                                       propagate->synchronize_with(act);
+                               if (acquire->happens_before(propagate)) {
+                                       propagate->synchronize_with(acquire);
                                        /* Re-check 'propagate' for mo_graph edges */
                                        work_queue->push_back(MOEdgeWorkEntry(propagate));
                                }
@@ -1944,6 +2110,12 @@ void ModelChecker::add_action_to_lists(ModelAction *act)
                thrd_last_action->resize(get_num_threads());
        (*thrd_last_action)[tid] = act;
 
+       if (act->is_fence() && act->is_release()) {
+               if ((int)thrd_last_fence_release->size() <= tid)
+                       thrd_last_fence_release->resize(get_num_threads());
+               (*thrd_last_fence_release)[tid] = act;
+       }
+
        if (act->is_wait()) {
                void *mutex_loc=(void *) act->get_value();
                get_safe_ptr_action(obj_map, mutex_loc)->push_back(act);
@@ -1952,10 +2124,6 @@ void ModelChecker::add_action_to_lists(ModelAction *act)
                if (tid >= (int)vec->size())
                        vec->resize(priv->next_thread_id);
                (*vec)[tid].push_back(act);
-
-               if ((int)thrd_last_action->size() <= tid)
-                       thrd_last_action->resize(get_num_threads());
-               (*thrd_last_action)[tid] = act;
        }
 }
 
@@ -1973,6 +2141,20 @@ ModelAction * ModelChecker::get_last_action(thread_id_t tid) const
                return NULL;
 }
 
+/**
+ * @brief Get the last fence release performed by a particular Thread
+ * @param tid The thread ID of the Thread in question
+ * @return The last fence release in the thread, if one exists; NULL otherwise
+ */
+ModelAction * ModelChecker::get_last_fence_release(thread_id_t tid) const
+{
+       int threadid = id_to_int(tid);
+       if (threadid < (int)thrd_last_fence_release->size())
+               return (*thrd_last_fence_release)[id_to_int(tid)];
+       else
+               return NULL;
+}
+
 /**
  * Gets the last memory_order_seq_cst write (in the total global sequence)
  * performed on a particular object (i.e., memory location), not including the
@@ -1981,7 +2163,7 @@ ModelAction * ModelChecker::get_last_action(thread_id_t tid) const
  * check
  * @return The last seq_cst write
  */
-ModelAction * ModelChecker::get_last_seq_cst(ModelAction *curr) const
+ModelAction * ModelChecker::get_last_seq_cst_write(ModelAction *curr) const
 {
        void *location = curr->get_location();
        action_list_t *list = get_safe_ptr_action(obj_map, location);
@@ -1993,6 +2175,35 @@ ModelAction * ModelChecker::get_last_seq_cst(ModelAction *curr) const
        return NULL;
 }
 
+/**
+ * Gets the last memory_order_seq_cst fence (in the total global sequence)
+ * performed in a particular thread, prior to a particular fence.
+ * @param tid The ID of the thread to check
+ * @param before_fence The fence from which to begin the search; if NULL, then
+ * search for the most recent fence in the thread.
+ * @return The last prior seq_cst fence in the thread, if exists; otherwise, NULL
+ */
+ModelAction * ModelChecker::get_last_seq_cst_fence(thread_id_t tid, const ModelAction *before_fence) const
+{
+       /* All fences should have NULL location */
+       action_list_t *list = get_safe_ptr_action(obj_map, NULL);
+       action_list_t::reverse_iterator rit = list->rbegin();
+
+       if (before_fence) {
+               for (; rit != list->rend(); rit++)
+                       if (*rit == before_fence)
+                               break;
+
+               ASSERT(*rit == before_fence);
+               rit++;
+       }
+
+       for (; rit != list->rend(); rit++)
+               if ((*rit)->is_fence() && (tid == (*rit)->get_tid()) && (*rit)->is_seqcst())
+                       return *rit;
+       return NULL;
+}
+
 /**
  * Gets the last unlock operation performed on a particular mutex (i.e., memory
  * location). This function identifies the mutex according to the current
@@ -2013,7 +2224,7 @@ ModelAction * ModelChecker::get_last_unlock(ModelAction *curr) const
        return NULL;
 }
 
-ModelAction * ModelChecker::get_parent_action(thread_id_t tid)
+ModelAction * ModelChecker::get_parent_action(thread_id_t tid) const
 {
        ModelAction *parent = get_last_action(tid);
        if (!parent)
@@ -2026,7 +2237,7 @@ ModelAction * ModelChecker::get_parent_action(thread_id_t tid)
  * @param tid The thread whose clock vector we want
  * @return Desired clock vector
  */
-ClockVector * ModelChecker::get_cv(thread_id_t tid)
+ClockVector * ModelChecker::get_cv(thread_id_t tid) const
 {
        return get_parent_action(tid)->get_cv();
 }
@@ -2049,7 +2260,7 @@ bool ModelChecker::resolve_promises(ModelAction *write)
                        if (read->is_rmw()) {
                                mo_graph->addRMWEdge(write, read);
                        }
-                       read->read_from(write);
+                       read_from(read, write);
                        //First fix up the modification order for actions that happened
                        //before the read
                        r_modification_order(read, write);
@@ -2230,16 +2441,16 @@ void ModelChecker::build_reads_from_past(ModelAction *curr)
        unsigned int i;
        ASSERT(curr->is_read());
 
-       ModelAction *last_seq_cst = NULL;
+       ModelAction *last_sc_write = NULL;
 
        /* Track whether this object has been initialized */
        bool initialized = false;
 
        if (curr->is_seqcst()) {
-               last_seq_cst = get_last_seq_cst(curr);
+               last_sc_write = get_last_seq_cst_write(curr);
                /* We have to at least see the last sequentially consistent write,
                         so we are initialized. */
-               if (last_seq_cst != NULL)
+               if (last_sc_write != NULL)
                        initialized = true;
        }
 
@@ -2256,15 +2467,20 @@ void ModelChecker::build_reads_from_past(ModelAction *curr)
                                continue;
 
                        /* Don't consider more than one seq_cst write if we are a seq_cst read. */
-                       if (!curr->is_seqcst() || (!act->is_seqcst() && (last_seq_cst == NULL || !act->happens_before(last_seq_cst))) || act == last_seq_cst) {
-                               if (!curr->get_sleep_flag() || curr->is_seqcst() || sleep_can_read_from(curr, act)) {
-                                       DEBUG("Adding action to may_read_from:\n");
-                                       if (DBG_ENABLED()) {
-                                               act->print();
-                                               curr->print();
-                                       }
-                                       curr->get_node()->add_read_from(act);
+                       bool allow_read = true;
+
+                       if (curr->is_seqcst() && (act->is_seqcst() || (last_sc_write != NULL && act->happens_before(last_sc_write))) && act != last_sc_write)
+                               allow_read = false;
+                       else if (curr->get_sleep_flag() && !curr->is_seqcst() && !sleep_can_read_from(curr, act))
+                               allow_read = false;
+
+                       if (allow_read) {
+                               DEBUG("Adding action to may_read_from:\n");
+                               if (DBG_ENABLED()) {
+                                       act->print();
+                                       curr->print();
                                }
+                               curr->get_node()->add_read_from(act);
                        }
 
                        /* Include at most one act per-thread that "happens before" curr */
@@ -2498,7 +2714,7 @@ bool ModelChecker::take_step() {
         * (4) no pending promises
         */
        if (!pending_rel_seqs->empty() && (!next || next->is_model_thread()) &&
-                       isfinalfeasible() && !unrealizedraces.empty()) {
+                       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,