]> git.sesse.net Git - vlc/blobdiff - src/misc/threads.c
* ALL: changed "struct foo_s" into "struct foo_t" to make greppers happy.
[vlc] / src / misc / threads.c
index 3500b05f89294d54d329564b89d82bfa48f9ceb3..02dcb2f53c62fb62a2408ebd7bd7763d99648d5a 100644 (file)
@@ -2,7 +2,7 @@
  * threads.c : threads implementation for the VideoLAN client
  *****************************************************************************
  * Copyright (C) 1999, 2000, 2001, 2002 VideoLAN
- * $Id: threads.c,v 1.4 2002/06/01 18:04:49 sam Exp $
+ * $Id: threads.c,v 1.10 2002/07/20 18:01:43 sam Exp $
  *
  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
  *          Samuel Hocevar <sam@zoy.org>
@@ -51,7 +51,7 @@ struct itimerval
 int setitimer(int kind, const struct itimerval* itnew, struct itimerval* itold);
 #   endif /* WIN32 */
 
-typedef struct wrapper_s
+typedef struct wrapper_t
 {
     /* Data lock access */
     vlc_mutex_t lock;
@@ -68,21 +68,50 @@ typedef struct wrapper_s
 
 #endif /* GPROF */
 
+/*****************************************************************************
+ * Global mutexes for lazy initialization of the threads system
+ *****************************************************************************/
+static volatile int i_initializations = 0;
+
+#if defined( PTH_INIT_IN_PTH_H )
+    /* Unimplemented */
+#elif defined( ST_INIT_IN_ST_H )
+    /* Unimplemented */
+#elif defined( WIN32 )
+    /* Unimplemented */
+#elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
+    static pthread_mutex_t once_mutex = PTHREAD_MUTEX_INITIALIZER;
+#elif defined( HAVE_CTHREADS_H )
+    /* Unimplemented */
+#elif defined( HAVE_KERNEL_SCHEDULER_H )
+    /* Unimplemented */
+#endif
+
 /*****************************************************************************
  * vlc_threads_init: initialize threads system
+ *****************************************************************************
+ * This function requires lazy initialization of a global lock in order to
+ * keep the library really thread-safe. Some architectures don't support this
+ * and thus do not guarantee the complete reentrancy.
  *****************************************************************************/
 int __vlc_threads_init( vlc_object_t *p_this )
 {
-    /* FIXME: this is definitely _not_ threadsafe, but at least it works
-     * under all implementations. We should for instance use pthread_once
-     * for lazy initialization of the global lock. */
-    static int i_status = VLC_THREADS_UNINITIALIZED;
-    int i_ret;
+    static volatile int i_status = VLC_THREADS_UNINITIALIZED;
+    int i_ret = 0;
 
-    if( i_status == VLC_THREADS_READY )
-    {
-        return 0;
-    }
+#if defined( PTH_INIT_IN_PTH_H )
+    /* Unimplemented */
+#elif defined( ST_INIT_IN_ST_H )
+    /* Unimplemented */
+#elif defined( WIN32 )
+    /* Unimplemented */
+#elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
+    pthread_mutex_lock( &once_mutex );
+#elif defined( HAVE_CTHREADS_H )
+    /* Unimplemented */
+#elif defined( HAVE_KERNEL_SCHEDULER_H )
+    /* Unimplemented */
+#endif
 
     if( i_status == VLC_THREADS_UNINITIALIZED )
     {
@@ -90,35 +119,49 @@ int __vlc_threads_init( vlc_object_t *p_this )
 
 #if defined( PTH_INIT_IN_PTH_H )
         i_ret = pth_init();
-
 #elif defined( ST_INIT_IN_ST_H )
         i_ret = st_init();
-
 #elif defined( WIN32 )
-        i_ret = 0;
-
+        /* Unimplemented */
 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
-        i_ret = 0;
-
+        /* Unimplemented */
 #elif defined( HAVE_CTHREADS_H )
-        i_ret = 0;
-
+        /* Unimplemented */
 #elif defined( HAVE_KERNEL_SCHEDULER_H )
-        i_ret = 0;
-
+        /* Unimplemented */
 #endif
+
         if( i_ret )
         {
             i_status = VLC_THREADS_ERROR;
-            return i_ret;
         }
+        else
+        {
+            vlc_mutex_init( p_this, p_this->p_vlc->p_global_lock );
+            i_status = VLC_THREADS_READY;
+        }
+    }
+    else
+    {
+        i_ret = ( i_status == VLC_THREADS_READY );
+    }
 
-        vlc_mutex_init( p_this, p_this->p_vlc->p_global_lock );
-
-        i_status = VLC_THREADS_READY;
+    i_initializations++;
 
-        return i_ret;
-    }
+#if defined( PTH_INIT_IN_PTH_H )
+    /* Unimplemented */
+#elif defined( ST_INIT_IN_ST_H )
+    /* Unimplemented */
+#elif defined( WIN32 )
+    /* Unimplemented */
+#elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
+    pthread_mutex_unlock( &once_mutex );
+    return i_ret;
+#elif defined( HAVE_CTHREADS_H )
+    /* Unimplemented */
+#elif defined( HAVE_KERNEL_SCHEDULER_H )
+    /* Unimplemented */
+#endif
 
     /* Wait until the other thread has initialized the thread library */
     while( i_status == VLC_THREADS_PENDING )
@@ -126,30 +169,45 @@ int __vlc_threads_init( vlc_object_t *p_this )
         msleep( THREAD_SLEEP );
     }
 
