#include "action.h"
#include "common.h"
#include "threads-model.h"
+#include "clockvector.h"
/** Initializes a CycleGraph object. */
CycleGraph::CycleGraph() :
- discovered(new HashTable<const CycleNode *, const CycleNode *, uintptr_t, 4, model_malloc, model_calloc, model_free>(16)),
- queue(new ModelVector<const CycleNode *>()),
- hasCycles(false),
- oldCycles(false)
+ queue(new SnapVector<const CycleNode *>())
{
}
CycleGraph::~CycleGraph()
{
delete queue;
- delete discovered;
}
/**
* @param action The ModelAction to find a node for
* @return The CycleNode paired with this action
*/
-CycleNode * CycleGraph::getNode(const ModelAction *action)
+CycleNode * CycleGraph::getNode(ModelAction *action)
{
CycleNode *node = getNode_noCreate(action);
if (node == NULL) {
* @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)
+void CycleGraph::addNodeEdge(CycleNode *fromnode, CycleNode *tonode, bool forceedge)
{
- if (fromnode->addEdge(tonode)) {
- rollbackvector.push_back(fromnode);
- if (!hasCycles)
- hasCycles = checkReachable(tonode, fromnode);
- } else
- return false;/* No new edge */
+ //quick check whether edge is redundant
+ if (checkReachable(fromnode, tonode) && !forceedge) {
+ return;
+ }
/*
- * If the fromnode has a rmwnode that is not the tonode, we should
- * follow its RMW chain to add an edge at the end, unless we encounter
- * tonode along the way
+ * If the fromnode has a rmwnode, we should
+ * follow its RMW chain to add an edge at the end.
*/
- CycleNode *rmwnode = fromnode->getRMW();
- if (rmwnode) {
- while (rmwnode != tonode && rmwnode->getRMW())
- rmwnode = rmwnode->getRMW();
-
- if (rmwnode != tonode) {
- if (rmwnode->addEdge(tonode)) {
- if (!hasCycles)
- hasCycles = checkReachable(tonode, rmwnode);
+ while (fromnode->getRMW()) {
+ CycleNode *nextnode = fromnode->getRMW();
+ if (nextnode == tonode)
+ break;
+ fromnode = nextnode;
+ }
- rollbackvector.push_back(rmwnode);
+ fromnode->addEdge(tonode); //Add edge to edgeSrcNode
+
+ /* Propagate clock vector changes */
+ if (tonode->cv->merge(fromnode->cv)) {
+ queue->push_back(tonode);
+ while(!queue->empty()) {
+ const CycleNode *node = queue->back();
+ queue->pop_back();
+ unsigned int numedges = node->getNumEdges();
+ for(unsigned int i = 0;i < numedges;i++) {
+ CycleNode * enode = node->getEdge(i);
+ if (enode->cv->merge(node->cv))
+ queue->push_back(enode);
}
}
}
- return true;
}
/**
* @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:
+ * the ModelAction from. The key differences are:
* -# No write can occur in between the @a rmw and @a from actions.
* -# Only one RMW action can read from a given write.
*
- * @param from The edge comes from this ModelAction/Promise
+ * @param from The edge comes from this ModelAction
* @param rmw The edge points to this ModelAction; this action must read from
- * the ModelAction/Promise from
+ * the ModelAction from
*/
-template <typename T>
-void CycleGraph::addRMWEdge(const T *from, const ModelAction *rmw)
+void CycleGraph::addRMWEdge(ModelAction *from, ModelAction *rmw)
{
ASSERT(from);
ASSERT(rmw);
CycleNode *fromnode = getNode(from);
CycleNode *rmwnode = getNode(rmw);
-
/* We assume that this RMW has no RMW reading from it yet */
ASSERT(!rmwnode->getRMW());
- /* Two RMW actions cannot read from the same write. */
- if (fromnode->setRMW(rmwnode))
- hasCycles = true;
- else
- rmwrollbackvector.push_back(fromnode);
+ fromnode->setRMW(rmwnode);
/* Transfer all outgoing edges from the from node to the rmw node */
/* This process should not add a cycle because either:
for (unsigned int i = 0;i < fromnode->getNumEdges();i++) {
CycleNode *tonode = fromnode->getEdge(i);
if (tonode != rmwnode) {
- if (rmwnode->addEdge(tonode))
- rollbackvector.push_back(rmwnode);
+ rmwnode->addEdge(tonode);
}
+ tonode->removeInEdge(fromnode);
}
+ fromnode->edges.clear();
- addNodeEdge(fromnode, rmwnode);
+ addNodeEdge(fromnode, rmwnode, true);
+}
+
+void CycleGraph::addEdges(SnapList<ModelAction *> * edgeset, ModelAction *to) {
+ for(sllnode<ModelAction*> * it = edgeset->begin();it!=NULL;) {
+ ModelAction *act = it->getVal();
+ CycleNode *node = getNode(act);
+ sllnode<ModelAction*> * it2 = it;
+ it2=it2->getNext();
+ for(;it2!=NULL; ) {
+ ModelAction *act2 = it2->getVal();
+ CycleNode *node2 = getNode(act2);
+ if (checkReachable(node, node2)) {
+ it = edgeset->erase(it);
+ goto endouterloop;
+ } else if (checkReachable(node2, node)) {
+ it2 = edgeset->erase(it2);
+ goto endinnerloop;
+ }
+ it2=it2->getNext();
+endinnerloop:
+ ;
+ }
+ it=it->getNext();
+endouterloop:
+ ;
+ }
+ for(sllnode<ModelAction*> *it = edgeset->begin();it!=NULL;it=it->getNext()) {
+ ModelAction *from = it->getVal();
+ addEdge(from, to, from->get_tid() == to->get_tid());
+ }
}
-/* Instantiate two forms of CycleGraph::addRMWEdge */
-template void CycleGraph::addRMWEdge(const ModelAction *from, const ModelAction *rmw);
/**
* @brief Adds an edge between objects
* @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)
+
+void CycleGraph::addEdge(ModelAction *from, ModelAction *to)
{
ASSERT(from);
ASSERT(to);
CycleNode *fromnode = getNode(from);
CycleNode *tonode = getNode(to);
- return addNodeEdge(fromnode, tonode);
+ addNodeEdge(fromnode, tonode, false);
+}
+
+void CycleGraph::addEdge(ModelAction *from, ModelAction *to, bool forceedge)
+{
+ ASSERT(from);
+ ASSERT(to);
+
+ CycleNode *fromnode = getNode(from);
+ CycleNode *tonode = getNode(to);
+
+ addNodeEdge(fromnode, tonode, forceedge);
}
-/* Instantiate four forms of CycleGraph::addEdge */
-template bool CycleGraph::addEdge(const ModelAction *from, const ModelAction *to);
#if SUPPORT_MOD_ORDER_DUMP
print_node(file, getNode(act), 1);
}
-template <typename T, typename U>
-void CycleGraph::dot_print_edge(FILE *file, const T *from, const U *to, const char *prop)
+void CycleGraph::dot_print_edge(FILE *file, const ModelAction *from, const ModelAction *to, const char *prop)
{
CycleNode *fromnode = getNode(from);
CycleNode *tonode = getNode(to);
print_edge(file, fromnode, tonode, prop);
}
-/* Instantiate two forms of CycleGraph::dot_print_edge */
-template void CycleGraph::dot_print_edge(FILE *file, const ModelAction *from, const ModelAction *to, const char *prop);
void CycleGraph::dumpNodes(FILE *file) const
{
*/
bool CycleGraph::checkReachable(const CycleNode *from, const CycleNode *to) const
{
- discovered->reset();
- queue->clear();
- queue->push_back(from);
- discovered->put(from, from);
- while (!queue->empty()) {
- const CycleNode *node = queue->back();
- queue->pop_back();
- if (node == to)
- return true;
- for (unsigned int i = 0;i < node->getNumEdges();i++) {
- CycleNode *next = node->getEdge(i);
- if (!discovered->contains(next)) {
- discovered->put(next, next);
- queue->push_back(next);
- }
- }
- }
- return false;
+ return to->cv->synchronized_since(from->action);
}
/**
- * 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
+ * Checks whether one ModelAction can reach another ModelAction
+ * @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
*/
-template <typename T, typename U>
-bool CycleGraph::checkReachable(const T *from, const U *to) const
+bool CycleGraph::checkReachable(const ModelAction *from, const ModelAction *to) const
{
CycleNode *fromnode = getNode_noCreate(from);
CycleNode *tonode = getNode_noCreate(to);
return checkReachable(fromnode, tonode);
}
-/* Instantiate four forms of CycleGraph::checkReachable */
-template bool CycleGraph::checkReachable(const ModelAction *from,
- const ModelAction *to) const;
-/** @brief Begin a new sequence of graph additions which can be rolled back */
-void CycleGraph::startChanges()
-{
- ASSERT(rollbackvector.empty());
- ASSERT(rmwrollbackvector.empty());
- ASSERT(oldCycles == hasCycles);
+void CycleGraph::freeAction(const ModelAction * act) {
+ CycleNode *cn = actionToNode.remove(act);
+ for(unsigned int i=0;i<cn->edges.size();i++) {
+ CycleNode *dst = cn->edges[i];
+ dst->removeInEdge(cn);
+ }
+ for(unsigned int i=0;i<cn->inedges.size();i++) {
+ CycleNode *src = cn->inedges[i];
+ src->removeEdge(cn);
+ }
+ delete cn;
}
-/** Commit changes to the cyclegraph. */
-void CycleGraph::commitChanges()
+/**
+ * @brief Constructor for a CycleNode
+ * @param act The ModelAction for this node
+ */
+CycleNode::CycleNode(ModelAction *act) :
+ action(act),
+ hasRMW(NULL),
+ cv(new ClockVector(NULL, act))
{
- rollbackvector.clear();
- rmwrollbackvector.clear();
- oldCycles = hasCycles;
}
-/** Rollback changes to the previous commit. */
-void CycleGraph::rollbackChanges()
-{
- for (unsigned int i = 0;i < rollbackvector.size();i++)
- rollbackvector[i]->removeEdge();
-
- for (unsigned int i = 0;i < rmwrollbackvector.size();i++)
- rmwrollbackvector[i]->clearRMW();
-
- hasCycles = oldCycles;
- rollbackvector.clear();
- rmwrollbackvector.clear();
+CycleNode::~CycleNode() {
+ delete cv;
}
-/** @returns whether a CycleGraph contains cycles. */
-bool CycleGraph::checkForCycles() const
-{
- return hasCycles;
+void CycleNode::removeInEdge(CycleNode *src) {
+ for(unsigned int i=0;i < inedges.size();i++) {
+ if (inedges[i] == src) {
+ inedges[i] = inedges[inedges.size()-1];
+ inedges.pop_back();
+ break;
+ }
+ }
}
-/**
- * @brief Constructor for a CycleNode
- * @param act The ModelAction for this node
- */
-CycleNode::CycleNode(const ModelAction *act) :
- action(act),
- hasRMW(NULL)
-{
+void CycleNode::removeEdge(CycleNode *dst) {
+ for(unsigned int i=0;i < edges.size();i++) {
+ if (edges[i] == dst) {
+ edges[i] = edges[edges.size()-1];
+ edges.pop_back();
+ break;
+ }
+ }
}
/**
}
/**
- * @param i The index of the back edge to return
- * @returns The CycleNode back-edge indexed by i
- */
-CycleNode * CycleNode::getBackEdge(unsigned int i) const
-{
- return back_edges[i];
-}
-
-/** @returns The number of edges entering this CycleNode */
-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(SnapVector<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
+ * @param i The index of the edge to return
+ * @returns The CycleNode edge indexed by i
*/
-CycleNode * CycleNode::removeEdge()
+CycleNode * CycleNode::getInEdge(unsigned int i) const
{
- if (edges.empty())
- return NULL;
-
- CycleNode *ret = edges.back();
- edges.pop_back();
- vector_remove_node(ret->back_edges, this);
- return ret;
+ return inedges[i];
}
-/**
- * @brief Remove a (back) edge from this CycleNode
- * @return The CycleNode which was popped, if one exists; otherwise NULL
- */
-CycleNode * CycleNode::removeBackEdge()
+/** @returns The number of edges leaving this CycleNode */
+unsigned int CycleNode::getNumInEdges() const
{
- if (back_edges.empty())
- return NULL;
-
- CycleNode *ret = back_edges.back();
- back_edges.pop_back();
- vector_remove_node(ret->edges, this);
- return ret;
+ return inedges.size();
}
/**
* @param node The node to which we add a directed edge
* @return True if this edge is a new edge; false otherwise
*/
-bool CycleNode::addEdge(CycleNode *node)
+void CycleNode::addEdge(CycleNode *node)
{
for (unsigned int i = 0;i < edges.size();i++)
if (edges[i] == node)
- return false;
+ return;
edges.push_back(node);
- node->back_edges.push_back(this);
- return true;
+ node->inedges.push_back(this);
}
/** @returns the RMW CycleNode that reads from the current CycleNode */