vlc_threads.h 15.3 KB
Newer Older
1
/*****************************************************************************
2 3
 * vlc_threads.h : threads implementation for the VideoLAN client
 * This header provides portable declarations for mutexes & conditions
4
 *****************************************************************************
5
 * Copyright (C) 1999, 2002 the VideoLAN team
6
 * Copyright © 2007-2008 Rémi Denis-Courmont
7
 *
8 9
 * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
 *          Samuel Hocevar <sam@via.ecp.fr>
Gildas Bazin's avatar
 
Gildas Bazin committed
10
 *          Gildas Bazin <gbazin@netcourrier.com>
11
 *          Christophe Massiot <massiot@via.ecp.fr>
12 13 14 15 16
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
17
 *
18 19
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
22
 *
23 24
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
25
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26
 *****************************************************************************/
27

28 29
#ifndef VLC_THREADS_H_
#define VLC_THREADS_H_
30

31 32 33 34 35 36
/**
 * \file
 * This file defines structures and functions for handling threads in vlc
 *
 */

37
#if defined( UNDER_CE )
38 39
#elif defined( WIN32 )
#   include <process.h>                                         /* Win32 API */
Sam Hocevar's avatar
 
Sam Hocevar committed
40

41 42 43 44 45
#elif defined( __OS2__ )                                        /* OS/2 API  */
#   include <errno.h>

#   define pthread_sigmask  sigprocmask

46
#else                                         /* pthreads (like Linux & BSD) */
47
#   define LIBVLC_USE_PTHREAD 1
48
#   define LIBVLC_USE_PTHREAD_CANCEL 1
49
#   define _APPLE_C_SOURCE    1 /* Proper pthread semantics on OSX */
50

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
51
#   include <unistd.h> /* _POSIX_SPIN_LOCKS */
Sam Hocevar's avatar
 
Sam Hocevar committed
52
#   include <pthread.h>
53 54 55 56 57 58 59 60

/* Unnamed POSIX semaphores not supported on Mac OS X, use Mach semaphores instead */
#   if defined (__APPLE__)
#      include <mach/semaphore.h>
#      include <mach/task.h>
#   else
#      include <semaphore.h>
#   endif
61

62
#endif
63

64
/*****************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
65
 * Constants
66
 *****************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
67

68
/* Thread priorities */
69
#ifdef __APPLE__
70 71 72 73 74 75
#   define VLC_THREAD_PRIORITY_LOW      0
#   define VLC_THREAD_PRIORITY_INPUT   22
#   define VLC_THREAD_PRIORITY_AUDIO   22
#   define VLC_THREAD_PRIORITY_VIDEO    0
#   define VLC_THREAD_PRIORITY_OUTPUT  22
#   define VLC_THREAD_PRIORITY_HIGHEST 22
Vincent Seguin's avatar
Vincent Seguin committed
76

77
#elif defined(LIBVLC_USE_PTHREAD)
78 79 80 81 82 83
#   define VLC_THREAD_PRIORITY_LOW      0
#   define VLC_THREAD_PRIORITY_INPUT   10
#   define VLC_THREAD_PRIORITY_AUDIO    5
#   define VLC_THREAD_PRIORITY_VIDEO    0
#   define VLC_THREAD_PRIORITY_OUTPUT  15
#   define VLC_THREAD_PRIORITY_HIGHEST 20
84

85
#elif defined(WIN32) || defined(UNDER_CE)
Gildas Bazin's avatar
 
Gildas Bazin committed
86
/* Define different priorities for WinNT/2K/XP and Win9x/Me */
87
#   define VLC_THREAD_PRIORITY_LOW 0
Gildas Bazin's avatar
 
Gildas Bazin committed
88
#   define VLC_THREAD_PRIORITY_INPUT \
Geoffroy Couprie's avatar
Geoffroy Couprie committed
89
        THREAD_PRIORITY_ABOVE_NORMAL
Gildas Bazin's avatar
 
Gildas Bazin committed
90
#   define VLC_THREAD_PRIORITY_AUDIO \
Geoffroy Couprie's avatar
Geoffroy Couprie committed
91 92
        THREAD_PRIORITY_HIGHEST
#   define VLC_THREAD_PRIORITY_VIDEO 0
Gildas Bazin's avatar
 
