model: simple refactoring
[c11tester.git] / model.cc
index ef9bed305fc0295af447e25365cccd6a8f2399a3..cc035084b036015b94181ef7c61b17d2bbf77f65 100644 (file)
--- a/model.cc
+++ b/model.cc
@@ -39,7 +39,6 @@ struct bug_message {
  */
 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),
@@ -48,6 +47,7 @@ struct model_snapshot_members {
                stats(),
                failed_promise(false),
                too_many_reads(false),
+               no_valid_reads(false),
                bad_synchronization(false),
                asserted(false)
        { }
@@ -58,7 +58,6 @@ struct model_snapshot_members {
                bugs.clear();
        }
 
-       ModelAction *current_action;
        unsigned int next_thread_id;
        modelclock_t used_sequence_numbers;
        ModelAction *next_backtrack;
@@ -66,6 +65,7 @@ struct model_snapshot_members {
        struct execution_stats stats;
        bool failed_promise;
        bool too_many_reads;
+       bool no_valid_reads;
        /** @brief Incorrectly-ordered synchronization was made */
        bool bad_synchronization;
        bool asserted;
@@ -159,6 +159,14 @@ void ModelChecker::reset_to_initial_state()
        /* Print all model-checker output before rollback */
        fflush(model_out);
 
+       /**
+        * FIXME: if we utilize partial rollback, we will need to free only
+        * those pending actions which were NOT pending before the rollback
+        * point
+        */
+       for (unsigned int i = 0; i < get_num_threads(); i++)
+               delete get_thread(int_to_id(i))->get_pending();
+
        snapshot_backtrack_before(0);
 }
 
@@ -216,7 +224,7 @@ Thread * ModelChecker::get_next_thread(ModelAction *curr)
        if (curr != NULL) {
                /* Do not split atomic actions. */
                if (curr->is_rmwr())
-                       return thread_current();
+                       return get_thread(curr);
                else if (curr->get_type() == THREAD_CREATE)
                        return curr->get_thread_operand();
        }
@@ -293,12 +301,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->is_sleep_set(thr) && thr->get_pending() == NULL) {
-                       thr->set_state(THREAD_RUNNING);
-                       scheduler->next_thread(thr);
-                       Thread::swap(&system_context, thr);
-                       priv->current_action->set_sleep_flag();
-                       thr->set_pending(priv->current_action);
+               if (scheduler->is_sleep_set(thr) && thr->get_pending()) {
+                       thr->get_pending()->set_sleep_flag();
                }
        }
 }
@@ -323,11 +327,22 @@ void ModelChecker::set_bad_synchronization()
        priv->bad_synchronization = true;
 }
 
+/**
+ * Check whether the current trace has triggered an assertion which should halt
+ * its execution.
+ *
+ * @return True, if the execution should be aborted; false otherwise
+ */
 bool ModelChecker::has_asserted() const
 {
        return priv->asserted;
 }
 
