/** @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>()),
obj_thrd_map(new HashTable<void *, std::vector<action_list_t>, uintptr_t, 4 >()),
promises(new std::vector<Promise *>()),
futurevalues(new std::vector<struct PendingFutureValue>()),
- lazy_sync_with_release(new HashTable<void *, action_list_t, uintptr_t, 4>()),
+ pending_acq_rel_seq(new std::vector<ModelAction *>()),
thrd_last_action(new std::vector<ModelAction *>(1)),
node_stack(new NodeStack()),
mo_graph(new CycleGraph()),
priv = (struct model_snapshot_members *)calloc(1, sizeof(*priv));
/* First thread created will have id INITIAL_THREAD_ID */
priv->next_thread_id = INITIAL_THREAD_ID;
-
- lazy_sync_size = &priv->lazy_sync_size;
}
/** @brief Destructor */
delete (*promises)[i];
delete promises;
- delete lazy_sync_with_release;
+ delete pending_acq_rel_seq;
delete thrd_last_action;
delete node_stack;
*
* @param the ModelAction to find backtracking points for.
*/
-
-
void ModelChecker::set_backtracking(ModelAction *act)
{
Thread *t = get_thread(act);
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;
return updated_mod_order || updated_promises;
}
+/**
+ * @brief Process the current action for thread-related activity
+ *
+ * Performs current-action processing for a THREAD_* ModelAction. Proccesses
+ * may include setting Thread status, completing THREAD_FINISH/THREAD_JOIN
+ * synchronization, etc. This function is a no-op for non-THREAD actions
+ * (e.g., ATOMIC_{READ,WRITE,RMW,LOCK}, etc.)
+ *
+ * @param curr The current action
+ * @return True if synchronization was updated
+ */
+bool ModelChecker::process_thread_action(ModelAction *curr)
+{
+ bool synchronized = false;
+
+ switch (curr->get_type()) {
+ case THREAD_CREATE: {
+ Thread *th = (Thread *)curr->get_location();
+ th->set_creation(curr);
+ break;
+ }
+ case THREAD_JOIN: {
+ Thread *waiting, *blocking;
+ waiting = get_thread(curr);
+ blocking = (Thread *)curr->get_location();
+ if (!blocking->is_complete()) {
+ blocking->push_wait_list(curr);
+ scheduler->sleep(waiting);
+ } else {
+ do_complete_join(curr);
+ synchronized = true;
+ }
+ break;
+ }
+ case THREAD_FINISH: {
+ Thread *th = get_thread(curr);
+ while (!th->wait_list_empty()) {
+ ModelAction *act = th->pop_wait_list();
+ Thread *wake = get_thread(act);
+ scheduler->wake(wake);
+ do_complete_join(act);
+ synchronized = true;
+ }
+ th->complete();
+ break;
+ }
+ case THREAD_START: {
+ check_promises(NULL, curr->get_cv());
+ break;
+ }
+ default:
+ break;
+ }
+
+ return synchronized;
+}
+
/**
* Initialize the current action by performing one or more of the following
* actions, as appropriate: merging RMWR and RMWC/RMW actions, stepping forward
* @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();
bool second_part_of_rmw = curr->is_rmwc() || curr->is_rmw();
if (!check_action_enabled(curr)) {
- //we'll make the execution look like we chose to run this action
- //much later...when a lock is actually available to relese
+ /* Make the execution look like we chose to run this action
+ * much later, when a lock is actually available to release */
get_current_thread()->set_pending(curr);
remove_thread(get_current_thread());
return get_next_thread(NULL);
build_reads_from_past(curr);
curr = newcurr;
- /* Thread specific actions */
- switch (curr->get_type()) {
- case THREAD_CREATE: {
- Thread *th = (Thread *)curr->get_location();
- th->set_creation(curr);
- break;
- }
- case THREAD_JOIN: {
- Thread *waiting, *blocking;
- waiting = get_thread(curr);
- blocking = (Thread *)curr->get_location();
- if (!blocking->is_complete()) {
- blocking->push_wait_list(curr);
- scheduler->sleep(waiting);
- } else {
- do_complete_join(curr);
- }
- break;
- }
- case THREAD_FINISH: {
- Thread *th = get_thread(curr);
- while (!th->wait_list_empty()) {
- ModelAction *act = th->pop_wait_list();
- Thread *wake = get_thread(act);
- scheduler->wake(wake);
- do_complete_join(act);
- }
- th->complete();
- break;
- }
- case THREAD_START: {
- check_promises(NULL, curr->get_cv());
- break;
- }
- default:
- break;
- }
-
work_queue_t work_queue(1, CheckCurrWorkEntry(curr));
while (!work_queue.empty()) {
case WORK_CHECK_CURR_ACTION: {
ModelAction *act = work.action;
bool updated = false;
+
+ process_thread_action(curr);
+
if (act->is_read() && process_read(act, second_part_of_rmw))
updated = true;
/** @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;
+ return promises->size() == 0 && pending_acq_rel_seq->size() == 0;
}
/** @return whether the current partial trace is feasible. */
* @param rf is the write ModelAction that curr reads from.
*
*/
-
void ModelChecker::post_r_modification_order(ModelAction *curr, const ModelAction *rf)
{
std::vector<action_list_t> *thrd_lists = obj_thrd_map->get_safe_ptr(curr->get_location());
/** 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) {
if (!writer->is_rmw())
return true;
*/
bool ModelChecker::release_seq_head(const ModelAction *rf, rel_heads_list_t *release_heads) const
{
- if (!rf) {
- /* read from future: need to settle this later */
- return false; /* incomplete */
- }
+ while (rf) {
+ ASSERT(rf->is_write());
- ASSERT(rf->is_write());
+ if (rf->is_release())
+ release_heads->push_back(rf);
+ if (!rf->is_rmw())
+ break; /* End of RMW chain */
- if (rf->is_release())
- release_heads->push_back(rf);
- if (rf->is_rmw()) {
- /* We need a RMW action that is both an acquire and release to stop */
/** @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. */
+
+ /* acq_rel RMW is a sufficient stopping condition */
if (rf->is_acquire() && rf->is_release())
return true; /* complete */
- return release_seq_head(rf->get_reads_from(), release_heads);
+
+ rf = rf->get_reads_from();
+ };
+ if (!rf) {
+ /* read from future: need to settle this later */
+ return false; /* incomplete */
}
+
if (rf->is_release())
return true; /* complete */
bool future_ordered = false;
ModelAction *last = get_last_action(int_to_id(i));
- if (last && rf->happens_before(last))
+ if (last && (rf->happens_before(last) ||
+ last->get_type() == THREAD_FINISH))
future_ordered = true;
for (rit = list->rbegin(); rit != list->rend(); rit++) {
complete = release_seq_head(rf, release_heads);
if (!complete) {
/* add act to 'lazy checking' list */
- action_list_t *list;
- list = lazy_sync_with_release->get_safe_ptr(act->get_location());
- list->push_back(act);
- (*lazy_sync_size)++;
+ pending_acq_rel_seq->push_back(act);
}
}
* modification order information is present at the time an action occurs.
*
* @param location The location/object that should be checked for release
- * sequence resolutions
+ * sequence resolutions. A NULL value means to check all locations.
* @param work_queue The work queue to which to add work items as they are
* generated
* @return True if any updates occurred (new synchronization, new mo_graph
*/
bool ModelChecker::resolve_release_sequences(void *location, work_queue_t *work_queue)
{
- action_list_t *list;
- list = lazy_sync_with_release->getptr(location);
- if (!list)
- return false;
-
bool updated = false;
- action_list_t::iterator it = list->begin();
- while (it != list->end()) {
+ std::vector<ModelAction *>::iterator it = pending_acq_rel_seq->begin();
+ while (it != pending_acq_rel_seq->end()) {
ModelAction *act = *it;
+
+ /* Only resolve sequences on the given location, if provided */
+ if (location && act->get_location() != location) {
+ it++;
+ continue;
+ }
+
const ModelAction *rf = act->get_reads_from();
rel_heads_list_t release_heads;
bool complete;
}
}
}
- if (complete) {
- it = list->erase(it);
- (*lazy_sync_size)--;
- } else
+ if (complete)
+ it = pending_acq_rel_seq->erase(it);
+ else
it++;
}
* Removes a thread from the scheduler.
* @param the thread to remove.
*/
-
void ModelChecker::remove_thread(Thread *t)
{
scheduler->remove_thread(t);