Gildas Bazin committed
93
#   define VLC_THREAD_PRIORITY_OUTPUT \
Geoffroy Couprie's avatar
Geoffroy Couprie committed
94
        THREAD_PRIORITY_ABOVE_NORMAL
Gildas Bazin's avatar
 
Gildas Bazin committed
95
#   define VLC_THREAD_PRIORITY_HIGHEST \
Geoffroy Couprie's avatar
Geoffroy Couprie committed
96
        THREAD_PRIORITY_TIME_CRITICAL
97

98 99 100 101 102 103 104 105
#elif defined(__OS2__)
#   define VLC_THREAD_PRIORITY_LOW      0
#   define VLC_THREAD_PRIORITY_INPUT    MAKESHORT( PRTYD_MAXIMUM / 2, PRTYC_REGULAR )
#   define VLC_THREAD_PRIORITY_AUDIO    MAKESHORT( PRTYD_MAXIMUM, PRTYC_REGULAR )
#   define VLC_THREAD_PRIORITY_VIDEO    0
#   define VLC_THREAD_PRIORITY_OUTPUT   MAKESHORT( PRTYD_MAXIMUM / 2, PRTYC_REGULAR )
#   define VLC_THREAD_PRIORITY_HIGHEST  MAKESHORT( 0, PRTYC_TIMECRITICAL )

106 107 108 109
#else
#   define VLC_THREAD_PRIORITY_LOW 0
#   define VLC_THREAD_PRIORITY_INPUT 0
#   define VLC_THREAD_PRIORITY_AUDIO 0
110
#   define VLC_THREAD_PRIORITY_VIDEO 0
111
#   define VLC_THREAD_PRIORITY_OUTPUT 0
112
#   define VLC_THREAD_PRIORITY_HIGHEST 0
Vincent Seguin's avatar
Vincent Seguin committed
113

114
#endif
Vincent Seguin's avatar
Vincent Seguin committed
115

116
/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
117
 * Type definitions
118
 *****************************************************************************/
119

120 121 122
#if defined (LIBVLC_USE_PTHREAD)
typedef pthread_t       vlc_thread_t;
typedef pthread_mutex_t vlc_mutex_t;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
123
#define VLC_STATIC_MUTEX PTHREAD_MUTEX_INITIALIZER
124
typedef pthread_cond_t  vlc_cond_t;
125
#define VLC_STATIC_COND  PTHREAD_COND_INITIALIZER
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
126
typedef pthread_rwlock_t vlc_rwlock_t;
127
#define VLC_STATIC_RWLOCK PTHREAD_RWLOCK_INITIALIZER
128
typedef pthread_key_t   vlc_threadvar_t;
129
typedef struct vlc_timer *vlc_timer_t;
130

131 132 133 134 135 136
#if defined (__APPLE__)
typedef semaphore_t     vlc_sem_t;
#else
typedef sem_t           vlc_sem_t;
#endif

137
#elif defined( WIN32 )
138
typedef struct vlc_thread *vlc_thread_t;
139

140 141
typedef struct
{
142 143 144
    bool dynamic;
    union
    {
145 146 147 148 149
        struct
        {
            bool locked;
            unsigned long contention;
        };
150 151
        CRITICAL_SECTION mutex;
    };
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
152
} vlc_mutex_t;
153
#define VLC_STATIC_MUTEX { false, { { false, 0 } } }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
154

155 156 157 158 159
typedef struct
{
    HANDLE   handle;
    unsigned clock;
} vlc_cond_t;
160
#define VLC_STATIC_COND { 0, 0 }
161

162
typedef HANDLE  vlc_sem_t;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
163 164 165 166

typedef struct
{
    vlc_mutex_t   mutex;
167
    vlc_cond_t    wait;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
168 169 170 171
    unsigned long readers;
    unsigned long writers;
    DWORD         writer;
} vlc_rwlock_t;
172
#define VLC_STATIC_RWLOCK \
173
    { VLC_STATIC_MUTEX, VLC_STATIC_COND, 0, 0, 0 }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
174

175
typedef struct vlc_threadvar *vlc_threadvar_t;
176
typedef struct vlc_timer *vlc_timer_t;
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226

#elif defined( __OS2__ )
typedef struct vlc_thread *vlc_thread_t;

typedef struct
{
    bool dynamic;
    union
    {
        struct
        {
            bool locked;
            unsigned long contention;
        };
        HMTX hmtx;
    };
} vlc_mutex_t;

