model: release_seq_head: improve ordering of tests
[c11tester.git] / model.cc
index 4f2d9e0f314b8fbc451867ee5676f81292ab6353..508f3b98284920860c9ce5eb4231b786b070b979 100644 (file)
--- a/model.cc
+++ b/model.cc
@@ -86,19 +86,19 @@ void ModelChecker::reset_to_initial_state()
        snapshotObject->backTrackBeforeStep(0);
 }
 
-/** @returns a thread ID for a new Thread */
+/** @return a thread ID for a new Thread */
 thread_id_t ModelChecker::get_next_id()
 {
        return priv->next_thread_id++;
 }
 
-/** @returns the number of user threads created during this execution */
+/** @return the number of user threads created during this execution */
 int ModelChecker::get_num_threads()
 {
        return priv->next_thread_id;
 }
 
-/** @returns a sequence number for a new ModelAction */
+/** @return a sequence number for a new ModelAction */
 modelclock_t ModelChecker::get_next_seq_num()
 {
        return ++priv->used_sequence_numbers;
@@ -200,9 +200,10 @@ bool ModelChecker::next_execution()
 
 ModelAction * ModelChecker::get_last_conflict(ModelAction *act)
 {
-       action_type type = act->get_type();
-
-       if (type==ATOMIC_READ||type==ATOMIC_WRITE||type==ATOMIC_RMW) {
+       switch (act->get_type()) {
+       case ATOMIC_READ:
+       case ATOMIC_WRITE:
+       case 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;
@@ -211,59 +212,77 @@ ModelAction * ModelChecker::get_last_conflict(ModelAction *act)
                        if (act->is_synchronizing(prev))
                                return prev;
                }
-       } else if (type==ATOMIC_LOCK||type==ATOMIC_TRYLOCK) {
+               break;
+       }
+       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::reverse_iterator rit;
                for (rit = list->rbegin(); rit != list->rend(); rit++) {
                        ModelAction *prev = *rit;
-                       if (prev->is_success_lock())
+                       if (act->is_conflicting_lock(prev))
                                return prev;
                }
-       } else if (type==ATOMIC_UNLOCK) {
+               break;
+       }
+       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::reverse_iterator rit;
                for (rit = list->rbegin(); rit != list->rend(); rit++) {
                        ModelAction *prev = *rit;
-                       if (prev->is_failed_trylock())
+                       if (!act->same_thread(prev)&&prev->is_failed_trylock())
                                return prev;
                }
+               break;
+       }
+       default:
+               break;
        }
        return NULL;
 }
 
 void ModelChecker::set_backtracking(ModelAction *act)
 {
-       ModelAction *prev;
-       Node *node;
        Thread *t = get_thread(act);
-
-       prev = get_last_conflict(act);
+       ModelAction * prev = get_last_conflict(act);
        if (prev == NULL)
                return;
 
-       node = prev->get_node()->get_parent();
+       Node * node = prev->get_node()->get_parent();
 
-       while (!node->is_enabled(t))
-               t = t->get_parent();
-
-       /* Check if this has been explored already */
-       if (node->has_been_explored(t->get_id()))
-               return;
-
-       /* Cache the latest backtracking point */
-       if (!priv->next_backtrack || *prev > *priv->next_backtrack)
-               priv->next_backtrack = prev;
+       int low_tid, high_tid;
+       if (node->is_enabled(t)) {
+               low_tid=id_to_int(act->get_tid());
+               high_tid=low_tid+1;
+       } else {
+               low_tid=0;
+               high_tid=get_num_threads();
+       }
+       
+       for(int i=low_tid;i<high_tid;i++) {
+               thread_id_t tid=int_to_id(i);
+               if (!node->is_enabled(tid))
+                       continue;
 
-       /* If this is a new backtracking point, mark the tree */
-       if (!node->set_backtrack(t->get_id()))
-               return;
-       DEBUG("Setting backtrack: conflict = %d, instead tid = %d\n",
-                       prev->get_tid(), t->get_id());
-       if (DBG_ENABLED()) {
-               prev->print();
-               act->print();
+               /* Check if this has been explored already */
+               if (node->has_been_explored(tid))
+                       continue;
+               
+               /* Cache the latest backtracking point */
+               if (!priv->next_backtrack || *prev > *priv->next_backtrack)
+                       priv->next_backtrack = prev;
+               
+               /* If this is a new backtracking point, mark the tree */
+               if (!node->set_backtrack(tid))
+                       continue;
+               DEBUG("Setting backtrack: conflict = %d, instead tid = %d\n",
+                                       prev->get_tid(), t->get_id());
+               if (DBG_ENABLED()) {
+                       prev->print();
+                       act->print();
+               }
        }
 }
 
@@ -325,6 +344,21 @@ bool ModelChecker::process_read(ModelAction *curr, bool second_part_of_rmw)
        }
 }
 
