X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmisc%2Fthreads.c;h=01e1d976d0cedbe13e9de81e1d840d6bbd093429;hb=39827e4a5e846bf58a247b22e29cf7b8bbe9c6bc;hp=1999c9ec4c91725d82499c83ecd1f0786be4e1cd;hpb=7f12470415d98e9e0ac725f2bb96b5fa74ef27f1;p=vlc diff --git a/src/misc/threads.c b/src/misc/threads.c index 1999c9ec4c..01e1d976d0 100644 --- a/src/misc/threads.c +++ b/src/misc/threads.c @@ -1,247 +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 -#ifdef HAVE_UNISTD_H -# include -#endif -#if defined( LIBVLC_USE_PTHREAD ) -# include -#endif +#include +/*** 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 +#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) { - 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( ¶m, 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, ¶m )) ) - { - 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 +#include - 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 */