model: rename isfinalfeasible -> is_feasible_prefix_ignore_relseq
[c11tester.git] / model.cc
index 2fa54d6e9ba1e76be577ec0625f6138ee7d1c5d4..da68074240d9ea2b416b40665ef7843d2c7df7ae 100644 (file)
--- a/model.cc
+++ b/model.cc
 #include "promise.h"
 #include "datarace.h"
 #include "threads-model.h"
+#include "output.h"
 
 #define INITIAL_THREAD_ID      0
 
 ModelChecker *model;
 
+struct bug_message {
+       bug_message(const char *str) {
+               const char *fmt = "  [BUG] %s\n";
+               msg = (char *)snapshot_malloc(strlen(fmt) + strlen(str));
+               sprintf(msg, fmt, str);
+       }
+       ~bug_message() { if (msg) snapshot_free(msg); }
+
+       char *msg;
+       void print() { model_print("%s", msg); }
+
+       SNAPSHOTALLOC
+};
+
+/**
+ * Structure for holding small ModelChecker members that should be snapshotted
+ */
+struct model_snapshot_members {
+       model_snapshot_members() :
+               current_action(NULL),
+               /* First thread created will have id INITIAL_THREAD_ID */
+               next_thread_id(INITIAL_THREAD_ID),
+               used_sequence_numbers(0),
+               nextThread(NULL),
+               next_backtrack(NULL),
+               bugs(),
+               stats(),
+               failed_promise(false),
+               too_many_reads(false),
+               bad_synchronization(false),
+               asserted(false)
+       { }
+
+       ~model_snapshot_members() {
+               for (unsigned int i = 0; i < bugs.size(); i++)
+                       delete bugs[i];
+               bugs.clear();
+       }
+
+       ModelAction *current_action;
+       unsigned int next_thread_id;
+       modelclock_t used_sequence_numbers;
+       Thread *nextThread;
+       ModelAction *next_backtrack;
+       std::vector< bug_message *, SnapshotAlloc<bug_message *> > bugs;
+       struct execution_stats stats;
+       bool failed_promise;
+       bool too_many_reads;
+       /** @brief Incorrectly-ordered synchronization was made */
+       bool bad_synchronization;
+       bool asserted;
+
+       SNAPSHOTALLOC
+};
+
 /** @brief Constructor */
 ModelChecker::ModelChecker(struct model_params params) :
        /* Initialize default scheduler */
        params(params),
        scheduler(new Scheduler()),
-       num_executions(0),
-       num_feasible_executions(0),
        diverge(NULL),
        earliest_diverge(NULL),
        action_trace(new action_list_t()),
        thread_map(new HashTable<int, Thread *, int>()),
-       obj_map(new HashTable<const void *, action_list_t, uintptr_t, 4>()),
-       lock_waiters_map(new HashTable<const void *, action_list_t, uintptr_t, 4>()),
-       condvar_waiters_map(new HashTable<const void *, action_list_t, uintptr_t, 4>()),
-       obj_thrd_map(new HashTable<void *, std::vector<action_list_t>, uintptr_t, 4 >()),
+       obj_map(new HashTable<const void *, action_list_t *, uintptr_t, 4>()),
+       lock_waiters_map(new HashTable<const void *, action_list_t *, uintptr_t, 4>()),
+       condvar_waiters_map(new HashTable<const void *, action_list_t *, uintptr_t, 4>()),
+       obj_thrd_map(new HashTable<void *, std::vector<action_list_t> *, uintptr_t, 4 >()),
        promises(new std::vector< Promise *, SnapshotAlloc<Promise *> >()),
        futurevalues(new std::vector< struct PendingFutureValue, SnapshotAlloc<struct PendingFutureValue> >()),
        pending_rel_seqs(new std::vector< struct release_seq *, SnapshotAlloc<struct release_seq *> >()),
        thrd_last_action(new std::vector< ModelAction *, SnapshotAlloc<ModelAction *> >(1)),
        node_stack(new NodeStack()),
-       mo_graph(new CycleGraph()),
-       failed_promise(false),
-       too_many_reads(false),
-       asserted(false),
-       bad_synchronization(false)
+       priv(new struct model_snapshot_members()),
+       mo_graph(new CycleGraph())
 {
-       /* Allocate this "size" on the snapshotting heap */
-       priv = (struct model_snapshot_members *)calloc(1, sizeof(*priv));
-       /* First thread created will have id INITIAL_THREAD_ID */
-       priv->next_thread_id = INITIAL_THREAD_ID;
-
        /* Initialize a model-checker thread, for special ModelActions */
        model_thread = new Thread(get_next_id());
        thread_map->put(id_to_int(model_thread->get_id()), model_thread);
@@ -77,6 +123,25 @@ ModelChecker::~ModelChecker()
        delete node_stack;
        delete scheduler;
        delete mo_graph;
+       delete priv;
+}
+
+static action_list_t * get_safe_ptr_action(HashTable<const void *, action_list_t *, uintptr_t, 4> * hash, void * ptr) {
+       action_list_t * tmp=hash->get(ptr);
+       if (tmp==NULL) {
+               tmp=new action_list_t();
+               hash->put(ptr, tmp);
+       }
+       return tmp;
+}
+
+static std::vector<action_list_t> * get_safe_ptr_vect_action(HashTable<void *, std::vector<action_list_t> *, uintptr_t, 4> * hash, void * ptr) {
+       std::vector<action_list_t> * tmp=hash->get(ptr);
+       if (tmp==NULL) {
+               tmp=new std::vector<action_list_t>();
+               hash->put(ptr, tmp);
+       }
+       return tmp;
 }
 
 /**
@@ -87,10 +152,10 @@ void ModelChecker::reset_to_initial_state()
 {
        DEBUG("+++ Resetting to initial state +++\n");
        node_stack->reset_execution();
-       failed_promise = false;
-       too_many_reads = false;
-       bad_synchronization = false;
-       reset_asserted();
+
+       /* Print all model-checker output before rollback */
+       fflush(model_out);
+
        snapshotObject->backTrackBeforeStep(0);
 }
 
@@ -101,7 +166,7 @@ thread_id_t ModelChecker::get_next_id()
 }
 
 /** @return the number of user threads created during this execution */
-unsigned int ModelChecker::get_num_threads()
+unsigned int ModelChecker::get_num_threads() const
 {
        return priv->next_thread_id;
 }
