winxp threads: detect SRWLock emulation reentrancy

We lack SRWLock on Windows XP, so we use CRITICAL_SECTION to emulate it
there.  SRWLock is non-recursive, but CRITICAL_SECTION will happily
allow itself to be acquired multiple times by the same thread.

We need to detect if our second acquire succeeded because of the
recursive nature of CRITICAL_SECTION.  In the case of a _lock()
operation, we would normally have deadlocked, so abort.  In the case of
a _trylock() operation, we need to ensure that FALSE is properly
returned.

Problem caught by Matthias Clasen and Chun-wei Fan.

https://bugzilla.gnome.org/show_bug.cgi?id=660096
This commit is contained in:
Ryan Lortie 2011-09-26 04:44:41 -04:00
parent 5ef7b964e5
commit fdc594e963

View File

@ -645,6 +645,7 @@ typedef struct
{
CRITICAL_SECTION writer_lock;
gboolean ever_shared; /* protected by writer_lock */
gboolean writer_locked; /* protected by writer_lock */
/* below is only ever touched if ever_shared becomes true */
CRITICAL_SECTION atomicity;
@ -695,6 +696,7 @@ g_thread_xp_get_srwlock (GThreadSRWLock * volatile *lock)
g_thread_abort (errno, "malloc");
InitializeCriticalSection (&result->writer_lock);
result->writer_locked = FALSE;
result->ever_shared = FALSE;
*lock = result;
@ -711,6 +713,12 @@ g_thread_xp_AcquireSRWLockExclusive (gpointer mutex)
EnterCriticalSection (&lock->writer_lock);
/* CRITICAL_SECTION is reentrant, but SRWLock is not.
* Detect the deadlock that would occur on later Windows version.
*/
g_assert (!lock->writer_locked);
lock->writer_locked = TRUE;
if (lock->ever_shared)
{
GThreadXpWaiter *waiter = NULL;
@ -735,6 +743,17 @@ g_thread_xp_TryAcquireSRWLockExclusive (gpointer mutex)
if (!TryEnterCriticalSection (&lock->writer_lock))
return FALSE;
/* CRITICAL_SECTION is reentrant, but SRWLock is not.
* Ensure that this properly returns FALSE (as SRWLock would).
*/
if G_UNLIKELY (lock->writer_locked)
{
LeaveCriticalSection (&lock->writer_lock);
return FALSE;
}
lock->writer_locked = TRUE;
if (lock->ever_shared)
{
gboolean available;
@ -758,6 +777,8 @@ g_thread_xp_ReleaseSRWLockExclusive (gpointer mutex)
{
GThreadSRWLock *lock = *(GThreadSRWLock * volatile *) mutex;
lock->writer_locked = FALSE;
/* We need this until we fix some weird parts of GLib that try to
* unlock freshly-allocated mutexes.
*/
@ -789,6 +810,9 @@ g_thread_xp_AcquireSRWLockShared (gpointer mutex)
EnterCriticalSection (&lock->writer_lock);
/* See g_thread_xp_AcquireSRWLockExclusive */
g_assert (!lock->writer_locked);
g_thread_xp_srwlock_become_reader (lock);
LeaveCriticalSection (&lock->writer_lock);
@ -802,6 +826,13 @@ g_thread_xp_TryAcquireSRWLockShared (gpointer mutex)
if (!TryEnterCriticalSection (&lock->writer_lock))
return FALSE;
/* See g_thread_xp_AcquireSRWLockExclusive */
if G_UNLIKELY (lock->writer_locked)
{
LeaveCriticalSection (&lock->writer_lock);
return FALSE;
}
g_thread_xp_srwlock_become_reader (lock);
LeaveCriticalSection (&lock->writer_lock);