]> git.sesse.net Git - vlc/blobdiff - include/threads_funcs.h
Directory browsing and files from file list works. There is one caveat left when...
[vlc] / include / threads_funcs.h
index 07c03313061e79abf3344471e05ad46306be5b9e..8bb0d195c1b6de350611fafddef9d44a4ca840ed 100644 (file)
@@ -3,7 +3,7 @@
  * This header provides a portable threads implementation.
  *****************************************************************************
  * Copyright (C) 1999, 2000 VideoLAN
- * $Id: threads_funcs.h,v 1.4 2002/05/19 23:51:37 massiot Exp $
+ * $Id: threads_funcs.h,v 1.4.2.4 2002/07/30 07:54:05 gbazin Exp $
  *
  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
  *          Samuel Hocevar <sam@via.ecp.fr>
@@ -97,7 +97,7 @@ static inline int vlc_mutex_init( vlc_mutex_t *p_mutex )
      * function and have a 100% correct vlc_cond_wait() implementation.
      * As this function is not available on Win9x, we can use the faster
      * CriticalSections */
-    if( (GetVersion() < 0x80000000) && !p_main->p_sys->b_fast_pthread )
+    if( p_main->p_sys->SignalObjectAndWait && !p_main->p_sys->b_fast_mutex )
     {
         /* We are running on NT/2K/XP, we can use SignalObjectAndWait */
         p_mutex->mutex = CreateMutex( 0, FALSE, 0 );
@@ -105,8 +105,8 @@ static inline int vlc_mutex_init( vlc_mutex_t *p_mutex )
     }
     else
     {
-        InitializeCriticalSection( &p_mutex->csection );
         p_mutex->mutex = NULL;
+        InitializeCriticalSection( &p_mutex->csection );
         return 0;
     }
 
@@ -346,16 +346,38 @@ static inline int vlc_cond_init( vlc_cond_t *p_condvar )
     return ( *p_condvar == NULL ) ? errno : 0;
 
 #elif defined( WIN32 )
-    /* initialise counter */
+    /* Initialize counter */
     p_condvar->i_waiting_threads = 0;
 
-    /* Create an auto-reset event. */
-    p_condvar->signal = CreateEvent( NULL, /* no security */
-                                     FALSE,  /* auto-reset event */
-                                     FALSE,  /* non-signaled initially */
-                                     NULL ); /* unnamed */
+    if( p_main->p_sys->i_win9x_cv == 0 )
+    {
+        /* Create an auto-reset event. */
+        p_condvar->event = CreateEvent( NULL,   /* no security */
+                                        FALSE,  /* auto-reset event */
+                                        FALSE,  /* start non-signaled */
+                                        NULL ); /* unnamed */
+
+        p_condvar->semaphore = NULL;
+        return !p_condvar->event;
+    }
+    else
+    {
+        p_condvar->semaphore = CreateSemaphore( NULL,       /* no security */
+                                                0,          /* initial count */
+                                                0x7fffffff, /* max count */
+                                                NULL );     /* unnamed */
+
+        if( p_main->p_sys->i_win9x_cv == 1 )
+            /* Create a manual-reset event initially signaled. */
+            p_condvar->event = CreateEvent( NULL, TRUE, TRUE, NULL );
+        else
+            /* Create a auto-reset event. */
+            p_condvar->event = CreateEvent( NULL, FALSE, FALSE, NULL );
 
-    return( !p_condvar->signal );
+        InitializeCriticalSection( &p_condvar->csection );
+
+        return !p_condvar->semaphore || !p_condvar->event;
+    }
 
 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
     return pthread_cond_init( p_condvar, NULL );
@@ -402,7 +424,42 @@ static inline int vlc_cond_signal( vlc_cond_t *p_condvar )
     /* Release one waiting thread if one is available. */
     /* For this trick to work properly, the vlc_cond_signal must be surrounded
      * by a mutex. This will prevent another thread from stealing the signal */
-    PulseEvent( p_condvar->signal );
+
+    if( !p_condvar->semaphore )
+    {
+        PulseEvent( p_condvar->event );
+    }
+    else if( p_main->p_sys->i_win9x_cv == 1 )
+    {
+        /* Wait for the gate to be open */
+        WaitForSingleObject( p_condvar->event, INFINITE );
+
+        if( p_condvar->i_waiting_threads )
+        {
+            /* Using a semaphore exposes us to a race condition. It is
+             * possible for another thread to start waiting on the semaphore
+             * just after we signaled it and thus steal the signal.
+             * We have to prevent new threads from entering the cond_wait(). */
+            ResetEvent( p_condvar->event );
+
+            /* A semaphore is used here because Win9x doesn't have
+             * SignalObjectAndWait() and thus a race condition exists
+             * during the time we release the mutex and the time we start
+             * waiting on the event (more precisely, the signal can sometimes
+             * be missed by the waiting thread if we use PulseEvent()). */
+            ReleaseSemaphore( p_condvar->semaphore, 1, 0 );
+        }
+    }
+    else
+    {
+        if( p_condvar->i_waiting_threads )
+        {
+            ReleaseSemaphore( p_condvar->semaphore, 1, 0 );
+
+            /* Wait for the last thread to be awakened */
+            WaitForSingleObject( p_condvar->event, INFINITE );
+        }
+    }
     return 0;
 
 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
@@ -473,13 +530,36 @@ static inline int vlc_cond_broadcast( vlc_cond_t *p_condvar )
 
 #elif defined( WIN32 )
     /* Release all waiting threads. */
