From: Julian Seward Date: Wed, 17 Apr 2002 23:21:37 +0000 (+0000) Subject: Sanity check mutex implementation, and add support for recursive mutexes. X-Git-Tag: svn/VALGRIND_1_0_3~356 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1c469385c2ff282dcc5862ee9015b81010956649;p=thirdparty%2Fvalgrind.git Sanity check mutex implementation, and add support for recursive mutexes. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@94 --- diff --git a/coregrind/arch/x86-linux/vg_libpthread.c b/coregrind/arch/x86-linux/vg_libpthread.c index 11f5200a0d..f32c5533ea 100644 --- a/coregrind/arch/x86-linux/vg_libpthread.c +++ b/coregrind/arch/x86-linux/vg_libpthread.c @@ -23,7 +23,6 @@ #include "valgrind.h" /* For the request-passing mechanism */ #include "vg_include.h" /* For the VG_USERREQ__* constants */ -#include #include #include @@ -116,6 +115,14 @@ static void ignored ( char* msg ) Pass pthread_ calls to Valgrind's request mechanism. ------------------------------------------------------------------ */ +#include +#include +#include + +/* --------------------------------------------------- + THREAD ATTRIBUTES + ------------------------------------------------ */ + int pthread_attr_init(pthread_attr_t *attr) { ignored("pthread_attr_init"); @@ -129,6 +136,11 @@ int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) } + +/* --------------------------------------------------- + THREADs + ------------------------------------------------ */ + int pthread_create (pthread_t *__restrict __thread, __const pthread_attr_t *__restrict __attr, @@ -157,20 +169,6 @@ pthread_join (pthread_t __th, void **__thread_return) } -/* What are these? Anybody know? I don't. */ - -void _pthread_cleanup_push_defer ( void ) -{ - // char* str = "_pthread_cleanup_push_defer\n"; - // write(2, str, strlen(str)); -} - -void _pthread_cleanup_pop_restore ( void ) -{ - // char* str = "_pthread_cleanup_pop_restore\n"; - // write(2, str, strlen(str)); -} - static int thread_specific_errno[VG_N_THREADS]; @@ -188,33 +186,49 @@ int* __errno_location ( void ) } +/* --------------------------------------------------- + MUTEX ATTRIBUTES + ------------------------------------------------ */ + int pthread_mutexattr_init(pthread_mutexattr_t *attr) { - ignored("pthread_mutexattr_init"); + attr->__mutexkind = PTHREAD_MUTEX_ERRORCHECK_NP; return 0; } -int pthread_mutex_init(pthread_mutex_t *mutex, - const pthread_mutexattr_t *mutexattr) +int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) { - int res; - ensure_valgrind("pthread_mutex_init"); - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_MUTEX_INIT, - mutex, mutexattr, 0, 0); - return res; + switch (type) { + case PTHREAD_MUTEX_TIMED_NP: + case PTHREAD_MUTEX_RECURSIVE_NP: + case PTHREAD_MUTEX_ERRORCHECK_NP: + case PTHREAD_MUTEX_ADAPTIVE_NP: + attr->__mutexkind = type; + return 0; + default: + return EINVAL; + } } int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) { - ignored("pthread_mutexattr_destroy"); return 0; } -int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) + +/* --------------------------------------------------- + MUTEXes + ------------------------------------------------ */ + +int pthread_mutex_init(pthread_mutex_t *mutex, + const pthread_mutexattr_t *mutexattr) { - ignored("pthread_mutexattr_settype"); - return 0; + int res; + ensure_valgrind("pthread_mutex_init"); + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__PTHREAD_MUTEX_INIT, + mutex, mutexattr, 0, 0); + return res; } int pthread_mutex_lock(pthread_mutex_t *mutex) @@ -249,18 +263,6 @@ int pthread_mutex_unlock(pthread_mutex_t *mutex) } } -pthread_t pthread_self(void) -{ - int tid; - ensure_valgrind("pthread_self"); - VALGRIND_MAGIC_SEQUENCE(tid, 0 /* default */, - VG_USERREQ__PTHREAD_GET_THREADID, - 0, 0, 0, 0); - if (tid < 0 || tid >= VG_N_THREADS) - barf("pthread_self: invalid ThreadId"); - return tid; -} - int pthread_mutex_destroy(pthread_mutex_t *mutex) { int res; @@ -278,6 +280,10 @@ int pthread_mutex_destroy(pthread_mutex_t *mutex) } +/* --------------------------------------------------- + CANCELLATION + ------------------------------------------------ */ + int pthread_setcanceltype(int type, int *oldtype) { ignored("pthread_setcanceltype"); @@ -296,6 +302,9 @@ int pthread_cancel(pthread_t thread) } +/* --------------------------------------------------- + THREAD-SPECIFICs + ------------------------------------------------ */ int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *)) @@ -322,16 +331,47 @@ void * pthread_getspecific(pthread_key_t key) return NULL; } + +/* --------------------------------------------------- + MISC + ------------------------------------------------ */ + +/* What are these? Anybody know? I don't. */ + +void _pthread_cleanup_push_defer ( void ) +{ + // char* str = "_pthread_cleanup_push_defer\n"; + // write(2, str, strlen(str)); +} + +void _pthread_cleanup_pop_restore ( void ) +{ + // char* str = "_pthread_cleanup_pop_restore\n"; + // write(2, str, strlen(str)); +} + + +pthread_t pthread_self(void) +{ + int tid; + ensure_valgrind("pthread_self"); + VALGRIND_MAGIC_SEQUENCE(tid, 0 /* default */, + VG_USERREQ__PTHREAD_GET_THREADID, + 0, 0, 0, 0); + if (tid < 0 || tid >= VG_N_THREADS) + barf("pthread_self: invalid ThreadId"); + return tid; +} + + /* --------------------------------------------------------------------- These are here (I think) because they are deemed cancellation points by POSIX. For the moment we'll simply pass the call along to the corresponding thread-unaware (?) libc routine. ------------------------------------------------------------------ */ -#include #include #include -#include #include #include diff --git a/coregrind/vg_libpthread.c b/coregrind/vg_libpthread.c index 11f5200a0d..f32c5533ea 100644 --- a/coregrind/vg_libpthread.c +++ b/coregrind/vg_libpthread.c @@ -23,7 +23,6 @@ #include "valgrind.h" /* For the request-passing mechanism */ #include "vg_include.h" /* For the VG_USERREQ__* constants */ -#include #include #include @@ -116,6 +115,14 @@ static void ignored ( char* msg ) Pass pthread_ calls to Valgrind's request mechanism. ------------------------------------------------------------------ */ +#include +#include +#include + +/* --------------------------------------------------- + THREAD ATTRIBUTES + ------------------------------------------------ */ + int pthread_attr_init(pthread_attr_t *attr) { ignored("pthread_attr_init"); @@ -129,6 +136,11 @@ int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) } + +/* --------------------------------------------------- + THREADs + ------------------------------------------------ */ + int pthread_create (pthread_t *__restrict __thread, __const pthread_attr_t *__restrict __attr, @@ -157,20 +169,6 @@ pthread_join (pthread_t __th, void **__thread_return) } -/* What are these? Anybody know? I don't. */ - -void _pthread_cleanup_push_defer ( void ) -{ - // char* str = "_pthread_cleanup_push_defer\n"; - // write(2, str, strlen(str)); -} - -void _pthread_cleanup_pop_restore ( void ) -{ - // char* str = "_pthread_cleanup_pop_restore\n"; - // write(2, str, strlen(str)); -} - static int thread_specific_errno[VG_N_THREADS]; @@ -188,33 +186,49 @@ int* __errno_location ( void ) } +/* --------------------------------------------------- + MUTEX ATTRIBUTES + ------------------------------------------------ */ + int pthread_mutexattr_init(pthread_mutexattr_t *attr) { - ignored("pthread_mutexattr_init"); + attr->__mutexkind = PTHREAD_MUTEX_ERRORCHECK_NP; return 0; } -int pthread_mutex_init(pthread_mutex_t *mutex, - const pthread_mutexattr_t *mutexattr) +int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) { - int res; - ensure_valgrind("pthread_mutex_init"); - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_MUTEX_INIT, - mutex, mutexattr, 0, 0); - return res; + switch (type) { + case PTHREAD_MUTEX_TIMED_NP: + case PTHREAD_MUTEX_RECURSIVE_NP: + case PTHREAD_MUTEX_ERRORCHECK_NP: + case PTHREAD_MUTEX_ADAPTIVE_NP: + attr->__mutexkind = type; + return 0; + default: + return EINVAL; + } } int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) { - ignored("pthread_mutexattr_destroy"); return 0; } -int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) + +/* --------------------------------------------------- + MUTEXes + ------------------------------------------------ */ + +int pthread_mutex_init(pthread_mutex_t *mutex, + const pthread_mutexattr_t *mutexattr) { - ignored("pthread_mutexattr_settype"); - return 0; + int res; + ensure_valgrind("pthread_mutex_init"); + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__PTHREAD_MUTEX_INIT, + mutex, mutexattr, 0, 0); + return res; } int pthread_mutex_lock(pthread_mutex_t *mutex) @@ -249,18 +263,6 @@ int pthread_mutex_unlock(pthread_mutex_t *mutex) } } -pthread_t pthread_self(void) -{ - int tid; - ensure_valgrind("pthread_self"); - VALGRIND_MAGIC_SEQUENCE(tid, 0 /* default */, - VG_USERREQ__PTHREAD_GET_THREADID, - 0, 0, 0, 0); - if (tid < 0 || tid >= VG_N_THREADS) - barf("pthread_self: invalid ThreadId"); - return tid; -} - int pthread_mutex_destroy(pthread_mutex_t *mutex) { int res; @@ -278,6 +280,10 @@ int pthread_mutex_destroy(pthread_mutex_t *mutex) } +/* --------------------------------------------------- + CANCELLATION + ------------------------------------------------ */ + int pthread_setcanceltype(int type, int *oldtype) { ignored("pthread_setcanceltype"); @@ -296,6 +302,9 @@ int pthread_cancel(pthread_t thread) } +/* --------------------------------------------------- + THREAD-SPECIFICs + ------------------------------------------------ */ int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *)) @@ -322,16 +331,47 @@ void * pthread_getspecific(pthread_key_t key) return NULL; } + +/* --------------------------------------------------- + MISC + ------------------------------------------------ */ + +/* What are these? Anybody know? I don't. */ + +void _pthread_cleanup_push_defer ( void ) +{ + // char* str = "_pthread_cleanup_push_defer\n"; + // write(2, str, strlen(str)); +} + +void _pthread_cleanup_pop_restore ( void ) +{ + // char* str = "_pthread_cleanup_pop_restore\n"; + // write(2, str, strlen(str)); +} + + +pthread_t pthread_self(void) +{ + int tid; + ensure_valgrind("pthread_self"); + VALGRIND_MAGIC_SEQUENCE(tid, 0 /* default */, + VG_USERREQ__PTHREAD_GET_THREADID, + 0, 0, 0, 0); + if (tid < 0 || tid >= VG_N_THREADS) + barf("pthread_self: invalid ThreadId"); + return tid; +} + + /* --------------------------------------------------------------------- These are here (I think) because they are deemed cancellation points by POSIX. For the moment we'll simply pass the call along to the corresponding thread-unaware (?) libc routine. ------------------------------------------------------------------ */ -#include #include #include -#include #include #include diff --git a/coregrind/vg_scheduler.c b/coregrind/vg_scheduler.c index 6e354e2843..fadde58fc4 100644 --- a/coregrind/vg_scheduler.c +++ b/coregrind/vg_scheduler.c @@ -110,10 +110,14 @@ typedef struct { /* Is this slot in use, or free? */ Bool in_use; - /* If in_use, is this mutex held by some thread, or not? */ - Bool held; - /* if held==True, owner indicates who by. */ + /* When == 0, indicated not locked. + When > 0, number of times it has been locked by the owner. */ + UInt count; + /* if count > 0, owner indicates who by. */ ThreadId owner; + /* True if a recursive mutex. When False, count must never + exceed 1. */ + Bool is_rec; } VgMutex; @@ -1470,21 +1474,27 @@ void do_pthread_create ( ThreadId parent_tid, */ static -void initialise_mutex ( ThreadId tid, pthread_mutex_t *mutex ) +void initialise_mutex ( ThreadId tid, + pthread_mutex_t *mutex ) { MutexId mid; Char msg_buf[100]; + Bool is_rec; /* vg_alloc_MutexId aborts if we can't allocate a mutex, for whatever reason. */ mid = vg_alloc_VgMutex(); vg_mutexes[mid].in_use = True; - vg_mutexes[mid].held = False; + vg_mutexes[mid].count = 0; vg_mutexes[mid].owner = VG_INVALID_THREADID; /* irrelevant */ + is_rec = mutex->__m_kind == PTHREAD_MUTEX_RECURSIVE_NP; + vg_mutexes[mid].is_rec = is_rec; + vg_mutexes[mid].count = 0; mutex->__m_reserved = mid; mutex->__m_count = 1; /* initialised */ if (VG_(clo_trace_pthread_level) >= 1) { - VG_(sprintf)(msg_buf, "(initialise mutex) (%p) -> %d", - mutex, mid ); + VG_(sprintf)(msg_buf, "(initialise mutex) (%p, %s) -> %d", + mutex, is_rec ? "RECURSIVE" : "NORMAL", + mid ); print_pthread_event(tid, msg_buf); } } @@ -1500,6 +1510,8 @@ void do_pthread_mutex_init ( ThreadId tid, /* Paranoia ... */ vg_assert(sizeof(pthread_mutex_t) >= sizeof(UInt)); + if (mutexattr) + mutex->__m_kind = mutexattr->__mutexkind; initialise_mutex(tid, mutex); if (VG_(clo_trace_pthread_level) >= 1) { @@ -1537,6 +1549,8 @@ void do_pthread_mutex_lock( ThreadId tid, pthread_mutex_t *mutex ) } if (mutex->__m_count == 0) { + /* The mutex->__m_kind will have been set by the static + initialisation. */ initialise_mutex(tid, mutex); } @@ -1558,27 +1572,43 @@ void do_pthread_mutex_lock( ThreadId tid, pthread_mutex_t *mutex ) /* Assume tid valid. */ vg_assert(vg_threads[tid].status == VgTs_Runnable); - if (vg_mutexes[mid].held) { + if (vg_mutexes[mid].count > 0) { + + /* Someone has it already. */ if (vg_mutexes[mid].owner == tid) { - vg_threads[tid].m_edx = EDEADLK; + /* It's locked -- by me! */ + if (vg_mutexes[mid].is_rec) { + /* return 0 (success). */ + vg_mutexes[mid].count++; + vg_threads[tid].m_edx = 0; + VG_(printf)("!!!!!! tid %d, mutex %d -> locked %d\n", + tid, mid, vg_mutexes[mid].count); + return; + } else { + vg_threads[tid].m_edx = EDEADLK; + return; + } + } else { + /* Someone else has it; we have to wait. */ + vg_threads[tid].status = VgTs_WaitMX; + vg_threads[tid].waited_on_mid = mid; + /* No assignment to %EDX, since we're blocking. */ + if (VG_(clo_trace_pthread_level) >= 1) { + VG_(sprintf)(msg_buf, "pthread_mutex_lock %d: BLOCK", + mid ); + print_pthread_event(tid, msg_buf); + } return; } - /* Someone else has it; we have to wait. */ - vg_threads[tid].status = VgTs_WaitMX; - vg_threads[tid].waited_on_mid = mid; - /* No assignment to %EDX, since we're blocking. */ - if (VG_(clo_trace_pthread_level) >= 1) { - VG_(sprintf)(msg_buf, "pthread_mutex_lock %d: BLOCK", - mid ); - print_pthread_event(tid, msg_buf); - } + } else { - /* We get it! */ - vg_mutexes[mid].held = True; + /* We get it! [for the first time]. */ + vg_mutexes[mid].count = 1; vg_mutexes[mid].owner = tid; /* return 0 (success). */ vg_threads[tid].m_edx = 0; } + } @@ -1612,11 +1642,25 @@ void do_pthread_mutex_unlock ( ThreadId tid, vg_assert(vg_threads[tid].status == VgTs_Runnable); /* Barf if we don't currently hold the mutex. */ - if (!vg_mutexes[mid].held || vg_mutexes[mid].owner != tid) { + if (vg_mutexes[mid].count == 0 /* nobody holds it */ + || vg_mutexes[mid].owner != tid /* we don't hold it */) { vg_threads[tid].m_edx = EPERM; return; } + /* If it's a multiply-locked recursive mutex, just decrement the + lock count and return. */ + if (vg_mutexes[mid].count > 1) { + vg_assert(vg_mutexes[mid].is_rec); + vg_mutexes[mid].count --; + vg_threads[tid].m_edx = 0; /* success */ + return; + } + + /* Now we're sure mid is locked exactly once, and by the thread who + is now doing an unlock on it. */ + vg_assert(vg_mutexes[mid].count == 1); + /* Find some arbitrary thread waiting on this mutex, and make it runnable. If none are waiting, mark the mutex as not held. */ for (i = 0; i < VG_N_THREADS; i++) { @@ -1630,10 +1674,11 @@ void do_pthread_mutex_unlock ( ThreadId tid, vg_assert(i <= VG_N_THREADS); if (i == VG_N_THREADS) { /* Nobody else is waiting on it. */ - vg_mutexes[mid].held = False; + vg_mutexes[mid].count = 0; } else { /* Notionally transfer the hold to thread i, whose pthread_mutex_lock() call now returns with 0 (success). */ + /* The .count is already == 1. */ vg_mutexes[mid].owner = i; vg_threads[i].status = VgTs_Runnable; vg_threads[i].m_edx = 0; /* pth_lock() success */ @@ -1679,7 +1724,7 @@ static void do_pthread_mutex_destroy ( ThreadId tid, vg_assert(vg_threads[tid].status == VgTs_Runnable); /* Barf if the mutex is currently held. */ - if (vg_mutexes[mid].held) { + if (vg_mutexes[mid].count > 0) { vg_threads[tid].m_edx = EBUSY; return; } diff --git a/vg_libpthread.c b/vg_libpthread.c index 11f5200a0d..f32c5533ea 100644 --- a/vg_libpthread.c +++ b/vg_libpthread.c @@ -23,7 +23,6 @@ #include "valgrind.h" /* For the request-passing mechanism */ #include "vg_include.h" /* For the VG_USERREQ__* constants */ -#include #include #include @@ -116,6 +115,14 @@ static void ignored ( char* msg ) Pass pthread_ calls to Valgrind's request mechanism. ------------------------------------------------------------------ */ +#include +#include +#include + +/* --------------------------------------------------- + THREAD ATTRIBUTES + ------------------------------------------------ */ + int pthread_attr_init(pthread_attr_t *attr) { ignored("pthread_attr_init"); @@ -129,6 +136,11 @@ int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) } + +/* --------------------------------------------------- + THREADs + ------------------------------------------------ */ + int pthread_create (pthread_t *__restrict __thread, __const pthread_attr_t *__restrict __attr, @@ -157,20 +169,6 @@ pthread_join (pthread_t __th, void **__thread_return) } -/* What are these? Anybody know? I don't. */ - -void _pthread_cleanup_push_defer ( void ) -{ - // char* str = "_pthread_cleanup_push_defer\n"; - // write(2, str, strlen(str)); -} - -void _pthread_cleanup_pop_restore ( void ) -{ - // char* str = "_pthread_cleanup_pop_restore\n"; - // write(2, str, strlen(str)); -} - static int thread_specific_errno[VG_N_THREADS]; @@ -188,33 +186,49 @@ int* __errno_location ( void ) } +/* --------------------------------------------------- + MUTEX ATTRIBUTES + ------------------------------------------------ */ + int pthread_mutexattr_init(pthread_mutexattr_t *attr) { - ignored("pthread_mutexattr_init"); + attr->__mutexkind = PTHREAD_MUTEX_ERRORCHECK_NP; return 0; } -int pthread_mutex_init(pthread_mutex_t *mutex, - const pthread_mutexattr_t *mutexattr) +int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) { - int res; - ensure_valgrind("pthread_mutex_init"); - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_MUTEX_INIT, - mutex, mutexattr, 0, 0); - return res; + switch (type) { + case PTHREAD_MUTEX_TIMED_NP: + case PTHREAD_MUTEX_RECURSIVE_NP: + case PTHREAD_MUTEX_ERRORCHECK_NP: + case PTHREAD_MUTEX_ADAPTIVE_NP: + attr->__mutexkind = type; + return 0; + default: + return EINVAL; + } } int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) { - ignored("pthread_mutexattr_destroy"); return 0; } -int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) + +/* --------------------------------------------------- + MUTEXes + ------------------------------------------------ */ + +int pthread_mutex_init(pthread_mutex_t *mutex, + const pthread_mutexattr_t *mutexattr) { - ignored("pthread_mutexattr_settype"); - return 0; + int res; + ensure_valgrind("pthread_mutex_init"); + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__PTHREAD_MUTEX_INIT, + mutex, mutexattr, 0, 0); + return res; } int pthread_mutex_lock(pthread_mutex_t *mutex) @@ -249,18 +263,6 @@ int pthread_mutex_unlock(pthread_mutex_t *mutex) } } -pthread_t pthread_self(void) -{ - int tid; - ensure_valgrind("pthread_self"); - VALGRIND_MAGIC_SEQUENCE(tid, 0 /* default */, - VG_USERREQ__PTHREAD_GET_THREADID, - 0, 0, 0, 0); - if (tid < 0 || tid >= VG_N_THREADS) - barf("pthread_self: invalid ThreadId"); - return tid; -} - int pthread_mutex_destroy(pthread_mutex_t *mutex) { int res; @@ -278,6 +280,10 @@ int pthread_mutex_destroy(pthread_mutex_t *mutex) } +/* --------------------------------------------------- + CANCELLATION + ------------------------------------------------ */ + int pthread_setcanceltype(int type, int *oldtype) { ignored("pthread_setcanceltype"); @@ -296,6 +302,9 @@ int pthread_cancel(pthread_t thread) } +/* --------------------------------------------------- + THREAD-SPECIFICs + ------------------------------------------------ */ int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *)) @@ -322,16 +331,47 @@ void * pthread_getspecific(pthread_key_t key) return NULL; } + +/* --------------------------------------------------- + MISC + ------------------------------------------------ */ + +/* What are these? Anybody know? I don't. */ + +void _pthread_cleanup_push_defer ( void ) +{ + // char* str = "_pthread_cleanup_push_defer\n"; + // write(2, str, strlen(str)); +} + +void _pthread_cleanup_pop_restore ( void ) +{ + // char* str = "_pthread_cleanup_pop_restore\n"; + // write(2, str, strlen(str)); +} + + +pthread_t pthread_self(void) +{ + int tid; + ensure_valgrind("pthread_self"); + VALGRIND_MAGIC_SEQUENCE(tid, 0 /* default */, + VG_USERREQ__PTHREAD_GET_THREADID, + 0, 0, 0, 0); + if (tid < 0 || tid >= VG_N_THREADS) + barf("pthread_self: invalid ThreadId"); + return tid; +} + + /* --------------------------------------------------------------------- These are here (I think) because they are deemed cancellation points by POSIX. For the moment we'll simply pass the call along to the corresponding thread-unaware (?) libc routine. ------------------------------------------------------------------ */ -#include #include #include -#include #include #include diff --git a/vg_scheduler.c b/vg_scheduler.c index 6e354e2843..fadde58fc4 100644 --- a/vg_scheduler.c +++ b/vg_scheduler.c @@ -110,10 +110,14 @@ typedef struct { /* Is this slot in use, or free? */ Bool in_use; - /* If in_use, is this mutex held by some thread, or not? */ - Bool held; - /* if held==True, owner indicates who by. */ + /* When == 0, indicated not locked. + When > 0, number of times it has been locked by the owner. */ + UInt count; + /* if count > 0, owner indicates who by. */ ThreadId owner; + /* True if a recursive mutex. When False, count must never + exceed 1. */ + Bool is_rec; } VgMutex; @@ -1470,21 +1474,27 @@ void do_pthread_create ( ThreadId parent_tid, */ static -void initialise_mutex ( ThreadId tid, pthread_mutex_t *mutex ) +void initialise_mutex ( ThreadId tid, + pthread_mutex_t *mutex ) { MutexId mid; Char msg_buf[100]; + Bool is_rec; /* vg_alloc_MutexId aborts if we can't allocate a mutex, for whatever reason. */ mid = vg_alloc_VgMutex(); vg_mutexes[mid].in_use = True; - vg_mutexes[mid].held = False; + vg_mutexes[mid].count = 0; vg_mutexes[mid].owner = VG_INVALID_THREADID; /* irrelevant */ + is_rec = mutex->__m_kind == PTHREAD_MUTEX_RECURSIVE_NP; + vg_mutexes[mid].is_rec = is_rec; + vg_mutexes[mid].count = 0; mutex->__m_reserved = mid; mutex->__m_count = 1; /* initialised */ if (VG_(clo_trace_pthread_level) >= 1) { - VG_(sprintf)(msg_buf, "(initialise mutex) (%p) -> %d", - mutex, mid ); + VG_(sprintf)(msg_buf, "(initialise mutex) (%p, %s) -> %d", + mutex, is_rec ? "RECURSIVE" : "NORMAL", + mid ); print_pthread_event(tid, msg_buf); } } @@ -1500,6 +1510,8 @@ void do_pthread_mutex_init ( ThreadId tid, /* Paranoia ... */ vg_assert(sizeof(pthread_mutex_t) >= sizeof(UInt)); + if (mutexattr) + mutex->__m_kind = mutexattr->__mutexkind; initialise_mutex(tid, mutex); if (VG_(clo_trace_pthread_level) >= 1) { @@ -1537,6 +1549,8 @@ void do_pthread_mutex_lock( ThreadId tid, pthread_mutex_t *mutex ) } if (mutex->__m_count == 0) { + /* The mutex->__m_kind will have been set by the static + initialisation. */ initialise_mutex(tid, mutex); } @@ -1558,27 +1572,43 @@ void do_pthread_mutex_lock( ThreadId tid, pthread_mutex_t *mutex ) /* Assume tid valid. */ vg_assert(vg_threads[tid].status == VgTs_Runnable); - if (vg_mutexes[mid].held) { + if (vg_mutexes[mid].count > 0) { + + /* Someone has it already. */ if (vg_mutexes[mid].owner == tid) { - vg_threads[tid].m_edx = EDEADLK; + /* It's locked -- by me! */ + if (vg_mutexes[mid].is_rec) { + /* return 0 (success). */ + vg_mutexes[mid].count++; + vg_threads[tid].m_edx = 0; + VG_(printf)("!!!!!! tid %d, mutex %d -> locked %d\n", + tid, mid, vg_mutexes[mid].count); + return; + } else { + vg_threads[tid].m_edx = EDEADLK; + return; + } + } else { + /* Someone else has it; we have to wait. */ + vg_threads[tid].status = VgTs_WaitMX; + vg_threads[tid].waited_on_mid = mid; + /* No assignment to %EDX, since we're blocking. */ + if (VG_(clo_trace_pthread_level) >= 1) { + VG_(sprintf)(msg_buf, "pthread_mutex_lock %d: BLOCK", + mid ); + print_pthread_event(tid, msg_buf); + } return; } - /* Someone else has it; we have to wait. */ - vg_threads[tid].status = VgTs_WaitMX; - vg_threads[tid].waited_on_mid = mid; - /* No assignment to %EDX, since we're blocking. */ - if (VG_(clo_trace_pthread_level) >= 1) { - VG_(sprintf)(msg_buf, "pthread_mutex_lock %d: BLOCK", - mid ); - print_pthread_event(tid, msg_buf); - } + } else { - /* We get it! */ - vg_mutexes[mid].held = True; + /* We get it! [for the first time]. */ + vg_mutexes[mid].count = 1; vg_mutexes[mid].owner = tid; /* return 0 (success). */ vg_threads[tid].m_edx = 0; } + } @@ -1612,11 +1642,25 @@ void do_pthread_mutex_unlock ( ThreadId tid, vg_assert(vg_threads[tid].status == VgTs_Runnable); /* Barf if we don't currently hold the mutex. */ - if (!vg_mutexes[mid].held || vg_mutexes[mid].owner != tid) { + if (vg_mutexes[mid].count == 0 /* nobody holds it */ + || vg_mutexes[mid].owner != tid /* we don't hold it */) { vg_threads[tid].m_edx = EPERM; return; } + /* If it's a multiply-locked recursive mutex, just decrement the + lock count and return. */ + if (vg_mutexes[mid].count > 1) { + vg_assert(vg_mutexes[mid].is_rec); + vg_mutexes[mid].count --; + vg_threads[tid].m_edx = 0; /* success */ + return; + } + + /* Now we're sure mid is locked exactly once, and by the thread who + is now doing an unlock on it. */ + vg_assert(vg_mutexes[mid].count == 1); + /* Find some arbitrary thread waiting on this mutex, and make it runnable. If none are waiting, mark the mutex as not held. */ for (i = 0; i < VG_N_THREADS; i++) { @@ -1630,10 +1674,11 @@ void do_pthread_mutex_unlock ( ThreadId tid, vg_assert(i <= VG_N_THREADS); if (i == VG_N_THREADS) { /* Nobody else is waiting on it. */ - vg_mutexes[mid].held = False; + vg_mutexes[mid].count = 0; } else { /* Notionally transfer the hold to thread i, whose pthread_mutex_lock() call now returns with 0 (success). */ + /* The .count is already == 1. */ vg_mutexes[mid].owner = i; vg_threads[i].status = VgTs_Runnable; vg_threads[i].m_edx = 0; /* pth_lock() success */ @@ -1679,7 +1724,7 @@ static void do_pthread_mutex_destroy ( ThreadId tid, vg_assert(vg_threads[tid].status == VgTs_Runnable); /* Barf if the mutex is currently held. */ - if (vg_mutexes[mid].held) { + if (vg_mutexes[mid].count > 0) { vg_threads[tid].m_edx = EBUSY; return; }