@@ -208,7 +273,7 @@ Thread * ModelChecker::get_next_thread(ModelAction *curr)
        return thread_map->get(id_to_int(tid));
 }
 
-/** 
+/**
  * We need to know what the next actions of all threads in the sleep
  * set will be.  This method computes them and stores the actions at
  * the corresponding thread object's pending action.
@@ -218,7 +283,8 @@ void ModelChecker::execute_sleep_set() {
        for(unsigned int i=0;i<get_num_threads();i++) {
                thread_id_t tid=int_to_id(i);
                Thread *thr=get_thread(tid);
-               if ( scheduler->get_enabled(thr) == THREAD_SLEEP_SET ) {
+               if ( scheduler->get_enabled(thr) == THREAD_SLEEP_SET &&
+                                thr->get_pending() == NULL ) {
                        thr->set_state(THREAD_RUNNING);
                        scheduler->next_thread(thr);
                        Thread::swap(&system_context, thr);
@@ -240,7 +306,170 @@ void ModelChecker::wake_up_sleeping_actions(ModelAction * curr) {
                                scheduler->remove_sleep(thr);
                        }
                }
-       }       
+       }
+}
+
+/** @brief Alert the model-checker that an incorrectly-ordered
+ * synchronization was made */
+void ModelChecker::set_bad_synchronization()
+{
+       priv->bad_synchronization = true;
+}
+
+bool ModelChecker::has_asserted() const
+{
+       return priv->asserted;
+}
+
+void ModelChecker::set_assert()
+{
+       priv->asserted = true;
+}
+
+/**
+ * Check if we are in a deadlock. Should only be called at the end of an
+ * execution, although it should not give false positives in the middle of an
+ * execution (there should be some ENABLED thread).
+ *
+ * @return True if program is in a deadlock; false otherwise
+ */
+bool ModelChecker::is_deadlocked() const
+{
+       bool blocking_threads = false;
+       for (unsigned int i = 0; i < get_num_threads(); i++) {
+               thread_id_t tid = int_to_id(i);
+               if (is_enabled(tid))
+                       return false;
+               Thread *t = get_thread(tid);
+               if (!t->is_model_thread() && t->get_pending())
+                       blocking_threads = true;
+       }
+       return blocking_threads;
+}
+
+/**
+ * Check if this is a complete execution. That is, have all thread completed
+ * execution (rather than exiting because sleep sets have forced a redundant
+ * execution).
+ *
+ * @return True if the execution is complete.
+ */
+bool ModelChecker::is_complete_execution() const
+{
+       for (unsigned int i = 0; i < get_num_threads(); i++)
+               if (is_enabled(int_to_id(i)))
+                       return false;
+       return true;
+}
+
+/**
+ * @brief Assert a bug in the executing program.
+ *
+ * Use this function to assert any sort of bug in the user program. If the
+ * current trace is feasible (actually, a prefix of some feasible execution),
+ * then this execution will be aborted, printing the appropriate message. If
+ * the current trace is not yet feasible, the error message will be stashed and
+ * printed if the execution ever becomes feasible.
+ *
+ * @param msg Descriptive message for the bug (do not include newline char)
+ * @return True if bug is immediately-feasible
+ */
+bool ModelChecker::assert_bug(const char *msg)
+{
+       priv->bugs.push_back(new bug_message(msg));
+
+       if (isfeasibleprefix()) {
+               set_assert();
+               return true;
+       }
+       return false;
+}
+
+/**
+ * @brief Assert a bug in the executing program, asserted by a user thread
+ * @see ModelChecker::assert_bug
+ * @param msg Descriptive message for the bug (do not include newline char)
+ */
+void ModelChecker::assert_user_bug(const char *msg)
+{
+       /* If feasible bug, bail out now */
+       if (assert_bug(msg))
+               switch_to_master(NULL);
+}
+
+/** @return True, if any bugs have been reported for this execution */
+bool ModelChecker::have_bug_reports() const
+{
+       return priv->bugs.size() != 0;
+}
+
+/** @brief Print bug report listing for this execution (if any bugs exist) */
+void ModelChecker::print_bugs() const
+{
+       if (have_bug_reports()) {
+               model_print("Bug report: %zu bug%s detected\n",
+                               priv->bugs.size(),
+                               priv->bugs.size() > 1 ? "s" : "");
+               for (unsigned int i = 0; i < priv->bugs.size(); i++)
+                       priv->bugs[i]->print();
+       }
+}
+
+/**
+ * @brief Record end-of-execution stats
+ *
+ * Must be run when exiting an execution. Records various stats.
+ * @see struct execution_stats
+ */
+void ModelChecker::record_stats()
+{
+       stats.num_total++;
+       if (!isfeasibleprefix())
+               stats.num_infeasible++;
+       else if (have_bug_reports())
+               stats.num_buggy_executions++;
+       else if (is_complete_execution())
+               stats.num_complete++;
+       else
+               stats.num_redundant++;
+}
+
+/** @brief Print execution stats */
+void ModelChecker::print_stats() const
+{
+       model_print("Number of complete, bug-free executions: %d\n", stats.num_complete);
+       model_print("Number of redundant executions: %d\n", stats.num_redundant);
+       model_print("Number of buggy executions: %d\n", stats.num_buggy_executions);
+       model_print("Number of infeasible executions: %d\n", stats.num_infeasible);
+       model_print("Total executions: %d\n", stats.num_total);
+       model_print("Total nodes created: %d\n", node_stack->get_total_nodes());
+}
+
+/**
+ * @brief End-of-exeuction print
+ * @param printbugs Should any existing bugs be printed?
+ */
+void ModelChecker::print_execution(bool printbugs) const
+{
+       print_program_output();
+
+       if (DBG_ENABLED() || params.verbose) {
+               model_print("Earliest divergence point since last feasible execution:\n");
+               if (earliest_diverge)
+                       earliest_diverge->print();
+               else
+                       model_print("(Not set)\n");
+
+               model_print("\n");
+               print_stats();
+       }
+
+       /* Don't print invalid bugs */
+       if (printbugs)
+               print_bugs();
+
+       model_print("\n");
+       print_summary();
 }
 
 /**
@@ -253,32 +482,34 @@ void ModelChecker::wake_up_sleeping_actions(ModelAction * curr) {
 bool ModelChecker::next_execution()
 {
        DBG();
+       /* Is this execution a feasible execution that's worth bug-checking? */
+       bool complete = isfeasibleprefix() && (is_complete_execution() ||
+                       have_bug_reports());
 
