cyclegraph: fixup support for dumping the modification order graph
[c11tester.git] / model.cc
index b6ef861ff78fb31a7a35fb4f5b2af8df9d71dd65..2b8ab3cc870a6f0fc9dae9ec5758f7b2e00f8092 100644 (file)
--- a/model.cc
+++ b/model.cc
@@ -721,7 +721,6 @@ bool ModelChecker::process_read(ModelAction *curr, bool second_part_of_rmw)
                                r_status = r_modification_order(curr, reads_from);
                        }
 
-
                        if (!second_part_of_rmw && is_infeasible() && (curr->get_node()->increment_read_from() || curr->get_node()->increment_future_value())) {
                                mo_graph->rollbackChanges();
                                priv->too_many_reads = false;
@@ -736,9 +735,13 @@ bool ModelChecker::process_read(ModelAction *curr, bool second_part_of_rmw)
                } else if (!second_part_of_rmw) {
                        /* Read from future value */
                        struct future_value fv = curr->get_node()->get_future_value();
+                       Promise *promise = new Promise(curr, fv);
                        value = fv.value;
-                       curr->set_read_from(NULL);
-                       promises->push_back(new Promise(curr, fv));
+                       curr->set_read_from_promise(promise);
+                       promises->push_back(promise);
+                       mo_graph->startChanges();
+                       updated = r_modification_order(curr, promise);
+                       mo_graph->commitChanges();
                }
                get_thread(curr)->set_return_value(value);
                return updated;
@@ -855,10 +858,22 @@ bool ModelChecker::process_mutex(ModelAction *curr)
 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) &&
-                       reader->get_node()->add_future_value(writer,
-                               writer->get_seq_number() + params.maxfuturedelay))
-               set_latest_backtrack(reader);
+       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_value(),
+                       writer->get_seq_number() + params.maxfuturedelay,
+                       write_thread->get_id(),
+               };
+               if (node->add_future_value(fv))
+                       set_latest_backtrack(reader);
+       }
 }
 
 /**
@@ -955,6 +970,12 @@ bool ModelChecker::process_thread_action(ModelAction *curr)
        case THREAD_CREATE: {
                Thread *th = curr->get_thread_operand();
                th->set_creation(curr);
+               /* Promises can be satisfied by children */
+               for (unsigned int i = 0; i < promises->size(); i++) {
+                       Promise *promise = (*promises)[i];
+                       if (promise->thread_is_available(curr->get_tid()))
+                               promise->add_thread(th->get_id());
+               }
                break;
        }
        case THREAD_JOIN: {
@@ -971,6 +992,13 @@ bool ModelChecker::process_thread_action(ModelAction *curr)
                        scheduler->wake(get_thread(act));
                }
                th->complete();
+               /* Completed thread can't satisfy promises */
+               for (unsigned int i = 0; i < promises->size(); i++) {
+                       Promise *promise = (*promises)[i];
+                       if (promise->thread_is_available(th->get_id()))
+                               if (promise->eliminate_thread(th->get_id()))
+                                       priv->failed_promise = true;
+               }
                updated = true; /* trigger rel-seq checks */
                break;
        }
