Defining the interfaces to add various regions to snapshot
[c11tester.git] / model.cc
index 19194cda06a766ccb84b1674b6fb5142bcc4bef4..51715d22614afaa3e61afa05df063cddba64c4a6 100644 (file)
--- a/model.cc
+++ b/model.cc
 #include <stdio.h>
 
 #include "model.h"
+#include "action.h"
+#include "tree.h"
 #include "schedule.h"
+#include "snapshot-interface.h"
+#undef DEBUG
+#include "common.h"
+
+#define INITIAL_THREAD_ID      0
+
+class Backtrack {
+public:
+       Backtrack(ModelAction *d, action_list_t *t) {
+               diverge = d;
+               actionTrace = t;
+               iter = actionTrace->begin();
+       }
+       ModelAction * get_diverge() { return diverge; }
+       action_list_t * get_trace() { return actionTrace; }
+       void advance_state() { iter++; }
+       ModelAction * get_state() {
+               return iter == actionTrace->end() ? NULL : *iter;
+       }
+private:
+       ModelAction *diverge;
+       action_list_t *actionTrace;
+       /* points to position in actionTrace as we replay */
+       action_list_t::iterator iter;
+};
 
 ModelChecker *model;
 
+void free_action_list(action_list_t *list)
+{
+       action_list_t::iterator it;
+       for (it = list->begin(); it != list->end(); it++)
+               delete (*it);
+       delete list;
+}
+
 ModelChecker::ModelChecker()
 {
-       /* First thread created (system_thread) will have id 1 */
-       this->used_thread_id = 0;
+       /* First thread created will have id INITIAL_THREAD_ID */
+       this->next_thread_id = INITIAL_THREAD_ID;
+       used_sequence_numbers = 0;
        /* Initialize default scheduler */
-       this->scheduler = new DefaultScheduler();
+       this->scheduler = new Scheduler();
+
+       num_executions = 0;
+       this->current_action = NULL;
+       this->exploring = NULL;
+       this->nextThread = THREAD_ID_T_NONE;
+
+       rootNode = new TreeNode();
+       currentNode = rootNode;
+       action_trace = new action_list_t();
 }
 
 ModelChecker::~ModelChecker()
 {
+       std::map<int, class Thread *, std::less< int >, MyAlloc< std::pair< int, class Thread * > > >::iterator it;
+       for (it = thread_map.begin(); it != thread_map.end(); it++)
+               delete (*it).second;
+       thread_map.clear();
+
+       free_action_list(action_trace);
+
        delete this->scheduler;
+       delete rootNode;
+}
+
+void ModelChecker::reset_to_initial_state()
+{
+       DEBUG("+++ Resetting to initial state +++\n");
+       std::map<int, class Thread *, std::less< int >, MyAlloc< std::pair< int, class Thread * > > >::iterator it;
+       for (it = thread_map.begin(); it != thread_map.end(); it++)
+               delete (*it).second;
+       thread_map.clear();
+       action_trace = new action_list_t();
+       currentNode = rootNode;
+       current_action = NULL;
+       next_thread_id = INITIAL_THREAD_ID;
+       used_sequence_numbers = 0;
+       /* scheduler reset ? */
+}
+
+thread_id_t ModelChecker::get_next_id()
+{
+       return next_thread_id++;
+}
+
+int ModelChecker::get_next_seq_num()
+{
+       return ++used_sequence_numbers;
+}
+
+Thread * ModelChecker::schedule_next_thread()
+{
+       Thread *t;
+       if (nextThread == THREAD_ID_T_NONE)
+               return NULL;
+       t = thread_map[id_to_int(nextThread)];
+
+       ASSERT(t != NULL);
+
+       return t;
+}
+
+/*
+ * get_next_replay_thread() - Choose the next thread in the replay sequence
+ *
+ * If we've reached the 'diverge' point, then we pick a thread from the
+ *   backtracking set.
+ * Otherwise, we simply return the next thread in the sequence.
+ */
+thread_id_t ModelChecker::get_next_replay_thread()
+{
+       ModelAction *next;
+       thread_id_t tid;
+
+       next = exploring->get_state();
+
+       if (next == exploring->get_diverge()) {
+               TreeNode *node = next->get_node();
+
+               /* Reached divergence point; discard our current 'exploring' */
+               DEBUG("*** Discard 'Backtrack' object ***\n");
+               tid = node->getNextBacktrack();
+               delete exploring;
+               exploring = NULL;
+       } else {
+               tid = next->get_tid();
+       }
+       DEBUG("*** ModelChecker chose next thread = %d ***\n", tid);
+       return tid;
+}
+
+thread_id_t ModelChecker::advance_backtracking_state()
+{
+       /* Have we completed exploring the preselected path? */
+       if (exploring == NULL)
+               return THREAD_ID_T_NONE;
+
+       /* Else, we are trying to replay an execution */
+       exploring->advance_state();
+
+       ASSERT(exploring->get_state() != NULL);
+
+       return get_next_replay_thread();
 }
 
