model: remove free_action_list() function
[c11tester.git] / model.cc
1 #include <stdio.h>
2
3 #include "model.h"
4 #include "action.h"
5 #include "nodestack.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         /* Initialize default scheduler */
16         scheduler(new Scheduler()),
17         /* First thread created will have id INITIAL_THREAD_ID */
18         next_thread_id(INITIAL_THREAD_ID),
19         used_sequence_numbers(0),
20
21         num_executions(0),
22         current_action(NULL),
23         diverge(NULL),
24         nextThread(THREAD_ID_T_NONE),
25         action_trace(new action_list_t()),
26         node_stack(new NodeStack()),
27         next_backtrack(NULL)
28 {
29 }
30
31 ModelChecker::~ModelChecker()
32 {
33         std::map<int, class Thread *>::iterator it;
34         for (it = thread_map.begin(); it != thread_map.end(); it++)
35                 delete (*it).second;
36         thread_map.clear();
37
38         delete action_trace;
39
40         delete node_stack;
41         delete scheduler;
42 }
43
44 void ModelChecker::reset_to_initial_state()
45 {
46         DEBUG("+++ Resetting to initial state +++\n");
47         std::map<int, class Thread *>::iterator it;
48         for (it = thread_map.begin(); it != thread_map.end(); it++)
49                 delete (*it).second;
50         thread_map.clear();
51         delete action_trace;
52         action_trace = new action_list_t();
53         node_stack->reset_execution();
54         current_action = NULL;
55         next_thread_id = INITIAL_THREAD_ID;
56         used_sequence_numbers = 0;
57         nextThread = 0;
58         next_backtrack = NULL;
59         /* scheduler reset ? */
60 }
61
62 thread_id_t ModelChecker::get_next_id()
63 {
64         return next_thread_id++;
65 }
66
67 int ModelChecker::get_next_seq_num()
68 {
69         return ++used_sequence_numbers;
70 }
71
72 Thread * ModelChecker::schedule_next_thread()
73 {
74         Thread *t;
75         if (nextThread == THREAD_ID_T_NONE)
76                 return NULL;
77         t = thread_map[id_to_int(nextThread)];
78
79         ASSERT(t != NULL);
80
81         return t;
82 }
83
84 /*
85  * get_next_replay_thread() - Choose the next thread in the replay sequence
86  *
87  * If we've reached the 'diverge' point, then we pick a thread from the
88  *   backtracking set.
89  * Otherwise, we simply return the next thread in the sequence.
90  */
91 thread_id_t ModelChecker::get_next_replay_thread()
92 {
93         ModelAction *next;
94         thread_id_t tid;
95
96         /* Have we completed exploring the preselected path? */
97         if (diverge == NULL)
98                 return THREAD_ID_T_NONE;
99
100         /* Else, we are trying to replay an execution */
101         next = node_stack->get_next()->get_action();
102
103         if (next == diverge) {
104                 Node *node = next->get_node();
105
106                 /* Reached divergence point */
107                 DEBUG("*** Divergence point ***\n");
108                 tid = node->get_next_backtrack();
109                 diverge = NULL;
110         } else {
111                 tid = next->get_tid();
112         }
113         DEBUG("*** ModelChecker chose next thread = %d ***\n", tid);
114         return tid;
115 }
116
117 bool ModelChecker::next_execution()
118 {
119         DBG();
120
121         num_executions++;
122         print_summary();
123         if ((diverge = model->get_next_backtrack()) == NULL)
124                 return false;
125
126         if (DBG_ENABLED()) {
127                 printf("Next execution will diverge at:\n");
128                 diverge->print();
129         }
130
131         model->reset_to_initial_state();
132         return true;
133 }
134
135 ModelAction * ModelChecker::get_last_conflict(ModelAction *act)
136 {
137         action_type type = act->get_type();
138
139         switch (type) {
140                 case THREAD_CREATE:
141                 case THREAD_YIELD:
142                 case THREAD_JOIN:
143                         return NULL;
144                 case ATOMIC_READ:
145                 case ATOMIC_WRITE:
146                 default:
147                         break;
148         }
149         /* linear search: from most recent to oldest */
150         action_list_t::reverse_iterator rit;
151         for (rit = action_trace->rbegin(); rit != action_trace->rend(); rit++) {
152                 ModelAction *prev = *rit;
153                 if (act->is_dependent(prev))
154                         return prev;
155         }
156         return NULL;
157 }
158
159 void ModelChecker::set_backtracking(ModelAction *act)
160 {
161         ModelAction *prev;
162         Node *node;
163         Thread *t = get_thread(act->get_tid());
164
165         prev = get_last_conflict(act);
166         if (prev == NULL)
167                 return;
168
169         node = prev->get_node();
170
171         while (!node->is_enabled(t))
172                 t = t->get_parent();
173
174         /* Check if this has been explored already */
175         if (node->has_been_explored(t->get_id()))
176                 return;
177
178         if (!next_backtrack || *prev > *next_backtrack)
179                 next_backtrack = prev;
180
181         /* If this is a new backtracking point, mark the tree */
182         if (!node->set_backtrack(t->get_id()))
183                 return;
184         DEBUG("Setting backtrack: conflict = %d, instead tid = %d\n",
185                         prev->get_tid(), t->get_id());
186         if (DBG_ENABLED()) {
187                 prev->print();
188                 act->print();
189         }
190 }
191
192 ModelAction * ModelChecker::get_next_backtrack()
193 {
194         ModelAction *next = next_backtrack;
195         next_backtrack = NULL;
196         return next;
197 }
198
199 void ModelChecker::check_current_action(void)
200 {
201         Node *currnode;
202
203         ModelAction *curr = this->current_action;
204         current_action = NULL;
205         if (!curr) {
206                 DEBUG("trying to push NULL action...\n");
207                 return;
208         }
209
210         curr = node_stack->explore_action(curr);
211         nextThread = get_next_replay_thread();
212
213         currnode = curr->get_node();
214
215         if (!currnode->backtrack_empty())
216                 if (!next_backtrack || *curr > *next_backtrack)
217                         next_backtrack = curr;
218
219         set_backtracking(curr);
220         this->action_trace->push_back(curr);
221 }
222
223 void ModelChecker::print_summary(void)
224 {
225         printf("\n");
226         printf("Number of executions: %d\n", num_executions);
227         printf("Total nodes created: %d\n", Node::get_total_nodes());
228
229         scheduler->print();
230
231         print_list(action_trace);
232         printf("\n");
233 }
234
235 void ModelChecker::print_list(action_list_t *list)
236 {
237         action_list_t::iterator it;
238
239         printf("---------------------------------------------------------------------\n");
240         printf("Trace:\n");
241
242         for (it = list->begin(); it != list->end(); it++) {
243                 (*it)->print();
244         }
245         printf("---------------------------------------------------------------------\n");
246 }
247
248 int ModelChecker::add_thread(Thread *t)
249 {
250         thread_map[id_to_int(t->get_id())] = t;
251         scheduler->add_thread(t);
252         return 0;
253 }
254
255 void ModelChecker::remove_thread(Thread *t)
256 {
257         scheduler->remove_thread(t);
258 }
259
260 int ModelChecker::switch_to_master(ModelAction *act)
261 {
262         Thread *old;
263
264         DBG();
265         old = thread_current();
266         set_current_action(act);
267         old->set_state(THREAD_READY);
268         return Thread::swap(old, get_system_context());
269 }