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