-void ModelChecker::assign_id(struct thread *t)
+bool ModelChecker::next_execution()
 {
-       t->id = ++this->used_thread_id;
+       DBG();
+
+       num_executions++;
+       print_summary();
+       if ((exploring = model->get_next_backtrack()) == NULL)
+               return false;
+
+       if (DBG_ENABLED()) {
+               printf("Next execution will diverge at:\n");
+               exploring->get_diverge()->print();
+               print_list(exploring->get_trace());
+       }
+
+       model->reset_to_initial_state();
+       nextThread = get_next_replay_thread();
+       return true;
+}
+
+ModelAction * ModelChecker::get_last_conflict(ModelAction *act)
+{
+       action_type type = act->get_type();
+
+       switch (type) {
+               case THREAD_CREATE:
+               case THREAD_YIELD:
+               case THREAD_JOIN:
+                       return NULL;
+               case ATOMIC_READ:
+               case ATOMIC_WRITE:
+               default:
+                       break;
+       }
+       /* linear search: from most recent to oldest */
+       action_list_t::reverse_iterator rit;
+       for (rit = action_trace->rbegin(); rit != action_trace->rend(); rit++) {
+               ModelAction *prev = *rit;
+               if (act->is_dependent(prev))
+                       return prev;
+       }
+       return NULL;
+}
+
+void ModelChecker::set_backtracking(ModelAction *act)
+{
+       ModelAction *prev;
+       TreeNode *node;
+       Thread *t = get_thread(act->get_tid());
+
+       prev = get_last_conflict(act);
+       if (prev == NULL)
+               return;
+
+       node = prev->get_node();
+
+       while (t && !node->is_enabled(t))
+               t = t->get_parent();
+
+       /* Check if this has been explored already */
+       if (node->hasBeenExplored(t->get_id()))
+               return;
+       /* If this is a new backtracking point, mark the tree */
+       if (node->setBacktrack(t->get_id()) != 0)
+               return;
+
+       DEBUG("Setting backtrack: conflict = %d, instead tid = %d\n",
+                       prev->get_tid(), t->get_id());
+       if (DBG_ENABLED()) {
+               prev->print();
+               act->print();
+       }
+
+       Backtrack *back = new Backtrack(prev, action_trace);
+       backtrack_list.push_back(back);
 }
 
-void ModelChecker::add_system_thread(struct thread *t)
+Backtrack * ModelChecker::get_next_backtrack()
 {
-       this->system_thread = t;
+       Backtrack *next;
+       if (backtrack_list.empty())
+               return NULL;
+       next = backtrack_list.back();
+       backtrack_list.pop_back();
+       return next;
+}
+
+void ModelChecker::check_current_action(void)
+{
+       ModelAction *next = this->current_action;
+
+       if (!next) {
+               DEBUG("trying to push NULL action...\n");
+               return;
+       }
+       current_action = NULL;
+       nextThread = advance_backtracking_state();
+       next->set_node(currentNode);
+       set_backtracking(next);
+       currentNode = currentNode->explore_child(next);
+       this->action_trace->push_back(next);
 }
 
-ModelAction::ModelAction(action_type_t type, memory_order order, void *loc, int value)
+void ModelChecker::print_summary(void)
 {
-       struct thread *t = thread_current();
-       ModelAction *act = this;
+       printf("\n");
+       printf("Number of executions: %d\n", num_executions);
+       printf("Total nodes created: %d\n", TreeNode::getTotalNodes());
+
+       scheduler->print();
+
+       print_list(action_trace);
+       printf("\n");
 
-       act->type = type;
-       act->order = order;
-       act->location = loc;
-       act->tid = t->id;
-       act->value = value;
 }
 
-void ModelAction::print(void)
+void ModelChecker::print_list(action_list_t *list)
 {
-       const char *type_str;
-       switch (this->type) {
-       case THREAD_CREATE:
-               type_str = "thread create";
-               break;
-       case THREAD_YIELD:
-               type_str = "thread yield";
-               break;
-       case THREAD_JOIN:
-               type_str = "thread join";
-               break;
-       case ATOMIC_READ:
-               type_str = "atomic read";
-               break;
-       case ATOMIC_WRITE:
-               type_str = "atomic write";
-               break;
-       default:
-               type_str = "unknown type";
+       action_list_t::iterator it;
+
+       printf("---------------------------------------------------------------------\n");
+       printf("Trace:\n");
+
+       for (it = list->begin(); it != list->end(); it++) {
+               (*it)->print();
        }
+       printf("---------------------------------------------------------------------\n");
+}
+
+int ModelChecker::add_thread(Thread *t)
+{
+       thread_map[id_to_int(t->get_id())] = t;
+       scheduler->add_thread(t);
+       return 0;
+}
+
+void ModelChecker::remove_thread(Thread *t)
+{
+       scheduler->remove_thread(t);
+}
+
+int ModelChecker::switch_to_master(ModelAction *act)
+{
+       Thread *old;
 
-       printf("Thread: %d\tAction: %s\tMO: %d\tLoc: %#014zx\tValue: %d\n", tid, type_str, order, (size_t)location, value);
+       DBG();
+       old = thread_current();
+       set_current_action(act);
+       old->set_state(THREAD_READY);
+       return Thread::swap(old, get_system_context());
 }