f1ded212492b371d14e5cc345cd36f55cd7b4e7e
[vlc.git] / include / threads_funcs.h
1 /*****************************************************************************
2  * threads_funcs.h : threads implementation for the VideoLAN client
3  * This header provides a portable threads implementation.
4  *****************************************************************************
5  * Copyright (C) 1999, 2000 VideoLAN
6  * $Id: threads_funcs.h,v 1.5 2002/06/01 12:31:58 sam Exp $
7  *
8  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
9  *          Samuel Hocevar <sam@zoy.org>
10  *          Gildas Bazin <gbazin@netcourrier.com>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Function definitions
29  *****************************************************************************/
30 VLC_EXPORT( int,  __vlc_threads_init,  ( vlc_object_t * ) );
31 VLC_EXPORT( int,    vlc_threads_end,   ( void ) );
32 VLC_EXPORT( int,  __vlc_mutex_init,    ( vlc_object_t *, vlc_mutex_t * ) );
33 VLC_EXPORT( int,  __vlc_mutex_destroy, ( char *, int, vlc_mutex_t * ) );
34 VLC_EXPORT( int,    vlc_cond_init,     ( vlc_cond_t * ) );
35 VLC_EXPORT( int,  __vlc_cond_destroy,  ( char *, int, vlc_cond_t * ) );
36 VLC_EXPORT( int,  __vlc_thread_create, ( vlc_object_t *, char *, int, char *, void * ( * ) ( void * ), vlc_bool_t ) );
37 VLC_EXPORT( void, __vlc_thread_ready,  ( vlc_object_t * ) );
38 VLC_EXPORT( void, __vlc_thread_join,   ( vlc_object_t *, char *, int ) );
39
40 /*****************************************************************************
41  * vlc_threads_init: initialize threads system
42  *****************************************************************************/
43 #define vlc_threads_init( P_THIS )                                          \
44     __vlc_threads_init( CAST_TO_VLC_OBJECT(P_THIS) )
45
46 /*****************************************************************************
47  * vlc_mutex_init: initialize a mutex
48  *****************************************************************************/
49 #define vlc_mutex_init( P_THIS, P_MUTEX )                                   \
50     __vlc_mutex_init( CAST_TO_VLC_OBJECT(P_THIS), P_MUTEX )
51
52 /*****************************************************************************
53  * vlc_mutex_lock: lock a mutex
54  *****************************************************************************/
55 #ifdef DEBUG
56 #   define vlc_mutex_lock( P_MUTEX )                                        \
57         __vlc_mutex_lock( __FILE__, __LINE__, P_MUTEX )
58 #else
59 #   define vlc_mutex_lock( P_MUTEX )                                        \
60         __vlc_mutex_lock( "(unknown)", 0, P_MUTEX )
61 #endif
62
63 static inline int __vlc_mutex_lock( char * psz_file, int i_line,
64                                     vlc_mutex_t *p_mutex )
65 {
66 #if defined( PTH_INIT_IN_PTH_H )
67     return pth_mutex_acquire( p_mutex, TRUE, NULL );
68
69 #elif defined( ST_INIT_IN_ST_H )
70     return st_mutex_lock( *p_mutex );
71
72 #elif defined( WIN32 )
73     if( p_mutex->mutex )
74     {
75         WaitForSingleObject( p_mutex->mutex, INFINITE );
76     }
77     else
78     {
79         EnterCriticalSection( &p_mutex->csection );
80     }
81     return 0;
82
83 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
84     int i_return = pthread_mutex_lock( p_mutex );
85     if( i_return )
86     {
87 //        msg_Err( "thread %d: mutex_lock failed at %s:%d (%s)",
88 //                 pthread_self(), psz_file, i_line, strerror(i_return) );
89     }
90     return i_return;
91
92 #elif defined( HAVE_CTHREADS_H )
93     mutex_lock( p_mutex );
94     return 0;
95
96 #elif defined( HAVE_KERNEL_SCHEDULER_H )
97     status_t err;
98
99     if( !p_mutex )
100     {
101         return B_BAD_VALUE;
102     }
103
104     if( p_mutex->init < 2000 )
105     {
106         return B_NO_INIT;
107     }
108
109     err = acquire_sem( p_mutex->lock );
110     return err;
111
112 #endif
113 }
114
115 /*****************************************************************************
116  * vlc_mutex_unlock: unlock a mutex
117  *****************************************************************************/
118 #ifdef DEBUG
119 #   define vlc_mutex_unlock( P_MUTEX )                                      \
120         __vlc_mutex_unlock( __FILE__, __LINE__, P_MUTEX )
121 #else
122 #   define vlc_mutex_unlock( P_MUTEX )                                      \
123         __vlc_mutex_unlock( "(unknown)", 0, P_MUTEX )
124 #endif
125
126 static inline int __vlc_mutex_unlock( char * psz_file, int i_line,
127                                       vlc_mutex_t *p_mutex )
128 {
129 #if defined( PTH_INIT_IN_PTH_H )
130     return pth_mutex_release( p_mutex );
131
132 #elif defined( ST_INIT_IN_ST_H )
133     return st_mutex_unlock( *p_mutex );
134
135 #elif defined( WIN32 )
136     if( p_mutex->mutex )
137     {
138         ReleaseMutex( p_mutex->mutex );
139     }
140     else
141     {
142         LeaveCriticalSection( &p_mutex->csection );
143     }
144     return 0;
145
146 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
147     int i_return = pthread_mutex_unlock( p_mutex );
148     if( i_return )
149     {
150 //        msg_Err( "thread %d: mutex_unlock failed at %s:%d (%s)",
151 //                 pthread_self(), psz_file, i_line, strerror(i_return) );
152     }
153     return i_return;
154
155 #elif defined( HAVE_CTHREADS_H )
156     mutex_unlock( p_mutex );
157     return 0;
158
159 #elif defined( HAVE_KERNEL_SCHEDULER_H )
160     if( !p_mutex)
161     {
162         return B_BAD_VALUE;
163     }
164
165     if( p_mutex->init < 2000 )
166     {
167         return B_NO_INIT;
168     }
169
170     release_sem( p_mutex->lock );
171     return B_OK;
172
173 #endif
174 }
175
176 /*****************************************************************************
177  * vlc_mutex_destroy: destroy a mutex
178  *****************************************************************************/
179 #ifdef DEBUG
180 #   define vlc_mutex_destroy( P_MUTEX )                                     \
181         __vlc_mutex_destroy( __FILE__, __LINE__, P_MUTEX )
182 #else
183 #   define vlc_mutex_destroy( P_MUTEX )                                     \
184         __vlc_mutex_destroy( "(unknown)", 0, P_MUTEX )
185 #endif
186
187 /*****************************************************************************
188  * vlc_cond_signal: start a thread on condition completion
189  *****************************************************************************/
190 static inline int vlc_cond_signal( vlc_cond_t *p_condvar )
191 {
192 #if defined( PTH_INIT_IN_PTH_H )
193     return pth_cond_notify( p_condvar, FALSE );
194
195 #elif defined( ST_INIT_IN_ST_H )
196     return st_cond_signal( *p_condvar );
197
198 #elif defined( WIN32 )
199     /* Release one waiting thread if one is available. */
200     /* For this trick to work properly, the vlc_cond_signal must be surrounded
201      * by a mutex. This will prevent another thread from stealing the signal */
202     PulseEvent( p_condvar->signal );
203     return 0;
204
205 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
206     return pthread_cond_signal( p_condvar );
207
208 #elif defined( HAVE_CTHREADS_H )
209     /* condition_signal() */
210     if ( p_condvar->queue.head || p_condvar->implications )
211     {
212         cond_signal( (condition_t)p_condvar );
213     }
214     return 0;
215
216 #elif defined( HAVE_KERNEL_SCHEDULER_H )
217     if( !p_condvar )
218     {
219         return B_BAD_VALUE;
220     }
221
222     if( p_condvar->init < 2000 )
223     {
224         return B_NO_INIT;
225     }
226
227     while( p_condvar->thread != -1 )
228     {
229         thread_info info;
230         if( get_thread_info(p_condvar->thread, &info) == B_BAD_VALUE )
231         {
232             return 0;
233         }
234
235         if( info.state != B_THREAD_SUSPENDED )
236         {
237             /* The  waiting thread is not suspended so it could
238              * have been interrupted beetwen the unlock and the
239              * suspend_thread line. That is why we sleep a little
240              * before retesting p_condver->thread. */
241             snooze( 10000 );
242         }
243         else
244         {
245             /* Ok, we have to wake up that thread */
246             resume_thread( p_condvar->thread );
247             return 0;
248         }
249     }
250     return 0;
251
252 #endif
253 }
254
255 /*****************************************************************************
256  * vlc_cond_broadcast: start all threads waiting on condition completion
257  *****************************************************************************/
258 /*
259  * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
260  * Only works with pthreads, you need to adapt it for others
261  * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
262  */
263 static inline int vlc_cond_broadcast( vlc_cond_t *p_condvar )
264 {
265 #if defined( PTH_INIT_IN_PTH_H )
266     return pth_cond_notify( p_condvar, FALSE );
267
268 #elif defined( ST_INIT_IN_ST_H )
269     return st_cond_broadcast( p_condvar );
270
271 #elif defined( WIN32 )
272     /* Release all waiting threads. */
273     /* For this trick to work properly, the vlc_cond_signal must be surrounded
274      * by a mutex. This will prevent another thread from stealing the signal */
275     while( p_condvar->i_waiting_threads )
276     {
277         PulseEvent( p_condvar->signal );
278         Sleep( 1 ); /* deschedule the current thread */
279     }
280     return 0;
281
282 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
283     return pthread_cond_broadcast( p_condvar );
284
285 #elif defined( HAVE_CTHREADS_H )
286     /* condition_signal() */
287     if ( p_condvar->queue.head || p_condvar->implications )
288     {
289         cond_signal( (condition_t)p_condvar );
290     }
291     return 0;
292
293 #elif defined( HAVE_KERNEL_SCHEDULER_H )
294     if( !p_condvar )
295     {
296         return B_BAD_VALUE;
297     }
298
299     if( p_condvar->init < 2000 )
300     {
301         return B_NO_INIT;
302     }
303
304     while( p_condvar->thread != -1 )
305     {
306         thread_info info;
307         if( get_thread_info(p_condvar->thread, &info) == B_BAD_VALUE )
308         {
309             return 0;
310         }
311
312         if( info.state != B_THREAD_SUSPENDED )
313         {
314             /* The  waiting thread is not suspended so it could
315              * have been interrupted beetwen the unlock and the
316              * suspend_thread line. That is why we sleep a little
317              * before retesting p_condver->thread. */
318             snooze( 10000 );
319         }
320         else
321         {
322             /* Ok, we have to wake up that thread */
323             resume_thread( p_condvar->thread );
324             return 0;
325         }
326     }
327     return 0;
328
329 #endif
330 }
331
332 /*****************************************************************************
333  * vlc_cond_wait: wait until condition completion
334  *****************************************************************************/
335 #ifdef DEBUG
336 #   define vlc_cond_wait( P_COND, P_MUTEX )                                   \
337         __vlc_cond_wait( __FILE__, __LINE__, P_COND, P_MUTEX  )
338 #else
339 #   define vlc_cond_wait( P_COND, P_MUTEX )                                   \
340         __vlc_cond_wait( "(unknown)", 0, P_COND, P_MUTEX )
341 #endif
342
343 static inline int __vlc_cond_wait( char * psz_file, int i_line,
344                                    vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex )
345 {
346 #if defined( PTH_INIT_IN_PTH_H )
347     return pth_cond_await( p_condvar, p_mutex, NULL );
348
349 #elif defined( ST_INIT_IN_ST_H )
350     int i_ret;
351
352     st_mutex_unlock( *p_mutex );
353     i_ret = st_cond_wait( *p_condvar );
354     st_mutex_lock( *p_mutex );
355
356     return i_ret;
357
358 #elif defined( WIN32 )
359     /* The ideal would be to use a function which atomically releases the
360      * mutex and initiate the waiting.
361      * Unfortunately only the SignalObjectAndWait function does this and it's
362      * only supported on WinNT/2K, furthermore it cannot take multiple
363      * events as parameters.
364      *
365      * The solution we use should however fulfill all our needs (even though
366      * it is not a correct pthreads implementation)
367      */
368     int i_result;
369
370     p_condvar->i_waiting_threads ++;
371
372     if( p_mutex->mutex )
373     {
374         p_mutex->SignalObjectAndWait( p_mutex->mutex, p_condvar->signal,
375                                       INFINITE, FALSE );
376     }
377     else
378     {
379         /* Release the mutex */
380         vlc_mutex_unlock( p_mutex );
381         i_result = WaitForSingleObject( p_condvar->signal, INFINITE); 
382         p_condvar->i_waiting_threads --;
383     }
384
385     /* Reacquire the mutex before returning. */
386     vlc_mutex_lock( p_mutex );
387
388     return( i_result == WAIT_FAILED );
389
390 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
391
392 #   ifdef DEBUG
393     /* In debug mode, timeout */
394     struct timeval now;
395     struct timespec timeout;
396     int    i_result;
397
398     for( ; ; )
399     {
400         gettimeofday( &now, NULL );
401         timeout.tv_sec = now.tv_sec + THREAD_COND_TIMEOUT;
402         timeout.tv_nsec = now.tv_usec * 1000;
403
404         i_result = pthread_cond_timedwait( p_condvar, p_mutex, &timeout );
405
406         if( i_result == ETIMEDOUT )
407         {
408 //X            msg_Warn( "thread %d: possible deadlock detected "
409 //X                      "in cond_wait at %s:%d (%s)", pthread_self(),
410 //X                      psz_file, i_line, strerror(i_result) );
411             continue;
412         }
413
414         if( i_result )
415         {
416 //X            msg_Err( "thread %d: cond_wait failed at %s:%d (%s)",
417 //X                     pthread_self(), psz_file, i_line, strerror(i_result) );
418         }
419         return( i_result );
420     }
421 #   else
422     return pthread_cond_wait( p_condvar, p_mutex );
423 #   endif
424
425 #elif defined( HAVE_CTHREADS_H )
426     condition_wait( (condition_t)p_condvar, (mutex_t)p_mutex );
427     return 0;
428
429 #elif defined( HAVE_KERNEL_SCHEDULER_H )
430     if( !p_condvar )
431     {
432         return B_BAD_VALUE;
433     }
434
435     if( !p_mutex )
436     {
437         return B_BAD_VALUE;
438     }
439
440     if( p_condvar->init < 2000 )
441     {
442         return B_NO_INIT;
443     }
444
445     /* The p_condvar->thread var is initialized before the unlock because
446      * it enables to identify when the thread is interrupted beetwen the
447      * unlock line and the suspend_thread line */
448     p_condvar->thread = find_thread( NULL );
449     vlc_mutex_unlock( p_mutex );
450     suspend_thread( p_condvar->thread );
451     p_condvar->thread = -1;
452
453     vlc_mutex_lock( p_mutex );
454     return 0;
455
456 #endif
457 }
458
459 /*****************************************************************************
460  * vlc_cond_destroy: destroy a condition
461  *****************************************************************************/
462 #ifdef DEBUG
463 #   define vlc_cond_destroy( P_COND )                                       \
464         __vlc_cond_destroy( __FILE__, __LINE__, P_COND )
465 #else
466 #   define vlc_cond_destroy( P_COND )                                       \
467         __vlc_cond_destroy( "(unknown)", 0, P_COND )
468 #endif
469
470 /*****************************************************************************
471  * vlc_thread_create: create a thread
472  *****************************************************************************/
473 #   define vlc_thread_create( P_THIS, PSZ_NAME, FUNC, WAIT )                \
474         __vlc_thread_create( CAST_TO_VLC_OBJECT(P_THIS), __FILE__, __LINE__, PSZ_NAME, (void * ( * ) ( void * ))FUNC, WAIT )
475
476 /*****************************************************************************
477  * vlc_thread_ready: tell the parent thread we were successfully spawned
478  *****************************************************************************/
479 #   define vlc_thread_ready( P_THIS )                                       \
480         __vlc_thread_ready( CAST_TO_VLC_OBJECT(P_THIS) )
481
482 /*****************************************************************************
483  * vlc_thread_join: wait until a thread exits
484  *****************************************************************************/
485 #ifdef DEBUG
486 #   define vlc_thread_join( P_THIS )                                        \
487         __vlc_thread_join( CAST_TO_VLC_OBJECT(P_THIS), __FILE__, __LINE__ ) 
488 #else
489 #   define vlc_thread_join( P_THIS )                                        \
490         __vlc_thread_join( CAST_TO_VLC_OBJECT(P_THIS), "(unknown)", 0 ) 
491 #endif
492