chromecast: Store the media session id as an integer
[vlc.git] / src / misc / threads.c
blobb1f16254fe67c83f592fec6c28af5fb0d84c5d79
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 VLC_STATIC_MUTEX,
40 #ifdef _WIN32
41 VLC_STATIC_MUTEX, // For MTA holder
42 #endif
44 static_assert (VLC_MAX_MUTEX == (sizeof (locks) / sizeof (locks[0])),
45 "Wrong number of global mutexes");
46 assert (n < (sizeof (locks) / sizeof (locks[0])));
48 vlc_mutex_t *lock = locks + n;
49 if (acquire)
50 vlc_mutex_lock (lock);
51 else
52 vlc_mutex_unlock (lock);
55 #if defined (_WIN32) && (_WIN32_WINNT < _WIN32_WINNT_WIN8)
56 /* Cannot define OS version-dependent stuff in public headers */
57 # undef LIBVLC_NEED_SLEEP
58 # undef LIBVLC_NEED_SEMAPHORE
59 #endif
61 #if defined(LIBVLC_NEED_SLEEP) || defined(LIBVLC_NEED_CONDVAR)
62 #include <stdatomic.h>
64 static void vlc_cancel_addr_prepare(void *addr)
66 /* Let thread subsystem on address to broadcast for cancellation */
67 vlc_cancel_addr_set(addr);
68 vlc_cleanup_push(vlc_cancel_addr_clear, addr);
69 /* Check if cancellation was pending before vlc_cancel_addr_set() */
70 vlc_testcancel();
71 vlc_cleanup_pop();
74 static void vlc_cancel_addr_finish(void *addr)
76 vlc_cancel_addr_clear(addr);
77 /* Act on cancellation as potential wake-up source */
78 vlc_testcancel();
80 #endif
82 #ifdef LIBVLC_NEED_SLEEP
83 void (mwait)(mtime_t deadline)
85 mtime_t delay;
86 atomic_int value = ATOMIC_VAR_INIT(0);
88 vlc_cancel_addr_prepare(&value);
90 while ((delay = (deadline - mdate())) > 0)
92 vlc_addr_timedwait(&value, 0, delay);
93 vlc_testcancel();
96 vlc_cancel_addr_finish(&value);
99 void (msleep)(mtime_t delay)
101 mwait(mdate() + delay);
103 #endif
105 #ifdef LIBVLC_NEED_CONDVAR
106 #include <stdalign.h>
108 static inline atomic_uint *vlc_cond_value(vlc_cond_t *cond)
110 /* XXX: ugly but avoids including stdatomic.h in vlc_threads.h */
111 static_assert (sizeof (cond->value) <= sizeof (atomic_uint),
112 "Size mismatch!");
113 static_assert ((alignof (cond->value) % alignof (atomic_uint)) == 0,
114 "Alignment mismatch");
115 return (atomic_uint *)&cond->value;
118 void vlc_cond_init(vlc_cond_t *cond)
120 /* Initial value is irrelevant but set it for happy debuggers */
121 atomic_init(vlc_cond_value(cond), 0);
124 void vlc_cond_init_daytime(vlc_cond_t *cond)
126 vlc_cond_init(cond);
129 void vlc_cond_destroy(vlc_cond_t *cond)
131 /* Tempting sanity check but actually incorrect:
132 assert((atomic_load_explicit(vlc_cond_value(cond),
133 memory_order_relaxed) & 1) == 0);
134 * Due to timeouts and spurious wake-ups, the futex value can look like
135 * there are waiters, even though there are none. */
136 (void) cond;
139 void vlc_cond_signal(vlc_cond_t *cond)
141 /* Probably the best documented approach is that of Bionic: increment
142 * the futex here, and simply load the value in cnd_wait(). This has a bug
143 * as unlikely as well-known: signals get lost if the futex is incremented
144 * an exact multiple of 2^(CHAR_BIT * sizeof (int)) times.
146 * A different presumably bug-free solution is used here:
147 * - cnd_signal() sets the futex to the equal-or-next odd value, while
148 * - cnd_wait() sets the futex to the equal-or-next even value.
150 atomic_fetch_or_explicit(vlc_cond_value(cond), 1, memory_order_relaxed);
151 vlc_addr_signal(&cond->value);
154 void vlc_cond_broadcast(vlc_cond_t *cond)
156 atomic_fetch_or_explicit(vlc_cond_value(cond), 1, memory_order_relaxed);
157 vlc_addr_broadcast(&cond->value);
160 void vlc_cond_wait(vlc_cond_t *cond, vlc_mutex_t *mutex)
162 unsigned value = atomic_load_explicit(vlc_cond_value(cond),
163 memory_order_relaxed);
164 while (value & 1)
166 if (atomic_compare_exchange_weak_explicit(vlc_cond_value(cond), &value,
167 value + 1,
168 memory_order_relaxed,
169 memory_order_relaxed))
170 value++;
173 vlc_cancel_addr_prepare(&cond->value);
174 vlc_mutex_unlock(mutex);
176 vlc_addr_wait(&cond->value, value);
178 vlc_mutex_lock(mutex);
179 vlc_cancel_addr_finish(&cond->value);
182 static int vlc_cond_wait_delay(vlc_cond_t *cond, vlc_mutex_t *mutex,
183 mtime_t delay)
185 unsigned value = atomic_load_explicit(vlc_cond_value(cond),
186 memory_order_relaxed);
187 while (value & 1)
189 if (atomic_compare_exchange_weak_explicit(vlc_cond_value(cond), &value,
190 value + 1,
191 memory_order_relaxed,
192 memory_order_relaxed))
193 value++;
196 vlc_cancel_addr_prepare(&cond->value);
197 vlc_mutex_unlock(mutex);
199 if (delay > 0)
200 value = vlc_addr_timedwait(&cond->value, value, delay);
201 else
202 value = 0;
204 vlc_mutex_lock(mutex);
205 vlc_cancel_addr_finish(&cond->value);
207 return value ? 0 : ETIMEDOUT;
210 int vlc_cond_timedwait(vlc_cond_t *cond, vlc_mutex_t *mutex, mtime_t deadline)
212 return vlc_cond_wait_delay(cond, mutex, deadline - mdate());
215 int vlc_cond_timedwait_daytime(vlc_cond_t *cond, vlc_mutex_t *mutex,
216 time_t deadline)
218 struct timespec ts;
220 timespec_get(&ts, TIME_UTC);
221 deadline -= ts.tv_sec * CLOCK_FREQ;
222 deadline -= ts.tv_nsec / (1000000000 / CLOCK_FREQ);
224 return vlc_cond_wait_delay(cond, mutex, deadline);
226 #endif
228 #ifdef LIBVLC_NEED_RWLOCK
229 /*** Generic read/write locks ***/
230 #include <stdlib.h>
231 #include <limits.h>
232 /* NOTE:
233 * lock->state is a signed long integer:
234 * - The sign bit is set when the lock is held for writing.
235 * - The other bits code the number of times the lock is held for reading.
236 * Consequently:
237 * - The value is negative if and only if the lock is held for writing.
238 * - The value is zero if and only if the lock is not held at all.
240 #define READER_MASK LONG_MAX
241 #define WRITER_BIT LONG_MIN
243 void vlc_rwlock_init (vlc_rwlock_t *lock)
245 vlc_mutex_init (&lock->mutex);
246 vlc_cond_init (&lock->wait);
247 lock->state = 0;
250 void vlc_rwlock_destroy (vlc_rwlock_t *lock)
252 vlc_cond_destroy (&lock->wait);
253 vlc_mutex_destroy (&lock->mutex);
256 void vlc_rwlock_rdlock (vlc_rwlock_t *lock)
258 vlc_mutex_lock (&lock->mutex);
259 /* Recursive read-locking is allowed.
260 * Ensure that there is no active writer. */
261 while (lock->state < 0)
263 assert (lock->state == WRITER_BIT);
264 mutex_cleanup_push (&lock->mutex);
265 vlc_cond_wait (&lock->wait, &lock->mutex);
266 vlc_cleanup_pop ();
268 if (unlikely(lock->state >= READER_MASK))
269 abort (); /* An overflow is certainly a recursion bug. */
270 lock->state++;
271 vlc_mutex_unlock (&lock->mutex);
274 void vlc_rwlock_wrlock (vlc_rwlock_t *lock)
276 vlc_mutex_lock (&lock->mutex);
277 /* Wait until nobody owns the lock in any way. */
278 while (lock->state != 0)
280 mutex_cleanup_push (&lock->mutex);
281 vlc_cond_wait (&lock->wait, &lock->mutex);
282 vlc_cleanup_pop ();
284 lock->state = WRITER_BIT;
285 vlc_mutex_unlock (&lock->mutex);
288 void vlc_rwlock_unlock (vlc_rwlock_t *lock)
290 vlc_mutex_lock (&lock->mutex);
291 if (lock->state < 0)
292 { /* Write unlock */
293 assert (lock->state == WRITER_BIT);
294 /* Let reader and writer compete. OS scheduler decides who wins. */
295 lock->state = 0;
296 vlc_cond_broadcast (&lock->wait);
298 else
299 { /* Read unlock */
300 assert (lock->state > 0);
301 /* If there are no readers left, wake up one pending writer. */
302 if (--lock->state == 0)
303 vlc_cond_signal (&lock->wait);
305 vlc_mutex_unlock (&lock->mutex);
307 #endif /* LIBVLC_NEED_RWLOCK */
309 #ifdef LIBVLC_NEED_SEMAPHORE
310 /*** Generic semaphores ***/
311 #include <limits.h>
312 #include <errno.h>
314 void vlc_sem_init (vlc_sem_t *sem, unsigned value)
316 vlc_mutex_init (&sem->lock);
317 vlc_cond_init (&sem->wait);
318 sem->value = value;
321 void vlc_sem_destroy (vlc_sem_t *sem)
323 vlc_cond_destroy (&sem->wait);
324 vlc_mutex_destroy (&sem->lock);
327 int vlc_sem_post (vlc_sem_t *sem)
329 int ret = 0;
331 vlc_mutex_lock (&sem->lock);
332 if (likely(sem->value != UINT_MAX))
333 sem->value++;
334 else
335 ret = EOVERFLOW;
336 vlc_mutex_unlock (&sem->lock);
337 vlc_cond_signal (&sem->wait);
339 return ret;
342 void vlc_sem_wait (vlc_sem_t *sem)
344 vlc_mutex_lock (&sem->lock);
345 mutex_cleanup_push (&sem->lock);
346 while (!sem->value)
347 vlc_cond_wait (&sem->wait, &sem->lock);
348 sem->value--;
349 vlc_cleanup_pop ();
350 vlc_mutex_unlock (&sem->lock);
352 #endif /* LIBVLC_NEED_SEMAPHORE */