/**
* Processes a read or rmw model action.
* @param curr is the read model action to process.
- * @param th is the thread
* @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, Thread * th, bool second_part_of_rmw) {
+bool ModelChecker::process_read(ModelAction *curr, bool second_part_of_rmw)
+{
uint64_t value;
bool updated = false;
while (true) {
bool r_status = false;
if (!second_part_of_rmw) {
- check_recency(curr,false);
+ check_recency(curr);
r_status = r_modification_order(curr, reads_from);
}
Promise *valuepromise = new Promise(curr, value, expiration);
promises->push_back(valuepromise);
}
- th->set_return_value(value);
+ get_thread(curr)->set_return_value(value);
return updated;
}
}
+/**
+ * Process a write ModelAction
+ * @param curr The ModelAction to process
+ * @return True if the mo_graph was updated or promises were resolved
+ */
+bool ModelChecker::process_write(ModelAction *curr)
+{
+ bool updated_mod_order = w_modification_order(curr);
+ bool updated_promises = resolve_promises(curr);
+
+ if (promises->size() == 0) {
+ for (unsigned int i = 0; i<futurevalues->size(); i++) {
+ struct PendingFutureValue pfv = (*futurevalues)[i];
+ if (pfv.act->get_node()->add_future_value(pfv.value, pfv.expiration) &&
+ (!priv->next_backtrack || *pfv.act > *priv->next_backtrack))
+ priv->next_backtrack = pfv.act;
+ }
+ futurevalues->resize(0);
+ }
+
+ mo_graph->commitChanges();
+ get_thread(curr)->set_return_value(VALUE_NONE);
+ return updated_mod_order || updated_promises;
+}
+
+ModelAction * ModelChecker::initialize_curr_action(ModelAction *curr)
+{
+ ModelAction *newcurr;
+
+ if (curr->is_rmwc() || curr->is_rmw()) {
+ newcurr = process_rmw(curr);
+ delete curr;
+ compute_promises(newcurr);
+ return newcurr;
+ }
+
+ newcurr = node_stack->explore_action(curr);
+ if (newcurr) {
+ /* First restore type and order in case of RMW operation */
+ if (curr->is_rmwr())
+ newcurr->copy_typeandorder(curr);
+
+ /* Discard duplicate ModelAction; use action from NodeStack */
+ delete curr;
+
+ /* If we have diverged, we need to reset the clock vector. */
+ if (diverge == NULL)
+ newcurr->create_cv(get_parent_action(newcurr->get_tid()));
+ } else {
+ newcurr = curr;
+ /*
+ * Perform one-time actions when pushing new ModelAction onto
+ * NodeStack
+ */
+ curr->create_cv(get_parent_action(curr->get_tid()));
+ if (curr->is_write())
+ compute_promises(curr);
+ }
+ return newcurr;
+}
+
/**
* This is the heart of the model checker routine. It performs model-checking
* actions corresponding to a given "current action." Among other processes, it
*/
Thread * ModelChecker::check_current_action(ModelAction *curr)
{
- bool second_part_of_rmw = false;
-
ASSERT(curr);
- if (curr->is_rmwc() || curr->is_rmw()) {
- ModelAction *tmp = process_rmw(curr);
- second_part_of_rmw = true;
- delete curr;
- curr = tmp;
- compute_promises(curr);
- } else {
- ModelAction *tmp = node_stack->explore_action(curr);
- if (tmp) {
- /* Discard duplicate ModelAction; use action from NodeStack */
- /* First restore type and order in case of RMW operation */
- if (curr->is_rmwr())
- tmp->copy_typeandorder(curr);
-
- /* If we have diverged, we need to reset the clock vector. */
- if (diverge == NULL)
- tmp->create_cv(get_parent_action(tmp->get_tid()));
-
- delete curr;
- curr = tmp;
- } else {
- /*
- * Perform one-time actions when pushing new ModelAction onto
- * NodeStack
- */
- curr->create_cv(get_parent_action(curr->get_tid()));
- /* Build may_read_from set */
- if (curr->is_read())
- build_reads_from_past(curr);
- if (curr->is_write())
- compute_promises(curr);
- }
- }
+ bool second_part_of_rmw = curr->is_rmwc() || curr->is_rmw();
+
+ ModelAction *newcurr = initialize_curr_action(curr);
+
+ /* Build may_read_from set for newly-created actions */
+ if (curr == newcurr && curr->is_read())
+ build_reads_from_past(curr);
+ curr = newcurr;
/* Thread specific actions */
switch (curr->get_type()) {
break;
}
- bool updated = false;
+ /* Add current action to lists before work_queue loop */
+ if (!second_part_of_rmw)
+ add_action_to_lists(curr);
- if (curr->is_read()) {
- updated = process_read(curr, get_thread(curr), second_part_of_rmw);
- }
+ work_queue_t work_queue(1, CheckCurrWorkEntry(curr));
- if (curr->is_write()) {
- bool updated_mod_order = w_modification_order(curr);
- bool updated_promises = resolve_promises(curr);
- updated = updated || updated_mod_order || updated_promises;
-
- if (promises->size()==0) {
- for (unsigned int i = 0; i<futurevalues->size(); i++) {
- struct PendingFutureValue pfv=(*futurevalues)[i];
- if (pfv.act->get_node()->add_future_value(pfv.value, pfv.expiration) &&
- (!priv->next_backtrack || *pfv.act > *priv->next_backtrack))
- priv->next_backtrack = pfv.act;
- }
- futurevalues->resize(0);
- }
+ while (!work_queue.empty()) {
+ WorkQueueEntry work = work_queue.front();
+ work_queue.pop_front();
- mo_graph->commitChanges();
- get_thread(curr)->set_return_value(VALUE_NONE);
- }
+ switch (work.type) {
+ case WORK_CHECK_CURR_ACTION: {
+ ModelAction *act = work.action;
+ bool updated = false;
+ if (act->is_read() && process_read(act, second_part_of_rmw))
+ updated = true;
- if (updated)
- resolve_release_sequences(curr->get_location());
+ if (act->is_write() && process_write(act))
+ updated = true;
- /* Add action to list. */
- if (!second_part_of_rmw)
- add_action_to_lists(curr);
+ if (updated)
+ work_queue.push_back(CheckRelSeqWorkEntry(act->get_location()));
+ break;
+ }
+ case WORK_CHECK_RELEASE_SEQ:
+ resolve_release_sequences(work.location, &work_queue);
+ break;
+ case WORK_CHECK_MO_EDGES: {
+ /** @todo Complete verification of work_queue */
+ ModelAction *act = work.action;
+ bool updated = false;
+
+ if (act->is_read()) {
+ if (r_modification_order(act, act->get_reads_from()))
+ updated = true;
+ }
+ if (act->is_write()) {
+ if (w_modification_order(act))
+ updated = true;
+ }
+
+ if (updated)
+ work_queue.push_back(CheckRelSeqWorkEntry(act->get_location()));
+ break;
+ }
+ default:
+ ASSERT(false);
+ break;
+ }
+ }
check_curr_backtracking(curr);
/** @returns whether the current partial trace is feasible other than
* multiple RMW reading from the same store. */
bool ModelChecker::isfeasibleotherthanRMW() {
+ if (DBG_ENABLED()) {
+ if (mo_graph->checkForCycles())
+ DEBUG("Infeasible: modification order cycles\n");
+ if (failed_promise)
+ DEBUG("Infeasible: failed promise\n");
+ if (too_many_reads)
+ DEBUG("Infeasible: too many reads\n");
+ if (promises_expired())
+ DEBUG("Infeasible: promises expired\n");
+ }
return !mo_graph->checkForCycles() && !failed_promise && !too_many_reads && !promises_expired();
}
/** Returns whether the current completed trace is feasible. */
bool ModelChecker::isfinalfeasible() {
+ if (DBG_ENABLED() && promises->size() != 0)
+ DEBUG("Infeasible: unrevolved promises\n");
+
return isfeasible() && promises->size() == 0;
}
*
* If so, we decide that the execution is no longer feasible.
*/
-void ModelChecker::check_recency(ModelAction *curr, bool already_added) {
+void ModelChecker::check_recency(ModelAction *curr) {
if (params.maxreads != 0) {
if (curr->get_node()->get_read_from_size() <= 1)
return;
action_list_t::reverse_iterator rit = list->rbegin();
/* Skip past curr */
- if (already_added) {
- for (; (*rit) != curr; rit++)
- ;
- /* go past curr now */
- rit++;
- }
+ 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
for (rit = list->rbegin(); rit != list->rend(); rit++) {
ModelAction *act = *rit;
- /* Include at most one act per-thread that "happens before" curr */
- if (act->happens_before(curr)) {
+ /*
+ * Include at most one act per-thread that "happens
+ * before" curr. Don't consider reflexively.
+ */
+ if (act->happens_before(curr) && act != curr) {
if (act->is_write()) {
- if (rf != act && act != curr) {
+ if (rf != act) {
mo_graph->addEdge(act, rf);
added = true;
}
added = true;
}
}
-
break;
}
}
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->get_location());
+ ModelAction *last_seq_cst = get_last_seq_cst(curr);
if (last_seq_cst != NULL) {
mo_graph->addEdge(last_seq_cst, curr);
added = true;
action_list_t::reverse_iterator rit;
for (rit = list->rbegin(); rit != list->rend(); rit++) {
ModelAction *act = *rit;
+ if (act == curr) {
+ /*
+ * If RMW, we already have all relevant edges,
+ * so just skip to next thread.
+ * If normal write, we need to look at earlier
+ * actions, so continue processing list.
+ */
+ if (curr->is_rmw())
+ break;
+ else
+ continue;
+ }
- /* Include at most one act per-thread that "happens before" curr */
+ /*
+ * Include at most one act per-thread that "happens
+ * before" curr
+ */
if (act->happens_before(curr)) {
/*
* Note: if act is RMW, just add edge:
* The following edge should be handled elsewhere:
* readfrom(act) --mo--> act
*/
- if (act->is_write()) {
- //RMW shouldn't have an edge to themselves
- if (act!=curr)
- mo_graph->addEdge(act, curr);
- } else if (act->is_read() && act->get_reads_from() != NULL)
+ if (act->is_write())
+ mo_graph->addEdge(act, curr);
+ else if (act->is_read() && act->get_reads_from() != NULL)
mo_graph->addEdge(act->get_reads_from(), curr);
added = true;
break;
*
* @param location The location/object that should be checked for release
* sequence resolutions
- * @return True if any updates occurred (new synchronization, new mo_graph edges)
+ * @param work_queue The work queue to which to add work items as they are
+ * generated
+ * @return True if any updates occurred (new synchronization, new mo_graph
+ * edges)
*/
-bool ModelChecker::resolve_release_sequences(void *location)
+bool ModelChecker::resolve_release_sequences(void *location, work_queue_t *work_queue)
{
std::list<ModelAction *> *list;
list = lazy_sync_with_release->getptr(location);
}
if (updated) {
+ /* Re-check act for mo_graph edges */
+ work_queue->push_back(MOEdgeWorkEntry(act));
+
/* propagate synchronization to later actions */
action_list_t::reverse_iterator it = action_trace->rbegin();
while ((*it) != act) {
ModelAction *propagate = *it;
- if (act->happens_before(propagate))
- /** @todo new mo_graph edges along with
- * this synchronization? */
+ if (act->happens_before(propagate)) {
propagate->synchronize_with(act);
+ /* Re-check 'propagate' for mo_graph edges */
+ work_queue->push_back(MOEdgeWorkEntry(propagate));
+ }
}
}
if (complete) {
}
/**
- * Gets the last memory_order_seq_cst action (in the total global sequence)
- * performed on a particular object (i.e., memory location).
- * @param location The object location to check
- * @return The last seq_cst action performed
+ * 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
+ * current action.
+ * @param curr The current ModelAction; also denotes the object location to
+ * check
+ * @return The last seq_cst write
*/
-ModelAction * ModelChecker::get_last_seq_cst(const void *location)
+ModelAction * ModelChecker::get_last_seq_cst(ModelAction *curr)
{
+ void *location = curr->get_location();
action_list_t *list = obj_map->get_safe_ptr(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())
+ if ((*rit)->is_write() && (*rit)->is_seqcst() && (*rit) != curr)
return *rit;
return NULL;
}
bool initialized = false;
if (curr->is_seqcst()) {
- last_seq_cst = get_last_seq_cst(curr->get_location());
+ last_seq_cst = get_last_seq_cst(curr);
/* We have to at least see the last sequentially consistent write,
so we are initialized. */
if (last_seq_cst != NULL)
ModelAction *act = *rit;
/* Only consider 'write' actions */
- if (!act->is_write())
+ if (!act->is_write() || act == curr)
continue;
/* Don't consider more than one seq_cst write if we are a seq_cst read. */