]> git.sesse.net Git - vlc/blobdiff - src/misc/threads.c
decoder: remove unnecessary special case
[vlc] / src / misc / threads.c
index 1999c9ec4c91725d82499c83ecd1f0786be4e1cd..01e1d976d0cedbe13e9de81e1d840d6bbd093429 100644 (file)
 /*****************************************************************************
- * threads.c : threads implementation for the VideoLAN client
+ * threads.c: LibVLC generic thread support
  *****************************************************************************
- * Copyright (C) 1999-2008 the VideoLAN team
- * $Id$
+ * Copyright (C) 2009-2012 Rémi Denis-Courmont
  *
- * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
- *          Samuel Hocevar <sam@zoy.org>
- *          Gildas Bazin <gbazin@netcourrier.com>
- *          Clément Sténac
- *          Rémi Denis-Courmont
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
 #ifdef HAVE_CONFIG_H
 # include "config.h"
 #endif
 
-#include <vlc_common.h>
-
-#include "libvlc.h"
 #include <assert.h>
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
 
-#if defined( LIBVLC_USE_PTHREAD )
-# include <sched.h>
-#endif
+#include <vlc_common.h>
 
+/*** Global locks ***/
 
-struct vlc_thread_boot
+void vlc_global_mutex (unsigned n, bool acquire)
 {
-    void * (*entry) (vlc_object_t *);
-    vlc_object_t *object;
-};
+    static vlc_mutex_t locks[] = {
+        VLC_STATIC_MUTEX,
+        VLC_STATIC_MUTEX,
+        VLC_STATIC_MUTEX,
+        VLC_STATIC_MUTEX,
+        VLC_STATIC_MUTEX,
+        VLC_STATIC_MUTEX,
+    };
+    static_assert (VLC_MAX_MUTEX == (sizeof (locks) / sizeof (locks[0])),
+                   "Wrong number of global mutexes");
+    assert (n < (sizeof (locks) / sizeof (locks[0])));
+
+    vlc_mutex_t *lock = locks + n;
+    if (acquire)
+        vlc_mutex_lock (lock);
+    else
+        vlc_mutex_unlock (lock);
+}
 
-static void *thread_entry (void *data)
+#ifdef LIBVLC_NEED_RWLOCK
+/*** Generic read/write locks ***/
+#include <stdlib.h>
+#include <limits.h>
+/* NOTE:
+ * lock->state is a signed long integer:
+ *  - The sign bit is set when the lock is held for writing.
+ *  - The other bits code the number of times the lock is held for reading.
+ * Consequently:
+ *  - The value is negative if and only if the lock is held for writing.
+ *  - The value is zero if and only if the lock is not held at all.
+ */
+#define READER_MASK LONG_MAX
+#define WRITER_BIT  LONG_MIN
+
+void vlc_rwlock_init (vlc_rwlock_t *lock)
 {
-    vlc_object_t *obj = ((struct vlc_thread_boot *)data)->object;
-    void *(*func) (vlc_object_t *) = ((struct vlc_thread_boot *)data)->entry;
-
-    free (data);
-    msg_Dbg (obj, "thread started");
-    func (obj);
-    msg_Dbg (obj, "thread ended");
-
-    return NULL;
+    vlc_mutex_init (&lock->mutex);
+    vlc_cond_init (&lock->wait);
+    lock->state = 0;
 }
 
-#undef vlc_thread_create
-/*****************************************************************************
- * vlc_thread_create: create a thread
- *****************************************************************************
- * Note that i_priority is only taken into account on platforms supporting
- * userland real-time priority threads.
- *****************************************************************************/
-int vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line,
-                       const char *psz_name, void *(*func) ( vlc_object_t * ),
-                       int i_priority )
+void vlc_rwlock_destroy (vlc_rwlock_t *lock)
 {
-    int i_ret;
-    vlc_object_internals_t *p_priv = vlc_internals( p_this );
-
-    struct vlc_thread_boot *boot = malloc (sizeof (*boot));
-    if (boot == NULL)
-        return errno;
-    boot->entry = func;
-    boot->object = 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 )
-#ifndef __APPLE__
-    if( config_GetInt( p_this, "rt-priority" ) > 0 )
-#endif
-    {
-        /* Hack to avoid error msg */
-        if( config_GetType( p_this, "rt-offset" ) )
-            i_priority += config_GetInt( p_this, "rt-offset" );
-    }
-#endif
+    vlc_cond_destroy (&lock->wait);
+    vlc_mutex_destroy (&lock->mutex);
+}
 
