commit untested condvar code
[c11tester.git] / action.cc
1 #include <stdio.h>
2 #define __STDC_FORMAT_MACROS
3 #include <inttypes.h>
4 #include <vector>
5
6 #include "model.h"
7 #include "action.h"
8 #include "clockvector.h"
9 #include "common.h"
10 #include "threads-model.h"
11 #include "nodestack.h"
12
13 #define ACTION_INITIAL_CLOCK 0
14
15 /**
16  * @brief Construct a new ModelAction
17  *
18  * @param type The type of action
19  * @param order The memory order of this action. A "don't care" for non-ATOMIC
20  * actions (e.g., THREAD_* or MODEL_* actions).
21  * @param loc The location that this action acts upon
22  * @param value (optional) A value associated with the action (e.g., the value
23  * read or written). Defaults to a given macro constant, for debugging purposes.
24  * @param thread (optional) The Thread in which this action occurred. If NULL
25  * (default), then a Thread is assigned according to the scheduler.
26  */
27 ModelAction::ModelAction(action_type_t type, memory_order order, void *loc,
28                 uint64_t value, Thread *thread) :
29         type(type),
30         order(order),
31         location(loc),
32         value(value),
33         reads_from(NULL),
34         node(NULL),
35         seq_number(ACTION_INITIAL_CLOCK),
36         cv(NULL),
37         sleep_flag(false)
38 {
39         /* References to NULL atomic variables can end up here */
40         ASSERT(loc || type == MODEL_FIXUP_RELSEQ);
41
42         Thread *t = thread ? thread : thread_current();
43         this->tid = t->get_id();
44 }
45
46 /** @brief ModelAction destructor */
47 ModelAction::~ModelAction()
48 {
49         /**
50          * We can't free the clock vector:
51          * Clock vectors are snapshotting state. When we delete model actions,
52          * they are at the end of the node list and have invalid old clock
53          * vectors which have already been rolled back to an unallocated state.
54          */
55
56         /*
57          if (cv)
58                 delete cv; */
59 }
60
61 void ModelAction::copy_from_new(ModelAction *newaction)
62 {
63         seq_number = newaction->seq_number;
64 }
65
66 void ModelAction::set_seq_number(modelclock_t num)
67 {
68         ASSERT(seq_number == ACTION_INITIAL_CLOCK);
69         seq_number = num;
70 }
71
72 bool ModelAction::is_relseq_fixup() const
73 {
74         return type == MODEL_FIXUP_RELSEQ;
75 }
76
77 bool ModelAction::is_mutex_op() const
78 {
79         return type == ATOMIC_LOCK || type == ATOMIC_TRYLOCK || type == ATOMIC_UNLOCK || type == ATOMIC_WAIT || type == ATOMIC_NOTIFY_ONE || type == ATOMIC_NOTIFY_ALL;
80 }
81
82 bool ModelAction::is_lock() const
83 {
84         return type == ATOMIC_LOCK;
85 }
86
87 bool ModelAction::is_wait() const {
88         return type == ATOMIC_WAIT;
89 }
90
91 bool ModelAction::is_notify() const {
92         return type==ATOMIC_NOTIFY_ONE || type==ATOMIC_NOTIFY_ALL;
93 }
94
95 bool ModelAction::is_notify_one() const {
96         return type==ATOMIC_NOTIFY_ONE;
97 }
98
99 bool ModelAction::is_unlock() const
100 {
101         return type == ATOMIC_UNLOCK;
102 }
103
104 bool ModelAction::is_trylock() const
105 {
106         return type == ATOMIC_TRYLOCK;
107 }
108
109 bool ModelAction::is_success_lock() const
110 {
111         return type == ATOMIC_LOCK || (type == ATOMIC_TRYLOCK && value == VALUE_TRYSUCCESS);
112 }
113
114 bool ModelAction::is_failed_trylock() const
115 {
116         return (type == ATOMIC_TRYLOCK && value == VALUE_TRYFAILED);
117 }
118
119 bool ModelAction::is_read() const
120 {
121         return type == ATOMIC_READ || type == ATOMIC_RMWR || type == ATOMIC_RMW;
122 }
123
124 bool ModelAction::is_write() const
125 {
126         return type == ATOMIC_WRITE || type == ATOMIC_RMW || type == ATOMIC_INIT;
127 }
128
129 bool ModelAction::could_be_write() const
130 {
131         return is_write() || is_rmwr();
132 }
133
134 bool ModelAction::is_rmwr() const
135 {
136         return type == ATOMIC_RMWR;
137 }
138
139 bool ModelAction::is_rmw() const
140 {
141         return type == ATOMIC_RMW;
142 }
143
144 bool ModelAction::is_rmwc() const
145 {
146         return type == ATOMIC_RMWC;
147 }
148
149 bool ModelAction::is_fence() const 
150 {
151         return type == ATOMIC_FENCE;
152 }
153
154 bool ModelAction::is_initialization() const
155 {
156         return type == ATOMIC_INIT;
157 }
158
159 bool ModelAction::is_acquire() const
160 {
161         switch (order) {
162         case std::memory_order_acquire:
163         case std::memory_order_acq_rel:
164         case std::memory_order_seq_cst:
165                 return true;
166         default:
167                 return false;
168         }
169 }
170
171 bool ModelAction::is_release() const
172 {
173         switch (order) {
174         case std::memory_order_release:
175         case std::memory_order_acq_rel:
176         case std::memory_order_seq_cst:
177                 return true;
178         default:
179                 return false;
180         }
181 }
182
183 bool ModelAction::is_seqcst() const
184 {
185         return order==std::memory_order_seq_cst;
186 }
187
188 bool ModelAction::same_var(const ModelAction *act) const
189 {
190         return location == act->location;
191 }
192
193 bool ModelAction::same_thread(const ModelAction *act) const
194 {
195         return tid == act->tid;
196 }
197
198 void ModelAction::copy_typeandorder(ModelAction * act) {
199         this->type = act->type;
200         this->order = act->order;
201 }
202
203 /** This method changes an existing read part of an RMW action into either:
204  *  (1) a full RMW action in case of the completed write or
205  *  (2) a READ action in case a failed action.
206  * @todo  If the memory_order changes, we may potentially need to update our
207  * clock vector.
208  */
209 void ModelAction::process_rmw(ModelAction * act) {
210         this->order=act->order;
211         if (act->is_rmwc())
212                 this->type=ATOMIC_READ;
213         else if (act->is_rmw()) {
214                 this->type=ATOMIC_RMW;
215                 this->value=act->value;
216         }
217 }
218
219 /** The is_synchronizing method should only explore interleavings if:
220  *  (1) the operations are seq_cst and don't commute or
221  *  (2) the reordering may establish or break a synchronization relation.
222  *  Other memory operations will be dealt with by using the reads_from
223  *  relation.
224  *
225  *  @param act is the action to consider exploring a reordering.
226  *  @return tells whether we have to explore a reordering.
227  */
228 bool ModelAction::could_synchronize_with(const ModelAction *act) const
229 {
230         //Same thread can't be reordered
231         if (same_thread(act))
232                 return false;
233
234         // Different locations commute
235         if (!same_var(act))
236                 return false;
237
238         // Explore interleavings of seqcst writes to guarantee total order
239         // of seq_cst operations that don't commute
240         if ((could_be_write() || act->could_be_write()) && is_seqcst() && act->is_seqcst())
241                 return true;
242
243         // Explore synchronizing read/write pairs
244         if (is_read() && is_acquire() && act->could_be_write() && act->is_release())
245                 return true;
246
247         // Otherwise handle by reads_from relation
248         return false;
249 }
250
251 bool ModelAction::is_conflicting_lock(const ModelAction *act) const
252 {
253         //Must be different threads to reorder
254         if (same_thread(act))
255                 return false;
256         
257         //Try to reorder a lock past a successful lock
258         if (act->is_success_lock())
259                 return true;
260         
261         //Try to push a successful trylock past an unlock
262         if (act->is_unlock() && is_trylock() && value == VALUE_TRYSUCCESS)
263                 return true;
264
265         //Try to push a successful trylock past a wait
266         if (act->is_wait() && is_trylock() && value == VALUE_TRYSUCCESS)
267                 return true;
268
269         return false;
270 }
271
272 /**
273  * Create a new clock vector for this action. Note that this function allows a
274  * user to clobber (and leak) a ModelAction's existing clock vector. A user
275  * should ensure that the vector has already either been rolled back
276  * (effectively "freed") or freed.
277  *
278  * @param parent A ModelAction from which to inherit a ClockVector
279  */
280 void ModelAction::create_cv(const ModelAction *parent)
281 {
282         if (parent)
283                 cv = new ClockVector(parent->cv, this);
284         else
285                 cv = new ClockVector(NULL, this);
286 }
287
288 void ModelAction::set_try_lock(bool obtainedlock) {
289         if (obtainedlock)
290                 value=VALUE_TRYSUCCESS;
291         else
292                 value=VALUE_TRYFAILED;
293 }
294
295 /**
296  * Update the model action's read_from action
297  * @param act The action to read from; should be a write
298  * @return True if this read established synchronization
299  */
300 bool ModelAction::read_from(const ModelAction *act)
301 {
302         ASSERT(cv);
303         reads_from = act;
304         if (act != NULL && this->is_acquire()) {
305                 rel_heads_list_t release_heads;
306                 model->get_release_seq_heads(this, &release_heads);
307                 int num_heads = release_heads.size();
308                 for (unsigned int i = 0; i < release_heads.size(); i++)
309                         if (!synchronize_with(release_heads[i])) {
310                                 model->set_bad_synchronization();
311                                 num_heads--;
312                         }
313                 return num_heads > 0;
314         }
315         return false;
316 }
317
318 /**
319  * Synchronize the current thread with the thread corresponding to the
320  * ModelAction parameter.
321  * @param act The ModelAction to synchronize with
322  * @return True if this is a valid synchronization; false otherwise
323  */
324 bool ModelAction::synchronize_with(const ModelAction *act) {
325         if (*this < *act && type != THREAD_JOIN && type != ATOMIC_LOCK)
326                 return false;
327         model->check_promises(act->get_tid(), cv, act->cv);
328         cv->merge(act->cv);
329         return true;
330 }
331
332 bool ModelAction::has_synchronized_with(const ModelAction *act) const
333 {
334         return cv->has_synchronized_with(act->cv);
335 }
336
337 /**
338  * Check whether 'this' happens before act, according to the memory-model's
339  * happens before relation. This is checked via the ClockVector constructs.
340  * @return true if this action's thread has synchronized with act's thread
341  * since the execution of act, false otherwise.
342  */
343 bool ModelAction::happens_before(const ModelAction *act) const
344 {
345         return act->cv->synchronized_since(this);
346 }
347
348 /** @brief Print nicely-formatted info about this ModelAction */
349 void ModelAction::print() const
350 {
351         const char *type_str, *mo_str;
352         switch (this->type) {
353         case MODEL_FIXUP_RELSEQ:
354                 type_str = "relseq fixup";
355                 break;
356         case THREAD_CREATE:
357                 type_str = "thread create";
358                 break;
359         case THREAD_START:
360                 type_str = "thread start";
361                 break;
362         case THREAD_YIELD:
363                 type_str = "thread yield";
364                 break;
365         case THREAD_JOIN:
366                 type_str = "thread join";
367                 break;
368         case THREAD_FINISH:
369                 type_str = "thread finish";
370                 break;
371         case ATOMIC_READ:
372                 type_str = "atomic read";
373                 break;
374         case ATOMIC_WRITE:
375                 type_str = "atomic write";
376                 break;
377         case ATOMIC_RMW:
378                 type_str = "atomic rmw";
379                 break;
380         case ATOMIC_FENCE:
381                 type_str = "fence";
382                 break;
383         case ATOMIC_RMWR:
384                 type_str = "atomic rmwr";
385                 break;
386         case ATOMIC_RMWC:
387                 type_str = "atomic rmwc";
388                 break;
389         case ATOMIC_INIT:
390                 type_str = "init atomic";
391                 break;
392         case ATOMIC_LOCK:
393                 type_str = "lock";
394                 break;
395         case ATOMIC_UNLOCK:
396                 type_str = "unlock";
397                 break;
398         case ATOMIC_TRYLOCK:
399                 type_str = "trylock";
400                 break;
401         default:
402                 type_str = "unknown type";
403         }
404
405         uint64_t valuetoprint=type==ATOMIC_READ?(reads_from!=NULL?reads_from->value:VALUE_NONE):value;
406
407         switch (this->order) {
408         case std::memory_order_relaxed:
409                 mo_str = "relaxed";
410                 break;
411         case std::memory_order_acquire:
412                 mo_str = "acquire";
413                 break;
414         case std::memory_order_release:
415                 mo_str = "release";
416                 break;
417         case std::memory_order_acq_rel:
418                 mo_str = "acq_rel";
419                 break;
420         case std::memory_order_seq_cst:
421                 mo_str = "seq_cst";
422                 break;
423         default:
424                 mo_str = "unknown";
425                 break;
426         }
427
428         printf("(%4d) Thread: %-2d   Action: %-13s   MO: %7s  Loc: %14p   Value: %-#18" PRIx64,
429                         seq_number, id_to_int(tid), type_str, mo_str, location, valuetoprint);
430         if (is_read()) {
431                 if (reads_from)
432                         printf("  Rf: %-3d", reads_from->get_seq_number());
433                 else
434                         printf("  Rf: ?  ");
435         }
436         if (cv) {
437                 if (is_read())
438                         printf(" ");
439                 else
440                         printf("          ");
441                 cv->print();
442         } else
443                 printf("\n");
444 }
445
446 /** @brief Print nicely-formatted info about this ModelAction */
447 unsigned int ModelAction::hash() const
448 {
449         unsigned int hash=(unsigned int) this->type;
450         hash^=((unsigned int)this->order)<<3;
451         hash^=seq_number<<5;
452         hash^=tid<<6;
453
454         if (is_read()) {
455                 if (reads_from)
456                         hash^=reads_from->get_seq_number();
457         }
458         return hash;
459 }