-    return( i_status == VLC_THREADS_READY );
+    return i_status == VLC_THREADS_READY;
 }
 
 /*****************************************************************************
  * vlc_threads_end: stop threads system
+ *****************************************************************************
+ * FIXME: This function is far from being threadsafe. We should undo exactly
+ * what we did above in vlc_threads_init.
  *****************************************************************************/
-int vlc_threads_end( void )
+int __vlc_threads_end( vlc_object_t *p_this )
 {
 #if defined( PTH_INIT_IN_PTH_H )
-    return pth_kill();
+    i_initializations--;
+    if( i_initializations == 0 )
+    {
+        return pth_kill();
+    }
+    return 0;
 
 #elif defined( ST_INIT_IN_ST_H )
+    i_initializations--;
     return 0;
 
 #elif defined( WIN32 )
+    i_initializations--;
     return 0;
 
 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
+    pthread_mutex_lock( &once_mutex );
+    i_initializations--;
+    pthread_mutex_unlock( &once_mutex );
     return 0;
 
 #elif defined( HAVE_CTHREADS_H )
+    i_initializations--;
     return 0;
 
 #elif defined( HAVE_KERNEL_SCHEDULER_H )
+    i_initializations--;
     return 0;
 
 #endif
@@ -176,13 +234,12 @@ int __vlc_mutex_init( vlc_object_t *p_this, vlc_mutex_t *p_mutex )
     {
         /* We are running on NT/2K/XP, we can use SignalObjectAndWait */
         p_mutex->mutex = CreateMutex( 0, FALSE, 0 );
-        p_mutex->SignalObjectAndWait = p_this->p_vlc->SignalObjectAndWait;
         return ( p_mutex->mutex ? 0 : 1 );
     }
     else
     {
-        InitializeCriticalSection( &p_mutex->csection );
         p_mutex->mutex = NULL;
+        InitializeCriticalSection( &p_mutex->csection );
         return 0;
     }
 
@@ -278,7 +335,7 @@ int __vlc_mutex_destroy( char * psz_file, int i_line, vlc_mutex_t *p_mutex )
 /*****************************************************************************
  * vlc_cond_init: initialize a condition
  *****************************************************************************/
-int vlc_cond_init( vlc_cond_t *p_condvar )
+int __vlc_cond_init( vlc_object_t *p_this, vlc_cond_t *p_condvar )
 {
 #if defined( PTH_INIT_IN_PTH_H )
     return pth_cond_init( p_condvar );
@@ -288,16 +345,32 @@ 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( (GetVersion() < 0x80000000) && !p_this->p_vlc->b_fast_pthread )
+    {
+        /* Create an auto-reset event and a semaphore. */
+        p_condvar->signal = CreateEvent( NULL, FALSE, FALSE, NULL );
+        p_condvar->semaphore = CreateSemaphore( NULL, 0, 0x7fffffff, NULL );
+
+        p_condvar->b_broadcast = 0;
+
+        /* We are running on NT/2K/XP, we can use SignalObjectAndWait */
+        p_condvar->SignalObjectAndWait = p_this->p_vlc->SignalObjectAndWait;
+
+        return !p_condvar->signal || !p_condvar->semaphore;
+    }
+    else
+    {
+        p_condvar->signal = NULL;
 
-    return( !p_condvar->signal );
+        /* Create an auto-reset event and a manual-reset event. */
+        p_condvar->p_events[0] = CreateEvent( NULL, FALSE, FALSE, NULL );
+        p_condvar->p_events[1] = CreateEvent( NULL, TRUE, FALSE, NULL );
+
+        return !p_condvar->p_events[0] || !p_condvar->p_events[1];
+    }
 
 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
     return pthread_cond_init( p_condvar, NULL );
@@ -341,7 +414,16 @@ int __vlc_cond_destroy( char * psz_file, int i_line, vlc_cond_t *p_condvar )
     return st_cond_destroy( *p_condvar );
 
 #elif defined( WIN32 )
-    return( !CloseHandle( p_condvar->signal ) );
+    if( p_condvar->signal )
+    {
+        return !CloseHandle( p_condvar->signal )
+                 || !CloseHandle( p_condvar->semaphore );
+    }
+    else
+    {
+        return !CloseHandle( p_condvar->p_events[0] )
+                 || !CloseHandle( p_condvar->p_events[1] );
+    }
 
 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
     int i_result = pthread_cond_destroy( p_condvar );
@@ -371,9 +453,7 @@ int __vlc_thread_create( vlc_object_t *p_this, char * psz_file, int i_line,
 {
     int i_ret;
 
-    vlc_mutex_init( p_this, &p_this->thread_lock );
-    vlc_cond_init( &p_this->thread_wait );
-    vlc_mutex_lock( &p_this->thread_lock );
+    vlc_mutex_lock( &p_this->object_lock );
 
 #ifdef GPROF
     wrapper_t wrapper;
@@ -383,7 +463,7 @@ int __vlc_thread_create( vlc_object_t *p_this, char * psz_file, int i_line,
     wrapper.p_data = (void *)p_this;
     getitimer( ITIMER_PROF, &wrapper.itimer );
     vlc_mutex_init( p_this, &wrapper.lock );
-    vlc_cond_init( &wrapper.wait );
+    vlc_cond_init( p_this, &wrapper.wait );
     vlc_mutex_lock( &wrapper.lock );
 
     /* Alter user-passed data so that we call the wrapper instead
@@ -404,13 +484,13 @@ int __vlc_thread_create( vlc_object_t *p_this, char * psz_file, int i_line,
     {
         unsigned threadID;
         /* When using the MSVCRT C library you have to use the _beginthreadex
-         * function instead of CreateThread, otherwise you'll end up with memory
-         * leaks and the signal functions not working */
+         * function instead of CreateThread, otherwise you'll end up with
+        * memory leaks and the signal functions not working */
         p_this->thread_id =
                 (HANDLE)_beginthreadex( NULL, 0, (PTHREAD_START) func, 
                                         (void *)p_this, 0, &threadID );
     }
-    
+
     i_ret = ( p_this->thread_id ? 0 : 1 );
 
 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
@@ -440,27 +520,24 @@ int __vlc_thread_create( vlc_object_t *p_this, char * psz_file, int i_line,
 
     if( i_ret == 0 )
     {
-        msg_Dbg( p_this, "thread %d (%s) created (%s:%d)",
-                         p_this->thread_id, psz_name, psz_file, i_line );
-
-        p_this->b_thread = 1;
-        p_this->i_thread = p_this->thread_id;   /* We hope the cast will work */
-
         if( b_wait )
         {
             msg_Dbg( p_this, "waiting for thread completion" );
-            vlc_cond_wait( &p_this->thread_wait, &p_this->thread_lock );
+            vlc_cond_wait( &p_this->object_wait, &p_this->object_lock );
         }
 
-        vlc_mutex_unlock( &p_this->thread_lock );
+        p_this->b_thread = 1;
+
+        msg_Dbg( p_this, "thread %d (%s) created (%s:%d)",
+                         p_this->thread_id, psz_name, psz_file, i_line );
+
+        vlc_mutex_unlock( &p_this->object_lock );
     }
     else
     {
         msg_Err( p_this, "%s thread could not be created at %s:%d (%s)",
                          psz_name, psz_file, i_line, strerror(i_ret) );
-        vlc_mutex_unlock( &p_this->thread_lock );
-        vlc_mutex_destroy( &p_this->thread_lock );
-        vlc_cond_destroy( &p_this->thread_wait );
+        vlc_mutex_unlock( &p_this->object_lock );
     }
 
     return i_ret;
@@ -471,9 +548,9 @@ int __vlc_thread_create( vlc_object_t *p_this, char * psz_file, int i_line,
  *****************************************************************************/
 void __vlc_thread_ready( vlc_object_t *p_this )
 {
-    vlc_mutex_lock( &p_this->thread_lock );
-    vlc_cond_signal( &p_this->thread_wait );
-    vlc_mutex_unlock( &p_this->thread_lock );
+    vlc_mutex_lock( &p_this->object_lock );
+    vlc_cond_signal( &p_this->object_wait );
+    vlc_mutex_unlock( &p_this->object_lock );
 }
 
 /*****************************************************************************
@@ -505,9 +582,6 @@ void __vlc_thread_join( vlc_object_t *p_this, char * psz_file, int i_line )
 
 #endif
 
-    vlc_mutex_destroy( &p_this->thread_lock );
-    vlc_cond_destroy( &p_this->thread_wait );
-
     if( i_ret )
     {
         msg_Err( p_this, "thread_join(%d) failed at %s:%d (%s)",