-    p_priv->b_thread = true;
-    i_ret = vlc_clone( &p_priv->thread_id, thread_entry, boot, i_priority );
-    if( i_ret == 0 )
-        msg_Dbg( p_this, "thread (%s) created at priority %d (%s:%d)",
-                 psz_name, i_priority, psz_file, i_line );
-    else
+void vlc_rwlock_rdlock (vlc_rwlock_t *lock)
+{
+    vlc_mutex_lock (&lock->mutex);
+    /* Recursive read-locking is allowed.
+     * Ensure that there is no active writer. */
+    while (lock->state < 0)
     {
-        p_priv->b_thread = false;
-        errno = i_ret;
-        msg_Err( p_this, "%s thread could not be created at %s:%d (%m)",
-                         psz_name, psz_file, i_line );
+        assert (lock->state == WRITER_BIT);
+        mutex_cleanup_push (&lock->mutex);
+        vlc_cond_wait (&lock->wait, &lock->mutex);
+        vlc_cleanup_pop ();
     }
-
-    return i_ret;
+    if (unlikely(lock->state >= READER_MASK))
+        abort (); /* An overflow is certainly a recursion bug. */
+    lock->state++;
+    vlc_mutex_unlock (&lock->mutex);
 }
 
-/*****************************************************************************
- * vlc_thread_set_priority: set the priority of the current thread when we
- * couldn't set it in vlc_thread_create (for instance for the main thread)
- *****************************************************************************/
-int __vlc_thread_set_priority( vlc_object_t *p_this, const char * psz_file,
-                               int i_line, int i_priority )
+void vlc_rwlock_wrlock (vlc_rwlock_t *lock)
 {
-    vlc_object_internals_t *p_priv = vlc_internals( p_this );
-
-    if( !p_priv->b_thread )
+    vlc_mutex_lock (&lock->mutex);
+    /* Wait until nobody owns the lock in any way. */
+    while (lock->state != 0)
     {
-        msg_Err( p_this, "couldn't set priority of non-existent thread" );
-        return ESRCH;
+        mutex_cleanup_push (&lock->mutex);
+        vlc_cond_wait (&lock->wait, &lock->mutex);
+        vlc_cleanup_pop ();
     }
+    lock->state = WRITER_BIT;
+    vlc_mutex_unlock (&lock->mutex);
+}
 
