fix two bugs in model.cc...mainly don't print bogus data race messages...
[model-checker.git] / model.cc
index c0cc93eb0a80523eefc48f2fe7ec3c8119d8619c..0fb315e24ce5b4a8e0c5db6d3c15516080f3e10e 100644 (file)
--- a/model.cc
+++ b/model.cc
@@ -1,5 +1,6 @@
 #include <stdio.h>
 #include <algorithm>
+#include <mutex>
 
 #include "model.h"
 #include "action.h"
@@ -11,7 +12,6 @@
 #include "cyclegraph.h"
 #include "promise.h"
 #include "datarace.h"
-#include "mutex.h"
 #include "threads-model.h"
 
 #define INITIAL_THREAD_ID      0
@@ -31,6 +31,7 @@ ModelChecker::ModelChecker(struct model_params params) :
        thread_map(new HashTable<int, Thread *, int>()),
        obj_map(new HashTable<const void *, action_list_t, uintptr_t, 4>()),
        lock_waiters_map(new HashTable<const void *, action_list_t, uintptr_t, 4>()),
+       condvar_waiters_map(new HashTable<const void *, action_list_t, uintptr_t, 4>()),
        obj_thrd_map(new HashTable<void *, std::vector<action_list_t>, uintptr_t, 4 >()),
        promises(new std::vector< Promise *, SnapshotAlloc<Promise *> >()),
        futurevalues(new std::vector< struct PendingFutureValue, SnapshotAlloc<struct PendingFutureValue> >()),
@@ -63,6 +64,7 @@ ModelChecker::~ModelChecker()
        delete obj_thrd_map;
        delete obj_map;
        delete lock_waiters_map;
+       delete condvar_waiters_map;
        delete action_trace;
 
        for (unsigned int i = 0; i < promises->size(); i++)
@@ -116,6 +118,10 @@ modelclock_t ModelChecker::get_next_seq_num()
        return ++priv->used_sequence_numbers;
 }
 
+Node * ModelChecker::get_curr_node() {
+       return node_stack->get_head();
+}
+
 /**
  * @brief Choose the next thread to execute.
  *
@@ -158,7 +164,11 @@ Thread * ModelChecker::get_next_thread(ModelAction *curr)
                scheduler->update_sleep_set(prevnode);
 
                /* Reached divergence point */
