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 /* Print a backtrace to the standard error for debugging purpose. */
59 void vlc_trace (const char *fn
, const char *file
, unsigned line
)
61 fprintf (stderr
, "at %s:%u in %s\n", file
, line
, fn
);
62 fflush (stderr
); /* needed before switch to low-level I/O */
64 int len
= backtrace (stack
, sizeof (stack
) / sizeof (stack
[0]));
65 backtrace_symbols_fd (stack
, len
, 2);
70 /* Reports a fatal error from the threading layer, for debugging purposes. */
72 vlc_thread_fatal (const char *action
, int error
,
73 const char *function
, const char *file
, unsigned line
)
75 int canc
= vlc_savecancel ();
76 fprintf (stderr
, "LibVLC fatal error %s (%d) in thread %lu ",
77 action
, error
, vlc_thread_id ());
78 vlc_trace (function
, file
, line
);
83 switch (strerror_r (error
, buf
, sizeof (buf
)))
88 case ERANGE
: /* should never happen */
89 msg
= "unknown (too big to display)";
92 msg
= "unknown (invalid error number)";
95 fprintf (stderr
, " Error message: %s\n", msg
);
98 vlc_restorecancel (canc
);
102 # define VLC_THREAD_ASSERT( action ) \
104 vlc_thread_fatal (action, val, __func__, __FILE__, __LINE__)
106 # define VLC_THREAD_ASSERT( action ) ((void)val)
109 /* Initializes a fast mutex. */
110 void vlc_mutex_init( vlc_mutex_t
*p_mutex
)
112 pthread_mutexattr_t attr
;
114 if (unlikely(pthread_mutexattr_init (&attr
)))
117 pthread_mutexattr_settype (&attr
, PTHREAD_MUTEX_DEFAULT
);
119 pthread_mutexattr_settype (&attr
, PTHREAD_MUTEX_ERRORCHECK
);
121 if (unlikely(pthread_mutex_init (p_mutex
, &attr
)))
123 pthread_mutexattr_destroy( &attr
);
126 void vlc_mutex_init_recursive( vlc_mutex_t
*p_mutex
)
128 pthread_mutexattr_t attr
;
130 if (unlikely(pthread_mutexattr_init (&attr
)))
132 pthread_mutexattr_settype (&attr
, PTHREAD_MUTEX_RECURSIVE
);
133 if (unlikely(pthread_mutex_init (p_mutex
, &attr
)))
135 pthread_mutexattr_destroy( &attr
);
139 void vlc_mutex_destroy (vlc_mutex_t
*p_mutex
)
141 int val
= pthread_mutex_destroy( p_mutex
);
142 VLC_THREAD_ASSERT ("destroying mutex");
146 # ifdef HAVE_VALGRIND_VALGRIND_H
147 # include <valgrind/valgrind.h>
149 # define RUNNING_ON_VALGRIND (0)
152 void vlc_assert_locked (vlc_mutex_t
*p_mutex
)
154 if (RUNNING_ON_VALGRIND
> 0)
156 assert (pthread_mutex_lock (p_mutex
) == EDEADLK
);
160 void vlc_mutex_lock (vlc_mutex_t
*p_mutex
)
162 int val
= pthread_mutex_lock( p_mutex
);
163 VLC_THREAD_ASSERT ("locking mutex");
166 int vlc_mutex_trylock (vlc_mutex_t
*p_mutex
)
168 int val
= pthread_mutex_trylock( p_mutex
);
171 VLC_THREAD_ASSERT ("locking mutex");
175 void vlc_mutex_unlock (vlc_mutex_t
*p_mutex
)
177 int val
= pthread_mutex_unlock( p_mutex
);
178 VLC_THREAD_ASSERT ("unlocking mutex");
181 void vlc_cond_init (vlc_cond_t
*p_condvar
)
183 if (unlikely(pthread_cond_init (p_condvar
, NULL
)))
187 void vlc_cond_init_daytime (vlc_cond_t
*p_condvar
)
189 if (unlikely(pthread_cond_init (p_condvar
, NULL
)))
193 void vlc_cond_destroy (vlc_cond_t
*p_condvar
)
195 int val
= pthread_cond_destroy (p_condvar
);
197 /* due to a faulty pthread implementation within Darwin 11 and
198 * later condition variables cannot be destroyed without
199 * terminating the application immediately.
200 * This Darwin kernel issue is still present in version 13
201 * and might not be resolved prior to Darwin 15.
204 * To work-around this, we are just leaking the condition variable
205 * which is acceptable due to VLC's low number of created variables
206 * and its usually limited runtime.
207 * Ideally, we should implement a re-useable pool.
211 printf("pthread_cond_destroy returned %i\n", val
);
218 VLC_THREAD_ASSERT ("destroying condition");
221 void vlc_cond_signal (vlc_cond_t
*p_condvar
)
223 int val
= pthread_cond_signal (p_condvar
);
224 VLC_THREAD_ASSERT ("signaling condition variable");
227 void vlc_cond_broadcast (vlc_cond_t
*p_condvar
)
229 pthread_cond_broadcast (p_condvar
);
232 void vlc_cond_wait (vlc_cond_t
*p_condvar
, vlc_mutex_t
*p_mutex
)
234 int val
= pthread_cond_wait (p_condvar
, p_mutex
);
235 VLC_THREAD_ASSERT ("waiting on condition");
238 int vlc_cond_timedwait (vlc_cond_t
*p_condvar
, vlc_mutex_t
*p_mutex
,
241 /* according to POSIX standards, cond_timedwait should be a cancellation point
242 * Of course, Darwin does not care */
243 pthread_testcancel();
246 * vlc_tick_now() is the monotonic clock, pthread_cond_timedwait expects
247 * origin of gettimeofday(). Use timedwait_relative_np() instead.
249 vlc_tick_t base
= vlc_tick_now();
254 struct timespec ts
= timespec_from_vlc_tick(deadline
);
255 int val
= pthread_cond_timedwait_relative_np(p_condvar
, p_mutex
, &ts
);
256 if (val
!= ETIMEDOUT
)
257 VLC_THREAD_ASSERT ("timed-waiting on condition");
261 /* variant for vlc_cond_init_daytime */
262 int vlc_cond_timedwait_daytime (vlc_cond_t
*p_condvar
, vlc_mutex_t
*p_mutex
,
266 * Note that both pthread_cond_timedwait_relative_np and pthread_cond_timedwait
267 * convert the given timeout to a mach absolute deadline, with system startup
268 * as the time origin. There is no way you can change this behaviour.
270 * For more details, see: https://devforums.apple.com/message/931605
273 pthread_testcancel();
276 * FIXME: It is assumed, that in this case the system waits until the real
277 * time deadline is passed, even if the real time is adjusted in between.
278 * This is not fulfilled, as described above.
280 struct timespec ts
= { deadline
, 0 };
281 int val
= pthread_cond_timedwait(p_condvar
, p_mutex
, &ts
);
283 if (val
!= ETIMEDOUT
)
284 VLC_THREAD_ASSERT ("timed-waiting on condition");
289 /* Initialize a semaphore. */
290 void vlc_sem_init (vlc_sem_t
*sem
, unsigned value
)
292 if (unlikely(semaphore_create(mach_task_self(), sem
, SYNC_POLICY_FIFO
, value
) != KERN_SUCCESS
))
296 void vlc_sem_destroy (vlc_sem_t
*sem
)
300 if (likely(semaphore_destroy(mach_task_self(), *sem
) == KERN_SUCCESS
))
305 VLC_THREAD_ASSERT ("destroying semaphore");
308 int vlc_sem_post (vlc_sem_t
*sem
)
312 if (likely(semaphore_signal(*sem
) == KERN_SUCCESS
))
317 if (unlikely(val
!= EOVERFLOW
))
318 VLC_THREAD_ASSERT ("unlocking semaphore");
322 void vlc_sem_wait (vlc_sem_t
*sem
)
326 if (likely(semaphore_wait(*sem
) == KERN_SUCCESS
))
331 VLC_THREAD_ASSERT ("locking semaphore");
334 void vlc_rwlock_init (vlc_rwlock_t
*lock
)
336 if (unlikely(pthread_rwlock_init (lock
, NULL
)))
340 void vlc_rwlock_destroy (vlc_rwlock_t
*lock
)
342 int val
= pthread_rwlock_destroy (lock
);
343 VLC_THREAD_ASSERT ("destroying R/W lock");
346 void vlc_rwlock_rdlock (vlc_rwlock_t
*lock
)
348 int val
= pthread_rwlock_rdlock (lock
);
349 VLC_THREAD_ASSERT ("acquiring R/W lock for reading");
352 void vlc_rwlock_wrlock (vlc_rwlock_t
*lock
)
354 int val
= pthread_rwlock_wrlock (lock
);
355 VLC_THREAD_ASSERT ("acquiring R/W lock for writing");
358 void vlc_rwlock_unlock (vlc_rwlock_t
*lock
)
360 int val
= pthread_rwlock_unlock (lock
);
361 VLC_THREAD_ASSERT ("releasing R/W lock");
364 void vlc_once(vlc_once_t
*once
, void (*cb
)(void))
366 int val
= pthread_once(once
, cb
);
367 VLC_THREAD_ASSERT("initializing once");
370 int vlc_threadvar_create (vlc_threadvar_t
*key
, void (*destr
) (void *))
372 return pthread_key_create (key
, destr
);
375 void vlc_threadvar_delete (vlc_threadvar_t
*p_tls
)
377 pthread_key_delete (*p_tls
);
380 int vlc_threadvar_set (vlc_threadvar_t key
, void *value
)
382 return pthread_setspecific (key
, value
);
385 void *vlc_threadvar_get (vlc_threadvar_t key
)
387 return pthread_getspecific (key
);
390 void vlc_threads_setup (libvlc_int_t
*p_libvlc
)
395 static int vlc_clone_attr (vlc_thread_t
*th
, pthread_attr_t
*attr
,
396 void *(*entry
) (void *), void *data
, int priority
)
404 sigdelset (&set
, SIGHUP
);
405 sigaddset (&set
, SIGINT
);
406 sigaddset (&set
, SIGQUIT
);
407 sigaddset (&set
, SIGTERM
);
409 sigaddset (&set
, SIGPIPE
); /* We don't want this one, really! */
410 pthread_sigmask (SIG_BLOCK
, &set
, &oldset
);
415 #define VLC_STACKSIZE (128 * sizeof (void *) * 1024)
418 ret
= pthread_attr_setstacksize (attr
, VLC_STACKSIZE
);
419 assert (ret
== 0); /* fails iif VLC_STACKSIZE is invalid */
422 ret
= pthread_create (th
, attr
, entry
, data
);
423 pthread_sigmask (SIG_SETMASK
, &oldset
, NULL
);
424 pthread_attr_destroy (attr
);
428 int vlc_clone (vlc_thread_t
*th
, void *(*entry
) (void *), void *data
,
433 pthread_attr_init (&attr
);
434 return vlc_clone_attr (th
, &attr
, entry
, data
, priority
);
437 void vlc_join (vlc_thread_t handle
, void **result
)
439 int val
= pthread_join (handle
, result
);
440 VLC_THREAD_ASSERT ("joining thread");
443 int vlc_clone_detach (vlc_thread_t
*th
, void *(*entry
) (void *), void *data
,
452 pthread_attr_init (&attr
);
453 pthread_attr_setdetachstate (&attr
, PTHREAD_CREATE_DETACHED
);
454 return vlc_clone_attr (th
, &attr
, entry
, data
, priority
);
457 vlc_thread_t
vlc_thread_self (void)
459 return pthread_self ();
462 unsigned long vlc_thread_id (void)
467 int vlc_set_priority (vlc_thread_t th
, int priority
)
469 (void) th
; (void) priority
;
473 void vlc_cancel (vlc_thread_t thread_id
)
475 pthread_cancel (thread_id
);
478 int vlc_savecancel (void)
481 int val
= pthread_setcancelstate (PTHREAD_CANCEL_DISABLE
, &state
);
483 VLC_THREAD_ASSERT ("saving cancellation");
487 void vlc_restorecancel (int state
)
492 val
= pthread_setcancelstate (state
, &oldstate
);
493 VLC_THREAD_ASSERT ("restoring cancellation");
495 if (unlikely(oldstate
!= PTHREAD_CANCEL_DISABLE
))
496 vlc_thread_fatal ("restoring cancellation while not disabled", EINVAL
,
497 __func__
, __FILE__
, __LINE__
);
499 pthread_setcancelstate (state
, NULL
);
503 void vlc_testcancel (void)
505 pthread_testcancel ();
508 void vlc_control_cancel (int cmd
, ...)
511 vlc_assert_unreachable ();
514 vlc_tick_t
vlc_tick_now (void)
517 uint64_t date
= mach_absolute_time();
519 /* denom is uint32_t, switch to 64 bits to prevent overflow. */
520 uint64_t denom
= vlc_clock_conversion_factor
.denom
;
522 /* Switch to microsecs */
525 /* Split the division to prevent overflow */
526 lldiv_t d
= lldiv (vlc_clock_conversion_factor
.numer
, denom
);
528 return (d
.quot
* date
) + ((d
.rem
* date
) / denom
);
532 void vlc_tick_wait (vlc_tick_t deadline
)
534 deadline
-= vlc_tick_now ();
536 vlc_tick_sleep (deadline
);
539 #undef vlc_tick_sleep
540 void vlc_tick_sleep (vlc_tick_t delay
)
542 struct timespec ts
= timespec_from_vlc_tick (delay
);
544 /* nanosleep uses mach_absolute_time and mach_wait_until internally,
545 but also handles kernel errors. Thus we use just this. */
546 while (nanosleep (&ts
, &ts
) == -1)
547 assert (errno
== EINTR
);
550 unsigned vlc_GetCPUCount(void)
552 return sysconf(_SC_NPROCESSORS_CONF
);