]> git.sesse.net Git - vlc/blobdiff - include/threads.h
* ./BUGS: added a list of known bugs. Please add your findings!
[vlc] / include / threads.h
index 0b1b090be04ee1be4c2021e3e03c6c829c8f879d..99c29527e52b5aef487df4d3716ad4d281e6bff9 100644 (file)
@@ -3,7 +3,7 @@
  * This header provides a portable threads implementation.
  *****************************************************************************
  * Copyright (C) 1999, 2000 VideoLAN
- * $Id: threads.h,v 1.21 2001/07/25 08:41:21 gbazin Exp $
+ * $Id: threads.h,v 1.34 2002/01/04 14:01:34 sam Exp $
  *
  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
  *          Samuel Hocevar <sam@via.ecp.fr>
@@ -25,7 +25,7 @@
 
 #include <stdio.h>
 
-#ifdef PROFILING
+#if defined(GPROF) || defined(DEBUG)
 #   include <sys/time.h>
 #endif
 
@@ -34,6 +34,8 @@
 
 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )  /* pthreads (like Linux & BSD) */
 #   include <pthread.h>
+/* This is not prototyped under Linux, though it exists. */
+int pthread_mutexattr_setkind_np( pthread_mutexattr_t *attr, int kind );
 
 #elif defined( HAVE_CTHREADS_H )                                  /* GNUMach */
 #   include <cthreads.h>
@@ -46,6 +48,7 @@
 #   include <byteorder.h>
 
 #elif defined( WIN32 )
+#define WIN32_LEAN_AND_MEAN
 #   include <windows.h>
 #   include <process.h>
 
@@ -153,32 +156,24 @@ typedef void *(*vlc_thread_func_t)(void *p_data);
  * Prototypes
  *****************************************************************************/
 
-static __inline__ int  vlc_threads_init  ( void );
-static __inline__ int  vlc_threads_end   ( void );
+#ifdef GPROF
+/* Wrapper function for profiling */
+static void *      vlc_thread_wrapper ( void *p_wrapper );
 
-static __inline__ int  vlc_mutex_init    ( vlc_mutex_t * );
-static __inline__ int  vlc_mutex_lock    ( vlc_mutex_t * );
-static __inline__ int  vlc_mutex_unlock  ( vlc_mutex_t * );
-static __inline__ int  vlc_mutex_destroy ( vlc_mutex_t * );
+#   ifdef WIN32
 
-static __inline__ int  vlc_cond_init     ( vlc_cond_t * );
-static __inline__ int  vlc_cond_signal   ( vlc_cond_t * );
-static __inline__ int  vlc_cond_wait     ( vlc_cond_t *, vlc_mutex_t * );
-static __inline__ int  vlc_cond_destroy  ( vlc_cond_t * );
+#       define ITIMER_REAL 1
+#       define ITIMER_PROF 2
 
-static __inline__ int  vlc_thread_create ( vlc_thread_t *, char *,
-                                           vlc_thread_func_t, void * );
-static __inline__ void vlc_thread_exit   ( void );
-static __inline__ void vlc_thread_join   ( vlc_thread_t );
+struct itimerval
+{
+    struct timeval it_value;
+    struct timeval it_interval;
+};
 
-#if 0
-static __inline__ int  vlc_cond_timedwait( vlc_cond_t *, vlc_mutex_t *,
-                                           mtime_t );
-#endif
+int setitimer(int kind, const struct itimerval* itnew, struct itimerval* itold);
 
-#ifdef PROFILING
-/* Wrapper function for profiling */
-static void *      vlc_thread_wrapper ( void *p_wrapper );
+#   endif /* WIN32 */
 
 typedef struct wrapper_s
 {
@@ -195,22 +190,7 @@ typedef struct wrapper_s
 
 } wrapper_t;
 
-#ifdef WIN32
-struct itimerval
-{
-    struct timeval it_value;
-    struct timeval it_interval;
-};
-
-int setitimer(int kind, const struct itimerval* itnew,
-             struct itimerval* itold);
-
-#define ITIMER_REAL 1
-#define ITIMER_PROF 2
-
-#endif /* WIN32 */
-
-#endif /* PROFILING */
+#endif /* GPROF */
 
 /*****************************************************************************
  * vlc_threads_init: initialize threads system
@@ -267,6 +247,18 @@ static __inline__ int vlc_mutex_init( vlc_mutex_t *p_mutex )
     return pth_mutex_init( p_mutex );
 
 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
+#   if defined(DEBUG) && defined(SYS_LINUX)
+    /* Create error-checking mutex to detect threads problems more easily. */
+    pthread_mutexattr_t attr;
+    int                 i_result;
+
+    pthread_mutexattr_init( &attr );
+    pthread_mutexattr_setkind_np( &attr, PTHREAD_MUTEX_ERRORCHECK_NP );
+    i_result = pthread_mutex_init( p_mutex, &attr );
+    pthread_mutexattr_destroy( &attr );
+    return( i_result );
+#   endif
+
     return pthread_mutex_init( p_mutex, NULL );
 
 #elif defined( HAVE_CTHREADS_H )
