*/
struct model_snapshot_members {
model_snapshot_members() :
- current_action(NULL),
/* First thread created will have id INITIAL_THREAD_ID */
next_thread_id(INITIAL_THREAD_ID),
used_sequence_numbers(0),
bugs.clear();
}
- ModelAction *current_action;
unsigned int next_thread_id;
modelclock_t used_sequence_numbers;
ModelAction *next_backtrack;
/* Print all model-checker output before rollback */
fflush(model_out);
+ /**
+ * FIXME: if we utilize partial rollback, we will need to free only
+ * those pending actions which were NOT pending before the rollback
+ * point
+ */
+ for (unsigned int i = 0; i < get_num_threads(); i++)
+ delete get_thread(int_to_id(i))->get_pending();
+
snapshot_backtrack_before(0);
}
if (curr != NULL) {
/* Do not split atomic actions. */
if (curr->is_rmwr())
- return thread_current();
+ return get_thread(curr);
else if (curr->get_type() == THREAD_CREATE)
return curr->get_thread_operand();
}
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->set_sleep_flag();
- thr->set_pending(priv->current_action);
+ if (scheduler->is_sleep_set(thr) && thr->get_pending()) {
+ thr->get_pending()->set_sleep_flag();
}
}
}
priv->bad_synchronization = true;
}
+/**
+ * Check whether the current trace has triggered an assertion which should halt
+ * its execution.
+ *
+ * @return True, if the execution should be aborted; false otherwise
+ */
bool ModelChecker::has_asserted() const
{
return priv->asserted;
}
+/**
+ * Trigger a trace assertion which should cause this execution to be halted.
+ * This can be due to a detected bug or due to an infeasibility that should
+ * halt ASAP.
+ */
void ModelChecker::set_assert()
{
priv->asserted = true;
switch (curr->get_type()) {
case THREAD_CREATE: {
- Thread *th = curr->get_thread_operand();
+ thrd_t *thrd = (thrd_t *)curr->get_location();
+ struct thread_params *params = (struct thread_params *)curr->get_value();
+ Thread *th = new Thread(thrd, params->func, params->arg);
+ add_thread(th);
th->set_creation(curr);
/* Promises can be satisfied by children */
for (unsigned int i = 0; i < promises->size(); i++) {
return true;
}
-/**
- * Stores the ModelAction for the current thread action. Call this
- * immediately before switching from user- to system-context to pass
- * data between them.
- * @param act The ModelAction created by the user-thread action
- */
-void ModelChecker::set_current_action(ModelAction *act) {
- priv->current_action = act;
-}
-
/**
* This is the heart of the model checker routine. It performs model-checking
* actions corresponding to a given "current action." Among other processes, it
return scheduler->is_enabled(tid);
}
+/**
+ * Switch from a model-checker context to a user-thread context. This is the
+ * complement of ModelChecker::switch_to_master and must be called from the
+ * model-checker context
+ *
+ * @param thread The user-thread to switch to
+ */
+void ModelChecker::switch_from_master(Thread *thread)
+{
+ scheduler->next_thread(thread);
+ Thread::swap(&system_context, thread);
+}
+
/**
* Switch from a user-context to the "master thread" context (a.k.a. system
* context). This switch is made with the intention of exploring a particular
{
DBG();
Thread *old = thread_current();
- set_current_action(act);
- old->set_state(THREAD_READY);
+ ASSERT(!old->get_pending());
+ old->set_pending(act);
if (Thread::swap(old, &system_context) < 0) {
perror("swap threads");
exit(EXIT_FAILURE);
/**
* Takes the next step in the execution, if possible.
* @param curr The current step to take
- * @return Returns true (success) if a step was taken and false otherwise.
+ * @return Returns the next Thread to run, if any; NULL if this execution
+ * should terminate
*/
-bool ModelChecker::take_step(ModelAction *curr)
+Thread * ModelChecker::take_step(ModelAction *curr)
{
- if (has_asserted())
- return false;
-
Thread *curr_thrd = get_thread(curr);
ASSERT(curr_thrd->get_state() == THREAD_READY);
/* Infeasible -> don't take any more steps */
if (is_infeasible())
- return false;
+ return NULL;
else if (isfeasibleprefix() && have_bug_reports()) {
set_assert();
- return false;
+ return NULL;
}
- if (params.bound != 0)
- if (priv->used_sequence_numbers > params.bound)
- return false;
+ if (params.bound != 0 && priv->used_sequence_numbers > params.bound)
+ return NULL;
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);
+ /* Only ask for the next thread from Scheduler if we haven't chosen one
+ * already */
+ if (!next_thrd)
+ 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);
- /*
- * Launch end-of-execution release sequence fixups only when there are:
- *
- * (1) no more user threads to run (or when execution replay chooses
- * the 'model_thread')
- * (2) pending release sequences
- * (3) pending assertions (i.e., data races)
- * (4) no pending promises
- */
- if (!pending_rel_seqs->empty() && (!next_thrd || next_thrd->is_model_thread()) &&
- is_feasible_prefix_ignore_relseq() && !unrealizedraces.empty()) {
- model_print("*** WARNING: release sequence fixup action (%zu pending release seuqences) ***\n",
- pending_rel_seqs->size());
- ModelAction *fixup = new ModelAction(MODEL_FIXUP_RELSEQ,
- std::memory_order_seq_cst, NULL, VALUE_NONE,
- model_thread);
- set_current_action(fixup);
- return true;
- }
-
- /* next_thrd == NULL -> don't take any more steps */
- if (!next_thrd)
- return false;
-
- next_thrd->set_state(THREAD_RUNNING);
-
- if (next_thrd->get_pending() != NULL) {
- /* restart a pending action */
- set_current_action(next_thrd->get_pending());
- next_thrd->set_pending(NULL);
- next_thrd->set_state(THREAD_READY);
- return true;
- }
-
- /* Return false only if swap fails with an error */
- return (Thread::swap(&system_context, next_thrd) == 0);
+ return next_thrd;
}
/** Wrapper to run the user's main function, with appropriate arguments */
do {
thrd_t user_thread;
Thread *t = new Thread(&user_thread, &user_main_wrapper, NULL);
-
add_thread(t);
- /* Run user thread up to its first action */
- scheduler->next_thread(t);
- Thread::swap(&system_context, t);
+ do {
+ /*
+ * Stash next pending action(s) for thread(s). There
+ * should only need to stash one thread's action--the
+ * thread which just took a step--plus the first step
+ * for any newly-created thread
+ */
+ for (unsigned int i = 0; i < get_num_threads(); i++) {
+ thread_id_t tid = int_to_id(i);
+ Thread *thr = get_thread(tid);
+ if (!thr->is_model_thread() && !thr->is_complete() && !thr->get_pending()) {
+ switch_from_master(thr);
+ }
+ }
+
+ /* Catch assertions from prior take_step or from
+ * between-ModelAction bugs (e.g., data races) */
+ if (has_asserted())
+ break;
+
+ /* Consume the next action for a Thread */
+ ModelAction *curr = t->get_pending();
+ t->set_pending(NULL);
+ t = take_step(curr);
+ } while (t && !t->is_model_thread());
- /* Wait for all threads to complete */
- while (take_step(priv->current_action));
+ /*
+ * Launch end-of-execution release sequence fixups only when
+ * the execution is otherwise feasible AND there are:
+ *
+ * (1) pending release sequences
+ * (2) pending assertions that could be invalidated by a change
+ * in clock vectors (i.e., data races)
+ * (3) no pending promises
+ */
+ while (!pending_rel_seqs->empty() &&
+ is_feasible_prefix_ignore_relseq() &&
+ !unrealizedraces.empty()) {
+ model_print("*** WARNING: release sequence fixup action "
+ "(%zu pending release seuqence(s)) ***\n",
+ pending_rel_seqs->size());
+ ModelAction *fixup = new ModelAction(MODEL_FIXUP_RELSEQ,
+ std::memory_order_seq_cst, NULL, VALUE_NONE,
+ model_thread);
+ take_step(fixup);
+ };
} while (next_execution());
print_stats();