-       num_executions++;
-
-       if (isfinalfeasible()) {
-               printf("Earliest divergence point since last feasible execution:\n");
-               if (earliest_diverge)
-                       earliest_diverge->print();
-               else
-                       printf("(Not set)\n");
+       /* End-of-execution bug checks */
+       if (complete) {
+               if (is_deadlocked())
+                       assert_bug("Deadlock detected");
 
-               earliest_diverge = NULL;
-               num_feasible_executions++;
+               checkDataRaces();
        }
 
-       DEBUG("Number of acquires waiting on pending release sequences: %zu\n",
-                       pending_rel_seqs->size());
+       record_stats();
 
+       /* Output */
+       if (DBG_ENABLED() || params.verbose || have_bug_reports())
+               print_execution(complete);
+       else
+               clear_program_output();
 
-       if (isfinalfeasible() || (params.bound != 0 && priv->used_sequence_numbers > params.bound ) || DBG_ENABLED() )
-               print_summary();
+       if (complete)
+               earliest_diverge = NULL;
 
        if ((diverge = get_next_backtrack()) == NULL)
                return false;
 
        if (DBG_ENABLED()) {
-               printf("Next execution will diverge at:\n");
+               model_print("Next execution will diverge at:\n");
                diverge->print();
        }
 
@@ -292,8 +523,11 @@ ModelAction * ModelChecker::get_last_conflict(ModelAction *act)
        case ATOMIC_READ:
        case ATOMIC_WRITE:
        case ATOMIC_RMW: {
+               /* Optimization: relaxed operations don't need backtracking */
+               if (act->is_relaxed())
+                       return NULL;
                /* linear search: from most recent to oldest */
-               action_list_t *list = obj_map->get_safe_ptr(act->get_location());
+               action_list_t *list = get_safe_ptr_action(obj_map, act->get_location());
                action_list_t::reverse_iterator rit;
                for (rit = list->rbegin(); rit != list->rend(); rit++) {
                        ModelAction *prev = *rit;
@@ -305,7 +539,7 @@ ModelAction * ModelChecker::get_last_conflict(ModelAction *act)
        case ATOMIC_LOCK:
        case ATOMIC_TRYLOCK: {
                /* linear search: from most recent to oldest */
-               action_list_t *list = obj_map->get_safe_ptr(act->get_location());
+               action_list_t *list = get_safe_ptr_action(obj_map, act->get_location());
                action_list_t::reverse_iterator rit;
                for (rit = list->rbegin(); rit != list->rend(); rit++) {
                        ModelAction *prev = *rit;
@@ -316,7 +550,7 @@ ModelAction * ModelChecker::get_last_conflict(ModelAction *act)
        }
        case ATOMIC_UNLOCK: {
                /* linear search: from most recent to oldest */
-               action_list_t *list = obj_map->get_safe_ptr(act->get_location());
+               action_list_t *list = get_safe_ptr_action(obj_map, act->get_location());
                action_list_t::reverse_iterator rit;
                for (rit = list->rbegin(); rit != list->rend(); rit++) {
                        ModelAction *prev = *rit;
@@ -327,7 +561,7 @@ ModelAction * ModelChecker::get_last_conflict(ModelAction *act)
        }
        case ATOMIC_WAIT: {
                /* linear search: from most recent to oldest */
-               action_list_t *list = obj_map->get_safe_ptr(act->get_location());
+               action_list_t *list = get_safe_ptr_action(obj_map, act->get_location());
                action_list_t::reverse_iterator rit;
                for (rit = list->rbegin(); rit != list->rend(); rit++) {
                        ModelAction *prev = *rit;
@@ -342,7 +576,7 @@ ModelAction * ModelChecker::get_last_conflict(ModelAction *act)
        case ATOMIC_NOTIFY_ALL:
        case ATOMIC_NOTIFY_ONE: {
                /* linear search: from most recent to oldest */
-               action_list_t *list = obj_map->get_safe_ptr(act->get_location());
+               action_list_t *list = get_safe_ptr_action(obj_map, act->get_location());
                action_list_t::reverse_iterator rit;
                for (rit = list->rbegin(); rit != list->rend(); rit++) {
                        ModelAction *prev = *rit;
@@ -388,9 +622,9 @@ void ModelChecker::set_backtracking(ModelAction *act)
                        break;
 
                /* Don't backtrack into a point where the thread is disabled or sleeping. */
-               if (node->get_enabled_array()[i]!=THREAD_ENABLED)
+               if (node->enabled_status(tid)!=THREAD_ENABLED)
                        continue;
-       
+
                /* Check if this has been explored already */
                if (node->has_been_explored(tid))
                        continue;
@@ -461,9 +695,9 @@ bool ModelChecker::process_read(ModelAction *curr, bool second_part_of_rmw)
                        }
 
 
-                       if (!second_part_of_rmw&&!isfeasible()&&(curr->get_node()->increment_read_from()||curr->get_node()->increment_future_value())) {
+                       if (!second_part_of_rmw&&is_infeasible()&&(curr->get_node()->increment_read_from()||curr->get_node()->increment_future_value())) {
                                mo_graph->rollbackChanges();
-                               too_many_reads = false;
+                               priv->too_many_reads = false;
                                continue;
                        }
 
@@ -525,10 +759,8 @@ bool ModelChecker::process_mutex(ModelAction *curr) {
        }
                //otherwise fall into the lock case
        case ATOMIC_LOCK: {
-               if (curr->get_cv()->getClock(state->alloc_tid) <= state->alloc_clock) {
-                       printf("Lock access before initialization\n");
-                       set_assert();
-               }
+               if (curr->get_cv()->getClock(state->alloc_tid) <= state->alloc_clock)
+                       assert_bug("Lock access before initialization");
                state->islocked = true;
                ModelAction *unlock = get_last_unlock(curr);
                //synchronize with the previous unlock statement
@@ -542,7 +774,7 @@ bool ModelChecker::process_mutex(ModelAction *curr) {
                //unlock the lock
                state->islocked = false;
                //wake up the other threads
-               action_list_t *waiters = lock_waiters_map->get_safe_ptr(curr->get_location());
+               action_list_t *waiters = get_safe_ptr_action(lock_waiters_map, curr->get_location());
                //activate all the waiting threads
                for (action_list_t::iterator rit = waiters->begin(); rit != waiters->end(); rit++) {
                        scheduler->wake(get_thread(*rit));
@@ -554,7 +786,7 @@ bool ModelChecker::process_mutex(ModelAction *curr) {
                //unlock the lock
                state->islocked = false;
                //wake up the other threads
-               action_list_t *waiters = lock_waiters_map->get_safe_ptr((void *) curr->get_value());
+               action_list_t *waiters = get_safe_ptr_action(lock_waiters_map, (void *) curr->get_value());
                //activate all the waiting threads
                for (action_list_t::iterator rit = waiters->begin(); rit != waiters->end(); rit++) {
                        scheduler->wake(get_thread(*rit));
@@ -562,14 +794,14 @@ bool ModelChecker::process_mutex(ModelAction *curr) {
                waiters->clear();
                //check whether we should go to sleep or not...simulate spurious failures
                if (curr->get_node()->get_misc()==0) {
-                       condvar_waiters_map->get_safe_ptr(curr->get_location())->push_back(curr);
+                       get_safe_ptr_action(condvar_waiters_map, curr->get_location())->push_back(curr);
                        //disable us
                        scheduler->sleep(get_current_thread());
                }
                break;
        }
        case ATOMIC_NOTIFY_ALL: {
-               action_list_t *waiters = condvar_waiters_map->get_safe_ptr(curr->get_location());
+               action_list_t *waiters = get_safe_ptr_action(condvar_waiters_map, curr->get_location());
                //activate all the waiting threads
                for (action_list_t::iterator rit = waiters->begin(); rit != waiters->end(); rit++) {
                        scheduler->wake(get_thread(*rit));
@@ -578,7 +810,7 @@ bool ModelChecker::process_mutex(ModelAction *curr) {
                break;
        }
        case ATOMIC_NOTIFY_ONE: {
-               action_list_t *waiters = condvar_waiters_map->get_safe_ptr(curr->get_location());
+               action_list_t *waiters = get_safe_ptr_action(condvar_waiters_map, curr->get_location());
                int wakeupthread=curr->get_node()->get_misc();
                action_list_t::iterator it = waiters->begin();
                advance(it, wakeupthread);
@@ -737,8 +969,7 @@ void ModelChecker::process_relseq_fixup(ModelAction *curr, work_queue_t *work_qu
        }
 
        /* See if we have realized a data race */
-       if (checkDataRaces())
-               set_assert();
+       checkDataRaces();
 }
 
 /**
@@ -802,7 +1033,7 @@ bool ModelChecker::initialize_curr_action(ModelAction **curr)
                else if (newcurr->is_wait())
                        newcurr->get_node()->set_misc_max(2);
                else if (newcurr->is_notify_one()) {
-                       newcurr->get_node()->set_misc_max(condvar_waiters_map->get_safe_ptr(newcurr->get_location())->size());
+                       newcurr->get_node()->set_misc_max(get_safe_ptr_action(condvar_waiters_map, newcurr->get_location())->size());
                }
                return true; /* This was a new ModelAction */
        }
@@ -824,7 +1055,7 @@ bool ModelChecker::check_action_enabled(ModelAction *curr) {
                struct std::mutex_state * state = lock->get_state();
                if (state->islocked) {
                        //Stick the action in the appropriate waiting queue
-                       lock_waiters_map->get_safe_ptr(curr->get_location())->push_back(curr);
+                       get_safe_ptr_action(lock_waiters_map, curr->get_location())->push_back(curr);
                        return false;
                }
        } else if (curr->get_type() == THREAD_JOIN) {
@@ -838,6 +1069,16 @@ bool ModelChecker::check_action_enabled(ModelAction *curr) {
        return true;
 }
 
+/**
+ * Stores the ModelAction for the current thread action.  Call this
+ * immediately before switching from user- to system-context to pass
+ * data between them.
+ * @param act The ModelAction created by the user-thread action
+ */
+void ModelChecker::set_current_action(ModelAction *act) {
+       priv->current_action = act;
+}
+
 /**
  * This is the heart of the model checker routine. It performs model-checking
  * actions corresponding to a given "current action." Among other processes, it
@@ -877,7 +1118,7 @@ Thread * ModelChecker::check_current_action(ModelAction *curr)
 
        /* Initialize work_queue with the "current action" work */
        work_queue_t work_queue(1, CheckCurrWorkEntry(curr));
-       while (!work_queue.empty()) {
+       while (!work_queue.empty() && !has_asserted()) {
                WorkQueueEntry work = work_queue.front();
                work_queue.pop_front();
 
@@ -958,7 +1199,8 @@ void ModelChecker::check_curr_backtracking(ModelAction * curr) {
        }
 }
 
-bool ModelChecker::promises_expired() {
+bool ModelChecker::promises_expired() const
+{
        for (unsigned int promise_index = 0; promise_index < promises->size(); promise_index++) {
                Promise *promise = (*promises)[promise_index];
                if (promise->get_expiration()<priv->used_sequence_numbers) {
@@ -968,44 +1210,67 @@ bool ModelChecker::promises_expired() {
        return false;
 }
 
-/** @return whether the current partial trace must be a prefix of a
- * feasible trace. */
-bool ModelChecker::isfeasibleprefix() {
-       return promises->size() == 0 && pending_rel_seqs->size() == 0;
+/**
+ * This is the strongest feasibility check available.
+ * @return whether the current trace (partial or complete) must be a prefix of
+ * a feasible trace.
+ */
+bool ModelChecker::isfeasibleprefix() const
+{
+       return pending_rel_seqs->size() == 0 && is_feasible_prefix_ignore_relseq();
+}
+
+/**
+ * Returns whether the current completed trace is feasible, except for pending
+ * release sequences.
+ */
+bool ModelChecker::is_feasible_prefix_ignore_relseq() const
+{
+       if (DBG_ENABLED() && promises->size() != 0)
+               DEBUG("Infeasible: unrevolved promises\n");
+
+       return !is_infeasible() && promises->size() == 0;
 }
 
-/** @return whether the current partial trace is feasible. */
-bool ModelChecker::isfeasible() {
+/**
+ * Check if the current partial trace is infeasible. Does not check any
+ * end-of-execution flags, which might rule out the execution. Thus, this is
+ * useful only for ruling an execution as infeasible.
+ * @return whether the current partial trace is infeasible.
+ */
+bool ModelChecker::is_infeasible() const
+{
        if (DBG_ENABLED() && mo_graph->checkForRMWViolation())
                DEBUG("Infeasible: RMW violation\n");
 
-       return !mo_graph->checkForRMWViolation() && isfeasibleotherthanRMW();
+       return mo_graph->checkForRMWViolation() || is_infeasible_ignoreRMW();
 }
 
-/** @return whether the current partial trace is feasible other than
- * multiple RMW reading from the same store. */
-bool ModelChecker::isfeasibleotherthanRMW() {
+/**
+ * Check If the current partial trace is infeasible, while ignoring
+ * infeasibility related to 2 RMW's reading from the same store. It does not
+ * check end-of-execution feasibility.
+ * @see ModelChecker::is_infeasible
+ * @return whether the current partial trace is infeasible, ignoring multiple
+ * RMWs reading from the same store.
+ * */
+bool ModelChecker::is_infeasible_ignoreRMW() const
+{
        if (DBG_ENABLED()) {
                if (mo_graph->checkForCycles())
                        DEBUG("Infeasible: modification order cycles\n");
-               if (failed_promise)
+               if (priv->failed_promise)
                        DEBUG("Infeasible: failed promise\n");
-               if (too_many_reads)
+               if (priv->too_many_reads)
                        DEBUG("Infeasible: too many reads\n");
-               if (bad_synchronization)
+               if (priv->bad_synchronization)
                        DEBUG("Infeasible: bad synchronization ordering\n");
                if (promises_expired())
                        DEBUG("Infeasible: promises expired\n");
        }
-       return !mo_graph->checkForCycles() && !failed_promise && !too_many_reads && !bad_synchronization && !promises_expired();
-}
-
-/** Returns whether the current completed trace is feasible. */
-bool ModelChecker::isfinalfeasible() {
-       if (DBG_ENABLED() && promises->size() != 0)
-               DEBUG("Infeasible: unrevolved promises\n");
-
-       return isfeasible() && promises->size() == 0;
+       return mo_graph->checkForCycles() || priv->failed_promise ||
+               priv->too_many_reads || priv->bad_synchronization ||
+               promises_expired();
 }
 
 /** Close out a RMWR by converting previous RMWR into a RMW or READ. */
@@ -1037,9 +1302,9 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) {
                        return;
                //Must make sure that execution is currently feasible...  We could
                //accidentally clear by rolling back
-               if (!isfeasible())
+               if (is_infeasible())
                        return;
-               std::vector<action_list_t> *thrd_lists = obj_thrd_map->get_safe_ptr(curr->get_location());
+               std::vector<action_list_t> *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, curr->get_location());
                int tid = id_to_int(curr->get_tid());
 
                /* Skip checks */
@@ -1063,7 +1328,7 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) {
                        ModelAction *act = *rit;
                        if (!act->is_read())
                                return;
-                       
+
                        if (act->get_reads_from() != rf)
                                return;
                        if (act->get_node()->get_read_from_size() <= 1)
@@ -1080,7 +1345,7 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) {
                        /* Test to see whether this is a feasible write to read from*/
                        mo_graph->startChanges();
                        r_modification_order(curr, write);
-                       bool feasiblereadfrom = isfeasible();
+                       bool feasiblereadfrom = !is_infeasible();
                        mo_graph->rollbackChanges();
 
                        if (!feasiblereadfrom)
@@ -1094,7 +1359,7 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) {
                                ModelAction *act=*rit;
                                bool foundvalue = false;
                                for (int j = 0; j<act->get_node()->get_read_from_size(); j++) {
-                                       if (act->get_node()->get_read_from_at(i)==write) {
+                                       if (act->get_node()->get_read_from_at(j)==write) {
                                                foundvalue = true;
                                                break;
                                        }
@@ -1105,7 +1370,7 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) {
                                }
                        }
                        if (feasiblewrite) {
-                               too_many_reads = true;
+                               priv->too_many_reads = true;
                                return;
                        }
                }
@@ -1131,7 +1396,7 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) {
  */
 bool ModelChecker::r_modification_order(ModelAction *curr, const ModelAction *rf)
 {
-       std::vector<action_list_t> *thrd_lists = obj_thrd_map->get_safe_ptr(curr->get_location());
+       std::vector<action_list_t> *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, curr->get_location());
        unsigned int i;
        bool added = false;
        ASSERT(curr->is_read());
@@ -1190,7 +1455,7 @@ bool ModelChecker::r_modification_order(ModelAction *curr, const ModelAction *rf
  */
 void ModelChecker::post_r_modification_order(ModelAction *curr, const ModelAction *rf)
 {
-       std::vector<action_list_t> *thrd_lists = obj_thrd_map->get_safe_ptr(curr->get_location());
+       std::vector<action_list_t> *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, curr->get_location());
        unsigned int i;
        ASSERT(curr->is_read());
 
@@ -1261,7 +1526,7 @@ void ModelChecker::post_r_modification_order(ModelAction *curr, const ModelActio
  */
 bool ModelChecker::w_modification_order(ModelAction *curr)
 {
-       std::vector<action_list_t> *thrd_lists = obj_thrd_map->get_safe_ptr(curr->get_location());
+       std::vector<action_list_t> *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, curr->get_location());
        unsigned int i;
        bool added = false;
        ASSERT(curr->is_write());
@@ -1288,7 +1553,7 @@ bool ModelChecker::w_modification_order(ModelAction *curr)
                                 * 1) If RMW and it actually read from something, then we
                                 * already have all relevant edges, so just skip to next
                                 * thread.
-                                * 
+                                *
                                 * 2) If RMW and it didn't read from anything, we should
                                 * whatever edge we can get to speed up convergence.
                                 *
@@ -1298,7 +1563,7 @@ bool ModelChecker::w_modification_order(ModelAction *curr)
                                if (curr->is_rmw()) {
                                        if (curr->get_reads_from()!=NULL)
                                                break;
-                                       else 
+                                       else
                                                continue;
                                } else
                                        continue;
@@ -1317,7 +1582,7 @@ bool ModelChecker::w_modification_order(ModelAction *curr)
                                 */
                                if (act->is_write())
                                        mo_graph->addEdge(act, curr);
-                               else if (act->is_read()) { 
+                               else if (act->is_read()) {
                                        //if previous read accessed a null, just keep going
                                        if (act->get_reads_from() == NULL)
                                                continue;
@@ -1340,8 +1605,8 @@ bool ModelChecker::w_modification_order(ModelAction *curr)
 
                                 */
                                if (thin_air_constraint_may_allow(curr, act)) {
-                                       if (isfeasible() ||
-                                                       (curr->is_rmw() && act->is_rmw() && curr->get_reads_from() == act->get_reads_from() && isfeasibleotherthanRMW())) {
+                                       if (!is_infeasible() ||
+                                                       (curr->is_rmw() && act->is_rmw() && curr->get_reads_from() == act->get_reads_from() && !is_infeasible_ignoreRMW())) {
                                                struct PendingFutureValue pfv = {curr,act};
                                                futurevalues->push_back(pfv);
                                        }
@@ -1383,12 +1648,11 @@ bool ModelChecker::thin_air_constraint_may_allow(const ModelAction * writer, con
  */
 bool ModelChecker::mo_may_allow(const ModelAction *writer, const ModelAction *reader)
 {
-       std::vector<action_list_t> *thrd_lists = obj_thrd_map->get_safe_ptr(reader->get_location());
+       std::vector<action_list_t> *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, reader->get_location());
        unsigned int i;
-
        /* Iterate over all threads */
        for (i = 0; i < thrd_lists->size(); i++) {
-               ModelAction *write_after_read = NULL;
+               const ModelAction *write_after_read = NULL;
 
                /* Iterate over actions in thread, starting from most recent */
                action_list_t *list = &(*thrd_lists)[i];
@@ -1400,12 +1664,14 @@ bool ModelChecker::mo_may_allow(const ModelAction *writer, const ModelAction *re
                                break;
                        else if (act->is_write())
                                write_after_read = act;
+                       else if (act->is_read() && act->get_reads_from() != NULL && act != reader) {
+                               write_after_read = act->get_reads_from();
+                       }
                }
 
-               if (write_after_read && mo_graph->checkReachable(write_after_read, writer))
+               if (write_after_read && write_after_read!=writer && mo_graph->checkReachable(write_after_read, writer))
                        return false;
        }
-
        return true;
 }
 
@@ -1474,7 +1740,7 @@ bool ModelChecker::release_seq_heads(const ModelAction *rf,
        /* else relaxed write; check modification order for contiguous subsequence
         * -> rf must be same thread as release */
        int tid = id_to_int(rf->get_tid());
-       std::vector<action_list_t> *thrd_lists = obj_thrd_map->get_safe_ptr(rf->get_location());
+       std::vector<action_list_t> *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, rf->get_location());
        action_list_t *list = &(*thrd_lists)[tid];
        action_list_t::const_reverse_iterator rit;
 
@@ -1509,7 +1775,7 @@ bool ModelChecker::release_seq_heads(const ModelAction *rf,
                ModelAction *last = get_last_action(int_to_id(i));
                Thread *th = get_thread(int_to_id(i));
                if ((last && rf->happens_before(last)) ||
-                               !scheduler->is_enabled(th) ||
+                               !is_enabled(th) ||
                                th->is_complete())
                        future_ordered = true;
 
@@ -1653,9 +1919,7 @@ bool ModelChecker::resolve_release_sequences(void *location, work_queue_t *work_
        }
 
        // If we resolved promises or data races, see if we have realized a data race.
-       if (checkDataRaces()) {
-               set_assert();
-       }
+       checkDataRaces();
 
        return updated;
 }
@@ -1672,9 +1936,9 @@ void ModelChecker::add_action_to_lists(ModelAction *act)
        int tid = id_to_int(act->get_tid());
        action_trace->push_back(act);
 
-       obj_map->get_safe_ptr(act->get_location())->push_back(act);
+       get_safe_ptr_action(obj_map, act->get_location())->push_back(act);
 
-       std::vector<action_list_t> *vec = obj_thrd_map->get_safe_ptr(act->get_location());
+       std::vector<action_list_t> *vec = get_safe_ptr_vect_action(obj_thrd_map, act->get_location());
        if (tid >= (int)vec->size())
                vec->resize(priv->next_thread_id);
        (*vec)[tid].push_back(act);
@@ -1685,9 +1949,9 @@ void ModelChecker::add_action_to_lists(ModelAction *act)
 
        if (act->is_wait()) {
                void *mutex_loc=(void *) act->get_value();
-               obj_map->get_safe_ptr(mutex_loc)->push_back(act);
-       
-               std::vector<action_list_t> *vec = obj_thrd_map->get_safe_ptr(mutex_loc);
+               get_safe_ptr_action(obj_map, mutex_loc)->push_back(act);
+
+               std::vector<action_list_t> *vec = get_safe_ptr_vect_action(obj_thrd_map, mutex_loc);
                if (tid >= (int)vec->size())
                        vec->resize(priv->next_thread_id);
                (*vec)[tid].push_back(act);
@@ -1723,7 +1987,7 @@ ModelAction * ModelChecker::get_last_action(thread_id_t tid) const
 ModelAction * ModelChecker::get_last_seq_cst(ModelAction *curr) const
 {
        void *location = curr->get_location();
-       action_list_t *list = obj_map->get_safe_ptr(location);
+       action_list_t *list = get_safe_ptr_action(obj_map, location);
        /* Find: max({i in dom(S) | seq_cst(t_i) && isWrite(t_i) && samevar(t_i, t)}) */
        action_list_t::reverse_iterator rit;
        for (rit = list->rbegin(); rit != list->rend(); rit++)
@@ -1743,7 +2007,7 @@ ModelAction * ModelChecker::get_last_seq_cst(ModelAction *curr) const
 ModelAction * ModelChecker::get_last_unlock(ModelAction *curr) const
 {
        void *location = curr->get_location();
-       action_list_t *list = obj_map->get_safe_ptr(location);
+       action_list_t *list = get_safe_ptr_action(obj_map, location);
        /* Find: max({i in dom(S) | isUnlock(t_i) && samevar(t_i, t)}) */
        action_list_t::reverse_iterator rit;
        for (rit = list->rbegin(); rit != list->rend(); rit++)
@@ -1798,7 +2062,7 @@ bool ModelChecker::resolve_promises(ModelAction *write)
                        //Make sure the promise's value matches the write's value
                        ASSERT(promise->get_value() == write->get_value());
                        delete(promise);
-                       
+
                        promises->erase(promises->begin() + promise_index);
                        threads_to_check.push_back(read->get_tid());
 
@@ -1833,7 +2097,7 @@ void ModelChecker::compute_promises(ModelAction *curr)
                                !act->same_thread(curr) &&
                                act->get_location() == curr->get_location() &&
                                promise->get_value() == curr->get_value()) {
-                       curr->get_node()->set_promise(i);
+                       curr->get_node()->set_promise(i, act->is_rmw());
                }
        }
 }
@@ -1848,17 +2112,27 @@ void ModelChecker::check_promises(thread_id_t tid, ClockVector *old_cv, ClockVec
                                merge_cv->synchronized_since(act)) {
                        if (promise->increment_threads(tid)) {
                                //Promise has failed
-                               failed_promise = true;
+                               priv->failed_promise = true;
                                return;
                        }
                }
        }
 }
 
+void ModelChecker::check_promises_thread_disabled() {
+       for (unsigned int i = 0; i < promises->size(); i++) {
+               Promise *promise = (*promises)[i];
+               if (promise->check_promise()) {
+                       priv->failed_promise = true;
+                       return;
+               }
+       }
+}
+
 /** Checks promises in response to addition to modification order for threads.
  * Definitions:
  * pthread is the thread that performed the read that created the promise
- * 
+ *
  * pread is the read that created the promise
  *
  * pwrite is either the first write to same location as pread by
@@ -1887,7 +2161,7 @@ void ModelChecker::mo_check_promises(thread_id_t tid, const ModelAction *write)
        for (unsigned int i = 0; i < promises->size(); i++) {
                Promise *promise = (*promises)[i];
                const ModelAction *act = promise->get_action();
-               
+
                //Is this promise on the same location?
                if ( act->get_location() != location )
                        continue;
@@ -1900,23 +2174,23 @@ void ModelChecker::mo_check_promises(thread_id_t tid, const ModelAction *write)
                                promise->set_write(write);
                                //The pwrite cannot happen before the promise
                                if (write->happens_before(act) && (write != act)) {
-                                       failed_promise = true;
+                                       priv->failed_promise = true;
                                        return;
                                }
                        }
                        if (mo_graph->checkPromise(write, promise)) {
-                               failed_promise = true;
+                               priv->failed_promise = true;
                                return;
                        }
                }
-               
+
                //Don't do any lookups twice for the same thread
                if (promise->has_sync_thread(tid))
                        continue;
-               
-               if (mo_graph->checkReachable(promise->get_write(), write)) {
+
+               if (promise->get_write()&&mo_graph->checkReachable(promise->get_write(), write)) {
                        if (promise->increment_threads(tid)) {
-                               failed_promise = true;
+                               priv->failed_promise = true;
                                return;
                        }
                }
@@ -1955,7 +2229,7 @@ void ModelChecker::compute_relseq_breakwrites(ModelAction *curr)
  */
 void ModelChecker::build_reads_from_past(ModelAction *curr)
 {
-       std::vector<action_list_t> *thrd_lists = obj_thrd_map->get_safe_ptr(curr->get_location());
+       std::vector<action_list_t> *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, curr->get_location());
        unsigned int i;
        ASSERT(curr->is_read());
 
@@ -1986,17 +2260,14 @@ void ModelChecker::build_reads_from_past(ModelAction *curr)
 
                        /* Don't consider more than one seq_cst write if we are a seq_cst read. */
                        if (!curr->is_seqcst() || (!act->is_seqcst() && (last_seq_cst == NULL || !act->happens_before(last_seq_cst))) || act == last_seq_cst) {
-                               DEBUG("Adding action to may_read_from:\n");
-                               if (DBG_ENABLED()) {
-                                       act->print();
-                                       curr->print();
-                               }
-
-                               if (curr->get_sleep_flag() && ! curr->is_seqcst()) {
-                                       if (sleep_can_read_from(curr, act))
-                                               curr->get_node()->add_read_from(act);
-                               } else
+                               if (!curr->get_sleep_flag() || curr->is_seqcst() || sleep_can_read_from(curr, act)) {
+                                       DEBUG("Adding action to may_read_from:\n");
+                                       if (DBG_ENABLED()) {
+                                               act->print();
+                                               curr->print();
+                                       }
                                        curr->get_node()->add_read_from(act);
+                               }
                        }
 
                        /* Include at most one act per-thread that "happens before" curr */
@@ -2007,25 +2278,23 @@ void ModelChecker::build_reads_from_past(ModelAction *curr)
                }
        }
 
-       if (!initialized) {
-               /** @todo Need a more informative way of reporting errors. */
-               printf("ERROR: may read from uninitialized atomic\n");
-               set_assert();
-       }
+       if (!initialized)
+               assert_bug("May read from uninitialized atomic");
 
        if (DBG_ENABLED() || !initialized) {
-               printf("Reached read action:\n");
+               model_print("Reached read action:\n");
                curr->print();
-               printf("Printing may_read_from\n");
+               model_print("Printing may_read_from\n");
                curr->get_node()->print_may_read_from();
-               printf("End printing may_read_from\n");
+               model_print("End printing may_read_from\n");
        }
 }
 
 bool ModelChecker::sleep_can_read_from(ModelAction * curr, const ModelAction *write) {
        while(true) {
                Node *prevnode=write->get_node()->get_parent();
-               bool thread_sleep=prevnode->get_enabled_array()[id_to_int(curr->get_tid())]==THREAD_SLEEP_SET;
+
+               bool thread_sleep=prevnode->enabled_status(curr->get_tid())==THREAD_SLEEP_SET;
                if (write->is_release()&&thread_sleep)
                        return true;
                if (!write->is_rmw()) {
@@ -2037,31 +2306,33 @@ bool ModelChecker::sleep_can_read_from(ModelAction * curr, const ModelAction *wr
        }
 }
 
-static void print_list(action_list_t *list)
+static void print_list(action_list_t *list, int exec_num = -1)
 {
        action_list_t::iterator it;
 
-       printf("---------------------------------------------------------------------\n");
-       printf("Trace:\n");
+       model_print("---------------------------------------------------------------------\n");
+       if (exec_num >= 0)
+               model_print("Execution %d:\n", exec_num);
+
        unsigned int hash=0;
-       
+
        for (it = list->begin(); it != list->end(); it++) {
                (*it)->print();
                hash=hash^(hash<<3)^((*it)->hash());
        }
-       printf("HASH %u\n", hash);
-       printf("---------------------------------------------------------------------\n");
+       model_print("HASH %u\n", hash);
+       model_print("---------------------------------------------------------------------\n");
 }
 
 #if SUPPORT_MOD_ORDER_DUMP
 void ModelChecker::dumpGraph(char *filename) {
        char buffer[200];
-  sprintf(buffer, "%s.dot",filename);
-  FILE *file=fopen(buffer, "w");
-  fprintf(file, "digraph %s {\n",filename);
+       sprintf(buffer, "%s.dot",filename);
+       FILE *file=fopen(buffer, "w");
+       fprintf(file, "digraph %s {\n",filename);
        mo_graph->dumpNodes(file);
        ModelAction ** thread_array=(ModelAction **)model_calloc(1, sizeof(ModelAction *)*get_num_threads());
-       
+
        for (action_list_t::iterator it = action_trace->begin(); it != action_trace->end(); it++) {
                ModelAction *action=*it;
                if (action->is_read()) {
@@ -2072,35 +2343,31 @@ void ModelChecker::dumpGraph(char *filename) {
                if (thread_array[action->get_tid()] != NULL) {
                        fprintf(file, "N%u -> N%u[label=\"sb\", color=blue];\n", thread_array[action->get_tid()]->get_seq_number(), action->get_seq_number());
                }
-               
+
                thread_array[action->get_tid()]=action;
        }
-  fprintf(file,"}\n");
+       fprintf(file,"}\n");
        model_free(thread_array);
-  fclose(file);        
+       fclose(file);
 }
 #endif
 
-void ModelChecker::print_summary()
+/** @brief Prints an execution trace summary. */
+void ModelChecker::print_summary() const
 {
-       printf("\n");
-       printf("Number of executions: %d\n", num_executions);
-       printf("Number of feasible executions: %d\n", num_feasible_executions);
-       printf("Total nodes created: %d\n", node_stack->get_total_nodes());
-
 #if SUPPORT_MOD_ORDER_DUMP
        scheduler->print();
        char buffername[100];
-       sprintf(buffername, "exec%04u", num_executions);
+       sprintf(buffername, "exec%04u", stats.num_total);
        mo_graph->dumpGraphToFile(buffername);
-       sprintf(buffername, "graph%04u", num_executions);
-  dumpGraph(buffername);
+       sprintf(buffername, "graph%04u", stats.num_total);
+       dumpGraph(buffername);
 #endif
 
-       if (!isfinalfeasible())
-               printf("INFEASIBLE EXECUTION!\n");
-       print_list(action_trace);
-       printf("\n");
+       if (!isfeasibleprefix())
+               model_print("INFEASIBLE EXECUTION!\n");
+       print_list(action_trace, stats.num_total);
+       model_print("\n");
 }
 
 /**
@@ -2115,7 +2382,7 @@ void ModelChecker::add_thread(Thread *t)
 }
 
 /**
- * Removes a thread from the scheduler. 
+ * Removes a thread from the scheduler.
  * @param the thread to remove.
  */
 void ModelChecker::remove_thread(Thread *t)
@@ -2143,6 +2410,26 @@ Thread * ModelChecker::get_thread(ModelAction *act) const
        return get_thread(act->get_tid());
 }
 
+/**
+ * @brief Check if a Thread is currently enabled
+ * @param t The Thread to check
+ * @return True if the Thread is currently enabled
+ */
+bool ModelChecker::is_enabled(Thread *t) const
+{
+       return scheduler->is_enabled(t);
+}
+
+/**
+ * @brief Check if a Thread is currently enabled
+ * @param tid The ID of the Thread to check
+ * @return True if the Thread is currently enabled
+ */
+bool ModelChecker::is_enabled(thread_id_t tid) const
+{
+       return scheduler->is_enabled(tid);
+}
+
 /**
  * Switch from a user-context to the "master thread" context (a.k.a. system
  * context). This switch is made with the intention of exploring a particular
@@ -2188,8 +2475,12 @@ bool ModelChecker::take_step() {
        Thread *next = scheduler->next_thread(priv->nextThread);
 
        /* Infeasible -> don't take any more steps */
-       if (!isfeasible())
+       if (is_infeasible())
+               return false;
+       else if (isfeasibleprefix() && have_bug_reports()) {
+               set_assert();
                return false;
+       }
 
        if (params.bound != 0) {
                if (priv->used_sequence_numbers > params.bound) {
@@ -2210,8 +2501,8 @@ bool ModelChecker::take_step() {
         * (4) no pending promises
         */
        if (!pending_rel_seqs->empty() && (!next || next->is_model_thread()) &&
-                       isfinalfeasible() && !unrealizedraces.empty()) {
-               printf("*** WARNING: release sequence fixup action (%zu pending release seuqences) ***\n",
+                       is_feasible_prefix_ignore_relseq() && !unrealizedraces.empty()) {
+               model_print("*** WARNING: release sequence fixup action (%zu pending release seuqences) ***\n",
                                pending_rel_seqs->size());
                ModelAction *fixup = new ModelAction(MODEL_FIXUP_RELSEQ,
                                std::memory_order_seq_cst, NULL, VALUE_NONE,
@@ -2238,9 +2529,24 @@ bool ModelChecker::take_step() {
        return (Thread::swap(&system_context, next) == 0);
 }
 
-/** Runs the current execution until threre are no more steps to take. */
-void ModelChecker::finish_execution() {
-       DBG();
+/** Wrapper to run the user's main function, with appropriate arguments */
+void user_main_wrapper(void *)
+{
+       user_main(model->params.argc, model->params.argv);
+}
+
+/** @brief Run ModelChecker for the user program */
+void ModelChecker::run()
+{
+       do {
+               thrd_t user_thread;
+
+               /* Start user program */
+               add_thread(new Thread(&user_thread, &user_main_wrapper, NULL));
+
+               /* Wait for all threads to complete */
+               while (take_step());
+       } while (next_execution());
 
-       while (take_step());
+       print_stats();
 }