]> 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 db8bcb9a382c5dfbffc63211c62090d9d70b8a07..9cac384deb65caebc5a68ef1508e18699abf5e15 100644 (file)
@@ -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
 
@@ -99,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;
@@ -124,8 +125,8 @@ void vlc_pthread_fatal (const char *action, int error,
             break;
     }
     fprintf (stderr, " Error message: %s\n", msg);
-    fflush (stderr);
 #endif
+    fflush (stderr);
 
 #ifdef HAVE_BACKTRACE
     void *stack[20];
@@ -163,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 )
         {
@@ -465,6 +466,12 @@ static THREAD_RTYPE thread_entry (void *data)
     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;
 }
 
@@ -480,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)
@@ -487,47 +495,56 @@ 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;
-    /* 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);
+    sigemptyset (&set);
+    sigdelset (&set, SIGHUP);
+    sigaddset (&set, SIGINT);
+    sigaddset (&set, SIGQUIT);
+    sigaddset (&set, SIGTERM);
+
+    sigaddset (&set, SIGPIPE); /* We don't want this one, really! */
     pthread_sigmask (SIG_BLOCK, &set, &oldset);
 
 #ifndef __APPLE__
     if( config_GetInt( p_this, "rt-priority" ) > 0 )
 #endif
     {
+        struct sched_param p = { .sched_priority = i_priority, };
+        int policy;
+
         /* Hack to avoid error msg */
         if( config_GetType( p_this, "rt-offset" ) )
-            i_priority += config_GetInt( p_this, "rt-offset" );
-        if( i_priority <= 0 )
-        {
-            struct sched_param param = { .sched_priority = -i_priority, };
-            pthread_attr_setschedpolicy (&attr, SCHED_OTHER);
-            pthread_attr_setschedparam (&attr, &param);
-        }
+            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
-        {
-            struct sched_param param = { .sched_priority = +i_priority, };
-            pthread_attr_setschedpolicy (&attr, SCHED_OTHER);
-            pthread_attr_setschedparam (&attr, &param);
-        }
+            p.sched_priority += sched_get_priority_min (policy = SCHED_RR);
+
+        pthread_attr_setschedpolicy (&attr, policy);
+        pthread_attr_setschedparam (&attr, &p);
     }
 
     i_ret = pthread_create( &p_priv->thread_id, &attr, thread_entry, boot );
@@ -535,34 +552,30 @@ int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line
     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,
@@ -591,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;
 }
 
@@ -604,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 )
@@ -625,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",
@@ -640,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" );
@@ -653,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
  *****************************************************************************/
@@ -673,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);