OBJECTS = libthreads.o schedule.o model.o threads.o librace.o action.o \
nodestack.o clockvector.o main.o snapshot-interface.o cyclegraph.o \
datarace.o impatomic.o cmodelint.o \
- snapshot.o malloc.o mymemory.o common.o mutex.o
+ snapshot.o malloc.o mymemory.o common.o mutex.o promise.o
CPPFLAGS += -Iinclude -I. -rdynamic
LDFLAGS = -ldl -lrt
bool ModelAction::synchronize_with(const ModelAction *act) {
if (*this < *act && type != THREAD_JOIN && type != ATOMIC_LOCK)
return false;
- model->check_promises(cv, act->cv);
+ model->check_promises(act->get_tid(), cv, act->cv);
cv->merge(act->cv);
return true;
}
#include "cyclegraph.h"
#include "action.h"
#include "common.h"
+#include "promise.h"
+#include "model.h"
/** Initializes a CycleGraph object. */
CycleGraph::CycleGraph() :
return false;
}
+bool CycleGraph::checkPromise(const ModelAction *fromact, Promise *promise) {
+ std::vector<CycleNode *, ModelAlloc<CycleNode *> > queue;
+ HashTable<CycleNode *, CycleNode *, uintptr_t, 4, model_malloc, model_calloc, model_free> discovered(64);
+ CycleNode *from = actionToNode.get(fromact);
+
+
+ queue.push_back(from);
+ discovered.put(from, from);
+ while(!queue.empty()) {
+ CycleNode * node=queue.back();
+ queue.pop_back();
+
+ if (promise->increment_threads(node->getAction()->get_tid())) {
+ return true;
+ }
+
+ for(unsigned int i=0;i<node->getEdges()->size();i++) {
+ CycleNode *next=(*node->getEdges())[i];
+ if (!discovered.contains(next)) {
+ discovered.put(next,next);
+ queue.push_back(next);
+ }
+ }
+ }
+ return false;
+}
+
void CycleGraph::startChanges() {
ASSERT(rollbackvector.size()==0);
ASSERT(rmwrollbackvector.size()==0);
#include "config.h"
#include "mymemory.h"
+class Promise;
class CycleNode;
class ModelAction;
bool checkForCycles();
bool checkForRMWViolation();
void addRMWEdge(const ModelAction *from, const ModelAction *rmw);
-
+ bool checkPromise(const ModelAction *from, Promise *p);
bool checkReachable(const ModelAction *from, const ModelAction *to);
void startChanges();
void commitChanges();
/** @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;
}
/** @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;
}
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:
post_r_modification_order(read, write);
//Make sure the promise's value matches the write's value
ASSERT(promise->get_value() == write->get_value());
+ mo_check_promises(read->get_tid(), write);
delete(promise);
promises->erase(promises->begin() + promise_index);
}
/** 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"
*/
struct model_snapshot_members {
ModelAction *current_action;
- int next_thread_id;
+ unsigned int next_thread_id;
modelclock_t used_sequence_numbers;
Thread *nextThread;
ModelAction *next_backtrack;
Thread * get_thread(ModelAction *act) const;
thread_id_t get_next_id();
- int get_num_threads();
+ unsigned int get_num_threads();
Thread * get_current_thread();
int switch_to_master(ModelAction *act);
bool isfeasible();
bool isfeasibleotherthanRMW();
bool isfinalfeasible();
- void check_promises(ClockVector *old_cv, ClockVector * merge_cv);
+ void mo_check_promises(thread_id_t tid, const ModelAction *write);
+ void check_promises(thread_id_t tid, ClockVector *old_cv, ClockVector * merge_cv);
void get_release_seq_heads(ModelAction *act, rel_heads_list_t *release_heads);
void finish_execution();
bool isfeasibleprefix();
void set_bad_synchronization() { bad_synchronization = true; }
const model_params params;
+ Scheduler * get_scheduler() { return scheduler;}
MEMALLOC
private:
--- /dev/null
+#include "promise.h"
+#include "model.h"
+#include "schedule.h"
+
+bool Promise::increment_threads(thread_id_t tid) {
+ unsigned int id=id_to_int(tid);
+ if (id>=synced_thread.size()) {
+ synced_thread.resize(id+1, false);
+ }
+ if (synced_thread[id])
+ return false;
+
+ synced_thread[id]=true;
+ bool * enabled=model->get_scheduler()->get_enabled();
+
+ for(unsigned int i=0;i<model->get_num_threads();i++) {
+ if (!synced_thread[id] && enabled[id])
+ return false;
+ }
+ return true;
+}
#define __PROMISE_H__
#include <inttypes.h>
+#include "threads.h"
-class ModelAction;
+#include "model.h"
class Promise {
public:
Promise(ModelAction *act, uint64_t value, modelclock_t expiration) :
- value(value), expiration(expiration), read(act), numthreads(1)
- { }
+ value(value), expiration(expiration), read(act), write(NULL)
+ {
+ increment_threads(act->get_tid());
+ }
modelclock_t get_expiration() const {return expiration;}
ModelAction * get_action() const { return read; }
- int increment_threads() { return ++numthreads; }
+ bool increment_threads(thread_id_t tid);
+
+ bool has_sync_thread(thread_id_t tid) {
+ unsigned int id=id_to_int(tid);
+ if (id>=synced_thread.size()) {
+ return false;
+ }
+ return synced_thread[id];
+ }
+
uint64_t get_value() const { return value; }
+ void set_write(const ModelAction *act) { write = act; }
+ const ModelAction * get_write() { return write; }
SNAPSHOTALLOC
private:
+ std::vector<bool> synced_thread;
const uint64_t value;
const modelclock_t expiration;
ModelAction * const read;
- unsigned int numthreads;
+ const ModelAction * write;
};
#endif
*/
void push_wait_list(ModelAction *act) { wait_list.push_back(act); }
+ unsigned int num_wait_list() {
+ return wait_list.size();
+ }
+
+ ModelAction * get_waiter(unsigned int i) {
+ return wait_list[i];
+ }
+
ModelAction * get_pending() { return pending; }
void set_pending(ModelAction *act) { pending = act; }
/**