-    /* For this trick to work properly, the vlc_cond_signal must be surrounded
-     * by a mutex. This will prevent another thread from stealing the signal */
-    while( p_condvar->i_waiting_threads )
+    int i;
+
+    if( !p_condvar->semaphore )
+        for( i = p_condvar->i_waiting_threads; i > 0; i-- )
+            PulseEvent( p_condvar->event );
+    else if( p_main->p_sys->i_win9x_cv == 1 )
     {
-        PulseEvent( p_condvar->signal );
-        Sleep( 1 ); /* deschedule the current thread */
+        /* Wait for the gate to be open */
+        WaitForSingleObject( p_condvar->event, INFINITE );
+
+        if( p_condvar->i_waiting_threads )
+        {
+            /* close gate */
+            ResetEvent( p_condvar->event );
+
+            ReleaseSemaphore( p_condvar->semaphore,
+                              p_condvar->i_waiting_threads, 0 );
+        }
     }
+    else
+    {
+        if( p_condvar->i_waiting_threads )
+        {
+            ReleaseSemaphore( p_condvar->semaphore,
+                              p_condvar->i_waiting_threads, 0 );
+            /* Wait for the last thread to be awakened */
+            WaitForSingleObject( p_condvar->event, INFINITE );
+        }
+    }
+
     return 0;
 
 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
@@ -560,36 +640,77 @@ static inline int _vlc_cond_wait( char * psz_file, int i_line,
     return i_ret;
 
 #elif defined( WIN32 )
-    /* The ideal would be to use a function which atomically releases the
-     * mutex and initiate the waiting.
-     * Unfortunately only the SignalObjectAndWait function does this and it's
-     * only supported on WinNT/2K, furthermore it cannot take multiple
-     * events as parameters.
-     *
-     * The solution we use should however fulfill all our needs (even though
-     * it is not a correct pthreads implementation)
-     */
-    int i_result;
-
-    p_condvar->i_waiting_threads ++;
+    if( !p_condvar->semaphore )
+    {
+        /* Increase our wait count */
+        p_condvar->i_waiting_threads++;
+
+        if( p_main->p_sys->SignalObjectAndWait && p_mutex->mutex )
+        /* It is only possible to atomically release the mutex and initiate the
+         * waiting on WinNT/2K/XP. Win9x doesn't have SignalObjectAndWait(). */
+            p_main->p_sys->SignalObjectAndWait( p_mutex->mutex,
+                                                p_condvar->event,
+                                                INFINITE, FALSE );
+        else
+        {
+            LeaveCriticalSection( &p_mutex->csection );
+            WaitForSingleObject( p_condvar->event, INFINITE );
+        }
 
-    if( p_mutex->mutex )
+        p_condvar->i_waiting_threads--;
+    }
+    else if( p_main->p_sys->i_win9x_cv == 1 )
     {
-        p_main->p_sys->SignalObjectAndWait( p_mutex->mutex, p_condvar->signal,
-                                            INFINITE, FALSE );
+        int i_waiting_threads;
+
+        /* Wait for the gate to be open */
+        WaitForSingleObject( p_condvar->event, INFINITE );
+
+        /* Increase our wait count */
+        p_condvar->i_waiting_threads++;
+
+        LeaveCriticalSection( &p_mutex->csection );
+        WaitForSingleObject( p_condvar->semaphore, INFINITE );
+
+        /* Decrement and test must be atomic */
+        EnterCriticalSection( &p_condvar->csection );
+
+        /* Decrease our wait count */
+        i_waiting_threads = --p_condvar->i_waiting_threads;
+
+        LeaveCriticalSection( &p_condvar->csection );
+
+        /* Reopen the gate if we were the last waiting thread */
+        if( !i_waiting_threads )
+            SetEvent( p_condvar->event );
     }
     else
     {
-        /* Release the mutex */
-        vlc_mutex_unlock( p_mutex );
-        i_result = WaitForSingleObject( p_condvar->signal, INFINITE); 
-        p_condvar->i_waiting_threads --;
+        int i_waiting_threads;
+
+        /* Increase our wait count */
+        p_condvar->i_waiting_threads++;
+
+        LeaveCriticalSection( &p_mutex->csection );
+        WaitForSingleObject( p_condvar->semaphore, INFINITE );
+
+        /* Decrement and test must be atomic */
+        EnterCriticalSection( &p_condvar->csection );
+
+        /* Decrease our wait count */
+        i_waiting_threads = --p_condvar->i_waiting_threads;
+
+        LeaveCriticalSection( &p_condvar->csection );
+
+        /* Signal that the last waiting thread just went through */
+        if( !i_waiting_threads )
+            SetEvent( p_condvar->event );
     }
 
     /* Reacquire the mutex before returning. */
     vlc_mutex_lock( p_mutex );
 
-    return( i_result == WAIT_FAILED );
+    return 0;
 
 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
 
@@ -680,7 +801,11 @@ static inline int _vlc_cond_destroy( char * psz_file, int i_line,
     return st_cond_destroy( *p_condvar );
 
 #elif defined( WIN32 )
-    return( !CloseHandle( p_condvar->signal ) );
+    if( !p_condvar->semaphore )
+        return !CloseHandle( p_condvar->event );
+    else
+        return !CloseHandle( p_condvar->event )
+          || !CloseHandle( p_condvar->semaphore );
 
 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
     int i_result = pthread_cond_destroy( p_condvar );