fix bug
[model-checker.git] / model.cc
index 1712a60a9dd3990efeef26f257467f5bff7d6bb0..fb492a5363d1cfea467d3a8ef1438c812fd11e83 100644 (file)
--- a/model.cc
+++ b/model.cc
@@ -21,6 +21,7 @@ ModelChecker::ModelChecker(struct model_params params) :
        /* Initialize default scheduler */
        scheduler(new Scheduler()),
        num_executions(0),
+       num_feasible_executions(0),
        params(params),
        diverge(NULL),
        action_trace(new action_list_t()),
@@ -174,6 +175,8 @@ bool ModelChecker::next_execution()
        DBG();
 
        num_executions++;
+       if (isfinalfeasible())
+               num_feasible_executions++;
 
        if (isfinalfeasible() || DBG_ENABLED())
                print_summary();
@@ -194,21 +197,33 @@ ModelAction * ModelChecker::get_last_conflict(ModelAction *act)
 {
        action_type type = act->get_type();
 
-       switch (type) {
-               case ATOMIC_READ:
-               case ATOMIC_WRITE:
-               case ATOMIC_RMW:
-                       break;
-               default:
-                       return NULL;
-       }
-       /* linear search: from most recent to oldest */
-       action_list_t *list = obj_map->get_safe_ptr(act->get_location());
-       action_list_t::reverse_iterator rit;
-       for (rit = list->rbegin(); rit != list->rend(); rit++) {
-               ModelAction *prev = *rit;
-               if (act->is_synchronizing(prev))
-                       return prev;
+       if (type==ATOMIC_READ||type==ATOMIC_WRITE||type==ATOMIC_RMW) {
+               /* linear search: from most recent to oldest */
+               action_list_t *list = obj_map->get_safe_ptr(act->get_location());
+               action_list_t::reverse_iterator rit;
+               for (rit = list->rbegin(); rit != list->rend(); rit++) {
+                       ModelAction *prev = *rit;
+                       if (act->is_synchronizing(prev))
+                               return prev;
+               }
+       } else if (type==ATOMIC_LOCK||type==ATOMIC_TRYLOCK) {
+               /* linear search: from most recent to oldest */
+               action_list_t *list = obj_map->get_safe_ptr(act->get_location());
+               action_list_t::reverse_iterator rit;
+               for (rit = list->rbegin(); rit != list->rend(); rit++) {
+                       ModelAction *prev = *rit;
+                       if (prev->is_success_lock())
+                               return prev;
+               }
+       } else if (type==ATOMIC_UNLOCK) {
+               /* linear search: from most recent to oldest */
+               action_list_t *list = obj_map->get_safe_ptr(act->get_location());
+               action_list_t::reverse_iterator rit;
+               for (rit = list->rbegin(); rit != list->rend(); rit++) {
+                       ModelAction *prev = *rit;
+                       if (prev->is_failed_trylock())
+                               return prev;
+               }
        }
        return NULL;
 }
@@ -262,11 +277,11 @@ ModelAction * ModelChecker::get_next_backtrack()
 /**
  * Processes a read or rmw model action.
  * @param curr is the read model action to process.
- * @param th is the thread
  * @param second_part_of_rmw is boolean that is true is this is the second action of a rmw.
  * @return True if processing this read updates the mo_graph.
  */
-bool ModelChecker::process_read(ModelAction *curr, Thread * th, bool second_part_of_rmw) {
+bool ModelChecker::process_read(ModelAction *curr, bool second_part_of_rmw)
+{
        uint64_t value;
        bool updated = false;
        while (true) {
@@ -300,11 +315,36 @@ bool ModelChecker::process_read(ModelAction *curr, Thread * th, bool second_part
                        Promise *valuepromise = new Promise(curr, value, expiration);
                        promises->push_back(valuepromise);
                }
-               th->set_return_value(value);
+               get_thread(curr)->set_return_value(value);
                return updated;
        }
 }
 
+/**
+ * Process a write ModelAction
+ * @param curr The ModelAction to process
+ * @return True if the mo_graph was updated or promises were resolved
+ */
+bool ModelChecker::process_write(ModelAction *curr)
+{
+       bool updated_mod_order = w_modification_order(curr);
+       bool updated_promises = resolve_promises(curr);
+
+       if (promises->size() == 0) {
+               for (unsigned int i = 0; i<futurevalues->size(); i++) {
+                       struct PendingFutureValue pfv = (*futurevalues)[i];
+                       if (pfv.act->get_node()->add_future_value(pfv.value, pfv.expiration) &&
+                                       (!priv->next_backtrack || *pfv.act > *priv->next_backtrack))
+                               priv->next_backtrack = pfv.act;
+               }
+               futurevalues->resize(0);
+       }
+
+       mo_graph->commitChanges();
+       get_thread(curr)->set_return_value(VALUE_NONE);
+       return updated_mod_order || updated_promises;
+}
+
 /**
  * This is the heart of the model checker routine. It performs model-checking
  * actions corresponding to a given "current action." Among other processes, it
@@ -330,16 +370,18 @@ Thread * ModelChecker::check_current_action(ModelAction *curr)
                curr = tmp;
                compute_promises(curr);
        } else {
-               ModelAction *tmp = node_stack->explore_action(curr);
+               ModelAction *tmp = node_stack->explore_action(curr, scheduler->get_enabled());
                if (tmp) {
                        /* Discard duplicate ModelAction; use action from NodeStack */
                        /* First restore type and order in case of RMW operation */
                        if (curr->is_rmwr())
                                tmp->copy_typeandorder(curr);
-
+                       
                        /* If we have diverged, we need to reset the clock vector. */
                        if (diverge == NULL)
                                tmp->create_cv(get_parent_action(tmp->get_tid()));
+                       
+                       ASSERT(curr->get_location()==tmp->get_location());
 
                        delete curr;
                        curr = tmp;
@@ -392,33 +434,36 @@ Thread * ModelChecker::check_current_action(ModelAction *curr)
                break;
        }
 
-       bool updated = false;
+       work_queue_t work_queue(1, CheckCurrWorkEntry(curr));
 
-       if (curr->is_read()) {
-               updated = process_read(curr, get_thread(curr), second_part_of_rmw);
-       }
+       while (!work_queue.empty()) {
+               WorkQueueEntry work = work_queue.front();
+               work_queue.pop_front();
 
-       if (curr->is_write()) {
-               bool updated_mod_order = w_modification_order(curr);
-               bool updated_promises = resolve_promises(curr);
-               updated = updated || updated_mod_order || updated_promises;
-
-               if (promises->size()==0) {
-                       for (unsigned int i = 0; i<futurevalues->size(); i++) {
-                               struct PendingFutureValue pfv=(*futurevalues)[i];
-                               if (pfv.act->get_node()->add_future_value(pfv.value, pfv.expiration) &&
-                                               (!priv->next_backtrack || *pfv.act > *priv->next_backtrack))
-                                       priv->next_backtrack = pfv.act;
-                       }
-                       futurevalues->resize(0);
-               }
+               switch (work.type) {
+               case WORK_CHECK_CURR_ACTION: {
+                       ModelAction *act = work.action;
+                       bool updated = false;
+                       if (act->is_read() && process_read(act, second_part_of_rmw))
+                               updated = true;
 
-               mo_graph->commitChanges();
-               get_thread(curr)->set_return_value(VALUE_NONE);
-       }
+                       if (act->is_write() && process_write(act))
+                               updated = true;
 
-       if (updated)
-               resolve_release_sequences(curr->get_location());
+                       if (updated)
+                               work_queue.push_back(CheckRelSeqWorkEntry(act->get_location()));
+                       break;
+               }
+               case WORK_CHECK_RELEASE_SEQ:
+                       resolve_release_sequences(work.location, &work_queue);
+                       break;
+               case WORK_CHECK_MO_EDGES:
+                       /** @todo Perform follow-up mo_graph checks */
+               default:
+                       ASSERT(false);
+                       break;
+               }
+       }
 
        /* Add action to list.  */
        if (!second_part_of_rmw)
@@ -586,7 +631,18 @@ void ModelChecker::check_recency(ModelAction *curr, bool already_added) {
 }
 
 /**
- * Updates the mo_graph with the constraints imposed from the current read.
+ * Updates the mo_graph with the constraints imposed from the current
+ * read.  
+ *
+ * Basic idea is the following: Go through each other thread and find
+ * the lastest action that happened before our read.  Two cases:
+ *
+ * (1) The action is a write => that write must either occur before
+ * the write we read from or be the write we read from.
+ *
+ * (2) The action is a read => the write that that action read from
+ * must occur before the write we read from or be the same write.
+ *
  * @param curr The current action. Must be a read.
  * @param rf The action that curr reads from. Must be a write.
  * @return True if modification order edges were added; false otherwise
@@ -629,7 +685,22 @@ bool ModelChecker::r_modification_order(ModelAction *curr, const ModelAction *rf
        return added;
 }
 
-/** Updates the mo_graph with the constraints imposed from the current read. */
+/** This method fixes up the modification order when we resolve a
+ *  promises.  The basic problem is that actions that occur after the
+ *  read curr could not property add items to the modification order
+ *  for our read.
+ *  
+ *  So for each thread, we find the earliest item that happens after
+ *  the read curr.  This is the item we have to fix up with additional
+ *  constraints.  If that action is write, we add a MO edge between
+ *  the Action rf and that action.  If the action is a read, we add a
+ *  MO edge between the Action rf, and whatever the read accessed.
+ *
+ * @param curr is the read ModelAction that we are fixing up MO edges for.
+ * @param rf is the write ModelAction that curr reads from.
+ *
+ */
+
 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());
