X-Git-Url: http://plrg.eecs.uci.edu/git/?p=c11tester.git;a=blobdiff_plain;f=model.cc;h=f96249fcb9cf9ceeb9eb0b97b5a13c9b74b96b60;hp=9ed6d423452a52e5c4ee006fa624fad809bdebc3;hb=02e807a227da687ff2606d6eecf49aff372d3a51;hpb=80f5924fb6c148ecb703dfeae5a751948062588b diff --git a/model.cc b/model.cc index 9ed6d423..f96249fc 100644 --- a/model.cc +++ b/model.cc @@ -37,6 +37,27 @@ struct bug_message { * Structure for holding small ModelChecker members that should be snapshotted */ struct model_snapshot_members { + model_snapshot_members() : + current_action(NULL), + /* First thread created will have id INITIAL_THREAD_ID */ + next_thread_id(INITIAL_THREAD_ID), + used_sequence_numbers(0), + nextThread(NULL), + next_backtrack(NULL), + bugs(), + stats(), + failed_promise(false), + too_many_reads(false), + bad_synchronization(false), + asserted(false) + { } + + ~model_snapshot_members() { + for (unsigned int i = 0; i < bugs.size(); i++) + delete bugs[i]; + bugs.clear(); + } + ModelAction *current_action; unsigned int next_thread_id; modelclock_t used_sequence_numbers; @@ -44,6 +65,13 @@ struct model_snapshot_members { ModelAction *next_backtrack; std::vector< bug_message *, SnapshotAlloc > bugs; struct execution_stats stats; + bool failed_promise; + bool too_many_reads; + /** @brief Incorrectly-ordered synchronization was made */ + bool bad_synchronization; + bool asserted; + + SNAPSHOTALLOC }; /** @brief Constructor */ @@ -63,18 +91,11 @@ ModelChecker::ModelChecker(struct model_params params) : futurevalues(new std::vector< struct PendingFutureValue, SnapshotAlloc >()), pending_rel_seqs(new std::vector< struct release_seq *, SnapshotAlloc >()), thrd_last_action(new std::vector< ModelAction *, SnapshotAlloc >(1)), + thrd_last_fence_release(new std::vector< ModelAction *, SnapshotAlloc >()), node_stack(new NodeStack()), - mo_graph(new CycleGraph()), - failed_promise(false), - too_many_reads(false), - asserted(false), - bad_synchronization(false) + priv(new struct model_snapshot_members()), + mo_graph(new CycleGraph()) { - /* Allocate this "size" on the snapshotting heap */ - priv = (struct model_snapshot_members *)snapshot_calloc(1, sizeof(*priv)); - /* First thread created will have id INITIAL_THREAD_ID */ - priv->next_thread_id = INITIAL_THREAD_ID; - /* Initialize a model-checker thread, for special ModelActions */ model_thread = new Thread(get_next_id()); thread_map->put(id_to_int(model_thread->get_id()), model_thread); @@ -100,14 +121,11 @@ ModelChecker::~ModelChecker() delete pending_rel_seqs; delete thrd_last_action; + delete thrd_last_fence_release; delete node_stack; delete scheduler; delete mo_graph; - - for (unsigned int i = 0; i < priv->bugs.size(); i++) - delete priv->bugs[i]; - priv->bugs.clear(); - snapshot_free(priv); + delete priv; } static action_list_t * get_safe_ptr_action(HashTable * hash, void * ptr) { @@ -136,10 +154,6 @@ void ModelChecker::reset_to_initial_state() { DEBUG("+++ Resetting to initial state +++\n"); node_stack->reset_execution(); - failed_promise = false; - too_many_reads = false; - bad_synchronization = false; - reset_asserted(); /* Print all model-checker output before rollback */ fflush(model_out); @@ -160,7 +174,7 @@ unsigned int ModelChecker::get_num_threads() const } /** @return The currently executing Thread. */ -Thread * ModelChecker::get_current_thread() +Thread * ModelChecker::get_current_thread() const { return scheduler->get_current_thread(); } @@ -171,7 +185,8 @@ modelclock_t ModelChecker::get_next_seq_num() return ++priv->used_sequence_numbers; } -Node * ModelChecker::get_curr_node() { +Node * ModelChecker::get_curr_node() const +{ return node_stack->get_head(); } @@ -297,6 +312,23 @@ void ModelChecker::wake_up_sleeping_actions(ModelAction * curr) { } } +/** @brief Alert the model-checker that an incorrectly-ordered + * synchronization was made */ +void ModelChecker::set_bad_synchronization() +{ + priv->bad_synchronization = true; +} + +bool ModelChecker::has_asserted() const +{ + return priv->asserted; +} + +void ModelChecker::set_assert() +{ + priv->asserted = true; +} + /** * Check if we are in a deadlock. Should only be called at the end of an * execution, although it should not give false positives in the middle of an @@ -395,18 +427,21 @@ void ModelChecker::print_bugs() const void ModelChecker::record_stats() { stats.num_total++; - if (!isfinalfeasible()) + if (!isfeasibleprefix()) stats.num_infeasible++; else if (have_bug_reports()) stats.num_buggy_executions++; else if (is_complete_execution()) stats.num_complete++; + else + stats.num_redundant++; } /** @brief Print execution stats */ void ModelChecker::print_stats() const { model_print("Number of complete, bug-free executions: %d\n", stats.num_complete); + model_print("Number of redundant executions: %d\n", stats.num_redundant); model_print("Number of buggy executions: %d\n", stats.num_buggy_executions); model_print("Number of infeasible executions: %d\n", stats.num_infeasible); model_print("Total executions: %d\n", stats.num_total); @@ -414,39 +449,65 @@ void ModelChecker::print_stats() const } /** - * Queries the model-checker for more executions to explore and, if one - * exists, resets the model-checker state to execute a new execution. - * - * @return If there are more executions to explore, return true. Otherwise, - * return false. + * @brief End-of-exeuction print + * @param printbugs Should any existing bugs be printed? */ -bool ModelChecker::next_execution() +void ModelChecker::print_execution(bool printbugs) const { - DBG(); + print_program_output(); - if (isfinalfeasible() && (is_complete_execution() || have_bug_reports())) { + if (DBG_ENABLED() || params.verbose) { model_print("Earliest divergence point since last feasible execution:\n"); if (earliest_diverge) earliest_diverge->print(); else model_print("(Not set)\n"); - earliest_diverge = NULL; + model_print("\n"); + print_stats(); + } + /* Don't print invalid bugs */ + if (printbugs) + print_bugs(); + + model_print("\n"); + print_summary(); +} + +/** + * Queries the model-checker for more executions to explore and, if one + * exists, resets the model-checker state to execute a new execution. + * + * @return If there are more executions to explore, return true. Otherwise, + * return false. + */ +bool ModelChecker::next_execution() +{ + DBG(); + /* Is this execution a feasible execution that's worth bug-checking? */ + bool complete = isfeasibleprefix() && (is_complete_execution() || + have_bug_reports()); + + /* End-of-execution bug checks */ + if (complete) { if (is_deadlocked()) assert_bug("Deadlock detected"); checkDataRaces(); - print_bugs(); - model_print("\n"); - print_summary(); - } else if (DBG_ENABLED()) { - model_print("\n"); - print_summary(); } record_stats(); + /* Output */ + if (DBG_ENABLED() || params.verbose || have_bug_reports()) + print_execution(complete); + else + clear_program_output(); + + if (complete) + earliest_diverge = NULL; + if ((diverge = get_next_backtrack()) == NULL) return false; @@ -462,9 +523,13 @@ bool ModelChecker::next_execution() ModelAction * ModelChecker::get_last_conflict(ModelAction *act) { switch (act->get_type()) { + case ATOMIC_FENCE: case ATOMIC_READ: case ATOMIC_WRITE: case ATOMIC_RMW: { + /* Optimization: relaxed operations don't need backtracking */ + if (act->is_relaxed()) + return 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; @@ -634,9 +699,9 @@ bool ModelChecker::process_read(ModelAction *curr, bool second_part_of_rmw) } - if (!second_part_of_rmw&&!isfeasible()&&(curr->get_node()->increment_read_from()||curr->get_node()->increment_future_value())) { + if (!second_part_of_rmw&&is_infeasible()&&(curr->get_node()->increment_read_from()||curr->get_node()->increment_future_value())) { mo_graph->rollbackChanges(); - too_many_reads = false; + priv->too_many_reads = false; continue; } @@ -939,7 +1004,7 @@ bool ModelChecker::initialize_curr_action(ModelAction **curr) (*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_array()); if (newcurr) { /* First restore type and order in case of RMW operation */ if ((*curr)->is_rmwr()) @@ -961,6 +1026,10 @@ bool ModelChecker::initialize_curr_action(ModelAction **curr) /* Always compute new clock vector */ newcurr->create_cv(get_parent_action(newcurr->get_tid())); + + /* Assign most recent release fence */ + newcurr->set_last_fence_release(get_last_fence_release(newcurr->get_tid())); + /* * Perform one-time actions when pushing new ModelAction onto * NodeStack @@ -1149,48 +1218,67 @@ bool ModelChecker::promises_expired() const return false; } -/** @return whether the current partial trace must be a prefix of a - * feasible trace. */ +/** + * This is the strongest feasibility check available. + * @return whether the current trace (partial or complete) must be a prefix of + * a feasible trace. + */ bool ModelChecker::isfeasibleprefix() const { - return promises->size() == 0 && pending_rel_seqs->size() == 0 && isfeasible(); + return pending_rel_seqs->size() == 0 && is_feasible_prefix_ignore_relseq(); +} + +/** + * Returns whether the current completed trace is feasible, except for pending + * release sequences. + */ +bool ModelChecker::is_feasible_prefix_ignore_relseq() const +{ + if (DBG_ENABLED() && promises->size() != 0) + DEBUG("Infeasible: unrevolved promises\n"); + + return !is_infeasible() && promises->size() == 0; } -/** @return whether the current partial trace is feasible. */ -bool ModelChecker::isfeasible() const +/** + * Check if the current partial trace is infeasible. Does not check any + * end-of-execution flags, which might rule out the execution. Thus, this is + * useful only for ruling an execution as infeasible. + * @return whether the current partial trace is infeasible. + */ +bool ModelChecker::is_infeasible() const { if (DBG_ENABLED() && mo_graph->checkForRMWViolation()) DEBUG("Infeasible: RMW violation\n"); - return !mo_graph->checkForRMWViolation() && isfeasibleotherthanRMW(); + return mo_graph->checkForRMWViolation() || is_infeasible_ignoreRMW(); } -/** @return whether the current partial trace is feasible other than - * multiple RMW reading from the same store. */ -bool ModelChecker::isfeasibleotherthanRMW() const +/** + * Check If the current partial trace is infeasible, while ignoring + * infeasibility related to 2 RMW's reading from the same store. It does not + * check end-of-execution feasibility. + * @see ModelChecker::is_infeasible + * @return whether the current partial trace is infeasible, ignoring multiple + * RMWs reading from the same store. + * */ +bool ModelChecker::is_infeasible_ignoreRMW() const { if (DBG_ENABLED()) { if (mo_graph->checkForCycles()) DEBUG("Infeasible: modification order cycles\n"); - if (failed_promise) + if (priv->failed_promise) DEBUG("Infeasible: failed promise\n"); - if (too_many_reads) + if (priv->too_many_reads) DEBUG("Infeasible: too many reads\n"); - if (bad_synchronization) + if (priv->bad_synchronization) DEBUG("Infeasible: bad synchronization ordering\n"); if (promises_expired()) DEBUG("Infeasible: promises expired\n"); } - return !mo_graph->checkForCycles() && !failed_promise && !too_many_reads && !bad_synchronization && !promises_expired(); -} - -/** Returns whether the current completed trace is feasible. */ -bool ModelChecker::isfinalfeasible() const -{ - if (DBG_ENABLED() && promises->size() != 0) - DEBUG("Infeasible: unrevolved promises\n"); - - return isfeasible() && promises->size() == 0; + return mo_graph->checkForCycles() || priv->failed_promise || + priv->too_many_reads || priv->bad_synchronization || + promises_expired(); } /** Close out a RMWR by converting previous RMWR into a RMW or READ. */ @@ -1222,7 +1310,7 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) { return; //Must make sure that execution is currently feasible... We could //accidentally clear by rolling back - if (!isfeasible()) + if (is_infeasible()) return; std::vector *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, curr->get_location()); int tid = id_to_int(curr->get_tid()); @@ -1265,7 +1353,7 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) { /* Test to see whether this is a feasible write to read from*/ mo_graph->startChanges(); r_modification_order(curr, write); - bool feasiblereadfrom = isfeasible(); + bool feasiblereadfrom = !is_infeasible(); mo_graph->rollbackChanges(); if (!feasiblereadfrom) @@ -1290,7 +1378,7 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) { } } if (feasiblewrite) { - too_many_reads = true; + priv->too_many_reads = true; return; } } @@ -1321,14 +1409,48 @@ bool ModelChecker::r_modification_order(ModelAction *curr, const ModelAction *rf bool added = false; ASSERT(curr->is_read()); + /* Last SC fence in the current thread */ + ModelAction *last_sc_fence_local = get_last_seq_cst_fence(curr->get_tid(), NULL); + /* Iterate over all threads */ for (i = 0; i < thrd_lists->size(); i++) { + /* Last SC fence in thread i */ + ModelAction *last_sc_fence_thread_local = NULL; + if (int_to_id((int)i) != curr->get_tid()) + last_sc_fence_thread_local = get_last_seq_cst_fence(int_to_id(i), NULL); + + /* Last SC fence in thread i, before last SC fence in current thread */ + ModelAction *last_sc_fence_thread_before = NULL; + if (last_sc_fence_local) + last_sc_fence_thread_before = get_last_seq_cst_fence(int_to_id(i), last_sc_fence_local); + /* 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 (act->is_write() && act != rf && act != curr) { + /* C++, Section 29.3 statement 5 */ + if (curr->is_seqcst() && last_sc_fence_thread_local && + *act < *last_sc_fence_thread_local) { + mo_graph->addEdge(act, rf); + added = true; + } + /* C++, Section 29.3 statement 4 */ + else if (act->is_seqcst() && last_sc_fence_local && + *act < *last_sc_fence_local) { + mo_graph->addEdge(act, rf); + added = true; + } + /* C++, Section 29.3 statement 6 */ + else if (last_sc_fence_thread_before && + *act < *last_sc_fence_thread_before) { + mo_graph->addEdge(act, rf); + added = true; + } + } + /* * Include at most one act per-thread that "happens * before" curr. Don't consider reflexively. @@ -1454,15 +1576,23 @@ bool ModelChecker::w_modification_order(ModelAction *curr) if (curr->is_seqcst()) { /* We have to at least see the last sequentially consistent write, so we are initialized. */ - ModelAction *last_seq_cst = get_last_seq_cst(curr); + ModelAction *last_seq_cst = get_last_seq_cst_write(curr); if (last_seq_cst != NULL) { mo_graph->addEdge(last_seq_cst, curr); added = true; } } + /* Last SC fence in the current thread */ + ModelAction *last_sc_fence_local = get_last_seq_cst_fence(curr->get_tid(), NULL); + /* Iterate over all threads */ for (i = 0; i < thrd_lists->size(); i++) { + /* Last SC fence in thread i, before last SC fence in current thread */ + ModelAction *last_sc_fence_thread_before = NULL; + if (last_sc_fence_local && int_to_id((int)i) != curr->get_tid()) + last_sc_fence_thread_before = get_last_seq_cst_fence(int_to_id(i), last_sc_fence_local); + /* Iterate over actions in thread, starting from most recent */ action_list_t *list = &(*thrd_lists)[i]; action_list_t::reverse_iterator rit; @@ -1489,6 +1619,13 @@ bool ModelChecker::w_modification_order(ModelAction *curr) continue; } + /* C++, Section 29.3 statement 7 */ + if (last_sc_fence_thread_before && act->is_write() && + *act < *last_sc_fence_thread_before) { + mo_graph->addEdge(act, curr); + added = true; + } + /* * Include at most one act per-thread that "happens * before" curr @@ -1525,8 +1662,8 @@ bool ModelChecker::w_modification_order(ModelAction *curr) */ if (thin_air_constraint_may_allow(curr, act)) { - if (isfeasible() || - (curr->is_rmw() && act->is_rmw() && curr->get_reads_from() == act->get_reads_from() && isfeasibleotherthanRMW())) { + if (!is_infeasible() || + (curr->is_rmw() && act->is_rmw() && curr->get_reads_from() == act->get_reads_from() && !is_infeasible_ignoreRMW())) { struct PendingFutureValue pfv = {curr,act}; futurevalues->push_back(pfv); } @@ -1867,6 +2004,12 @@ void ModelChecker::add_action_to_lists(ModelAction *act) thrd_last_action->resize(get_num_threads()); (*thrd_last_action)[tid] = act; + if (act->is_fence() && act->is_release()) { + if ((int)thrd_last_fence_release->size() <= tid) + thrd_last_fence_release->resize(get_num_threads()); + (*thrd_last_fence_release)[tid] = act; + } + if (act->is_wait()) { void *mutex_loc=(void *) act->get_value(); get_safe_ptr_action(obj_map, mutex_loc)->push_back(act); @@ -1875,10 +2018,6 @@ void ModelChecker::add_action_to_lists(ModelAction *act) 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; } } @@ -1896,6 +2035,20 @@ ModelAction * ModelChecker::get_last_action(thread_id_t tid) const return NULL; } +/** + * @brief Get the last fence release performed by a particular Thread + * @param tid The thread ID of the Thread in question + * @return The last fence release in the thread, if one exists; NULL otherwise + */ +ModelAction * ModelChecker::get_last_fence_release(thread_id_t tid) const +{ + int threadid = id_to_int(tid); + if (threadid < (int)thrd_last_fence_release->size()) + return (*thrd_last_fence_release)[id_to_int(tid)]; + else + return NULL; +} + /** * Gets the last memory_order_seq_cst write (in the total global sequence) * performed on a particular object (i.e., memory location), not including the @@ -1904,7 +2057,7 @@ ModelAction * ModelChecker::get_last_action(thread_id_t tid) const * check * @return The last seq_cst write */ -ModelAction * ModelChecker::get_last_seq_cst(ModelAction *curr) const +ModelAction * ModelChecker::get_last_seq_cst_write(ModelAction *curr) const { void *location = curr->get_location(); action_list_t *list = get_safe_ptr_action(obj_map, location); @@ -1916,6 +2069,35 @@ ModelAction * ModelChecker::get_last_seq_cst(ModelAction *curr) const return NULL; } +/** + * Gets the last memory_order_seq_cst fence (in the total global sequence) + * performed in a particular thread, prior to a particular fence. + * @param tid The ID of the thread to check + * @param before_fence The fence from which to begin the search; if NULL, then + * search for the most recent fence in the thread. + * @return The last prior seq_cst fence in the thread, if exists; otherwise, NULL + */ +ModelAction * ModelChecker::get_last_seq_cst_fence(thread_id_t tid, const ModelAction *before_fence) const +{ + /* All fences should have NULL location */ + action_list_t *list = get_safe_ptr_action(obj_map, NULL); + action_list_t::reverse_iterator rit = list->rbegin(); + + if (before_fence) { + for (; rit != list->rend(); rit++) + if (*rit == before_fence) + break; + + ASSERT(*rit == before_fence); + rit++; + } + + for (; rit != list->rend(); rit++) + if ((*rit)->is_fence() && (tid == (*rit)->get_tid()) && (*rit)->is_seqcst()) + return *rit; + return NULL; +} + /** * Gets the last unlock operation performed on a particular mutex (i.e., memory * location). This function identifies the mutex according to the current @@ -1936,7 +2118,7 @@ ModelAction * ModelChecker::get_last_unlock(ModelAction *curr) const return NULL; } -ModelAction * ModelChecker::get_parent_action(thread_id_t tid) +ModelAction * ModelChecker::get_parent_action(thread_id_t tid) const { ModelAction *parent = get_last_action(tid); if (!parent) @@ -1949,7 +2131,7 @@ ModelAction * ModelChecker::get_parent_action(thread_id_t tid) * @param tid The thread whose clock vector we want * @return Desired clock vector */ -ClockVector * ModelChecker::get_cv(thread_id_t tid) +ClockVector * ModelChecker::get_cv(thread_id_t tid) const { return get_parent_action(tid)->get_cv(); } @@ -2032,7 +2214,7 @@ void ModelChecker::check_promises(thread_id_t tid, ClockVector *old_cv, ClockVec merge_cv->synchronized_since(act)) { if (promise->increment_threads(tid)) { //Promise has failed - failed_promise = true; + priv->failed_promise = true; return; } } @@ -2043,7 +2225,7 @@ 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; + priv->failed_promise = true; return; } } @@ -2094,12 +2276,12 @@ void ModelChecker::mo_check_promises(thread_id_t tid, const ModelAction *write) promise->set_write(write); //The pwrite cannot happen before the promise if (write->happens_before(act) && (write != act)) { - failed_promise = true; + priv->failed_promise = true; return; } } if (mo_graph->checkPromise(write, promise)) { - failed_promise = true; + priv->failed_promise = true; return; } } @@ -2110,7 +2292,7 @@ void ModelChecker::mo_check_promises(thread_id_t tid, const ModelAction *write) if (promise->get_write()&&mo_graph->checkReachable(promise->get_write(), write)) { if (promise->increment_threads(tid)) { - failed_promise = true; + priv->failed_promise = true; return; } } @@ -2153,16 +2335,16 @@ void ModelChecker::build_reads_from_past(ModelAction *curr) unsigned int i; ASSERT(curr->is_read()); - ModelAction *last_seq_cst = NULL; + ModelAction *last_sc_write = NULL; /* Track whether this object has been initialized */ bool initialized = false; if (curr->is_seqcst()) { - last_seq_cst = get_last_seq_cst(curr); + last_sc_write = get_last_seq_cst_write(curr); /* We have to at least see the last sequentially consistent write, so we are initialized. */ - if (last_seq_cst != NULL) + if (last_sc_write != NULL) initialized = true; } @@ -2179,15 +2361,20 @@ void ModelChecker::build_reads_from_past(ModelAction *curr) continue; /* Don't consider more than one seq_cst write if we are a seq_cst read. */ - if (!curr->is_seqcst() || (!act->is_seqcst() && (last_seq_cst == NULL || !act->happens_before(last_seq_cst))) || act == last_seq_cst) { - if (!curr->get_sleep_flag() || curr->is_seqcst() || sleep_can_read_from(curr, act)) { - DEBUG("Adding action to may_read_from:\n"); - if (DBG_ENABLED()) { - act->print(); - curr->print(); - } - curr->get_node()->add_read_from(act); + bool allow_read = true; + + if (curr->is_seqcst() && (act->is_seqcst() || (last_sc_write != NULL && act->happens_before(last_sc_write))) && act != last_sc_write) + allow_read = false; + else if (curr->get_sleep_flag() && !curr->is_seqcst() && !sleep_can_read_from(curr, act)) + allow_read = false; + + if (allow_read) { + DEBUG("Adding action to may_read_from:\n"); + if (DBG_ENABLED()) { + act->print(); + curr->print(); } + curr->get_node()->add_read_from(act); } /* Include at most one act per-thread that "happens before" curr */ @@ -2272,7 +2459,8 @@ void ModelChecker::dumpGraph(char *filename) { } #endif -void ModelChecker::print_summary() +/** @brief Prints an execution trace summary. */ +void ModelChecker::print_summary() const { #if SUPPORT_MOD_ORDER_DUMP scheduler->print(); @@ -2283,7 +2471,7 @@ void ModelChecker::print_summary() dumpGraph(buffername); #endif - if (!isfinalfeasible()) + if (!isfeasibleprefix()) model_print("INFEASIBLE EXECUTION!\n"); print_list(action_trace, stats.num_total); model_print("\n"); @@ -2394,7 +2582,7 @@ bool ModelChecker::take_step() { Thread *next = scheduler->next_thread(priv->nextThread); /* Infeasible -> don't take any more steps */ - if (!isfeasible()) + if (is_infeasible()) return false; else if (isfeasibleprefix() && have_bug_reports()) { set_assert(); @@ -2420,7 +2608,7 @@ bool ModelChecker::take_step() { * (4) no pending promises */ if (!pending_rel_seqs->empty() && (!next || next->is_model_thread()) && - isfinalfeasible() && !unrealizedraces.empty()) { + 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, @@ -2448,9 +2636,24 @@ bool ModelChecker::take_step() { return (Thread::swap(&system_context, next) == 0); } -/** Runs the current execution until threre are no more steps to take. */ -void ModelChecker::finish_execution() { - DBG(); +/** Wrapper to run the user's main function, with appropriate arguments */ +void user_main_wrapper(void *) +{ + user_main(model->params.argc, model->params.argv); +} + +/** @brief Run ModelChecker for the user program */ +void ModelChecker::run() +{ + do { + thrd_t user_thread; + + /* Start user program */ + add_thread(new Thread(&user_thread, &user_main_wrapper, NULL)); + + /* Wait for all threads to complete */ + while (take_step()); + } while (next_execution()); - while (take_step()); + print_stats(); }