Merge branch 'fences'
authorBrian Norris <banorris@uci.edu>
Tue, 12 Feb 2013 19:48:41 +0000 (11:48 -0800)
committerBrian Norris <banorris@uci.edu>
Tue, 12 Feb 2013 19:48:41 +0000 (11:48 -0800)
24 files changed:
.gitignore
Doxyfile
Makefile
action.cc
action.h
cyclegraph.cc
cyclegraph.h
model.cc
model.h
nodestack.cc
nodestack.h
promise.cc
promise.h
schedule.cc
schedule.h
test/Makefile
test/litmus/Makefile [new file with mode: 0644]
test/litmus/iriw.cc [new file with mode: 0644]
test/litmus/load-buffer.cc [new file with mode: 0644]
test/litmus/message-passing.cc [new file with mode: 0644]
test/litmus/seq-lock.cc [new file with mode: 0644]
test/litmus/store-buffer.cc [new file with mode: 0644]
test/litmus/wrc.cc [new file with mode: 0644]
test/rmwprog.c

index d7ca89a59497416520a82aeecc1a99a0ce05a2a1..4acd010bd5ab045daccae219f1063c3cb33a1717 100644 (file)
@@ -6,6 +6,7 @@
 *~
 *.dot
 .*.d
+*.pdf
 
 # files in this directory
 /tags
index bed3686402a045023b006d5a4dba4a55073518de..bb09915399dea5da3bddc8549e65c516a3babbc3 100644 (file)
--- a/Doxyfile
+++ b/Doxyfile
@@ -685,7 +685,7 @@ FILE_PATTERNS          =
 # should be searched for input files as well. Possible values are YES and NO.
 # If left blank NO is used.
 
-RECURSIVE              = YES
+RECURSIVE              = NO
 
 # The EXCLUDE tag can be used to specify files and/or directories that should be
 # excluded from the INPUT source files. This way you can easily exclude a
@@ -693,7 +693,7 @@ RECURSIVE              = YES
 # Note that relative paths are relative to the directory from which doxygen is
 # run.
 
-EXCLUDE                = malloc.c doc/ benchmarks/ test/
+EXCLUDE                = malloc.c
 
 # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
 # directories that are symbolic links (a Unix file system feature) are excluded
index 1cc4f4baa9cb1054f34b5ba2497bb32c175aa87f..ea73d93d7901e7b78d2a81b8aab1c54924eb68f9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -35,11 +35,14 @@ malloc.o: malloc.c
 %.o: %.cc
        $(CXX) -MMD -MF .$@.d -fPIC -c $< $(CPPFLAGS)
 
+%.pdf: %.dot
+       dot -Tpdf $< -o $@
+
 -include $(OBJECTS:%=.%.d)
 
 PHONY += clean
 clean:
-       rm -f *.o *.so .*.d
+       rm -f *.o *.so .*.d *.pdf *.dot
        $(MAKE) -C $(TESTS_DIR) clean
 
 PHONY += mrclean
@@ -66,4 +69,7 @@ benchmarks: $(LIB_SO)
        fi
        $(MAKE) -C $(BENCH_DIR)
 
+PHONY += pdfs
+pdfs: $(patsubst %.dot,%.pdf,$(wildcard *.dot))
+
 .PHONY: $(PHONY)