#define VLC_STATIC_MUTEX { false, { { false, 0 } } }

typedef struct
{
    HEV      hev;
    unsigned clock;
} vlc_cond_t;

#define VLC_STATIC_COND { 0, 0 }

typedef struct
{
    HEV  hev;
    HMTX wait_mutex;
    HMTX count_mutex;
    int  count;
} vlc_sem_t;

typedef struct
{
    vlc_mutex_t   mutex;
    vlc_cond_t    wait;
    unsigned long readers;
    unsigned long writers;
    int           writer;
} vlc_rwlock_t;
#define VLC_STATIC_RWLOCK \
    { VLC_STATIC_MUTEX, VLC_STATIC_COND, 0, 0, 0 }

typedef struct vlc_threadvar *vlc_threadvar_t;
typedef struct vlc_timer *vlc_timer_t;

227 228 229 230 231 232 233 234 235
#endif

#if defined( WIN32 ) && !defined ETIMEDOUT
#  define ETIMEDOUT 10060 /* This is the value in winsock.h. */
#endif

/*****************************************************************************
 * Function definitions
 *****************************************************************************/
236 237 238 239
VLC_API void vlc_mutex_init( vlc_mutex_t * );
VLC_API void vlc_mutex_init_recursive( vlc_mutex_t * );
VLC_API void vlc_mutex_destroy( vlc_mutex_t * );
VLC_API void vlc_mutex_lock( vlc_mutex_t * );
240
VLC_API int vlc_mutex_trylock( vlc_mutex_t * ) VLC_USED;
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
VLC_API void vlc_mutex_unlock( vlc_mutex_t * );
VLC_API void vlc_cond_init( vlc_cond_t * );
VLC_API void vlc_cond_init_daytime( vlc_cond_t * );
VLC_API void vlc_cond_destroy( vlc_cond_t * );
VLC_API void vlc_cond_signal(vlc_cond_t *);
VLC_API void vlc_cond_broadcast(vlc_cond_t *);
VLC_API void vlc_cond_wait(vlc_cond_t *, vlc_mutex_t *);
VLC_API int vlc_cond_timedwait(vlc_cond_t *, vlc_mutex_t *, mtime_t);
VLC_API void vlc_sem_init(vlc_sem_t *, unsigned);
VLC_API void vlc_sem_destroy(vlc_sem_t *);
VLC_API int vlc_sem_post(vlc_sem_t *);
VLC_API void vlc_sem_wait(vlc_sem_t *);

VLC_API void vlc_rwlock_init(vlc_rwlock_t *);
VLC_API void vlc_rwlock_destroy(vlc_rwlock_t *);
VLC_API void vlc_rwlock_rdlock(vlc_rwlock_t *);
VLC_API void vlc_rwlock_wrlock(vlc_rwlock_t *);
VLC_API void vlc_rwlock_unlock(vlc_rwlock_t *);
VLC_API int vlc_threadvar_create(vlc_threadvar_t * , void (*) (void *) );
VLC_API void vlc_threadvar_delete(vlc_threadvar_t *);
VLC_API int vlc_threadvar_set(vlc_threadvar_t, void *);
VLC_API void * vlc_threadvar_get(vlc_threadvar_t);
263

264
VLC_API int vlc_clone(vlc_thread_t *, void * (*) (void *), void *, int) VLC_USED;
265 266 267 268
VLC_API void vlc_cancel(vlc_thread_t);
VLC_API void vlc_join(vlc_thread_t, void **);
VLC_API void vlc_control_cancel (int cmd, ...);

269 270 271 272 273 274 275
VLC_API mtime_t mdate(void);
VLC_API void mwait(mtime_t deadline);
VLC_API void msleep(mtime_t delay);

#define VLC_HARD_MIN_SLEEP   10000 /* 10 milliseconds = 1 tick at 100Hz */
#define VLC_SOFT_MIN_SLEEP 9000000 /* 9 seconds */

276
#if VLC_GCC_VERSION(4,3)
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
/* Linux has 100, 250, 300 or 1000Hz
 *
 * HZ=100 by default on FreeBSD, but some architectures use a 1000Hz timer
 */

static
__attribute__((unused))
__attribute__((noinline))
__attribute__((error("sorry, cannot sleep for such short a time")))
mtime_t impossible_delay( mtime_t delay )
{
    (void) delay;
    return VLC_HARD_MIN_SLEEP;
}

