/* 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(),
ModelAction *current_action;
unsigned int next_thread_id;
modelclock_t used_sequence_numbers;
- Thread *nextThread;
ModelAction *next_backtrack;
std::vector< bug_message *, SnapshotAlloc<bug_message *> > bugs;
struct execution_stats stats;
return priv->next_thread_id;
}
-/** @return The currently executing Thread. */
+/**
+ * Must be called from user-thread context (e.g., through the global
+ * thread_current() interface)
+ *
+ * @return The currently executing Thread.
+ */
Thread * ModelChecker::get_current_thread() const
{
return scheduler->get_current_thread();
/* Do not split atomic actions. */
if (curr->is_rmwr())
return thread_current();
- /* The THREAD_CREATE action points to the created Thread */
else if (curr->get_type() == THREAD_CREATE)
- return (Thread *)curr->get_location();
+ return curr->get_thread_operand();
}
/* Have we completed exploring the preselected path? */
tid = next->get_tid();
node_stack->pop_restofstack(2);
} else {
+ ASSERT(prevnode);
/* Make a different thread execute for next step */
- scheduler->add_sleep(thread_map->get(id_to_int(next->get_tid())));
+ scheduler->add_sleep(get_thread(next->get_tid()));
tid = prevnode->get_next_backtrack();
/* Make sure the backtracked thread isn't sleeping. */
node_stack->pop_restofstack(1);
record_stats();
/* Output */
- if (DBG_ENABLED() || params.verbose || have_bug_reports())
+ if (DBG_ENABLED() || params.verbose || (complete && have_bug_reports()))
print_execution(complete);
else
clear_program_output();
action_list_t::reverse_iterator rit;
for (rit = list->rbegin(); rit != list->rend(); rit++) {
ModelAction *prev = *rit;
- if (!act->same_thread(prev)&&prev->is_failed_trylock())
+ if (!act->same_thread(prev) && prev->is_failed_trylock())
return prev;
}
break;
action_list_t::reverse_iterator rit;
for (rit = list->rbegin(); rit != list->rend(); rit++) {
ModelAction *prev = *rit;
- if (!act->same_thread(prev)&&prev->is_failed_trylock())
+ if (!act->same_thread(prev) && prev->is_failed_trylock())
return prev;
- if (!act->same_thread(prev)&&prev->is_notify())
+ if (!act->same_thread(prev) && prev->is_notify())
return prev;
}
break;
action_list_t::reverse_iterator rit;
for (rit = list->rbegin(); rit != list->rend(); rit++) {
ModelAction *prev = *rit;
- if (!act->same_thread(prev)&&prev->is_wait())
+ if (!act->same_thread(prev) && prev->is_wait())
return prev;
}
break;
high_tid = get_num_threads();
}
- for(int i = low_tid; i < high_tid; i++) {
+ for (int i = low_tid; i < high_tid; i++) {
thread_id_t tid = int_to_id(i);
/* Make sure this thread can be enabled here. */
continue;
}
/* Cache the latest backtracking point */
- if (!priv->next_backtrack || *prev > *priv->next_backtrack)
- priv->next_backtrack = prev;
+ set_latest_backtrack(prev);
/* If this is a new backtracking point, mark the tree */
if (!node->set_backtrack(tid))
}
}
+/**
+ * @brief Cache the a backtracking point as the "most recent", if eligible
+ *
+ * Note that this does not prepare the NodeStack for this backtracking
+ * operation, it only caches the action on a per-execution basis
+ *
+ * @param act The operation at which we should explore a different next action
+ * (i.e., backtracking point)
+ * @return True, if this action is now the most recent backtracking point;
+ * false otherwise
+ */
+bool ModelChecker::set_latest_backtrack(ModelAction *act)
+{
+ if (!priv->next_backtrack || *act > *priv->next_backtrack) {
+ priv->next_backtrack = act;
+ return true;
+ }
+ return false;
+}
+
/**
* Returns last backtracking point. The model checker will explore a different
* path for this point in the next execution.
}
- if (!second_part_of_rmw&&is_infeasible()&&(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();
priv->too_many_reads = false;
continue;
if (curr->is_trylock() || curr->is_lock() || curr->is_unlock()) {
mutex = (std::mutex *)curr->get_location();
state = mutex->get_state();
- } else if(curr->is_wait()) {
+ } else if (curr->is_wait()) {
mutex = (std::mutex *)curr->get_value();
state = mutex->get_state();
}
if (curr->get_node()->get_misc() == 0) {
get_safe_ptr_action(condvar_waiters_map, curr->get_location())->push_back(curr);
//disable us
- scheduler->sleep(get_current_thread());
+ scheduler->sleep(get_thread(curr));
}
break;
}
for (unsigned int i = 0; i < futurevalues->size(); i++) {
struct PendingFutureValue pfv = (*futurevalues)[i];
//Do more ambitious checks now that mo is more complete
- if (mo_may_allow(pfv.writer, pfv.act)&&
- pfv.act->get_node()->add_future_value(pfv.writer->get_value(), pfv.writer->get_seq_number()+params.maxfuturedelay) &&
- (!priv->next_backtrack || *pfv.act > *priv->next_backtrack))
- priv->next_backtrack = pfv.act;
+ if (mo_may_allow(pfv.writer, pfv.act) &&
+ pfv.act->get_node()->add_future_value(pfv.writer->get_value(), pfv.writer->get_seq_number() + params.maxfuturedelay))
+ set_latest_backtrack(pfv.act);
}
futurevalues->resize(0);
}
switch (curr->get_type()) {
case THREAD_CREATE: {
- Thread *th = (Thread *)curr->get_location();
+ Thread *th = curr->get_thread_operand();
th->set_creation(curr);
break;
}
case THREAD_JOIN: {
- Thread *blocking = (Thread *)curr->get_location();
+ Thread *blocking = curr->get_thread_operand();
ModelAction *act = get_last_action(blocking->get_id());
curr->synchronize_with(act);
updated = true; /* trigger rel-seq checks */
* execution when running permutations of previously-observed executions.
*
* @param curr The current action to process
- * @return The next Thread that must be executed. May be NULL if ModelChecker
- * makes no choice (e.g., according to replay execution, combining RMW actions,
- * etc.)
+ * @return The ModelAction that is actually executed; may be different than
+ * curr; may be NULL, if the current action is not enabled to run
*/
-Thread * ModelChecker::check_current_action(ModelAction *curr)
+ModelAction * ModelChecker::check_current_action(ModelAction *curr)
{
ASSERT(curr);
bool second_part_of_rmw = curr->is_rmwc() || curr->is_rmw();
if (!check_action_enabled(curr)) {
/* Make the execution look like we chose to run this action
* much later, when a lock/join can succeed */
- get_current_thread()->set_pending(curr);
- scheduler->sleep(get_current_thread());
- return get_next_thread(NULL);
+ get_thread(curr)->set_pending(curr);
+ scheduler->sleep(get_thread(curr));
+ return NULL;
}
bool newly_explored = initialize_curr_action(&curr);
+ DBG();
+ if (DBG_ENABLED())
+ curr->print();
+
wake_up_sleeping_actions(curr);
/* Add the action to lists before any other model-checking tasks */
check_curr_backtracking(curr);
set_backtracking(curr);
- return get_next_thread(curr);
+ return curr;
}
void ModelChecker::check_curr_backtracking(ModelAction *curr)
Node *currnode = curr->get_node();
Node *parnode = currnode->get_parent();
- if ((!parnode->backtrack_empty() ||
+ if ((parnode && !parnode->backtrack_empty()) ||
!currnode->misc_empty() ||
!currnode->read_from_empty() ||
!currnode->future_value_empty() ||
!currnode->promise_empty() ||
- !currnode->relseq_break_empty())
- && (!priv->next_backtrack ||
- *curr > *priv->next_backtrack)) {
- priv->next_backtrack = curr;
+ !currnode->relseq_break_empty()) {
+ set_latest_backtrack(curr);
}
}
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++) {
+ for (; count < params.maxreads; rit++, count++) {
if (rit == list->rend())
return;
ModelAction *act = *rit;
if (write == rf)
continue;
- /* Test to see whether this is a feasible write to read from*/
+ /* Test to see whether this is a feasible write to read from */
mo_graph->startChanges();
r_modification_order(curr, write);
bool feasiblereadfrom = !is_infeasible();
bool feasiblewrite = true;
//new we need to see if this write works for everyone
- for (int loop = count; loop>0; loop--,rit++) {
+ 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_size(); j++) {
+ for (int j = 0; j < act->get_node()->get_read_from_size(); j++) {
if (act->get_node()->get_read_from_at(j) == write) {
foundvalue = true;
break;
/* Include at most one act per-thread that "happens before" curr */
if (lastact != NULL) {
- if (lastact== curr) {
+ if (lastact == curr) {
//Case 1: The resolved read is a RMW, and we need to make sure
//that the write portion of the RMW mod order after rf
if (thin_air_constraint_may_allow(curr, act)) {
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};
+ struct PendingFutureValue pfv = {curr, act};
futurevalues->push_back(pfv);
}
}
for (rit = list->rbegin(); rit != list->rend(); rit++) {
ModelAction *act = *rit;
- if (!reader->happens_before(act))
+ /* Don't disallow due to act == reader */
+ if (!reader->happens_before(act) || reader == act)
break;
else if (act->is_write())
write_after_read = act;
- else if (act->is_read() && act->get_reads_from() != NULL && act != reader) {
+ else if (act->is_read() && act->get_reads_from() != NULL)
write_after_read = act->get_reads_from();
- }
}
if (write_after_read && write_after_read != writer && mo_graph->checkReachable(write_after_read, writer))
const ModelAction *act = promise->get_action();
//Is this promise on the same location?
- if ( act->get_location() != location )
+ if (act->get_location() != location)
continue;
//same thread as the promise
- if ( act->get_tid() == tid ) {
+ if (act->get_tid() == tid) {
//do we have a pwrite for the promise, if not, set it
- if (promise->get_write() == NULL ) {
+ if (promise->get_write() == NULL) {
promise->set_write(write);
//The pwrite cannot happen before the promise
if (write->happens_before(act) && (write != act)) {
if (promise->has_sync_thread(tid))
continue;
- if (promise->get_write()&&mo_graph->checkReachable(promise->get_write(), write)) {
+ if (promise->get_write() && mo_graph->checkReachable(promise->get_write(), write)) {
if (promise->increment_threads(tid)) {
priv->failed_promise = true;
return;
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();
- }
+ if (allow_read)
curr->get_node()->add_read_from(act);
- }
/* Include at most one act per-thread that "happens before" curr */
if (act->happens_before(curr))
}
#if SUPPORT_MOD_ORDER_DUMP
-void ModelChecker::dumpGraph(char *filename) {
+void ModelChecker::dumpGraph(char *filename) const
+{
char buffer[200];
- sprintf(buffer, "%s.dot",filename);
+ sprintf(buffer, "%s.dot", filename);
FILE *file = fopen(buffer, "w");
- fprintf(file, "digraph %s {\n",filename);
+ fprintf(file, "digraph %s {\n", filename);
mo_graph->dumpNodes(file);
- ModelAction **thread_array = (ModelAction **)model_calloc(1, sizeof(ModelAction *)*get_num_threads());
+ ModelAction **thread_array = (ModelAction **)model_calloc(1, sizeof(ModelAction *) * get_num_threads());
for (action_list_t::iterator it = action_trace->begin(); it != action_trace->end(); it++) {
ModelAction *action = *it;
if (action->is_read()) {
- fprintf(file, "N%u [label=\"%u, T%u\"];\n", action->get_seq_number(),action->get_seq_number(), action->get_tid());
+ fprintf(file, "N%u [label=\"%u, T%u\"];\n", action->get_seq_number(), action->get_seq_number(), action->get_tid());
if (action->get_reads_from() != NULL)
fprintf(file, "N%u -> N%u[label=\"rf\", color=red];\n", action->get_seq_number(), action->get_reads_from()->get_seq_number());
}
thread_array[action->get_tid()] = action;
}
- fprintf(file,"}\n");
+ fprintf(file, "}\n");
model_free(thread_array);
fclose(file);
}
Thread *curr_thrd = get_thread(curr);
ASSERT(curr_thrd->get_state() == THREAD_READY);
- priv->nextThread = check_current_action(curr);
-
- if (curr_thrd->is_blocked() || curr_thrd->is_complete())
- scheduler->remove_thread(curr_thrd);
-
- Thread *next_thrd = scheduler->next_thread(priv->nextThread);
+ curr = check_current_action(curr);
/* Infeasible -> don't take any more steps */
if (is_infeasible())
return false;
}
- if (params.bound != 0) {
- if (priv->used_sequence_numbers > params.bound) {
+ if (params.bound != 0)
+ if (priv->used_sequence_numbers > params.bound)
return false;
- }
- }
+
+ if (curr_thrd->is_blocked() || curr_thrd->is_complete())
+ scheduler->remove_thread(curr_thrd);
+
+ Thread *next_thrd = get_next_thread(curr);
+ next_thrd = scheduler->next_thread(next_thrd);
DEBUG("(%d, %d)\n", curr_thrd ? id_to_int(curr_thrd->get_id()) : -1,
next_thrd ? id_to_int(next_thrd->get_id()) : -1);