#include <stdio.h>
#include <algorithm>
#include <mutex>
+#include <new>
#include "model.h"
#include "action.h"
delete priv;
}
-static action_list_t * get_safe_ptr_action(HashTable<const void *, action_list_t *, uintptr_t, 4> * 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<const void *, action_list_t *, uintptr_t, 4> * 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<action_list_t> * get_safe_ptr_vect_action(HashTable<void *, std::vector<action_list_t> *, uintptr_t, 4> * hash, void * ptr) {
- std::vector<action_list_t> * tmp=hash->get(ptr);
- if (tmp==NULL) {
- tmp=new std::vector<action_list_t>();
+static std::vector<action_list_t> * get_safe_ptr_vect_action(HashTable<void *, std::vector<action_list_t> *, uintptr_t, 4> * hash, void * ptr)
+{
+ std::vector<action_list_t> *tmp = hash->get(ptr);
+ if (tmp == NULL) {
+ tmp = new std::vector<action_list_t>();
hash->put(ptr, tmp);
}
return tmp;
{
thread_id_t tid;
- if (curr!=NULL) {
+ if (curr != NULL) {
/* Do not split atomic actions. */
if (curr->is_rmwr())
return thread_current();
* the corresponding thread object's pending action.
*/
-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->get_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);
priv->current_action = NULL;
}
-void ModelChecker::wake_up_sleeping_actions(ModelAction * curr) {
- 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->get_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);
- }
}
}
}
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)) {
continue;
}
- curr->read_from(reads_from);
+ read_from(curr, reads_from);
mo_graph->commitChanges();
mo_check_promises(curr->get_tid(), reads_from);
/* Read from future value */
value = curr->get_node()->get_future_value();
modelclock_t expiration = curr->get_node()->get_future_value_expiration();
- curr->read_from(NULL);
+ curr->set_read_from(NULL);
Promise *valuepromise = new Promise(curr, value, expiration);
promises->push_back(valuepromise);
}
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
*
}
}
+/**
+ * @brief Establish reads-from relation between two actions
+ *
+ * Perform basic operations involved with establishing a concrete rf relation,
+ * including setting the ModelAction data and checking for release sequences.
+ *
+ * @param act The action that is reading (must be a read)
+ * @param rf The action from which we are reading (must be a write)
+ *
+ * @return True if this read established synchronization
+ */
+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, 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])) {
+ set_bad_synchronization();
+ num_heads--;
+ }
+ return num_heads > 0;
+ }
+ return false;
+}
+
/**
* @brief Check whether a model action is enabled.
*
*/
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);
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;
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();
*
* 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
//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())
if (act->get_node()->get_read_from_size() <= 1)
return;
}
- 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);
+ 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*/
//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; j<act->get_node()->get_read_from_size(); j++) {
if (act->get_node()->get_read_from_at(j)==write) {
*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;
}
}
*act < *last_sc_fence_thread_before) {
mo_graph->addEdge(act, curr);
added = true;
+ break;
}
/*
/** 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;
if (rf->is_release())
release_heads->push_back(rf);
+ else if (rf->get_last_fence_release())
+ release_heads->push_back(rf->get_last_fence_release());
if (!rf->is_rmw())
break; /* End of RMW chain */
if (rf->is_release())
return true; /* complete */
- /* else relaxed write; check modification order for contiguous subsequence
- * -> rf must be same thread as release */
+ /* else relaxed write
+ * - check for fence-release in the same thread (29.8, stmt. 3)
+ * - check modification order for contiguous subsequence
+ * -> rf must be same thread as release */
+
+ const ModelAction *fence_release = rf->get_last_fence_release();
+ /* Synchronize with a fence-release unconditionally; we don't need to
+ * find any more "contiguous subsequence..." for it */
+ if (fence_release)
+ release_heads->push_back(fence_release);
+
int tid = id_to_int(rf->get_tid());
std::vector<action_list_t> *thrd_lists = get_safe_ptr_vect_action(obj_thrd_map, rf->get_location());
action_list_t *list = &(*thrd_lists)[tid];
rit = std::find(list->rbegin(), list->rend(), rf);
ASSERT(rit != list->rend());
- /* Find the last write/release */
- for (; rit != list->rend(); rit++)
+ /* Find the last {write,fence}-release */
+ for (; rit != list->rend(); rit++) {
+ if (fence_release && *(*rit) < *fence_release)
+ break;
if ((*rit)->is_release())
break;
+ }
if (rit == list->rend()) {
/* No write-release in this thread */
return true; /* complete */
- }
+ } else if (fence_release && *(*rit) < *fence_release) {
+ /* The fence-release is more recent (and so, "stronger") than
+ * the most recent write-release */
+ return true; /* complete */
+ } /* else, need to establish contiguous release sequence */
ModelAction *release = *rit;
ASSERT(rf->same_thread(release));
}
/**
- * A public interface for getting the release sequence head(s) with which a
+ * An interface for getting the release sequence head(s) with which a
* 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 */
std::vector< struct release_seq *, SnapshotAlloc<struct release_seq *> >::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();
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));
}
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<action_list_t> *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)
if (read->is_rmw()) {
mo_graph->addRMWEdge(write, read);
}
- read->read_from(write);
+ read_from(read, write);
//First fix up the modification order for actions that happened
//before the read
r_modification_order(read, write);
* 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();
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++) {
}
/* 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");
}
}
-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;
* @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();
}
/**