thread.c 27.3 KB
Newer Older
1
/*****************************************************************************
2
 * thread.c : pthread back-end for LibVLC
3
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
4
 * Copyright (C) 1999-2009 VLC authors and VideoLAN
5
6
7
8
9
10
11
 *
 * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
 *          Samuel Hocevar <sam@zoy.org>
 *          Gildas Bazin <gbazin@netcourrier.com>
 *          Clément Sténac
 *          Rémi Denis-Courmont
 *
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
12
13
14
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
15
16
17
18
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
19
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
21
 *
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
22
23
24
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25
26
27
28
29
30
31
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>
32
#include <vlc_atomic.h>
33
34
35
36

#include "libvlc.h"
#include <stdarg.h>
#include <signal.h>
37
#include <errno.h>
38
#include <time.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
39
#include <assert.h>
40

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
41
42
#include <sys/types.h>
#include <unistd.h> /* fsync() */
43
#include <pthread.h>
44
#include <sched.h>
45

46
47
48
49
50
51
#ifdef __linux__
# include <sys/syscall.h> /* SYS_gettid */
#endif
#ifdef HAVE_EXECINFO_H
# include <execinfo.h>
#endif
52
53
54
55
56
#if defined(__SunOS)
# include <sys/processor.h>
# include <sys/pset.h>
#endif

57
58
59
60
61
62
63
64
65
66
67
68
69
70
#if !defined (_POSIX_TIMERS)
# define _POSIX_TIMERS (-1)
#endif
#if !defined (_POSIX_CLOCK_SELECTION)
/* Clock selection was defined in 2001 and became mandatory in 2008. */
# define _POSIX_CLOCK_SELECTION (-1)
#endif
#if !defined (_POSIX_MONOTONIC_CLOCK)
# define _POSIX_MONOTONIC_CLOCK (-1)
#endif

#if (_POSIX_TIMERS > 0)
static unsigned vlc_clock_prec;

71
# if (_POSIX_MONOTONIC_CLOCK > 0) && (_POSIX_CLOCK_SELECTION > 0)
72
/* Compile-time POSIX monotonic clock support */
73
#  define vlc_clock_id (CLOCK_MONOTONIC)
74

75
# elif (_POSIX_MONOTONIC_CLOCK == 0) && (_POSIX_CLOCK_SELECTION > 0)
76
77
78
/* Run-time POSIX monotonic clock support (see clock_setup() below) */
static clockid_t vlc_clock_id;

79
# else
80
81
82
83
/* No POSIX monotonic clock support */
#   define vlc_clock_id (CLOCK_REALTIME)
#   warning Monotonic clock not available. Expect timing issues.

84
# endif /* _POSIX_MONOTONIC_CLOKC */
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

static void vlc_clock_setup_once (void)
{
# if (_POSIX_MONOTONIC_CLOCK == 0)
    long val = sysconf (_SC_MONOTONIC_CLOCK);
    assert (val != 0);
    vlc_clock_id = (val < 0) ? CLOCK_REALTIME : CLOCK_MONOTONIC;
# endif

    struct timespec res;
    if (unlikely(clock_getres (vlc_clock_id, &res) != 0 || res.tv_sec != 0))
        abort ();
    vlc_clock_prec = (res.tv_nsec + 500) / 1000;
}

static pthread_once_t vlc_clock_once = PTHREAD_ONCE_INIT;

# define vlc_clock_setup() \
    pthread_once(&vlc_clock_once, vlc_clock_setup_once)

#else /* _POSIX_TIMERS */

# include <sys/time.h> /* gettimeofday() */
# if defined (HAVE_DECL_NANOSLEEP) && !HAVE_DECL_NANOSLEEP
int nanosleep (struct timespec *, struct timespec *);
# endif

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
112
# define vlc_clock_setup() (void)0
113
# warning Monotonic clock not available. Expect timing issues.
114
#endif /* _POSIX_TIMERS */
115

116
117
118
119
120
121
122
123
static struct timespec mtime_to_ts (mtime_t date)
{
    lldiv_t d = lldiv (date, CLOCK_FREQ);
    struct timespec ts = { d.quot, d.rem * (1000000000 / CLOCK_FREQ) };

