]> git.sesse.net Git - vlc/blobdiff - src/misc/threads.c
threads: Make sure we don't re-create a thread if the object has already one.
[vlc] / src / misc / threads.c
index c68dab7c542a64e7c5073862662f9e68b62fc555..9cac384deb65caebc5a68ef1508e18699abf5e15 100644 (file)
@@ -28,7 +28,7 @@
 # include "config.h"
 #endif
 
-#include <vlc/vlc.h>
+#include <vlc_common.h>
 
 #include "libvlc.h"
 #include <assert.h>
@@ -48,6 +48,8 @@
 static volatile unsigned i_initializations = 0;
 
 #if defined( LIBVLC_USE_PTHREAD )
+# include <sched.h>
+
 static pthread_mutex_t once_mutex = PTHREAD_MUTEX_INITIALIZER;
 #endif
 
@@ -63,6 +65,7 @@ libvlc_global_data_t *vlc_global( void )
     return p_root;
 }
 
+#ifndef NDEBUG
 /**
  * Object running the current thread
  */
@@ -72,6 +75,7 @@ vlc_object_t *vlc_threadobj (void)
 {
     return vlc_threadvar_get (&thread_object_key);
 }
+#endif
 
 vlc_threadvar_t msg_context_global_key;
 
@@ -83,7 +87,7 @@ static inline unsigned long vlc_threadid (void)
      return v.i;
 }
 
-#if defined(__GLIBC__) || defined(__APPLE__)
+#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE)
 # include <execinfo.h>
 #endif
 