index 6a545d82a0badac66c25eb58cd124de6d8f892f0..d5eb581a23d4e7ec5b7ffe3aefeff54fb263ae94 100644 (file)
--- a/action.cc
+++ b/action.cc
@@ -31,6 +31,7 @@ ModelAction::ModelAction(action_type_t type, memory_order order, void *loc,
        location(loc),
        value(value),
        reads_from(NULL),
+       reads_from_promise(NULL),
        last_fence_release(NULL),
        node(NULL),
        seq_number(ACTION_INITIAL_CLOCK),
@@ -391,10 +392,22 @@ Node * ModelAction::get_node() const
 void ModelAction::set_read_from(const ModelAction *act)
 {
        reads_from = act;
+       reads_from_promise = NULL;
        if (act && act->is_uninitialized())
                model->assert_bug("May read from uninitialized atomic\n");
 }
 
+/**
+ * Set this action's read-from promise
+ * @param promise The promise to read from
+ */
+void ModelAction::set_read_from_promise(const Promise *promise)
+{
+       ASSERT(is_read());
+       reads_from_promise = promise;
+       reads_from = NULL;
+}
+
 /**
  * Synchronize the current thread with the thread corresponding to the
  * ModelAction parameter.
@@ -496,8 +509,10 @@ void ModelAction::print() const
        }
 
        uint64_t valuetoprint;
-       if (type == ATOMIC_READ && reads_from != NULL)
+       if (is_read() && reads_from)
                valuetoprint = reads_from->value;
+       else if (is_read() && reads_from_promise)
+               valuetoprint = reads_from_promise->get_value();
        else
                valuetoprint = value;
 
@@ -540,7 +555,7 @@ void ModelAction::print() const
                model_print("\n");
 }
 
-/** @brief Print nicely-formatted info about this ModelAction */
+/** @brief Get a (likely) unique hash for this ModelAction */
 unsigned int ModelAction::hash() const
 {
        unsigned int hash = (unsigned int)this->type;
index f742ddc62854a79fd2f2aff3721914781f115b36..c39af5ecdb177aff649eb79ba39eb3eff1b3253c 100644 (file)
--- a/action.h
+++ b/action.h
@@ -15,6 +15,7 @@
 
 class ClockVector;
 class Thread;
+class Promise;
 
 using std::memory_order;
 using std::memory_order_relaxed;
@@ -80,11 +81,13 @@ public:
        modelclock_t get_seq_number() const { return seq_number; }
        uint64_t get_value() const { return value; }
        const ModelAction * get_reads_from() const { return reads_from; }
+       const Promise * get_reads_from_promise() const { return reads_from_promise; }
 
        Node * get_node() const;
        void set_node(Node *n) { node = n; }
 
        void set_read_from(const ModelAction *act);
+       void set_read_from_promise(const Promise *promise);
 
        /** Store the most recent fence-release from the same thread
         *  @param fence The fence-release that occured prior to this */
@@ -148,6 +151,8 @@ public:
        bool get_sleep_flag() { return sleep_flag; }
        unsigned int hash() const;
 
+       bool equals(const ModelAction *x) const { return this == x; }
+       bool equals(const Promise *x) const { return false; }
        MEMALLOC
 private:
 
@@ -169,6 +174,9 @@ private:
        /** The action that this action reads from. Only valid for reads */
        const ModelAction *reads_from;
 
+       /** The promise that this action reads from. Only valid for reads */
+       const Promise *reads_from_promise;
+
        /** The last fence release from the same thread */
        const ModelAction *last_fence_release;
 
index 8326f28b7ee3a2c3be6332fc383f959707040eac..44f5a28bc15b69cc3289a4906b298341845ae324 100644 (file)
@@ -8,9 +8,7 @@
 CycleGraph::CycleGraph() :
        discovered(new HashTable<const CycleNode *, const CycleNode *, uintptr_t, 4, model_malloc, model_calloc, model_free>(16)),
        hasCycles(false),
-       oldCycles(false),
-       hasRMWViolation(false),
-       oldRMWViolation(false)
+       oldCycles(false)
 {
 }
 
@@ -33,14 +31,62 @@ void CycleGraph::putNode(const ModelAction *act, CycleNode *node)
 #endif
 }
 
+/**
+ * Add a CycleNode to the graph, corresponding to a Promise
+ * @param promise The Promise that should be added
+ * @param node The CycleNode that corresponds to the Promise
+ */
+void CycleGraph::putNode(const Promise *promise, CycleNode *node)
+{
+       const ModelAction *reader = promise->get_action();
+       readerToPromiseNode.put(reader, node);
+#if SUPPORT_MOD_ORDER_DUMP
+       nodeList.push_back(node);
+#endif
+}
+
+/**
+ * @brief Remove the Promise node from the graph
+ * @param promise The promise to remove from the graph
+ */
+void CycleGraph::erasePromiseNode(const Promise *promise)
+{
+       const ModelAction *reader = promise->get_action();
+       readerToPromiseNode.put(reader, NULL);
+#if SUPPORT_MOD_ORDER_DUMP
+       /* Remove the promise node from nodeList */
+       CycleNode *node = getNode_noCreate(promise);
+       for (unsigned int i = 0; i < nodeList.size(); )
+               if (nodeList[i] == node)
+                       nodeList.erase(nodeList.begin() + i);
+               else
+                       i++;
+#endif
+}
+
+/** @return The corresponding CycleNode, if exists; otherwise NULL */
+CycleNode * CycleGraph::getNode_noCreate(const ModelAction *act) const
+{
+       return actionToNode.get(act);
+}
+
+/** @return The corresponding CycleNode, if exists; otherwise NULL */
+CycleNode * CycleGraph::getNode_noCreate(const Promise *promise) const
+{
+       return readerToPromiseNode.get(promise->get_action());
+}
+
 /**
  * @brief Returns the CycleNode corresponding to a given ModelAction
+ *
+ * Gets (or creates, if none exist) a CycleNode corresponding to a ModelAction
+ *
  * @param action The ModelAction to find a node for
  * @return The CycleNode paired with this action
  */
 CycleNode * CycleGraph::getNode(const ModelAction *action)
 {
-       CycleNode *node = actionToNode.get(action);
+       CycleNode *node = getNode_noCreate(action);
        if (node == NULL) {
                node = new CycleNode(action);
                putNode(action, node);
@@ -49,51 +95,161 @@ CycleNode * CycleGraph::getNode(const ModelAction *action)
 }
 
 /**
- * Adds an edge between two ModelActions. The ModelAction @a to is ordered
- * after the ModelAction @a from.
- * @param to The edge points to this ModelAction
- * @param from The edge comes from this ModelAction
+ * @brief Returns a CycleNode corresponding to a promise
+ *
+ * Gets (or creates, if none exist) a CycleNode corresponding to a promised
+ * value.
+ *
+ * @param promise The Promise generated by a reader
+ * @return The CycleNode corresponding to the Promise
  */
-void CycleGraph::addEdge(const ModelAction *from, const ModelAction *to)
+CycleNode * CycleGraph::getNode(const Promise *promise)
 {
-       ASSERT(from);
-       ASSERT(to);
+       CycleNode *node = getNode_noCreate(promise);
+       if (node == NULL) {
+               node = new CycleNode(promise);
+               putNode(promise, node);
+       }
+       return node;
+}
 
-       CycleNode *fromnode = getNode(from);
-       CycleNode *tonode = getNode(to);
+/**
+ * @return false if the resolution results in a cycle; true otherwise
+ */
+bool CycleGraph::resolvePromise(ModelAction *reader, ModelAction *writer,
+               promise_list_t *mustResolve)
+{
+       CycleNode *promise_node = readerToPromiseNode.get(reader);
+       CycleNode *w_node = actionToNode.get(writer);
+       ASSERT(promise_node);
+
+       if (w_node)
+               return mergeNodes(w_node, promise_node, mustResolve);
+       /* No existing write-node; just convert the promise-node */
+       promise_node->resolvePromise(writer);
+       erasePromiseNode(promise_node->getPromise());
+       putNode(writer, promise_node);
+       return true;
+}
+
+/**
+ * @brief Merge two CycleNodes that represent the same write
+ *
+ * Note that this operation cannot be rolled back.
+ *
+ * @param w_node The write ModelAction node with which to merge
+ * @param p_node The Promise node to merge. Will be destroyed after this
+ * function.
+ * @param mustMerge Return (pass-by-reference) any additional Promises that
+ * must also be merged with w_node
+ *
+ * @return false if the merge results in a cycle; true otherwise
+ */
+bool CycleGraph::mergeNodes(CycleNode *w_node, CycleNode *p_node,
+               promise_list_t *mustMerge)
+{
+       ASSERT(!w_node->is_promise());
+       ASSERT(p_node->is_promise());
+
+       const Promise *promise = p_node->getPromise();
+       if (!promise->is_compatible(w_node->getAction())) {
+               hasCycles = true;
+               return false;
+       }
+
+       /* Transfer the RMW */
+       CycleNode *promise_rmw = p_node->getRMW();
+       if (promise_rmw && promise_rmw != w_node->getRMW() && w_node->setRMW(promise_rmw)) {
+               hasCycles = true;
+               return false;
+       }
+
+       /* Transfer back edges to w_node */
+       while (p_node->getNumBackEdges() > 0) {
+               CycleNode *back = p_node->removeBackEdge();
+               if (back == w_node)
+                       continue;
+               if (back->is_promise()) {
+                       if (checkReachable(w_node, back)) {
+                               /* Edge would create cycle; merge instead */
+                               mustMerge->push_back(back->getPromise());
+                               if (!mergeNodes(w_node, back, mustMerge))
+                                       return false;
+                       } else
+                               back->addEdge(w_node);
+               } else
+                       addNodeEdge(back, w_node);
+       }
+
+       /* Transfer forward edges to w_node */
+       while (p_node->getNumEdges() > 0) {
+               CycleNode *forward = p_node->removeEdge();
+               if (forward == w_node)
+                       continue;
+               if (forward->is_promise()) {
+                       if (checkReachable(forward, w_node)) {
+                               mustMerge->push_back(forward->getPromise());
+                               if (!mergeNodes(w_node, forward, mustMerge))
+                                       return false;
+                       } else
+                               w_node->addEdge(forward);
+               } else
+                       addNodeEdge(w_node, forward);
+       }
+
+       erasePromiseNode(promise);
+       /* Not deleting p_node, to maintain consistency if mergeNodes() fails */
+
+       return !hasCycles;
+}
+
+/**
+ * Adds an edge between two CycleNodes.
+ * @param fromnode The edge comes from this CycleNode
+ * @param tonode The edge points to this CycleNode
+ * @return True, if new edge(s) are added; otherwise false
+ */
+bool CycleGraph::addNodeEdge(CycleNode *fromnode, CycleNode *tonode)
+{
+       bool added;
 
        if (!hasCycles)
                hasCycles = checkReachable(tonode, fromnode);
 
-       if (fromnode->addEdge(tonode))
+       if ((added = fromnode->addEdge(tonode)))
                rollbackvector.push_back(fromnode);
 
-
-       CycleNode *rmwnode = fromnode->getRMW();
-
        /*
         * If the fromnode has a rmwnode that is not the tonode, we should add
         * an edge between its rmwnode and the tonode
-        *
-        * If tonode is also a rmw, don't do this check as the execution is
-        * doomed and we'll catch the problem elsewhere, but we want to allow
-        * for the possibility of sending to's write value to rmwnode
         */
-       if (rmwnode != NULL && !to->is_rmw()) {
+       CycleNode *rmwnode = fromnode->getRMW();
+       if (rmwnode && rmwnode != tonode) {
                if (!hasCycles)
                        hasCycles = checkReachable(tonode, rmwnode);
 
-               if (rmwnode->addEdge(tonode))
+               if (rmwnode->addEdge(tonode)) {
                        rollbackvector.push_back(rmwnode);
+                       added = true;
+               }
        }
+       return added;
 }
 
-/** Handles special case of a RMW action.  The ModelAction rmw reads
- *  from the ModelAction from.  The key differences are: (1) no write
- *  can occur in between the rmw and the from action.  Only one RMW
- *  action can read from a given write.
+/**
+ * @brief Add an edge between a write and the RMW which reads from it
+ *
+ * Handles special case of a RMW action, where the ModelAction rmw reads from
+ * the ModelAction/Promise from. The key differences are:
+ * (1) no write can occur in between the rmw and the from action.
+ * (2) Only one RMW action can read from a given write.
+ *
+ * @param from The edge comes from this ModelAction/Promise
+ * @param rmw The edge points to this ModelAction; this action must read from
+ * the ModelAction/Promise from
  */
-void CycleGraph::addRMWEdge(const ModelAction *from, const ModelAction *rmw)
+template <typename T>
+void CycleGraph::addRMWEdge(const T *from, const ModelAction *rmw)
 {
        ASSERT(from);
        ASSERT(rmw);
@@ -102,11 +258,10 @@ void CycleGraph::addRMWEdge(const ModelAction *from, const ModelAction *rmw)
        CycleNode *rmwnode = getNode(rmw);
 
        /* Two RMW actions cannot read from the same write. */
-       if (fromnode->setRMW(rmwnode)) {
-               hasRMWViolation = true;
-       } else {
+       if (fromnode->setRMW(rmwnode))
+               hasCycles = true;
+       else
                rmwrollbackvector.push_back(fromnode);
-       }
 
        /* Transfer all outgoing edges from the from node to the rmw node */
        /* This process should not add a cycle because either:
@@ -123,27 +278,87 @@ void CycleGraph::addRMWEdge(const ModelAction *from, const ModelAction *rmw)
                }
        }
 
-       if (!hasCycles)
-               hasCycles = checkReachable(rmwnode, fromnode);
+       addNodeEdge(fromnode, rmwnode);
+}
+/* Instantiate two forms of CycleGraph::addRMWEdge */
+template void CycleGraph::addRMWEdge(const ModelAction *from, const ModelAction *rmw);
+template void CycleGraph::addRMWEdge(const Promise *from, const ModelAction *rmw);
 
-       if (fromnode->addEdge(rmwnode))
-               rollbackvector.push_back(fromnode);
+/**
+ * @brief Adds an edge between objects
+ *
+ * This function will add an edge between any two objects which can be
+ * associated with a CycleNode. That is, if they have a CycleGraph::getNode
+ * implementation.
+ *
+ * The object to is ordered after the object from.
+ *
+ * @param to The edge points to this object, of type T
+ * @param from The edge comes from this object, of type U
+ * @return True, if new edge(s) are added; otherwise false
+ */
+template <typename T, typename U>
+bool CycleGraph::addEdge(const T *from, const U *to)
+{
+       ASSERT(from);
+       ASSERT(to);
+
+       CycleNode *fromnode = getNode(from);
+       CycleNode *tonode = getNode(to);
+
+       return addNodeEdge(fromnode, tonode);
 }
+/* Instantiate four forms of CycleGraph::addEdge */
+template bool CycleGraph::addEdge(const ModelAction *from, const ModelAction *to);
+template bool CycleGraph::addEdge(const ModelAction *from, const Promise *to);
+template bool CycleGraph::addEdge(const Promise *from, const ModelAction *to);
+template bool CycleGraph::addEdge(const Promise *from, const Promise *to);
 
 #if SUPPORT_MOD_ORDER_DUMP
+
+static void print_node(const CycleNode *node, FILE *file, int label)
+{
+       modelclock_t idx;
+       if (node->is_promise()) {
+               const Promise *promise = node->getPromise();
+               idx = promise->get_action()->get_seq_number();
+               fprintf(file, "P%u", idx);
+               if (label) {
+                       int first = 1;
+                       fprintf(file, " [label=\"P%u, T", idx);
+                       for (unsigned int i = 0 ; i < model->get_num_threads(); i++)
+                               if (promise->thread_is_available(int_to_id(i))) {
+                                       fprintf(file, "%s%u", first ? "": ",", i);
+                                       first = 0;
+                               }
+                       fprintf(file, "\"]");
+               }
+       } else {
+               const ModelAction *act = node->getAction();
+               idx = act->get_seq_number();
+               fprintf(file, "N%u", idx);
+               if (label)
+                       fprintf(file, " [label=\"N%u, T%u\"]", idx, act->get_tid());
+       }
+}
+
 void CycleGraph::dumpNodes(FILE *file) const
 {
        for (unsigned int i = 0; i < nodeList.size(); i++) {
-               CycleNode *cn = nodeList[i];
-               const ModelAction *action = cn->getAction();
-               fprintf(file, "N%u [label=\"%u, T%u\"];\n", action->get_seq_number(), action->get_seq_number(), action->get_tid());
-               if (cn->getRMW() != NULL) {
-                       fprintf(file, "N%u -> N%u[style=dotted];\n", action->get_seq_number(), cn->getRMW()->getAction()->get_seq_number());
+               CycleNode *n = nodeList[i];
+               print_node(n, file, 1);
+               fprintf(file, ";\n");
+               if (n->getRMW() != NULL) {
+                       print_node(n, file, 0);
+                       fprintf(file, " -> ");
+                       print_node(n->getRMW(), file, 0);
+                       fprintf(file, "[style=dotted];\n");
                }
-               for (unsigned int j = 0; j < cn->getNumEdges(); j++) {
-                       CycleNode *dst = cn->getEdge(j);
-                       const ModelAction *dstaction = dst->getAction();
-                       fprintf(file, "N%u -> N%u;\n", action->get_seq_number(), dstaction->get_seq_number());
+               for (unsigned int j = 0; j < n->getNumEdges(); j++) {
+                       print_node(n, file, 0);
+                       fprintf(file, " -> ");
+                       print_node(n->getEdge(j), file, 0);
+                       fprintf(file, ";\n");
                }
        }
 }
@@ -160,23 +375,6 @@ void CycleGraph::dumpGraphToFile(const char *filename) const
 }
 #endif
 
-/**
- * Checks whether one ModelAction can reach another.
- * @param from The ModelAction from which to begin exploration
- * @param to The ModelAction to reach
- * @return True, @a from can reach @a to; otherwise, false
- */
-bool CycleGraph::checkReachable(const ModelAction *from, const ModelAction *to) const
-{
-       CycleNode *fromnode = actionToNode.get(from);
-       CycleNode *tonode = actionToNode.get(to);
-
-       if (!fromnode || !tonode)
-               return false;
-
-       return checkReachable(fromnode, tonode);
-}
-
 /**
  * Checks whether one CycleNode can reach another.
  * @param from The CycleNode from which to begin exploration
@@ -207,6 +405,32 @@ bool CycleGraph::checkReachable(const CycleNode *from, const CycleNode *to) cons
        return false;
 }
 
+/**
+ * Checks whether one ModelAction/Promise can reach another ModelAction/Promise
+ * @param from The ModelAction or Promise from which to begin exploration
+ * @param to The ModelAction or Promise to reach
+ * @return True, @a from can reach @a to; otherwise, false
+ */
+template <typename T, typename U>
+bool CycleGraph::checkReachable(const T *from, const U *to) const
+{
+       CycleNode *fromnode = getNode_noCreate(from);
+       CycleNode *tonode = getNode_noCreate(to);
+
+       if (!fromnode || !tonode)
+               return false;
+
+       return checkReachable(fromnode, tonode);
+}
+/* Instantiate three forms of CycleGraph::checkReachable */
+template bool CycleGraph::checkReachable(const ModelAction *from,
+               const ModelAction *to) const;
+template bool CycleGraph::checkReachable(const ModelAction *from,
+               const Promise *to) const;
+template bool CycleGraph::checkReachable(const Promise *from,
+               const ModelAction *to) const;
+
+/** @return True, if the promise has failed; false otherwise */
 bool CycleGraph::checkPromise(const ModelAction *fromact, Promise *promise) const
 {
        std::vector< CycleNode *, ModelAlloc<CycleNode *> > queue;
@@ -219,9 +443,11 @@ bool CycleGraph::checkPromise(const ModelAction *fromact, Promise *promise) cons
                CycleNode *node = queue.back();
                queue.pop_back();
 
-               if (promise->increment_threads(node->getAction()->get_tid())) {
+               if (node->getPromise() == promise)
+                       return true;
+               if (!node->is_promise() &&
+                               promise->eliminate_thread(node->getAction()->get_tid()))
                        return true;
-               }
 
                for (unsigned int i = 0; i < node->getNumEdges(); i++) {
                        CycleNode *next = node->getEdge(i);
@@ -236,36 +462,31 @@ bool CycleGraph::checkPromise(const ModelAction *fromact, Promise *promise) cons
 
 void CycleGraph::startChanges()
 {
-       ASSERT(rollbackvector.size() == 0);
-       ASSERT(rmwrollbackvector.size() == 0);
+       ASSERT(rollbackvector.empty());
+       ASSERT(rmwrollbackvector.empty());
        ASSERT(oldCycles == hasCycles);
-       ASSERT(oldRMWViolation == hasRMWViolation);
 }
 
 /** Commit changes to the cyclegraph. */
 void CycleGraph::commitChanges()
 {
-       rollbackvector.resize(0);
-       rmwrollbackvector.resize(0);
+       rollbackvector.clear();
+       rmwrollbackvector.clear();
        oldCycles = hasCycles;
-       oldRMWViolation = hasRMWViolation;
 }
 
 /** Rollback changes to the previous commit. */
 void CycleGraph::rollbackChanges()
 {
-       for (unsigned int i = 0; i < rollbackvector.size(); i++) {
-               rollbackvector[i]->popEdge();
-       }
+       for (unsigned int i = 0; i < rollbackvector.size(); i++)
+               rollbackvector[i]->removeEdge();
 
-       for (unsigned int i = 0; i < rmwrollbackvector.size(); i++) {
+       for (unsigned int i = 0; i < rmwrollbackvector.size(); i++)
                rmwrollbackvector[i]->clearRMW();
-       }
 
        hasCycles = oldCycles;
-       hasRMWViolation = oldRMWViolation;
-       rollbackvector.resize(0);
-       rmwrollbackvector.resize(0);
+       rollbackvector.clear();
+       rmwrollbackvector.clear();
 }
 
 /** @returns whether a CycleGraph contains cycles. */
@@ -274,17 +495,24 @@ bool CycleGraph::checkForCycles() const
        return hasCycles;
 }
 
-bool CycleGraph::checkForRMWViolation() const
-{
-       return hasRMWViolation;
-}
-
 /**
  * @brief Constructor for a CycleNode
  * @param act The ModelAction for this node
  */
 CycleNode::CycleNode(const ModelAction *act) :
        action(act),
+       promise(NULL),
+       hasRMW(NULL)
+{
+}
+
+/**
+ * @brief Constructor for a Promise CycleNode
+ * @param promise The Promise which was generated
+ */
+CycleNode::CycleNode(const Promise *promise) :
+       action(NULL),
+       promise(promise),
        hasRMW(NULL)
 {
 }
@@ -314,6 +542,54 @@ unsigned int CycleNode::getNumBackEdges() const
        return back_edges.size();
 }
 
+/**
+ * @brief Remove an element from a vector
+ * @param v The vector
+ * @param n The element to remove
+ * @return True if the element was found; false otherwise
+ */
+template <typename T>
+static bool vector_remove_node(std::vector<T, SnapshotAlloc<T> >& v, const T n)
+{
+       for (unsigned int i = 0; i < v.size(); i++) {
+               if (v[i] == n) {
+                       v.erase(v.begin() + i);
+                       return true;
+               }
+       }
+       return false;
+}
+
+/**
+ * @brief Remove a (forward) edge from this CycleNode
+ * @return The CycleNode which was popped, if one exists; otherwise NULL
+ */
+CycleNode * CycleNode::removeEdge()
+{
+       if (edges.empty())
+               return NULL;
+
+       CycleNode *ret = edges.back();
+       edges.pop_back();
+       vector_remove_node(ret->back_edges, this);
+       return ret;
+}
+
+/**
+ * @brief Remove a (back) edge from this CycleNode
+ * @return The CycleNode which was popped, if one exists; otherwise NULL
+ */
+CycleNode * CycleNode::removeBackEdge()
+{
+       if (back_edges.empty())
+               return NULL;
+
+       CycleNode *ret = back_edges.back();
+       back_edges.pop_back();
+       vector_remove_node(ret->edges, this);
+       return ret;
+}
+
 /**
  * Adds an edge from this CycleNode to another CycleNode.
  * @param node The node to which we add a directed edge
@@ -348,3 +624,19 @@ bool CycleNode::setRMW(CycleNode *node)
        hasRMW = node;
        return false;
 }
+
+/**
+ * Convert a Promise CycleNode into a concrete-valued CycleNode. Should only be
+ * used when there's no existing ModelAction CycleNode for this write.
+ *
+ * @param writer The ModelAction which wrote the future value represented by
+ * this CycleNode
+ */
+void CycleNode::resolvePromise(const ModelAction *writer)
+{
+       ASSERT(is_promise());
+       ASSERT(promise->is_compatible(writer));
+       action = writer;
+       promise = NULL;
+       ASSERT(!is_promise());
+}
index 45e49fb7bec3950656054e02f92b06aa5cf3ae65..bb2ab2d87b4370065ff64fa9dfcac45e86f2ce39 100644 (file)
@@ -19,17 +19,26 @@ class Promise;
 class CycleNode;
 class ModelAction;
 
+typedef std::vector< const Promise *, ModelAlloc<const Promise *> > promise_list_t;
+
 /** @brief A graph of Model Actions for tracking cycles. */
 class CycleGraph {
  public:
        CycleGraph();
        ~CycleGraph();
-       void addEdge(const ModelAction *from, const ModelAction *to);
+
+       template <typename T, typename U>
+       bool addEdge(const T *from, const U *to);
+
+       template <typename T>
+       void addRMWEdge(const T *from, const ModelAction *rmw);
+
        bool checkForCycles() const;
-       bool checkForRMWViolation() const;
-       void addRMWEdge(const ModelAction *from, const ModelAction *rmw);
        bool checkPromise(const ModelAction *from, Promise *p) const;
-       bool checkReachable(const ModelAction *from, const ModelAction *to) const;
+
+       template <typename T, typename U>
+       bool checkReachable(const T *from, const U *to) const;
+
        void startChanges();
        void commitChanges();
        void rollbackChanges();
@@ -38,14 +47,30 @@ class CycleGraph {
        void dumpGraphToFile(const char *filename) const;
 #endif
 
+       bool resolvePromise(ModelAction *reader, ModelAction *writer,
+                       promise_list_t *mustResolve);
+
        SNAPSHOTALLOC
  private:
+       bool addNodeEdge(CycleNode *fromnode, CycleNode *tonode);
        void putNode(const ModelAction *act, CycleNode *node);
-       CycleNode * getNode(const ModelAction *);
+       void putNode(const Promise *promise, CycleNode *node);
+       void erasePromiseNode(const Promise *promise);
+       CycleNode * getNode(const ModelAction *act);
+       CycleNode * getNode(const Promise *promise);
+       CycleNode * getNode_noCreate(const ModelAction *act) const;
+       CycleNode * getNode_noCreate(const Promise *promise) const;
+       bool mergeNodes(CycleNode *node1, CycleNode *node2,
+                       promise_list_t *mustMerge);
+
        HashTable<const CycleNode *, const CycleNode *, uintptr_t, 4, model_malloc, model_calloc, model_free> *discovered;
 
        /** @brief A table for mapping ModelActions to CycleNodes */
        HashTable<const ModelAction *, CycleNode *, uintptr_t, 4> actionToNode;
+       /** @brief A table for mapping reader ModelActions to Promise
+        *  CycleNodes */
+       HashTable<const ModelAction *, CycleNode *, uintptr_t, 4> readerToPromiseNode;
+
 #if SUPPORT_MOD_ORDER_DUMP
        std::vector<CycleNode *> nodeList;
 #endif
@@ -56,38 +81,43 @@ class CycleGraph {
        bool hasCycles;
        bool oldCycles;
 
-       bool hasRMWViolation;
-       bool oldRMWViolation;
-
        std::vector< CycleNode *, SnapshotAlloc<CycleNode *> > rollbackvector;
        std::vector< CycleNode *, SnapshotAlloc<CycleNode *> > rmwrollbackvector;
 };
 
-/** @brief A node within a CycleGraph; corresponds to one ModelAction */
+/**
+ * @brief A node within a CycleGraph; corresponds either to one ModelAction or
+ * to a promised future value
+ */
 class CycleNode {
  public:
        CycleNode(const ModelAction *act);
+       CycleNode(const Promise *promise);
        bool addEdge(CycleNode *node);
        CycleNode * getEdge(unsigned int i) const;
        unsigned int getNumEdges() const;
        CycleNode * getBackEdge(unsigned int i) const;
        unsigned int getNumBackEdges() const;
+       CycleNode * removeEdge();
+       CycleNode * removeBackEdge();
+
        bool setRMW(CycleNode *);
        CycleNode * getRMW() const;
+       void clearRMW() { hasRMW = NULL; }
        const ModelAction * getAction() const { return action; }
-
-       void popEdge() {
-               edges.pop_back();
-       }
-       void clearRMW() {
-               hasRMW = NULL;
-       }
+       const Promise * getPromise() const { return promise; }
+       bool is_promise() const { return !action; }
+       void resolvePromise(const ModelAction *writer);
 
        SNAPSHOTALLOC
  private:
        /** @brief The ModelAction that this node represents */
        const ModelAction *action;
 
+       /** @brief The promise represented by this node; only valid when action
+        * is NULL */
+       const Promise *promise;
+
        /** @brief The edges leading out from this node */
        std::vector< CycleNode *, SnapshotAlloc<CycleNode *> > edges;
 
index 9c3488435e02d0ec592b7381b9e207448c494ce1..cbf1f3313ac3d2e89e7053e43c96bcf32a096ed6 100644 (file)
--- a/model.cc
+++ b/model.cc
@@ -48,6 +48,7 @@ struct model_snapshot_members {
                stats(),
                failed_promise(false),
                too_many_reads(false),
+               no_valid_reads(false),
                bad_synchronization(false),
                asserted(false)
        { }
@@ -66,6 +67,7 @@ struct model_snapshot_members {
        struct execution_stats stats;
        bool failed_promise;
        bool too_many_reads;
+       bool no_valid_reads;
        /** @brief Incorrectly-ordered synchronization was made */
        bool bad_synchronization;
        bool asserted;
@@ -614,7 +616,7 @@ void ModelChecker::set_backtracking(ModelAction *act)
        Node *node = prev->get_node()->get_parent();
 
        int low_tid, high_tid;
-       if (node->is_enabled(t)) {
+       if (node->enabled_status(t->get_id()) == THREAD_ENABLED) {
                low_tid = id_to_int(act->get_tid());
                high_tid = low_tid + 1;
        } else {
@@ -721,7 +723,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;
@@ -730,16 +731,19 @@ bool ModelChecker::process_read(ModelAction *curr, bool second_part_of_rmw)
 
                        read_from(curr, reads_from);
                        mo_graph->commitChanges();
-                       mo_check_promises(curr->get_tid(), reads_from);
+                       mo_check_promises(curr, true);
 
                        updated |= r_status;
                } else if (!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->set_read_from(NULL);
-                       Promise *valuepromise = new Promise(curr, value, expiration);
-                       promises->push_back(valuepromise);
+                       struct future_value fv = curr->get_node()->get_future_value();
+                       Promise *promise = new Promise(curr, fv);
+                       value = fv.value;
+                       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;
@@ -856,10 +860,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->get_value(),
-                               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);
+       }
 }
 
 /**
@@ -881,7 +897,7 @@ bool ModelChecker::process_write(ModelAction *curr)
        }
 
        mo_graph->commitChanges();
-       mo_check_promises(curr->get_tid(), curr);
+       mo_check_promises(curr, false);
 
        get_thread(curr)->set_return_value(VALUE_NONE);
        return updated_mod_order || updated_promises;
@@ -956,6 +972,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: {
@@ -972,6 +994,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;
        }
@@ -1280,8 +1309,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))
@@ -1348,14 +1383,14 @@ 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)
                ptr += sprintf(ptr, "[failed promise]");
        if (priv->too_many_reads)
                ptr += sprintf(ptr, "[too many reads]");
+       if (priv->no_valid_reads)
+               ptr += sprintf(ptr, "[no valid reads-from]");
        if (priv->bad_synchronization)
                ptr += sprintf(ptr, "[bad sw ordering]");
        if (promises_expired())
@@ -1383,21 +1418,11 @@ 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->no_valid_reads ||
+               priv->failed_promise ||
+               priv->too_many_reads ||
+               priv->bad_synchronization ||
                promises_expired();
 }
 
@@ -1405,8 +1430,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;
@@ -1471,13 +1499,9 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf)
                                continue;
 
                        /* Test to see whether this is a feasible write to read from */
-                       mo_graph->startChanges();
-                       r_modification_order(curr, write);
-                       bool feasiblereadfrom = !is_infeasible();
-                       mo_graph->rollbackChanges();
+                       /** NOTE: all members of read-from set should be
+                        *  feasible, so we no longer check it here **/
 
-                       if (!feasiblereadfrom)
-                               continue;
                        rit = ritcopy;
 
                        bool feasiblewrite = true;
@@ -1510,7 +1534,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.
@@ -1519,10 +1543,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;
@@ -1550,26 +1575,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;
                                }
                        }
@@ -1580,9 +1602,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();
@@ -1590,9 +1611,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;
@@ -1600,71 +1620,15 @@ 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;
-               }
+       /*
+        * All compatible, thread-exclusive promises must be ordered after any
+        * concrete loads from the same thread
+        */
+       for (unsigned int i = 0; i < promises->size(); i++)
+               if ((*promises)[i]->is_compatible_exclusive(curr))
+                       added = mo_graph->addEdge(rf, (*promises)[i]) || added;
 
-                       /* 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;
-               }
-       }
+       return added;
 }
 
 /**
@@ -1701,8 +1665,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;
                }
        }
 
@@ -1745,8 +1708,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;
                        }
 
@@ -1762,14 +1724,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)) {
@@ -1786,15 +1747,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;
 }
 
@@ -2309,42 +2279,44 @@ ClockVector * ModelChecker::get_cv(thread_id_t tid) const
  */
 bool ModelChecker::resolve_promises(ModelAction *write)
 {
-       bool resolved = false;
-       std::vector< thread_id_t, ModelAlloc<thread_id_t> > threads_to_check;
+       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);
-                       threads_to_check.push_back(read->get_tid());
+                       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 < threads_to_check.size(); i++)
-               mo_check_promises(threads_to_check[i], write);
+       for (unsigned int i = 0; i < actions_to_check.size(); i++) {
+               ModelAction *read = actions_to_check[i];
+               mo_check_promises(read, true);
+       }
 
-       return resolved;
+       return haveResolved;
 }
 
 /**
@@ -2377,7 +2349,7 @@ void ModelChecker::check_promises(thread_id_t tid, ClockVector *old_cv, ClockVec
                const ModelAction *act = promise->get_action();
                if ((old_cv == NULL || !old_cv->synchronized_since(act)) &&
                                merge_cv->synchronized_since(act)) {
-                       if (promise->increment_threads(tid)) {
+                       if (promise->eliminate_thread(tid)) {
                                //Promise has failed
                                priv->failed_promise = true;
                                return;
@@ -2386,77 +2358,54 @@ void ModelChecker::check_promises(thread_id_t tid, ClockVector *old_cv, ClockVec
        }
 }
 
-void ModelChecker::check_promises_thread_disabled() {
+void ModelChecker::check_promises_thread_disabled()
+{
        for (unsigned int i = 0; i < promises->size(); i++) {
                Promise *promise = (*promises)[i];
-               if (promise->check_promise()) {
+               if (promise->has_failed()) {
                        priv->failed_promise = true;
                        return;
                }
        }
 }
 
-/** 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.
+/**
+ * @brief Checks promises in response to addition to modification order for
+ * threads.
  *
- *     @param tid The thread that either read from the model action
- *     write, or actually did the model action write.
+ * We test whether threads are still available for satisfying promises after an
+ * addition to our modification order constraints. Those that are unavailable
+ * are "eliminated". Once all threads are eliminated from satisfying a promise,
+ * that promise has failed.
  *
- *     @param write The ModelAction representing the relevant write.
+ * @param act The ModelAction which updated the modification order
+ * @param is_read_check Should be true if act is a read and we must check for
+ * updates to the store from which it read (there is a distinction here for
+ * RMW's, which are both a load and a store)
  */
-void ModelChecker::mo_check_promises(thread_id_t tid, const ModelAction *write)
+void ModelChecker::mo_check_promises(const ModelAction *act, bool is_read_check)
 {
-       void *location = write->get_location();
+       const ModelAction *write = is_read_check ? act->get_reads_from() : act;
+
        for (unsigned int i = 0; i < promises->size(); i++) {
                Promise *promise = (*promises)[i];
-               const ModelAction *act = promise->get_action();
+               const ModelAction *pread = promise->get_action();
 
-               //Is this promise on the same location?
-               if (act->get_location() != location)
+               // Is this promise on the same location?
+               if (!pread->same_var(write))
                        continue;
 
-               //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);
-                               //The pwrite cannot happen before the promise
-                               if (write->happens_before(act) && (write != act)) {
-                                       priv->failed_promise = true;
-                                       return;
-                               }
-                       }
-                       if (mo_graph->checkPromise(write, promise)) {
-                               priv->failed_promise = true;
-                               return;
-                       }
+               if (pread->happens_before(act) && mo_graph->checkPromise(write, promise)) {
+                       priv->failed_promise = true;
+                       return;
                }
 
-               //Don't do any lookups twice for the same thread
-               if (promise->has_sync_thread(tid))
+               // Don't do any lookups twice for the same thread
+               if (!promise->thread_is_available(act->get_tid()))
                        continue;
 
-               if (promise->get_write() && mo_graph->checkReachable(promise->get_write(), write)) {
-                       if (promise->increment_threads(tid)) {
+               if (mo_graph->checkReachable(promise, write)) {
+                       if (mo_graph->checkPromise(write, promise)) {
                                priv->failed_promise = true;
                                return;
                        }
@@ -2525,14 +2474,25 @@ void ModelChecker::build_reads_from_past(ModelAction *curr)
                        else if (curr->get_sleep_flag() && !curr->is_seqcst() && !sleep_can_read_from(curr, act))
                                allow_read = false;
 
-                       if (allow_read)
-                               curr->get_node()->add_read_from(act);
+                       if (allow_read) {
+                               /* Only add feasible reads */
+                               mo_graph->startChanges();
+                               r_modification_order(curr, act);
+                               if (!is_infeasible())
+                                       curr->get_node()->add_read_from(act);
+                               mo_graph->rollbackChanges();
+                       }
 
                        /* Include at most one act per-thread that "happens before" curr */
                        if (act->happens_before(curr))
                                break;
                }
        }
+       /* We may find no valid may-read-from only if the execution is doomed */
+       if (!curr->get_node()->get_read_from_size()) {
+               priv->no_valid_reads = true;
+               set_assert();
+       }
 
        if (DBG_ENABLED()) {
                model_print("Reached read action:\n");
@@ -2605,7 +2565,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());
                }
@@ -2625,7 +2585,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);
@@ -2677,7 +2636,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());
 }
diff --git a/model.h b/model.h
index 97a898926e49dfd8480412c67a2900dfd8db5f15..99a93cdc9fd3e33dad9cbc5a4ef21954354c1191 100644 (file)
--- a/model.h
+++ b/model.h
@@ -108,7 +108,7 @@ public:
        void add_thread(Thread *t);
        void remove_thread(Thread *t);
        Thread * get_thread(thread_id_t tid) const;
-       Thread * get_thread(ModelAction *act) const;
+       Thread * get_thread(const ModelAction *act) const;
 
        bool is_enabled(Thread *t) const;
        bool is_enabled(thread_id_t tid) const;
@@ -121,7 +121,7 @@ public:
        ClockVector * get_cv(thread_id_t tid) const;
        ModelAction * get_parent_action(thread_id_t tid) const;
        void check_promises_thread_disabled();
-       void mo_check_promises(thread_id_t tid, const ModelAction *write);
+       void mo_check_promises(const ModelAction *act, bool is_read_check);
        void check_promises(thread_id_t tid, ClockVector *old_cv, ClockVector *merge_cv);
        bool isfeasibleprefix() const;
 
@@ -182,8 +182,10 @@ private:
        ModelAction * get_last_unlock(ModelAction *curr) const;
        void build_reads_from_past(ModelAction *curr);
        ModelAction * process_rmw(ModelAction *curr);
-       void post_r_modification_order(ModelAction *curr, const ModelAction *rf);
-       bool r_modification_order(ModelAction *curr, const ModelAction *rf);
+
+       template <typename rf_type>
+       bool r_modification_order(ModelAction *curr, const rf_type *rf);
+
        bool w_modification_order(ModelAction *curr);
        void get_release_seq_heads(ModelAction *acquire, ModelAction *read, rel_heads_list_t *release_heads);
        bool release_seq_heads(const ModelAction *rf, rel_heads_list_t *release_heads, struct release_seq *pending) const;
@@ -257,7 +259,6 @@ private:
 
        void print_infeasibility(const char *prefix) const;
        bool is_feasible_prefix_ignore_relseq() const;
-       bool is_infeasible_ignoreRMW() const;
        bool is_infeasible() const;
        bool is_deadlocked() const;
        bool is_complete_execution() const;
index 088fad8904b473aa603b457b188e6c9644cdd248..addf93f67469f1cd7f96a15ffad8ec5551cb1c1b 100644 (file)
@@ -1,3 +1,6 @@
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
 #include <string.h>
 
 #include "nodestack.h"
@@ -5,6 +8,7 @@
 #include "common.h"
 #include "model.h"
 #include "threads-model.h"
+#include "modeltypes.h"
 
 /**
  * @brief Node constructor
@@ -86,12 +90,24 @@ Node::~Node()
 }
 
 /** Prints debugging info for the ModelAction associated with this Node */
-void Node::print()
+void Node::print() const
 {
        action->print();
-       model_print("          backtrack: %s\n", backtrack_empty() ? "empty" : "non-empty");
-       model_print("          future values: %s\n", future_value_empty() ? "empty" : "non-empty");
-       model_print("          read-from: %s\n", read_from_empty() ? "empty" : "non-empty");
+       model_print("          backtrack: %s", backtrack_empty() ? "empty" : "non-empty ");
+       for (int i = 0; i < (int)backtrack.size(); i++)
+               if (backtrack[i] == true)
+                       model_print("[%d]", i);
+       model_print("\n");
+       model_print("          future values: %s", future_value_empty() ? "empty" : "non-empty ");
+       for (int i = future_index + 1; i < (int)future_values.size(); i++)
+               model_print("[%#" PRIx64 "]", future_values[i].value);
+       model_print("\n");
+
+       model_print("          read-from: %s", read_from_empty() ? "empty" : "non-empty ");
+       for (int i = read_from_index + 1; i < (int)may_read_from.size(); i++)
+               model_print("[%d]", may_read_from[i]->get_seq_number());
+       model_print("\n");
+
        model_print("          promises: %s\n", promise_empty() ? "empty" : "non-empty");
        model_print("          misc: %s\n", misc_empty() ? "empty" : "non-empty");
        model_print("          rel seq break: %s\n", relseq_break_empty() ? "empty" : "non-empty");
@@ -148,7 +164,7 @@ bool Node::increment_promise()
                                //sending our value to two rmws... not going to work..try next combination
                                continue;
                        }
-                       promises[i] = (promises[i] & PROMISE_RMW) |PROMISE_FULFILLED;
+                       promises[i] = (promises[i] & PROMISE_RMW) | PROMISE_FULFILLED;
                        while (i > 0) {
                                i--;
                                if ((promises[i] & PROMISE_MASK) == PROMISE_FULFILLED)
@@ -172,15 +188,14 @@ bool Node::promise_empty() const
        for (int i = promises.size() - 1; i >= 0; i--) {
                if (promises[i] == PROMISE_UNFULFILLED)
                        return false;
-               if (!fulfilledrmw && ((promises[i]&PROMISE_MASK) == PROMISE_UNFULFILLED))
+               if (!fulfilledrmw && ((promises[i] & PROMISE_MASK) == PROMISE_UNFULFILLED))
                        return false;
-               if (promises[i] == (PROMISE_FULFILLED|PROMISE_RMW))
+               if (promises[i] == (PROMISE_FULFILLED | PROMISE_RMW))
                        fulfilledrmw = true;
        }
        return true;
 }
 
-
 void Node::set_misc_max(int i)
 {
        misc_max = i;
@@ -201,7 +216,6 @@ bool Node::misc_empty() const
        return (misc_index + 1) >= misc_max;
 }
 
-
 /**
  * Adds a value from a weakly ordered future write to backtrack to. This
  * operation may "fail" if the future value has already been run (within some
@@ -212,11 +226,14 @@ bool Node::misc_empty() const
  * @param value is the value to backtrack to.
  * @return True if the future value was successully added; false otherwise
  */
-bool Node::add_future_value(uint64_t value, modelclock_t expiration)
+bool Node::add_future_value(struct future_value& fv)
 {
+       uint64_t value = fv.value;
+       modelclock_t expiration = fv.expiration;
+       thread_id_t tid = fv.tid;
        int idx = -1; /* Highest index where value is found */
        for (unsigned int i = 0; i < future_values.size(); i++) {
-               if (future_values[i].value == value) {
+               if (future_values[i].value == value && future_values[i].tid == tid) {
                        if (expiration <= future_values[i].expiration)
                                return false;
                        idx = i;
@@ -236,8 +253,7 @@ bool Node::add_future_value(uint64_t value, modelclock_t expiration)
                        (int)future_values.size() >= model->params.maxfuturevalues)
                return false;
 
-       struct future_value newfv = {value, expiration};
-       future_values.push_back(newfv);
+       future_values.push_back(fv);
        return true;
 }
 
@@ -368,23 +384,16 @@ void Node::add_read_from(const ModelAction *act)
 }
 
 /**
- * Gets the next 'future_value' value from this Node. Only valid for a node
- * where this->action is a 'read'.
+ * Gets the next 'future_value' from this Node. Only valid for a node where
+ * this->action is a 'read'.
  * @return The first element in future_values
  */
-uint64_t Node::get_future_value() const
+struct future_value Node::get_future_value() const
 {
        ASSERT(future_index >= 0 && future_index < ((int)future_values.size()));
-       return future_values[future_index].value;
+       return future_values[future_index];
 }
 
-modelclock_t Node::get_future_value_expiration() const
-{
-       ASSERT(future_index >= 0 && future_index < ((int)future_values.size()));
-       return future_values[future_index].expiration;
-}
-
-
 int Node::get_read_from_size() const
 {
        return may_read_from.size();
index 94bb58ee1e64d235e4daf0801ee6861491c6b292..47c72678aa13cffb9294e6a7d842860136e37baf 100644 (file)
@@ -5,14 +5,13 @@
 #ifndef __NODESTACK_H__
 #define __NODESTACK_H__
 
-#include <list>
 #include <vector>
 #include <cstddef>
 #include <inttypes.h>
 
 #include "mymemory.h"
-#include "modeltypes.h"
 #include "schedule.h"
+#include "promise.h"
 
 class ModelAction;
 class Thread;
@@ -33,18 +32,12 @@ class Thread;
 
 typedef int promise_t;
 
-struct future_value {
-       uint64_t value;
-       modelclock_t expiration;
-};
-
 struct fairness_info {
        unsigned int enabled_count;
        unsigned int turns;
        bool priority;
 };
 
-
 /**
  * @brief A single node in a NodeStack
  *
@@ -78,9 +71,8 @@ public:
         * occurred previously in the stack. */
        Node * get_parent() const { return parent; }
 
-       bool add_future_value(uint64_t value, modelclock_t expiration);
-       uint64_t get_future_value() const;
-       modelclock_t get_future_value_expiration() const;
+       bool add_future_value(struct future_value& fv);
+       struct future_value get_future_value() const;
        bool increment_future_value();
        bool future_value_empty() const;
 
@@ -107,7 +99,7 @@ public:
        bool increment_relseq_break();
        bool relseq_break_empty() const;
 
-       void print();
+       void print() const;
        void print_may_read_from();
 
        MEMALLOC
index 90591eb60249693d773c534dfbaedf96c4117521..e38696ea4d119fd4d159656d3b3eed74443d8f3c 100644 (file)
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
 #include "promise.h"
 #include "model.h"
 #include "schedule.h"
 
-bool Promise::increment_threads(thread_id_t tid) { 
-       unsigned int id=id_to_int(tid); 
-       if ( id >= synced_thread.size() ) {
-               synced_thread.resize(id+1, false);
-       }
-       if (synced_thread[id])
+/**
+ * Eliminate a thread which no longer can satisfy this promise. Once all
+ * enabled threads have been eliminated, this promise is unresolvable.
+ *
+ * @param tid The thread ID of the thread to eliminate
+ * @return True, if this elimination has invalidated the promise; false
+ * otherwise
+ */
+bool Promise::eliminate_thread(thread_id_t tid)
+{
+       unsigned int id = id_to_int(tid);
+       if (!thread_is_available(tid))
                return false;
-       
-       synced_thread[id]=true;
-       unsigned int sync_size=synced_thread.size();
-       int promise_tid=id_to_int(read->get_tid());
-       for(unsigned int i=1;i<model->get_num_threads();i++) {
-               if ((i >= sync_size || !synced_thread[i]) && ( (int)i != promise_tid ) && model->is_enabled(int_to_id(i))) {
-                       return false;
-               }
-       }
-       return true;
+
+       available_thread[id] = false;
+       num_available_threads--;
+       return has_failed();
 }
 
-bool Promise::check_promise() {
-       unsigned int sync_size=synced_thread.size();
-       for(unsigned int i=1;i<model->get_num_threads();i++) {
-               if ((i >= sync_size || !synced_thread[i]) && model->is_enabled(int_to_id(i))) {
-                       return false;
-               }
+/**
+ * Add a thread which may resolve this promise
+ *
+ * @param tid The thread ID
+ */
+void Promise::add_thread(thread_id_t tid)
+{
+       unsigned int id = id_to_int(tid);
+       if (id >= available_thread.size())
+               available_thread.resize(id + 1, false);
+       if (!available_thread[id]) {
+               available_thread[id] = true;
+               num_available_threads++;
        }
-       return true;
+}
+
+/**
+ * Check if a thread is available for resolving this promise. That is, the
+ * thread must have been previously marked for resolving this promise, and it
+ * cannot have been eliminated due to synchronization, etc.
+ *
+ * @param tid Thread ID of the thread to check
+ * @return True if the thread is available; false otherwise
+ */
+bool Promise::thread_is_available(thread_id_t tid) const
+{
+       unsigned int id = id_to_int(tid);
+       if (id >= available_thread.size())
+               return false;
+       return available_thread[id];
+}
+
+/** @brief Print debug info about the Promise */
+void Promise::print() const
+{
+       model_print("Promised value %#" PRIx64 ", read from thread %d, available threads to resolve: ", value, id_to_int(read->get_tid()));
+       for (unsigned int i = 0; i < available_thread.size(); i++)
+               if (available_thread[i])
+                       model_print("[%d]", i);
+       model_print("\n");
+}
+
+/**
+ * Check if this promise has failed. A promise can fail when all threads which
+ * could possibly satisfy the promise have been eliminated.
+ *
+ * @return True, if this promise has failed; false otherwise
+ */
+bool Promise::has_failed() const
+{
+       return num_available_threads == 0;
+}
+
+/**
+ * @brief Check if an action's thread and location are compatible for resolving
+ * this promise
+ * @param act The action to check against
+ * @return True if we are compatible; false otherwise
+ */
+bool Promise::is_compatible(const ModelAction *act) const
+{
+       return thread_is_available(act->get_tid()) && read->same_var(act);
+}
+
+/**
+ * @brief Check if an action's thread and location are compatible for resolving
+ * this promise, and that the promise is thread-exclusive
+ * @param act The action to check against
+ * @return True if we are compatible and exclusive; false otherwise
+ */
+bool Promise::is_compatible_exclusive(const ModelAction *act) const
+{
+       return get_num_available_threads() == 1 && is_compatible(act);
 }
index 9ddaea8d6dbfb296f187df94f47141a6cf0376f4..852fe714f896dfbffc0b409fcd499058841a1ee0 100644 (file)
--- a/promise.h
+++ b/promise.h
 #include "threads-model.h"
 
 #include "model.h"
+#include "modeltypes.h"
+
+struct future_value {
+       uint64_t value;
+       modelclock_t expiration;
+       thread_id_t tid;
+};
 
 class Promise {
  public:
- Promise(ModelAction *act, uint64_t value, modelclock_t expiration) :
-       value(value), expiration(expiration), read(act), write(NULL)
+       Promise(ModelAction *read, struct future_value fv) :
+               num_available_threads(0),
+               value(fv.value),
+               expiration(fv.expiration),
+               read(read),
+               write(NULL)
        {
-               increment_threads(act->get_tid());
+               add_thread(fv.tid);
+               eliminate_thread(read->get_tid());
        }
        modelclock_t get_expiration() const { return expiration; }
        ModelAction * get_action() const { return read; }
-       bool increment_threads(thread_id_t tid);
-
-       bool has_sync_thread(thread_id_t tid) {
-               unsigned int id = id_to_int(tid);
-               if (id >= synced_thread.size())
-                       return false;
-               return synced_thread[id];
-       }
-
-       bool check_promise();
+       bool eliminate_thread(thread_id_t tid);
+       void add_thread(thread_id_t tid);
+       bool thread_is_available(thread_id_t tid) const;
+       bool has_failed() const;
        uint64_t get_value() const { return value; }
        void set_write(const ModelAction *act) { write = act; }
-       const ModelAction * get_write() { return write; }
+       const ModelAction * get_write() const { return write; }
+       int get_num_available_threads() const { return num_available_threads; }
+       bool is_compatible(const ModelAction *act) const;
+       bool is_compatible_exclusive(const ModelAction *act) const;
+
+       void print() const;
 
        SNAPSHOTALLOC
  private:
-       std::vector<bool> synced_thread;
+       /** @brief Thread ID(s) for thread(s) that potentially can satisfy this
+        *  promise */
+       std::vector< bool, SnapshotAlloc<bool> > available_thread;
+
+       int num_available_threads;
+
        const uint64_t value;
        const modelclock_t expiration;
+
+       /** @brief The action which reads a promised value */
        ModelAction * const read;
-       const ModelAction * write;
+
+       const ModelAction *write;
 };
 
 #endif
index 444e1a82982535339f241d5eb203fddf1d00a15a..915bbc90fb3daa8d5985decdf9e1641a93e59993 100644 (file)
@@ -7,6 +7,32 @@
 #include "model.h"
 #include "nodestack.h"
 
+/**
+ * Format an "enabled_type_t" for printing
+ * @param e The type to format
+ * @param str The output character array
+ */
+static void enabled_type_to_string(enabled_type_t e, char *str)
+{
+       const char *res;
+       switch (e) {
+       case THREAD_DISABLED:
+               res = "disabled";
+               break;
+       case THREAD_ENABLED:
+               res = "enabled";
+               break;
+       case THREAD_SLEEP_SET:
+               res = "sleep";
+               break;
+       default:
+               ASSERT(0);
+               res = NULL;
+               break;
+       }
+       strcpy(str, res);
+}
+
 /** Constructor */
 Scheduler::Scheduler() :
        enabled(NULL),
@@ -184,7 +210,8 @@ Thread * Scheduler::next_thread(Thread *t)
                                break;
                        }
                        if (curr_thread_index == old_curr_thread) {
-                               print();
+                               if (DBG_ENABLED())
+                                       print();
                                return NULL;
                        }
                }
@@ -196,7 +223,8 @@ Thread * Scheduler::next_thread(Thread *t)
        }
 
        current = t;
-       print();
+       if (DBG_ENABLED())
+               print();
        return t;
 }
 
@@ -215,8 +243,13 @@ Thread * Scheduler::get_current_thread() const
  */
 void Scheduler::print() const
 {
-       if (current)
-               DEBUG("Current thread: %d\n", id_to_int(current->get_id()));
-       else
-               DEBUG("No current thread\n");
+       int curr_id = current ? id_to_int(current->get_id()) : -1;
+
+       model_print("Scheduler: ");
+       for (int i = 0; i < enabled_len; i++) {
+               char str[20];
+               enabled_type_to_string(enabled[i], str);
+               model_print("[%i: %s%s]", i, i == curr_id ? "current, " : "", str);
+       }
+       model_print("\n");
 }
index 035ee78caadcedfc3119939a8f29028c5e269de7..10312326528fadd4ad6e39f113030068d0cb19b7 100644 (file)
@@ -5,8 +5,8 @@
 #ifndef __SCHEDULE_H__
 #define __SCHEDULE_H__
 
-#include <list>
 #include "mymemory.h"
+#include "modeltypes.h"
 
 /* Forward declaration */
 class Thread;
index a3de6fe9e7c1030d4d783cc3d5880606ddd9140f..f1ea5b295d110f81090bf2807b2aeb25c2b9f8bc 100644 (file)
@@ -1,18 +1,24 @@
-include ../common.mk
+BASE = ..
 
-CPPFLAGS += -I.. -I../include
+include $(BASE)/common.mk
+
+CPPFLAGS += -I$(BASE) -I$(BASE)/include
 
 SRCS = $(wildcard *.c)
 CPSRCS = $(wildcard *.cc)
 OBJS = $(patsubst %.c,%.o,$(SRCS)) $(patsubst %.cc,%.o,$(CPSRCS))
 
-all: $(OBJS)
+all: $(OBJS) litmus-tests
+
+litmus-tests::
+       $(MAKE) -C litmus
 
 %.o: %.c
-       $(CC) -o $@ $< $(CPPFLAGS) -L.. -l$(LIB_NAME)
+       $(CC) -o $@ $< $(CPPFLAGS) -L$(BASE) -l$(LIB_NAME)
 
 %.o: %.cc
-       $(CXX) -o $@ $< $(CPPFLAGS) -L.. -l$(LIB_NAME)
+       $(CXX) -o $@ $< $(CPPFLAGS) -L$(BASE) -l$(LIB_NAME)
 
 clean::
        rm -f *.o
+       $(MAKE) -C litmus clean
diff --git a/test/litmus/Makefile b/test/litmus/Makefile
new file mode 100644 (file)
index 0000000..b4a1233
--- /dev/null
@@ -0,0 +1,20 @@
+BASE = ../..
+
+include $(BASE)/common.mk
+
+CPPFLAGS += -I$(BASE) -I$(BASE)/include
+
+SRCS = $(wildcard *.c)
+CPSRCS = $(wildcard *.cc)
+OBJS = $(patsubst %.c,%.o,$(SRCS)) $(patsubst %.cc,%.o,$(CPSRCS))
+
+all: $(OBJS)
+
+%.o: %.c
+       $(CC) -o $@ $< $(CPPFLAGS) -L$(BASE) -l$(LIB_NAME)
+
+%.o: %.cc
+       $(CXX) -o $@ $< $(CPPFLAGS) -L$(BASE) -l$(LIB_NAME)
+
+clean::
+       rm -f *.o
diff --git a/test/litmus/iriw.cc b/test/litmus/iriw.cc
new file mode 100644 (file)
index 0000000..fa4a034
--- /dev/null
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <threads.h>
+#include <atomic>
+
+std::atomic_int x;
+std::atomic_int y;
+
+std::memory_order store_mo = std::memory_order_release;
+std::memory_order load_mo = std::memory_order_acquire;
+
+static void a(void *obj)
+{
+       x.store(1, store_mo);
+}
+
+static void b(void *obj)
+{
+       y.store(1, store_mo);
+}
+
+static void c(void *obj)
+{
+       printf("x1: %d\n", x.load(load_mo));
+       printf("y1: %d\n", y.load(load_mo));
+}
+
+static void d(void *obj)
+{
+       printf("y2: %d\n", y.load(load_mo));
+       printf("x2: %d\n", x.load(load_mo));
+}
+
+int user_main(int argc, char **argv)
+{
+       thrd_t t1, t2, t3, t4;
+
+       /* Command-line argument 's' enables seq_cst test */
+       if (argc > 1 && *argv[1] == 's')
+               store_mo = load_mo = std::memory_order_seq_cst;
+
+       atomic_init(&x, 0);
+       atomic_init(&y, 0);
+
+       printf("Main thread: creating 4 threads\n");
+       thrd_create(&t1, (thrd_start_t)&a, NULL);
+       thrd_create(&t2, (thrd_start_t)&b, NULL);
+       thrd_create(&t3, (thrd_start_t)&c, NULL);
+       thrd_create(&t4, (thrd_start_t)&d, NULL);
+
+       thrd_join(t1);
+       thrd_join(t2);
+       thrd_join(t3);
+       thrd_join(t4);
+       printf("Main thread is finished\n");
+
+       return 0;
+}
diff --git a/test/litmus/load-buffer.cc b/test/litmus/load-buffer.cc
new file mode 100644 (file)
index 0000000..9c9923c
--- /dev/null
@@ -0,0 +1,36 @@
+#include <stdio.h>
+#include <threads.h>
+#include <atomic>
+
+std::atomic_int x;
+std::atomic_int y;
+
+static void a(void *obj)
+{
+       printf("x: %d\n", x.load(std::memory_order_relaxed));
+       y.store(1, std::memory_order_relaxed);
+}
+
+static void b(void *obj)
+{
+       printf("y: %d\n", y.load(std::memory_order_relaxed));
+       x.store(1, std::memory_order_relaxed);
+}
+
+int user_main(int argc, char **argv)
+{
+       thrd_t t1, t2;
+
+       atomic_init(&x, 0);
+       atomic_init(&y, 0);
+
+       printf("Main thread: creating 2 threads\n");
+       thrd_create(&t1, (thrd_start_t)&a, NULL);
+       thrd_create(&t2, (thrd_start_t)&b, NULL);
+
+       thrd_join(t1);
+       thrd_join(t2);
+       printf("Main thread is finished\n");
+
+       return 0;
+}
diff --git a/test/litmus/message-passing.cc b/test/litmus/message-passing.cc
new file mode 100644 (file)
index 0000000..6ef41eb
--- /dev/null
@@ -0,0 +1,44 @@
+#include <stdio.h>
+#include <threads.h>
+#include <atomic>
+
+std::atomic_int x;
+std::atomic_int y;
+
+static void a(void *obj)
+{
+       x.store(1, std::memory_order_relaxed);
+       y.store(1, std::memory_order_relaxed);
+}
+
+static void b(void *obj)
+{
+       printf("y1: %d\n", y.load(std::memory_order_relaxed));
+       printf("x1: %d\n", x.load(std::memory_order_relaxed));
+}
+
+static void c(void *obj)
+{
+       printf("x2: %d\n", x.load(std::memory_order_relaxed));
+       printf("y2: %d\n", y.load(std::memory_order_relaxed));
+}
+
+int user_main(int argc, char **argv)
+{
+       thrd_t t1, t2, t3;
+
+       atomic_init(&x, 0);
+       atomic_init(&y, 0);
+
+       printf("Main thread: creating 3 threads\n");
+       thrd_create(&t1, (thrd_start_t)&a, NULL);
+       thrd_create(&t2, (thrd_start_t)&b, NULL);
+       thrd_create(&t3, (thrd_start_t)&c, NULL);
+
+       thrd_join(t1);
+       thrd_join(t2);
+       thrd_join(t3);
+       printf("Main thread is finished\n");
+
+       return 0;
+}
diff --git a/test/litmus/seq-lock.cc b/test/litmus/seq-lock.cc
new file mode 100644 (file)
index 0000000..447123c
--- /dev/null
@@ -0,0 +1,52 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <threads.h>
+#include <atomic>
+
+std::atomic_int x;
+std::atomic_int y;
+std::atomic_int z;
+
+static int N = 1;
+
+static void a(void *obj)
+{
+       for (int i = 0; i < N; i++) {
+               x.store(2 * i + 1, std::memory_order_release);
+               y.store(i + 1, std::memory_order_release);
+               z.store(i + 1, std::memory_order_release);
+               x.store(2 * i + 2, std::memory_order_release);
+       }
+}
+
+static void b(void *obj)
+{
+       printf("x: %d\n", x.load(std::memory_order_acquire));
+       printf("y: %d\n", y.load(std::memory_order_acquire));
+       printf("z: %d\n", z.load(std::memory_order_acquire));
+       printf("x: %d\n", x.load(std::memory_order_acquire));
+}
+
+int user_main(int argc, char **argv)
+{
+       thrd_t t1, t2;
+
+       if (argc > 1)
+               N = atoi(argv[1]);
+
+       printf("N: %d\n", N);
+
+       atomic_init(&x, 0);
+       atomic_init(&y, 0);
+       atomic_init(&z, 0);
+
+       printf("Main thread: creating 2 threads\n");
+       thrd_create(&t1, (thrd_start_t)&a, NULL);
+       thrd_create(&t2, (thrd_start_t)&b, NULL);
+
+       thrd_join(t1);
+       thrd_join(t2);
+       printf("Main thread is finished\n");
+
+       return 0;
+}
diff --git a/test/litmus/store-buffer.cc b/test/litmus/store-buffer.cc
new file mode 100644 (file)
index 0000000..eb43d44
--- /dev/null
@@ -0,0 +1,36 @@
+#include <stdio.h>
+#include <threads.h>
+#include <atomic>
+
+std::atomic_int x;
+std::atomic_int y;
+
+static void a(void *obj)
+{
+       x.store(1, std::memory_order_relaxed);
+       printf("y: %d\n", y.load(std::memory_order_relaxed));
+}
+
+static void b(void *obj)
+{
+       y.store(1, std::memory_order_relaxed);
+       printf("x: %d\n", x.load(std::memory_order_relaxed));
+}
+
+int user_main(int argc, char **argv)
+{
+       thrd_t t1, t2;
+
+       atomic_init(&x, 0);
+       atomic_init(&y, 0);
+
+       printf("Main thread: creating 2 threads\n");
+       thrd_create(&t1, (thrd_start_t)&a, NULL);
+       thrd_create(&t2, (thrd_start_t)&b, NULL);
+
+       thrd_join(t1);
+       thrd_join(t2);
+       printf("Main thread is finished\n");
+
+       return 0;
+}
diff --git a/test/litmus/wrc.cc b/test/litmus/wrc.cc
new file mode 100644 (file)
index 0000000..225eb4b
--- /dev/null
@@ -0,0 +1,53 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <threads.h>
+#include <atomic>
+
+static int N = 2;
+
+std::atomic_int *x;
+
+static void a(void *obj)
+{
+       int idx = *((int *)obj);
+
+       if (idx > 0)
+               x[idx - 1].load(std::memory_order_relaxed);
+
+       if (idx < N)
+               x[idx].store(1, std::memory_order_relaxed);
+       else
+               x[0].load(std::memory_order_relaxed);
+}
+
+int user_main(int argc, char **argv)
+{
+       thrd_t *threads;
+       int *indexes;
+
+       if (argc > 1)
+               N = atoi(argv[1]);
+       if (N < 2) {
+               printf("Error: must have N >= 2\n");
+               return 1;
+       }
+       printf("N: %d\n", N);
+
+       threads = (thrd_t *)malloc((N + 1) * sizeof(thrd_t));
+       x = (std::atomic_int *)malloc(N * sizeof(std::atomic_int));
+       indexes = (int *)malloc((N + 1) * sizeof(int));
+       
+       for (int i = 0; i < N + 1; i++)
+               indexes[i] = i;
+
+       for (int i = 0; i < N; i++)
+               atomic_init(&x[i], 0);
+
+       for (int i = 0; i < N + 1; i++)
+               thrd_create(&threads[i], (thrd_start_t)&a, (void *)&indexes[i]);
+
+       for (int i = 0; i < N + 1; i++)
+               thrd_join(threads[i]);
+
+        return 0;
+}
index feac7766c9f68154e169c143d0903cbf18b44141..57ab54462bab9b03b48a87cf86578d06c8c5e070 100644 (file)
@@ -1,3 +1,4 @@
+#include <stdlib.h>
 #include <stdio.h>
 #include <threads.h>
 #include <stdatomic.h>
@@ -5,17 +6,22 @@
 #include "librace.h"
 
 atomic_int x;
+static int N = 2;
 
 static void a(void *obj)
 {
-       atomic_fetch_add_explicit(&x, 1, memory_order_relaxed);
-       atomic_fetch_add_explicit(&x, 1, memory_order_relaxed);
+       int i;
+       for (i = 0; i < N; i++)
+               atomic_fetch_add_explicit(&x, 1, memory_order_relaxed);
 }
 
 int user_main(int argc, char **argv)
 {
        thrd_t t1, t2;
 
+       if (argc > 1)
+               N = atoi(argv[1]);
+
        atomic_init(&x, 0);
        thrd_create(&t1, (thrd_start_t)&a, NULL);
        thrd_create(&t2, (thrd_start_t)&a, NULL);