@@ -668,6 +739,23 @@ void ModelChecker::post_r_modification_order(ModelAction *curr, const ModelActio
 
 /**
  * Updates the mo_graph with the constraints imposed from the current write.
+ *
+ * Basic idea is the following: Go through each other thread and find
+ * the lastest action that happened before our write.  Two cases:
+ *
+ * (1) The action is a write => that write must occur before
+ * the current write
+ *
+ * (2) The action is a read => the write that that action read from
+ * must occur before the current write.
+ *
+ * This method also handles two other issues:
+ *
+ * (I) Sequential Consistency: Making sure that if the current write is
+ * seq_cst, that it occurs after the previous seq_cst write.
+ *
+ * (II) Sending the write back to non-synchronizing reads.
+ *
  * @param curr The current action. Must be a write.
  * @return True if modification order edges were added; false otherwise
  */
@@ -908,9 +996,12 @@ void ModelChecker::get_release_seq_heads(ModelAction *act,
  *
  * @param location The location/object that should be checked for release
  * sequence resolutions
- * @return True if any updates occurred (new synchronization, new mo_graph edges)
+ * @param work_queue The work queue to which to add work items as they are
+ * generated
+ * @return True if any updates occurred (new synchronization, new mo_graph
+ * edges)
  */
-bool ModelChecker::resolve_release_sequences(void *location)
+bool ModelChecker::resolve_release_sequences(void *location, work_queue_t *work_queue)
 {
        std::list<ModelAction *> *list;
        list = lazy_sync_with_release->getptr(location);
@@ -933,14 +1024,18 @@ bool ModelChecker::resolve_release_sequences(void *location)
                }
 
                if (updated) {
+                       /* Re-check act for mo_graph edges */
+                       work_queue->push_back(MOEdgeWorkEntry(act));
+
                        /* propagate synchronization to later actions */
                        action_list_t::reverse_iterator it = action_trace->rbegin();
                        while ((*it) != act) {
                                ModelAction *propagate = *it;
-                               if (act->happens_before(propagate))
-                                       /** @todo new mo_graph edges along with
-                                        * this synchronization? */
+                               if (act->happens_before(propagate)) {
                                        propagate->synchronize_with(act);
+                                       /* Re-check 'propagate' for mo_graph edges */
+                                       work_queue->push_back(MOEdgeWorkEntry(propagate));
+                               }
                        }
                }
                if (complete) {
@@ -984,10 +1079,11 @@ void ModelChecker::add_action_to_lists(ModelAction *act)
 
 ModelAction * ModelChecker::get_last_action(thread_id_t tid)
 {
-       int nthreads = get_num_threads();
-       if ((int)thrd_last_action->size() < nthreads)
-               thrd_last_action->resize(nthreads);
-       return (*thrd_last_action)[id_to_int(tid)];
+       int threadid=id_to_int(tid);
+       if (threadid<(int)thrd_last_action->size())
+               return (*thrd_last_action)[id_to_int(tid)];
+       else
+               return NULL;
 }
 
 /**
@@ -1043,7 +1139,11 @@ bool ModelChecker::resolve_promises(ModelAction *write)
                        if (read->is_rmw()) {
                                mo_graph->addRMWEdge(write, read);
                        }
+                       //First fix up the modification order for actions that happened
+                       //before the read
                        r_modification_order(read, write);
+                       //Next fix up the modification order for actions that happened
+                       //after the read.
                        post_r_modification_order(read, write);
                        promises->erase(promises->begin() + promise_index);
                        resolved = true;
@@ -1182,6 +1282,7 @@ void ModelChecker::print_summary()
 {
        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
@@ -1235,25 +1336,23 @@ int ModelChecker::switch_to_master(ModelAction *act)
  * @return Returns true (success) if a step was taken and false otherwise.
  */
 bool ModelChecker::take_step() {
-       Thread *curr, *next;
-
        if (has_asserted())
                return false;
 
-       curr = thread_current();
+       Thread * curr = thread_current();
        if (curr) {
                if (curr->get_state() == THREAD_READY) {
                        ASSERT(priv->current_action);
 
                        priv->nextThread = check_current_action(priv->current_action);
                        priv->current_action = NULL;
-                       if (!curr->is_blocked() && !curr->is_complete())
-                               scheduler->add_thread(curr);
+                       if (curr->is_blocked() || curr->is_complete())
+                               scheduler->remove_thread(curr);
                } else {
                        ASSERT(false);
                }
        }
-       next = scheduler->next_thread(priv->nextThread);
+       Thread * next = scheduler->next_thread(priv->nextThread);
 
        /* Infeasible -> don't take any more steps */
        if (!isfeasible())