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 <vlc_common.h>
30 /*** Global locks ***/
32 void vlc_global_mutex (unsigned n
, bool acquire
)
34 static vlc_mutex_t locks
[] = {
41 VLC_STATIC_MUTEX
, // For MTA holder
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
;
50 vlc_mutex_lock (lock
);
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
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() */
74 static void vlc_cancel_addr_finish(void *addr
)
76 vlc_cancel_addr_clear(addr
);
77 /* Act on cancellation as potential wake-up source */
82 #ifdef LIBVLC_NEED_SLEEP
83 void (vlc_tick_wait
)(vlc_tick_t deadline
)
86 atomic_int value
= ATOMIC_VAR_INIT(0);
88 vlc_cancel_addr_prepare(&value
);
90 while ((delay
= (deadline
- vlc_tick_now())) > 0)
92 vlc_addr_timedwait(&value
, 0, delay
);
96 vlc_cancel_addr_finish(&value
);
99 void (vlc_tick_sleep
)(vlc_tick_t delay
)
101 vlc_tick_wait(vlc_tick_now() + delay
);
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
),
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
)
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. */
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
);
166 if (atomic_compare_exchange_weak_explicit(vlc_cond_value(cond
), &value
,
168 memory_order_relaxed
,
169 memory_order_relaxed
))
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
,
185 unsigned value
= atomic_load_explicit(vlc_cond_value(cond
),
186 memory_order_relaxed
);
189 if (atomic_compare_exchange_weak_explicit(vlc_cond_value(cond
), &value
,
191 memory_order_relaxed
,
192 memory_order_relaxed
))
196 vlc_cancel_addr_prepare(&cond
->value
);
197 vlc_mutex_unlock(mutex
);
200 value
= vlc_addr_timedwait(&cond
->value
, value
, delay
);
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
, vlc_tick_t deadline
)
212 return vlc_cond_wait_delay(cond
, mutex
, deadline
- vlc_tick_now());
215 int vlc_cond_timedwait_daytime(vlc_cond_t
*cond
, vlc_mutex_t
*mutex
,
216 time_t deadline_daytime
)
219 vlc_tick_t deadline
= vlc_tick_from_sec( deadline_daytime
);
221 timespec_get(&ts
, TIME_UTC
);
222 deadline
-= vlc_tick_from_timespec( &ts
);
224 return vlc_cond_wait_delay(cond
, mutex
, deadline
);
228 #ifdef LIBVLC_NEED_RWLOCK
229 /*** Generic read/write locks ***/
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.
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
);
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
);
268 if (unlikely(lock
->state
>= READER_MASK
))
269 abort (); /* An overflow is certainly a recursion bug. */
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
);
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
);
293 assert (lock
->state
== WRITER_BIT
);
294 /* Let reader and writer compete. OS scheduler decides who wins. */
296 vlc_cond_broadcast (&lock
->wait
);
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 ***/
314 void vlc_sem_init (vlc_sem_t
*sem
, unsigned value
)
316 vlc_mutex_init (&sem
->lock
);
317 vlc_cond_init (&sem
->wait
);
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
)
331 vlc_mutex_lock (&sem
->lock
);
332 if (likely(sem
->value
!= UINT_MAX
))
336 vlc_mutex_unlock (&sem
->lock
);
337 vlc_cond_signal (&sem
->wait
);
342 void vlc_sem_wait (vlc_sem_t
*sem
)
344 vlc_mutex_lock (&sem
->lock
);
345 mutex_cleanup_push (&sem
->lock
);
347 vlc_cond_wait (&sem
->wait
, &sem
->lock
);
350 vlc_mutex_unlock (&sem
->lock
);
352 #endif /* LIBVLC_NEED_SEMAPHORE */