X-Git-Url: http://plrg.eecs.uci.edu/git/?p=model-checker.git;a=blobdiff_plain;f=model.cc;h=fa4dcc6fd3976281633317152bd4450d636fcc0c;hp=503253af17a5dc0ba4727b3dcff08d1dfcbf07d2;hb=2daff868a7ef4f6235c2776b746cec9947a87b13;hpb=fcae856e66379752f8d227784d28c424206ab0c1 diff --git a/model.cc b/model.cc index 503253a..fa4dcc6 100644 --- a/model.cc +++ b/model.cc @@ -211,11 +211,14 @@ Node * ModelChecker::get_curr_node() const * 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 this will return - * NULL. - * @param curr The current ModelAction. This action might guide the choice of - * next thread. - * @return The next thread to run. If the model-checker has no preference, NULL. + * when exploring a new execution ordering), in which case 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. */ Thread * ModelChecker::get_next_thread(ModelAction *curr) { @@ -229,9 +232,12 @@ Thread * ModelChecker::get_next_thread(ModelAction *curr) return curr->get_thread_operand(); } - /* Have we completed exploring the preselected path? */ + /* + * Have we completed exploring the preselected path? Then let the + * scheduler decide + */ if (diverge == NULL) - return NULL; + return scheduler->select_next_thread(); /* Else, we are trying to replay an execution */ ModelAction *next = node_stack->get_next()->get_action(); @@ -257,10 +263,6 @@ Thread * ModelChecker::get_next_thread(ModelAction *curr) /* The next node will read from a different value. */ tid = next->get_tid(); node_stack->pop_restofstack(2); - } else if (nextnode->increment_future_value()) { - /* The next node will try to read from a different future value. */ - tid = next->get_tid(); - node_stack->pop_restofstack(2); } else if (nextnode->increment_relseq_break()) { /* The next node will try to resolve a release sequence differently */ tid = next->get_tid(); @@ -276,6 +278,8 @@ Thread * ModelChecker::get_next_thread(ModelAction *curr) earliest_diverge = prevnode->get_action(); } } + /* Start the round robin scheduler from this thread id */ + scheduler->set_scheduler_thread(tid); /* The correct sleep set is in the parent node. */ execute_sleep_set(); @@ -307,14 +311,41 @@ void ModelChecker::execute_sleep_set() } } +/** + * @brief Should the current action wake up a given thread? + * + * @param curr The current action + * @param thread The thread that we might wake up + * @return True, if we should wake up the sleeping thread; false otherwise + */ +bool ModelChecker::should_wake_up(const ModelAction *curr, const Thread *thread) const +{ + const ModelAction *asleep = thread->get_pending(); + /* Don't allow partial RMW to wake anyone up */ + if (curr->is_rmwr()) + return false; + /* Synchronizing actions may have been backtracked */ + if (asleep->could_synchronize_with(curr)) + return true; + /* All acquire/release fences and fence-acquire/store-release */ + if (asleep->is_fence() && asleep->is_acquire() && curr->is_release()) + return true; + /* Fence-release + store can awake load-acquire on the same location */ + if (asleep->is_read() && asleep->is_acquire() && curr->same_var(asleep) && curr->is_write()) { + ModelAction *fence_release = get_last_fence_release(curr->get_tid()); + if (fence_release && *(get_last_action(thread->get_id())) < *fence_release) + return true; + } + return false; +} + void ModelChecker::wake_up_sleeping_actions(ModelAction *curr) { for (unsigned int i = 0; i < get_num_threads(); i++) { Thread *thr = get_thread(int_to_id(i)); if (scheduler->is_sleep_set(thr)) { - ModelAction *pending_act = thr->get_pending(); - if ((!curr->is_rmwr()) && pending_act->could_synchronize_with(curr)) - //Remove this thread from sleep set + if (should_wake_up(curr, thr)) + /* Remove this thread from sleep set */ scheduler->remove_sleep(thr); } } @@ -327,11 +358,22 @@ void ModelChecker::set_bad_synchronization() priv->bad_synchronization = true; } +/** + * Check whether the current trace has triggered an assertion which should halt + * its execution. + * + * @return True, if the execution should be aborted; false otherwise + */ bool ModelChecker::has_asserted() const { return priv->asserted; } +/** + * Trigger a trace assertion which should cause this execution to be halted. + * This can be due to a detected bug or due to an infeasibility that should + * halt ASAP. + */ void ModelChecker::set_assert() { priv->asserted = true; @@ -441,8 +483,10 @@ void ModelChecker::record_stats() stats.num_buggy_executions++; else if (is_complete_execution()) stats.num_complete++; - else + else if (scheduler->all_threads_sleeping()) stats.num_redundant++; + else + ASSERT(false); } /** @brief Print execution stats */ @@ -528,25 +572,118 @@ bool ModelChecker::next_execution() return true; } -ModelAction * ModelChecker::get_last_conflict(ModelAction *act) +/** + * @brief Find the last fence-related backtracking conflict for a ModelAction + * + * This function performs the search for the most recent conflicting action + * against which we should perform backtracking, as affected by fence + * operations. This includes pairs of potentially-synchronizing actions which + * occur due to fence-acquire or fence-release, and hence should be explored in + * the opposite execution order. + * + * @param act The current action + * @return The most recent action which conflicts with act due to fences + */ +ModelAction * ModelChecker::get_last_fence_conflict(ModelAction *act) const +{ + /* Only perform release/acquire fence backtracking for stores */ + if (!act->is_write()) + return NULL; + + /* Find a fence-release (or, act is a release) */ + ModelAction *last_release; + if (act->is_release()) + last_release = act; + else + last_release = get_last_fence_release(act->get_tid()); + if (!last_release) + return NULL; + + /* Skip past the release */ + action_list_t *list = action_trace; + action_list_t::reverse_iterator rit; + for (rit = list->rbegin(); rit != list->rend(); rit++) + if (*rit == last_release) + break; + ASSERT(rit != list->rend()); + + /* Find a prior: + * load-acquire + * or + * load --sb-> fence-acquire */ + std::vector< ModelAction *, ModelAlloc > acquire_fences(get_num_threads(), NULL); + std::vector< ModelAction *, ModelAlloc > prior_loads(get_num_threads(), NULL); + bool found_acquire_fences = false; + for ( ; rit != list->rend(); rit++) { + ModelAction *prev = *rit; + if (act->same_thread(prev)) + continue; + + int tid = id_to_int(prev->get_tid()); + + if (prev->is_read() && act->same_var(prev)) { + if (prev->is_acquire()) { + /* Found most recent load-acquire, don't need + * to search for more fences */ + if (!found_acquire_fences) + return NULL; + } else { + prior_loads[tid] = prev; + } + } + if (prev->is_acquire() && prev->is_fence() && !acquire_fences[tid]) { + found_acquire_fences = true; + acquire_fences[tid] = prev; + } + } + + ModelAction *latest_backtrack = NULL; + for (unsigned int i = 0; i < acquire_fences.size(); i++) + if (acquire_fences[i] && prior_loads[i]) + if (!latest_backtrack || *latest_backtrack < *acquire_fences[i]) + latest_backtrack = acquire_fences[i]; + return latest_backtrack; +} + +/** + * @brief Find the last backtracking conflict for a ModelAction + * + * This function performs the search for the most recent conflicting action + * against which we should perform backtracking. This primary includes pairs of + * synchronizing actions which should be explored in the opposite execution + * order. + * + * @param act The current action + * @return The most recent action which conflicts with act + */ +ModelAction * ModelChecker::get_last_conflict(ModelAction *act) const { switch (act->get_type()) { - case ATOMIC_FENCE: + /* case ATOMIC_FENCE: fences don't directly cause backtracking */ case ATOMIC_READ: case ATOMIC_WRITE: case ATOMIC_RMW: { - /* Optimization: relaxed operations don't need backtracking */ - if (act->is_relaxed()) - return NULL; + ModelAction *ret = NULL; + /* linear search: from most recent to oldest */ action_list_t *list = get_safe_ptr_action(obj_map, act->get_location()); action_list_t::reverse_iterator rit; for (rit = list->rbegin(); rit != list->rend(); rit++) { ModelAction *prev = *rit; - if (prev->could_synchronize_with(act)) - return prev; + if (prev->could_synchronize_with(act)) { + ret = prev; + break; + } } - break; + + ModelAction *ret2 = get_last_fence_conflict(act); + if (!ret2) + return ret; + if (!ret) + return ret2; + if (*ret < *ret2) + return ret2; + return ret; } case ATOMIC_LOCK: case ATOMIC_TRYLOCK: { @@ -703,42 +840,52 @@ ModelAction * ModelChecker::get_next_backtrack() } /** - * Processes a read or rmw model action. + * Processes a read model action. * @param curr is the read model action to process. - * @param second_part_of_rmw is boolean that is true is this is the second action of a rmw. * @return True if processing this read updates the mo_graph. */ -bool ModelChecker::process_read(ModelAction *curr, bool second_part_of_rmw) +bool ModelChecker::process_read(ModelAction *curr) { + Node *node = curr->get_node(); uint64_t value = VALUE_NONE; bool updated = false; while (true) { - const ModelAction *reads_from = curr->get_node()->get_read_from(); - if (reads_from != NULL) { - mo_graph->startChanges(); - - value = reads_from->get_value(); - bool r_status = false; + switch (node->get_read_from_status()) { + case READ_FROM_PAST: { + const ModelAction *rf = node->get_read_from_past(); + ASSERT(rf); - if (!second_part_of_rmw) { - check_recency(curr, reads_from); - r_status = r_modification_order(curr, reads_from); - } + mo_graph->startChanges(); + value = rf->get_value(); + check_recency(curr, rf); + bool r_status = r_modification_order(curr, rf); - if (!second_part_of_rmw && is_infeasible() && (curr->get_node()->increment_read_from() || curr->get_node()->increment_future_value())) { + if (is_infeasible() && node->increment_read_from()) { mo_graph->rollbackChanges(); priv->too_many_reads = false; continue; } - read_from(curr, reads_from); + read_from(curr, rf); mo_graph->commitChanges(); mo_check_promises(curr, true); updated |= r_status; - } else if (!second_part_of_rmw) { + break; + } + case READ_FROM_PROMISE: { + Promise *promise = curr->get_node()->get_read_from_promise(); + promise->add_reader(curr); + value = promise->get_value(); + curr->set_read_from_promise(promise); + mo_graph->startChanges(); + updated = r_modification_order(curr, promise); + mo_graph->commitChanges(); + break; + } + case READ_FROM_FUTURE: { /* Read from future value */ - struct future_value fv = curr->get_node()->get_future_value(); + struct future_value fv = node->get_future_value(); Promise *promise = new Promise(curr, fv); value = fv.value; curr->set_read_from_promise(promise); @@ -746,6 +893,10 @@ bool ModelChecker::process_read(ModelAction *curr, bool second_part_of_rmw) mo_graph->startChanges(); updated = r_modification_order(curr, promise); mo_graph->commitChanges(); + break; + } + default: + ASSERT(false); } get_thread(curr)->set_return_value(value); return updated; @@ -974,7 +1125,7 @@ bool ModelChecker::process_thread_action(ModelAction *curr) case THREAD_CREATE: { thrd_t *thrd = (thrd_t *)curr->get_location(); struct thread_params *params = (struct thread_params *)curr->get_value(); - Thread *th = new Thread(thrd, params->func, params->arg); + Thread *th = new Thread(thrd, params->func, params->arg, get_thread(curr)); add_thread(th); th->set_creation(curr); /* Promises can be satisfied by children */ @@ -1188,6 +1339,35 @@ bool ModelChecker::read_from(ModelAction *act, const ModelAction *rf) return false; } +/** + * Check promises and eliminate potentially-satisfying threads when a thread is + * blocked (e.g., join, lock). A thread which is waiting on another thread can + * no longer satisfy a promise generated from that thread. + * + * @param blocker The thread on which a thread is waiting + * @param waiting The waiting thread + */ +void ModelChecker::thread_blocking_check_promises(Thread *blocker, Thread *waiting) +{ + for (unsigned int i = 0; i < promises->size(); i++) { + Promise *promise = (*promises)[i]; + if (!promise->thread_is_available(waiting->get_id())) + continue; + for (unsigned int j = 0; j < promise->get_num_readers(); j++) { + ModelAction *reader = promise->get_reader(j); + if (reader->get_tid() != blocker->get_id()) + continue; + if (promise->eliminate_thread(waiting->get_id())) { + /* Promise has failed */ + priv->failed_promise = true; + } else { + /* Only eliminate the 'waiting' thread once */ + return; + } + } + } +} + /** * @brief Check whether a model action is enabled. * @@ -1211,6 +1391,7 @@ bool ModelChecker::check_action_enabled(ModelAction *curr) { Thread *blocking = (Thread *)curr->get_location(); if (!blocking->is_complete()) { blocking->push_wait_list(curr); + thread_blocking_check_promises(blocking, get_thread(curr)); return false; } } @@ -1256,7 +1437,7 @@ ModelAction * ModelChecker::check_current_action(ModelAction *curr) /* Build may_read_from set for newly-created actions */ if (newly_explored && curr->is_read()) - build_reads_from_past(curr); + build_may_read_from(curr); /* Initialize work_queue with the "current action" work */ work_queue_t work_queue(1, CheckCurrWorkEntry(curr)); @@ -1273,7 +1454,7 @@ ModelAction * ModelChecker::check_current_action(ModelAction *curr) if (process_thread_action(curr)) update_all = true; - if (act->is_read() && process_read(act, second_part_of_rmw)) + if (act->is_read() && !second_part_of_rmw && process_read(act)) update = true; if (act->is_write() && process_write(act)) @@ -1342,7 +1523,6 @@ void ModelChecker::check_curr_backtracking(ModelAction *curr) if ((parnode && !parnode->backtrack_empty()) || !currnode->misc_empty() || !currnode->read_from_empty() || - !currnode->future_value_empty() || !currnode->promise_empty() || !currnode->relseq_break_empty()) { set_latest_backtrack(curr); @@ -1449,7 +1629,7 @@ ModelAction * ModelChecker::process_rmw(ModelAction *act) { void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) { if (params.maxreads != 0) { - if (curr->get_node()->get_read_from_size() <= 1) + if (curr->get_node()->get_read_from_past_size() <= 1) return; //Must make sure that execution is currently feasible... We could //accidentally clear by rolling back @@ -1482,12 +1662,12 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) if (act->get_reads_from() != rf) return; - if (act->get_node()->get_read_from_size() <= 1) + if (act->get_node()->get_read_from_past_size() <= 1) return; } - for (int i = 0; i < curr->get_node()->get_read_from_size(); i++) { + for (int i = 0; i < curr->get_node()->get_read_from_past_size(); i++) { /* Get write */ - const ModelAction *write = curr->get_node()->get_read_from_at(i); + const ModelAction *write = curr->get_node()->get_read_from_past(i); /* Need a different write */ if (write == rf) @@ -1505,8 +1685,8 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) for (int loop = count; loop > 0; loop--, rit++) { 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(j) == write) { + for (int j = 0; j < act->get_node()->get_read_from_past_size(); j++) { + if (act->get_node()->get_read_from_past(j) == write) { foundvalue = true; break; } @@ -1601,13 +1781,13 @@ bool ModelChecker::r_modification_order(ModelAction *curr, const rf_type *rf) added = mo_graph->addEdge(act, rf) || added; } } else { - const ModelAction *prevreadfrom = act->get_reads_from(); - //if the previous read is unresolved, keep going... - if (prevreadfrom == NULL) - continue; - - if (!prevreadfrom->equals(rf)) { - added = mo_graph->addEdge(prevreadfrom, rf) || added; + const ModelAction *prevrf = act->get_reads_from(); + const Promise *prevrf_promise = act->get_reads_from_promise(); + if (prevrf) { + if (!prevrf->equals(rf)) + added = mo_graph->addEdge(prevrf, rf) || added; + } else if (!prevrf_promise->equals(rf)) { + added = mo_graph->addEdge(prevrf_promise, rf) || added; } } break; @@ -1853,7 +2033,7 @@ bool ModelChecker::release_seq_heads(const ModelAction *rf, if (mo_graph->checkForCycles()) return false; - while (rf) { + for ( ; rf != NULL; rf = rf->get_reads_from()) { ASSERT(rf->is_write()); if (rf->is_release()) @@ -1873,8 +2053,6 @@ bool ModelChecker::release_seq_heads(const ModelAction *rf, /* acq_rel RMW is a sufficient stopping condition */ if (rf->is_acquire() && rf->is_release()) return true; /* complete */ - - rf = rf->get_reads_from(); }; if (!rf) { /* read from future: need to settle this later */ @@ -2281,15 +2459,17 @@ bool ModelChecker::resolve_promises(ModelAction *write) for (unsigned int i = 0, promise_index = 0; promise_index < promises->size(); i++) { Promise *promise = (*promises)[promise_index]; if (write->get_node()->get_promise(i)) { - ModelAction *read = promise->get_action(); - read_from(read, write); + for (unsigned int j = 0; j < promise->get_num_readers(); j++) { + ModelAction *read = promise->get_reader(j); + read_from(read, write); + actions_to_check.push_back(read); + } //Make sure the promise's value matches the write's value ASSERT(promise->is_compatible(write)); - mo_graph->resolvePromise(read, write, &mustResolve); + mo_graph->resolvePromise(promise, write, &mustResolve); resolved.push_back(promise); promises->erase(promises->begin() + promise_index); - actions_to_check.push_back(read); haveResolved = true; } else @@ -2324,15 +2504,20 @@ void ModelChecker::compute_promises(ModelAction *curr) { for (unsigned int i = 0; i < promises->size(); i++) { Promise *promise = (*promises)[i]; - const ModelAction *act = promise->get_action(); - if (!act->happens_before(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, act->is_rmw()); + if (!promise->is_compatible(curr) || promise->get_value() != curr->get_value()) + continue; + + bool satisfy = true; + for (unsigned int j = 0; j < promise->get_num_readers(); j++) { + const ModelAction *act = promise->get_reader(j); + if (act->happens_before(curr) || + act->could_synchronize_with(curr)) { + satisfy = false; + break; + } } + if (satisfy) + curr->get_node()->set_promise(i); } } @@ -2341,13 +2526,17 @@ void ModelChecker::check_promises(thread_id_t tid, ClockVector *old_cv, ClockVec { for (unsigned int i = 0; i < promises->size(); i++) { Promise *promise = (*promises)[i]; - const ModelAction *act = promise->get_action(); - if ((old_cv == NULL || !old_cv->synchronized_since(act)) && - merge_cv->synchronized_since(act)) { - if (promise->eliminate_thread(tid)) { - //Promise has failed - priv->failed_promise = true; - return; + if (!promise->thread_is_available(tid)) + continue; + for (unsigned int j = 0; j < promise->get_num_readers(); j++) { + const ModelAction *act = promise->get_reader(j); + if ((!old_cv || !old_cv->synchronized_since(act)) && + merge_cv->synchronized_since(act)) { + if (promise->eliminate_thread(tid)) { + /* Promise has failed */ + priv->failed_promise = true; + return; + } } } } @@ -2384,15 +2573,20 @@ void ModelChecker::mo_check_promises(const ModelAction *act, bool is_read_check) for (unsigned int i = 0; i < promises->size(); i++) { Promise *promise = (*promises)[i]; - const ModelAction *pread = promise->get_action(); // Is this promise on the same location? - if (!pread->same_var(write)) + if (promise->get_value() != write->get_value()) continue; - if (pread->happens_before(act) && mo_graph->checkPromise(write, promise)) { - priv->failed_promise = true; - return; + for (unsigned int j = 0; j < promise->get_num_readers(); j++) { + const ModelAction *pread = promise->get_reader(j); + if (!pread->happens_before(act)) + continue; + if (mo_graph->checkPromise(write, promise)) { + priv->failed_promise = true; + return; + } + break; } // Don't do any lookups twice for the same thread @@ -2433,12 +2627,12 @@ void ModelChecker::compute_relseq_breakwrites(ModelAction *curr) /** * Build up an initial set of all past writes that this 'read' action may read - * from. This set is determined by the clock vector's "happens before" - * relationship. + * from, as well as any previously-observed future values that must still be valid. + * * @param curr is the current ModelAction that we are exploring; it must be a * 'read' operation. */ -void ModelChecker::build_reads_from_past(ModelAction *curr) +void ModelChecker::build_may_read_from(ModelAction *curr) { std::vector *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, curr->get_location()); unsigned int i; @@ -2474,7 +2668,7 @@ void ModelChecker::build_reads_from_past(ModelAction *curr) mo_graph->startChanges(); r_modification_order(curr, act); if (!is_infeasible()) - curr->get_node()->add_read_from(act); + curr->get_node()->add_read_from_past(act); mo_graph->rollbackChanges(); } @@ -2483,8 +2677,23 @@ void ModelChecker::build_reads_from_past(ModelAction *curr) break; } } + + /* Inherit existing, promised future values */ + for (i = 0; i < promises->size(); i++) { + const Promise *promise = (*promises)[i]; + const ModelAction *promise_read = promise->get_reader(0); + if (promise_read->same_var(curr)) { + /* Only add feasible future-values */ + mo_graph->startChanges(); + r_modification_order(curr, promise); + if (!is_infeasible()) + curr->get_node()->add_read_from_promise(promise_read); + mo_graph->rollbackChanges(); + } + } + /* We may find no valid may-read-from only if the execution is doomed */ - if (!curr->get_node()->get_read_from_size()) { + if (!curr->get_node()->read_from_size()) { priv->no_valid_reads = true; set_assert(); } @@ -2492,15 +2701,15 @@ void ModelChecker::build_reads_from_past(ModelAction *curr) if (DBG_ENABLED()) { model_print("Reached read action:\n"); curr->print(); - model_print("Printing may_read_from\n"); - curr->get_node()->print_may_read_from(); - model_print("End printing may_read_from\n"); + model_print("Printing read_from_past\n"); + curr->get_node()->print_read_from_past(); + model_print("End printing read_from_past\n"); } } bool ModelChecker::sleep_can_read_from(ModelAction *curr, const ModelAction *write) { - while (true) { + for ( ; write != NULL; write = write->get_reads_from()) { /* UNINIT actions don't have a Node, and they never sleep */ if (write->is_uninitialized()) return true; @@ -2509,13 +2718,10 @@ bool ModelChecker::sleep_can_read_from(ModelAction *curr, const ModelAction *wri bool thread_sleep = prevnode->enabled_status(curr->get_tid()) == THREAD_SLEEP_SET; if (write->is_release() && thread_sleep) return true; - if (!write->is_rmw()) { + if (!write->is_rmw()) return false; - } - if (write->get_reads_from() == NULL) - return true; - write = write->get_reads_from(); } + return true; } /** @@ -2588,9 +2794,11 @@ void ModelChecker::print_summary() const #endif model_print("Execution %d:", stats.num_total); - if (isfeasibleprefix()) + if (isfeasibleprefix()) { + if (scheduler->all_threads_sleeping()) + model_print(" SLEEP-SET REDUNDANT"); model_print("\n"); - else + } else print_infeasibility(" INFEASIBLE"); print_list(action_trace); model_print("\n"); @@ -2636,6 +2844,26 @@ Thread * ModelChecker::get_thread(const ModelAction *act) const return get_thread(act->get_tid()); } +/** + * @brief Get a Promise's "promise number" + * + * A "promise number" is an index number that is unique to a promise, valid + * only for a specific snapshot of an execution trace. Promises may come and go + * as they are generated an resolved, so an index only retains meaning for the + * current snapshot. + * + * @param promise The Promise to check + * @return The promise index, if the promise still is valid; otherwise -1 + */ +int ModelChecker::get_promise_number(const Promise *promise) const +{ + for (unsigned int i = 0; i < promises->size(); i++) + if ((*promises)[i] == promise) + return i; + /* Not found */ + return -1; +} + /** * @brief Check if a Thread is currently enabled * @param t The Thread to check @@ -2656,6 +2884,19 @@ bool ModelChecker::is_enabled(thread_id_t tid) const return scheduler->is_enabled(tid); } +/** + * Switch from a model-checker context to a user-thread context. This is the + * complement of ModelChecker::switch_to_master and must be called from the + * model-checker context + * + * @param thread The user-thread to switch to + */ +void ModelChecker::switch_from_master(Thread *thread) +{ + scheduler->set_current_thread(thread); + Thread::swap(&system_context, thread); +} + /** * Switch from a user-context to the "master thread" context (a.k.a. system * context). This switch is made with the intention of exploring a particular @@ -2708,35 +2949,10 @@ Thread * ModelChecker::take_step(ModelAction *curr) scheduler->remove_thread(curr_thrd); Thread *next_thrd = get_next_thread(curr); - next_thrd = scheduler->next_thread(next_thrd); DEBUG("(%d, %d)\n", curr_thrd ? id_to_int(curr_thrd->get_id()) : -1, next_thrd ? id_to_int(next_thrd->get_id()) : -1); - /* - * Launch end-of-execution release sequence fixups only when there are: - * - * (1) no more user threads to run (or when execution replay chooses - * the 'model_thread') - * (2) pending release sequences - * (3) pending assertions (i.e., data races) - * (4) no pending promises - */ - if (!pending_rel_seqs->empty() && (!next_thrd || next_thrd->is_model_thread()) && - is_feasible_prefix_ignore_relseq() && !unrealizedraces.empty()) { - model_print("*** WARNING: release sequence fixup action (%zu pending release seuqences) ***\n", - pending_rel_seqs->size()); - ModelAction *fixup = new ModelAction(MODEL_FIXUP_RELSEQ, - std::memory_order_seq_cst, NULL, VALUE_NONE, - model_thread); - model_thread->set_pending(fixup); - return model_thread; - } - - /* next_thrd == NULL -> don't take any more steps */ - if (!next_thrd) - return NULL; - return next_thrd; } @@ -2751,16 +2967,21 @@ void ModelChecker::run() { do { thrd_t user_thread; - Thread *t = new Thread(&user_thread, &user_main_wrapper, NULL); + Thread *t = new Thread(&user_thread, &user_main_wrapper, NULL, NULL); add_thread(t); do { + /* + * Stash next pending action(s) for thread(s). There + * should only need to stash one thread's action--the + * thread which just took a step--plus the first step + * for any newly-created thread + */ for (unsigned int i = 0; i < get_num_threads(); i++) { thread_id_t tid = int_to_id(i); Thread *thr = get_thread(tid); if (!thr->is_model_thread() && !thr->is_complete() && !thr->get_pending()) { - scheduler->next_thread(thr); - Thread::swap(&system_context, thr); + switch_from_master(thr); } } @@ -2774,8 +2995,29 @@ void ModelChecker::run() t->set_pending(NULL); t = take_step(curr); } while (t && !t->is_model_thread()); - /** @TODO Re-write release sequence fixups here */ + + /* + * Launch end-of-execution release sequence fixups only when + * the execution is otherwise feasible AND there are: + * + * (1) pending release sequences + * (2) pending assertions that could be invalidated by a change + * in clock vectors (i.e., data races) + * (3) no pending promises + */ + while (!pending_rel_seqs->empty() && + is_feasible_prefix_ignore_relseq() && + !unrealizedraces.empty()) { + model_print("*** WARNING: release sequence fixup action " + "(%zu pending release seuqence(s)) ***\n", + pending_rel_seqs->size()); + ModelAction *fixup = new ModelAction(MODEL_FIXUP_RELSEQ, + std::memory_order_seq_cst, NULL, VALUE_NONE, + model_thread); + take_step(fixup); + }; } while (next_execution()); + model_print("******* Model-checking complete: *******\n"); print_stats(); }