threads: remove now unused VLC_HIGHLIGHT_MUTEX
[vlc.git] / src / misc / threads.c
blob9e99ab0d02c15788a83ac8543581f663064c22f1
1 /*****************************************************************************
2 * threads.c: LibVLC generic thread support
3 *****************************************************************************
4 * Copyright (C) 2009-2016 RĂ©mi Denis-Courmont
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.
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.
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 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 #include <assert.h>
26 #include <errno.h>
28 #include <vlc_common.h>
30 /*** Global locks ***/
32 void vlc_global_mutex (unsigned n, bool acquire)
34 static vlc_mutex_t locks[] = {
35 VLC_STATIC_MUTEX,
36 VLC_STATIC_MUTEX,
37 VLC_STATIC_MUTEX,
38 VLC_STATIC_MUTEX,
39 #ifdef _WIN32
40 VLC_STATIC_MUTEX, // For MTA holder
41 #endif
43 static_assert (VLC_MAX_MUTEX == (sizeof (locks) / sizeof (locks[0])),
44 "Wrong number of global mutexes");
45 assert (n < (sizeof (locks) / sizeof (locks[0])));
47 vlc_mutex_t *lock = locks + n;
48 if (acquire)
49 vlc_mutex_lock (lock);
50 else
51 vlc_mutex_unlock (lock);
54 #if defined (_WIN32) && (_WIN32_WINNT < _WIN32_WINNT_WIN8)
55 /* Cannot define OS version-dependent stuff in public headers */
56 # undef LIBVLC_NEED_SLEEP
57 # undef LIBVLC_NEED_SEMAPHORE
58 #endif
60 #if defined(LIBVLC_NEED_SLEEP) || defined(LIBVLC_NEED_CONDVAR)
61 #include <stdatomic.h>
63 static void vlc_cancel_addr_prepare(void *addr)
65 /* Let thread subsystem on address to broadcast for cancellation */
66 vlc_cancel_addr_set(addr);
67 vlc_cleanup_push(vlc_cancel_addr_clear, addr);
68 /* Check if cancellation was pending before vlc_cancel_addr_set() */
69 vlc_testcancel();
70 vlc_cleanup_pop();
73 static void vlc_cancel_addr_finish(void *addr)
75 vlc_cancel_addr_clear(addr);
76 /* Act on cancellation as potential wake-up source */
77 vlc_testcancel();
79 #endif
81 #ifdef LIBVLC_NEED_SLEEP
82 void (vlc_tick_wait)(vlc_tick_t deadline)
84 vlc_tick_t delay;
85 atomic_int value = ATOMIC_VAR_INIT(0);
87 vlc_cancel_addr_prepare(&value);
89 while ((delay = (deadline - vlc_tick_now())) > 0)
91 vlc_addr_timedwait(&value, 0, delay);
92 vlc_testcancel();
95 vlc_cancel_addr_finish(&value);
98 void (vlc_tick_sleep)(vlc_tick_t delay)
100 vlc_tick_wait(vlc_tick_now() + delay);
102 #endif
104 #ifdef LIBVLC_NEED_CONDVAR
105 #include <stdalign.h>
107 static inline atomic_uint *vlc_cond_value(vlc_cond_t *cond)
109 /* XXX: ugly but avoids including stdatomic.h in vlc_threads.h */
110 static_assert (sizeof (cond->value) <= sizeof (atomic_uint),
111 "Size mismatch!");
112 static_assert ((alignof (cond->value) % alignof (atomic_uint)) == 0,
113 "Alignment mismatch");
114 return (atomic_uint *)&cond->value;
117 void vlc_cond_init(vlc_cond_t *cond)
119 /* Initial value is irrelevant but set it for happy debuggers */
120 atomic_init(vlc_cond_value(cond), 0);
123 void vlc_cond_init_daytime(vlc_cond_t *cond)
125 vlc_cond_init(cond);
128 void vlc_cond_destroy(vlc_cond_t *cond)
130 /* Tempting sanity check but actually incorrect:
131 assert((atomic_load_explicit(vlc_cond_value(cond),
132 memory_order_relaxed) & 1) == 0);
133 * Due to timeouts and spurious wake-ups, the futex value can look like
134 * there are waiters, even though there are none. */
135 (void) cond;
138 void vlc_cond_signal(vlc_cond_t *cond)
140 /* Probably the best documented approach is that of Bionic: increment
141 * the futex here, and simply load the value in cnd_wait(). This has a bug
142 * as unlikely as well-known: signals get lost if the futex is incremented
143 * an exact multiple of 2^(CHAR_BIT * sizeof (int)) times.
145 * A different presumably bug-free solution is used here:
146 * - cnd_signal() sets the futex to the equal-or-next odd value, while
147 * - cnd_wait() sets the futex to the equal-or-next even value.
149 atomic_fetch_or_explicit(vlc_cond_value(cond), 1, memory_order_relaxed);
150 vlc_addr_signal(&cond->value);
153 void vlc_cond_broadcast(vlc_cond_t *cond)
155 atomic_fetch_or_explicit(vlc_cond_value(cond), 1, memory_order_relaxed);
156 vlc_addr_broadcast(&cond->value);
159 void vlc_cond_wait(vlc_cond_t *cond, vlc_mutex_t *mutex)
161 unsigned value = atomic_load_explicit(vlc_cond_value(cond),
162 memory_order_relaxed);
163 while (value & 1)
165 if (atomic_compare_exchange_weak_explicit(vlc_cond_value(cond), &value,
166 value + 1,
167 memory_order_relaxed,
168 memory_order_relaxed))
169 value++;
172 vlc_cancel_addr_prepare(&cond->value);
173 vlc_mutex_unlock(mutex);
175 vlc_addr_wait(&cond->value, value);
177 vlc_mutex_lock(mutex);
178 vlc_cancel_addr_finish(&cond->value);
181 static int vlc_cond_wait_delay(vlc_cond_t *cond, vlc_mutex_t *mutex,
182 vlc_tick_t delay)
184 unsigned value = atomic_load_explicit(vlc_cond_value(cond),
185 memory_order_relaxed);
186 while (value & 1)
188 if (atomic_compare_exchange_weak_explicit(vlc_cond_value(cond), &value,
189 value + 1,
190 memory_order_relaxed,
191 memory_order_relaxed))
192 value++;
195 vlc_cancel_addr_prepare(&cond->value);
196 vlc_mutex_unlock(mutex);
198 if (delay > 0)
199 value = vlc_addr_timedwait(&cond->value, value, delay);
200 else
201 value = 0;
203 vlc_mutex_lock(mutex);
204 vlc_cancel_addr_finish(&cond->value);
206 return value ? 0 : ETIMEDOUT;
209 int vlc_cond_timedwait(vlc_cond_t *cond, vlc_mutex_t *mutex, vlc_tick_t deadline)
211 return vlc_cond_wait_delay(cond, mutex, deadline - vlc_tick_now());
214 int vlc_cond_timedwait_daytime(vlc_cond_t *cond, vlc_mutex_t *mutex,
215 time_t deadline_daytime)
217 struct timespec ts;
218 vlc_tick_t deadline = vlc_tick_from_sec( deadline_daytime );
220 timespec_get(&ts, TIME_UTC);
221 deadline -= vlc_tick_from_timespec( &ts );
223 return vlc_cond_wait_delay(cond, mutex, deadline);
225 #endif
227 #ifdef LIBVLC_NEED_RWLOCK
228 /*** Generic read/write locks ***/
229 #include <stdlib.h>
230 #include <limits.h>
231 /* NOTE:
232 * lock->state is a signed long integer:
233 * - The sign bit is set when the lock is held for writing.
234 * - The other bits code the number of times the lock is held for reading.
235 * Consequently:
236 * - The value is negative if and only if the lock is held for writing.
237 * - The value is zero if and only if the lock is not held at all.
239 #define READER_MASK LONG_MAX
240 #define WRITER_BIT LONG_MIN
242 void vlc_rwlock_init (vlc_rwlock_t *lock)
244 vlc_mutex_init (&lock->mutex);
245 vlc_cond_init (&lock->wait);
246 lock->state = 0;
249 void vlc_rwlock_destroy (vlc_rwlock_t *lock)
251 vlc_cond_destroy (&lock->wait);
252 vlc_mutex_destroy (&lock->mutex);
255 void vlc_rwlock_rdlock (vlc_rwlock_t *lock)
257 vlc_mutex_lock (&lock->mutex);
258 /* Recursive read-locking is allowed.
259 * Ensure that there is no active writer. */
260 while (lock->state < 0)
262 assert (lock->state == WRITER_BIT);
263 mutex_cleanup_push (&lock->mutex);
264 vlc_cond_wait (&lock->wait, &lock->mutex);
265 vlc_cleanup_pop ();
267 if (unlikely(lock->state >= READER_MASK))
268 abort (); /* An overflow is certainly a recursion bug. */
269 lock->state++;
270 vlc_mutex_unlock (&lock->mutex);
273 void vlc_rwlock_wrlock (vlc_rwlock_t *lock)
275 vlc_mutex_lock (&lock->mutex);
276 /* Wait until nobody owns the lock in any way. */
277 while (lock->state != 0)
279 mutex_cleanup_push (&lock->mutex);
280 vlc_cond_wait (&lock->wait, &lock->mutex);
281 vlc_cleanup_pop ();
283 lock->state = WRITER_BIT;
284 vlc_mutex_unlock (&lock->mutex);
287 void vlc_rwlock_unlock (vlc_rwlock_t *lock)
289 vlc_mutex_lock (&lock->mutex);
290 if (lock->state < 0)
291 { /* Write unlock */
292 assert (lock->state == WRITER_BIT);
293 /* Let reader and writer compete. OS scheduler decides who wins. */
294 lock->state = 0;
295 vlc_cond_broadcast (&lock->wait);
297 else
298 { /* Read unlock */
299 assert (lock->state > 0);
300 /* If there are no readers left, wake up one pending writer. */
301 if (--lock->state == 0)
302 vlc_cond_signal (&lock->wait);
304 vlc_mutex_unlock (&lock->mutex);
306 #endif /* LIBVLC_NEED_RWLOCK */
308 #ifdef LIBVLC_NEED_SEMAPHORE
309 /*** Generic semaphores ***/
310 #include <limits.h>
311 #include <errno.h>
313 void vlc_sem_init (vlc_sem_t *sem, unsigned value)
315 vlc_mutex_init (&sem->lock);
316 vlc_cond_init (&sem->wait);
317 sem->value = value;
320 void vlc_sem_destroy (vlc_sem_t *sem)
322 vlc_cond_destroy (&sem->wait);
323 vlc_mutex_destroy (&sem->lock);
326 int vlc_sem_post (vlc_sem_t *sem)
328 int ret = 0;
330 vlc_mutex_lock (&sem->lock);
331 if (likely(sem->value != UINT_MAX))
332 sem->value++;
333 else
334 ret = EOVERFLOW;
335 vlc_mutex_unlock (&sem->lock);
336 vlc_cond_signal (&sem->wait);
338 return ret;
341 void vlc_sem_wait (vlc_sem_t *sem)
343 vlc_mutex_lock (&sem->lock);
344 mutex_cleanup_push (&sem->lock);
345 while (!sem->value)
346 vlc_cond_wait (&sem->wait, &sem->lock);
347 sem->value--;
348 vlc_cleanup_pop ();
349 vlc_mutex_unlock (&sem->lock);
351 #endif /* LIBVLC_NEED_SEMAPHORE */