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 *****************************************************************************/
28 #include <stdatomic.h>
30 #include <vlc_common.h>
33 /* <stdatomic.h> types cannot be used in the C++ view of <vlc_threads.h> */
34 struct vlc_suuint { union { unsigned int value; }; };
36 static_assert (sizeof (atomic_uint) <= sizeof (struct vlc_suuint),
38 static_assert (alignof (atomic_uint) <= alignof (struct vlc_suuint),
39 "Alignment mismatch");
41 /*** Global locks ***/
43 void vlc_global_mutex (unsigned n, bool acquire)
45 static vlc_mutex_t locks[] = {
51 VLC_STATIC_MUTEX, // For MTA holder
54 static_assert (VLC_MAX_MUTEX == (sizeof (locks) / sizeof (locks[0])),
55 "Wrong number of global mutexes");
56 assert (n < (sizeof (locks) / sizeof (locks[0])));
58 vlc_mutex_t *lock = locks + n;
60 vlc_mutex_lock (lock);
62 vlc_mutex_unlock (lock);
76 static int vlc_lock_mark_cmp(const void *a, const void *b)
78 const struct vlc_lock_mark *ma = a, *mb = b;
80 if (ma->object == mb->object)
83 return ((uintptr_t)(ma->object) > (uintptr_t)(mb->object)) ? +1 : -1;
86 static void vlc_lock_mark(const void *lock, void **rootp)
88 struct vlc_lock_mark *mark = malloc(sizeof (*mark));
89 if (unlikely(mark == NULL))
95 void **entry = tsearch(mark, rootp, vlc_lock_mark_cmp);
96 if (unlikely(entry == NULL))
99 if (unlikely(*entry != mark)) {
100 /* Recursive locking: lock is already in the tree */
108 static void vlc_lock_unmark(const void *lock, void **rootp)
110 struct vlc_lock_mark *mark = &(struct vlc_lock_mark){ lock, 0 };
111 void **entry = tfind(mark, rootp, vlc_lock_mark_cmp);
113 assert(entry != NULL);
115 assert(mark->refs > 0);
117 if (likely(--mark->refs == 0)) {
118 tdelete(mark, rootp, vlc_lock_mark_cmp);
123 static bool vlc_lock_marked(const void *lock, void **rootp)
125 struct vlc_lock_mark *mark = &(struct vlc_lock_mark){ lock, 0 };
127 return tfind(mark, rootp, vlc_lock_mark_cmp) != NULL;
130 static _Thread_local void *vlc_mutex_marks = NULL;
132 void vlc_mutex_mark(const vlc_mutex_t *mutex)
134 vlc_lock_mark(mutex, &vlc_mutex_marks);
137 void vlc_mutex_unmark(const vlc_mutex_t *mutex)
139 vlc_lock_unmark(mutex, &vlc_mutex_marks);
142 bool vlc_mutex_marked(const vlc_mutex_t *mutex)
144 return vlc_lock_marked(mutex, &vlc_mutex_marks);
147 bool vlc_mutex_marked(const vlc_mutex_t *mutex)
153 #if defined (_WIN32) && (_WIN32_WINNT < _WIN32_WINNT_WIN8)
154 /* Cannot define OS version-dependent stuff in public headers */
155 # undef LIBVLC_NEED_SLEEP
158 #if defined(_WIN32) || defined(__ANDROID__)
159 static void do_vlc_cancel_addr_clear(void *addr)
161 vlc_cancel_addr_clear(addr);
164 static void vlc_cancel_addr_prepare(atomic_uint *addr)
166 /* Let thread subsystem on address to broadcast for cancellation */
167 vlc_cancel_addr_set(addr);
168 vlc_cleanup_push(do_vlc_cancel_addr_clear, addr);
169 /* Check if cancellation was pending before vlc_cancel_addr_set() */
174 static void vlc_cancel_addr_finish(atomic_uint *addr)
176 vlc_cancel_addr_clear(addr);
177 /* Act on cancellation as potential wake-up source */
181 # define vlc_cancel_addr_prepare(addr) ((void)0)
182 # define vlc_cancel_addr_finish(addr) ((void)0)
185 #ifdef LIBVLC_NEED_SLEEP
186 void (vlc_tick_wait)(vlc_tick_t deadline)
188 atomic_uint value = ATOMIC_VAR_INIT(0);
190 vlc_cancel_addr_prepare(&value);
192 while (vlc_atomic_timedwait(&value, 0, deadline) == 0)
195 vlc_cancel_addr_finish(&value);
198 void (vlc_tick_sleep)(vlc_tick_t delay)
200 vlc_tick_wait(vlc_tick_now() + delay);
204 void vlc_cond_init(vlc_cond_t *cond)
207 vlc_mutex_init(&cond->lock);
210 void vlc_cond_init_daytime(vlc_cond_t *cond)
215 void vlc_cond_destroy(vlc_cond_t *cond)
217 assert(cond->head == NULL);
218 vlc_mutex_destroy(&cond->lock);
221 struct vlc_cond_waiter {
222 struct vlc_cond_waiter **pprev, *next;
228 static void vlc_cond_signal_waiter(struct vlc_cond_waiter *waiter)
230 waiter->pprev = &waiter->next;
232 atomic_fetch_add_explicit(&waiter->value, 1, memory_order_relaxed);
233 vlc_atomic_notify_one(&waiter->value);
236 void vlc_cond_signal(vlc_cond_t *cond)
238 struct vlc_cond_waiter *waiter;
240 /* Some call sites signal their condition variable without holding the
241 * corresponding lock. Thus an extra lock is needed here to ensure the
242 * consistency of the linked list and the lifetime of its elements.
243 * If all call sites locked cleanly, the inner lock would be unnecessary.
245 vlc_mutex_lock(&cond->lock);
248 if (waiter != NULL) {
249 struct vlc_cond_waiter *next = waiter->next;
250 struct vlc_cond_waiter **pprev = waiter->pprev;
257 vlc_cond_signal_waiter(waiter);
260 vlc_mutex_unlock(&cond->lock);
263 void vlc_cond_broadcast(vlc_cond_t *cond)
265 struct vlc_cond_waiter *waiter;
267 vlc_mutex_lock(&cond->lock);
271 /* Keep the lock here so that waiters don't go out of scope */
272 while (waiter != NULL) {
273 struct vlc_cond_waiter *next = waiter->next;
275 vlc_cond_signal_waiter(waiter);
279 vlc_mutex_unlock(&cond->lock);
282 static void vlc_cond_wait_prepare(struct vlc_cond_waiter *waiter,
283 vlc_cond_t *cond, vlc_mutex_t *mutex)
285 struct vlc_cond_waiter *next;
287 waiter->pprev = &cond->head;
288 atomic_init(&waiter->value, 0);
290 waiter->mutex = mutex;
292 vlc_mutex_lock(&cond->lock);
298 next->pprev = &waiter->next;
300 vlc_mutex_unlock(&cond->lock);
301 vlc_cancel_addr_prepare(&waiter->value);
302 vlc_mutex_unlock(mutex);
305 static void vlc_cond_wait_finish(struct vlc_cond_waiter *waiter,
306 vlc_cond_t *cond, vlc_mutex_t *mutex)
308 struct vlc_cond_waiter *next;
310 /* If this waiter is still on the linked list, remove it before it goes
311 * out of scope. Otherwise, this is a no-op.
313 vlc_mutex_lock(&cond->lock);
315 *(waiter->pprev) = next;
318 next->pprev = waiter->pprev;
320 vlc_mutex_unlock(&cond->lock);
322 /* Lock the caller's mutex as required by condition variable semantics. */
323 vlc_mutex_lock(mutex);
324 vlc_cancel_addr_finish(&waiter->value);
327 static void vlc_cond_wait_cleanup(void *data)
329 struct vlc_cond_waiter *waiter = data;
331 vlc_cond_wait_finish(waiter, waiter->cond, waiter->mutex);
334 void vlc_cond_wait(vlc_cond_t *cond, vlc_mutex_t *mutex)
336 struct vlc_cond_waiter waiter;
338 vlc_cond_wait_prepare(&waiter, cond, mutex);
339 vlc_cleanup_push(vlc_cond_wait_cleanup, &waiter);
340 vlc_atomic_wait(&waiter.value, 0);
342 vlc_cond_wait_cleanup(&waiter);
345 int vlc_cond_timedwait(vlc_cond_t *cond, vlc_mutex_t *mutex,
348 struct vlc_cond_waiter waiter;
351 vlc_cond_wait_prepare(&waiter, cond, mutex);
352 vlc_cleanup_push(vlc_cond_wait_cleanup, &waiter);
353 ret = vlc_atomic_timedwait(&waiter.value, 0, deadline);
355 vlc_cond_wait_cleanup(&waiter);
360 int vlc_cond_timedwait_daytime(vlc_cond_t *cond, vlc_mutex_t *mutex,
363 struct vlc_cond_waiter waiter;
366 vlc_cond_wait_prepare(&waiter, cond, mutex);
367 vlc_cleanup_push(vlc_cond_wait_cleanup, &waiter);
368 ret = vlc_atomic_timedwait_daytime(&waiter.value, 0, deadline);
370 vlc_cond_wait_cleanup(&waiter);
375 #ifdef LIBVLC_NEED_RWLOCK
376 /*** Generic read/write locks ***/
380 * lock->state is a signed long integer:
381 * - The sign bit is set when the lock is held for writing.
382 * - The other bits code the number of times the lock is held for reading.
384 * - The value is negative if and only if the lock is held for writing.
385 * - The value is zero if and only if the lock is not held at all.
387 #define READER_MASK LONG_MAX
388 #define WRITER_BIT LONG_MIN
390 void vlc_rwlock_init (vlc_rwlock_t *lock)
392 vlc_mutex_init (&lock->mutex);
393 vlc_cond_init (&lock->wait);
397 void vlc_rwlock_destroy (vlc_rwlock_t *lock)
399 vlc_cond_destroy (&lock->wait);
400 vlc_mutex_destroy (&lock->mutex);
403 void vlc_rwlock_rdlock (vlc_rwlock_t *lock)
405 vlc_mutex_lock (&lock->mutex);
406 /* Recursive read-locking is allowed.
407 * Ensure that there is no active writer. */
408 while (lock->state < 0)
410 assert (lock->state == WRITER_BIT);
411 mutex_cleanup_push (&lock->mutex);
412 vlc_cond_wait (&lock->wait, &lock->mutex);
415 if (unlikely(lock->state >= READER_MASK))
416 abort (); /* An overflow is certainly a recursion bug. */
418 vlc_mutex_unlock (&lock->mutex);
421 void vlc_rwlock_wrlock (vlc_rwlock_t *lock)
423 vlc_mutex_lock (&lock->mutex);
424 /* Wait until nobody owns the lock in any way. */
425 while (lock->state != 0)
427 mutex_cleanup_push (&lock->mutex);
428 vlc_cond_wait (&lock->wait, &lock->mutex);
431 lock->state = WRITER_BIT;
432 vlc_mutex_unlock (&lock->mutex);
435 void vlc_rwlock_unlock (vlc_rwlock_t *lock)
437 vlc_mutex_lock (&lock->mutex);
440 assert (lock->state == WRITER_BIT);
441 /* Let reader and writer compete. OS scheduler decides who wins. */
443 vlc_cond_broadcast (&lock->wait);
447 assert (lock->state > 0);
448 /* If there are no readers left, wake up one pending writer. */
449 if (--lock->state == 0)
450 vlc_cond_signal (&lock->wait);
452 vlc_mutex_unlock (&lock->mutex);
454 #endif /* LIBVLC_NEED_RWLOCK */
456 /*** Generic semaphores ***/
458 void vlc_sem_init (vlc_sem_t *sem, unsigned value)
460 atomic_init(&sem->value, value);
463 int vlc_sem_post (vlc_sem_t *sem)
465 unsigned exp = atomic_load_explicit(&sem->value, memory_order_relaxed);
469 if (unlikely(exp == UINT_MAX))
471 } while (!atomic_compare_exchange_weak_explicit(&sem->value, &exp, exp + 1,
472 memory_order_release,
473 memory_order_relaxed));
475 vlc_atomic_notify_one(&sem->value);
479 void vlc_sem_wait (vlc_sem_t *sem)
483 while (!atomic_compare_exchange_weak_explicit(&sem->value, &exp, exp - 1,
484 memory_order_acquire,
485 memory_order_relaxed))
487 if (likely(exp == 0))
489 vlc_atomic_wait(&sem->value, 0);
495 int vlc_sem_timedwait(vlc_sem_t *sem, vlc_tick_t deadline)
499 while (!atomic_compare_exchange_weak_explicit(&sem->value, &exp, exp - 1,
500 memory_order_acquire,
501 memory_order_relaxed))
503 if (likely(exp == 0))
505 int ret = vlc_atomic_timedwait(&sem->value, 0, deadline);