]> git.sesse.net Git - vlc/blob - include/threads.h
9 avril:
[vlc] / include / threads.h
1 /*****************************************************************************
2  * threads.h : threads implementation for the VideoLAN client
3  * This header provides a portable threads implementation.
4  *****************************************************************************
5  * Copyright (C) 1999, 2000 VideoLAN
6  *
7  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
8  *          Samuel Hocevar <sam@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public
21  * License along with this program; if not, write to the
22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  *****************************************************************************/
25
26 #if defined(HAVE_PTHREAD_H)            /* pthreads (Linux & BSD for example) */
27 #include <pthread.h>
28
29 #elif defined(HAVE_CTHREADS_H)                                    /* GNUMach */
30 #include <cthreads.h>
31
32 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)   /* BeOS */
33 #include <kernel/OS.h>
34 #include <kernel/scheduler.h>
35
36 #else
37 #error no threads available on your system !
38 #endif
39
40 /*****************************************************************************
41  * Constants
42  *****************************************************************************
43  * These constants are used by all threads in *_CreateThread() and
44  * *_DestroyThreads() functions. Since those calls are non-blocking, an integer
45  * value is used as a shared flag to represent the status of the thread.
46  *****************************************************************************/
47
48 /* Void status - this value can be used to make sure no operation is currently
49  * in progress on the concerned thread in an array of recorded threads */
50 #define THREAD_NOP          0                            /* nothing happened */
51
52 /* Creation status */
53 #define THREAD_CREATE       10                     /* thread is initializing */
54 #define THREAD_START        11                          /* thread has forked */
55 #define THREAD_READY        19                            /* thread is ready */
56
57 /* Destructions status */
58 #define THREAD_DESTROY      20            /* destruction order has been sent */
59 #define THREAD_END          21        /* destruction order has been received */
60 #define THREAD_OVER         29             /* thread does not exist any more */
61
62 /* Error status */
63 #define THREAD_ERROR        30                           /* an error occured */
64 #define THREAD_FATAL        31  /* an fatal error occured - program must end */
65
66 /*****************************************************************************
67  * Types definition
68  *****************************************************************************/
69
70 #if defined(HAVE_CTHREADS_H)
71
72 typedef cthread_t        vlc_thread_t;
73
74 /* those structs are the ones defined in /include/cthreads.h but we need
75  * to handle (*foo) where foo is a (mutex_t) while they handle (foo) where
76  * foo is a (mutex_t*) */
77 typedef struct s_mutex {
78     spin_lock_t held;
79     spin_lock_t lock;
80     char *name;
81     struct cthread_queue queue;
82 } vlc_mutex_t;
83
84 typedef struct s_condition {
85     spin_lock_t lock;
86     struct cthread_queue queue;
87     char *name;
88     struct cond_imp *implications;
89 } vlc_cond_t;
90
91 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
92
93 typedef thread_id vlc_thread_t;
94
95 typedef struct
96 {
97     int32           init;
98     sem_id          lock;
99     thread_id       owner;
100 } vlc_mutex_t;
101
102 typedef struct
103 {
104     int32           init;
105     sem_id          sem;
106     sem_id          handshakeSem;
107     sem_id          signalSem;
108     volatile int32  nw;
109     volatile int32  ns;
110 } vlc_cond_t;
111
112 #elif defined(HAVE_PTHREAD_H)
113
114 typedef pthread_t        vlc_thread_t;
115 typedef pthread_mutex_t  vlc_mutex_t;
116 typedef pthread_cond_t   vlc_cond_t;
117
118 #endif
119
120 typedef void *(*vlc_thread_func_t)(void *p_data);
121
122 /*****************************************************************************
123  * Prototypes
124  *****************************************************************************/
125
126 static __inline__ int  vlc_thread_create( vlc_thread_t *p_thread, char *psz_name,
127                                           vlc_thread_func_t func, void *p_data );
128 static __inline__ void vlc_thread_exit  ( void );
129 static __inline__ void vlc_thread_join  ( vlc_thread_t thread );
130
131 static __inline__ int  vlc_mutex_init   ( vlc_mutex_t *p_mutex );
132 static __inline__ int  vlc_mutex_lock   ( vlc_mutex_t *p_mutex );
133 static __inline__ int  vlc_mutex_unlock ( vlc_mutex_t *p_mutex );
134
135 static __inline__ int  vlc_cond_init    ( vlc_cond_t *p_condvar );
136 static __inline__ int  vlc_cond_signal  ( vlc_cond_t *p_condvar );
137 static __inline__ int  vlc_cond_wait    ( vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex );
138
139 #if 0
140 static _inline__ int    vlc_cond_timedwait   ( vlc_cond_t * condvar, vlc_mutex_t * mutex,
141                               mtime_t absoute_timeout_time );
142 #endif
143
144 /*****************************************************************************
145  * vlc_thread_create: create a thread
146  *****************************************************************************/
147 static __inline__ int vlc_thread_create( vlc_thread_t *p_thread,
148                                          char *psz_name, vlc_thread_func_t func,
149                                          void *p_data)
150 {
151 #if defined(HAVE_CTHREADS_H)
152     *p_thread = cthread_fork( (cthread_fn_t)func, (any_t)p_data );
153     return 0;
154
155 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
156     *p_thread = spawn_thread( (thread_func)func, psz_name,
157                               B_NORMAL_PRIORITY, p_data );
158     return resume_thread( *p_thread );
159
160 #elif defined(HAVE_PTHREAD_H)
161     return pthread_create( p_thread, NULL, func, p_data );
162
163 #endif
164 }
165
166 /*****************************************************************************
167  * vlc_thread_exit: terminate a thread
168  *****************************************************************************/
169 static __inline__ void vlc_thread_exit( void )
170 {
171 #if defined(HAVE_CTHREADS_H)
172     int result;
173     cthread_exit( &result );
174
175 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
176     exit_thread( 0 );
177
178 #elif defined(HAVE_PTHREAD_H)
179     pthread_exit( 0 );
180
181 #endif
182 }
183
184 /*****************************************************************************
185  * vlc_thread_join: wait until a thread exits
186  *****************************************************************************/
187 static __inline__ void vlc_thread_join( vlc_thread_t thread )
188 {
189 #if defined(HAVE_CTHREADS_H)
190     cthread_join( thread );
191
192 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
193     int32 exit_value;
194     wait_for_thread( thread, &exit_value );
195
196 #elif defined(HAVE_PTHREAD_H)
197     pthread_join( thread, NULL );
198
199 #endif
200 }
201
202 #if defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
203 /* lazy_init_mutex */
204 static __inline__ void lazy_init_mutex(vlc_mutex_t* p_mutex)
205 {
206     int32 v = atomic_or( &p_mutex->init, 1 );
207     if( 2000 == v ) /* we're the first, so do the init */
208     {
209         vlc_mutex_init( p_mutex );
210     }
211     else /* we're not the first, so wait until the init is finished */
212     {
213         while( p_mutex->init != 9999 )
214         {
215             snooze( 10000 );
216         }
217     }
218 }
219 #endif
220
221 /*****************************************************************************
222  * vlc_mutex_init: initialize a mutex
223  *****************************************************************************/
224 static __inline__ int vlc_mutex_init( vlc_mutex_t *p_mutex )
225 {
226 #if defined(HAVE_CTHREADS_H)
227     mutex_init( p_mutex );
228     return 0;
229
230 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
231     // check the arguments and whether it's already been initialized
232     if( !p_mutex )
233         return B_BAD_VALUE;
234
235     if( p_mutex->init == 9999 )
236         return EALREADY;
237
238     p_mutex->lock = create_sem( 1, "BeMutex" );
239     p_mutex->owner = -1;
240     p_mutex->init = 9999;
241     return B_OK;
242
243 #elif defined(HAVE_PTHREAD_H)
244     return pthread_mutex_init( p_mutex, NULL );
245
246 #endif
247 }
248
249 /*****************************************************************************
250  * vlc_mutex_lock: lock a mutex
251  *****************************************************************************/
252 static __inline__ int vlc_mutex_lock( vlc_mutex_t *p_mutex )
253 {
254 #if defined(HAVE_CTHREADS_H)
255     mutex_lock( p_mutex );
256     return 0;
257
258 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
259     status_t err;
260
261     if( !p_mutex )
262         return B_BAD_VALUE;
263
264     if( p_mutex->init < 2000 )
265         return B_NO_INIT;
266
267     lazy_init_mutex( p_mutex );
268
269     err = acquire_sem( p_mutex->lock );
270     if( !err )
271         p_mutex->owner = find_thread( NULL );
272
273     return err;
274
275 #elif defined(HAVE_PTHREAD_H)
276     return pthread_mutex_lock( p_mutex );
277
278 #endif
279 }
280
281 /*****************************************************************************
282  * vlc_mutex_unlock: unlock a mutex
283  *****************************************************************************/
284 static __inline__ int vlc_mutex_unlock( vlc_mutex_t *p_mutex )
285 {
286 #if defined(HAVE_CTHREADS_H)
287     mutex_unlock( p_mutex );
288     return 0;
289
290 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
291     if(! p_mutex)
292         return B_BAD_VALUE;
293
294     if( p_mutex->init < 2000 )
295         return B_NO_INIT;
296
297     lazy_init_mutex( p_mutex );
298
299     if( p_mutex->owner != find_thread(NULL) )
300         return ENOLCK;
301
302     p_mutex->owner = -1;
303     release_sem( p_mutex->lock );
304     return B_OK;
305
306 #elif defined(HAVE_PTHREAD_H)
307     return pthread_mutex_unlock( p_mutex );
308
309 #endif
310 }
311
312 #if defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
313 /* lazy_init_cond */
314 static __inline__ void lazy_init_cond( vlc_cond_t* p_condvar )
315 {
316     int32 v = atomic_or( &p_condvar->init, 1 );
317     if( 2000 == v ) /* we're the first, so do the init */
318     {
319         vlc_cond_init( p_condvar );
320     }
321     else /* we're not the first, so wait until the init is finished */
322     {
323         while( p_condvar->init != 9999 )
324         {
325             snooze( 10000 );
326         }
327     }
328 }
329 #endif
330
331 /*****************************************************************************
332  * vlc_cond_init: initialize a condition
333  *****************************************************************************/
334 static __inline__ int vlc_cond_init( vlc_cond_t *p_condvar )
335 {
336 #if defined(HAVE_CTHREADS_H)
337     /* condition_init() */
338     spin_lock_init( &p_condvar->lock );
339     cthread_queue_init( &p_condvar->queue );
340     p_condvar->name = 0;
341     p_condvar->implications = 0;
342
343     return 0;
344
345 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
346     if( !p_condvar )
347         return B_BAD_VALUE;
348
349     if( p_condvar->init == 9999 )
350         return EALREADY;
351
352     p_condvar->sem = create_sem( 0, "CVSem" );
353     p_condvar->handshakeSem = create_sem( 0, "CVHandshake" );
354     p_condvar->signalSem = create_sem( 1, "CVSignal" );
355     p_condvar->ns = p_condvar->nw = 0;
356     p_condvar->init = 9999;
357     return B_OK;
358
359 #elif defined(HAVE_PTHREAD_H)
360     return pthread_cond_init( p_condvar, NULL );
361
362 #endif
363 }
364
365 /*****************************************************************************
366  * vlc_cond_signal: start a thread on condition completion
367  *****************************************************************************/
368 static __inline__ int vlc_cond_signal( vlc_cond_t *p_condvar )
369 {
370 #if defined(HAVE_CTHREADS_H)
371     /* condition_signal() */
372     if ( p_condvar->queue.head || p_condvar->implications )
373     {
374         cond_signal( (condition_t)p_condvar );
375     }
376     return 0;
377
378 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
379     status_t err = B_OK;
380
381     if( !p_condvar )
382         return B_BAD_VALUE;
383
384     if( p_condvar->init < 2000 )
385         return B_NO_INIT;
386
387     lazy_init_cond( p_condvar );
388
389     if( acquire_sem(p_condvar->signalSem) == B_INTERRUPTED)
390         return B_INTERRUPTED;
391
392     if( p_condvar->nw > p_condvar->ns )
393     {
394         p_condvar->ns += 1;
395         release_sem( p_condvar->sem );
396         release_sem( p_condvar->signalSem );
397
398         while( acquire_sem(p_condvar->handshakeSem) == B_INTERRUPTED )
399         {
400             err = B_INTERRUPTED;
401         }
402     }
403     else
404     {
405         release_sem( p_condvar->signalSem );
406     }
407     return err;
408
409 #elif defined(HAVE_PTHREAD_H)
410     return pthread_cond_signal( p_condvar );
411
412 #endif
413 }
414
415 /*****************************************************************************
416  * vlc_cond_wait: wait until condition completion
417  *****************************************************************************/
418 static __inline__ int vlc_cond_wait( vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex )
419 {
420 #if defined(HAVE_CTHREADS_H)
421     condition_wait( (condition_t)p_condvar, (mutex_t)p_mutex );
422     return 0;
423
424 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
425     status_t err;
426
427     if( !p_condvar )
428         return B_BAD_VALUE;
429
430     if( !p_mutex )
431         return B_BAD_VALUE;
432
433     if( p_condvar->init < 2000 )
434         return B_NO_INIT;
435
436     lazy_init_cond( p_condvar );
437
438     if( acquire_sem(p_condvar->signalSem) == B_INTERRUPTED )
439         return B_INTERRUPTED;
440
441     p_condvar->nw += 1;
442     release_sem( p_condvar->signalSem );
443
444     vlc_mutex_unlock( p_mutex );
445     err = acquire_sem( p_condvar->sem );
446
447     while( acquire_sem(p_condvar->signalSem) == B_INTERRUPTED)
448     {
449         err = B_INTERRUPTED;
450     }
451
452     if( p_condvar->ns > 0 )
453     {
454         release_sem( p_condvar->handshakeSem );
455         p_condvar->ns -= 1;
456     }
457     p_condvar->nw -= 1;
458     release_sem( p_condvar->signalSem );
459
460     while( vlc_mutex_lock(p_mutex) == B_INTERRUPTED)
461     {
462         err = B_INTERRUPTED;
463     }
464     return err;
465
466 #elif defined(HAVE_PTHREAD_H)
467     return pthread_cond_wait( p_condvar, p_mutex );
468
469 #endif
470 }