    return ts;
}

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/**
 * Print a backtrace to the standard error for debugging purpose.
 */
void vlc_trace (const char *fn, const char *file, unsigned line)
{
     fprintf (stderr, "at %s:%u in %s\n", file, line, fn);
     fflush (stderr); /* needed before switch to low-level I/O */
#ifdef HAVE_BACKTRACE
     void *stack[20];
     int len = backtrace (stack, sizeof (stack) / sizeof (stack[0]));
     backtrace_symbols_fd (stack, len, 2);
#endif
     fsync (2);
}

static inline unsigned long vlc_threadid (void)
{
#if defined (__linux__)
     /* glibc does not provide a call for this */
143
     return syscall (__NR_gettid);
144
145
146
147
148
149
150
151
152
153

#else
     union { pthread_t th; unsigned long int i; } v = { };
     v.th = pthread_self ();
     return v.i;

#endif
}

#ifndef NDEBUG
154
155
156
/**
 * Reports a fatal error from the threading layer, for debugging purposes.
 */
157
158
159
160
static void
vlc_thread_fatal (const char *action, int error,
                  const char *function, const char *file, unsigned line)
{
161
    int canc = vlc_savecancel ();
162
163
164
    fprintf (stderr, "LibVLC fatal error %s (%d) in thread %lu ",
             action, error, vlc_threadid ());
    vlc_trace (function, file, line);
165
    perror ("Thread error");
166
167
    fflush (stderr);

168
    vlc_restorecancel (canc);
169
170
171
172
    abort ();
}

# define VLC_THREAD_ASSERT( action ) \
173
174
    if (unlikely(val)) \
        vlc_thread_fatal (action, val, __func__, __FILE__, __LINE__)
175
176
177
178
#else
# define VLC_THREAD_ASSERT( action ) ((void)val)
#endif

179
180
181
/**
 * Initializes a fast mutex.
 */
182
void vlc_mutex_init( vlc_mutex_t *p_mutex )
183
184
185
{
    pthread_mutexattr_t attr;

186
    if (unlikely(pthread_mutexattr_init (&attr)))
187
        abort();
188
#ifdef NDEBUG
189
    pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_DEFAULT);
190
#else
191
    pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ERRORCHECK);
192
#endif
193
    if (unlikely(pthread_mutex_init (p_mutex, &attr)))
194
        abort();
195
196
197
    pthread_mutexattr_destroy( &attr );
}

198
199
200
201
/**
 * Initializes a recursive mutex.
 * \warning This is strongly discouraged. Please use normal mutexes.
 */
202
void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
203
204
205
{
    pthread_mutexattr_t attr;

206
207
    if (unlikely(pthread_mutexattr_init (&attr)))
        abort();
208
    pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
209
    if (unlikely(pthread_mutex_init (p_mutex, &attr)))
210
        abort();
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
    pthread_mutexattr_destroy( &attr );
}


/**
 * Destroys a mutex. The mutex must not be locked.
 *
 * @param p_mutex mutex to destroy
 * @return always succeeds
 */
void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
{
    int val = pthread_mutex_destroy( p_mutex );
    VLC_THREAD_ASSERT ("destroying mutex");
}

#ifndef NDEBUG
# ifdef HAVE_VALGRIND_VALGRIND_H
#  include <valgrind/valgrind.h>
# else
#  define RUNNING_ON_VALGRIND (0)
# endif

234
235
236
/**
 * Asserts that a mutex is locked by the calling thread.
 */
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
void vlc_assert_locked (vlc_mutex_t *p_mutex)
{
    if (RUNNING_ON_VALGRIND > 0)
        return;
    assert (pthread_mutex_lock (p_mutex) == EDEADLK);
}
#endif

/**
 * Acquires a mutex. If needed, waits for any other thread to release it.
 * Beware of deadlocks when locking multiple mutexes at the same time,
 * or when using mutexes from callbacks.
 * This function is not a cancellation-point.
 *
 * @param p_mutex mutex initialized with vlc_mutex_init() or
 *                vlc_mutex_init_recursive()
 */
