vlc_threads.h 13.4 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>
gbazin's avatar
   
gbazin 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
dionoea's avatar
dionoea 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 )
geal's avatar
geal committed
38
#   include <errno.h>                                           /* WinCE API */
39
40
#elif defined( WIN32 )
#   include <process.h>                                         /* Win32 API */
41
#   include <errno.h>
Sam Hocevar's avatar
   
Sam Hocevar committed
42

43
#else                                         /* pthreads (like Linux & BSD) */
44
#   define LIBVLC_USE_PTHREAD 1
45
#   define LIBVLC_USE_PTHREAD_CANCEL 1
46
#   define _APPLE_C_SOURCE    1 /* Proper pthread semantics on OSX */
47

Rafaël Carré's avatar
Rafaël Carré committed
48
#   include <stdlib.h> /* lldiv_t definition (only in C99) */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
49
#   include <unistd.h> /* _POSIX_SPIN_LOCKS */
Sam Hocevar's avatar
   
Sam Hocevar committed
50
#   include <pthread.h>
51
52
    /* Needed for pthread_cond_timedwait */
#   include <errno.h>
53
#   include <time.h>
54

55
#endif
56

57
/*****************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
58
 * Constants
59
 *****************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
60

61
/* Thread priorities */
62
#ifdef __APPLE__
63
64
65
66
67
68
#   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
69

70
#elif defined(LIBVLC_USE_PTHREAD)
71
72
73
74
75
76
#   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
77

78
#elif defined(WIN32) || defined(UNDER_CE)
gbazin's avatar
   
gbazin committed
79
/* Define different priorities for WinNT/2K/XP and Win9x/Me */
80
#   define VLC_THREAD_PRIORITY_LOW 0
gbazin's avatar
   
gbazin committed
81
#   define VLC_THREAD_PRIORITY_INPUT \
geal's avatar
geal committed
82
        THREAD_PRIORITY_ABOVE_NORMAL
gbazin's avatar
   
gbazin committed
83
#   define VLC_THREAD_PRIORITY_AUDIO \
geal's avatar
geal committed
84
85
        THREAD_PRIORITY_HIGHEST
#   define VLC_THREAD_PRIORITY_VIDEO 0
gbazin's avatar
   
gbazin committed
86
#   define VLC_THREAD_PRIORITY_OUTPUT \
geal's avatar
geal committed
87
        THREAD_PRIORITY_ABOVE_NORMAL
gbazin's avatar
   
gbazin committed
88
#   define VLC_THREAD_PRIORITY_HIGHEST \
geal's avatar
geal committed
89
        THREAD_PRIORITY_TIME_CRITICAL
90

91
92
93
94
#else
#   define VLC_THREAD_PRIORITY_LOW 0
#   define VLC_THREAD_PRIORITY_INPUT 0
#   define VLC_THREAD_PRIORITY_AUDIO 0
95
#   define VLC_THREAD_PRIORITY_VIDEO 0
96
#   define VLC_THREAD_PRIORITY_OUTPUT 0
97
#   define VLC_THREAD_PRIORITY_HIGHEST 0
Vincent Seguin's avatar
Vincent Seguin committed
98

99
#endif
Vincent Seguin's avatar
Vincent Seguin committed
100

101
/*****************************************************************************
gbazin's avatar
   
gbazin committed
102
 * Type definitions
103
 *****************************************************************************/
104

105
106
107
#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
108
#define VLC_STATIC_MUTEX PTHREAD_MUTEX_INITIALIZER
109
typedef pthread_cond_t  vlc_cond_t;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
110
typedef pthread_rwlock_t vlc_rwlock_t;
111
typedef pthread_key_t   vlc_threadvar_t;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
112
typedef struct vlc_timer_t vlc_timer_t;
113
114
115
116
117
118

#ifndef __APPLE__
/* There is no POSIX timer on Mac OS X. Move that to configure eventually. */
#define HAVE_POSIX_TIMER 1
#endif

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
119
120
struct vlc_timer_t
{
121
#ifdef HAVE_POSIX_TIMER
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
122
    timer_t handle;
123
#endif
124
    void (*func) (void *);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
125
126
    void *data;
};
127

