]> git.sesse.net Git - vlc/blob - src/misc/threads.c
3d51708e4f58f71614ba14143a45d31abbf2c496
[vlc] / src / misc / threads.c
1 /*****************************************************************************
2  * threads.c: LibVLC generic thread support
3  *****************************************************************************
4  * Copyright (C) 2009-2012 RĂ©mi Denis-Courmont
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19  *****************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include <assert.h>
26
27 #include <vlc_common.h>
28
29 /*** Global locks ***/
30
31 void vlc_global_mutex (unsigned n, bool acquire)
32 {
33     static vlc_mutex_t locks[] = {
34         VLC_STATIC_MUTEX,
35         VLC_STATIC_MUTEX,
36         VLC_STATIC_MUTEX,
37         VLC_STATIC_MUTEX,
38         VLC_STATIC_MUTEX,
39         VLC_STATIC_MUTEX,
40     };
41     static_assert (VLC_MAX_MUTEX == (sizeof (locks) / sizeof (locks[0])),
42                    "Wrong number of global mutexes");
43     assert (n < (sizeof (locks) / sizeof (locks[0])));
44
45     vlc_mutex_t *lock = locks + n;
46     if (acquire)
47         vlc_mutex_lock (lock);
48     else
49         vlc_mutex_unlock (lock);
50 }
51
52 #ifdef LIBVLC_NEED_RWLOCK
53 /*** Generic read/write locks ***/
54 #include <stdlib.h>
55 #include <limits.h>
56 /* NOTE:
57  * lock->state is a signed long integer:
58  *  - The sign bit is set when the lock is held for writing.
59  *  - The other bits code the number of times the lock is held for reading.
60  * Consequently:
61  *  - The value is negative if and only if the lock is held for writing.
62  *  - The value is zero if and only if the lock is not held at all.
63  */
64 #define READER_MASK LONG_MAX
65 #define WRITER_BIT  LONG_MIN
66
67 void vlc_rwlock_init (vlc_rwlock_t *lock)
68 {
69     vlc_mutex_init (&lock->mutex);
70     vlc_cond_init (&lock->wait);
71     lock->state = 0;
72 }
73
74 void vlc_rwlock_destroy (vlc_rwlock_t *lock)
75 {
76     vlc_cond_destroy (&lock->wait);
77     vlc_mutex_destroy (&lock->mutex);
78 }
79
80 void vlc_rwlock_rdlock (vlc_rwlock_t *lock)
81 {
82     vlc_mutex_lock (&lock->mutex);
83     /* Recursive read-locking is allowed.
84      * Ensure that there is no active writer. */
85     while (lock->state < 0)
86     {
87         assert (lock->state == WRITER_BIT);
88         mutex_cleanup_push (&lock->mutex);
89         vlc_cond_wait (&lock->wait, &lock->mutex);
90         vlc_cleanup_pop ();
91     }
92     if (unlikely(lock->state >= READER_MASK))
93         abort (); /* An overflow is certainly a recursion bug. */
94     lock->state++;
95     vlc_mutex_unlock (&lock->mutex);
96 }
97
98 void vlc_rwlock_wrlock (vlc_rwlock_t *lock)
99 {
100     vlc_mutex_lock (&lock->mutex);
101     /* Wait until nobody owns the lock in any way. */
102     while (lock->state != 0)
103     {
104         mutex_cleanup_push (&lock->mutex);
105         vlc_cond_wait (&lock->wait, &lock->mutex);
106         vlc_cleanup_pop ();
107     }
108     lock->state = WRITER_BIT;
109     vlc_mutex_unlock (&lock->mutex);
110 }
111
112 void vlc_rwlock_unlock (vlc_rwlock_t *lock)
113 {
114     vlc_mutex_lock (&lock->mutex);
115     if (lock->state < 0)
116     {   /* Write unlock */
117         assert (lock->state == WRITER_BIT);
118         /* Let reader and writer compete. OS scheduler decides who wins. */
119         lock->state = 0;
120         vlc_cond_broadcast (&lock->wait);
121     }
122     else
123     {   /* Read unlock */
124         assert (lock->state > 0);
125         /* If there are no readers left, wake up one pending writer. */
126         if (--lock->state == 0)
127             vlc_cond_signal (&lock->wait);
128     }
129     vlc_mutex_unlock (&lock->mutex);
130 }
131 #endif /* LIBVLC_NEED_RWLOCK */
132
133 #ifdef LIBVLC_NEED_SEMAPHORE
134 /*** Generic semaphores ***/
135 #include <limits.h>
136 #include <errno.h>
137
138 void vlc_sem_init (vlc_sem_t *sem, unsigned value)
139 {
140     vlc_mutex_init (&sem->lock);
141     vlc_cond_init (&sem->wait);
142     sem->value = value;
143 }
144
145 void vlc_sem_destroy (vlc_sem_t *sem)
146 {
147     vlc_cond_destroy (&sem->wait);
148     vlc_mutex_destroy (&sem->lock);
149 }
150
151 int vlc_sem_post (vlc_sem_t *sem)
152 {
153     int ret = 0;
154
155     vlc_mutex_lock (&sem->lock);
156     if (likely(sem->value != UINT_MAX))
157         sem->value++;
158     else
159         ret = EOVERFLOW;
160     vlc_mutex_unlock (&sem->lock);
161     vlc_cond_signal (&sem->wait);
162
163     return ret;
164 }
165
166 void vlc_sem_wait (vlc_sem_t *sem)
167 {
168     vlc_mutex_lock (&sem->lock);
169     mutex_cleanup_push (&lock->mutex);
170     while (!sem->value)
171         vlc_cond_wait (&sem->wait, &sem->lock);
172     sem->value--;
173     vlc_cleanup_run ();
174 }
175 #endif /* LIBVLC_NEED_SEMAPHORE */