+/**
+ * Processes a lock, trylock, or unlock model action.  @param curr is
+ * the read model action to process.  
+ *
+ * The try lock operation checks whether the lock is taken.  If not,
+ * it falls to the normal lock operation case.  If so, it returns
+ * fail.
+ *
+ * The lock operation has already been checked that it is enabled, so
+ * it just grabs the lock and synchronizes with the previous unlock.
+ *
+ * The unlock operation has to re-enable all of the threads that are
+ * waiting on the lock.
+ */
+
 void ModelChecker::process_mutex(ModelAction *curr) {
        std::mutex * mutex=(std::mutex *) curr->get_location();
        struct std::mutex_state * state=mutex->get_state();
@@ -358,7 +392,7 @@ void ModelChecker::process_mutex(ModelAction *curr) {
                action_list_t * waiters = lock_waiters_map->get_safe_ptr(curr->get_location());
                //activate all the waiting threads
                for(action_list_t::iterator rit = waiters->begin(); rit!=waiters->end(); rit++) {
-                       add_thread(get_thread((*rit)->get_tid()));
+                       scheduler->add_thread(get_thread((*rit)->get_tid()));
                }
                waiters->clear();
                break;
@@ -423,6 +457,7 @@ ModelAction * ModelChecker::initialize_curr_action(ModelAction *curr)
                        newcurr->copy_typeandorder(curr);
 
                ASSERT(curr->get_location()==newcurr->get_location());
+               newcurr->copy_from_new(curr);
 
                /* Discard duplicate ModelAction; use action from NodeStack */
                delete curr;
@@ -443,6 +478,14 @@ ModelAction * ModelChecker::initialize_curr_action(ModelAction *curr)
        return newcurr;
 }
 
+/**
+ * This method checks whether a model action is enabled at the given point.
+ * At this point, it checks whether a lock operation would be successful at this point.
+ * If not, it puts the thread in a waiter list.
+ * @param curr is the ModelAction to check whether it is enabled.
+ * @return a bool that indicates whether the action is enabled.
+ */
+
 bool ModelChecker::check_action_enabled(ModelAction *curr) {
        if (curr->is_lock()) {
                std::mutex * lock=(std::mutex *) curr->get_location();
@@ -626,18 +669,18 @@ bool ModelChecker::promises_expired() {
        return false;
 }
 
-/** @returns whether the current partial trace must be a prefix of a
+/** @return whether the current partial trace must be a prefix of a
  * feasible trace. */
 bool ModelChecker::isfeasibleprefix() {
        return promises->size() == 0 && *lazy_sync_size == 0;
 }
 
-/** @returns whether the current partial trace is feasible. */
+/** @return whether the current partial trace is feasible. */
 bool ModelChecker::isfeasible() {
        return !mo_graph->checkForRMWViolation() && isfeasibleotherthanRMW();
 }
 
-/** @returns whether the current partial trace is feasible other than
+/** @return whether the current partial trace is feasible other than
  * multiple RMW reading from the same store. */
 bool ModelChecker::isfeasibleotherthanRMW() {
        if (DBG_ENABLED()) {
@@ -1075,10 +1118,12 @@ bool ModelChecker::release_seq_head(const ModelAction *rf, rel_heads_list_t *rel
                 * the release seq? */
                bool future_ordered = false;
 
+               ModelAction *last = get_last_action(int_to_id(i));
+               if (last && rf->happens_before(last))
+                       future_ordered = true;
+
                for (rit = list->rbegin(); rit != list->rend(); rit++) {
                        const ModelAction *act = *rit;
-                       if (!act->is_write())
-                               continue;
                        /* Reach synchronization -> this thread is complete */
                        if (act->happens_before(release))
                                break;
@@ -1087,6 +1132,10 @@ bool ModelChecker::release_seq_head(const ModelAction *rf, rel_heads_list_t *rel
                                continue;
                        }
 
+                       /* Only writes can break release sequences */
+                       if (!act->is_write())
+                               continue;
+
                        /* Check modification order */
                        if (mo_graph->checkReachable(rf, act)) {
                                /* rf --mo--> act */
@@ -1227,10 +1276,15 @@ void ModelChecker::add_action_to_lists(ModelAction *act)
        (*thrd_last_action)[tid] = act;
 }
 
-ModelAction * ModelChecker::get_last_action(thread_id_t tid)
+/**
+ * @brief Get the last action performed by a particular Thread
+ * @param tid The thread ID of the Thread in question
+ * @return The last action in the thread
+ */
+ModelAction * ModelChecker::get_last_action(thread_id_t tid) const
 {
-       int threadid=id_to_int(tid);
-       if (threadid<(int)thrd_last_action->size())
+       int threadid = id_to_int(tid);
+       if (threadid < (int)thrd_last_action->size())
                return (*thrd_last_action)[id_to_int(tid)];
        else
                return NULL;
@@ -1244,7 +1298,7 @@ ModelAction * ModelChecker::get_last_action(thread_id_t tid)
  * check
  * @return The last seq_cst write
  */
-ModelAction * ModelChecker::get_last_seq_cst(ModelAction *curr)
+ModelAction * ModelChecker::get_last_seq_cst(ModelAction *curr) const
 {
        void *location = curr->get_location();
        action_list_t *list = obj_map->get_safe_ptr(location);
@@ -1256,11 +1310,19 @@ ModelAction * ModelChecker::get_last_seq_cst(ModelAction *curr)
        return NULL;
 }
 
-ModelAction * ModelChecker::get_last_unlock(ModelAction *curr)
+/**
+ * Gets the last unlock operation performed on a particular mutex (i.e., memory
+ * location). This function identifies the mutex according to the current
+ * action, which is presumed to perform on the same mutex.
+ * @param curr The current ModelAction; also denotes the object location to
+ * check
+ * @return The last unlock operation
+ */
+ModelAction * ModelChecker::get_last_unlock(ModelAction *curr) const
 {
        void *location = curr->get_location();
        action_list_t *list = obj_map->get_safe_ptr(location);
-       /* Find: max({i in dom(S) | seq_cst(t_i) && isWrite(t_i) && samevar(t_i, t)}) */
+       /* 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++)
                if ((*rit)->is_unlock())
@@ -1453,7 +1515,7 @@ void ModelChecker::print_summary()
 #if SUPPORT_MOD_ORDER_DUMP
        scheduler->print();
        char buffername[100];
-       sprintf(buffername, "exec%u",num_executions);
+       sprintf(buffername, "exec%04u", num_executions);
        mo_graph->dumpGraphToFile(buffername);
 #endif