@@ -97,14 +101,13 @@ void vlc_pthread_fatal (const char *action, int error,
 {
     fprintf (stderr, "LibVLC fatal error %s in thread %lu at %s:%u: %d\n",
              action, vlc_threadid (), file, line, error);
-    fflush (stderr);
 
     /* Sometimes strerror_r() crashes too, so make sure we print an error
      * message before we invoke it */
 #ifdef __GLIBC__
     /* Avoid the strerror_r() prototype brain damage in glibc */
     errno = error;
-    dprintf (2, " Error message: %m at:\n");
+    fprintf (stderr, " Error message: %m at:\n");
 #else
     char buf[1000];
     const char *msg;
@@ -122,10 +125,10 @@ void vlc_pthread_fatal (const char *action, int error,
             break;
     }
     fprintf (stderr, " Error message: %s\n", msg);
-    fflush (stderr);
 #endif
+    fflush (stderr);
 
-#if defined(__GLIBC__) || defined(__APPLE__)
+#ifdef HAVE_BACKTRACE
     void *stack[20];
     int len = backtrace (stack, sizeof (stack) / sizeof (stack[0]));
     backtrace_symbols_fd (stack, len, 2);
@@ -161,7 +164,7 @@ int vlc_threads_init( void )
 
     if( i_initializations == 0 )
     {
-        p_root = vlc_custom_create( NULL, sizeof( *p_root ),
+        p_root = vlc_custom_create( (vlc_object_t *)NULL, sizeof( *p_root ),
                                     VLC_OBJECT_GENERIC, "root" );
         if( p_root == NULL )
         {
@@ -170,7 +173,9 @@ int vlc_threads_init( void )
         }
 
         /* We should be safe now. Do all the initialization stuff we want. */
+#ifndef NDEBUG
         vlc_threadvar_create( &thread_object_key, NULL );
+#endif
         vlc_threadvar_create( &msg_context_global_key, msg_StackDestroy );
     }
     i_initializations++;
@@ -202,7 +207,9 @@ void vlc_threads_end( void )
     {
         vlc_object_release( p_root );
         vlc_threadvar_delete( &msg_context_global_key );
+#ifndef NDEBUG
         vlc_threadvar_delete( &thread_object_key );
+#endif
     }
     i_initializations--;
 
@@ -453,10 +460,18 @@ static THREAD_RTYPE thread_entry (void *data)
     void *(*func) (void *) = ((struct vlc_thread_boot *)data)->entry;
 
     free (data);
+#ifndef NDEBUG
     vlc_threadvar_set (&thread_object_key, obj);
+#endif
     msg_Dbg (obj, "thread started");
     func (obj);
     msg_Dbg (obj, "thread ended");
+
+    libvlc_priv_t *libpriv = libvlc_priv (obj->p_libvlc);
+    vlc_mutex_lock (&libpriv->threads_lock);
+    if (--libpriv->threads_count == 0)
+        vlc_cond_signal (&libpriv->threads_wait);
+    vlc_mutex_unlock (&libpriv->threads_lock);
     return THREAD_RVAL;
 }
 
@@ -472,6 +487,7 @@ int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line
 {
     int i_ret;
     vlc_object_internals_t *p_priv = vlc_internals( p_this );
+    libvlc_priv_t *libpriv = libvlc_priv (p_this->p_libvlc);
 
     struct vlc_thread_boot *boot = malloc (sizeof (*boot));
     if (boot == NULL)
@@ -479,92 +495,87 @@ int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line
     boot->entry = func;
     boot->object = p_this;
 
-    vlc_mutex_lock( &p_this->object_lock );
+    vlc_mutex_lock (&libpriv->threads_lock);
+    libpriv->threads_count++;
+    vlc_mutex_unlock (&libpriv->threads_lock);
+
+    vlc_object_lock( p_this );
+
+    /* Make sure we don't re-create a thread if the object has already one */
+    assert( !p_priv->b_thread );
 
 #if defined( LIBVLC_USE_PTHREAD )
+    pthread_attr_t attr;
+    pthread_attr_init (&attr);
+
+    /* Block the signals that signals interface plugin handles.
+     * If the LibVLC caller wants to handle some signals by itself, it should
+     * block these before whenever invoking LibVLC. And it must obviously not
+     * start the VLC signals interface plugin.
+     *
+     * LibVLC will normally ignore any interruption caused by an asynchronous
+     * signal during a system call. But there may well be some buggy cases
+     * where it fails to handle EINTR (bug reports welcome). Some underlying
+     * libraries might also not handle EINTR properly.
+     */
     sigset_t set, oldset;
+    sigemptyset (&set);
+    sigdelset (&set, SIGHUP);
+    sigaddset (&set, SIGINT);
+    sigaddset (&set, SIGQUIT);
+    sigaddset (&set, SIGTERM);
 
-    /* We really don't want signals to (literaly) interrupt our blocking I/O
-     * system calls. SIGPIPE is especially bad, as it can be caused by remote
-     * peers through connected sockets. Generally, we cannot know which signals
-     * are handled by the main program. Also, external LibVLC bindings tend not
-     * to setup a proper signal mask before invoking LibVLC.
-     * Hence, we hereby block all signals, except those for which blocking is
-     * undefined, as listed below. Note that SIGKILL and SIGSTOP need not be
-     * listed (see the documentation for pthread_sigmask) here. */
-    sigfillset (&set);
-    sigdelset (&set, SIGFPE);
-    sigdelset (&set, SIGILL);
-    sigdelset (&set, SIGSEGV);
-    sigdelset (&set, SIGBUS);
+    sigaddset (&set, SIGPIPE); /* We don't want this one, really! */
     pthread_sigmask (SIG_BLOCK, &set, &oldset);
 
-    i_ret = pthread_create( &p_priv->thread_id, NULL, thread_entry, boot );
-    pthread_sigmask (SIG_SETMASK, &oldset, NULL);
-
 #ifndef __APPLE__
     if( config_GetInt( p_this, "rt-priority" ) > 0 )
 #endif
     {
-        int i_error, i_policy;
-        struct sched_param param;
+        struct sched_param p = { .sched_priority = i_priority, };
+        int policy;
 
-        memset( &param, 0, sizeof(struct sched_param) );
+        /* Hack to avoid error msg */
         if( config_GetType( p_this, "rt-offset" ) )
-            i_priority += config_GetInt( p_this, "rt-offset" );
-        if( i_priority <= 0 )
-        {
-            param.sched_priority = (-1) * i_priority;
-            i_policy = SCHED_OTHER;
-        }
+            p.sched_priority += config_GetInt( p_this, "rt-offset" );
+        if( p.sched_priority <= 0 )
+            p.sched_priority += sched_get_priority_max (policy = SCHED_OTHER);
         else
-        {
-            param.sched_priority = i_priority;
-            i_policy = SCHED_RR;
-        }
-        if( (i_error = pthread_setschedparam( p_priv->thread_id,
-                                               i_policy, &param )) )
-        {
-            errno = i_error;
-            msg_Warn( p_this, "couldn't set thread priority (%s:%d): %m",
-                      psz_file, i_line );
-            i_priority = 0;
-        }
+            p.sched_priority += sched_get_priority_min (policy = SCHED_RR);
+
+        pthread_attr_setschedpolicy (&attr, policy);
+        pthread_attr_setschedparam (&attr, &p);
     }
-#ifndef __APPLE__
-    else
-        i_priority = 0;
-#endif
+
+    i_ret = pthread_create( &p_priv->thread_id, &attr, thread_entry, boot );
+    pthread_sigmask (SIG_SETMASK, &oldset, NULL);
+    pthread_attr_destroy (&attr);
 
 #elif defined( WIN32 ) || defined( UNDER_CE )
-    {
-        /* 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 (see Microsoft
-         * Knowledge Base, article 104641) */
+    /* 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 (see Microsoft
+     * Knowledge Base, article 104641) */
 #if defined( UNDER_CE )
-        HANDLE hThread = CreateThread( NULL, 0, thread_entry,
-                                       (LPVOID)boot, CREATE_SUSPENDED,
-                                        NULL );
+    HANDLE hThread = CreateThread( NULL, 0, thread_entry,
+                                  (LPVOID)boot, CREATE_SUSPENDED, NULL );
 #else
-        HANDLE hThread = (HANDLE)(uintptr_t)
-            _beginthreadex( NULL, 0, thread_entry, boot,
-                            CREATE_SUSPENDED, NULL );
+    HANDLE hThread = (HANDLE)(uintptr_t)
+        _beginthreadex( NULL, 0, thread_entry, boot, CREATE_SUSPENDED, NULL );
 #endif
-        p_priv->thread_id = hThread;
-        ResumeThread(hThread);
-    }
-
-    i_ret = ( p_priv->thread_id ? 0 : errno );
-
-    if( !i_ret && i_priority )
+    if( hThread )
     {
-        if( !SetThreadPriority(p_priv->thread_id, i_priority) )
+        p_priv->thread_id = hThread;
+        ResumeThread (hThread);
+        i_ret = 0;
+        if( i_priority && !SetThreadPriority (hThread, i_priority) )
         {
             msg_Warn( p_this, "couldn't set a faster priority" );
             i_priority = 0;
         }
     }
+    else
+        i_ret = errno;
 
 #elif defined( HAVE_KERNEL_SCHEDULER_H )
     p_priv->thread_id = spawn_thread( (thread_func)thread_entry, psz_name,
@@ -593,7 +604,15 @@ int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line
                          psz_name, psz_file, i_line );
     }
 
-    vlc_mutex_unlock( &p_this->object_lock );
+    vlc_object_unlock( p_this );
+
+    if (i_ret)
+    {
+        vlc_mutex_lock (&libpriv->threads_lock);
+        if (--libpriv->threads_count == 0)
+            vlc_cond_signal (&libpriv->threads_wait);
+        vlc_mutex_unlock (&libpriv->threads_lock);
+    }
     return i_ret;
 }
 
@@ -606,6 +625,12 @@ int __vlc_thread_set_priority( vlc_object_t *p_this, const char * psz_file,
 {
     vlc_object_internals_t *p_priv = vlc_internals( p_this );
 
+    if( !p_priv->b_thread )
+    {
+        msg_Err( p_this, "couldn't set priority of non-existent thread" );
+        return ESRCH;
+    }
+
 #if defined( LIBVLC_USE_PTHREAD )
 # ifndef __APPLE__
     if( config_GetInt( p_this, "rt-priority" ) > 0 )
@@ -627,10 +652,8 @@ int __vlc_thread_set_priority( vlc_object_t *p_this, const char * psz_file,
             param.sched_priority = i_priority;
             i_policy = SCHED_RR;
         }
-        if( !p_priv->thread_id )
-            p_priv->thread_id = pthread_self();
         if( (i_error = pthread_setschedparam( p_priv->thread_id,
-                                               i_policy, &param )) )
+                                              i_policy, &param )) )
         {
             errno = i_error;
             msg_Warn( p_this, "couldn't set thread priority (%s:%d): %m",
@@ -642,8 +665,6 @@ int __vlc_thread_set_priority( vlc_object_t *p_this, const char * psz_file,
 #elif defined( WIN32 ) || defined( UNDER_CE )
     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
 
-    if( !p_priv->thread_id )
-        p_priv->thread_id = GetCurrentThread();
     if( !SetThreadPriority(p_priv->thread_id, i_priority) )
     {
         msg_Warn( p_this, "couldn't set a faster priority" );
@@ -655,14 +676,6 @@ int __vlc_thread_set_priority( vlc_object_t *p_this, const char * psz_file,
     return 0;
 }
 
-/*****************************************************************************
- * vlc_thread_ready: tell the parent thread we were successfully spawned
- *****************************************************************************/
-void __vlc_thread_ready( vlc_object_t *p_this )
-{
-    vlc_object_signal( p_this );
-}
-
 /*****************************************************************************
  * vlc_thread_join: wait until a thread exits, inner version
  *****************************************************************************/
@@ -675,7 +688,10 @@ void __vlc_thread_join( vlc_object_t *p_this, const char * psz_file, int i_line
     /* Make sure we do return if we are calling vlc_thread_join()
      * from the joined thread */
     if (pthread_equal (pthread_self (), p_priv->thread_id))
+    {
+        msg_Warn (p_this, "joining the active thread (VLC might crash)");
         i_ret = pthread_detach (p_priv->thread_id);
+    }
     else
         i_ret = pthread_join (p_priv->thread_id, NULL);