@@ -1279,8 +1307,14 @@ ModelAction * ModelChecker::check_current_action(ModelAction *curr)
 
                        if (act->is_read()) {
                                const ModelAction *rf = act->get_reads_from();
-                               if (rf != NULL && r_modification_order(act, rf))
-                                       updated = true;
+                               const Promise *promise = act->get_reads_from_promise();
+                               if (rf) {
+                                       if (r_modification_order(act, rf))
+                                               updated = true;
+                               } else if (promise) {
+                                       if (r_modification_order(act, promise))
+                                               updated = true;
+                               }
                        }
                        if (act->is_write()) {
                                if (w_modification_order(act))
@@ -1347,8 +1381,6 @@ void ModelChecker::print_infeasibility(const char *prefix) const
 {
        char buf[100];
        char *ptr = buf;
-       if (mo_graph->checkForRMWViolation())
-               ptr += sprintf(ptr, "[RMW atomicity]");
        if (mo_graph->checkForCycles())
                ptr += sprintf(ptr, "[mo cycle]");
        if (priv->failed_promise)
@@ -1382,21 +1414,10 @@ bool ModelChecker::is_feasible_prefix_ignore_relseq() const
  */
 bool ModelChecker::is_infeasible() const
 {
-       return mo_graph->checkForRMWViolation() || is_infeasible_ignoreRMW();
-}
-
-/**
- * Check If the current partial trace is infeasible, while ignoring
- * infeasibility related to 2 RMW's reading from the same store. It does not
- * check end-of-execution feasibility.
- * @see ModelChecker::is_infeasible
- * @return whether the current partial trace is infeasible, ignoring multiple
- * RMWs reading from the same store.
- * */
-bool ModelChecker::is_infeasible_ignoreRMW() const
-{
-       return mo_graph->checkForCycles() || priv->failed_promise ||
-               priv->too_many_reads || priv->bad_synchronization ||
+       return mo_graph->checkForCycles() ||
+               priv->failed_promise ||
+               priv->too_many_reads ||
+               priv->bad_synchronization ||
                promises_expired();
 }
 
@@ -1404,8 +1425,11 @@ bool ModelChecker::is_infeasible_ignoreRMW() const
 ModelAction * ModelChecker::process_rmw(ModelAction *act) {
        ModelAction *lastread = get_last_action(act->get_tid());
        lastread->process_rmw(act);
-       if (act->is_rmw() && lastread->get_reads_from() != NULL) {
-               mo_graph->addRMWEdge(lastread->get_reads_from(), lastread);
+       if (act->is_rmw()) {
+               if (lastread->get_reads_from())
+                       mo_graph->addRMWEdge(lastread->get_reads_from(), lastread);
+               else
+                       mo_graph->addRMWEdge(lastread->get_reads_from_promise(), lastread);
                mo_graph->commitChanges();
        }
        return lastread;
@@ -1509,7 +1533,7 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf)
  * read.
  *
  * Basic idea is the following: Go through each other thread and find
- * the lastest action that happened before our read.  Two cases:
+ * the last action that happened before our read.  Two cases:
  *
  * (1) The action is a write => that write must either occur before
  * the write we read from or be the write we read from.
@@ -1518,10 +1542,11 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf)
  * must occur before the write we read from or be the same write.
  *
  * @param curr The current action. Must be a read.
- * @param rf The action that curr reads from. Must be a write.
+ * @param rf The ModelAction or Promise that curr reads from. Must be a write.
  * @return True if modification order edges were added; false otherwise
  */
-bool ModelChecker::r_modification_order(ModelAction *curr, const ModelAction *rf)
+template <typename rf_type>
+bool ModelChecker::r_modification_order(ModelAction *curr, const rf_type *rf)
 {
        std::vector<action_list_t> *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, curr->get_location());
        unsigned int i;
@@ -1549,26 +1574,23 @@ bool ModelChecker::r_modification_order(ModelAction *curr, const ModelAction *rf
                for (rit = list->rbegin(); rit != list->rend(); rit++) {
                        ModelAction *act = *rit;
 
-                       if (act->is_write() && act != rf && act != curr) {
+                       if (act->is_write() && !act->equals(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;
+                                       added = mo_graph->addEdge(act, rf) || added;
                                        break;
                                }
                                /* 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;
+                                       added = mo_graph->addEdge(act, rf) || added;
                                        break;
                                }
                                /* 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;
+                                       added = mo_graph->addEdge(act, rf) || added;
                                        break;
                                }
                        }
@@ -1579,9 +1601,8 @@ bool ModelChecker::r_modification_order(ModelAction *curr, const ModelAction *rf
                         */
                        if (act->happens_before(curr) && act != curr) {
                                if (act->is_write()) {
-                                       if (rf != act) {
-                                               mo_graph->addEdge(act, rf);
-                                               added = true;
+                                       if (!act->equals(rf)) {
+                                               added = mo_graph->addEdge(act, rf) || added;
                                        }
                                } else {
                                        const ModelAction *prevreadfrom = act->get_reads_from();
@@ -1589,9 +1610,8 @@ bool ModelChecker::r_modification_order(ModelAction *curr, const ModelAction *rf
                                        if (prevreadfrom == NULL)
                                                continue;
 
-                                       if (rf != prevreadfrom) {
-                                               mo_graph->addEdge(prevreadfrom, rf);
-                                               added = true;
+                                       if (!prevreadfrom->equals(rf)) {
+                                               added = mo_graph->addEdge(prevreadfrom, rf) || added;
                                        }
                                }
                                break;
@@ -1602,70 +1622,6 @@ bool ModelChecker::r_modification_order(ModelAction *curr, const ModelAction *rf
        return added;
 }
 
-/** This method fixes up the modification order when we resolve a
- *  promises.  The basic problem is that actions that occur after the
- *  read curr could not property add items to the modification order
- *  for our read.
- *
- *  So for each thread, we find the earliest item that happens after
- *  the read curr.  This is the item we have to fix up with additional
- *  constraints.  If that action is write, we add a MO edge between
- *  the Action rf and that action.  If the action is a read, we add a
- *  MO edge between the Action rf, and whatever the read accessed.
- *
- * @param curr is the read ModelAction that we are fixing up MO edges for.
- * @param rf is the write ModelAction that curr reads from.
- *
- */
-void ModelChecker::post_r_modification_order(ModelAction *curr, const ModelAction *rf)
-{
-       std::vector<action_list_t> *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, curr->get_location());
-       unsigned int i;
-       ASSERT(curr->is_read());
-
-       /* Iterate over all threads */
-       for (i = 0; i < thrd_lists->size(); i++) {
-               /* Iterate over actions in thread, starting from most recent */
-               action_list_t *list = &(*thrd_lists)[i];
-               action_list_t::reverse_iterator rit;
-               ModelAction *lastact = NULL;
-
-               /* Find last action that happens after curr that is either not curr or a rmw */
-               for (rit = list->rbegin(); rit != list->rend(); rit++) {
-                       ModelAction *act = *rit;
-                       if (curr->happens_before(act) && (curr != act || curr->is_rmw())) {
-                               lastact = act;
-                       } else
-                               break;
-               }
-
-                       /* Include at most one act per-thread that "happens before" curr */
-               if (lastact != NULL) {
-                       if (lastact == curr) {
-                               //Case 1: The resolved read is a RMW, and we need to make sure
-                               //that the write portion of the RMW mod order after rf
-
-                               mo_graph->addEdge(rf, lastact);
-                       } else if (lastact->is_read()) {
-                               //Case 2: The resolved read is a normal read and the next
-                               //operation is a read, and we need to make sure the value read
-                               //is mod ordered after rf
-
-                               const ModelAction *postreadfrom = lastact->get_reads_from();
-                               if (postreadfrom != NULL && rf != postreadfrom)
-                                       mo_graph->addEdge(rf, postreadfrom);
-                       } else {
-                               //Case 3: The resolved read is a normal read and the next
-                               //operation is a write, and we need to make sure that the
-                               //write is mod ordered after rf
-                               if (lastact != rf)
-                                       mo_graph->addEdge(rf, lastact);
-                       }
-                       break;
-               }
-       }
-}
-
 /**
  * Updates the mo_graph with the constraints imposed from the current write.
  *
@@ -1700,8 +1656,7 @@ bool ModelChecker::w_modification_order(ModelAction *curr)
                         so we are initialized. */
                ModelAction *last_seq_cst = get_last_seq_cst_write(curr);
                if (last_seq_cst != NULL) {
-                       mo_graph->addEdge(last_seq_cst, curr);
-                       added = true;
+                       added = mo_graph->addEdge(last_seq_cst, curr) || added;
                }
        }
 
@@ -1744,8 +1699,7 @@ bool ModelChecker::w_modification_order(ModelAction *curr)
                        /* 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;
+                               added = mo_graph->addEdge(act, curr) || added;
                                break;
                        }
 
@@ -1761,14 +1715,13 @@ bool ModelChecker::w_modification_order(ModelAction *curr)
                                 *   readfrom(act) --mo--> act
                                 */
                                if (act->is_write())
-                                       mo_graph->addEdge(act, curr);
+                                       added = mo_graph->addEdge(act, curr) || added;
                                else if (act->is_read()) {
                                        //if previous read accessed a null, just keep going
                                        if (act->get_reads_from() == NULL)
                                                continue;
-                                       mo_graph->addEdge(act->get_reads_from(), curr);
+                                       added = mo_graph->addEdge(act->get_reads_from(), curr) || added;
                                }
-                               added = true;
                                break;
                        } else if (act->is_read() && !act->could_synchronize_with(curr) &&
                                                     !act->same_thread(curr)) {
@@ -1785,15 +1738,24 @@ bool ModelChecker::w_modification_order(ModelAction *curr)
 
                                 */
                                if (thin_air_constraint_may_allow(curr, act)) {
-                                       if (!is_infeasible() ||
-                                                       (curr->is_rmw() && act->is_rmw() && curr->get_reads_from() == act->get_reads_from() && !is_infeasible_ignoreRMW())) {
+                                       if (!is_infeasible())
                                                futurevalues->push_back(PendingFutureValue(curr, 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);
                                }
                        }
                }
        }
 
+       /*
+        * All compatible, thread-exclusive promises must be ordered after any
+        * concrete stores to the same thread, or else they can be merged with
+        * this store later
+        */
+       for (unsigned int i = 0; i < promises->size(); i++)
+               if ((*promises)[i]->is_compatible_exclusive(curr))
+                       added = mo_graph->addEdge(curr, (*promises)[i]) || added;
+
        return added;
 }
 
@@ -2308,44 +2270,44 @@ ClockVector * ModelChecker::get_cv(thread_id_t tid) const
  */
 bool ModelChecker::resolve_promises(ModelAction *write)
 {
-       bool resolved = false;
+       bool haveResolved = false;
        std::vector< ModelAction *, ModelAlloc<ModelAction *> > actions_to_check;
+       promise_list_t mustResolve, resolved;
 
        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();
-                       if (read->is_rmw()) {
-                               mo_graph->addRMWEdge(write, read);
-                       }
                        read_from(read, write);
-                       //First fix up the modification order for actions that happened
-                       //before the read
-                       r_modification_order(read, write);
-                       //Next fix up the modification order for actions that happened
-                       //after the read.
-                       post_r_modification_order(read, write);
                        //Make sure the promise's value matches the write's value
-                       ASSERT(promise->get_value() == write->get_value());
-                       delete(promise);
+                       ASSERT(promise->is_compatible(write));
+                       mo_graph->resolvePromise(read, write, &mustResolve);
 
+                       resolved.push_back(promise);
                        promises->erase(promises->begin() + promise_index);
                        actions_to_check.push_back(read);
 
-                       resolved = true;
+                       haveResolved = true;
                } else
                        promise_index++;
        }
 
+       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
 
        for (unsigned int i = 0; i < actions_to_check.size(); i++) {
-               ModelAction *read=actions_to_check[i];
+               ModelAction *read = actions_to_check[i];
                mo_check_promises(read->get_tid(), write, read);
        }
 
-       return resolved;
+       return haveResolved;
 }
 
 /**
@@ -2462,7 +2424,7 @@ void ModelChecker::mo_check_promises(thread_id_t tid, const ModelAction *write,
                }
 
                // Don't do any lookups twice for the same thread
-               if (promise->thread_is_eliminated(tid))
+               if (!promise->thread_is_available(tid))
                        continue;
 
                if (promise->get_write() && mo_graph->checkReachable(promise->get_write(), write)) {
@@ -2615,7 +2577,7 @@ void ModelChecker::dumpGraph(char *filename) const
        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=\"%u, T%u\"];\n", action->get_seq_number(), action->get_seq_number(), action->get_tid());
+                       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());
                }
@@ -2635,7 +2597,6 @@ void ModelChecker::dumpGraph(char *filename) const
 void ModelChecker::print_summary() const
 {
 #if SUPPORT_MOD_ORDER_DUMP
-       scheduler->print();
        char buffername[100];
        sprintf(buffername, "exec%04u", stats.num_total);
        mo_graph->dumpGraphToFile(buffername);
@@ -2687,7 +2648,7 @@ Thread * ModelChecker::get_thread(thread_id_t tid) const
  * @param act The ModelAction
  * @return A Thread reference
  */
-Thread * ModelChecker::get_thread(ModelAction *act) const
+Thread * ModelChecker::get_thread(const ModelAction *act) const
 {
        return get_thread(act->get_tid());
 }