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
[] = {
40 VLC_STATIC_MUTEX
, // For MTA holder
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
;
49 vlc_mutex_lock (lock
);
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
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() */
73 static void vlc_cancel_addr_finish(void *addr
)
75 vlc_cancel_addr_clear(addr
);
76 /* Act on cancellation as potential wake-up source */
81 #ifdef LIBVLC_NEED_SLEEP
82 void (vlc_tick_wait
)(vlc_tick_t deadline
)
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
);
95 vlc_cancel_addr_finish(&value
);
98 void (vlc_tick_sleep
)(vlc_tick_t delay
)
100 vlc_tick_wait(vlc_tick_now() + delay
);
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
),
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
)
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. */
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
);
165 if (atomic_compare_exchange_weak_explicit(vlc_cond_value(cond
), &value
,
167 memory_order_relaxed
,
168 memory_order_relaxed
))
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
,
184 unsigned value
= atomic_load_explicit(vlc_cond_value(cond
),
185 memory_order_relaxed
);
188 if (atomic_compare_exchange_weak_explicit(vlc_cond_value(cond
), &value
,
190 memory_order_relaxed
,
191 memory_order_relaxed
))
195 vlc_cancel_addr_prepare(&cond
->value
);
196 vlc_mutex_unlock(mutex
);
199 value
= vlc_addr_timedwait(&cond
->value
, value
, delay
);
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
)
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
);
227 #ifdef LIBVLC_NEED_RWLOCK
228 /*** Generic read/write locks ***/
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.
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
);
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
);
267 if (unlikely(lock
->state
>= READER_MASK
))
268 abort (); /* An overflow is certainly a recursion bug. */
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
);
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
);
292 assert (lock
->state
== WRITER_BIT
);
293 /* Let reader and writer compete. OS scheduler decides who wins. */
295 vlc_cond_broadcast (&lock
->wait
);
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 ***/
313 void vlc_sem_init (vlc_sem_t
*sem
, unsigned value
)
315 vlc_mutex_init (&sem
->lock
);
316 vlc_cond_init (&sem
->wait
);
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
)
330 vlc_mutex_lock (&sem
->lock
);
331 if (likely(sem
->value
!= UINT_MAX
))
335 vlc_mutex_unlock (&sem
->lock
);
336 vlc_cond_signal (&sem
->wait
);
341 void vlc_sem_wait (vlc_sem_t
*sem
)
343 vlc_mutex_lock (&sem
->lock
);
344 mutex_cleanup_push (&sem
->lock
);
346 vlc_cond_wait (&sem
->wait
, &sem
->lock
);
349 vlc_mutex_unlock (&sem
->lock
);
351 #endif /* LIBVLC_NEED_SEMAPHORE */