/* caller is polling; so return immediately. */
SET_EDX(tid, EBUSY);
} else {
+ VG_TRACK ( pre_mutex_lock, tid, mutex );
+
VG_(threads)[tid].status = VgTs_WaitMX;
VG_(threads)[tid].associated_mx = mutex;
SET_EDX(tid, 0); /* pth_mx_lock success value */
} else {
/* Nobody owns it. Sanity check ... */
vg_assert(mutex->__m_owner == VG_INVALID_THREADID);
+
+ VG_TRACK ( pre_mutex_lock, tid, mutex );
+
/* We get it! [for the first time]. */
mutex->__m_count = 1;
mutex->__m_owner = (_pthread_descr)tid;
locked now so that it balances with unlocks */
if (mutex->__m_kind & VG_PTHREAD_PREHISTORY) {
mutex->__m_kind &= ~VG_PTHREAD_PREHISTORY;
+ VG_TRACK( pre_mutex_lock, (ThreadId)mutex->__m_owner, mutex );
VG_TRACK( post_mutex_lock, (ThreadId)mutex->__m_owner, mutex );
}
} else {
/* Currently held. Make thread tid be blocked on it. */
vg_assert(mx->__m_count > 0);
+ VG_TRACK( pre_mutex_lock, tid, mx );
+
VG_(threads)[tid].status = VgTs_WaitMX;
SET_EDX(tid, ETIMEDOUT); /* pthread_cond_wait return value */
VG_(threads)[tid].associated_cv = NULL;
mx = VG_(threads)[i].associated_mx;
vg_assert(mx != NULL);
+ VG_TRACK( pre_mutex_lock, i, mx );
+
if (mx->__m_owner == VG_INVALID_THREADID) {
/* Currently unheld; hand it out to thread i. */
vg_assert(mx->__m_count == 0);
return check_cycle_inner(start, lockset);
}
-/* catch bad mutex state changes (though the common ones are handled
- by core) */
-static void set_mutex_state(Mutex *mutex, MutexState state,
- ThreadId tid, ThreadState *tst)
+/* test to see if a mutex state change would be problematic; this
+ makes no changes to the mutex state. This should be called before
+ the locking thread has actually blocked. */
+static void test_mutex_state(Mutex *mutex, MutexState state,
+ ThreadId tid, ThreadState *tst)
{
static const Bool debug = False;
- if (debug)
- VG_(printf)("\ntid %d changing mutex (%p)->%p%(y state %s -> %s\n",
- tid, mutex, mutex->mutexp, mutex->mutexp,
- pp_MutexState(mutex->state), pp_MutexState(state));
-
if (mutex->state == MxDead) {
Char *str;
switch(state) {
case MxLocked:
- if (mutex->state == MxLocked) {
- if (mutex->tid != tid)
- record_mutex_error(tid, mutex, "take lock held by someone else",
- mutex->location);
- else
- record_mutex_error(tid, mutex, "take lock we already hold",
- mutex->location);
-
- VG_(skin_panic)("core should have checked this\n");
- break;
- }
-
sk_assert(!check_cycle(mutex, mutex->lockdep));
if (debug)
print_LockSet("lockdep", mutex->lockdep);
}
}
- mutex->tid = tid;
break;
case MxUnlocked:
record_mutex_error(tid, mutex,
"unlock someone else's mutex", mutex->location);
}
+ break;
+
+ case MxDead:
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Update a mutex state. Expects most error testing and reporting to
+ have happened in test_mutex_state(). The assumption is that no
+ client code is run by thread tid between test and set, either
+ because it is blocked or test and set are called together
+ atomically.
+
+ Setting state to MxDead is the exception, since that can happen as
+ a result of any thread freeing memory; in this case set_mutex_state
+ does all the error reporting as well.
+*/
+static void set_mutex_state(Mutex *mutex, MutexState state,
+ ThreadId tid, ThreadState *tst)
+{
+ static const Bool debug = False;
+
+ if (debug)
+ VG_(printf)("\ntid %d changing mutex (%p)->%p%(y state %s -> %s\n",
+ tid, mutex, mutex->mutexp, mutex->mutexp,
+ pp_MutexState(mutex->state), pp_MutexState(state));
+
+ if (mutex->state == MxDead) {
+ /* can't do anything legal to a destroyed mutex */
+ return;
+ }
+
+ switch(state) {
+ case MxLocked:
+ if (mutex->state == MxLocked) {
+ if (mutex->tid != tid)
+ record_mutex_error(tid, mutex, "take lock held by someone else",
+ mutex->location);
+ else
+ record_mutex_error(tid, mutex, "take lock we already hold",
+ mutex->location);
+
+ VG_(skin_panic)("core should have checked this\n");
+ break;
+ }
+
+ sk_assert(!check_cycle(mutex, mutex->lockdep));
+
+ mutex->tid = tid;
+ break;
+
+ case MxUnlocked:
+ if (debug)
+ print_LockSet("thread holding", thread_locks[tid]);
+
+ if (mutex->state != MxLocked || mutex->tid != tid)
+ break;
+
mutex->tid = VG_INVALID_THREADID;
break;
}
+static void eraser_pre_mutex_lock(ThreadId tid, void* void_mutex)
+{
+ Mutex *mutex = get_mutex((Addr)void_mutex);
+
+ test_mutex_state(mutex, MxLocked, tid, VG_(get_ThreadState)(tid));
+}
+
static void eraser_post_mutex_lock(ThreadId tid, void* void_mutex)
{
static const Bool debug = False;
Mutex *mutex = get_mutex((Addr)void_mutex);
const LockSet *ls;
+ test_mutex_state(mutex, MxUnlocked, tid, VG_(get_ThreadState)(tid));
set_mutex_state(mutex, MxUnlocked, tid, VG_(get_ThreadState)(tid));
if (!ismember(thread_locks[tid], mutex))
track->pre_mem_write = & eraser_pre_mem_write;
track->post_mem_write = NULL;
+ track->pre_mutex_lock = & eraser_pre_mutex_lock;
track->post_mutex_lock = & eraser_post_mutex_lock;
track->post_mutex_unlock = & eraser_post_mutex_unlock;
/* Mutex events (not exhaustive) */
+
+ /* Called before a thread can block while waiting for a mutex
+ (called regardless of whether the thread will block or
+ not) */
+ void (*pre_mutex_lock) ( ThreadId tid,
+ void* /*pthread_mutex_t* */ mutex );
+ /* Called once the thread actually holds the mutex (always
+ paired with pre_mutex_lock) */
void (*post_mutex_lock) ( ThreadId tid,
void* /*pthread_mutex_t* */ mutex );
+ /* Called after a thread has released a mutex (no need for a
+ corresponding pre_mutex_unlock, because unlocking can't
+ block) */
void (*post_mutex_unlock) ( ThreadId tid,
void* /*pthread_mutex_t* */ mutex );
+ /* Called during thread create, before the new thread has run
+ any instructions (or touched any memory). */
void (*post_thread_create)( ThreadId tid, ThreadId child );
+ /* Called once the joinee thread is terminated and the joining
+ thread is about to resume. */
void (*post_thread_join) ( ThreadId joiner, ThreadId joinee );
/* Others... thread, condition variable, signal events... */