X-Git-Url: http://plrg.eecs.uci.edu/git/?p=c11tester.git;a=blobdiff_plain;f=model.cc;h=b0ca05ea3c9009734d1196fad234b2aceaa8b001;hp=9ebc85344cd8c4343349a4d7df21e9d034915edc;hb=745b71256a4b96ddf4843c7f66b11d0cb3daa3cb;hpb=e0f80c403452e544452f64687b3c489a869e4f77 diff --git a/model.cc b/model.cc index 9ebc8534..b0ca05ea 100644 --- a/model.cc +++ b/model.cc @@ -483,10 +483,16 @@ void ModelChecker::record_stats() stats.num_buggy_executions++; else if (is_complete_execution()) stats.num_complete++; - else if (scheduler->all_threads_sleeping()) + else { stats.num_redundant++; - else - ASSERT(false); + + /** + * @todo We can violate this ASSERT() when fairness/sleep sets + * conflict to cause an execution to terminate, e.g. with: + * Scheduler: [0: disabled][1: disabled][2: sleep][3: current, enabled] + */ + //ASSERT(scheduler->all_threads_sleeping()); + } } /** @brief Print execution stats */ @@ -848,17 +854,19 @@ bool ModelChecker::process_read(ModelAction *curr) { Node *node = curr->get_node(); uint64_t value = VALUE_NONE; - bool updated = false; while (true) { + bool updated = false; switch (node->get_read_from_status()) { case READ_FROM_PAST: { const ModelAction *rf = node->get_read_from_past(); ASSERT(rf); mo_graph->startChanges(); - value = rf->get_value(); - check_recency(curr, rf); - bool r_status = r_modification_order(curr, rf); + + ASSERT(!is_infeasible()); + if (!check_recency(curr, rf)) + priv->too_many_reads = true; + updated = r_modification_order(curr, rf); if (is_infeasible() && node->increment_read_from()) { mo_graph->rollbackChanges(); @@ -866,11 +874,11 @@ bool ModelChecker::process_read(ModelAction *curr) continue; } + value = rf->get_value(); read_from(curr, rf); mo_graph->commitChanges(); mo_check_promises(curr, true); - updated |= r_status; break; } case READ_FROM_PROMISE: { @@ -1022,7 +1030,7 @@ void ModelChecker::add_future_value(const ModelAction *writer, ModelAction *read write_thread = write_thread->get_parent(); struct future_value fv = { - writer->get_value(), + writer->get_write_value(), writer->get_seq_number() + params.maxfuturedelay, write_thread->get_id(), }; @@ -1038,8 +1046,26 @@ void ModelChecker::add_future_value(const ModelAction *writer, ModelAction *read */ bool ModelChecker::process_write(ModelAction *curr) { - bool updated_mod_order = w_modification_order(curr); - bool updated_promises = resolve_promises(curr); + /* Readers to which we may send our future value */ + std::vector< ModelAction *, ModelAlloc > send_fv; + + bool updated_mod_order = w_modification_order(curr, &send_fv); + int promise_idx = get_promise_to_resolve(curr); + const ModelAction *earliest_promise_reader; + bool updated_promises = false; + + if (promise_idx >= 0) { + earliest_promise_reader = (*promises)[promise_idx]->get_reader(0); + updated_promises = resolve_promise(curr, promise_idx); + } else + earliest_promise_reader = NULL; + + /* Don't send future values to reads after the Promise we resolve */ + for (unsigned int i = 0; i < send_fv.size(); i++) { + ModelAction *read = send_fv[i]; + if (!earliest_promise_reader || *read < *earliest_promise_reader) + futurevalues->push_back(PendingFutureValue(curr, read)); + } if (promises->size() == 0) { for (unsigned int i = 0; i < futurevalues->size(); i++) { @@ -1324,8 +1350,9 @@ bool ModelChecker::initialize_curr_action(ModelAction **curr) */ bool ModelChecker::read_from(ModelAction *act, const ModelAction *rf) { + ASSERT(rf); act->set_read_from(rf); - if (rf != NULL && act->is_acquire()) { + if (act->is_acquire()) { rel_heads_list_t release_heads; get_release_seq_heads(act, act, &release_heads); int num_heads = release_heads.size(); @@ -1495,7 +1522,7 @@ ModelAction * ModelChecker::check_current_action(ModelAction *curr) } } if (act->is_write()) { - if (w_modification_order(act)) + if (w_modification_order(act, NULL)) updated = true; } mo_graph->commitChanges(); @@ -1615,6 +1642,33 @@ ModelAction * ModelChecker::process_rmw(ModelAction *act) { return lastread; } +template +bool ModelChecker::should_read_instead(const ModelAction *curr, const ModelAction *rf, const T *other_rf) const +{ + /* Need a different write/promise */ + if (other_rf->equals(rf)) + return false; + + /* Only look for "newer" writes/promises */ + if (!mo_graph->checkReachable(rf, other_rf)) + return false; + + std::vector *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, curr->get_location()); + action_list_t *list = &(*thrd_lists)[id_to_int(curr->get_tid())]; + action_list_t::reverse_iterator rit = list->rbegin(); + ASSERT((*rit) == curr); + /* Skip past curr */ + rit++; + + /* Does this write/promise work for everyone? */ + for (int i = 0; i < params.maxreads; i++, rit++) { + ModelAction *act = *rit; + if (!act->may_read_from(other_rf)) + return false; + } + return true; +} + /** * Checks whether a thread has read from the same write for too many times * without seeing the effects of a later write. @@ -1625,83 +1679,57 @@ ModelAction * ModelChecker::process_rmw(ModelAction *act) { * 3) that other write must have been in the reads_from set for maxreads times. * * If so, we decide that the execution is no longer feasible. + * + * @param curr The current action. Must be a read. + * @param rf The store from which we might read. + * @return True if the read should succeed; false otherwise */ -void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) +bool ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) const { - if (params.maxreads != 0) { - 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 - 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()); - - /* Skip checks */ - if ((int)thrd_lists->size() <= tid) - return; - action_list_t *list = &(*thrd_lists)[tid]; - - action_list_t::reverse_iterator rit = list->rbegin(); - /* Skip past curr */ - for (; (*rit) != curr; rit++) - ; - /* go past curr now */ - rit++; - - action_list_t::reverse_iterator ritcopy = rit; - //See if we have enough reads from the same value - int count = 0; - for (; count < params.maxreads; rit++, count++) { - if (rit == list->rend()) - return; - ModelAction *act = *rit; - if (!act->is_read()) - return; - - if (act->get_reads_from() != rf) - return; - if (act->get_node()->get_read_from_past_size() <= 1) - return; - } - 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_past(i); - - /* Need a different write */ - if (write == rf) - continue; - - /* Test to see whether this is a feasible write to read from */ - /** NOTE: all members of read-from set should be - * feasible, so we no longer check it here **/ - - rit = ritcopy; + if (!params.maxreads) + return true; - bool feasiblewrite = true; - //new we need to see if this write works for everyone + //NOTE: Next check is just optimization, not really necessary.... + if (curr->get_node()->get_read_from_past_size() + + curr->get_node()->get_read_from_promise_size() <= 1) + return true; - 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_past_size(); j++) { - if (act->get_node()->get_read_from_past(j) == write) { - foundvalue = true; - break; - } - } - if (!foundvalue) { - feasiblewrite = false; - break; - } - } - if (feasiblewrite) { - priv->too_many_reads = true; - return; - } - } + std::vector *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, curr->get_location()); + int tid = id_to_int(curr->get_tid()); + ASSERT(tid < (int)thrd_lists->size()); + action_list_t *list = &(*thrd_lists)[tid]; + action_list_t::reverse_iterator rit = list->rbegin(); + ASSERT((*rit) == curr); + /* Skip past curr */ + rit++; + + action_list_t::reverse_iterator ritcopy = rit; + /* See if we have enough reads from the same value */ + for (int count = 0; count < params.maxreads; ritcopy++, count++) { + if (ritcopy == list->rend()) + return true; + ModelAction *act = *ritcopy; + if (!act->is_read()) + return true; + if (act->get_reads_from_promise() && !act->get_reads_from_promise()->equals(rf)) + return true; + if (act->get_reads_from() && !act->get_reads_from()->equals(rf)) + return true; + if (act->get_node()->get_read_from_past_size() + + act->get_node()->get_read_from_promise_size() <= 1) + return true; } + for (int i = 0; i < curr->get_node()->get_read_from_past_size(); i++) { + const ModelAction *write = curr->get_node()->get_read_from_past(i); + if (should_read_instead(curr, rf, write)) + return false; /* liveness failure */ + } + for (int i = 0; i < curr->get_node()->get_read_from_promise_size(); i++) { + const Promise *promise = curr->get_node()->get_read_from_promise(i); + if (should_read_instead(curr, rf, promise)) + return false; /* liveness failure */ + } + return true; } /** @@ -1826,9 +1854,11 @@ bool ModelChecker::r_modification_order(ModelAction *curr, const rf_type *rf) * (II) Sending the write back to non-synchronizing reads. * * @param curr The current action. Must be a write. + * @param send_fv A vector for stashing reads to which we may pass our future + * value. If NULL, then don't record any future values. * @return True if modification order edges were added; false otherwise */ -bool ModelChecker::w_modification_order(ModelAction *curr) +bool ModelChecker::w_modification_order(ModelAction *curr, std::vector< ModelAction *, ModelAlloc > *send_fv) { std::vector *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, curr->get_location()); unsigned int i; @@ -1921,9 +1951,9 @@ bool ModelChecker::w_modification_order(ModelAction *curr) pendingfuturevalue. */ - if (thin_air_constraint_may_allow(curr, act)) { + if (send_fv && thin_air_constraint_may_allow(curr, act)) { if (!is_infeasible()) - futurevalues->push_back(PendingFutureValue(curr, act)); + send_fv->push_back(act); else if (curr->is_rmw() && act->is_rmw() && curr->get_reads_from() && curr->get_reads_from() == act->get_reads_from()) add_future_value(curr, act); } @@ -2445,44 +2475,49 @@ ClockVector * ModelChecker::get_cv(thread_id_t tid) const } /** - * Resolve a set of Promises with a current write. The set is provided in the - * Node corresponding to @a write. + * @brief Find the promise, if any to resolve for the current action + * @param curr The current ModelAction. Should be a write. + * @return The (non-negative) index for the Promise to resolve, if any; + * otherwise -1 + */ +int ModelChecker::get_promise_to_resolve(const ModelAction *curr) const +{ + for (unsigned int i = 0; i < promises->size(); i++) + if (curr->get_node()->get_promise(i)) + return i; + return -1; +} + +/** + * Resolve a Promise with a current write. * @param write The ModelAction that is fulfilling Promises - * @return True if promises were resolved; false otherwise + * @param promise_idx The index corresponding to the promise + * @return True if the Promise was successfully resolved; false otherwise */ -bool ModelChecker::resolve_promises(ModelAction *write) +bool ModelChecker::resolve_promise(ModelAction *write, unsigned int promise_idx) { - bool haveResolved = false; std::vector< ModelAction *, ModelAlloc > actions_to_check; - promise_list_t mustResolve, resolved; - - 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)) { - 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(promise, write, &mustResolve); - - resolved.push_back(promise); - promises->erase(promises->begin() + promise_index); + promise_list_t mustResolve; + Promise *promise = (*promises)[promise_idx]; - haveResolved = true; - } else - promise_index++; + for (unsigned int i = 0; i < promise->get_num_readers(); i++) { + ModelAction *read = promise->get_reader(i); + 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) && promise->same_value(write)); + mo_graph->resolvePromise(promise, write, &mustResolve); + + promises->erase(promises->begin() + promise_idx); + + /** @todo simplify the 'mustResolve' stuff */ + ASSERT(mustResolve.size() <= 1); + + if (!mustResolve.empty() && mustResolve[0] != promise) + priv->failed_promise = true; + delete promise; - for (unsigned int i = 0; i < mustResolve.size(); i++) { - if (std::find(resolved.begin(), resolved.end(), mustResolve[i]) - == resolved.end()) - priv->failed_promise = true; - } - for (unsigned int i = 0; i < resolved.size(); i++) - delete resolved[i]; //Check whether reading these writes has made threads unable to //resolve promises @@ -2491,7 +2526,7 @@ bool ModelChecker::resolve_promises(ModelAction *write) mo_check_promises(read, true); } - return haveResolved; + return true; } /** @@ -2504,7 +2539,7 @@ void ModelChecker::compute_promises(ModelAction *curr) { for (unsigned int i = 0; i < promises->size(); i++) { Promise *promise = (*promises)[i]; - if (!promise->is_compatible(curr) || promise->get_value() != curr->get_value()) + if (!promise->is_compatible(curr) || !promise->same_value(curr)) continue; bool satisfy = true; @@ -2575,7 +2610,7 @@ void ModelChecker::mo_check_promises(const ModelAction *act, bool is_read_check) Promise *promise = (*promises)[i]; // Is this promise on the same location? - if (promise->get_value() != write->get_value()) + if (!promise->same_location(write)) continue; for (unsigned int j = 0; j < promise->get_num_readers(); j++) {