static
__attribute__((unused))
__attribute__((noinline))
__attribute__((warning("use proper event handling instead of short delay")))
mtime_t harmful_delay( mtime_t delay )
{
    return delay;
}

# define check_delay( d ) \
    ((__builtin_constant_p(d < VLC_HARD_MIN_SLEEP) \
   && (d < VLC_HARD_MIN_SLEEP)) \
       ? impossible_delay(d) \
       : ((__builtin_constant_p(d < VLC_SOFT_MIN_SLEEP) \
       && (d < VLC_SOFT_MIN_SLEEP)) \
           ? harmful_delay(d) \
           : d))

static
__attribute__((unused))
__attribute__((noinline))
__attribute__((error("deadlines can not be constant")))
mtime_t impossible_deadline( mtime_t deadline )
{
    return deadline;
}

# define check_deadline( d ) \
    (__builtin_constant_p(d) ? impossible_deadline(d) : d)
#else
# define check_delay(d) (d)
# define check_deadline(d) (d)
#endif

#define msleep(d) msleep(check_delay(d))
#define mwait(d) mwait(check_deadline(d))

329
VLC_API int vlc_timer_create(vlc_timer_t *, void (*) (void *), void *) VLC_USED;
330 331
VLC_API void vlc_timer_destroy(vlc_timer_t);
VLC_API void vlc_timer_schedule(vlc_timer_t, bool, mtime_t, mtime_t);
332
VLC_API unsigned vlc_timer_getoverrun(vlc_timer_t) VLC_USED;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
333

334 335
VLC_API unsigned vlc_GetCPUCount(void);

336
#ifndef LIBVLC_USE_PTHREAD_CANCEL
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
337
enum {
338 339
    VLC_CLEANUP_PUSH,
    VLC_CLEANUP_POP,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
340 341
};
#endif
342

343 344 345
VLC_API int vlc_savecancel(void);
VLC_API void vlc_restorecancel(int state);
VLC_API void vlc_testcancel(void);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
346

347
#if defined (LIBVLC_USE_PTHREAD_CANCEL)
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
/**
 * Registers a new procedure to run if the thread is cancelled (or otherwise
 * exits prematurely). Any call to vlc_cleanup_push() <b>must</b> paired with a
 * call to either vlc_cleanup_pop() or vlc_cleanup_run(). Branching into or out
 * of the block between these two function calls is not allowed (read: it will
 * likely crash the whole process). If multiple procedures are registered,
 * they are handled in last-in first-out order.
 *
 * @param routine procedure to call if the thread ends
 * @param arg argument for the procedure
 */
# define vlc_cleanup_push( routine, arg ) pthread_cleanup_push (routine, arg)

/**
 * Removes a cleanup procedure that was previously registered with
 * vlc_cleanup_push().
 */
# define vlc_cleanup_pop( ) pthread_cleanup_pop (0)

/**
 * Removes a cleanup procedure that was previously registered with
 * vlc_cleanup_push(), and executes it.
 */
# define vlc_cleanup_run( ) pthread_cleanup_pop (1)
#else
typedef struct vlc_cleanup_t vlc_cleanup_t;

struct vlc_cleanup_t
{
    vlc_cleanup_t *next;
    void         (*proc) (void *);
    void          *data;
};

/* This macros opens a code block on purpose. This is needed for multiple
383
 * calls within a single function. This also prevent Win32 developers from
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
 * writing code that would break on POSIX (POSIX opens a block as well). */
# define vlc_cleanup_push( routine, arg ) \
    do { \
        vlc_cleanup_t vlc_cleanup_data = { NULL, routine, arg, }; \
        vlc_control_cancel (VLC_CLEANUP_PUSH, &vlc_cleanup_data)

# define vlc_cleanup_pop( ) \
        vlc_control_cancel (VLC_CLEANUP_POP); \
    } while (0)

# define vlc_cleanup_run( ) \
        vlc_control_cancel (VLC_CLEANUP_POP); \
        vlc_cleanup_data.proc (vlc_cleanup_data.data); \
    } while (0)

399
#endif /* LIBVLC_USE_PTHREAD_CANCEL */
400

401 402 403 404 405 406
static inline void vlc_cleanup_lock (void *lock)
{
    vlc_mutex_unlock ((vlc_mutex_t *)lock);
}
#define mutex_cleanup_push( lock ) vlc_cleanup_push (vlc_cleanup_lock, lock)