void vlc_mutex_lock (vlc_mutex_t *p_mutex)
{
    int val = pthread_mutex_lock( p_mutex );
    VLC_THREAD_ASSERT ("locking mutex");
}

/**
 * Acquires a mutex if and only if it is not currently held by another thread.
 * This function never sleeps and can be used in delay-critical code paths.
 * This function is not a cancellation-point.
 *
 * <b>Beware</b>: If this function fails, then the mutex is held... by another
 * thread. The calling thread must deal with the error appropriately. That
 * typically implies postponing the operations that would have required the
 * mutex. If the thread cannot defer those operations, then it must use
 * vlc_mutex_lock(). If in doubt, use vlc_mutex_lock() instead.
 *
 * @param p_mutex mutex initialized with vlc_mutex_init() or
 *                vlc_mutex_init_recursive()
 * @return 0 if the mutex could be acquired, an error code otherwise.
 */
int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
{
    int val = pthread_mutex_trylock( p_mutex );

    if (val != EBUSY)
        VLC_THREAD_ASSERT ("locking mutex");
    return val;
}

/**
 * Releases a mutex (or crashes if the mutex is not locked by the caller).
 * @param p_mutex mutex locked with vlc_mutex_lock().
 */
void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
{
    int val = pthread_mutex_unlock( p_mutex );
    VLC_THREAD_ASSERT ("unlocking mutex");
}

294
295
296
297
/**
 * Initializes a condition variable.
 */
void vlc_cond_init (vlc_cond_t *p_condvar)
298
299
300
{
    pthread_condattr_t attr;

301
    if (unlikely(pthread_condattr_init (&attr)))
302
        abort ();
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
303
#if (_POSIX_CLOCK_SELECTION > 0)
304
    vlc_clock_setup ();
305
    pthread_condattr_setclock (&attr, vlc_clock_id);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
306
#endif
307
    if (unlikely(pthread_cond_init (p_condvar, &attr)))
308
        abort ();
309
310
311
    pthread_condattr_destroy (&attr);
}

312
313
314
315
316
317
318
319
320
321
322
/**
 * Initializes a condition variable.
 * Contrary to vlc_cond_init(), the wall clock will be used as a reference for
 * the vlc_cond_timedwait() time-out parameter.
 */
void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
{
    if (unlikely(pthread_cond_init (p_condvar, NULL)))
        abort ();
}

323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
/**
 * Destroys a condition variable. No threads shall be waiting or signaling the
 * condition.
 * @param p_condvar condition variable to destroy
 */
void vlc_cond_destroy (vlc_cond_t *p_condvar)
{
    int val = pthread_cond_destroy( p_condvar );
    VLC_THREAD_ASSERT ("destroying condition");
}

/**
 * Wakes up one thread waiting on a condition variable, if any.
 * @param p_condvar condition variable
 */
void vlc_cond_signal (vlc_cond_t *p_condvar)
{
    int val = pthread_cond_signal( p_condvar );
    VLC_THREAD_ASSERT ("signaling condition variable");
}

/**
 * Wakes up all threads (if any) waiting on a condition variable.
 * @param p_cond condition variable
 */
void vlc_cond_broadcast (vlc_cond_t *p_condvar)
{
    pthread_cond_broadcast (p_condvar);
}

/**
 * Waits for a condition variable. The calling thread will be suspended until
 * another thread calls vlc_cond_signal() or vlc_cond_broadcast() on the same
 * condition variable, the thread is cancelled with vlc_cancel(), or the
 * system causes a "spurious" unsolicited wake-up.
 *
 * A mutex is needed to wait on a condition variable. It must <b>not</b> be
 * a recursive mutex. Although it is possible to use the same mutex for
 * multiple condition, it is not valid to use different mutexes for the same
 * condition variable at the same time from different threads.
 *
 * In case of thread cancellation, the mutex is always locked before
 * cancellation proceeds.
 *
 * The canonical way to use a condition variable to wait for event foobar is:
 @code
   vlc_mutex_lock (&lock);
   mutex_cleanup_push (&lock); // release the mutex in case of cancellation

   while (!foobar)
       vlc_cond_wait (&wait, &lock);

   --- foobar is now true, do something about it here --

   vlc_cleanup_run (); // release the mutex
  @endcode
 *
 * @param p_condvar condition variable to wait on
 * @param p_mutex mutex which is unlocked while waiting,
 *                then locked again when waking up.
 * @param deadline <b>absolute</b> timeout
 */