-#if defined( LIBVLC_USE_PTHREAD )
-# ifndef __APPLE__
-    if( config_GetInt( p_this, "rt-priority" ) > 0 )
-# endif
-    {
-        int i_error, i_policy;
-        struct sched_param param;
-
-        memset( &param, 0, sizeof(struct sched_param) );
-        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;
-        }
-        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;
-        }
+void vlc_rwlock_unlock (vlc_rwlock_t *lock)
+{
+    vlc_mutex_lock (&lock->mutex);
+    if (lock->state < 0)
+    {   /* Write unlock */
+        assert (lock->state == WRITER_BIT);
+        /* Let reader and writer compete. OS scheduler decides who wins. */
+        lock->state = 0;
+        vlc_cond_broadcast (&lock->wait);
     }
-
-#elif defined( WIN32 ) || defined( UNDER_CE )
-    VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
-
-    if( !SetThreadPriority(p_priv->thread_id->handle, i_priority) )
-    {
-        msg_Warn( p_this, "couldn't set a faster priority" );
-        return 1;
+    else
+    {   /* Read unlock */
+        assert (lock->state > 0);
+        /* If there are no readers left, wake up one pending writer. */
+        if (--lock->state == 0)
+            vlc_cond_signal (&lock->wait);
     }
+    vlc_mutex_unlock (&lock->mutex);
+}
+#endif /* LIBVLC_NEED_RWLOCK */
 
-#endif
+#ifdef LIBVLC_NEED_SEMAPHORE
+/*** Generic semaphores ***/
+#include <limits.h>
+#include <errno.h>
 
-    return 0;
+void vlc_sem_init (vlc_sem_t *sem, unsigned value)
+{
+    vlc_mutex_init (&sem->lock);
+    vlc_cond_init (&sem->wait);
+    sem->value = value;
 }
 
-/*****************************************************************************
- * vlc_thread_join: wait until a thread exits, inner version
- *****************************************************************************/
-void __vlc_thread_join( vlc_object_t *p_this )
+void vlc_sem_destroy (vlc_sem_t *sem)
 {
-    vlc_object_internals_t *p_priv = vlc_internals( p_this );
-
-#if defined( LIBVLC_USE_PTHREAD )
-    vlc_join (p_priv->thread_id, NULL);
-
-#elif defined( UNDER_CE ) || defined( WIN32 )
-    HANDLE hThread;
-    FILETIME create_ft, exit_ft, kernel_ft, user_ft;
-    int64_t real_time, kernel_time, user_time;
-
-#ifndef UNDER_CE
-    if( ! DuplicateHandle(GetCurrentProcess(),
-            p_priv->thread_id->handle,
-            GetCurrentProcess(),
-            &hThread,
-            0,
-            FALSE,
-            DUPLICATE_SAME_ACCESS) )
-    {
-        p_priv->b_thread = false;
-        return; /* We have a problem! */
-    }
-#else
-    hThread = p_priv->thread_id->handle;
-#endif
-
-    vlc_join( p_priv->thread_id, NULL );
-
-    if( GetThreadTimes( hThread, &create_ft, &exit_ft, &kernel_ft, &user_ft ) )
-    {
-        real_time =
-          ((((int64_t)exit_ft.dwHighDateTime)<<32)| exit_ft.dwLowDateTime) -
-          ((((int64_t)create_ft.dwHighDateTime)<<32)| create_ft.dwLowDateTime);
-        real_time /= 10;
-
-        kernel_time =
-          ((((int64_t)kernel_ft.dwHighDateTime)<<32)|
-           kernel_ft.dwLowDateTime) / 10;
-
-        user_time =
-          ((((int64_t)user_ft.dwHighDateTime)<<32)|
-           user_ft.dwLowDateTime) / 10;
-
-        msg_Dbg( p_this, "thread times: "
-                 "real %"PRId64"m%fs, kernel %"PRId64"m%fs, user %"PRId64"m%fs",
-                 real_time/60/1000000,
-                 (double)((real_time%(60*1000000))/1000000.0),
-                 kernel_time/60/1000000,
-                 (double)((kernel_time%(60*1000000))/1000000.0),
-                 user_time/60/1000000,
-                 (double)((user_time%(60*1000000))/1000000.0) );
-    }
-    CloseHandle( hThread );
+    vlc_cond_destroy (&sem->wait);
+    vlc_mutex_destroy (&sem->lock);
+}
 
-#else
-    vlc_join( p_priv->thread_id, NULL );
+int vlc_sem_post (vlc_sem_t *sem)
+{
+    int ret = 0;
 
-#endif
+    vlc_mutex_lock (&sem->lock);
+    if (likely(sem->value != UINT_MAX))
+        sem->value++;
+    else
+        ret = EOVERFLOW;
+    vlc_mutex_unlock (&sem->lock);
+    vlc_cond_signal (&sem->wait);
 
-    p_priv->b_thread = false;
+    return ret;
 }
 
-void vlc_thread_cancel (vlc_object_t *obj)
+void vlc_sem_wait (vlc_sem_t *sem)
 {
-    vlc_object_internals_t *priv = vlc_internals (obj);
-
-    if (priv->b_thread)
-        vlc_cancel (priv->thread_id);
+    vlc_mutex_lock (&sem->lock);
+    mutex_cleanup_push (&sem->lock);
+    while (!sem->value)
+        vlc_cond_wait (&sem->wait, &sem->lock);
+    sem->value--;
+    vlc_cleanup_run ();
 }
+#endif /* LIBVLC_NEED_SEMAPHORE */