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