/** @brief Constructor */
ModelChecker::ModelChecker(struct model_params params) :
/* Initialize default scheduler */
+ params(params),
scheduler(new Scheduler()),
num_executions(0),
num_feasible_executions(0),
- params(params),
diverge(NULL),
action_trace(new action_list_t()),
thread_map(new HashTable<int, Thread *, int>()),
snapshotObject->backTrackBeforeStep(0);
}
-/** @returns a thread ID for a new Thread */
+/** @return a thread ID for a new Thread */
thread_id_t ModelChecker::get_next_id()
{
return priv->next_thread_id++;
}
-/** @returns the number of user threads created during this execution */
+/** @return the number of user threads created during this execution */
int ModelChecker::get_num_threads()
{
return priv->next_thread_id;
}
-/** @returns a sequence number for a new ModelAction */
+/** @return a sequence number for a new ModelAction */
modelclock_t ModelChecker::get_next_seq_num()
{
return ++priv->used_sequence_numbers;
return NULL;
}
+/** This method find backtracking points where we should try to
+ * reorder the parameter ModelAction against.
+ *
+ * @param the ModelAction to find backtracking points for.
+ */
+
+
void ModelChecker::set_backtracking(ModelAction *act)
{
Thread *t = get_thread(act);
int low_tid, high_tid;
if (node->is_enabled(t)) {
- low_tid=id_to_int(act->get_tid());
- high_tid=low_tid+1;
+ low_tid = id_to_int(act->get_tid());
+ high_tid = low_tid+1;
} else {
- low_tid=0;
- high_tid=get_num_threads();
+ low_tid = 0;
+ high_tid = get_num_threads();
}
-
- for(int i=low_tid;i<high_tid;i++) {
- thread_id_t tid=int_to_id(i);
+
+ for(int i = low_tid; i < high_tid; i++) {
+ thread_id_t tid = int_to_id(i);
if (!node->is_enabled(tid))
continue;
/* Check if this has been explored already */
if (node->has_been_explored(tid))
continue;
-
+
+ /* See if fairness allows */
+ if (model->params.fairwindow != 0 && !node->has_priority(tid)) {
+ bool unfair=false;
+ for(int t=0;t<node->get_num_threads();t++) {
+ thread_id_t tother=int_to_id(t);
+ if (node->is_enabled(tother) && node->has_priority(tother)) {
+ unfair=true;
+ break;
+ }
+ }
+ if (unfair)
+ continue;
+ }
+
/* Cache the latest backtracking point */
if (!priv->next_backtrack || *prev > *priv->next_backtrack)
priv->next_backtrack = prev;
-
+
/* If this is a new backtracking point, mark the tree */
if (!node->set_backtrack(tid))
continue;
}
}
+/**
+ * Processes a lock, trylock, or unlock model action. @param curr is
+ * the read model action to process.
+ *
+ * The try lock operation checks whether the lock is taken. If not,
+ * it falls to the normal lock operation case. If so, it returns
+ * fail.
+ *
+ * The lock operation has already been checked that it is enabled, so
+ * it just grabs the lock and synchronizes with the previous unlock.
+ *
+ * The unlock operation has to re-enable all of the threads that are
+ * waiting on the lock.
+ */
void ModelChecker::process_mutex(ModelAction *curr) {
- std::mutex * mutex=(std::mutex *) curr->get_location();
- struct std::mutex_state * state=mutex->get_state();
- switch(curr->get_type()) {
+ std::mutex *mutex = (std::mutex *)curr->get_location();
+ struct std::mutex_state *state = mutex->get_state();
+ switch (curr->get_type()) {
case ATOMIC_TRYLOCK: {
- bool success=!state->islocked;
+ bool success = !state->islocked;
curr->set_try_lock(success);
if (!success) {
get_thread(curr)->set_return_value(0);
}
//otherwise fall into the lock case
case ATOMIC_LOCK: {
- if (curr->get_cv()->getClock(state->alloc_tid)<=state->alloc_clock) {
+ if (curr->get_cv()->getClock(state->alloc_tid) <= state->alloc_clock) {
printf("Lock access before initialization\n");
set_assert();
}
- state->islocked=true;
- ModelAction *unlock=get_last_unlock(curr);
+ state->islocked = true;
+ ModelAction *unlock = get_last_unlock(curr);
//synchronize with the previous unlock statement
- if ( unlock != NULL )
+ if (unlock != NULL)
curr->synchronize_with(unlock);
break;
}
case ATOMIC_UNLOCK: {
//unlock the lock
- state->islocked=false;
+ state->islocked = false;
//wake up the other threads
- action_list_t * waiters = lock_waiters_map->get_safe_ptr(curr->get_location());
+ action_list_t *waiters = lock_waiters_map->get_safe_ptr(curr->get_location());
//activate all the waiting threads
- for(action_list_t::iterator rit = waiters->begin(); rit!=waiters->end(); rit++) {
- add_thread(get_thread((*rit)->get_tid()));
+ for (action_list_t::iterator rit = waiters->begin(); rit != waiters->end(); rit++) {
+ scheduler->add_thread(get_thread((*rit)->get_tid()));
}
waiters->clear();
break;
}
}
-
/**
* Process a write ModelAction
* @param curr The ModelAction to process
bool updated_promises = resolve_promises(curr);
if (promises->size() == 0) {
- for (unsigned int i = 0; i<futurevalues->size(); i++) {
+ for (unsigned int i = 0; i < futurevalues->size(); i++) {
struct PendingFutureValue pfv = (*futurevalues)[i];
if (pfv.act->get_node()->add_future_value(pfv.value, pfv.expiration) &&
(!priv->next_backtrack || *pfv.act > *priv->next_backtrack))
if (curr->is_rmwr())
newcurr->copy_typeandorder(curr);
- ASSERT(curr->get_location()==newcurr->get_location());
+ ASSERT(curr->get_location() == newcurr->get_location());
newcurr->copy_from_new(curr);
/* Discard duplicate ModelAction; use action from NodeStack */
return newcurr;
}
+/**
+ * This method checks whether a model action is enabled at the given point.
+ * At this point, it checks whether a lock operation would be successful at this point.
+ * If not, it puts the thread in a waiter list.
+ * @param curr is the ModelAction to check whether it is enabled.
+ * @return a bool that indicates whether the action is enabled.
+ */
+
bool ModelChecker::check_action_enabled(ModelAction *curr) {
if (curr->is_lock()) {
- std::mutex * lock=(std::mutex *) curr->get_location();
+ 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
if (act->is_write() && process_write(act))
updated = true;
- if (act->is_mutex_op())
+ if (act->is_mutex_op())
process_mutex(act);
if (updated)
return false;
}
-/** @returns whether the current partial trace must be a prefix of a
+/** @return whether the current partial trace must be a prefix of a
* feasible trace. */
bool ModelChecker::isfeasibleprefix() {
return promises->size() == 0 && *lazy_sync_size == 0;
}
-/** @returns whether the current partial trace is feasible. */
+/** @return whether the current partial trace is feasible. */
bool ModelChecker::isfeasible() {
return !mo_graph->checkForRMWViolation() && isfeasibleotherthanRMW();
}
-/** @returns whether the current partial trace is feasible other than
+/** @return whether the current partial trace is feasible other than
* multiple RMW reading from the same store. */
bool ModelChecker::isfeasibleotherthanRMW() {
if (DBG_ENABLED()) {
/**
* Updates the mo_graph with the constraints imposed from the current
- * read.
+ * read.
*
* Basic idea is the following: Go through each other thread and find
* the lastest action that happened before our read. Two cases:
* promises. The basic problem is that actions that occur after the
* read curr could not property add items to the modification order
* for our read.
- *
+ *
* So for each thread, we find the earliest item that happens after
* the read curr. This is the item we have to fix up with additional
* constraints. If that action is write, we add a MO edge between
*/
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())) {
+ (curr->is_rmw() && act->is_rmw() && curr->get_reads_from() == act->get_reads_from() && isfeasibleotherthanRMW())) {
struct PendingFutureValue pfv = {curr->get_value(),curr->get_seq_number()+params.maxfuturedelay,act};
futurevalues->push_back(pfv);
}
return true;
for (const ModelAction *search = writer->get_reads_from(); search != NULL; search = search->get_reads_from()) {
- if (search==reader)
+ if (search == reader)
return false;
if (search->get_tid() == reader->get_tid() &&
search->happens_before(reader))
/** @todo Need to be smarter here... In the linux lock
* example, this will run to the beginning of the program for
* every acquire. */
+ /** @todo The way to be smarter here is to keep going until 1
+ * thread has a release preceded by an acquire and you've seen
+ * both. */
+
if (rf->is_acquire() && rf->is_release())
return true; /* complete */
+
+ /** @todo Might it be better to make this into a loop... */
+
return release_seq_head(rf->get_reads_from(), release_heads);
}
if (rf->is_release())
* the release seq? */
bool future_ordered = false;
+ ModelAction *last = get_last_action(int_to_id(i));
+ if (last && rf->happens_before(last))
+ future_ordered = true;
+
for (rit = list->rbegin(); rit != list->rend(); rit++) {
const ModelAction *act = *rit;
- if (!act->is_write())
- continue;
/* Reach synchronization -> this thread is complete */
if (act->happens_before(release))
break;
continue;
}
+ /* Only writes can break release sequences */
+ if (!act->is_write())
+ continue;
+
/* Check modification order */
if (mo_graph->checkReachable(rf, act)) {
/* rf --mo--> act */
/* propagate synchronization to later actions */
action_list_t::reverse_iterator it = action_trace->rbegin();
- while ((*it) != act) {
+ for (; (*it) != act; it++) {
ModelAction *propagate = *it;
if (act->happens_before(propagate)) {
propagate->synchronize_with(act);
(*thrd_last_action)[tid] = act;
}
-ModelAction * ModelChecker::get_last_action(thread_id_t tid)
+/**
+ * @brief Get the last action performed by a particular Thread
+ * @param tid The thread ID of the Thread in question
+ * @return The last action in the thread
+ */
+ModelAction * ModelChecker::get_last_action(thread_id_t tid) const
{
- int threadid=id_to_int(tid);
- if (threadid<(int)thrd_last_action->size())
+ int threadid = id_to_int(tid);
+ if (threadid < (int)thrd_last_action->size())
return (*thrd_last_action)[id_to_int(tid)];
else
return NULL;
* check
* @return The last seq_cst write
*/
-ModelAction * ModelChecker::get_last_seq_cst(ModelAction *curr)
+ModelAction * ModelChecker::get_last_seq_cst(ModelAction *curr) const
{
void *location = curr->get_location();
action_list_t *list = obj_map->get_safe_ptr(location);
return NULL;
}
-ModelAction * ModelChecker::get_last_unlock(ModelAction *curr)
+/**
+ * Gets the last unlock operation performed on a particular mutex (i.e., memory
+ * location). This function identifies the mutex according to the current
+ * action, which is presumed to perform on the same mutex.
+ * @param curr The current ModelAction; also denotes the object location to
+ * check
+ * @return The last unlock operation
+ */
+ModelAction * ModelChecker::get_last_unlock(ModelAction *curr) const
{
void *location = curr->get_location();
action_list_t *list = obj_map->get_safe_ptr(location);
- /* Find: max({i in dom(S) | seq_cst(t_i) && isWrite(t_i) && samevar(t_i, t)}) */
+ /* Find: max({i in dom(S) | isUnlock(t_i) && samevar(t_i, t)}) */
action_list_t::reverse_iterator rit;
for (rit = list->rbegin(); rit != list->rend(); rit++)
if ((*rit)->is_unlock())
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->is_seqcst() || (!act->is_seqcst() && (last_seq_cst == NULL || !act->happens_before(last_seq_cst))) || act == last_seq_cst) {
DEBUG("Adding action to may_read_from:\n");
if (DBG_ENABLED()) {
act->print();
#if SUPPORT_MOD_ORDER_DUMP
scheduler->print();
char buffername[100];
- sprintf(buffername, "exec%u",num_executions);
+ sprintf(buffername, "exec%04u", num_executions);
mo_graph->dumpGraphToFile(buffername);
#endif
scheduler->add_thread(t);
}
+/**
+ * Removes a thread from the scheduler.
+ * @param the thread to remove.
+ */
+
void ModelChecker::remove_thread(Thread *t)
{
scheduler->remove_thread(t);