threads: prepare system to loop over many executions
[model-checker.git] / model.cc
1 #include <stdio.h>
2
3 #include "model.h"
4 #include "schedule.h"
5 #include "common.h"
6
7 ModelChecker *model;
8
9 ModelChecker::ModelChecker()
10 {
11         /* First thread created (system_thread) will have id 1 */
12         this->used_thread_id = 0;
13         /* Initialize default scheduler */
14         this->scheduler = new Scheduler();
15
16         this->current_action = NULL;
17         this->exploring = NULL;
18         this->nextThread = THREAD_ID_T_NONE;
19
20         rootNode = new TreeNode(NULL);
21         currentNode = rootNode;
22         action_trace = new action_list_t();
23 }
24
25 ModelChecker::~ModelChecker()
26 {
27         delete action_trace;
28         delete this->scheduler;
29         delete rootNode;
30 }
31
32 void ModelChecker::assign_id(Thread *t)
33 {
34         t->set_id(++used_thread_id);
35 }
36
37 void ModelChecker::add_system_thread(Thread *t)
38 {
39         this->system_thread = t;
40 }
41
42 Thread *ModelChecker::schedule_next_thread()
43 {
44         Thread *t;
45         if (nextThread == THREAD_ID_T_NONE)
46                 return NULL;
47         t = thread_map[nextThread];
48         if (t == NULL)
49                 DEBUG("*** error: thread not in thread_map: id = %d\n", nextThread);
50         return t;
51 }
52
53 /*
54  * get_next_replay_thread() - Choose the next thread in the replay sequence
55  *
56  * If we've reached the 'diverge' point, then we pick a thread from the
57  *   backtracking set.
58  * Otherwise, we simply return the next thread in the sequence.
59  */
60 thread_id_t ModelChecker::get_next_replay_thread()
61 {
62         ModelAction *next;
63         thread_id_t tid;
64
65         next = exploring->get_state();
66
67         if (next == exploring->get_diverge()) {
68                 TreeNode *node = next->get_node();
69
70                 /* Reached divergence point; discard our current 'exploring' */
71                 DEBUG("*** Discard 'Backtrack' object ***\n");
72                 tid = node->getNextBacktrack();
73                 delete exploring;
74                 exploring = NULL;
75         } else {
76                 tid = next->get_tid();
77         }
78         DEBUG("*** ModelChecker chose next thread = %d ***\n", tid);
79         return tid;
80 }
81
82 thread_id_t ModelChecker::advance_backtracking_state()
83 {
84         /* Have we completed exploring the preselected path? */
85         if (exploring == NULL)
86                 return THREAD_ID_T_NONE;
87
88         /* Else, we are trying to replay an execution */
89         exploring->advance_state();
90         if (exploring->get_state() == NULL)
91                 DEBUG("*** error: reached end of backtrack trace\n");
92
93         return get_next_replay_thread();
94 }
95
96 bool ModelChecker::next_execution()
97 {
98         return false;
99 }
100
101 ModelAction *ModelChecker::get_last_conflict(ModelAction *act)
102 {
103         void *loc = act->get_location();
104         action_type type = act->get_type();
105         thread_id_t id = act->get_tid();
106
107         switch (type) {
108                 case THREAD_CREATE:
109                 case THREAD_YIELD:
110                 case THREAD_JOIN:
111                         return NULL;
112                 case ATOMIC_READ:
113                 case ATOMIC_WRITE:
114                 default:
115                         break;
116         }
117         /* linear search: from most recent to oldest */
118         action_list_t::reverse_iterator rit;
119         for (rit = action_trace->rbegin(); rit != action_trace->rend(); rit++) {
120                 ModelAction *prev = *rit;
121                 if (prev->get_location() != loc)
122                         continue;
123                 if (type == ATOMIC_READ && prev->get_type() != ATOMIC_WRITE)
124                         continue;
125                 /* Conflict from the same thread is not really a conflict */
126                 if (id == prev->get_tid())
127                         return NULL;
128                 return prev;
129         }
130         return NULL;
131 }
132
133 void ModelChecker::set_backtracking(ModelAction *act)
134 {
135         ModelAction *prev;
136         TreeNode *node;
137
138         prev = get_last_conflict(act);
139         if (prev == NULL)
140                 return;
141
142         node = prev->get_node();
143
144         /* Check if this has been explored already */
145         if (node->hasBeenExplored(act->get_tid()))
146                 return;
147         /* If this is a new backtracking point, mark the tree */
148         if (node->setBacktrack(act->get_tid()) != 0)
149                 return;
150
151         printf("Setting backtrack: conflict = %d, instead tid = %d\n",
152                         prev->get_tid(), act->get_tid());
153         prev->print();
154         act->print();
155
156         Backtrack *back = new Backtrack(prev, action_trace);
157         backtrack_list.push_back(back);
158 }
159
160 void ModelChecker::check_current_action(void)
161 {
162         ModelAction *next = this->current_action;
163
164         if (!next) {
165                 DEBUG("trying to push NULL action...\n");
166                 return;
167         }
168         nextThread = advance_backtracking_state();
169         next->set_node(currentNode);
170         set_backtracking(next);
171         currentNode = currentNode->exploreChild(next->get_tid());
172         this->action_trace->push_back(next);
173 }
174
175 void ModelChecker::print_trace(void)
176 {
177         action_list_t::iterator it;
178
179         printf("\n");
180         printf("---------------------------------------------------------------------\n");
181         printf("Total nodes created: %d\n\n", TreeNode::getTotalNodes());
182
183         scheduler->print();
184
185         printf("\nTrace:\n\n");
186
187         for (it = action_trace->begin(); it != action_trace->end(); it++) {
188                 DBG();
189                 (*it)->print();
190         }
191         printf("---------------------------------------------------------------------\n");
192 }
193
194 int ModelChecker::add_thread(Thread *t)
195 {
196         thread_map[t->get_id()] = t;
197         return 0;
198 }
199
200 int ModelChecker::switch_to_master(ModelAction *act)
201 {
202         Thread *old, *next;
203
204         DBG();
205         old = thread_current();
206         set_current_action(act);
207         old->set_state(THREAD_READY);
208         next = system_thread;
209         return old->swap(next);
210 }
211
212 ModelAction::ModelAction(action_type_t type, memory_order order, void *loc, int value)
213 {
214         Thread *t = thread_current();
215         ModelAction *act = this;
216
217         act->type = type;
218         act->order = order;
219         act->location = loc;
220         act->tid = t->get_id();
221         act->value = value;
222 }
223
224 void ModelAction::print(void)
225 {
226         const char *type_str;
227         switch (this->type) {
228         case THREAD_CREATE:
229                 type_str = "thread create";
230                 break;
231         case THREAD_YIELD:
232                 type_str = "thread yield";
233                 break;
234         case THREAD_JOIN:
235                 type_str = "thread join";
236                 break;
237         case ATOMIC_READ:
238                 type_str = "atomic read";
239                 break;
240         case ATOMIC_WRITE:
241                 type_str = "atomic write";
242                 break;
243         default:
244                 type_str = "unknown type";
245         }
246
247         printf("Thread: %d\tAction: %s\tMO: %d\tLoc: %#014zx\tValue: %d\n", tid, type_str, order, (size_t)location, value);
248 }