128
#elif defined( WIN32 )
129
130
131
typedef struct
{
    HANDLE handle;
132
    void  *result;
Pierre Ynard's avatar
Pierre Ynard committed
133
134
135
#if defined( UNDER_CE )
    HANDLE cancel_event;
#endif
136
137
} *vlc_thread_t;

138
139
typedef struct
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
140
    LONG initialized;
141
    CRITICAL_SECTION mutex;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
142
} vlc_mutex_t;
143
#define VLC_STATIC_MUTEX { 0, }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
144

145
typedef HANDLE  vlc_cond_t;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
146
147
148
149
150
151
152
153
154
155
156

typedef struct
{
    vlc_mutex_t   mutex;
    vlc_cond_t    read_wait;
    vlc_cond_t    write_wait;
    unsigned long readers;
    unsigned long writers;
    DWORD         writer;
} vlc_rwlock_t;

157
typedef DWORD   vlc_threadvar_t;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
158
159
160
161
typedef struct vlc_timer_t vlc_timer_t;
struct vlc_timer_t
{
    HANDLE handle;
162
    void (*func) (void *);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
163
164
    void *data;
};
165

166
167
168
169
170
171
172
173
174
#endif

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

/*****************************************************************************
 * Function definitions
 *****************************************************************************/
175
176
VLC_EXPORT( void, vlc_mutex_init,    ( vlc_mutex_t * ) );
VLC_EXPORT( void, vlc_mutex_init_recursive, ( vlc_mutex_t * ) );
177
178
VLC_EXPORT( void, vlc_mutex_destroy, ( vlc_mutex_t * ) );
VLC_EXPORT( void, vlc_mutex_lock, ( vlc_mutex_t * ) );
179
VLC_EXPORT( int,  vlc_mutex_trylock, ( vlc_mutex_t * ) LIBVLC_USED );
180
VLC_EXPORT( void, vlc_mutex_unlock, ( vlc_mutex_t * ) );
181
VLC_EXPORT( void, vlc_cond_init,     ( vlc_cond_t * ) );
182
183
VLC_EXPORT( void, vlc_cond_destroy,  ( vlc_cond_t * ) );
VLC_EXPORT( void, vlc_cond_signal, (vlc_cond_t *) );
184
VLC_EXPORT( void, vlc_cond_broadcast, (vlc_cond_t *) );
185
186
VLC_EXPORT( void, vlc_cond_wait, (vlc_cond_t *, vlc_mutex_t *) );
VLC_EXPORT( int, vlc_cond_timedwait, (vlc_cond_t *, vlc_mutex_t *, mtime_t) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
187
188
189
190
191
VLC_EXPORT( void, vlc_rwlock_init, (vlc_rwlock_t *) );
VLC_EXPORT( void, vlc_rwlock_destroy, (vlc_rwlock_t *) );
VLC_EXPORT( void, vlc_rwlock_rdlock, (vlc_rwlock_t *) );
VLC_EXPORT( void, vlc_rwlock_wrlock, (vlc_rwlock_t *) );
VLC_EXPORT( void, vlc_rwlock_unlock, (vlc_rwlock_t *) );
192
193
VLC_EXPORT( int, vlc_threadvar_create, (vlc_threadvar_t * , void (*) (void *) ) );
VLC_EXPORT( void, vlc_threadvar_delete, (vlc_threadvar_t *) );
194
195
VLC_EXPORT( int, vlc_threadvar_set, (vlc_threadvar_t, void *) );
VLC_EXPORT( void *, vlc_threadvar_get, (vlc_threadvar_t) );
196
VLC_EXPORT( int,  vlc_thread_create, ( vlc_object_t *, const char *, int, const char *, void * ( * ) ( vlc_object_t * ), int ) LIBVLC_USED );
197
VLC_EXPORT( int,  __vlc_thread_set_priority, ( vlc_object_t *, const char *, int, int ) );
198
VLC_EXPORT( void, __vlc_thread_join,   ( vlc_object_t * ) );
199

200
VLC_EXPORT( int, vlc_clone, (vlc_thread_t *, void * (*) (void *), void *, int) LIBVLC_USED );
201
VLC_EXPORT( void, vlc_cancel, (vlc_thread_t) );
202
VLC_EXPORT( void, vlc_join, (vlc_thread_t, void **) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
203
204
VLC_EXPORT (void, vlc_control_cancel, (int cmd, ...));

205
VLC_EXPORT( int, vlc_timer_create, (vlc_timer_t *, void (*) (void *), void *) LIBVLC_USED );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
206
207
VLC_EXPORT( void, vlc_timer_destroy, (vlc_timer_t *) );
VLC_EXPORT( void, vlc_timer_schedule, (vlc_timer_t *, bool, mtime_t, mtime_t) );
208
VLC_EXPORT( unsigned, vlc_timer_getoverrun, (const vlc_timer_t *) LIBVLC_USED );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
209

210
#ifndef LIBVLC_USE_PTHREAD_CANCEL
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
211
enum {
212
    VLC_DO_CANCEL,
213
214
    VLC_CLEANUP_PUSH,
    VLC_CLEANUP_POP,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
215
216
};
#endif
217

218
219
220
VLC_EXPORT( int, vlc_savecancel, (void) );
VLC_EXPORT( void, vlc_restorecancel, (int state) );
VLC_EXPORT( void, vlc_testcancel, (void) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
221

222
#if defined (LIBVLC_USE_PTHREAD_CANCEL)
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/**
 * 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
258
 * calls within a single function. This also prevent Win32 developers from
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
 * 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)

274
#endif /* LIBVLC_USE_PTHREAD_CANCEL */
275

276
277
278
279
280
281
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)

282
283
284
285
286
287
# if defined (_POSIX_SPIN_LOCKS) && ((_POSIX_SPIN_LOCKS - 0) > 0)
typedef pthread_spinlock_t vlc_spinlock_t;

/**
 * Initializes a spinlock.
 */
288
static inline void vlc_spin_init (vlc_spinlock_t *spin)
289
{
290
291
    if (pthread_spin_init (spin, PTHREAD_PROCESS_PRIVATE))
        abort ();
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
}

/**
 * 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);
}

318
#elif defined (WIN32) && !defined (UNDER_CE)
319
320
321
322
323
324

typedef CRITICAL_SECTION vlc_spinlock_t;

/**
 * Initializes a spinlock.
 */
325
static inline void vlc_spin_init (vlc_spinlock_t *spin)
326
{
327
328
    if (!InitializeCriticalSectionAndSpinCount(spin, 4000))
        abort ();
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
}

/**
 * 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;

360
static inline void vlc_spin_init (vlc_spinlock_t *spin)
361
{
362
    vlc_mutex_init (spin);
363
364
365
366
367
368
369
}

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

370
371
372
/**
 * Issues a full memory barrier.
 */
373
374
375
#if defined (__APPLE__)
# include <libkern/OSAtomic.h> /* OSMemoryBarrier() */
#endif
376
377
static inline void barrier (void)
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
378
#if defined (__GNUC__) && !defined (__APPLE__) && \
379
            ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1))