void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
{
    int val = pthread_cond_wait( p_condvar, p_mutex );
    VLC_THREAD_ASSERT ("waiting on condition");
}

/**
 * Waits for a condition variable up to a certain date.
393
394
395
396
397
 * This works like vlc_cond_wait(), except for the additional time-out.
 *
 * If the variable was initialized with vlc_cond_init(), the timeout has the
 * same arbitrary origin as mdate(). If the variable was initialized with
 * vlc_cond_init_daytime(), the timeout is expressed from the Unix epoch.
398
399
400
401
402
403
404
405
406
407
408
 *
 * @param p_condvar condition variable to wait on
 * @param p_mutex mutex which is unlocked while waiting,
 *                then locked again when waking up.
 * @param deadline <b>absolute</b> timeout
 *
 * @return 0 if the condition was signaled, an error code in case of timeout.
 */
int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
                        mtime_t deadline)
{
409
    struct timespec ts = mtime_to_ts (deadline);
410
411
412
413
414
415
    int val = pthread_cond_timedwait (p_condvar, p_mutex, &ts);
    if (val != ETIMEDOUT)
        VLC_THREAD_ASSERT ("timed-waiting on condition");
    return val;
}

416
417
418
419
420
/**
 * Initializes a semaphore.
 */
void vlc_sem_init (vlc_sem_t *sem, unsigned value)
{
421
    if (unlikely(sem_init (sem, 0, value)))
422
423
424
425
426
427
428
429
        abort ();
}

/**
 * Destroys a semaphore.
 */
void vlc_sem_destroy (vlc_sem_t *sem)
{
430
431
    int val;

432
433
434
    if (likely(sem_destroy (sem) == 0))
        return;

435
436
    val = errno;

437
438
439
440
441
    VLC_THREAD_ASSERT ("destroying semaphore");
}

/**
 * Increments the value of a semaphore.
442
 * @return 0 on success, EOVERFLOW in case of integer overflow
443
444
445
 */
int vlc_sem_post (vlc_sem_t *sem)
{
446
447
    int val;

448
449
450
    if (likely(sem_post (sem) == 0))
        return 0;

451
452
    val = errno;

453
    if (unlikely(val != EOVERFLOW))
454
455
456
457
458
459
460
461
462
463
464
        VLC_THREAD_ASSERT ("unlocking semaphore");
    return val;
}

/**
 * Atomically wait for the semaphore to become non-zero (if needed),
 * then decrements it.
 */
