/*****************************************************************************
- * 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>
-#include <errno.h>
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-#if defined( LIBVLC_USE_PTHREAD )
-# include <sched.h>
-#endif
+#include <vlc_common.h>
-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, void *(*func) ( vlc_object_t * ),
- int i_priority )
+#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)
{
- 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;
+ vlc_mutex_init (&lock->mutex);
+ vlc_cond_init (&lock->wait);
+ lock->state = 0;
+}
- /* Make sure we don't re-create a thread if the object has already one */
- assert( !p_priv->b_thread );
+void vlc_rwlock_destroy (vlc_rwlock_t *lock)
+{
+ vlc_cond_destroy (&lock->wait);
+ vlc_mutex_destroy (&lock->mutex);
+}
- i_ret = vlc_clone( &p_priv->thread_id, thread_entry, boot, i_priority );
- if( i_ret == 0 )
- p_priv->b_thread = true;
- 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)
{
- errno = i_ret;
- msg_Err( p_this, "cannot create thread (%m)" );
- free (boot);
+ 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);
}
-#undef vlc_thread_join
-/*****************************************************************************
- * vlc_thread_join: wait until a thread exits, inner version
- *****************************************************************************/
-void vlc_thread_join( vlc_object_t *p_this )
+void vlc_rwlock_wrlock (vlc_rwlock_t *lock)
{
- vlc_object_internals_t *p_priv = vlc_internals( p_this );
-
- vlc_join( p_priv->thread_id, NULL );
- p_priv->b_thread = false;
+ vlc_mutex_lock (&lock->mutex);
+ /* Wait until nobody owns the lock in any way. */
+ while (lock->state != 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);
}
-void vlc_thread_cancel (vlc_object_t *obj)
+void vlc_rwlock_unlock (vlc_rwlock_t *lock)
{
- vlc_object_internals_t *priv = vlc_internals (obj);
+ 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);
+ }
+ 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 */
- if (priv->b_thread)
- vlc_cancel (priv->thread_id);
+#ifdef LIBVLC_NEED_SEMAPHORE
+/*** Generic semaphores ***/
+#include <limits.h>
+#include <errno.h>
+
+void vlc_sem_init (vlc_sem_t *sem, unsigned value)
+{
+ vlc_mutex_init (&sem->lock);
+ vlc_cond_init (&sem->wait);
+ sem->value = value;
}
-/*** Global locks ***/
+void vlc_sem_destroy (vlc_sem_t *sem)
+{
+ vlc_cond_destroy (&sem->wait);
+ vlc_mutex_destroy (&sem->lock);
+}
-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 */