cyclegraph: don't export CycleNode::edges directly, use accessors
[model-checker.git] / cyclegraph.cc
1 #include "cyclegraph.h"
2 #include "action.h"
3 #include "common.h"
4 #include "promise.h"
5 #include "model.h"
6
7 /** Initializes a CycleGraph object. */
8 CycleGraph::CycleGraph() :
9         discovered(new HashTable<CycleNode *, CycleNode *, uintptr_t, 4, model_malloc, model_calloc, model_free>(16)),
10         hasCycles(false),
11         oldCycles(false),
12         hasRMWViolation(false),
13         oldRMWViolation(false)
14 {
15 }
16
17 /** CycleGraph destructor */
18 CycleGraph::~CycleGraph() {
19 }
20
21 /**
22  * @brief Returns the CycleNode corresponding to a given ModelAction
23  * @param action The ModelAction to find a node for
24  * @return The CycleNode paired with this action
25  */
26 CycleNode * CycleGraph::getNode(const ModelAction *action) {
27         CycleNode *node=actionToNode.get(action);
28         if (node==NULL) {
29                 node=new CycleNode(action);
30                 actionToNode.put(action, node);
31 #if SUPPORT_MOD_ORDER_DUMP
32                 nodeList.push_back(node);
33 #endif
34         }
35         return node;
36 }
37
38 /**
39  * Adds an edge between two ModelActions. The ModelAction @a to is ordered
40  * after the ModelAction @a from.
41  * @param to The edge points to this ModelAction
42  * @param from The edge comes from this ModelAction
43  */
44 void CycleGraph::addEdge(const ModelAction *from, const ModelAction *to) {
45         ASSERT(from);
46         ASSERT(to);
47
48         CycleNode *fromnode=getNode(from);
49         CycleNode *tonode=getNode(to);
50
51         if (!hasCycles) {
52                 // Reflexive edges are cycles
53                 hasCycles = (from == to);
54         }
55         if (!hasCycles) {
56                 // Check for Cycles
57                 hasCycles=checkReachable(tonode, fromnode);
58         }
59
60         if (fromnode->addEdge(tonode))
61                 rollbackvector.push_back(fromnode);
62
63
64         CycleNode * rmwnode=fromnode->getRMW();
65
66         //If the fromnode has a rmwnode that is not the tonode, we
67         //should add an edge between its rmwnode and the tonode
68
69         //If tonode is also a rmw, don't do this check as the execution is
70         //doomed and we'll catch the problem elsewhere, but we want to allow
71         //for the possibility of sending to's write value to rmwnode
72
73         if (rmwnode!=NULL&&!to->is_rmw()) {
74                 if (!hasCycles) {
75                         // Check for Cycles
76                         hasCycles=checkReachable(tonode, rmwnode);
77                 }
78
79                 if (rmwnode->addEdge(tonode))
80                         rollbackvector.push_back(rmwnode);
81         }
82 }
83
84 /** Handles special case of a RMW action.  The ModelAction rmw reads
85  *  from the ModelAction from.  The key differences are: (1) no write
86  *  can occur in between the rmw and the from action.  Only one RMW
87  *  action can read from a given write.
88  */
89 void CycleGraph::addRMWEdge(const ModelAction *from, const ModelAction *rmw) {
90         ASSERT(from);
91         ASSERT(rmw);
92
93         CycleNode *fromnode=getNode(from);
94         CycleNode *rmwnode=getNode(rmw);
95
96         /* Two RMW actions cannot read from the same write. */
97         if (fromnode->setRMW(rmwnode)) {
98                 hasRMWViolation=true;
99         } else {
100                 rmwrollbackvector.push_back(fromnode);
101         }
102
103         /* Transfer all outgoing edges from the from node to the rmw node */
104         /* This process should not add a cycle because either:
105          * (1) The rmw should not have any incoming edges yet if it is the
106          * new node or
107          * (2) the fromnode is the new node and therefore it should not
108          * have any outgoing edges.
109          */
110         for (unsigned int i = 0; i < fromnode->getNumEdges(); i++) {
111                 CycleNode *tonode = fromnode->getEdge(i);
112                 if (tonode!=rmwnode) {
113                         if (rmwnode->addEdge(tonode))
114                                 rollbackvector.push_back(rmwnode);
115                 }
116         }
117
118
119         if (!hasCycles) {
120                 // Reflexive edges are cycles
121                 hasCycles = (from == rmw);
122         }
123         if (!hasCycles) {
124                 // With promises we could be setting up a cycle here if we aren't
125                 // careful...avoid it..
126                 hasCycles=checkReachable(rmwnode, fromnode);
127         }
128         if (fromnode->addEdge(rmwnode))
129                 rollbackvector.push_back(fromnode);
130 }
131
132 #if SUPPORT_MOD_ORDER_DUMP
133 void CycleGraph::dumpNodes(FILE *file) {
134         for (unsigned int i=0;i<nodeList.size();i++) {
135                 CycleNode *cn=nodeList[i];
136                 const ModelAction *action=cn->getAction();
137                 fprintf(file, "N%u [label=\"%u, T%u\"];\n",action->get_seq_number(),action->get_seq_number(), action->get_tid());
138                 if (cn->getRMW()!=NULL) {
139                         fprintf(file, "N%u -> N%u[style=dotted];\n", action->get_seq_number(), cn->getRMW()->getAction()->get_seq_number());
140                 }
141                 for (unsigned int j = 0; j < cn->getNumEdges(); j++) {
142                         CycleNode *dst = cn->getEdge(j);
143                         const ModelAction *dstaction=dst->getAction();
144                         fprintf(file, "N%u -> N%u;\n", action->get_seq_number(), dstaction->get_seq_number());
145                 }
146         }
147 }
148
149 void CycleGraph::dumpGraphToFile(const char *filename) {
150         char buffer[200];
151         sprintf(buffer, "%s.dot",filename);
152         FILE *file=fopen(buffer, "w");
153         fprintf(file, "digraph %s {\n",filename);
154         dumpNodes(file);
155         fprintf(file,"}\n");
156         fclose(file);   
157 }
158 #endif
159
160 /**
161  * Checks whether one ModelAction can reach another.
162  * @param from The ModelAction from which to begin exploration
163  * @param to The ModelAction to reach
164  * @return True, @a from can reach @a to; otherwise, false
165  */
166 bool CycleGraph::checkReachable(const ModelAction *from, const ModelAction *to) {
167         CycleNode *fromnode = actionToNode.get(from);
168         CycleNode *tonode = actionToNode.get(to);
169
170         if (!fromnode || !tonode)
171                 return false;
172
173         return checkReachable(fromnode, tonode);
174 }
175
176 /**
177  * Checks whether one CycleNode can reach another.
178  * @param from The CycleNode from which to begin exploration
179  * @param to The CycleNode to reach
180  * @return True, @a from can reach @a to; otherwise, false
181  */
182 bool CycleGraph::checkReachable(CycleNode *from, CycleNode *to) {
183         std::vector<CycleNode *, ModelAlloc<CycleNode *> > queue;
184         discovered->reset();
185
186         queue.push_back(from);
187         discovered->put(from, from);
188         while(!queue.empty()) {
189                 CycleNode * node=queue.back();
190                 queue.pop_back();
191                 if (node==to)
192                         return true;
193
194                 for (unsigned int i = 0; i < node->getNumEdges(); i++) {
195                         CycleNode *next = node->getEdge(i);
196                         if (!discovered->contains(next)) {
197                                 discovered->put(next,next);
198                                 queue.push_back(next);
199                         }
200                 }
201         }
202         return false;
203 }
204
205 bool CycleGraph::checkPromise(const ModelAction *fromact, Promise *promise) {
206         std::vector<CycleNode *, ModelAlloc<CycleNode *> > queue;
207         discovered->reset();
208         CycleNode *from = actionToNode.get(fromact);
209
210
211         queue.push_back(from);
212         discovered->put(from, from);
213         while(!queue.empty()) {
214                 CycleNode * node=queue.back();
215                 queue.pop_back();
216
217                 if (promise->increment_threads(node->getAction()->get_tid())) {
218                         return true;
219                 }
220
221                 for (unsigned int i = 0; i < node->getNumEdges(); i++) {
222                         CycleNode *next = node->getEdge(i);
223                         if (!discovered->contains(next)) {
224                                 discovered->put(next,next);
225                                 queue.push_back(next);
226                         }
227                 }
228         }
229         return false;
230 }
231
232 void CycleGraph::startChanges() {
233         ASSERT(rollbackvector.size()==0);
234         ASSERT(rmwrollbackvector.size()==0);
235         ASSERT(oldCycles==hasCycles);
236         ASSERT(oldRMWViolation==hasRMWViolation);
237 }
238
239 /** Commit changes to the cyclegraph. */
240 void CycleGraph::commitChanges() {
241         rollbackvector.resize(0);
242         rmwrollbackvector.resize(0);
243         oldCycles=hasCycles;
244         oldRMWViolation=hasRMWViolation;
245 }
246
247 /** Rollback changes to the previous commit. */
248 void CycleGraph::rollbackChanges() {
249         for (unsigned int i = 0; i < rollbackvector.size(); i++) {
250                 rollbackvector[i]->popEdge();
251         }
252
253         for (unsigned int i = 0; i < rmwrollbackvector.size(); i++) {
254                 rmwrollbackvector[i]->clearRMW();
255         }
256
257         hasCycles = oldCycles;
258         hasRMWViolation = oldRMWViolation;
259         rollbackvector.resize(0);
260         rmwrollbackvector.resize(0);
261 }
262
263 /** @returns whether a CycleGraph contains cycles. */
264 bool CycleGraph::checkForCycles() {
265         return hasCycles;
266 }
267
268 bool CycleGraph::checkForRMWViolation() {
269         return hasRMWViolation;
270 }
271
272 /**
273  * Constructor for a CycleNode.
274  * @param modelaction The ModelAction for this node
275  */
276 CycleNode::CycleNode(const ModelAction *modelaction) :
277         action(modelaction),
278         hasRMW(NULL)
279 {
280 }
281
282 /**
283  * @param i The index of the edge to return
284  * @returns The a CycleNode edge indexed by i
285  */
286 CycleNode * CycleNode::getEdge(unsigned int i) const
287 {
288         return edges[i];
289 }
290
291 /** @returns The number of edges leaving this CycleNode */
292 unsigned int CycleNode::getNumEdges() const
293 {
294         return edges.size();
295 }
296
297 /**
298  * Adds an edge from this CycleNode to another CycleNode.
299  * @param node The node to which we add a directed edge
300  */
301 bool CycleNode::addEdge(CycleNode *node) {
302         for(unsigned int i=0;i<edges.size();i++)
303                 if (edges[i]==node)
304                         return false;
305         edges.push_back(node);
306         return true;
307 }
308
309 /** @returns the RMW CycleNode that reads from the current CycleNode */
310 CycleNode * CycleNode::getRMW() {
311         return hasRMW;
312 }
313
314 /**
315  * Set a RMW action node that reads from the current CycleNode.
316  * @param node The RMW that reads from the current node
317  * @return True, if this node already was read by another RMW; false otherwise
318  * @see CycleGraph::addRMWEdge
319  */
320 bool CycleNode::setRMW(CycleNode *node) {
321         if (hasRMW!=NULL)
322                 return true;
323         hasRMW=node;
324         return false;
325 }