#include "promise.h"
#include "datarace.h"
#include "mutex.h"
+#include "threads.h"
#define INITIAL_THREAD_ID 0
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>()),
- pending_acq_rel_seq(new std::vector<ModelAction *>()),
+ pending_rel_seqs(new std::vector<struct release_seq *>()),
thrd_last_action(new std::vector<ModelAction *>(1)),
node_stack(new NodeStack()),
mo_graph(new CycleGraph()),
/** @brief Destructor */
ModelChecker::~ModelChecker()
{
- for (int i = 0; i < get_num_threads(); i++)
+ for (unsigned int i = 0; i < get_num_threads(); i++)
delete thread_map->get(i);
delete thread_map;
delete (*promises)[i];
delete promises;
- delete pending_acq_rel_seq;
+ delete pending_rel_seqs;
delete thrd_last_action;
delete node_stack;
}
/** @return the number of user threads created during this execution */
-int ModelChecker::get_num_threads()
+unsigned int ModelChecker::get_num_threads()
{
return priv->next_thread_id;
}
+/** @return The currently executing Thread. */
+Thread * ModelChecker::get_current_thread()
+{
+ return scheduler->get_current_thread();
+}
+
/** @return a sequence number for a new ModelAction */
modelclock_t ModelChecker::get_next_seq_num()
{
} else {
tid = next->get_tid();
}
- DEBUG("*** ModelChecker chose next thread = %d ***\n", tid);
+ DEBUG("*** ModelChecker chose next thread = %d ***\n", id_to_int(tid));
ASSERT(tid != THREAD_ID_T_NONE);
return thread_map->get(id_to_int(tid));
}
if (isfinalfeasible()) {
printf("Earliest divergence point since last feasible execution:\n");
if (earliest_diverge)
- earliest_diverge->print(false);
+ earliest_diverge->print();
else
printf("(Not set)\n");
}
DEBUG("Number of acquires waiting on pending release sequences: %lu\n",
- pending_acq_rel_seq->size());
+ pending_rel_seqs->size());
if (isfinalfeasible() || DBG_ENABLED())
print_summary();
if (!node->set_backtrack(tid))
continue;
DEBUG("Setting backtrack: conflict = %d, instead tid = %d\n",
- prev->get_tid(), t->get_id());
+ id_to_int(prev->get_tid()),
+ id_to_int(t->get_id()));
if (DBG_ENABLED()) {
prev->print();
act->print();
curr->read_from(reads_from);
mo_graph->commitChanges();
+ mo_check_promises(curr->get_tid(), reads_from);
+
updated |= r_status;
} else if (!second_part_of_rmw) {
/* Read from future value */
}
mo_graph->commitChanges();
+ mo_check_promises(curr->get_tid(), curr);
+
get_thread(curr)->set_return_value(VALUE_NONE);
return updated_mod_order || updated_promises;
}
break;
}
case THREAD_START: {
- check_promises(NULL, curr->get_cv());
+ check_promises(curr->get_tid(), NULL, curr->get_cv());
break;
}
default:
/** @return whether the current partial trace must be a prefix of a
* feasible trace. */
bool ModelChecker::isfeasibleprefix() {
- return promises->size() == 0 && pending_acq_rel_seq->size() == 0;
+ return promises->size() == 0 && pending_rel_seqs->size() == 0;
}
/** @return whether the current partial trace is feasible. */
/** Close out a RMWR by converting previous RMWR into a RMW or READ. */
ModelAction * ModelChecker::process_rmw(ModelAction *act) {
- int tid = id_to_int(act->get_tid());
- ModelAction *lastread = get_last_action(tid);
+ ModelAction *lastread = get_last_action(act->get_tid());
lastread->process_rmw(act);
if (act->is_rmw() && lastread->get_reads_from()!=NULL) {
mo_graph->addRMWEdge(lastread->get_reads_from(), lastread);
* @todo Finish lazy updating, when promises are fulfilled in the future
* @param rf The action that might be part of a release sequence. Must be a
* write.
- * @param release_heads A pass-by-reference style return parameter. After
+ * @param release_heads A pass-by-reference style return parameter. After
* execution of this function, release_heads will contain the heads of all the
- * relevant release sequences, if any exists
+ * relevant release sequences, if any exists with certainty
+ * @param pending A pass-by-reference style return parameter which is only used
+ * when returning false (i.e., uncertain). Returns most information regarding
+ * an uncertain release sequence, including any write operations that might
+ * break the sequence.
* @return true, if the ModelChecker is certain that release_heads is complete;
* false otherwise
*/
-bool ModelChecker::release_seq_head(const ModelAction *rf, rel_heads_list_t *release_heads) const
+bool ModelChecker::release_seq_heads(const ModelAction *rf,
+ rel_heads_list_t *release_heads,
+ struct release_seq *pending) const
{
/* Only check for release sequences if there are no cycles */
if (mo_graph->checkForCycles())
};
if (!rf) {
/* read from future: need to settle this later */
+ pending->rf = NULL;
return false; /* incomplete */
}
ASSERT(rf->same_thread(release));
+ pending->writes.clear();
+
bool certain = true;
for (unsigned int i = 0; i < thrd_lists->size(); i++) {
if (id_to_int(rf->get_tid()) == (int)i)
ModelAction *last = get_last_action(int_to_id(i));
if (last && (rf->happens_before(last) ||
- last->get_type() == THREAD_FINISH))
+ get_thread(int_to_id(i))->is_complete()))
future_ordered = true;
for (rit = list->rbegin(); rit != list->rend(); rit++) {
/* release --mo-> act --mo--> rf */
return true; /* complete */
}
+ /* act may break release sequence */
+ pending->writes.push_back(act);
certain = false;
}
if (!future_ordered)
- return false; /* This thread is uncertain */
+ certain = false; /* This thread is uncertain */
}
- if (certain)
+ if (certain) {
release_heads->push_back(release);
+ pending->writes.clear();
+ } else {
+ pending->release = release;
+ pending->rf = rf;
+ }
return certain;
}
* @param act The 'acquire' action that may read from a release sequence
* @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_head
+ * @see ModelChecker::release_seq_heads
*/
void ModelChecker::get_release_seq_heads(ModelAction *act, rel_heads_list_t *release_heads)
{
const ModelAction *rf = act->get_reads_from();
- bool complete;
- complete = release_seq_head(rf, release_heads);
- if (!complete) {
+ struct release_seq *sequence = (struct release_seq *)snapshot_calloc(1, sizeof(struct release_seq));
+ sequence->acquire = act;
+
+ if (!release_seq_heads(rf, release_heads, sequence)) {
/* add act to 'lazy checking' list */
- pending_acq_rel_seq->push_back(act);
+ pending_rel_seqs->push_back(sequence);
+ } else {
+ snapshot_free(sequence);
}
}
bool ModelChecker::resolve_release_sequences(void *location, work_queue_t *work_queue)
{
bool updated = false;
- std::vector<ModelAction *>::iterator it = pending_acq_rel_seq->begin();
- while (it != pending_acq_rel_seq->end()) {
- ModelAction *act = *it;
+ std::vector<struct release_seq *>::iterator it = pending_rel_seqs->begin();
+ while (it != pending_rel_seqs->end()) {
+ struct release_seq *pending = *it;
+ ModelAction *act = pending->acquire;
/* Only resolve sequences on the given location, if provided */
if (location && act->get_location() != location) {
const ModelAction *rf = act->get_reads_from();
rel_heads_list_t release_heads;
bool complete;
- complete = release_seq_head(rf, &release_heads);
+ 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 (complete)
- it = pending_acq_rel_seq->erase(it);
- else
+ if (complete) {
+ it = pending_rel_seqs->erase(it);
+ snapshot_free(pending);
+ } else {
it++;
+ }
}
// If we resolved promises or data races, see if we have realized a data race.
bool ModelChecker::resolve_promises(ModelAction *write)
{
bool resolved = false;
+ std::vector<thread_id_t> threads_to_check;
for (unsigned int i = 0, promise_index = 0; promise_index < promises->size(); i++) {
Promise *promise = (*promises)[promise_index];
post_r_modification_order(read, write);
//Make sure the promise's value matches the write's value
ASSERT(promise->get_value() == write->get_value());
-
delete(promise);
+
promises->erase(promises->begin() + promise_index);
+ threads_to_check.push_back(read->get_tid());
+
resolved = true;
} else
promise_index++;
}
+ for(unsigned int i=0;i<threads_to_check.size();i++)
+ mo_check_promises(threads_to_check[i], write);
+
return resolved;
}
}
/** Checks promises in response to change in ClockVector Threads. */
-void ModelChecker::check_promises(ClockVector *old_cv, ClockVector *merge_cv)
+void ModelChecker::check_promises(thread_id_t tid, ClockVector *old_cv, ClockVector *merge_cv)
{
for (unsigned int i = 0; i < promises->size(); i++) {
Promise *promise = (*promises)[i];
const ModelAction *act = promise->get_action();
if ((old_cv == NULL || !old_cv->synchronized_since(act)) &&
merge_cv->synchronized_since(act)) {
- //This thread is no longer able to send values back to satisfy the promise
- int num_synchronized_threads = promise->increment_threads();
- if (num_synchronized_threads == get_num_threads()) {
+ if (promise->increment_threads(tid)) {
//Promise has failed
failed_promise = true;
return;
}
}
+/** Checks promises in response to change in ClockVector Threads. */
+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();
+
+ //Is this promise on the same location?
+ if ( act->get_location() != location )
+ continue;
+
+ if ( act->get_tid()==tid) {
+ if (promise->get_write() == NULL ) {
+ promise->set_write(write);
+ }
+ if (mo_graph->checkPromise(write, promise)) {
+ failed_promise = true;
+ return;
+ }
+ }
+
+ //Don't do any lookups twice for the same thread
+ if (promise->has_sync_thread(tid))
+ continue;
+
+ if (mo_graph->checkReachable(promise->get_write(), write)) {
+ if (promise->increment_threads(tid)) {
+ failed_promise = true;
+ return;
+ }
+ }
+ }
+}
+
/**
* Build up an initial set of all past writes that this 'read' action may read
* from. This set is determined by the clock vector's "happens before"
scheduler->remove_thread(t);
}
+/**
+ * @brief Get a Thread reference by its ID
+ * @param tid The Thread's ID
+ * @return A Thread reference
+ */
+Thread * ModelChecker::get_thread(thread_id_t tid) const
+{
+ return thread_map->get(id_to_int(tid));
+}
+
+/**
+ * @brief Get a reference to the Thread in which a ModelAction was executed
+ * @param act The ModelAction
+ * @return A Thread reference
+ */
+Thread * ModelChecker::get_thread(ModelAction *act) const
+{
+ return get_thread(act->get_tid());
+}
+
/**
* 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
* model-checking action (described by a ModelAction object). Must be called
* from a user-thread context.
- * @param act The current action that will be explored. Must not be NULL.
+ *
+ * @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)
*/
int ModelChecker::switch_to_master(ModelAction *act)
if (has_asserted())
return false;
- Thread * curr = thread_current();
+ Thread *curr = thread_current();
if (curr) {
if (curr->get_state() == THREAD_READY) {
ASSERT(priv->current_action);
ASSERT(false);
}
}
- Thread * next = scheduler->next_thread(priv->nextThread);
+ Thread *next = scheduler->next_thread(priv->nextThread);
/* Infeasible -> don't take any more steps */
if (!isfeasible())
return false;
- if (next)
- next->set_state(THREAD_RUNNING);
- DEBUG("(%d, %d)\n", curr ? curr->get_id() : -1, next ? next->get_id() : -1);
+ DEBUG("(%d, %d)\n", curr ? id_to_int(curr->get_id()) : -1,
+ next ? id_to_int(next->get_id()) : -1);
/* next == NULL -> don't take any more steps */
if (!next)
return false;
- if ( next->get_pending() != NULL ) {
- //restart a pending action
+ next->set_state(THREAD_RUNNING);
+
+ if (next->get_pending() != NULL) {
+ /* restart a pending action */
set_current_action(next->get_pending());
next->set_pending(NULL);
next->set_state(THREAD_READY);