model: index thread_map by int, not thread_id_t
[c11tester.git] / model.cc
1 #include <stdio.h>
2
3 #include "model.h"
4 #include "action.h"
5 #include "tree.h"
6 #include "schedule.h"
7 #include "common.h"
8
9 #define INITIAL_THREAD_ID       0
10
11 ModelChecker *model;
12
13 ModelChecker::ModelChecker()
14 {
15         /* First thread created will have id (INITIAL_THREAD_ID + 1) */
16         this->used_thread_id = INITIAL_THREAD_ID;
17         /* Initialize default scheduler */
18         this->scheduler = new Scheduler();
19
20         num_executions = 0;
21         this->current_action = NULL;
22         this->exploring = NULL;
23         this->nextThread = THREAD_ID_T_NONE;
24
25         rootNode = new TreeNode(NULL);
26         currentNode = rootNode;
27         action_trace = new action_list_t();
28 }
29
30 ModelChecker::~ModelChecker()
31 {
32         delete action_trace;
33         delete this->scheduler;
34         delete rootNode;
35 }
36
37 void ModelChecker::reset_to_initial_state()
38 {
39         DEBUG("+++ Resetting to initial state +++\n");
40         std::map<int, class Thread *>::iterator it;
41         for (it = thread_map.begin(); it != thread_map.end(); it++)
42                 delete (*it).second;
43         thread_map.clear();
44         action_trace = new action_list_t();
45         currentNode = rootNode;
46         current_action = NULL;
47         used_thread_id = INITIAL_THREAD_ID;
48         /* scheduler reset ? */
49 }
50
51 thread_id_t ModelChecker::get_next_id()
52 {
53         return ++used_thread_id;
54 }
55
56 Thread * ModelChecker::schedule_next_thread()
57 {
58         Thread *t;
59         if (nextThread == THREAD_ID_T_NONE)
60                 return NULL;
61         t = thread_map[id_to_int(nextThread)];
62         if (t == NULL)
63                 DEBUG("*** error: thread not in thread_map: id = %d\n", nextThread);
64         return t;
65 }
66
67 /*
68  * get_next_replay_thread() - Choose the next thread in the replay sequence
69  *
70  * If we've reached the 'diverge' point, then we pick a thread from the
71  *   backtracking set.
72  * Otherwise, we simply return the next thread in the sequence.
73  */
74 thread_id_t ModelChecker::get_next_replay_thread()
75 {
76         ModelAction *next;
77         thread_id_t tid;
78
79         next = exploring->get_state();
80
81         if (next == exploring->get_diverge()) {
82                 TreeNode *node = next->get_node();
83
84                 /* Reached divergence point; discard our current 'exploring' */
85                 DEBUG("*** Discard 'Backtrack' object ***\n");
86                 tid = node->getNextBacktrack();
87                 delete exploring;
88                 exploring = NULL;
89         } else {
90                 tid = next->get_tid();
91         }
92         DEBUG("*** ModelChecker chose next thread = %d ***\n", tid);
93         return tid;
94 }
95
96 thread_id_t ModelChecker::advance_backtracking_state()
97 {
98         /* Have we completed exploring the preselected path? */
99         if (exploring == NULL)
100                 return THREAD_ID_T_NONE;
101
102         /* Else, we are trying to replay an execution */
103         exploring->advance_state();
104         if (exploring->get_state() == NULL)
105                 DEBUG("*** error: reached end of backtrack trace\n");
106
107         return get_next_replay_thread();
108 }
109
110 bool ModelChecker::next_execution()
111 {
112         num_executions++;
113         print_summary();
114         if ((exploring = model->get_next_backtrack()) == NULL)
115                 return false;
116         model->reset_to_initial_state();
117         nextThread = get_next_replay_thread();
118         return true;
119 }
120
121 ModelAction * ModelChecker::get_last_conflict(ModelAction *act)
122 {
123         action_type type = act->get_type();
124
125         switch (type) {
126                 case THREAD_CREATE:
127                 case THREAD_YIELD:
128                 case THREAD_JOIN:
129                         return NULL;
130                 case ATOMIC_READ:
131                 case ATOMIC_WRITE:
132                 default:
133                         break;
134         }
135         /* linear search: from most recent to oldest */
136         action_list_t::reverse_iterator rit;
137         for (rit = action_trace->rbegin(); rit != action_trace->rend(); rit++) {
138                 ModelAction *prev = *rit;
139                 if (act->is_dependent(prev))
140                         return prev;
141         }
142         return NULL;
143 }
144
145 void ModelChecker::set_backtracking(ModelAction *act)
146 {
147         ModelAction *prev;
148         TreeNode *node;
149
150         prev = get_last_conflict(act);
151         if (prev == NULL)
152                 return;
153
154         node = prev->get_node();
155
156         /* Check if this has been explored already */
157         if (node->hasBeenExplored(act->get_tid()))
158                 return;
159         /* If this is a new backtracking point, mark the tree */
160         if (node->setBacktrack(act->get_tid()) != 0)
161                 return;
162
163         DEBUG("Setting backtrack: conflict = %d, instead tid = %d\n",
164                         prev->get_tid(), act->get_tid());
165         if (DBG_ENABLED()) {
166                 prev->print();
167                 act->print();
168         }
169
170         Backtrack *back = new Backtrack(prev, action_trace);
171         backtrack_list.push_back(back);
172 }
173
174 Backtrack * ModelChecker::get_next_backtrack()
175 {
176         Backtrack *next;
177         if (backtrack_list.empty())
178                 return NULL;
179         next = backtrack_list.back();
180         backtrack_list.pop_back();
181         return next;
182 }
183
184 void ModelChecker::check_current_action(void)
185 {
186         ModelAction *next = this->current_action;
187
188         if (!next) {
189                 DEBUG("trying to push NULL action...\n");
190                 return;
191         }
192         current_action = NULL;
193         nextThread = advance_backtracking_state();
194         next->set_node(currentNode);
195         set_backtracking(next);
196         currentNode = currentNode->exploreChild(next->get_tid());
197         this->action_trace->push_back(next);
198 }
199
200 void ModelChecker::print_summary(void)
201 {
202         action_list_t::iterator it;
203
204         printf("\n");
205         printf("---------------------------------------------------------------------\n");
206         printf("Number of executions: %d\n", num_executions);
207         printf("Total nodes created: %d\n\n", TreeNode::getTotalNodes());
208
209         scheduler->print();
210
211         printf("Trace:\n\n");
212
213         for (it = action_trace->begin(); it != action_trace->end(); it++) {
214                 DBG();
215                 (*it)->print();
216         }
217         printf("---------------------------------------------------------------------\n");
218 }
219
220 int ModelChecker::add_thread(Thread *t)
221 {
222         thread_map[id_to_int(t->get_id())] = t;
223         scheduler->add_thread(t);
224         return 0;
225 }
226
227 void ModelChecker::remove_thread(Thread *t)
228 {
229         scheduler->remove_thread(t);
230 }
231
232 int ModelChecker::switch_to_master(ModelAction *act)
233 {
234         Thread *old;
235
236         DBG();
237         old = thread_current();
238         set_current_action(act);
239         old->set_state(THREAD_READY);
240         return Thread::swap(old, get_system_context());
241 }
242
243 ModelAction::ModelAction(action_type_t type, memory_order order, void *loc, int value)
244 {
245         Thread *t = thread_current();
246         ModelAction *act = this;
247
248         act->type = type;
249         act->order = order;
250         act->location = loc;
251         act->tid = t->get_id();
252         act->value = value;
253 }
254
255 bool ModelAction::is_read()
256 {
257         return type == ATOMIC_READ;
258 }
259
260 bool ModelAction::is_write()
261 {
262         return type == ATOMIC_WRITE;
263 }
264
265 bool ModelAction::is_acquire()
266 {
267         switch (order) {
268         case memory_order_acquire:
269         case memory_order_acq_rel:
270         case memory_order_seq_cst:
271                 return true;
272         default:
273                 return false;
274         }
275 }
276
277 bool ModelAction::is_release()
278 {
279         switch (order) {
280         case memory_order_release:
281         case memory_order_acq_rel:
282         case memory_order_seq_cst:
283                 return true;
284         default:
285                 return false;
286         }
287 }
288
289 bool ModelAction::same_var(ModelAction *act)
290 {
291         return location == act->location;
292 }
293
294 bool ModelAction::same_thread(ModelAction *act)
295 {
296         return tid == act->tid;
297 }
298
299 bool ModelAction::is_dependent(ModelAction *act)
300 {
301         if (!is_read() && !is_write())
302                 return false;
303         if (!act->is_read() && !act->is_write())
304                 return false;
305         if (same_var(act) && !same_thread(act) &&
306                         (is_write() || act->is_write()))
307                 return true;
308         return false;
309 }
310
311 void ModelAction::print(void)
312 {
313         const char *type_str;
314         switch (this->type) {
315         case THREAD_CREATE:
316                 type_str = "thread create";
317                 break;
318         case THREAD_YIELD:
319                 type_str = "thread yield";
320                 break;
321         case THREAD_JOIN:
322                 type_str = "thread join";
323                 break;
324         case ATOMIC_READ:
325                 type_str = "atomic read";
326                 break;
327         case ATOMIC_WRITE:
328                 type_str = "atomic write";
329                 break;
330         default:
331                 type_str = "unknown type";
332         }
333
334         printf("Thread: %d\tAction: %s\tMO: %d\tLoc: %#014zx\tValue: %d\n", id_to_int(tid), type_str, order, (size_t)location, value);
335 }