model: THREAD_JOIN synchronizes with THREAD_FINISH
[model-checker.git] / model.cc
index 9faf4e2a28eb26b7c7891114f86592aee80480b9..2b295dea1b24d631ce2a0da7942aeed734c8fc46 100644 (file)
--- a/model.cc
+++ b/model.cc
@@ -333,6 +333,53 @@ bool ModelChecker::process_write(ModelAction *curr)
        return updated_mod_order || updated_promises;
 }
 
+/**
+ * Initialize the current action by performing one or more of the following
+ * actions, as appropriate: merging RMWR and RMWC/RMW actions, stepping forward
+ * in the NodeStack, manipulating backtracking sets, allocating and
+ * initializing clock vectors, and computing the promises to fulfill.
+ *
+ * @param curr The current action, as passed from the user context; may be
+ * freed/invalidated after the execution of this function
+ * @return The current action, as processed by the ModelChecker. Is only the
+ * same as the parameter @a curr if this is a newly-explored action.
+ */
+ModelAction * ModelChecker::initialize_curr_action(ModelAction *curr)
+{
+       ModelAction *newcurr;
+
+       if (curr->is_rmwc() || curr->is_rmw()) {
+               newcurr = process_rmw(curr);
+               delete curr;
+               compute_promises(newcurr);
+               return newcurr;
+       }
+
+       newcurr = node_stack->explore_action(curr);
+       if (newcurr) {
+               /* First restore type and order in case of RMW operation */
+               if (curr->is_rmwr())
+                       newcurr->copy_typeandorder(curr);
+
+               /* Discard duplicate ModelAction; use action from NodeStack */
+               delete curr;
+
+               /* If we have diverged, we need to reset the clock vector. */
+               if (diverge == NULL)
+                       newcurr->create_cv(get_parent_action(newcurr->get_tid()));
+       } else {
+               newcurr = curr;
+               /*
+                * Perform one-time actions when pushing new ModelAction onto
+                * NodeStack
+                */
+               curr->create_cv(get_parent_action(curr->get_tid()));
+               if (curr->is_write())
+                       compute_promises(curr);
+       }
+       return newcurr;
+}
+
 /**
  * This is the heart of the model checker routine. It performs model-checking
  * actions corresponding to a given "current action." Among other processes, it
@@ -347,43 +394,20 @@ bool ModelChecker::process_write(ModelAction *curr)
  */
 Thread * ModelChecker::check_current_action(ModelAction *curr)
 {
-       bool second_part_of_rmw = false;
-
        ASSERT(curr);
 
-       if (curr->is_rmwc() || curr->is_rmw()) {
-               ModelAction *tmp = process_rmw(curr);
-               second_part_of_rmw = true;
-               delete curr;
-               curr = tmp;
-               compute_promises(curr);
-       } else {
-               ModelAction *tmp = node_stack->explore_action(curr);
-               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()));
-
-                       delete curr;
-                       curr = tmp;
-               } else {
-                       /*
-                        * Perform one-time actions when pushing new ModelAction onto
-                        * NodeStack
-                        */
-                       curr->create_cv(get_parent_action(curr->get_tid()));
-                       /* Build may_read_from set */
-                       if (curr->is_read())
-                               build_reads_from_past(curr);
-                       if (curr->is_write())
-                               compute_promises(curr);
-               }
-       }
+       bool second_part_of_rmw = curr->is_rmwc() || curr->is_rmw();
+
+       ModelAction *newcurr = initialize_curr_action(curr);
+
+       /* Add the action to lists before any other model-checking tasks */
+       if (!second_part_of_rmw)
+               add_action_to_lists(newcurr);
+
+       /* Build may_read_from set for newly-created actions */
+       if (curr == newcurr && curr->is_read())
+               build_reads_from_past(curr);
+       curr = newcurr;
 
        /* Thread specific actions */
        switch (curr->get_type()) {
@@ -399,6 +423,8 @@ Thread * ModelChecker::check_current_action(ModelAction *curr)
                if (!blocking->is_complete()) {
                        blocking->push_wait_list(curr);
                        scheduler->sleep(waiting);
+               } else {
+                       do_complete_join(curr);
                }
                break;
        }
@@ -408,6 +434,7 @@ Thread * ModelChecker::check_current_action(ModelAction *curr)
                        ModelAction *act = th->pop_wait_list();
                        Thread *wake = get_thread(act);
                        scheduler->wake(wake);
+                       do_complete_join(act);
                }
                th->complete();
                break;
@@ -420,10 +447,6 @@ Thread * ModelChecker::check_current_action(ModelAction *curr)
                break;
        }
 
-       /* Add current action to lists before work_queue loop */
-       if (!second_part_of_rmw)
-               add_action_to_lists(curr);
-
        work_queue_t work_queue(1, CheckCurrWorkEntry(curr));
 
        while (!work_queue.empty()) {
@@ -447,8 +470,24 @@ Thread * ModelChecker::check_current_action(ModelAction *curr)
                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 */
+               case WORK_CHECK_MO_EDGES: {
+                       /** @todo Complete verification of work_queue */
+                       ModelAction *act = work.action;
+                       bool updated = false;
+
+                       if (act->is_read()) {
+                               if (r_modification_order(act, act->get_reads_from()))
+                                       updated = true;
+                       }
+                       if (act->is_write()) {
+                               if (w_modification_order(act))
+                                       updated = true;
+                       }
+
+                       if (updated)
+                               work_queue.push_back(CheckRelSeqWorkEntry(act->get_location()));
+                       break;
+               }
                default:
                        ASSERT(false);
                        break;
@@ -462,6 +501,19 @@ Thread * ModelChecker::check_current_action(ModelAction *curr)
        return get_next_thread(curr);
 }
 
+/**
+ * Complete a THREAD_JOIN operation, by synchronizing with the THREAD_FINISH
+ * operation from the Thread it is joining with. Must be called after the
+ * completion of the Thread in question.
+ * @param join The THREAD_JOIN action
+ */
+void ModelChecker::do_complete_join(ModelAction *join)
+{
+       Thread *blocking = (Thread *)join->get_location();
+       ModelAction *act = get_last_action(blocking->get_id());
+       join->synchronize_with(act);
+}
+
 void ModelChecker::check_curr_backtracking(ModelAction * curr) {
        Node *currnode = curr->get_node();
        Node *parnode = currnode->get_parent();
@@ -1242,7 +1294,7 @@ void ModelChecker::build_reads_from_past(ModelAction *curr)
                        ModelAction *act = *rit;
 
                        /* Only consider 'write' actions */
-                       if (!act->is_write())
+                       if (!act->is_write() || act == curr)
                                continue;
 
                        /* Don't consider more than one seq_cst write if we are a seq_cst read. */