@@ -305,13 +297,28 @@ static __inline__ int vlc_mutex_init( vlc_mutex_t *p_mutex )
 /*****************************************************************************
  * vlc_mutex_lock: lock a mutex
  *****************************************************************************/
-static __inline__ int vlc_mutex_lock( vlc_mutex_t *p_mutex )
+#ifdef DEBUG
+#   define vlc_mutex_lock( P_MUTEX )                                        \
+        _vlc_mutex_lock( __FILE__, __LINE__, P_MUTEX )
+#else
+#   define vlc_mutex_lock( P_MUTEX )                                        \
+        _vlc_mutex_lock( NULL, 0, P_MUTEX )
+#endif
+
+static __inline__ int _vlc_mutex_lock( char * psz_file, int i_line,
+                                       vlc_mutex_t *p_mutex )
 {
 #if defined( PTH_INIT_IN_PTH_H )
     return pth_mutex_acquire( p_mutex, TRUE, NULL );
 
 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
-    return pthread_mutex_lock( p_mutex );
+    int i_return = pthread_mutex_lock( p_mutex );
+    if( i_return )
+    {
+        intf_ErrMsg( "thread %d error: mutex_lock failed at %s:%d (%s)",
+                     pthread_self(), psz_file, i_line, strerror(i_return) );
+    }
+    return i_return;
 
 #elif defined( HAVE_CTHREADS_H )
     mutex_lock( p_mutex );
@@ -343,13 +350,28 @@ static __inline__ int vlc_mutex_lock( vlc_mutex_t *p_mutex )
 /*****************************************************************************
  * vlc_mutex_unlock: unlock a mutex
  *****************************************************************************/
-static __inline__ int vlc_mutex_unlock( vlc_mutex_t *p_mutex )
+#ifdef DEBUG
+#   define vlc_mutex_unlock( P_MUTEX )                                      \
+        _vlc_mutex_unlock( __FILE__, __LINE__, P_MUTEX )
+#else
+#   define vlc_mutex_unlock( P_MUTEX )                                      \
+        _vlc_mutex_unlock( NULL, 0, P_MUTEX )
+#endif
+
+static __inline__ int _vlc_mutex_unlock( char * psz_file, int i_line,
+                                         vlc_mutex_t *p_mutex )
 {
 #if defined( PTH_INIT_IN_PTH_H )
     return pth_mutex_release( p_mutex );
 
 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
-    return pthread_mutex_unlock( p_mutex );
+    int i_return = pthread_mutex_unlock( p_mutex );
+    if( i_return )
+    {
+        intf_ErrMsg( "thread %d error: mutex_unlock failed at %s:%d (%s)",
+                     pthread_self(), psz_file, i_line, strerror(i_return) );
+    }
+    return i_return;
 
 #elif defined( HAVE_CTHREADS_H )
     mutex_unlock( p_mutex );
@@ -379,13 +401,31 @@ static __inline__ int vlc_mutex_unlock( vlc_mutex_t *p_mutex )
 /*****************************************************************************
  * vlc_mutex_destroy: destroy a mutex
  *****************************************************************************/
-static __inline__ int vlc_mutex_destroy( vlc_mutex_t *p_mutex )
+#ifdef DEBUG
+#   define vlc_mutex_destroy( P_MUTEX )                                     \
+        _vlc_mutex_destroy( __FILE__, __LINE__, P_MUTEX )
+#else
+#   define vlc_mutex_destroy( P_MUTEX )                                     \
+        _vlc_mutex_destroy( NULL, 0, P_MUTEX )
+#endif
+
+static __inline__ int _vlc_mutex_destroy( char * psz_file, int i_line,
+                                          vlc_mutex_t *p_mutex )
 {
 #if defined( PTH_INIT_IN_PTH_H )
     return 0;
 
 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )    
-    return pthread_mutex_destroy( p_mutex );
+    int i_return = pthread_mutex_destroy( p_mutex );
+    if( i_return )
+    {
+        intf_ErrMsg( "thread %d error: mutex_destroy failed at %s:%d (%s)",
+                     pthread_self(), psz_file, i_line, strerror(i_return) );
+    }
+    return i_return;
+
+#elif defined( HAVE_CTHREADS_H )
+    return 0;
 
 #elif defined( HAVE_KERNEL_SCHEDULER_H )
     if( p_mutex->init == 9999 )
@@ -444,9 +484,9 @@ static __inline__ int vlc_cond_init( vlc_cond_t *p_condvar )
 
     /* Create an auto-reset event. */
     p_condvar->signal = CreateEvent( NULL, /* no security */
-                                    FALSE,  /* auto-reset event */
-                                    FALSE,  /* non-signaled initially */
-                                    NULL ); /* unnamed */
+                                     FALSE,  /* auto-reset event */
+                                     FALSE,  /* non-signaled initially */
+                                     NULL ); /* unnamed */
 
     return( !p_condvar->signal );
     
@@ -512,7 +552,9 @@ 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 */
-    while( p_condvar->i_waiting_threads )
+    int i_waiting_threads = p_condvar->i_waiting_threads;
+    while( p_condvar->i_waiting_threads
+           && p_condvar->i_waiting_threads == i_waiting_threads )
     {
         PulseEvent( p_condvar->signal );
         Sleep( 0 ); /* deschedule the current thread */
@@ -599,13 +641,48 @@ static __inline__ int vlc_cond_broadcast( vlc_cond_t *p_condvar )
 /*****************************************************************************
  * vlc_cond_wait: wait until condition completion
  *****************************************************************************/
-static __inline__ int vlc_cond_wait( vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex )
+#ifdef DEBUG
+#   define vlc_cond_wait( P_COND, P_MUTEX )                                 \
+        _vlc_cond_wait( __FILE__, __LINE__, P_COND, P_MUTEX  )
+#else
+#   define vlc_cond_wait( P_COND, P_MUTEX )                                 \
+        _vlc_cond_wait( NULL, 0, P_COND, P_MUTEX )
+#endif
+
+static __inline__ int _vlc_cond_wait( char * psz_file, int i_line,
+                                      vlc_cond_t *p_condvar,
+                                      vlc_mutex_t *p_mutex )
 {
 #if defined( PTH_INIT_IN_PTH_H )
     return pth_cond_await( p_condvar, p_mutex, NULL );
 
 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
+
+#ifndef DEBUG
     return pthread_cond_wait( p_condvar, p_mutex );
+#else
+    /* In debug mode, timeout */
+    struct timeval now;
+    struct timespec timeout;
+    int    i_result;
+
+    for( ; ; )
+    {
+        gettimeofday( &now, NULL );
+        timeout.tv_sec = now.tv_sec + THREAD_COND_TIMEOUT;
+        timeout.tv_nsec = now.tv_usec * 1000;
+
+        if( (i_result = pthread_cond_timedwait( p_condvar, p_mutex, &timeout )) )
+        {
+            intf_WarnMsg( 1, "thread %d warning: Possible deadlock detected in cond_wait at %s:%d (%s)",
+                          pthread_self(), psz_file, i_line, strerror(i_result) );
+        }
+        else
+        {
+            return i_result;
+        }
+    }
+#endif
 
 #elif defined( HAVE_CTHREADS_H )
     condition_wait( (condition_t)p_condvar, (mutex_t)p_mutex );
@@ -671,13 +748,31 @@ static __inline__ int vlc_cond_wait( vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex
 /*****************************************************************************
  * vlc_cond_destroy: destroy a condition
  *****************************************************************************/
-static __inline__ int vlc_cond_destroy( vlc_cond_t *p_condvar )
+#ifdef DEBUG
+#   define vlc_cond_destroy( P_COND )                                       \
+        _vlc_cond_destroy( __FILE__, __LINE__, P_COND )
+#else
+#   define vlc_cond_destroy( P_COND )                                       \
+        _vlc_cond_destroy( NULL, 0, P_COND )
+#endif
+
+static __inline__ int _vlc_cond_destroy( char * psz_file, int i_line,
+                                         vlc_cond_t *p_condvar )
 {
 #if defined( PTH_INIT_IN_PTH_H )
     return 0;
 
 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
-    return pthread_cond_destroy( p_condvar );
+    int i_result = pthread_cond_destroy( p_condvar );
+    if( i_result )
+    {
+        intf_ErrMsg( "thread %d error: cond_destroy failed at %s:%d (%s)",
+                     pthread_self(), psz_file, i_line, strerror(i_result) );
+    }
+    return i_result;
+
+#elif defined( HAVE_CTHREADS_H )
+    return 0;
 
 #elif defined( HAVE_KERNEL_SCHEDULER_H )
     p_condvar->init = 0;
@@ -692,14 +787,23 @@ static __inline__ int vlc_cond_destroy( vlc_cond_t *p_condvar )
 /*****************************************************************************
  * vlc_thread_create: create a thread
  *****************************************************************************/
-static __inline__ int vlc_thread_create( vlc_thread_t *p_thread,
-                                         char *psz_name,
-                                         vlc_thread_func_t func,
-                                         void *p_data )
+#ifdef DEBUG
+#   define vlc_thread_create( P_THREAD, PSZ_NAME, FUNC, P_DATA )            \
+        _vlc_thread_create( __FILE__, __LINE__, P_THREAD, PSZ_NAME, FUNC, P_DATA )
+#else
+#   define vlc_thread_create( P_THREAD, PSZ_NAME, FUNC, P_DATA )            \
+        _vlc_thread_create( NULL, 0, P_THREAD, PSZ_NAME, FUNC, P_DATA )
+#endif
+
+static __inline__ int _vlc_thread_create( char * psz_file, int i_line,
+                                          vlc_thread_t *p_thread,
+                                          char *psz_name,
+                                          vlc_thread_func_t func,
+                                          void *p_data )
 {
     int i_ret;
 
-#ifdef PROFILING
+#ifdef GPROF
     wrapper_t wrapper;
 
     /* Initialize the wrapper structure */
@@ -751,7 +855,7 @@ static __inline__ int vlc_thread_create( vlc_thread_t *p_thread,
 
 #endif
 
-#ifdef PROFILING
+#ifdef GPROF
     if( i_ret == 0 )
     {
         vlc_cond_wait( &wrapper.wait, &wrapper.lock );
@@ -762,6 +866,17 @@ static __inline__ int vlc_thread_create( vlc_thread_t *p_thread,
     vlc_cond_destroy( &wrapper.wait );
 #endif
 
+    if( i_ret == 0 )
+    {
+        intf_WarnMsg( 2, "thread info: %d (%s) has been created (%s:%d)",
+                      *p_thread, psz_name, psz_file, i_line );
+    }
+    else
+    {
+        intf_ErrMsg( "thread error: %s couldn't be created at %s:%d (%s)",
+                     psz_name, psz_file, i_line, strerror(i_ret) );
+    }
+
     return i_ret;
 }
 
@@ -797,28 +912,51 @@ static __inline__ void vlc_thread_exit( void )
 /*****************************************************************************
  * vlc_thread_join: wait until a thread exits
  *****************************************************************************/
-static __inline__ void vlc_thread_join( vlc_thread_t thread )
+#ifdef DEBUG
+#   define vlc_thread_join( THREAD )                                        \
+        _vlc_thread_join( __FILE__, __LINE__, THREAD ) 
+#else
+#   define vlc_thread_join( THREAD )                                        \
+        _vlc_thread_join( NULL, 0, THREAD ) 
+#endif
+
+static __inline__ void _vlc_thread_join( char * psz_file, int i_line,
+                                         vlc_thread_t thread )
 {
+    int i_ret = 0;
+
 #if defined( PTH_INIT_IN_PTH_H )
-    pth_join( thread, NULL );
+    i_ret = pth_join( thread, NULL );
 
 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
-    pthread_join( thread, NULL );
+    i_ret = pthread_join( thread, NULL );
 
 #elif defined( HAVE_CTHREADS_H )
     cthread_join( thread );
+    i_ret = 1;
 
 #elif defined( HAVE_KERNEL_SCHEDULER_H )
     int32 exit_value;
     wait_for_thread( thread, &exit_value );
 
 #elif defined( WIN32 )
-    WaitForSingleObject( thread, INFINITE);
+    WaitForSingleObject( thread, INFINITE );
 
 #endif
+
+    if( i_ret )
+    {
+        intf_ErrMsg( "thread error: thread_join(%d) failed at %s:%d (%s)",
+                     thread, psz_file, i_line, strerror(i_ret) );
+    }
+    else
+    {
+        intf_WarnMsg( 2, "thread info: %d has been joined (%s:%d)",
+                      thread, psz_file, i_line );
+    }
 }
 
-#ifdef PROFILING
+#ifdef GPROF
 static void *vlc_thread_wrapper( void *p_wrapper )
 {
     /* Put user data in thread-local variables */
@@ -837,3 +975,4 @@ static void *vlc_thread_wrapper( void *p_wrapper )
     return func( p_data );
 }
 #endif
+