X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=model.cc;h=d7b87f6359a94d3107421521f0f46ec5927ab58a;hb=05d91de8ac8098425d51e9af2704eb91e04a7f9b;hp=a9ea7b0493071eebac0b034e7d84d23f3cd21d8f;hpb=7966c737f2a88e5e1a56817eb9f6fdaead3eca92;p=model-checker.git diff --git a/model.cc b/model.cc index a9ea7b0..d7b87f6 100644 --- a/model.cc +++ b/model.cc @@ -1,6 +1,7 @@ #include #include #include +#include #include "model.h" #include "action.h" @@ -128,19 +129,21 @@ ModelChecker::~ModelChecker() delete priv; } -static action_list_t * get_safe_ptr_action(HashTable * hash, void * ptr) { - action_list_t * tmp=hash->get(ptr); - if (tmp==NULL) { - tmp=new action_list_t(); +static action_list_t * get_safe_ptr_action(HashTable * hash, void * ptr) +{ + action_list_t *tmp = hash->get(ptr); + if (tmp == NULL) { + tmp = new action_list_t(); hash->put(ptr, tmp); } return tmp; } -static std::vector * get_safe_ptr_vect_action(HashTable *, uintptr_t, 4> * hash, void * ptr) { - std::vector * tmp=hash->get(ptr); - if (tmp==NULL) { - tmp=new std::vector(); +static std::vector * get_safe_ptr_vect_action(HashTable *, uintptr_t, 4> * hash, void * ptr) +{ + std::vector *tmp = hash->get(ptr); + if (tmp == NULL) { + tmp = new std::vector(); hash->put(ptr, tmp); } return tmp; @@ -207,7 +210,7 @@ Thread * ModelChecker::get_next_thread(ModelAction *curr) { thread_id_t tid; - if (curr!=NULL) { + if (curr != NULL) { /* Do not split atomic actions. */ if (curr->is_rmwr()) return thread_current(); @@ -282,12 +285,12 @@ Thread * ModelChecker::get_next_thread(ModelAction *curr) * the corresponding thread object's pending action. */ -void ModelChecker::execute_sleep_set() { - for(unsigned int i=0;iget_enabled(thr) == THREAD_SLEEP_SET && - thr->get_pending() == NULL ) { +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->is_sleep_set(thr) && thr->get_pending() == NULL) { thr->set_state(THREAD_RUNNING); scheduler->next_thread(thr); Thread::swap(&system_context, thr); @@ -298,16 +301,15 @@ void ModelChecker::execute_sleep_set() { priv->current_action = NULL; } -void ModelChecker::wake_up_sleeping_actions(ModelAction * curr) { - for(unsigned int i=0;iget_enabled(thr) == THREAD_SLEEP_SET ) { - ModelAction *pending_act=thr->get_pending(); - if ((!curr->is_rmwr())&&pending_act->could_synchronize_with(curr)) { +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 scheduler->remove_sleep(thr); - } } } } @@ -603,11 +605,11 @@ ModelAction * ModelChecker::get_last_conflict(ModelAction *act) void ModelChecker::set_backtracking(ModelAction *act) { Thread *t = get_thread(act); - ModelAction * prev = get_last_conflict(act); + ModelAction *prev = get_last_conflict(act); if (prev == NULL) return; - Node * node = prev->get_node()->get_parent(); + Node *node = prev->get_node()->get_parent(); int low_tid, high_tid; if (node->is_enabled(t)) { @@ -714,7 +716,7 @@ bool ModelChecker::process_read(ModelAction *curr, bool second_part_of_rmw) /* Read from future value */ value = curr->get_node()->get_future_value(); modelclock_t expiration = curr->get_node()->get_future_value_expiration(); - read_from(curr, NULL); + curr->set_read_from(NULL); Promise *valuepromise = new Promise(curr, value, expiration); promises->push_back(valuepromise); } @@ -858,6 +860,56 @@ bool ModelChecker::process_write(ModelAction *curr) return updated_mod_order || updated_promises; } +/** + * Process a fence ModelAction + * @param curr The ModelAction to process + * @return True if synchronization was updated + */ +bool ModelChecker::process_fence(ModelAction *curr) +{ + /* + * fence-relaxed: no-op + * fence-release: only log the occurence (not in this function), for + * use in later synchronization + * fence-acquire (this function): search for hypothetical release + * sequences + */ + bool updated = false; + if (curr->is_acquire()) { + action_list_t *list = action_trace; + action_list_t::reverse_iterator rit; + /* Find X : is_read(X) && X --sb-> curr */ + for (rit = list->rbegin(); rit != list->rend(); rit++) { + ModelAction *act = *rit; + if (act == curr) + continue; + if (act->get_tid() != curr->get_tid()) + continue; + /* Stop at the beginning of the thread */ + if (act->is_thread_start()) + break; + /* Stop once we reach a prior fence-acquire */ + if (act->is_fence() && act->is_acquire()) + break; + if (!act->is_read()) + continue; + /* read-acquire will find its own release sequences */ + if (act->is_acquire()) + continue; + + /* Establish hypothetical release sequences */ + rel_heads_list_t release_heads; + get_release_seq_heads(curr, act, &release_heads); + for (unsigned int i = 0; i < release_heads.size(); i++) + if (!curr->synchronize_with(release_heads[i])) + set_bad_synchronization(); + if (release_heads.size() != 0) + updated = true; + } + } + return updated; +} + /** * @brief Process the current action for thread-related activity * @@ -1063,7 +1115,7 @@ bool ModelChecker::read_from(ModelAction *act, const ModelAction *rf) act->set_read_from(rf); if (rf != NULL && act->is_acquire()) { rel_heads_list_t release_heads; - get_release_seq_heads(act, &release_heads); + get_release_seq_heads(act, act, &release_heads); int num_heads = release_heads.size(); for (unsigned int i = 0; i < release_heads.size(); i++) if (!act->synchronize_with(release_heads[i])) { @@ -1087,8 +1139,8 @@ bool ModelChecker::read_from(ModelAction *act, const ModelAction *rf) */ 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(); + std::mutex *lock = (std::mutex *)curr->get_location(); + struct std::mutex_state *state = lock->get_state(); if (state->islocked) { //Stick the action in the appropriate waiting queue get_safe_ptr_action(lock_waiters_map, curr->get_location())->push_back(curr); @@ -1173,6 +1225,9 @@ Thread * ModelChecker::check_current_action(ModelAction *curr) if (act->is_write() && process_write(act)) update = true; + if (act->is_fence() && process_fence(act)) + update_all = true; + if (act->is_mutex_op() && process_mutex(act)) update_all = true; @@ -1219,7 +1274,8 @@ Thread * ModelChecker::check_current_action(ModelAction *curr) return get_next_thread(curr); } -void ModelChecker::check_curr_backtracking(ModelAction * curr) { +void ModelChecker::check_curr_backtracking(ModelAction *curr) +{ Node *currnode = curr->get_node(); Node *parnode = currnode->get_parent(); @@ -1237,11 +1293,10 @@ void ModelChecker::check_curr_backtracking(ModelAction * curr) { bool ModelChecker::promises_expired() const { - for (unsigned int promise_index = 0; promise_index < promises->size(); promise_index++) { - Promise *promise = (*promises)[promise_index]; - if (promise->get_expiration()used_sequence_numbers) { + for (unsigned int i = 0; i < promises->size(); i++) { + Promise *promise = (*promises)[i]; + if (promise->get_expiration() < priv->used_sequence_numbers) return true; - } } return false; } @@ -1331,9 +1386,9 @@ ModelAction * ModelChecker::process_rmw(ModelAction *act) { * * If so, we decide that the execution is no longer feasible. */ -void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) { +void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) +{ if (params.maxreads != 0) { - if (curr->get_node()->get_read_from_size() <= 1) return; //Must make sure that execution is currently feasible... We could @@ -1359,7 +1414,7 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) { //See if we have enough reads from the same value int count = 0; for (; count < params.maxreads; rit++,count++) { - if (rit==list->rend()) + if (rit == list->rend()) return; ModelAction *act = *rit; if (!act->is_read()) @@ -1370,12 +1425,12 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) { if (act->get_node()->get_read_from_size() <= 1) return; } - for (int i = 0; iget_node()->get_read_from_size(); i++) { - //Get write - const ModelAction * write = curr->get_node()->get_read_from_at(i); + for (int i = 0; i < curr->get_node()->get_read_from_size(); i++) { + /* Get write */ + const ModelAction *write = curr->get_node()->get_read_from_at(i); - //Need a different write - if (write==rf) + /* Need a different write */ + if (write == rf) continue; /* Test to see whether this is a feasible write to read from*/ @@ -1392,7 +1447,7 @@ void ModelChecker::check_recency(ModelAction *curr, const ModelAction *rf) { //new we need to see if this write works for everyone for (int loop = count; loop>0; loop--,rit++) { - ModelAction *act=*rit; + ModelAction *act = *rit; bool foundvalue = false; for (int j = 0; jget_node()->get_read_from_size(); j++) { if (act->get_node()->get_read_from_at(j)==write) { @@ -1464,18 +1519,21 @@ bool ModelChecker::r_modification_order(ModelAction *curr, const ModelAction *rf *act < *last_sc_fence_thread_local) { mo_graph->addEdge(act, rf); added = true; + break; } /* 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; + break; } /* 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; + break; } } @@ -1652,6 +1710,7 @@ bool ModelChecker::w_modification_order(ModelAction *curr) *act < *last_sc_fence_thread_before) { mo_graph->addEdge(act, curr); added = true; + break; } /* @@ -1706,7 +1765,8 @@ bool ModelChecker::w_modification_order(ModelAction *curr) /** 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::thin_air_constraint_may_allow(const ModelAction * writer, const ModelAction *reader) { +bool ModelChecker::thin_air_constraint_may_allow(const ModelAction *writer, const ModelAction *reader) +{ if (!writer->is_rmw()) return true; @@ -1935,18 +1995,25 @@ bool ModelChecker::release_seq_heads(const ModelAction *rf, * given ModelAction must synchronize. This function only returns a non-empty * result when it can locate a release sequence head with certainty. Otherwise, * it may mark the internal state of the ModelChecker so that it will handle - * the release sequence at a later time, causing @a act to update its + * the release sequence at a later time, causing @a acquire to update its * synchronization at some later point in execution. - * @param act The 'acquire' action that may read from a release sequence + * + * @param acquire The 'acquire' action that may synchronize with a release + * sequence + * @param read The read action that may read from a release sequence; this may + * be the same as acquire, or else an earlier action in the same thread (i.e., + * when 'acquire' is a fence-acquire) * @param release_heads A pass-by-reference return parameter. Will be filled * with the head(s) of the release sequence(s), if they exists with certainty. * @see ModelChecker::release_seq_heads */ -void ModelChecker::get_release_seq_heads(ModelAction *act, rel_heads_list_t *release_heads) +void ModelChecker::get_release_seq_heads(ModelAction *acquire, + ModelAction *read, rel_heads_list_t *release_heads) { - const ModelAction *rf = act->get_reads_from(); + const ModelAction *rf = read->get_reads_from(); struct release_seq *sequence = (struct release_seq *)snapshot_calloc(1, sizeof(struct release_seq)); - sequence->acquire = act; + sequence->acquire = acquire; + sequence->read = read; if (!release_seq_heads(rf, release_heads, sequence)) { /* add act to 'lazy checking' list */ @@ -1975,21 +2042,22 @@ bool ModelChecker::resolve_release_sequences(void *location, work_queue_t *work_ std::vector< struct release_seq *, SnapshotAlloc >::iterator it = pending_rel_seqs->begin(); while (it != pending_rel_seqs->end()) { struct release_seq *pending = *it; - ModelAction *act = pending->acquire; + ModelAction *acquire = pending->acquire; + const ModelAction *read = pending->read; /* Only resolve sequences on the given location, if provided */ - if (location && act->get_location() != location) { + if (location && read->get_location() != location) { it++; continue; } - const ModelAction *rf = act->get_reads_from(); + const ModelAction *rf = read->get_reads_from(); rel_heads_list_t release_heads; bool complete; complete = release_seq_heads(rf, &release_heads, pending); for (unsigned int i = 0; i < release_heads.size(); i++) { - if (!act->has_synchronized_with(release_heads[i])) { - if (act->synchronize_with(release_heads[i])) + if (!acquire->has_synchronized_with(release_heads[i])) { + if (acquire->synchronize_with(release_heads[i])) updated = true; else set_bad_synchronization(); @@ -1999,15 +2067,16 @@ bool ModelChecker::resolve_release_sequences(void *location, work_queue_t *work_ if (updated) { /* Re-check all pending release sequences */ work_queue->push_back(CheckRelSeqWorkEntry(NULL)); - /* Re-check act for mo_graph edges */ - work_queue->push_back(MOEdgeWorkEntry(act)); + /* Re-check read-acquire for mo_graph edges */ + if (acquire->is_read()) + work_queue->push_back(MOEdgeWorkEntry(acquire)); /* propagate synchronization to later actions */ action_list_t::reverse_iterator rit = action_trace->rbegin(); - for (; (*rit) != act; rit++) { + for (; (*rit) != acquire; rit++) { ModelAction *propagate = *rit; - if (act->happens_before(propagate)) { - propagate->synchronize_with(act); + if (acquire->happens_before(propagate)) { + propagate->synchronize_with(acquire); /* Re-check 'propagate' for mo_graph edges */ work_queue->push_back(MOEdgeWorkEntry(propagate)); } @@ -2037,18 +2106,32 @@ bool ModelChecker::resolve_release_sequences(void *location, work_queue_t *work_ void ModelChecker::add_action_to_lists(ModelAction *act) { int tid = id_to_int(act->get_tid()); - action_trace->push_back(act); + ModelAction *uninit = NULL; + int uninit_id = -1; + action_list_t *list = get_safe_ptr_action(obj_map, act->get_location()); + if (list->empty() && act->is_atomic_var()) { + uninit = new_uninitialized_action(act->get_location()); + uninit_id = id_to_int(uninit->get_tid()); + list->push_back(uninit); + } + list->push_back(act); - get_safe_ptr_action(obj_map, act->get_location())->push_back(act); + action_trace->push_back(act); + if (uninit) + action_trace->push_front(uninit); std::vector *vec = get_safe_ptr_vect_action(obj_thrd_map, act->get_location()); if (tid >= (int)vec->size()) vec->resize(priv->next_thread_id); (*vec)[tid].push_back(act); + if (uninit) + (*vec)[uninit_id].push_front(uninit); if ((int)thrd_last_action->size() <= tid) thrd_last_action->resize(get_num_threads()); (*thrd_last_action)[tid] = act; + if (uninit) + (*thrd_last_action)[uninit_id] = uninit; if (act->is_fence() && act->is_release()) { if ((int)thrd_last_fence_release->size() <= tid) @@ -2298,14 +2381,14 @@ void ModelChecker::check_promises_thread_disabled() { * cannot perform a future write that will resolve the promise due to * modificatin order constraints. * - * @parem tid The thread that either read from the model action + * @param tid The thread that either read from the model action * write, or actually did the model action write. * - * @parem write The ModelAction representing the relevant write. + * @param write The ModelAction representing the relevant write. */ - -void ModelChecker::mo_check_promises(thread_id_t tid, const ModelAction *write) { - void * location = write->get_location(); +void ModelChecker::mo_check_promises(thread_id_t tid, const ModelAction *write) +{ + void *location = write->get_location(); for (unsigned int i = 0; i < promises->size(); i++) { Promise *promise = (*promises)[i]; const ModelAction *act = promise->get_action(); @@ -2383,16 +2466,8 @@ void ModelChecker::build_reads_from_past(ModelAction *curr) ModelAction *last_sc_write = NULL; - /* Track whether this object has been initialized */ - bool initialized = false; - - if (curr->is_seqcst()) { + if (curr->is_seqcst()) 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_sc_write != NULL) - initialized = true; - } /* Iterate over all threads */ for (i = 0; i < thrd_lists->size(); i++) { @@ -2424,17 +2499,12 @@ void ModelChecker::build_reads_from_past(ModelAction *curr) } /* Include at most one act per-thread that "happens before" curr */ - if (act->happens_before(curr)) { - initialized = true; + if (act->happens_before(curr)) break; - } } } - if (!initialized) - assert_bug("May read from uninitialized atomic"); - - if (DBG_ENABLED() || !initialized) { + if (DBG_ENABLED()) { model_print("Reached read action:\n"); curr->print(); model_print("Printing may_read_from\n"); @@ -2443,22 +2513,39 @@ void ModelChecker::build_reads_from_past(ModelAction *curr) } } -bool ModelChecker::sleep_can_read_from(ModelAction * curr, const ModelAction *write) { - while(true) { - Node *prevnode=write->get_node()->get_parent(); +bool ModelChecker::sleep_can_read_from(ModelAction *curr, const ModelAction *write) +{ + while (true) { + /* UNINIT actions don't have a Node, and they never sleep */ + if (write->is_uninitialized()) + return true; + Node *prevnode = write->get_node()->get_parent(); - bool thread_sleep=prevnode->enabled_status(curr->get_tid())==THREAD_SLEEP_SET; - if (write->is_release()&&thread_sleep) + bool thread_sleep = prevnode->enabled_status(curr->get_tid()) == THREAD_SLEEP_SET; + if (write->is_release() && thread_sleep) return true; if (!write->is_rmw()) { return false; } - if (write->get_reads_from()==NULL) + if (write->get_reads_from() == NULL) return true; write=write->get_reads_from(); } } +/** + * @brief Create a new action representing an uninitialized atomic + * @param location The memory location of the atomic object + * @return A pointer to a new ModelAction + */ +ModelAction * ModelChecker::new_uninitialized_action(void *location) const +{ + ModelAction *act = (ModelAction *)snapshot_malloc(sizeof(class ModelAction)); + act = new (act) ModelAction(ATOMIC_UNINIT, std::memory_order_relaxed, location, 0, model_thread); + act->create_cv(NULL); + return act; +} + static void print_list(action_list_t *list, int exec_num = -1) { action_list_t::iterator it; @@ -2592,15 +2679,19 @@ bool ModelChecker::is_enabled(thread_id_t tid) const * @param act The current action that will be explored. May be NULL only if * trace is exiting via an assertion (see ModelChecker::set_assert and * ModelChecker::has_asserted). - * @return Return status from the 'swap' call (i.e., success/fail, 0/-1) + * @return Return the value returned by the current action */ -int ModelChecker::switch_to_master(ModelAction *act) +uint64_t ModelChecker::switch_to_master(ModelAction *act) { DBG(); Thread *old = thread_current(); set_current_action(act); old->set_state(THREAD_READY); - return Thread::swap(old, &system_context); + if (Thread::swap(old, &system_context) < 0) { + perror("swap threads"); + exit(EXIT_FAILURE); + } + return old->get_return_value(); } /**