380
    __sync_synchronize ();
381
382
383
384
#elif defined(__APPLE__)
    OSMemoryBarrier ();
#elif defined(__powerpc__)
    asm volatile ("sync":::"memory");
385
#elif 0 // defined(__i386__) /*  Requires SSE2 support */
386
    asm volatile ("mfence":::"memory");
387
#else
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
388
    vlc_spinlock_t spin;
389
390
391
392
    vlc_spin_init (&spin);
    vlc_spin_lock (&spin);
    vlc_spin_unlock (&spin);
    vlc_spin_destroy (&spin);
393
394
395
#endif
}

396
397
398
/*****************************************************************************
 * vlc_thread_create: create a thread
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
399
400
#define vlc_thread_create( P_THIS, PSZ_NAME, FUNC, PRIORITY )         \
    vlc_thread_create( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PSZ_NAME, FUNC, PRIORITY )
401
402
403
404
405
406
407
408
409
410
411

/*****************************************************************************
 * vlc_thread_set_priority: set the priority of the calling thread
 *****************************************************************************/
#define vlc_thread_set_priority( P_THIS, PRIORITY )                         \
    __vlc_thread_set_priority( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PRIORITY )

/*****************************************************************************
 * vlc_thread_join: wait until a thread exits
 *****************************************************************************/
#define vlc_thread_join( P_THIS )                                           \
412
    __vlc_thread_join( VLC_OBJECT(P_THIS) )
413

414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
#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

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