void vlc_sem_wait (vlc_sem_t *sem)
{
    int val;
465

466
    do
467
468
469
470
        if (likely(sem_wait (sem) == 0))
            return;
    while ((val = errno) == EINTR);

471
472
473
    VLC_THREAD_ASSERT ("locking semaphore");
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
474
475
476
477
478
/**
 * Initializes a read/write lock.
 */
void vlc_rwlock_init (vlc_rwlock_t *lock)
{
479
    if (unlikely(pthread_rwlock_init (lock, NULL)))
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
480
481
482
483
484
485
486
487
488
489
490
491
492
493
        abort ();
}

/**
 * Destroys an initialized unused read/write lock.
 */
void vlc_rwlock_destroy (vlc_rwlock_t *lock)
{
    int val = pthread_rwlock_destroy (lock);
    VLC_THREAD_ASSERT ("destroying R/W lock");
}

/**
 * Acquires a read/write lock for reading. Recursion is allowed.
494
 * @note This function may be a point of cancellation.
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
495
496
497
498
499
500
501
502
503
 */
void vlc_rwlock_rdlock (vlc_rwlock_t *lock)
{
    int val = pthread_rwlock_rdlock (lock);
    VLC_THREAD_ASSERT ("acquiring R/W lock for reading");
}

/**
 * Acquires a read/write lock for writing. Recursion is not allowed.
504
 * @note This function may be a point of cancellation.
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
 */
void vlc_rwlock_wrlock (vlc_rwlock_t *lock)
{
    int val = pthread_rwlock_wrlock (lock);
    VLC_THREAD_ASSERT ("acquiring R/W lock for writing");
}

/**
 * Releases a read/write lock.
 */
void vlc_rwlock_unlock (vlc_rwlock_t *lock)
{
    int val = pthread_rwlock_unlock (lock);
    VLC_THREAD_ASSERT ("releasing R/W lock");
}

521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
/**
 * Allocates a thread-specific variable.
 * @param key where to store the thread-specific variable handle
 * @param destr a destruction callback. It is called whenever a thread exits
 * and the thread-specific variable has a non-NULL value.
 * @return 0 on success, a system error code otherwise. This function can
 * actually fail because there is a fixed limit on the number of
 * thread-specific variable in a process on most systems.
 */
int vlc_threadvar_create (vlc_threadvar_t *key, void (*destr) (void *))
{
    return pthread_key_create (key, destr);
}

void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
{
    pthread_key_delete (*p_tls);
}

/**
 * Sets a thread-specific variable.
 * @param key thread-local variable key (created with vlc_threadvar_create())
 * @param value new value for the variable for the calling thread
 * @return 0 on success, a system error code otherwise.
 */
int vlc_threadvar_set (vlc_threadvar_t key, void *value)
{
    return pthread_setspecific (key, value);
}

/**
 * Gets the value of a thread-local variable for the calling thread.
 * This function cannot fail.
 * @return the value associated with the given variable for the calling
 * or NULL if there is no value.
 */
void *vlc_threadvar_get (vlc_threadvar_t key)
{
    return pthread_getspecific (key);
}

562
563
564
565
566
567
568
569
570
571
572
573
574
static bool rt_priorities = false;
static int rt_offset;

void vlc_threads_setup (libvlc_int_t *p_libvlc)
{
    static vlc_mutex_t lock = VLC_STATIC_MUTEX;
    static bool initialized = false;

    vlc_mutex_lock (&lock);
    /* Initializes real-time priorities before any thread is created,
     * just once per process. */
    if (!initialized)
    {
575
        if (var_InheritBool (p_libvlc, "rt-priority"))
576
        {
577
            rt_offset = var_InheritInteger (p_libvlc, "rt-offset");
578
579
580
581
582
583
584
            rt_priorities = true;
        }
        initialized = true;
    }
    vlc_mutex_unlock (&lock);
}

585
586
587

static int vlc_clone_attr (vlc_thread_t *th, pthread_attr_t *attr,
                           void *(*entry) (void *), void *data, int priority)
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
{
    int ret;

    /* Block the signals that signals interface plugin handles.
     * If the LibVLC caller wants to handle some signals by itself, it should
     * block these before whenever invoking LibVLC. And it must obviously not
     * start the VLC signals interface plugin.
     *
     * LibVLC will normally ignore any interruption caused by an asynchronous
     * signal during a system call. But there may well be some buggy cases
     * where it fails to handle EINTR (bug reports welcome). Some underlying
     * libraries might also not handle EINTR properly.
     */
    sigset_t oldset;
    {
        sigset_t set;
        sigemptyset (&set);
        sigdelset (&set, SIGHUP);
        sigaddset (&set, SIGINT);
        sigaddset (&set, SIGQUIT);
        sigaddset (&set, SIGTERM);

        sigaddset (&set, SIGPIPE); /* We don't want this one, really! */
        pthread_sigmask (SIG_BLOCK, &set, &oldset);
    }

#if defined (_POSIX_PRIORITY_SCHEDULING) && (_POSIX_PRIORITY_SCHEDULING >= 0) \
 && defined (_POSIX_THREAD_PRIORITY_SCHEDULING) \
 && (_POSIX_THREAD_PRIORITY_SCHEDULING >= 0)
617
    if (rt_priorities)
618
    {
619
        struct sched_param sp = { .sched_priority = priority + rt_offset, };
620
621
622
623
624
625
626
        int policy;

        if (sp.sched_priority <= 0)
            sp.sched_priority += sched_get_priority_max (policy = SCHED_OTHER);
        else
            sp.sched_priority += sched_get_priority_min (policy = SCHED_RR);

627
628
        pthread_attr_setschedpolicy (attr, policy);
        pthread_attr_setschedparam (attr, &sp);
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
    }
#else
    (void) priority;
#endif

    /* The thread stack size.
     * The lower the value, the less address space per thread, the highest
     * maximum simultaneous threads per process. Too low values will cause
     * stack overflows and weird crashes. Set with caution. Also keep in mind
     * that 64-bits platforms consume more stack than 32-bits one.
     *
     * Thanks to on-demand paging, thread stack size only affects address space
     * consumption. In terms of memory, threads only use what they need
     * (rounded up to the page boundary).
     *
     * For example, on Linux i386, the default is 2 mega-bytes, which supports
     * about 320 threads per processes. */
#define VLC_STACKSIZE (128 * sizeof (void *) * 1024)

#ifdef VLC_STACKSIZE
649
    ret = pthread_attr_setstacksize (attr, VLC_STACKSIZE);
650
651
652
    assert (ret == 0); /* fails iif VLC_STACKSIZE is invalid */
#endif

653
    ret = pthread_create (th, attr, entry, data);
654
    pthread_sigmask (SIG_SETMASK, &oldset, NULL);
655
    pthread_attr_destroy (attr);
656
657
658
659
    return ret;
}

/**
660
661
662
663
664
665
666
667
668
669
670
 * Creates and starts new thread.
 *
 * The thread must be <i>joined</i> with vlc_join() to reclaim resources
 * when it is not needed anymore.
 *
 * @param th [OUT] pointer to write the handle of the created thread to
 *                 (mandatory, must be non-NULL)
 * @param entry entry point for the thread
 * @param data data parameter given to the entry point
 * @param priority thread priority value
 * @return 0 on success, a standard error code on error.
671
 */
672
673
int vlc_clone (vlc_thread_t *th, void *(*entry) (void *), void *data,
               int priority)
674
{
675
676
677
678
    pthread_attr_t attr;

    pthread_attr_init (&attr);
    return vlc_clone_attr (th, &attr, entry, data, priority);
679
680
681
}

/**
Rémi Denis-Courmont's avatar
Warning    
Rémi Denis-Courmont committed
682
 * Waits for a thread to complete (if needed), then destroys it.
683
684
 * This is a cancellation point; in case of cancellation, the join does _not_
 * occur.
Rémi Denis-Courmont's avatar
Warning    
Rémi Denis-Courmont committed
685
686
 * @warning
 * A thread cannot join itself (normally VLC will abort if this is attempted).
687
 * Also, a detached thread <b>cannot</b> be joined.
688
689
690
691
692
693
694
695
696
697
 *
 * @param handle thread handle
 * @param p_result [OUT] pointer to write the thread return value or NULL
 */
void vlc_join (vlc_thread_t handle, void **result)
{
    int val = pthread_join (handle, result);
    VLC_THREAD_ASSERT ("joining thread");
}

698
/**
699
700
701
 * Creates and starts new detached thread.
 * A detached thread cannot be joined. Its resources will be automatically
 * released whenever the thread exits (in particular, its call stack will be
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
702
 * reclaimed).
703
 *
704
705
706
 * Detached thread are particularly useful when some work needs to be done
 * asynchronously, that is likely to be completed much earlier than the thread
 * can practically be joined. In this case, thread detach can spare memory.
707
 *
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
708
709
710
711
712
713
714
 * A detached thread may be cancelled, so as to expedite its termination.
 * Be extremely careful if you do this: while a normal joinable thread can
 * safely be cancelled after it has already exited, cancelling an already
 * exited detached thread is undefined: The thread handle would is destroyed
 * immediately when the detached thread exits. So you need to ensure that the
 * detached thread is still running before cancellation is attempted.
 *
715
716
 * @warning Care must be taken that any resources used by the detached thread
 * remains valid until the thread completes.
717
 *
718
719
720
721
 * @note A detached thread must eventually exit just like another other
 * thread. In practice, LibVLC will wait for detached threads to exit before
 * it unloads the plugins.
 *
722
 * @param th [OUT] pointer to hold the thread handle, or NULL
723
724
725
726
 * @param entry entry point for the thread
 * @param data data parameter given to the entry point
 * @param priority thread priority value
 * @return 0 on success, a standard error code on error.
727
 */
728
729
int vlc_clone_detach (vlc_thread_t *th, void *(*entry) (void *), void *data,
                      int priority)
730
{
731
    vlc_thread_t dummy;
732
733
    pthread_attr_t attr;

734
735
736
    if (th == NULL)
        th = &dummy;

737
738
    pthread_attr_init (&attr);
    pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
739
    return vlc_clone_attr (th, &attr, entry, data, priority);
740
741
}

742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
int vlc_set_priority (vlc_thread_t th, int priority)
{
#if defined (_POSIX_PRIORITY_SCHEDULING) && (_POSIX_PRIORITY_SCHEDULING >= 0) \
 && defined (_POSIX_THREAD_PRIORITY_SCHEDULING) \
 && (_POSIX_THREAD_PRIORITY_SCHEDULING >= 0)
    if (rt_priorities)
    {
        struct sched_param sp = { .sched_priority = priority + rt_offset, };
        int policy;

        if (sp.sched_priority <= 0)
            sp.sched_priority += sched_get_priority_max (policy = SCHED_OTHER);
        else
            sp.sched_priority += sched_get_priority_min (policy = SCHED_RR);

        if (pthread_setschedparam (th, policy, &sp))
            return VLC_EGENERIC;
    }
#else
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
761
    (void) th; (void) priority;
762
763
764
765
#endif
    return VLC_SUCCESS;
}

766
767
768
769
770
771
772
773
774
775
/**
 * Marks a thread as cancelled. Next time the target thread reaches a
 * cancellation point (while not having disabled cancellation), it will
 * run its cancellation cleanup handler, the thread variable destructors, and
 * terminate. vlc_join() must be used afterward regardless of a thread being
 * cancelled or not.
 */
void vlc_cancel (vlc_thread_t thread_id)
{
    pthread_cancel (thread_id);
776
777
}

778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
/**
 * Save the current cancellation state (enabled or disabled), then disable
 * cancellation for the calling thread.
 * This function must be called before entering a piece of code that is not
 * cancellation-safe, unless it can be proven that the calling thread will not
 * be cancelled.
 * @return Previous cancellation state (opaque value for vlc_restorecancel()).
 */
int vlc_savecancel (void)
{
    int state;
    int val = pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state);

    VLC_THREAD_ASSERT ("saving cancellation");
    return state;
}

