1 /*****************************************************************************
2 * thread.c : pthread back-end for LibVLC
3 *****************************************************************************
4 * Copyright (C) 1999-2013 VLC authors and VideoLAN
6 * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
7 * Samuel Hocevar <sam@zoy.org>
8 * Gildas Bazin <gbazin@netcourrier.com>
11 * Felix Paul Kühne <fkuehne # videolan.org>
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU Lesser General Public License as published by
15 * the Free Software Foundation; either version 2.1 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public License
24 * along with this program; if not, write to the Free Software Foundation,
25 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_atomic.h>
41 #include <mach/mach_init.h> /* mach_task_self in semaphores */
42 #include <mach/mach_time.h>
45 static mach_timebase_info_data_t vlc_clock_conversion_factor
;
47 static void vlc_clock_setup_once (void)
49 if (unlikely(mach_timebase_info (&vlc_clock_conversion_factor
) != 0))
53 static pthread_once_t vlc_clock_once
= PTHREAD_ONCE_INIT
;
55 #define vlc_clock_setup() \
56 pthread_once(&vlc_clock_once, vlc_clock_setup_once)
58 static struct timespec
mtime_to_ts (mtime_t date
)
60 lldiv_t d
= lldiv (date
, CLOCK_FREQ
);
61 struct timespec ts
= { d
.quot
, d
.rem
* (1000000000 / CLOCK_FREQ
) };
66 /* Print a backtrace to the standard error for debugging purpose. */
67 void vlc_trace (const char *fn
, const char *file
, unsigned line
)
69 fprintf (stderr
, "at %s:%u in %s\n", file
, line
, fn
);
70 fflush (stderr
); /* needed before switch to low-level I/O */
72 int len
= backtrace (stack
, sizeof (stack
) / sizeof (stack
[0]));
73 backtrace_symbols_fd (stack
, len
, 2);
78 /* Reports a fatal error from the threading layer, for debugging purposes. */
80 vlc_thread_fatal (const char *action
, int error
,
81 const char *function
, const char *file
, unsigned line
)
83 int canc
= vlc_savecancel ();
84 fprintf (stderr
, "LibVLC fatal error %s (%d) in thread %lu ",
85 action
, error
, vlc_thread_id ());
86 vlc_trace (function
, file
, line
);
91 switch (strerror_r (error
, buf
, sizeof (buf
)))
96 case ERANGE
: /* should never happen */
97 msg
= "unknown (too big to display)";
100 msg
= "unknown (invalid error number)";
103 fprintf (stderr
, " Error message: %s\n", msg
);
106 vlc_restorecancel (canc
);
110 # define VLC_THREAD_ASSERT( action ) \
112 vlc_thread_fatal (action, val, __func__, __FILE__, __LINE__)
114 # define VLC_THREAD_ASSERT( action ) ((void)val)
117 /* Initializes a fast mutex. */
118 void vlc_mutex_init( vlc_mutex_t
*p_mutex
)
120 pthread_mutexattr_t attr
;
122 if (unlikely(pthread_mutexattr_init (&attr
)))
125 pthread_mutexattr_settype (&attr
, PTHREAD_MUTEX_DEFAULT
);
127 pthread_mutexattr_settype (&attr
, PTHREAD_MUTEX_ERRORCHECK
);
129 if (unlikely(pthread_mutex_init (p_mutex
, &attr
)))
131 pthread_mutexattr_destroy( &attr
);
134 void vlc_mutex_init_recursive( vlc_mutex_t
*p_mutex
)
136 pthread_mutexattr_t attr
;
138 if (unlikely(pthread_mutexattr_init (&attr
)))
140 pthread_mutexattr_settype (&attr
, PTHREAD_MUTEX_RECURSIVE
);
141 if (unlikely(pthread_mutex_init (p_mutex
, &attr
)))
143 pthread_mutexattr_destroy( &attr
);
147 void vlc_mutex_destroy (vlc_mutex_t
*p_mutex
)
149 int val
= pthread_mutex_destroy( p_mutex
);
150 VLC_THREAD_ASSERT ("destroying mutex");
154 # ifdef HAVE_VALGRIND_VALGRIND_H
155 # include <valgrind/valgrind.h>
157 # define RUNNING_ON_VALGRIND (0)
160 void vlc_assert_locked (vlc_mutex_t
*p_mutex
)
162 if (RUNNING_ON_VALGRIND
> 0)
164 assert (pthread_mutex_lock (p_mutex
) == EDEADLK
);
168 void vlc_mutex_lock (vlc_mutex_t
*p_mutex
)
170 int val
= pthread_mutex_lock( p_mutex
);
171 VLC_THREAD_ASSERT ("locking mutex");
174 int vlc_mutex_trylock (vlc_mutex_t
*p_mutex
)
176 int val
= pthread_mutex_trylock( p_mutex
);
179 VLC_THREAD_ASSERT ("locking mutex");
183 void vlc_mutex_unlock (vlc_mutex_t
*p_mutex
)
185 int val
= pthread_mutex_unlock( p_mutex
);
186 VLC_THREAD_ASSERT ("unlocking mutex");
189 void vlc_cond_init (vlc_cond_t
*p_condvar
)
191 if (unlikely(pthread_cond_init (p_condvar
, NULL
)))
195 void vlc_cond_init_daytime (vlc_cond_t
*p_condvar
)
197 if (unlikely(pthread_cond_init (p_condvar
, NULL
)))
201 void vlc_cond_destroy (vlc_cond_t
*p_condvar
)
203 int val
= pthread_cond_destroy (p_condvar
);
205 /* due to a faulty pthread implementation within Darwin 11 and
206 * later condition variables cannot be destroyed without
207 * terminating the application immediately.
208 * This Darwin kernel issue is still present in version 13
209 * and might not be resolved prior to Darwin 15.
212 * To work-around this, we are just leaking the condition variable
213 * which is acceptable due to VLC's low number of created variables
214 * and its usually limited runtime.
215 * Ideally, we should implement a re-useable pool.
219 printf("pthread_cond_destroy returned %i\n", val
);
226 VLC_THREAD_ASSERT ("destroying condition");
229 void vlc_cond_signal (vlc_cond_t
*p_condvar
)
231 int val
= pthread_cond_signal (p_condvar
);
232 VLC_THREAD_ASSERT ("signaling condition variable");
235 void vlc_cond_broadcast (vlc_cond_t
*p_condvar
)
237 pthread_cond_broadcast (p_condvar
);
240 void vlc_cond_wait (vlc_cond_t
*p_condvar
, vlc_mutex_t
*p_mutex
)
242 int val
= pthread_cond_wait (p_condvar
, p_mutex
);
243 VLC_THREAD_ASSERT ("waiting on condition");
246 int vlc_cond_timedwait (vlc_cond_t
*p_condvar
, vlc_mutex_t
*p_mutex
,
249 /* according to POSIX standards, cond_timedwait should be a cancellation point
250 * Of course, Darwin does not care */
251 pthread_testcancel();
254 * mdate() is the monotonic clock, pthread_cond_timedwait expects
255 * origin of gettimeofday(). Use timedwait_relative_np() instead.
257 mtime_t base
= mdate();
262 struct timespec ts
= mtime_to_ts(deadline
);
263 int val
= pthread_cond_timedwait_relative_np(p_condvar
, p_mutex
, &ts
);
264 if (val
!= ETIMEDOUT
)
265 VLC_THREAD_ASSERT ("timed-waiting on condition");
269 /* variant for vlc_cond_init_daytime */
270 int vlc_cond_timedwait_daytime (vlc_cond_t
*p_condvar
, vlc_mutex_t
*p_mutex
,
274 * Note that both pthread_cond_timedwait_relative_np and pthread_cond_timedwait
275 * convert the given timeout to a mach absolute deadline, with system startup
276 * as the time origin. There is no way you can change this behaviour.
278 * For more details, see: https://devforums.apple.com/message/931605
281 pthread_testcancel();
284 * FIXME: It is assumed, that in this case the system waits until the real
285 * time deadline is passed, even if the real time is adjusted in between.
286 * This is not fulfilled, as described above.
288 struct timespec ts
= mtime_to_ts(deadline
);
289 int val
= pthread_cond_timedwait(p_condvar
, p_mutex
, &ts
);
291 if (val
!= ETIMEDOUT
)
292 VLC_THREAD_ASSERT ("timed-waiting on condition");
297 /* Initialize a semaphore. */
298 void vlc_sem_init (vlc_sem_t
*sem
, unsigned value
)
300 if (unlikely(semaphore_create(mach_task_self(), sem
, SYNC_POLICY_FIFO
, value
) != KERN_SUCCESS
))
304 void vlc_sem_destroy (vlc_sem_t
*sem
)
308 if (likely(semaphore_destroy(mach_task_self(), *sem
) == KERN_SUCCESS
))
313 VLC_THREAD_ASSERT ("destroying semaphore");
316 int vlc_sem_post (vlc_sem_t
*sem
)
320 if (likely(semaphore_signal(*sem
) == KERN_SUCCESS
))
325 if (unlikely(val
!= EOVERFLOW
))
326 VLC_THREAD_ASSERT ("unlocking semaphore");
330 void vlc_sem_wait (vlc_sem_t
*sem
)
334 if (likely(semaphore_wait(*sem
) == KERN_SUCCESS
))
339 VLC_THREAD_ASSERT ("locking semaphore");
342 void vlc_rwlock_init (vlc_rwlock_t
*lock
)
344 if (unlikely(pthread_rwlock_init (lock
, NULL
)))
348 void vlc_rwlock_destroy (vlc_rwlock_t
*lock
)
350 int val
= pthread_rwlock_destroy (lock
);
351 VLC_THREAD_ASSERT ("destroying R/W lock");
354 void vlc_rwlock_rdlock (vlc_rwlock_t
*lock
)
356 int val
= pthread_rwlock_rdlock (lock
);
357 VLC_THREAD_ASSERT ("acquiring R/W lock for reading");
360 void vlc_rwlock_wrlock (vlc_rwlock_t
*lock
)
362 int val
= pthread_rwlock_wrlock (lock
);
363 VLC_THREAD_ASSERT ("acquiring R/W lock for writing");
366 void vlc_rwlock_unlock (vlc_rwlock_t
*lock
)
368 int val
= pthread_rwlock_unlock (lock
);
369 VLC_THREAD_ASSERT ("releasing R/W lock");
372 void vlc_once(vlc_once_t
*once
, void (*cb
)(void))
374 int val
= pthread_once(once
, cb
);
375 VLC_THREAD_ASSERT("initializing once");
378 int vlc_threadvar_create (vlc_threadvar_t
*key
, void (*destr
) (void *))
380 return pthread_key_create (key
, destr
);
383 void vlc_threadvar_delete (vlc_threadvar_t
*p_tls
)
385 pthread_key_delete (*p_tls
);
388 int vlc_threadvar_set (vlc_threadvar_t key
, void *value
)
390 return pthread_setspecific (key
, value
);
393 void *vlc_threadvar_get (vlc_threadvar_t key
)
395 return pthread_getspecific (key
);
398 void vlc_threads_setup (libvlc_int_t
*p_libvlc
)
403 static int vlc_clone_attr (vlc_thread_t
*th
, pthread_attr_t
*attr
,
404 void *(*entry
) (void *), void *data
, int priority
)
412 sigdelset (&set
, SIGHUP
);
413 sigaddset (&set
, SIGINT
);
414 sigaddset (&set
, SIGQUIT
);
415 sigaddset (&set
, SIGTERM
);
417 sigaddset (&set
, SIGPIPE
); /* We don't want this one, really! */
418 pthread_sigmask (SIG_BLOCK
, &set
, &oldset
);
423 #define VLC_STACKSIZE (128 * sizeof (void *) * 1024)
426 ret
= pthread_attr_setstacksize (attr
, VLC_STACKSIZE
);
427 assert (ret
== 0); /* fails iif VLC_STACKSIZE is invalid */
430 ret
= pthread_create (th
, attr
, entry
, data
);
431 pthread_sigmask (SIG_SETMASK
, &oldset
, NULL
);
432 pthread_attr_destroy (attr
);
436 int vlc_clone (vlc_thread_t
*th
, void *(*entry
) (void *), void *data
,
441 pthread_attr_init (&attr
);
442 return vlc_clone_attr (th
, &attr
, entry
, data
, priority
);
445 void vlc_join (vlc_thread_t handle
, void **result
)
447 int val
= pthread_join (handle
, result
);
448 VLC_THREAD_ASSERT ("joining thread");
451 int vlc_clone_detach (vlc_thread_t
*th
, void *(*entry
) (void *), void *data
,
460 pthread_attr_init (&attr
);
461 pthread_attr_setdetachstate (&attr
, PTHREAD_CREATE_DETACHED
);
462 return vlc_clone_attr (th
, &attr
, entry
, data
, priority
);
465 vlc_thread_t
vlc_thread_self (void)
467 return pthread_self ();
470 unsigned long vlc_thread_id (void)
475 int vlc_set_priority (vlc_thread_t th
, int priority
)
477 (void) th
; (void) priority
;
481 void vlc_cancel (vlc_thread_t thread_id
)
483 pthread_cancel (thread_id
);
486 int vlc_savecancel (void)
489 int val
= pthread_setcancelstate (PTHREAD_CANCEL_DISABLE
, &state
);
491 VLC_THREAD_ASSERT ("saving cancellation");
495 void vlc_restorecancel (int state
)
500 val
= pthread_setcancelstate (state
, &oldstate
);
501 VLC_THREAD_ASSERT ("restoring cancellation");
503 if (unlikely(oldstate
!= PTHREAD_CANCEL_DISABLE
))
504 vlc_thread_fatal ("restoring cancellation while not disabled", EINVAL
,
505 __func__
, __FILE__
, __LINE__
);
507 pthread_setcancelstate (state
, NULL
);
511 void vlc_testcancel (void)
513 pthread_testcancel ();
516 void vlc_control_cancel (int cmd
, ...)
519 vlc_assert_unreachable ();
525 uint64_t date
= mach_absolute_time();
527 /* denom is uint32_t, switch to 64 bits to prevent overflow. */
528 uint64_t denom
= vlc_clock_conversion_factor
.denom
;
530 /* Switch to microsecs */
533 /* Split the division to prevent overflow */
534 lldiv_t d
= lldiv (vlc_clock_conversion_factor
.numer
, denom
);
536 return (d
.quot
* date
) + ((d
.rem
* date
) / denom
);
540 void mwait (mtime_t deadline
)
542 deadline
-= mdate ();
548 void msleep (mtime_t delay
)
550 struct timespec ts
= mtime_to_ts (delay
);
552 /* nanosleep uses mach_absolute_time and mach_wait_until internally,
553 but also handles kernel errors. Thus we use just this. */
554 while (nanosleep (&ts
, &ts
) == -1)
555 assert (errno
== EINTR
);
558 unsigned vlc_GetCPUCount(void)
560 return sysconf(_SC_NPROCESSORS_CONF
);