-               if (nextnode->increment_promise()) {
+               if (nextnode->increment_misc()) {
+                       /* The next node will try to satisfy a different misc_index values. */
+                       tid = next->get_tid();
+                       node_stack->pop_restofstack(2);
+               } else if (nextnode->increment_promise()) {
                        /* The next node will try to satisfy a different set of promises. */
                        tid = next->get_tid();
                        node_stack->pop_restofstack(2);
@@ -208,7 +218,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->get_enabled(thr) == THREAD_SLEEP_SET ) {
+               if ( scheduler->get_enabled(thr) == THREAD_SLEEP_SET &&
+                                thr->get_pending() == NULL ) {
                        thr->set_state(THREAD_RUNNING);
                        scheduler->next_thread(thr);
                        Thread::swap(&system_context, thr);
@@ -225,7 +236,7 @@ void ModelChecker::wake_up_sleeping_actions(ModelAction * curr) {
                Thread *thr=get_thread(tid);
                if ( scheduler->get_enabled(thr) == THREAD_SLEEP_SET ) {
                        ModelAction *pending_act=thr->get_pending();
-                       if (pending_act->could_synchronize_with(curr)) {
+                       if ((!curr->is_rmwr())&&pending_act->could_synchronize_with(curr)) {
                                //Remove this thread from sleep set
                                scheduler->remove_sleep(thr);
                        }
@@ -315,6 +326,32 @@ ModelAction * ModelChecker::get_last_conflict(ModelAction *act)
                }
                break;
        }
+       case ATOMIC_WAIT: {
+               /* 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->same_thread(prev)&&prev->is_failed_trylock())
+                               return prev;
+                       if (!act->same_thread(prev)&&prev->is_notify())
+                               return prev;
+               }
+               break;
+       }
+
+       case ATOMIC_NOTIFY_ALL:
+       case ATOMIC_NOTIFY_ONE: {
+               /* 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->same_thread(prev)&&prev->is_wait())
+                               return prev;
+               }
+               break;
+       }
        default:
                break;
        }
@@ -347,8 +384,12 @@ void ModelChecker::set_backtracking(ModelAction *act)
        for(int i = low_tid; i < high_tid; i++) {
                thread_id_t tid = int_to_id(i);
 
+               /* Make sure this thread can be enabled here. */
+               if (i >= node->get_num_threads())
+                       break;
+
                /* Don't backtrack into a point where the thread is disabled or sleeping. */
-               if (node->get_enabled_array()[i]!=THREAD_ENABLED)
+               if (node->enabled_status(tid)!=THREAD_ENABLED)
                        continue;
        
                /* Check if this has been explored already */
@@ -405,7 +446,7 @@ ModelAction * ModelChecker::get_next_backtrack()
  */
 bool ModelChecker::process_read(ModelAction *curr, bool second_part_of_rmw)
 {
-       uint64_t value;
+       uint64_t value = VALUE_NONE;
        bool updated = false;
        while (true) {
                const ModelAction *reads_from = curr->get_node()->get_read_from();
@@ -462,8 +503,17 @@ bool ModelChecker::process_read(ModelAction *curr, bool second_part_of_rmw)
  * @return True if synchronization was updated; false otherwise
  */
 bool ModelChecker::process_mutex(ModelAction *curr) {
-       std::mutex *mutex = (std::mutex *)curr->get_location();
-       struct std::mutex_state *state = mutex->get_state();
+       std::mutex *mutex=NULL;
+       struct std::mutex_state *state=NULL;
+
+       if (curr->is_trylock() || curr->is_lock() || curr->is_unlock()) {
+               mutex = (std::mutex *)curr->get_location();
+               state = mutex->get_state();
+       } else if(curr->is_wait()) {
+               mutex = (std::mutex *)curr->get_value();
+               state = mutex->get_state();
+       }
+
        switch (curr->get_type()) {
        case ATOMIC_TRYLOCK: {
                bool success = !state->islocked;
@@ -501,6 +551,43 @@ bool ModelChecker::process_mutex(ModelAction *curr) {
                waiters->clear();
                break;
        }
+       case ATOMIC_WAIT: {
+               //unlock the lock
+               state->islocked = false;
+               //wake up the other threads
+               action_list_t *waiters = lock_waiters_map->get_safe_ptr((void *) curr->get_value());
+               //activate all the waiting threads
+               for (action_list_t::iterator rit = waiters->begin(); rit != waiters->end(); rit++) {
+                       scheduler->wake(get_thread(*rit));
+               }
+               waiters->clear();
+               //check whether we should go to sleep or not...simulate spurious failures
+               if (curr->get_node()->get_misc()==0) {
+                       condvar_waiters_map->get_safe_ptr(curr->get_location())->push_back(curr);
+                       //disable us
+                       scheduler->sleep(get_current_thread());
+               }
+               break;
+       }
+       case ATOMIC_NOTIFY_ALL: {
+               action_list_t *waiters = condvar_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++) {
+                       scheduler->wake(get_thread(*rit));
+               }
+               waiters->clear();
+               break;
+       }
+       case ATOMIC_NOTIFY_ONE: {
+               action_list_t *waiters = condvar_waiters_map->get_safe_ptr(curr->get_location());
+               int wakeupthread=curr->get_node()->get_misc();
+               action_list_t::iterator it = waiters->begin();
+               advance(it, wakeupthread);
+               scheduler->wake(get_thread(*it));
+               waiters->erase(it);
+               break;
+       }
+
        default:
                ASSERT(0);
        }
@@ -662,41 +749,46 @@ void ModelChecker::process_relseq_fixup(ModelAction *curr, work_queue_t *work_qu
  * 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.
+ * freed/invalidated after the execution of this function, with a different
+ * action "returned" its place (pass-by-reference)
+ * @return True if curr is a newly-explored action; false otherwise
  */
-ModelAction * ModelChecker::initialize_curr_action(ModelAction *curr)
+bool ModelChecker::initialize_curr_action(ModelAction **curr)
 {
        ModelAction *newcurr;
 
-       if (curr->is_rmwc() || curr->is_rmw()) {
-               newcurr = process_rmw(curr);
-               delete curr;
+       if ((*curr)->is_rmwc() || (*curr)->is_rmw()) {
+               newcurr = process_rmw(*curr);
+               delete *curr;
 
                if (newcurr->is_rmw())
                        compute_promises(newcurr);
-               return newcurr;
+
+               *curr = newcurr;
+               return false;
        }
 
-       curr->set_seq_number(get_next_seq_num());
+       (*curr)->set_seq_number(get_next_seq_num());
 
-       newcurr = node_stack->explore_action(curr, scheduler->get_enabled());
+       newcurr = node_stack->explore_action(*curr, scheduler->get_enabled());
        if (newcurr) {
                /* First restore type and order in case of RMW operation */
-               if (curr->is_rmwr())
-                       newcurr->copy_typeandorder(curr);
+               if ((*curr)->is_rmwr())
+                       newcurr->copy_typeandorder(*curr);
 
-               ASSERT(curr->get_location() == newcurr->get_location());
-               newcurr->copy_from_new(curr);
+               ASSERT((*curr)->get_location() == newcurr->get_location());
+               newcurr->copy_from_new(*curr);
 
                /* Discard duplicate ModelAction; use action from NodeStack */
-               delete curr;
+               delete *curr;
 
                /* Always compute new clock vector */
                newcurr->create_cv(get_parent_action(newcurr->get_tid()));
+
+               *curr = newcurr;
+               return false; /* Action was explored previously */
        } else {
-               newcurr = curr;
+               newcurr = *curr;
 
                /* Always compute new clock vector */
                newcurr->create_cv(get_parent_action(newcurr->get_tid()));
@@ -708,8 +800,13 @@ ModelAction * ModelChecker::initialize_curr_action(ModelAction *curr)
                        compute_promises(newcurr);
                else if (newcurr->is_relseq_fixup())
                        compute_relseq_breakwrites(newcurr);
+               else if (newcurr->is_wait())
+                       newcurr->get_node()->set_misc_max(2);
+               else if (newcurr->is_notify_one()) {
+                       newcurr->get_node()->set_misc_max(condvar_waiters_map->get_safe_ptr(newcurr->get_location())->size());
+               }
+               return true; /* This was a new ModelAction */
        }
-       return newcurr;
 }
 
 /**
@@ -767,19 +864,17 @@ Thread * ModelChecker::check_current_action(ModelAction *curr)
                return get_next_thread(NULL);
        }
 
-       wake_up_sleeping_actions(curr);
-
-       ModelAction *newcurr = initialize_curr_action(curr);
+       bool newly_explored = initialize_curr_action(&curr);
 
+       wake_up_sleeping_actions(curr);
 
        /* Add the action to lists before any other model-checking tasks */
        if (!second_part_of_rmw)
-               add_action_to_lists(newcurr);
+               add_action_to_lists(curr);
 
        /* Build may_read_from set for newly-created actions */
-       if (curr == newcurr && curr->is_read())
+       if (newly_explored && curr->is_read())
                build_reads_from_past(curr);
-       curr = newcurr;
 
        /* Initialize work_queue with the "current action" work */
        work_queue_t work_queue(1, CheckCurrWorkEntry(curr));
@@ -853,6 +948,7 @@ void ModelChecker::check_curr_backtracking(ModelAction * curr) {
        Node *parnode = currnode->get_parent();
 
        if ((!parnode->backtrack_empty() ||
+                        !currnode->misc_empty() ||
                         !currnode->read_from_empty() ||
                         !currnode->future_value_empty() ||
                         !currnode->promise_empty() ||
@@ -876,7 +972,7 @@ bool ModelChecker::promises_expired() {
 /** @return whether the current partial trace must be a prefix of a
  * feasible trace. */
 bool ModelChecker::isfeasibleprefix() {
-       return promises->size() == 0 && pending_rel_seqs->size() == 0;
+       return promises->size() == 0 && pending_rel_seqs->size() == 0 && isfeasible();
 }
 
 /** @return whether the current partial trace is feasible. */
@@ -1279,32 +1375,40 @@ bool ModelChecker::thin_air_constraint_may_allow(const ModelAction * writer, con
        return true;
 }
 
-/** Arbitrary reads from the future are not allowed.  Section 29.3
- * part 9 places some constraints.  This method checks one result of constraint
- * constraint.  Others require compiler support. */
-bool ModelChecker::mo_may_allow(const ModelAction * writer, const ModelAction *reader) {
+/**
+ * Arbitrary reads from the future are not allowed. Section 29.3 part 9 places
+ * some constraints. This method checks one the following constraint (others
+ * require compiler support):
+ *
+ *   If X --hb-> Y --mo-> Z, then X should not read from Z.
+ */
+bool ModelChecker::mo_may_allow(const ModelAction *writer, const ModelAction *reader)
+{
        std::vector<action_list_t> *thrd_lists = obj_thrd_map->get_safe_ptr(reader->get_location());
+       unsigned int i;
 
-       //Get write that follows reader action
-       action_list_t *list = &(*thrd_lists)[id_to_int(reader->get_tid())];
-       action_list_t::reverse_iterator rit;
-       ModelAction *first_write_after_read=NULL;
-
-       for (rit = list->rbegin(); rit != list->rend(); rit++) {
-               ModelAction *act = *rit;
-               if (act==reader)
-                       break;
-               if (act->is_write())
-                       first_write_after_read=act;
-       }
+       /* Iterate over all threads */
+       for (i = 0; i < thrd_lists->size(); i++) {
+               ModelAction *write_after_read = NULL;
 
-       if (first_write_after_read==NULL)
-               return true;
+               /* Iterate over actions in thread, starting from most recent */
+               action_list_t *list = &(*thrd_lists)[i];
+               action_list_t::reverse_iterator rit;
+               for (rit = list->rbegin(); rit != list->rend(); rit++) {
+                       ModelAction *act = *rit;
 
-       return !mo_graph->checkReachable(first_write_after_read, writer);
-}
+                       if (!reader->happens_before(act))
+                               break;
+                       else if (act->is_write())
+                               write_after_read = act;
+               }
 
+               if (write_after_read && mo_graph->checkReachable(write_after_read, writer))
+                       return false;
+       }
 
+       return true;
+}
 
 /**
  * Finds the head(s) of the release sequence(s) containing a given ModelAction.
@@ -1422,8 +1526,8 @@ bool ModelChecker::release_seq_heads(const ModelAction *rf,
                                continue;
                        }
 
-                       /* Only writes can break release sequences */
-                       if (!act->is_write())
+                       /* Only non-RMW writes can break release sequences */
+                       if (!act->is_write() || act->is_rmw())
                                continue;
 
                        /* Check modification order */
@@ -1579,6 +1683,20 @@ void ModelChecker::add_action_to_lists(ModelAction *act)
        if ((int)thrd_last_action->size() <= tid)
                thrd_last_action->resize(get_num_threads());
        (*thrd_last_action)[tid] = 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);
+               (*vec)[tid].push_back(act);
+
+               if ((int)thrd_last_action->size() <= tid)
+                       thrd_last_action->resize(get_num_threads());
+               (*thrd_last_action)[tid] = act;
+       }
 }
 
 /**
@@ -1630,7 +1748,7 @@ ModelAction * ModelChecker::get_last_unlock(ModelAction *curr) const
        /* 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())
+               if ((*rit)->is_unlock() || (*rit)->is_wait())
                        return *rit;
        return NULL;
 }
@@ -1714,6 +1832,7 @@ void ModelChecker::compute_promises(ModelAction *curr)
                                act->is_read() &&
                                !act->could_synchronize_with(curr) &&
                                !act->same_thread(curr) &&
+                               act->get_location() == curr->get_location() &&
                                promise->get_value() == curr->get_value()) {
                        curr->get_node()->set_promise(i);
                }
@@ -1892,6 +2011,7 @@ void ModelChecker::build_reads_from_past(ModelAction *curr)
        if (!initialized) {
                /** @todo Need a more informative way of reporting errors. */
                printf("ERROR: may read from uninitialized atomic\n");
+               set_assert();
        }
 
        if (DBG_ENABLED() || !initialized) {
@@ -1901,14 +2021,13 @@ void ModelChecker::build_reads_from_past(ModelAction *curr)
                curr->get_node()->print_may_read_from();
                printf("End printing may_read_from\n");
        }
-
-       ASSERT(initialized);
 }
 
 bool ModelChecker::sleep_can_read_from(ModelAction * curr, const ModelAction *write) {
        while(true) {
                Node *prevnode=write->get_node()->get_parent();
-               bool thread_sleep=prevnode->get_enabled_array()[id_to_int(curr->get_tid())]==THREAD_SLEEP_SET;
+               
+               bool thread_sleep=prevnode->enabled_status(curr->get_tid())==THREAD_SLEEP_SET;
                if (write->is_release()&&thread_sleep)
                        return true;
                if (!write->is_rmw()) {