/**
 * Restore the cancellation state for the calling thread.
 * @param state previous state as returned by vlc_savecancel().
 * @return Nothing, always succeeds.
 */
void vlc_restorecancel (int state)
{
#ifndef NDEBUG
    int oldstate, val;

    val = pthread_setcancelstate (state, &oldstate);
    /* This should fail if an invalid value for given for state */
    VLC_THREAD_ASSERT ("restoring cancellation");

809
    if (unlikely(oldstate != PTHREAD_CANCEL_DISABLE))
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
         vlc_thread_fatal ("restoring cancellation while not disabled", EINVAL,
                           __func__, __FILE__, __LINE__);
#else
    pthread_setcancelstate (state, NULL);
#endif
}

/**
 * Issues an explicit deferred cancellation point.
 * This has no effect if thread cancellation is disabled.
 * This can be called when there is a rather slow non-sleeping operation.
 * This is also used to force a cancellation point in a function that would
 * otherwise "not always" be a one (block_FifoGet() is an example).
 */
void vlc_testcancel (void)
{
    pthread_testcancel ();
}

void vlc_control_cancel (int cmd, ...)
{
    (void) cmd;
    assert (0);
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
834

835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
/**
 * Precision monotonic clock.
 *
 * In principles, the clock has a precision of 1 MHz. But the actual resolution
 * may be much lower, especially when it comes to sleeping with mwait() or
 * msleep(). Most general-purpose operating systems provide a resolution of
 * only 100 to 1000 Hz.
 *
 * @warning The origin date (time value "zero") is not specified. It is
 * typically the time the kernel started, but this is platform-dependent.
 * If you need wall clock time, use gettimeofday() instead.
 *
 * @return a timestamp in microseconds.
 */
mtime_t mdate (void)
{
#if (_POSIX_TIMERS > 0)
    struct timespec ts;

    vlc_clock_setup ();
    if (unlikely(clock_gettime (vlc_clock_id, &ts) != 0))
        abort ();

    return (INT64_C(1000000) * ts.tv_sec) + (ts.tv_nsec / 1000);

#else
    struct timeval tv;

    if (unlikely(gettimeofday (&tv, NULL) != 0))
        abort ();
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
865
    return (INT64_C(1000000) * tv.tv_sec) + tv.tv_usec;
866
867
868
869
870
871
872
873
874
875
876

#endif
}

#undef mwait
/**
 * Waits until a deadline (possibly later due to OS scheduling).
 * @param deadline timestamp to wait for (see mdate())
 */
void mwait (mtime_t deadline)
{
877
#if (_POSIX_CLOCK_SELECTION > 0)
878
879
880
881
882
    vlc_clock_setup ();
    /* If the deadline is already elapsed, or within the clock precision,
     * do not even bother the system timer. */
    deadline -= vlc_clock_prec;

883
    struct timespec ts = mtime_to_ts (deadline);
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901

    while (clock_nanosleep (vlc_clock_id, TIMER_ABSTIME, &ts, NULL) == EINTR);

#else
    deadline -= mdate ();
    if (deadline > 0)
        msleep (deadline);

#endif
}

#undef msleep
/**
 * Waits for an interval of time.
 * @param delay how long to wait (in microseconds)
 */
void msleep (mtime_t delay)
{
902
    struct timespec ts = mtime_to_ts (delay);
903

904
#if (_POSIX_CLOCK_SELECTION > 0)
905
    vlc_clock_setup ();
906
907
908
909
910
911
912
913
914
    while (clock_nanosleep (vlc_clock_id, 0, &ts, &ts) == EINTR);

#else
    while (nanosleep (&ts, &ts) == -1)
        assert (errno == EINTR);

#endif
}

915

916
917
918
919
920
921
922
923
924
925
/**
 * Count CPUs.
 * @return number of available (logical) CPUs.
 */
unsigned vlc_GetCPUCount(void)
{
#if defined(HAVE_SCHED_GETAFFINITY)
    cpu_set_t cpu;

    CPU_ZERO(&cpu);
926
    if (sched_getaffinity (0, sizeof (cpu), &cpu) < 0)
927
928
        return 1;

929
    return CPU_COUNT (&cpu);
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950

#elif defined(__SunOS)
    unsigned count = 0;
    int type;
    u_int numcpus;
    processor_info_t cpuinfo;

    processorid_t *cpulist = malloc (sizeof (*cpulist) * sysconf(_SC_NPROCESSORS_MAX));
    if (unlikely(cpulist == NULL))
        return 1;

    if (pset_info(PS_MYID, &type, &numcpus, cpulist) == 0)
    {
        for (u_int i = 0; i < numcpus; i++)
            if (processor_info (cpulist[i], &cpuinfo) == 0)
                count += (cpuinfo.pi_state == P_ONLINE);
    }
    else
        count = sysconf (_SC_NPROCESSORS_ONLN);
    free (cpulist);
    return count ? count : 1;
951
#elif defined(_SC_NPROCESSORS_CONF)
952
    return sysconf(_SC_NPROCESSORS_CONF);
953
954
955
956
957
#else
#   warning "vlc_GetCPUCount is not implemented for your platform"
    return 1;
#endif
}