+/**
+ * Trigger a trace assertion which should cause this execution to be halted.
+ * This can be due to a detected bug or due to an infeasibility that should
+ * halt ASAP.
+ */
 void ModelChecker::set_assert()
 {
        priv->asserted = true;
@@ -968,7 +983,10 @@ bool ModelChecker::process_thread_action(ModelAction *curr)
 
        switch (curr->get_type()) {
        case THREAD_CREATE: {
-               Thread *th = curr->get_thread_operand();
+               thrd_t *thrd = (thrd_t *)curr->get_location();
+               struct thread_params *params = (struct thread_params *)curr->get_value();
+               Thread *th = new Thread(thrd, params->func, params->arg);
+               add_thread(th);
                th->set_creation(curr);
                /* Promises can be satisfied by children */
                for (unsigned int i = 0; i < promises->size(); i++) {
@@ -1211,16 +1229,6 @@ 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
@@ -1387,6 +1395,8 @@ void ModelChecker::print_infeasibility(const char *prefix) const
                ptr += sprintf(ptr, "[failed promise]");
        if (priv->too_many_reads)
                ptr += sprintf(ptr, "[too many reads]");
+       if (priv->no_valid_reads)
+               ptr += sprintf(ptr, "[no valid reads-from]");
        if (priv->bad_synchronization)
                ptr += sprintf(ptr, "[bad sw ordering]");
        if (promises_expired())
@@ -1415,6 +1425,7 @@ bool ModelChecker::is_feasible_prefix_ignore_relseq() const
 bool ModelChecker::is_infeasible() const
 {
        return mo_graph->checkForCycles() ||
+               priv->no_valid_reads ||
                priv->failed_promise ||
                priv->too_many_reads ||
                priv->bad_synchronization ||
@@ -1494,13 +1505,9 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf)
                                continue;
 
                        /* Test to see whether this is a feasible write to read from */
-                       mo_graph->startChanges();
-                       r_modification_order(curr, write);
-                       bool feasiblereadfrom = !is_infeasible();
-                       mo_graph->rollbackChanges();
+                       /** NOTE: all members of read-from set should be
+                        *  feasible, so we no longer check it here **/
 
-                       if (!feasiblereadfrom)
-                               continue;
                        rit = ritcopy;
 
                        bool feasiblewrite = true;
@@ -2473,14 +2480,25 @@ void ModelChecker::build_reads_from_past(ModelAction *curr)
                        else if (curr->get_sleep_flag() && !curr->is_seqcst() && !sleep_can_read_from(curr, act))
                                allow_read = false;
 
-                       if (allow_read)
-                               curr->get_node()->add_read_from(act);
+                       if (allow_read) {
+                               /* Only add feasible reads */
+                               mo_graph->startChanges();
+                               r_modification_order(curr, act);
+                               if (!is_infeasible())
+                                       curr->get_node()->add_read_from(act);
+                               mo_graph->rollbackChanges();
+                       }
 
                        /* Include at most one act per-thread that "happens before" curr */
                        if (act->happens_before(curr))
                                break;
                }
        }
+       /* We may find no valid may-read-from only if the execution is doomed */
+       if (!curr->get_node()->get_read_from_size()) {
+               priv->no_valid_reads = true;
+               set_assert();
+       }
 
        if (DBG_ENABLED()) {
                model_print("Reached read action:\n");
@@ -2664,8 +2682,8 @@ uint64_t ModelChecker::switch_to_master(ModelAction *act)
 {
        DBG();
        Thread *old = thread_current();
-       set_current_action(act);
-       old->set_state(THREAD_READY);
+       ASSERT(!old->get_pending());
+       old->set_pending(act);
        if (Thread::swap(old, &system_context) < 0) {
                perror("swap threads");
                exit(EXIT_FAILURE);
@@ -2676,13 +2694,11 @@ uint64_t ModelChecker::switch_to_master(ModelAction *act)
 /**
  * Takes the next step in the execution, if possible.
  * @param curr The current step to take
- * @return Returns true (success) if a step was taken and false otherwise.
+ * @return Returns the next Thread to run, if any; NULL if this execution
+ * should terminate
  */
-bool ModelChecker::take_step(ModelAction *curr)
+Thread * ModelChecker::take_step(ModelAction *curr)
 {
-       if (has_asserted())
-               return false;
-
        Thread *curr_thrd = get_thread(curr);
        ASSERT(curr_thrd->get_state() == THREAD_READY);
 
@@ -2690,15 +2706,14 @@ bool ModelChecker::take_step(ModelAction *curr)
 
        /* Infeasible -> don't take any more steps */
        if (is_infeasible())
-               return false;
+               return NULL;
        else if (isfeasibleprefix() && have_bug_reports()) {
                set_assert();
-               return false;
+               return NULL;
        }
 
-       if (params.bound != 0)
-               if (priv->used_sequence_numbers > params.bound)
-                       return false;
+       if (params.bound != 0 && priv->used_sequence_numbers > params.bound)
+               return NULL;
 
        if (curr_thrd->is_blocked() || curr_thrd->is_complete())
                scheduler->remove_thread(curr_thrd);
@@ -2725,26 +2740,11 @@ bool ModelChecker::take_step(ModelAction *curr)
                ModelAction *fixup = new ModelAction(MODEL_FIXUP_RELSEQ,
                                std::memory_order_seq_cst, NULL, VALUE_NONE,
                                model_thread);
-               set_current_action(fixup);
-               return true;
-       }
-
-       /* next_thrd == NULL -> don't take any more steps */
-       if (!next_thrd)
-               return false;
-
-       next_thrd->set_state(THREAD_RUNNING);
-
-       if (next_thrd->get_pending() != NULL) {
-               /* restart a pending action */
-               set_current_action(next_thrd->get_pending());
-               next_thrd->set_pending(NULL);
-               next_thrd->set_state(THREAD_READY);
-               return true;
+               model_thread->set_pending(fixup);
+               return model_thread;
        }
 
-       /* Return false only if swap fails with an error */
-       return (Thread::swap(&system_context, next_thrd) == 0);
+       return next_thrd;
 }
 
 /** Wrapper to run the user's main function, with appropriate arguments */
@@ -2759,15 +2759,35 @@ void ModelChecker::run()
        do {
                thrd_t user_thread;
                Thread *t = new Thread(&user_thread, &user_main_wrapper, NULL);
-
                add_thread(t);
 
-               /* Run user thread up to its first action */
-               scheduler->next_thread(t);
-               Thread::swap(&system_context, t);
+               do {
+                       /*
+                        * Stash next pending action(s) for thread(s). There
+                        * should only need to stash one thread's action--the
+                        * thread which just took a step--plus the first step
+                        * for any newly-created thread
+                        */
+                       for (unsigned int i = 0; i < get_num_threads(); i++) {
+                               thread_id_t tid = int_to_id(i);
+                               Thread *thr = get_thread(tid);
+                               if (!thr->is_model_thread() && !thr->is_complete() && !thr->get_pending()) {
+                                       scheduler->next_thread(thr);
+                                       Thread::swap(&system_context, thr);
+                               }
+                       }
+
+                       /* Catch assertions from prior take_step or from
+                        * between-ModelAction bugs (e.g., data races) */
+                       if (has_asserted())
+                               break;
 
-               /* Wait for all threads to complete */
-               while (take_step(priv->current_action));
+                       /* Consume the next action for a Thread */
+                       ModelAction *curr = t->get_pending();
+                       t->set_pending(NULL);
+                       t = take_step(curr);
+               } while (t && !t->is_model_thread());
+               /** @TODO Re-write release sequence fixups here */
        } while (next_execution());
 
        print_stats();