Commit cbea1a49 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

Win32: rework mutex/condition implementation.

Get rid of unsafe PulseEvent().
Fix recursive mutex implementation (hopefully).
Use critical section (fast non-shared/intra-process mutexes)
 rather than mutex handle (slow shared/inter-process mutexes) [1].
Do not rely on unspecified locking when SignalObjectAndWait is alerted.
Real vlc_cond_broadcast() support (hopefully).

[1] should also merge the WinCE support with WinNT.
parent 0f58689c
...@@ -109,7 +109,7 @@ typedef pthread_mutex_t vlc_mutex_t; ...@@ -109,7 +109,7 @@ typedef pthread_mutex_t vlc_mutex_t;
typedef pthread_cond_t vlc_cond_t; typedef pthread_cond_t vlc_cond_t;
typedef pthread_key_t vlc_threadvar_t; typedef pthread_key_t vlc_threadvar_t;
#elif defined( WIN32 ) || defined( UNDER_CE ) #elif defined( WIN32 )
typedef struct typedef struct
{ {
HANDLE handle; HANDLE handle;
...@@ -117,7 +117,14 @@ typedef struct ...@@ -117,7 +117,14 @@ typedef struct
void *data; void *data;
} *vlc_thread_t; } *vlc_thread_t;
typedef HANDLE vlc_mutex_t; typedef struct
{
CRITICAL_SECTION mutex;
LONG owner;
unsigned recursion;
bool recursive;
}
vlc_mutex_t;
typedef HANDLE vlc_cond_t; typedef HANDLE vlc_cond_t;
typedef DWORD vlc_threadvar_t; typedef DWORD vlc_threadvar_t;
......
...@@ -267,13 +267,11 @@ int vlc_mutex_init( vlc_mutex_t *p_mutex ) ...@@ -267,13 +267,11 @@ int vlc_mutex_init( vlc_mutex_t *p_mutex )
i_result = pthread_mutex_init( p_mutex, &attr ); i_result = pthread_mutex_init( p_mutex, &attr );
pthread_mutexattr_destroy( &attr ); pthread_mutexattr_destroy( &attr );
return i_result; return i_result;
#elif defined( UNDER_CE )
InitializeCriticalSection( &p_mutex->csection );
return 0;
#elif defined( WIN32 ) #elif defined( WIN32 )
*p_mutex = CreateMutex( NULL, FALSE, NULL ); InitializeCriticalSection (&p_mutex->mutex);
return (*p_mutex != NULL) ? 0 : ENOMEM; p_mutex->recursive = false;
return 0;
#endif #endif
} }
...@@ -296,12 +294,14 @@ int vlc_mutex_init_recursive( vlc_mutex_t *p_mutex ) ...@@ -296,12 +294,14 @@ int vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
i_result = pthread_mutex_init( p_mutex, &attr ); i_result = pthread_mutex_init( p_mutex, &attr );
pthread_mutexattr_destroy( &attr ); pthread_mutexattr_destroy( &attr );
return( i_result ); return( i_result );
#elif defined( WIN32 ) #elif defined( WIN32 )
/* Create mutex returns a recursive mutex */ InitializeCriticalSection( &p_mutex->mutex );
*p_mutex = CreateMutex( 0, FALSE, 0 ); p_mutex->owner = 0; /* the error value for GetThreadId()! */
return (*p_mutex != NULL) ? 0 : ENOMEM; p_mutex->recursion = 0;
#else p_mutex->recursive = true;
# error Unimplemented! return 0;
#endif #endif
} }
...@@ -318,11 +318,8 @@ void vlc_mutex_destroy (vlc_mutex_t *p_mutex) ...@@ -318,11 +318,8 @@ void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
int val = pthread_mutex_destroy( p_mutex ); int val = pthread_mutex_destroy( p_mutex );
VLC_THREAD_ASSERT ("destroying mutex"); VLC_THREAD_ASSERT ("destroying mutex");
#elif defined( UNDER_CE )
DeleteCriticalSection( &p_mutex->csection );
#elif defined( WIN32 ) #elif defined( WIN32 )
CloseHandle( *p_mutex ); DeleteCriticalSection (&p_mutex->mutex);
#endif #endif
} }
...@@ -341,11 +338,26 @@ void vlc_mutex_lock (vlc_mutex_t *p_mutex) ...@@ -341,11 +338,26 @@ void vlc_mutex_lock (vlc_mutex_t *p_mutex)
int val = pthread_mutex_lock( p_mutex ); int val = pthread_mutex_lock( p_mutex );
VLC_THREAD_ASSERT ("locking mutex"); VLC_THREAD_ASSERT ("locking mutex");
#elif defined( UNDER_CE )
EnterCriticalSection( &p_mutex->csection );
#elif defined( WIN32 ) #elif defined( WIN32 )
WaitForSingleObject( *p_mutex, INFINITE ); if (p_mutex->recursive)
{
DWORD self = GetCurrentThreadId ();
if ((DWORD)InterlockedCompareExchange (&p_mutex->owner, self,
self) == self)
{ /* We already locked this recursive mutex */
p_mutex->recursion++;
return;
}
/* We need to lock this recursive mutex */
EnterCriticalSection (&p_mutex->mutex);
self = InterlockedCompareExchange (&p_mutex->owner, self, 0);
assert (self == 0); /* no previous owner */
return;
}
/* Non-recursive mutex */
EnterCriticalSection (&p_mutex->mutex);
#endif #endif
} }
...@@ -361,11 +373,19 @@ void vlc_mutex_unlock (vlc_mutex_t *p_mutex) ...@@ -361,11 +373,19 @@ void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
int val = pthread_mutex_unlock( p_mutex ); int val = pthread_mutex_unlock( p_mutex );
VLC_THREAD_ASSERT ("unlocking mutex"); VLC_THREAD_ASSERT ("unlocking mutex");
#elif defined( UNDER_CE )
LeaveCriticalSection( &p_mutex->csection );
#elif defined( WIN32 ) #elif defined( WIN32 )
ReleaseMutex( *p_mutex ); if (p_mutex->recursive)
{
if (--p_mutex->recursion != 0)
return; /* We still own this mutex */
/* We release the mutex */
DWORD self = GetCurrentThreadId ();
self = InterlockedCompareExchange (&p_mutex->owner, self, 0);
assert (self == GetCurrentThreadId ());
/* fall through */
}
LeaveCriticalSection (&p_mutex->mutex);
#endif #endif
} }
...@@ -396,12 +416,9 @@ int vlc_cond_init( vlc_cond_t *p_condvar ) ...@@ -396,12 +416,9 @@ int vlc_cond_init( vlc_cond_t *p_condvar )
pthread_condattr_destroy (&attr); pthread_condattr_destroy (&attr);
return ret; return ret;
#elif defined( UNDER_CE ) || defined( WIN32 ) #elif defined( WIN32 )
/* Create an auto-reset event. */ /* Create a manual-reset event (manual reset is needed for broadcast). */
*p_condvar = CreateEvent( NULL, /* no security */ *p_condvar = CreateEvent( NULL, TRUE, FALSE, NULL );
FALSE, /* auto-reset event */
FALSE, /* start non-signaled */
NULL ); /* unnamed */
return *p_condvar ? 0 : ENOMEM; return *p_condvar ? 0 : ENOMEM;
#endif #endif
...@@ -418,7 +435,7 @@ void vlc_cond_destroy (vlc_cond_t *p_condvar) ...@@ -418,7 +435,7 @@ void vlc_cond_destroy (vlc_cond_t *p_condvar)
int val = pthread_cond_destroy( p_condvar ); int val = pthread_cond_destroy( p_condvar );
VLC_THREAD_ASSERT ("destroying condition"); VLC_THREAD_ASSERT ("destroying condition");
#elif defined( UNDER_CE ) || defined( WIN32 ) #elif defined( WIN32 )
CloseHandle( *p_condvar ); CloseHandle( *p_condvar );
#endif #endif
...@@ -434,14 +451,13 @@ void vlc_cond_signal (vlc_cond_t *p_condvar) ...@@ -434,14 +451,13 @@ void vlc_cond_signal (vlc_cond_t *p_condvar)
int val = pthread_cond_signal( p_condvar ); int val = pthread_cond_signal( p_condvar );
VLC_THREAD_ASSERT ("signaling condition variable"); VLC_THREAD_ASSERT ("signaling condition variable");
#elif defined( UNDER_CE ) || defined( WIN32 ) #elif defined( WIN32 )
/* Release one waiting thread if one is available. */ /* NOTE: This will cause a broadcast, that is wrong.
/* For this trick to work properly, the vlc_cond_signal must be surrounded * This will also wake up the next waiting thread if no thread are yet
* by a mutex. This will prevent another thread from stealing the signal */ * waiting, which is also wrong. However both of these issues are allowed
/* PulseEvent() only works if none of the waiting threads is suspended. * by the provision for spurious wakeups. Better have too many wakeups
* This is particularily problematic under a debug session. * than too few (= deadlocks). */
* as documented in http://support.microsoft.com/kb/q173260/ */ SetEvent (*p_condvar);
PulseEvent( *p_condvar );
#endif #endif
} }
...@@ -461,6 +477,11 @@ void vlc_cond_broadcast (vlc_cond_t *p_condvar) ...@@ -461,6 +477,11 @@ void vlc_cond_broadcast (vlc_cond_t *p_condvar)
#endif #endif
} }
#ifdef UNDER_CE
# warning FIXME
# define WaitForMultipleObjectsEx(a,b,c) WaitForMultipleObjects(a,b)
#endif
/** /**
* Waits for a condition variable. The calling thread will be suspended until * Waits for a condition variable. The calling thread will be suspended until
* another thread calls vlc_cond_signal() or vlc_cond_broadcast() on the same * another thread calls vlc_cond_signal() or vlc_cond_broadcast() on the same
...@@ -501,21 +522,16 @@ void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex) ...@@ -501,21 +522,16 @@ void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
int val = pthread_cond_wait( p_condvar, p_mutex ); int val = pthread_cond_wait( p_condvar, p_mutex );
VLC_THREAD_ASSERT ("waiting on condition"); VLC_THREAD_ASSERT ("waiting on condition");
#elif defined( UNDER_CE )
LeaveCriticalSection( &p_mutex->csection );
WaitForSingleObject( *p_condvar, INFINITE );
/* Reacquire the mutex before returning. */
vlc_mutex_lock( p_mutex );
#elif defined( WIN32 ) #elif defined( WIN32 )
DWORD result;
do do
{
vlc_testcancel (); vlc_testcancel ();
while (SignalObjectAndWait (*p_mutex, *p_condvar, INFINITE, TRUE) LeaveCriticalSection (&p_mutex->mutex);
== WAIT_IO_COMPLETION); result = WaitForSingleObjectEx (*p_condvar, INFINITE, TRUE);
EnterCriticalSection (&p_mutex->mutex);
/* Reacquire the mutex before returning. */ }
vlc_mutex_lock( p_mutex ); while (result == WAIT_IO_COMPLETION);
#endif #endif
} }
...@@ -543,20 +559,6 @@ int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex, ...@@ -543,20 +559,6 @@ int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
VLC_THREAD_ASSERT ("timed-waiting on condition"); VLC_THREAD_ASSERT ("timed-waiting on condition");
return val; return val;
#elif defined( UNDER_CE )
mtime_t delay_ms = (deadline - mdate()) / (CLOCK_FREQ / 1000);
DWORD result;
if( delay_ms < 0 )
delay_ms = 0;
LeaveCriticalSection( &p_mutex->csection );
result = WaitForSingleObject( *p_condvar, delay_ms );
/* Reacquire the mutex before returning. */
vlc_mutex_lock( p_mutex );
return (result == WAIT_TIMEOUT) ? ETIMEDOUT : 0;
#elif defined( WIN32 ) #elif defined( WIN32 )
DWORD result; DWORD result;
...@@ -569,12 +571,12 @@ int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex, ...@@ -569,12 +571,12 @@ int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
total = 0; total = 0;
DWORD delay = (total > 0x7fffffff) ? 0x7fffffff : total; DWORD delay = (total > 0x7fffffff) ? 0x7fffffff : total;
result = SignalObjectAndWait (*p_mutex, *p_condvar, delay, TRUE); LeaveCriticalSection (&p_mutex->mutex);
result = WaitForSingleObjectEx (*p_condvar, delay, TRUE);
EnterCriticalSection (&p_mutex->mutex);
} }
while (result == WAIT_IO_COMPLETION); while (result == WAIT_IO_COMPLETION);
/* Reacquire the mutex before return/cancel. */
vlc_mutex_lock (p_mutex);
return (result == WAIT_OBJECT_0) ? 0 : ETIMEDOUT; return (result == WAIT_OBJECT_0) ? 0 : ETIMEDOUT;
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment