obj_thrd_map(new HashTable<void *, std::vector<action_list_t>, uintptr_t, 4 >()),
promises(new std::vector<Promise *>()),
futurevalues(new std::vector<struct PendingFutureValue>()),
- pending_acq_rel_seq(new std::vector<ModelAction *>()),
+ pending_rel_seqs(new std::vector<struct release_seq *>()),
thrd_last_action(new std::vector<ModelAction *>(1)),
node_stack(new NodeStack()),
mo_graph(new CycleGraph()),
priv = (struct model_snapshot_members *)calloc(1, sizeof(*priv));
/* First thread created will have id INITIAL_THREAD_ID */
priv->next_thread_id = INITIAL_THREAD_ID;
+
+ /* Initialize a model-checker thread, for special ModelActions */
+ model_thread = new Thread(get_next_id());
+ thread_map->put(id_to_int(model_thread->get_id()), model_thread);
}
/** @brief Destructor */
delete (*promises)[i];
delete promises;
- delete pending_acq_rel_seq;
+ delete pending_rel_seqs;
delete thrd_last_action;
delete node_stack;
/* 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();
+ node_stack->pop_restofstack(2);
} else {
/* Make a different thread execute for next step */
Node *node = nextnode->get_parent();
num_feasible_executions++;
}
- DEBUG("Number of acquires waiting on pending release sequences: %lu\n",
- pending_acq_rel_seq->size());
+ DEBUG("Number of acquires waiting on pending release sequences: %zu\n",
+ pending_rel_seqs->size());
if (isfinalfeasible() || DBG_ENABLED())
print_summary();
action_list_t::reverse_iterator rit;
for (rit = list->rbegin(); rit != list->rend(); rit++) {
ModelAction *prev = *rit;
- if (act->is_synchronizing(prev))
+ if (prev->could_synchronize_with(act))
return prev;
}
break;
return updated;
}
+/**
+ * @brief Process the current action for release sequence fixup activity
+ *
+ * Performs model-checker release sequence fixups for the current action,
+ * forcing a single pending release sequence to break (with a given, potential
+ * "loose" write) or to complete (i.e., synchronize). If a pending release
+ * sequence forms a complete release sequence, then we must perform the fixup
+ * synchronization, mo_graph additions, etc.
+ *
+ * @param curr The current action; must be a release sequence fixup action
+ * @param work_queue The work queue to which to add work items as they are
+ * generated
+ */
+void ModelChecker::process_relseq_fixup(ModelAction *curr, work_queue_t *work_queue)
+{
+ const ModelAction *write = curr->get_node()->get_relseq_break();
+ struct release_seq *sequence = pending_rel_seqs->back();
+ pending_rel_seqs->pop_back();
+ ASSERT(sequence);
+ ModelAction *acquire = sequence->acquire;
+ const ModelAction *rf = sequence->rf;
+ const ModelAction *release = sequence->release;
+ ASSERT(acquire);
+ ASSERT(release);
+ ASSERT(rf);
+ ASSERT(release->same_thread(rf));
+
+ if (write == NULL) {
+ /**
+ * @todo Forcing a synchronization requires that we set
+ * modification order constraints. For instance, we can't allow
+ * a fixup sequence in which two separate read-acquire
+ * operations read from the same sequence, where the first one
+ * synchronizes and the other doesn't. Essentially, we can't
+ * allow any writes to insert themselves between 'release' and
+ * 'rf'
+ */
+
+ /* Must synchronize */
+ if (!acquire->synchronize_with(release)) {
+ set_bad_synchronization();
+ return;
+ }
+ /* Re-check all pending release sequences */
+ work_queue->push_back(CheckRelSeqWorkEntry(NULL));
+ /* Re-check act for mo_graph edges */
+ work_queue->push_back(MOEdgeWorkEntry(acquire));
+
+ /* propagate synchronization to later actions */
+ action_list_t::reverse_iterator rit = action_trace->rbegin();
+ for (; (*rit) != acquire; rit++) {
+ ModelAction *propagate = *rit;
+ if (acquire->happens_before(propagate)) {
+ propagate->synchronize_with(acquire);
+ /* Re-check 'propagate' for mo_graph edges */
+ work_queue->push_back(MOEdgeWorkEntry(propagate));
+ }
+ }
+ } else {
+ /* Break release sequence with new edges:
+ * release --mo--> write --mo--> rf */
+ mo_graph->addEdge(release, write);
+ mo_graph->addEdge(write, rf);
+ }
+
+ /* See if we have realized a data race */
+ if (checkDataRaces())
+ set_assert();
+}
+
/**
* Initialize the current action by performing one or more of the following
* actions, as appropriate: merging RMWR and RMWC/RMW actions, stepping forward
*/
if (newcurr->is_write())
compute_promises(newcurr);
+ else if (newcurr->is_relseq_fixup())
+ compute_relseq_breakwrites(newcurr);
}
return newcurr;
}
if (act->is_mutex_op() && process_mutex(act))
update_all = true;
+ if (act->is_relseq_fixup())
+ process_relseq_fixup(curr, &work_queue);
+
if (update_all)
work_queue.push_back(CheckRelSeqWorkEntry(NULL));
else if (update)
if ((!parnode->backtrack_empty() ||
!currnode->read_from_empty() ||
!currnode->future_value_empty() ||
- !currnode->promise_empty())
+ !currnode->promise_empty() ||
+ !currnode->relseq_break_empty())
&& (!priv->next_backtrack ||
*curr > *priv->next_backtrack)) {
priv->next_backtrack = curr;
/** @return whether the current partial trace must be a prefix of a
* feasible trace. */
bool ModelChecker::isfeasibleprefix() {
- return promises->size() == 0 && pending_acq_rel_seq->size() == 0;
+ return promises->size() == 0 && pending_rel_seqs->size() == 0;
}
/** @return whether the current partial trace is feasible. */
}
added = true;
break;
- } else if (act->is_read() && !act->is_synchronizing(curr) &&
+ } else if (act->is_read() && !act->could_synchronize_with(curr) &&
!act->same_thread(curr)) {
/* We have an action that:
(1) did not happen before us
* @todo Finish lazy updating, when promises are fulfilled in the future
* @param rf The action that might be part of a release sequence. Must be a
* write.
- * @param release_heads A pass-by-reference style return parameter. After
+ * @param release_heads A pass-by-reference style return parameter. After
* execution of this function, release_heads will contain the heads of all the
- * relevant release sequences, if any exists
+ * relevant release sequences, if any exists with certainty
+ * @param pending A pass-by-reference style return parameter which is only used
+ * when returning false (i.e., uncertain). Returns most information regarding
+ * an uncertain release sequence, including any write operations that might
+ * break the sequence.
* @return true, if the ModelChecker is certain that release_heads is complete;
* false otherwise
*/
-bool ModelChecker::release_seq_heads(const ModelAction *rf, rel_heads_list_t *release_heads) const
+bool ModelChecker::release_seq_heads(const ModelAction *rf,
+ rel_heads_list_t *release_heads,
+ struct release_seq *pending) const
{
/* Only check for release sequences if there are no cycles */
if (mo_graph->checkForCycles())
};
if (!rf) {
/* read from future: need to settle this later */
+ pending->rf = NULL;
return false; /* incomplete */
}
ASSERT(rf->same_thread(release));
+ pending->writes.clear();
+
bool certain = true;
for (unsigned int i = 0; i < thrd_lists->size(); i++) {
if (id_to_int(rf->get_tid()) == (int)i)
bool future_ordered = false;
ModelAction *last = get_last_action(int_to_id(i));
- if (last && (rf->happens_before(last) ||
- get_thread(int_to_id(i))->is_complete()))
+ Thread *th = get_thread(int_to_id(i));
+ if ((last && rf->happens_before(last)) ||
+ !scheduler->is_enabled(th) ||
+ th->is_complete())
future_ordered = true;
+ ASSERT(!th->is_model_thread() || future_ordered);
+
for (rit = list->rbegin(); rit != list->rend(); rit++) {
const ModelAction *act = *rit;
/* Reach synchronization -> this thread is complete */
/* release --mo-> act --mo--> rf */
return true; /* complete */
}
+ /* act may break release sequence */
+ pending->writes.push_back(act);
certain = false;
}
if (!future_ordered)
- return false; /* This thread is uncertain */
+ certain = false; /* This thread is uncertain */
}
- if (certain)
+ if (certain) {
release_heads->push_back(release);
+ pending->writes.clear();
+ } else {
+ pending->release = release;
+ pending->rf = rf;
+ }
return certain;
}
void ModelChecker::get_release_seq_heads(ModelAction *act, rel_heads_list_t *release_heads)
{
const ModelAction *rf = act->get_reads_from();
+ struct release_seq *sequence = (struct release_seq *)snapshot_calloc(1, sizeof(struct release_seq));
+ sequence->acquire = act;
- if (!release_seq_heads(rf, release_heads)) {
+ if (!release_seq_heads(rf, release_heads, sequence)) {
/* add act to 'lazy checking' list */
- pending_acq_rel_seq->push_back(act);
+ pending_rel_seqs->push_back(sequence);
+ } else {
+ snapshot_free(sequence);
}
}
bool ModelChecker::resolve_release_sequences(void *location, work_queue_t *work_queue)
{
bool updated = false;
- std::vector<ModelAction *>::iterator it = pending_acq_rel_seq->begin();
- while (it != pending_acq_rel_seq->end()) {
- ModelAction *act = *it;
+ std::vector<struct release_seq *>::iterator it = pending_rel_seqs->begin();
+ while (it != pending_rel_seqs->end()) {
+ struct release_seq *pending = *it;
+ ModelAction *act = pending->acquire;
/* Only resolve sequences on the given location, if provided */
if (location && act->get_location() != location) {
const ModelAction *rf = act->get_reads_from();
rel_heads_list_t release_heads;
bool complete;
- complete = release_seq_heads(rf, &release_heads);
+ 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 (complete)
- it = pending_acq_rel_seq->erase(it);
- else
+ if (complete) {
+ it = pending_rel_seqs->erase(it);
+ snapshot_free(pending);
+ } else {
it++;
+ }
}
// If we resolved promises or data races, see if we have realized a data race.
} else
promise_index++;
}
+
+ //Check whether reading these writes has made threads unable to
+ //resolve promises
+
for(unsigned int i=0;i<threads_to_check.size();i++)
mo_check_promises(threads_to_check[i], write);
const ModelAction *act = promise->get_action();
if (!act->happens_before(curr) &&
act->is_read() &&
- !act->is_synchronizing(curr) &&
+ !act->could_synchronize_with(curr) &&
!act->same_thread(curr) &&
promise->get_value() == curr->get_value()) {
curr->get_node()->set_promise(i);
}
}
-/** Checks promises in response to change in ClockVector Threads. */
+/** 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 value read by the
+ * first read to the same lcoation as pread by pthread that is
+ * sequenced after pread..
+ *
+ * 1. If tid=pthread, then we check what other threads are reachable
+ * through the mode order starting with pwrite. Those threads cannot
+ * perform a write that will resolve the promise due to modification
+ * order constraints.
+ *
+ * 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.
+ *
+ * @parem tid The thread that either read from the model action
+ * write, or actually did the model action write.
+ *
+ * @parem write The ModelAction representing the relevant write.
+ */
+
void ModelChecker::mo_check_promises(thread_id_t tid, const ModelAction *write) {
void * location = write->get_location();
for (unsigned int i = 0; i < promises->size(); i++) {
if ( act->get_location() != location )
continue;
- if ( act->get_tid()==tid) {
+ //same thread as the promise
+ if ( act->get_tid()==tid ) {
+
+ //do we have a pwrite for the promise, if not, set it
if (promise->get_write() == NULL ) {
promise->set_write(write);
}
}
}
+/**
+ * Compute the set of writes that may break the current pending release
+ * sequence. This information is extracted from previou release sequence
+ * calculations.
+ *
+ * @param curr The current ModelAction. Must be a release sequence fixup
+ * action.
+ */
+void ModelChecker::compute_relseq_breakwrites(ModelAction *curr)
+{
+ if (pending_rel_seqs->empty())
+ return;
+
+ struct release_seq *pending = pending_rel_seqs->back();
+ for (unsigned int i = 0; i < pending->writes.size(); i++) {
+ const ModelAction *write = pending->writes[i];
+ curr->get_node()->add_relseq_break(write);
+ }
+
+ /* NULL means don't break the sequence; just synchronize */
+ curr->get_node()->add_relseq_break(NULL);
+}
+
/**
* 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"
if (has_asserted())
return false;
- Thread *curr = thread_current();
+ Thread *curr = priv->current_action ? get_thread(priv->current_action) : NULL;
if (curr) {
if (curr->get_state() == THREAD_READY) {
ASSERT(priv->current_action);
DEBUG("(%d, %d)\n", curr ? id_to_int(curr->get_id()) : -1,
next ? id_to_int(next->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 || next->is_model_thread()) &&
+ isfinalfeasible() && !unrealizedraces.empty()) {
+ printf("*** 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 == NULL -> don't take any more steps */
if (!next)
return false;