407 408 409 410 411 412
# if defined (_POSIX_SPIN_LOCKS) && ((_POSIX_SPIN_LOCKS - 0) > 0)
typedef pthread_spinlock_t vlc_spinlock_t;

/**
 * Initializes a spinlock.
 */
413
static inline void vlc_spin_init (vlc_spinlock_t *spin)
414
{
415 416
    if (pthread_spin_init (spin, PTHREAD_PROCESS_PRIVATE))
        abort ();
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
}

/**
 * Acquires a spinlock.
 */
static inline void vlc_spin_lock (vlc_spinlock_t *spin)
{
    pthread_spin_lock (spin);
}

/**
 * Releases a spinlock.
 */
static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
{
    pthread_spin_unlock (spin);
}

/**
 * Deinitializes a spinlock.
 */
static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
{
    pthread_spin_destroy (spin);
}

443
#elif defined (WIN32) && !defined (UNDER_CE)
444 445 446 447 448 449

typedef CRITICAL_SECTION vlc_spinlock_t;

/**
 * Initializes a spinlock.
 */
450
static inline void vlc_spin_init (vlc_spinlock_t *spin)
451
{
452 453
    if (!InitializeCriticalSectionAndSpinCount(spin, 4000))
        abort ();
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
}

/**
 * Acquires a spinlock.
 */
static inline void vlc_spin_lock (vlc_spinlock_t *spin)
{
    EnterCriticalSection(spin);
}

/**
 * Releases a spinlock.
 */
static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
{
    LeaveCriticalSection(spin);
}

/**
 * Deinitializes a spinlock.
 */
static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
{
    DeleteCriticalSection(spin);
}

#else

/* Fallback to plain mutexes if spinlocks are not available */
typedef vlc_mutex_t vlc_spinlock_t;

485
static inline void vlc_spin_init (vlc_spinlock_t *spin)
486
{
487
    vlc_mutex_init (spin);
488 489 490 491 492 493 494
}

# define vlc_spin_lock    vlc_mutex_lock
# define vlc_spin_unlock  vlc_mutex_unlock
# define vlc_spin_destroy vlc_mutex_destroy
#endif

495 496 497
/**
 * Issues a full memory barrier.
 */
498 499 500
#if defined (__APPLE__)
# include <libkern/OSAtomic.h> /* OSMemoryBarrier() */
#endif
501 502
static inline void barrier (void)
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
503
#if defined (__GNUC__) && !defined (__APPLE__) && \
504
            ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1))
505
    __sync_synchronize ();
506 507 508 509
#elif defined(__APPLE__)
    OSMemoryBarrier ();
#elif defined(__powerpc__)
    asm volatile ("sync":::"memory");
510
#elif 0 // defined(__i386__) /*  Requires SSE2 support */
511
    asm volatile ("mfence":::"memory");
512
#else
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
513
    vlc_spinlock_t spin;
514 515 516 517
    vlc_spin_init (&spin);
    vlc_spin_lock (&spin);
    vlc_spin_unlock (&spin);
    vlc_spin_destroy (&spin);
518 519 520
#endif
}

521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
#ifdef __cplusplus
/**
 * Helper C++ class to lock a mutex.
 * The mutex is locked when the object is created, and unlocked when the object
 * is destroyed.
 */
class vlc_mutex_locker
{
    private:
        vlc_mutex_t *lock;
    public:
        vlc_mutex_locker (vlc_mutex_t *m) : lock (m)
        {
            vlc_mutex_lock (lock);
        }

        ~vlc_mutex_locker (void)
        {
            vlc_mutex_unlock (lock);
        }
};
#endif

544 545 546
enum {
   VLC_AVCODEC_MUTEX = 0,
   VLC_GCRYPT_MUTEX,
547
   VLC_XLIB_MUTEX,
548
   VLC_MOSAIC_MUTEX,
549
   VLC_HIGHLIGHT_MUTEX,
550
   /* Insert new entry HERE */
551 552 553
   VLC_MAX_MUTEX
};

554
VLC_API void vlc_global_mutex( unsigned, bool );
555 556 557
#define vlc_global_lock( n ) vlc_global_mutex( n, true )
#define vlc_global_unlock( n ) vlc_global_mutex( n, false )

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
558
#endif /* !_VLC_THREADS_H */