X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmisc%2Fthreads.c;h=01e1d976d0cedbe13e9de81e1d840d6bbd093429;hb=39827e4a5e846bf58a247b22e29cf7b8bbe9c6bc;hp=cbb61bc709d9d0dd14a19bdd63f20c25ee969346;hpb=7b59c12c2416c3de7bc223bd73da98f20f3c843a;p=vlc diff --git a/src/misc/threads.c b/src/misc/threads.c index cbb61bc709..01e1d976d0 100644 --- a/src/misc/threads.c +++ b/src/misc/threads.c @@ -1,205 +1,175 @@ /***************************************************************************** - * 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 - * Samuel Hocevar - * Gildas Bazin - * 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 - -#include "libvlc.h" #include -#include -#ifdef HAVE_UNISTD_H -# include -#endif -#if defined( LIBVLC_USE_PTHREAD ) -# include -#endif +#include -struct vlc_thread_boot -{ - void * (*entry) (vlc_object_t *); - vlc_object_t *object; -}; +/*** Global locks ***/ -static void *thread_entry (void *data) +void vlc_global_mutex (unsigned n, bool acquire) { - 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"); + 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]))); - return NULL; + vlc_mutex_t *lock = locks + n; + if (acquire) + vlc_mutex_lock (lock); + else + vlc_mutex_unlock (lock); } -#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 ) +#ifdef LIBVLC_NEED_RWLOCK +/*** Generic read/write locks ***/ +#include +#include +/* 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) { - 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 ); - - 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 - { - 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 ); - } - - return i_ret; + vlc_mutex_init (&lock->mutex); + vlc_cond_init (&lock->wait); + lock->state = 0; } -#undef vlc_thread_set_priority -/***************************************************************************** - * 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_destroy (vlc_rwlock_t *lock) { - vlc_object_internals_t *p_priv = vlc_internals( p_this ); + vlc_cond_destroy (&lock->wait); + vlc_mutex_destroy (&lock->mutex); +} - if( !p_priv->b_thread ) +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) { - msg_Err( p_this, "couldn't set priority of non-existent thread" ); - return ESRCH; + assert (lock->state == WRITER_BIT); + mutex_cleanup_push (&lock->mutex); + vlc_cond_wait (&lock->wait, &lock->mutex); + vlc_cleanup_pop (); } + if (unlikely(lock->state >= READER_MASK)) + abort (); /* An overflow is certainly a recursion bug. */ + lock->state++; + vlc_mutex_unlock (&lock->mutex); +} -#if defined( LIBVLC_USE_PTHREAD ) -# ifndef __APPLE__ - if( var_InheritBool( p_this, "rt-priority" ) ) -# endif +void vlc_rwlock_wrlock (vlc_rwlock_t *lock) +{ + vlc_mutex_lock (&lock->mutex); + /* Wait until nobody owns the lock in any way. */ + while (lock->state != 0) { - int i_error, i_policy; - struct sched_param param; - - memset( ¶m, 0, sizeof(struct sched_param) ); - if( config_GetType( p_this, "rt-offset" ) ) - i_priority += var_InheritInteger( 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, ¶m )) ) - { - errno = i_error; - msg_Warn( p_this, "couldn't set thread priority (%s:%d): %m", - psz_file, i_line ); - i_priority = 0; - } + mutex_cleanup_push (&lock->mutex); + vlc_cond_wait (&lock->wait, &lock->mutex); + vlc_cleanup_pop (); } + lock->state = WRITER_BIT; + vlc_mutex_unlock (&lock->mutex); +} -#elif defined( WIN32 ) || defined( UNDER_CE ) - VLC_UNUSED( psz_file); VLC_UNUSED( i_line ); - -#warning vlc_thread_set_priority() is BROKEN - if( true /*!SetThreadPriority(p_priv->thread_id->id, i_priority)*/ ) - { - msg_Warn( p_this, "couldn't set a faster priority" ); - return 1; +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); } - -#endif - - return 0; + 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 */ -#undef vlc_thread_join -/***************************************************************************** - * vlc_thread_join: wait until a thread exits, inner version - *****************************************************************************/ -void vlc_thread_join( vlc_object_t *p_this ) -{ - vlc_object_internals_t *p_priv = vlc_internals( p_this ); +#ifdef LIBVLC_NEED_SEMAPHORE +/*** Generic semaphores ***/ +#include +#include - vlc_join( p_priv->thread_id, NULL ); - p_priv->b_thread = false; +void vlc_sem_init (vlc_sem_t *sem, unsigned value) +{ + vlc_mutex_init (&sem->lock); + vlc_cond_init (&sem->wait); + sem->value = value; } -void vlc_thread_cancel (vlc_object_t *obj) +void vlc_sem_destroy (vlc_sem_t *sem) { - vlc_object_internals_t *priv = vlc_internals (obj); - - if (priv->b_thread) - vlc_cancel (priv->thread_id); + vlc_cond_destroy (&sem->wait); + vlc_mutex_destroy (&sem->lock); } -/*** Global locks ***/ - -void vlc_global_mutex (unsigned n, bool acquire) +int vlc_sem_post (vlc_sem_t *sem) { - static vlc_mutex_t locks[] = { - VLC_STATIC_MUTEX, - VLC_STATIC_MUTEX, - VLC_STATIC_MUTEX, - }; - assert (n < (sizeof (locks) / sizeof (locks[0]))); - vlc_mutex_t *lock = locks + n; + int ret = 0; - if (acquire) - vlc_mutex_lock (lock); + vlc_mutex_lock (&sem->lock); + if (likely(sem->value != UINT_MAX)) + sem->value++; else - vlc_mutex_unlock (lock); + ret = EOVERFLOW; + vlc_mutex_unlock (&sem->lock); + vlc_cond_signal (&sem->wait); + + return ret; +} - /* Compile-time assertion ;-) */ - char enough_locks[(sizeof (locks) / sizeof (locks[0])) - VLC_MAX_MUTEX]; - (void) enough_locks; +void vlc_sem_wait (vlc_sem_t *sem) +{ + 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 */