X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;ds=sidebyside;f=model.cc;h=d15a094f7d8049e1a1e81ed8ac78527b6d1ca66a;hb=ed060a0d38302898816bedc7205d0283fffcc6f1;hp=7741760025162b786b48c903aaf86937bb4cb0f1;hpb=db5cad55a147218984ef544439ee8a936303ddf8;p=c11tester.git diff --git a/model.cc b/model.cc index 77417600..d15a094f 100644 --- a/model.cc +++ b/model.cc @@ -204,34 +204,43 @@ Node * ModelChecker::get_curr_node() const return node_stack->get_head(); } +/** + * @brief Select the next thread to execute based on the curren action + * + * RMW actions occur in two parts, and we cannot split them. And THREAD_CREATE + * actions should be followed by the execution of their child thread. In either + * case, the current action should determine the next thread schedule. + * + * @param curr The current action + * @return The next thread to run, if the current action will determine this + * selection; otherwise NULL + */ +Thread * ModelChecker::action_select_next_thread(const ModelAction *curr) const +{ + /* Do not split atomic RMW */ + if (curr->is_rmwr()) + return get_thread(curr); + /* Follow CREATE with the created thread */ + if (curr->get_type() == THREAD_CREATE) + return curr->get_thread_operand(); + return NULL; +} + /** * @brief Choose the next thread to execute. * - * This function chooses the next thread that should execute. It can force the - * adjacency of read/write portions of a RMW action, force THREAD_CREATE to be - * followed by a THREAD_START, or it can enforce execution replay/backtracking. - * The model-checker may have no preference regarding the next thread (i.e., - * when exploring a new execution ordering), in which case we defer to the - * scheduler. + * This function chooses the next thread that should execute. It can enforce + * execution replay/backtracking or, if the model-checker has no preference + * regarding the next thread (i.e., when exploring a new execution ordering), + * we defer to the scheduler. * - * @param curr Optional: The current ModelAction. Only used if non-NULL and it - * might guide the choice of next thread (i.e., THREAD_CREATE should be - * followed by THREAD_START, or ATOMIC_RMWR followed by ATOMIC_{RMW,RMWC}) - * @return The next chosen thread to run, if any exist. Or else if no threads - * remain to be executed, return NULL. + * @return The next chosen thread to run, if any exist. Or else if the current + * execution should terminate, return NULL. */ -Thread * ModelChecker::get_next_thread(ModelAction *curr) +Thread * ModelChecker::get_next_thread() { thread_id_t tid; - if (curr != NULL) { - /* Do not split atomic actions. */ - if (curr->is_rmwr()) - return get_thread(curr); - else if (curr->get_type() == THREAD_CREATE) - return curr->get_thread_operand(); - } - /* * Have we completed exploring the preselected path? Then let the * scheduler decide @@ -400,6 +409,22 @@ bool ModelChecker::is_deadlocked() const return blocking_threads; } +/** + * Check if a Thread has entered a circular wait deadlock situation. This will + * not check other threads for potential deadlock situations, and may miss + * deadlocks involving WAIT. + * + * @param t The thread which may have entered a deadlock + * @return True if this Thread entered a deadlock; false otherwise + */ +bool ModelChecker::is_circular_wait(const Thread *t) const +{ + for (Thread *waiting = t->waiting_on() ; waiting != NULL; waiting = waiting->waiting_on()) + if (waiting == t) + return true; + return false; +} + /** * Check if this is a complete execution. That is, have all thread completed * execution (rather than exiting because sleep sets have forced a redundant @@ -514,7 +539,7 @@ void ModelChecker::print_execution(bool printbugs) const { print_program_output(); - if (DBG_ENABLED() || params.verbose) { + if (params.verbose) { model_print("Earliest divergence point since last feasible execution:\n"); if (earliest_diverge) earliest_diverge->print(); @@ -558,7 +583,7 @@ bool ModelChecker::next_execution() record_stats(); /* Output */ - if (DBG_ENABLED() || params.verbose || (complete && have_bug_reports())) + if (params.verbose || (complete && have_bug_reports())) print_execution(complete); else clear_program_output(); @@ -935,7 +960,7 @@ bool ModelChecker::process_mutex(ModelAction *curr) switch (curr->get_type()) { case ATOMIC_TRYLOCK: { - bool success = !state->islocked; + bool success = !state->locked; curr->set_try_lock(success); if (!success) { get_thread(curr)->set_return_value(0); @@ -947,7 +972,7 @@ bool ModelChecker::process_mutex(ModelAction *curr) case ATOMIC_LOCK: { if (curr->get_cv()->getClock(state->alloc_tid) <= state->alloc_clock) assert_bug("Lock access before initialization"); - state->islocked = true; + state->locked = get_thread(curr); ModelAction *unlock = get_last_unlock(curr); //synchronize with the previous unlock statement if (unlock != NULL) { @@ -958,7 +983,7 @@ bool ModelChecker::process_mutex(ModelAction *curr) } case ATOMIC_UNLOCK: { //unlock the lock - state->islocked = false; + state->locked = NULL; //wake up the other threads action_list_t *waiters = get_safe_ptr_action(lock_waiters_map, curr->get_location()); //activate all the waiting threads @@ -970,7 +995,7 @@ bool ModelChecker::process_mutex(ModelAction *curr) } case ATOMIC_WAIT: { //unlock the lock - state->islocked = false; + state->locked = NULL; //wake up the other threads action_list_t *waiters = get_safe_ptr_action(lock_waiters_map, (void *) curr->get_value()); //activate all the waiting threads @@ -1402,7 +1427,7 @@ bool ModelChecker::check_action_enabled(ModelAction *curr) { if (curr->is_lock()) { std::mutex *lock = (std::mutex *)curr->get_location(); struct std::mutex_state *state = lock->get_state(); - if (state->islocked) { + if (state->locked) { //Stick the action in the appropriate waiting queue get_safe_ptr_action(lock_waiters_map, curr->get_location())->push_back(curr); return false; @@ -2856,6 +2881,14 @@ void ModelChecker::print_summary() const print_infeasibility(" INFEASIBLE"); print_list(action_trace); model_print("\n"); + if (!promises->empty()) { + model_print("Pending promises:\n"); + for (unsigned int i = 0; i < promises->size(); i++) { + model_print(" [P%u] ", i); + (*promises)[i]->print(); + } + model_print("\n"); + } } /** @@ -3002,7 +3035,11 @@ Thread * ModelChecker::take_step(ModelAction *curr) if (curr_thrd->is_blocked() || curr_thrd->is_complete()) scheduler->remove_thread(curr_thrd); - Thread *next_thrd = get_next_thread(curr); + Thread *next_thrd = NULL; + if (curr) + next_thrd = action_select_next_thread(curr); + if (!next_thrd) + next_thrd = get_next_thread(); DEBUG("(%d, %d)\n", curr_thrd ? id_to_int(curr_thrd->get_id()) : -1, next_thrd ? id_to_int(next_thrd->get_id()) : -1); @@ -3036,6 +3073,8 @@ void ModelChecker::run() Thread *thr = get_thread(tid); if (!thr->is_model_thread() && !thr->is_complete() && !thr->get_pending()) { switch_from_master(thr); + if (is_circular_wait(thr)) + assert_bug("Deadlock detected"); } }