optimization - a given write can resolve at most one promise from a rmw
[model-checker.git] / model.cc
index 5753c5c4e016e2a6b7433aeee60706e777505860..1e730d794baca5dc99387053bb98125b3756193a 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);
                        }
@@ -260,8 +271,11 @@ bool ModelChecker::next_execution()
        DEBUG("Number of acquires waiting on pending release sequences: %zu\n",
                        pending_rel_seqs->size());
 
-       if (isfinalfeasible() || DBG_ENABLED())
+
+       if (isfinalfeasible() || (params.bound != 0 && priv->used_sequence_numbers > params.bound ) || DBG_ENABLED() ) {
+               checkDataRaces();
                print_summary();
+       }
 
        if ((diverge = get_next_backtrack()) == NULL)
                return false;
@@ -314,6 +328,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;
        }
@@ -346,8 +386,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 */
@@ -404,7 +448,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();
@@ -461,8 +505,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;
@@ -500,6 +553,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);
        }
@@ -661,41 +751,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()));
@@ -707,8 +802,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;
 }
 
 /**
@@ -766,19 +866,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));
@@ -852,6 +950,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() ||
@@ -875,7 +974,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. */
@@ -998,7 +1097,7 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) {
                                ModelAction *act=*rit;
                                bool foundvalue = false;
                                for (int j = 0; j<act->get_node()->get_read_from_size(); j++) {
-                                       if (act->get_node()->get_read_from_at(i)==write) {
+                                       if (act->get_node()->get_read_from_at(j)==write) {
                                                foundvalue = true;
                                                break;
                                        }
@@ -1278,33 +1377,44 @@ 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;
+       /* Iterate over all threads */
+       for (i = 0; i < thrd_lists->size(); i++) {
+               const ModelAction *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 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;
 
-       if (first_write_after_read==NULL)
-               return true;
+                       if (!reader->happens_before(act))
+                               break;
+                       else if (act->is_write())
+                               write_after_read = act;
+                       else if (act->is_read()&&act->get_reads_from()!=NULL) {
+                               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 !mo_graph->checkReachable(first_write_after_read, writer);
+       return true;
 }
 
-
-
 /**
  * Finds the head(s) of the release sequence(s) containing a given ModelAction.
  * The ModelAction under consideration is expected to be taking part in
@@ -1421,8 +1531,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 */
@@ -1578,6 +1688,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;
+       }
 }
 
 /**
@@ -1629,7 +1753,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;
 }
@@ -1713,8 +1837,9 @@ 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);
+                       curr->get_node()->set_promise(i, act->is_rmw());
                }
        }
 }
@@ -1736,6 +1861,16 @@ void ModelChecker::check_promises(thread_id_t tid, ClockVector *old_cv, ClockVec
        }
 }
 
+void ModelChecker::check_promises_thread_disabled() {
+       for (unsigned int i = 0; i < promises->size(); i++) {
+               Promise *promise = (*promises)[i];
+               if (promise->check_promise()) {
+                       failed_promise = true;
+                       return;
+               }
+       }
+}
+
 /** Checks promises in response to addition to modification order for threads.
  * Definitions:
  * pthread is the thread that performed the read that created the promise
@@ -1795,7 +1930,7 @@ void ModelChecker::mo_check_promises(thread_id_t tid, const ModelAction *write)
                if (promise->has_sync_thread(tid))
                        continue;
                
-               if (mo_graph->checkReachable(promise->get_write(), write)) {
+               if (promise->get_write()&&mo_graph->checkReachable(promise->get_write(), write)) {
                        if (promise->increment_threads(tid)) {
                                failed_promise = true;
                                return;
@@ -1873,7 +2008,7 @@ void ModelChecker::build_reads_from_past(ModelAction *curr)
                                        curr->print();
                                }
 
-                               if (curr->get_sleep_flag()) {
+                               if (curr->get_sleep_flag() && ! curr->is_seqcst()) {
                                        if (sleep_can_read_from(curr, act))
                                                curr->get_node()->add_read_from(act);
                                } else
@@ -1891,6 +2026,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) {
@@ -1900,14 +2036,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()) {
@@ -2073,6 +2208,12 @@ bool ModelChecker::take_step() {
        if (!isfeasible())
                return false;
 
+       if (params.bound != 0) {
+               if (priv->used_sequence_numbers > params.bound) {
+                       return false;
+               }
+       }
+
        DEBUG("(%d, %d)\n", curr ? id_to_int(curr->get_id()) : -1,
                        next ? id_to_int(next->get_id()) : -1);