model/promise: use ModelChecker is_enabled() interface
[model-checker.git] / model.cc
index 1e730d794baca5dc99387053bb98125b3756193a..3f200ed8e1afd2e7aff81d7b0a23ae6f0cbb7a19 100644 (file)
--- a/model.cc
+++ b/model.cc
 
 ModelChecker *model;
 
+/**
+ * Structure for holding small ModelChecker members that should be snapshotted
+ */
+struct model_snapshot_members {
+       ModelAction *current_action;
+       unsigned int next_thread_id;
+       modelclock_t used_sequence_numbers;
+       Thread *nextThread;
+       ModelAction *next_backtrack;
+};
+
 /** @brief Constructor */
 ModelChecker::ModelChecker(struct model_params params) :
        /* Initialize default scheduler */
@@ -101,7 +112,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 +219,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.
@@ -241,7 +252,28 @@ void ModelChecker::wake_up_sleeping_actions(ModelAction * curr) {
                                scheduler->remove_sleep(thr);
                        }
                }
-       }       
+       }
+}
+
+/**
+ * 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;
 }
 
 /**
@@ -257,6 +289,8 @@ bool ModelChecker::next_execution()
 
        num_executions++;
 
+       if (is_deadlocked())
+               printf("ERROR: DEADLOCK\n");
        if (isfinalfeasible()) {
                printf("Earliest divergence point since last feasible execution:\n");
                if (earliest_diverge)
@@ -393,7 +427,7 @@ void ModelChecker::set_backtracking(ModelAction *act)
                /* Don't backtrack into a point where the thread is disabled or sleeping. */
                if (node->enabled_status(tid)!=THREAD_ENABLED)
                        continue;
-       
+
                /* Check if this has been explored already */
                if (node->has_been_explored(tid))
                        continue;
@@ -841,6 +875,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
@@ -1066,7 +1110,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)
@@ -1291,7 +1335,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.
                                 *
@@ -1301,7 +1345,7 @@ bool ModelChecker::w_modification_order(ModelAction *curr)
                                if (curr->is_rmw()) {
                                        if (curr->get_reads_from()!=NULL)
                                                break;
-                                       else 
+                                       else
                                                continue;
                                } else
                                        continue;
@@ -1320,7 +1364,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;
@@ -1388,7 +1432,6 @@ bool ModelChecker::mo_may_allow(const ModelAction *writer, const ModelAction *re
 {
        std::vector<action_list_t> *thrd_lists = obj_thrd_map->get_safe_ptr(reader->get_location());
        unsigned int i;
-
        /* Iterate over all threads */
        for (i = 0; i < thrd_lists->size(); i++) {
                const ModelAction *write_after_read = NULL;
@@ -1403,15 +1446,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) {
+                       else if (act->is_read() && act->get_reads_from() != NULL && act != reader) {
                                write_after_read = act->get_reads_from();
                        }
                }
-               
+
                if (write_after_read && write_after_read!=writer && mo_graph->checkReachable(write_after_read, writer))
                        return false;
        }
-
        return true;
 }
 
@@ -1515,7 +1557,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;
 
@@ -1692,7 +1734,7 @@ 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);
                if (tid >= (int)vec->size())
                        vec->resize(priv->next_thread_id);
@@ -1804,7 +1846,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());
 
@@ -1874,7 +1916,7 @@ void ModelChecker::check_promises_thread_disabled() {
 /** 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
@@ -1903,7 +1945,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;
@@ -1925,11 +1967,11 @@ void ModelChecker::mo_check_promises(thread_id_t tid, const ModelAction *write)
                                return;
                        }
                }
-               
+
                //Don't do any lookups twice for the same thread
                if (promise->has_sync_thread(tid))
                        continue;
-               
+
                if (promise->get_write()&&mo_graph->checkReachable(promise->get_write(), write)) {
                        if (promise->increment_threads(tid)) {
                                failed_promise = true;
@@ -2002,17 +2044,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 */
@@ -2041,7 +2080,7 @@ void ModelChecker::build_reads_from_past(ModelAction *curr)
 bool ModelChecker::sleep_can_read_from(ModelAction * curr, const ModelAction *write) {
        while(true) {
                Node *prevnode=write->get_node()->get_parent();
-               
+
                bool thread_sleep=prevnode->enabled_status(curr->get_tid())==THREAD_SLEEP_SET;
                if (write->is_release()&&thread_sleep)
                        return true;
@@ -2061,7 +2100,7 @@ static void print_list(action_list_t *list)
        printf("---------------------------------------------------------------------\n");
        printf("Trace:\n");
        unsigned int hash=0;
-       
+
        for (it = list->begin(); it != list->end(); it++) {
                (*it)->print();
                hash=hash^(hash<<3)^((*it)->hash());
@@ -2073,12 +2112,12 @@ static void print_list(action_list_t *list)
 #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()) {
@@ -2089,12 +2128,12 @@ 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
 
@@ -2111,7 +2150,7 @@ void ModelChecker::print_summary()
        sprintf(buffername, "exec%04u", num_executions);
        mo_graph->dumpGraphToFile(buffername);
        sprintf(buffername, "graph%04u", num_executions);
-  dumpGraph(buffername);
+       dumpGraph(buffername);
 #endif
 
        if (!isfinalfeasible())
@@ -2132,7 +2171,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)
@@ -2160,6 +2199,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