scheduler->update_sleep_set(prevnode);
/* Reached divergence point */
- if (nextnode->increment_misc()) {
- /* The next node will try to satisfy a different misc_index values. */
- tid = next->get_tid();
- node_stack->pop_restofstack(2);
- } else if (nextnode->increment_promise()) {
- /* The next node will try to satisfy a different set of promises. */
- tid = next->get_tid();
- node_stack->pop_restofstack(2);
- } else if (nextnode->increment_read_from()) {
- /* The next node will read from a different 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 */
+ if (nextnode->increment_behaviors()) {
+ /* Execute the same thread with a new behavior */
tid = next->get_tid();
node_stack->pop_restofstack(2);
} else {
return blocking_threads;
}
-/**
- * Check if a Thread has entered a circular wait deadlock situation. This will
- * not check other threads for potential deadlock situations, and may miss
- * deadlocks involving WAIT.
- *
- * @param t The thread which may have entered a deadlock
- * @return True if this Thread entered a deadlock; false otherwise
- */
-bool ModelChecker::is_circular_wait(const Thread *t) const
-{
- for (Thread *waiting = t->waiting_on() ; waiting != NULL; waiting = waiting->waiting_on())
- if (waiting == t)
- return true;
- return false;
-}
-
/**
* Check if this is a complete execution. That is, have all thread completed
* execution (rather than exiting because sleep sets have forced a redundant
}
break;
}
+ case ATOMIC_WAIT:
case ATOMIC_UNLOCK: {
//unlock the lock
state->locked = NULL;
//wake up the other threads
- action_list_t *waiters = get_safe_ptr_action(lock_waiters_map, curr->get_location());
+ action_list_t *waiters = get_safe_ptr_action(lock_waiters_map, mutex);
//activate all the waiting threads
for (action_list_t::iterator rit = waiters->begin(); rit != waiters->end(); rit++) {
scheduler->wake(get_thread(*rit));
}
waiters->clear();
- break;
- }
- case ATOMIC_WAIT: {
- //unlock the lock
- state->locked = NULL;
- //wake up the other threads
- action_list_t *waiters = get_safe_ptr_action(lock_waiters_map, (void *) curr->get_value());
- //activate all the waiting threads
- for (action_list_t::iterator rit = waiters->begin(); rit != waiters->end(); rit++) {
- scheduler->wake(get_thread(*rit));
- }
- waiters->clear();
- //check whether we should go to sleep or not...simulate spurious failures
+
+ if (!curr->is_wait())
+ break; /* The rest is only for ATOMIC_WAIT */
+
+ /* Should we go to sleep? (simulate spurious failures) */
if (curr->get_node()->get_misc() == 0) {
get_safe_ptr_action(condvar_waiters_map, curr->get_location())->push_back(curr);
- //disable us
+ /* disable us */
scheduler->sleep(get_thread(curr));
}
break;
return false;
}
+/**
+ * @brief Check if the current pending promises allow a future value to be sent
+ *
+ * If one of the following is true:
+ * (a) there are no pending promises
+ * (b) the reader and writer do not cross any promises
+ * Then, it is safe to pass a future value back now.
+ *
+ * Otherwise, we must save the pending future value until (a) or (b) is true
+ *
+ * @param writer The operation which sends the future value. Must be a write.
+ * @param reader The operation which will observe the value. Must be a read.
+ * @return True if the future value can be sent now; false if it must wait.
+ */
+bool ModelChecker::promises_may_allow(const ModelAction *writer,
+ const ModelAction *reader) const
+{
+ if (promises->empty())
+ return true;
+ for(int i=promises->size()-1;i>=0;i--) {
+ ModelAction *pr=(*promises)[i]->get_reader(0);
+ //reader is after promise...doesn't cross any promise
+ if (*reader > *pr)
+ return true;
+ //writer is after promise, reader before...bad...
+ if (*writer > *pr)
+ return false;
+ }
+ return true;
+}
+
+/**
+ * @brief Add a future value to a reader
+ *
+ * This function performs a few additional checks to ensure that the future
+ * value can be feasibly observed by the reader
+ *
+ * @param writer The operation whose value is sent. Must be a write.
+ * @param reader The read operation which may read the future value. Must be a read.
+ */
void ModelChecker::add_future_value(const ModelAction *writer, ModelAction *reader)
{
/* Do more ambitious checks now that mo is more complete */
- if (mo_may_allow(writer, reader)) {
- Node *node = reader->get_node();
-
- /* Find an ancestor thread which exists at the time of the reader */
- Thread *write_thread = get_thread(writer);
- while (id_to_int(write_thread->get_id()) >= node->get_num_threads())
- write_thread = write_thread->get_parent();
-
- struct future_value fv = {
- writer->get_write_value(),
- writer->get_seq_number() + params.maxfuturedelay,
- write_thread->get_id(),
- };
- if (node->add_future_value(fv))
- set_latest_backtrack(reader);
- }
+ if (!mo_may_allow(writer, reader))
+ return;
+
+ Node *node = reader->get_node();
+
+ /* Find an ancestor thread which exists at the time of the reader */
+ Thread *write_thread = get_thread(writer);
+ while (id_to_int(write_thread->get_id()) >= node->get_num_threads())
+ write_thread = write_thread->get_parent();
+
+ struct future_value fv = {
+ writer->get_write_value(),
+ writer->get_seq_number() + params.maxfuturedelay,
+ write_thread->get_id(),
+ };
+ if (node->add_future_value(fv))
+ set_latest_backtrack(reader);
}
/**
/* Readers to which we may send our future value */
ModelVector<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);
+ bool updated_mod_order = w_modification_order(curr, &send_fv);
+ Promise *promise = pop_promise_to_resolve(curr);
+
+ if (promise) {
+ earliest_promise_reader = promise->get_reader(0);
+ updated_promises = resolve_promise(curr, promise);
} 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));
+
+ /* Don't send future values to reads after the Promise we resolve */
+ if (!earliest_promise_reader || *read < *earliest_promise_reader) {
+ /* Check if future value can be sent immediately */
+ if (promises_may_allow(curr, read)) {
+ add_future_value(curr, read);
+ } else {
+ futurevalues->push_back(PendingFutureValue(curr, read));
+ }
+ }
}
- if (promises->size() == 0) {
- for (unsigned int i = 0; i < futurevalues->size(); i++) {
- struct PendingFutureValue pfv = (*futurevalues)[i];
- add_future_value(pfv.writer, pfv.act);
+ /* Check the pending future values */
+ for (int i = (int)futurevalues->size() - 1; i >= 0; i--) {
+ struct PendingFutureValue pfv = (*futurevalues)[i];
+ if (promises_may_allow(pfv.writer, pfv.reader)) {
+ add_future_value(pfv.writer, pfv.reader);
+ futurevalues->erase(futurevalues->begin() + i);
}
- futurevalues->clear();
}
mo_graph->commitChanges();
* use in later synchronization
* fence-acquire (this function): search for hypothetical release
* sequences
+ * fence-seq-cst: MO constraints formed in {r,w}_modification_order
*/
bool updated = false;
if (curr->is_acquire()) {
get_safe_ptr_action(lock_waiters_map, curr->get_location())->push_back(curr);
return false;
}
- } else if (curr->get_type() == THREAD_JOIN) {
+ } else if (curr->is_thread_join()) {
Thread *blocking = (Thread *)curr->get_location();
if (!blocking->is_complete()) {
blocking->push_wait_list(curr);
/* Last SC fence in the current thread */
ModelAction *last_sc_fence_local = get_last_seq_cst_fence(curr->get_tid(), NULL);
+ ModelAction *last_sc_write = NULL;
+ if (curr->is_seqcst())
+ last_sc_write = get_last_seq_cst_write(curr);
/* Iterate over all threads */
for (i = 0; i < thrd_lists->size(); i++) {
for (rit = list->rbegin(); rit != list->rend(); rit++) {
ModelAction *act = *rit;
- if (act->is_write() && !act->equals(rf) && act != curr) {
+ /* Skip curr */
+ if (act == curr)
+ continue;
+ /* Don't want to add reflexive edges on 'rf' */
+ if (act->equals(rf)) {
+ if (act->happens_before(curr))
+ break;
+ else
+ continue;
+ }
+
+ if (act->is_write()) {
/* C++, Section 29.3 statement 5 */
if (curr->is_seqcst() && last_sc_fence_thread_local &&
*act < *last_sc_fence_thread_local) {
}
}
+ /* C++, Section 29.3 statement 3 (second subpoint) */
+ if (curr->is_seqcst() && last_sc_write && act == last_sc_write) {
+ added = mo_graph->addEdge(act, rf) || added;
+ break;
+ }
+
/*
* Include at most one act per-thread that "happens
- * before" curr. Don't consider reflexively.
+ * before" curr
*/
- if (act->happens_before(curr) && act != curr) {
+ if (act->happens_before(curr)) {
if (act->is_write()) {
- if (!act->equals(rf)) {
- added = mo_graph->addEdge(act, rf) || added;
- }
+ added = mo_graph->addEdge(act, rf) || added;
} else {
const ModelAction *prevrf = act->get_reads_from();
const Promise *prevrf_promise = act->get_reads_from_promise();
/** Arbitrary reads from the future are not allowed. Section 29.3
* part 9 places some constraints. This method checks one result of constraint
* constraint. Others require compiler support. */
-bool ModelChecker::thin_air_constraint_may_allow(const ModelAction *writer, const ModelAction *reader)
+bool ModelChecker::thin_air_constraint_may_allow(const ModelAction *writer, const ModelAction *reader) const
{
if (!writer->is_rmw())
return true;
action_list_t *list = get_safe_ptr_action(obj_map, location);
/* Find: max({i in dom(S) | seq_cst(t_i) && isWrite(t_i) && samevar(t_i, t)}) */
action_list_t::reverse_iterator rit;
- for (rit = list->rbegin(); rit != list->rend(); rit++)
- if ((*rit)->is_write() && (*rit)->is_seqcst() && (*rit) != curr)
+ for (rit = list->rbegin(); (*rit) != curr; rit++)
+ ;
+ rit++; /* Skip past curr */
+ for ( ; rit != list->rend(); rit++)
+ if ((*rit)->is_write() && (*rit)->is_seqcst())
return *rit;
return NULL;
}
}
/**
- * @brief Find the promise, if any to resolve for the current action
+ * @brief Find the promise (if any) to resolve for the current action and
+ * remove it from the pending promise vector
* @param curr The current ModelAction. Should be a write.
- * @return The (non-negative) index for the Promise to resolve, if any;
- * otherwise -1
+ * @return The Promise to resolve, if any; otherwise NULL
*/
-int ModelChecker::get_promise_to_resolve(const ModelAction *curr) const
+Promise * ModelChecker::pop_promise_to_resolve(const ModelAction *curr)
{
for (unsigned int i = 0; i < promises->size(); i++)
- if (curr->get_node()->get_promise(i))
- return i;
- return -1;
+ if (curr->get_node()->get_promise(i)) {
+ Promise *ret = (*promises)[i];
+ promises->erase(promises->begin() + i);
+ return ret;
+ }
+ return NULL;
}
/**
* Resolve a Promise with a current write.
* @param write The ModelAction that is fulfilling Promises
- * @param promise_idx The index corresponding to the promise
+ * @param promise The Promise to resolve
* @return True if the Promise was successfully resolved; false otherwise
*/
-bool ModelChecker::resolve_promise(ModelAction *write, unsigned int promise_idx)
+bool ModelChecker::resolve_promise(ModelAction *write, Promise *promise)
{
ModelVector<ModelAction *> actions_to_check;
- Promise *promise = (*promises)[promise_idx];
for (unsigned int i = 0; i < promise->get_num_readers(); i++) {
ModelAction *read = promise->get_reader(i);
if (!mo_graph->resolvePromise(promise, write))
priv->failed_promise = true;
- promises->erase(promises->begin() + promise_idx);
/**
* @todo It is possible to end up in an inconsistent state, where a
* "resolved" promise may still be referenced if
{
DBG();
Thread *old = thread_current();
+ scheduler->set_current_thread(NULL);
ASSERT(!old->get_pending());
old->set_pending(act);
if (Thread::swap(old, &system_context) < 0) {
Thread *thr = get_thread(tid);
if (!thr->is_model_thread() && !thr->is_complete() && !thr->get_pending()) {
switch_from_master(thr);
- if (is_circular_wait(thr))
+ if (thr->is_waiting_on(thr))
assert_bug("Deadlock detected");
}
}