]> git.sesse.net Git - vlc/blob - include/vlc_threads_funcs.h
Following vlc_object_kill(), add vlc_object_dying()
[vlc] / include / vlc_threads_funcs.h
1 /*****************************************************************************
2  * vlc_threads_funcs.h : threads implementation for the VideoLAN client
3  * This header provides a portable threads implementation.
4  *****************************************************************************
5  * Copyright (C) 1999, 2002 the VideoLAN team
6  * $Id$
7  *
8  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
9  *          Samuel Hocevar <sam@via.ecp.fr>
10  *          Gildas Bazin <gbazin@netcourrier.com>
11  *          Christophe Massiot <massiot@via.ecp.fr>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 #if !defined( __LIBVLC__ )
29   #error You are not libvlc or one of its plugins. You cannot include this file
30 #endif
31
32 #ifndef _VLC_THREADFUNCS_H_
33 #define _VLC_THREADFUNCS_H_
34
35 /*****************************************************************************
36  * Function definitions
37  *****************************************************************************/
38 VLC_EXPORT( int,  __vlc_threads_init,  ( vlc_object_t * ) );
39 VLC_EXPORT( int,  __vlc_threads_end,   ( vlc_object_t * ) );
40 VLC_EXPORT( int,  __vlc_mutex_init,    ( vlc_object_t *, vlc_mutex_t * ) );
41 VLC_EXPORT( int,  __vlc_mutex_destroy, ( const char *, int, vlc_mutex_t * ) );
42 VLC_EXPORT( int,  __vlc_cond_init,     ( vlc_object_t *, vlc_cond_t * ) );
43 VLC_EXPORT( int,  __vlc_cond_destroy,  ( const char *, int, vlc_cond_t * ) );
44 VLC_EXPORT( int, __vlc_threadvar_create, (vlc_object_t *, vlc_threadvar_t * ) );
45 VLC_EXPORT( int,  __vlc_thread_create, ( vlc_object_t *, const char *, int, const char *, void * ( * ) ( void * ), int, vlc_bool_t ) );
46 VLC_EXPORT( int,  __vlc_thread_set_priority, ( vlc_object_t *, const char *, int, int ) );
47 VLC_EXPORT( void, __vlc_thread_ready,  ( vlc_object_t * ) );
48 VLC_EXPORT( void, __vlc_thread_join,   ( vlc_object_t *, const char *, int ) );
49
50
51 /*****************************************************************************
52  * vlc_threads_init: initialize threads system
53  *****************************************************************************/
54 #define vlc_threads_init( P_THIS )                                          \
55     __vlc_threads_init( VLC_OBJECT(P_THIS) )
56
57 /*****************************************************************************
58  * vlc_threads_end: deinitialize threads system
59  *****************************************************************************/
60 #define vlc_threads_end( P_THIS )                                           \
61     __vlc_threads_end( VLC_OBJECT(P_THIS) )
62
63 /*****************************************************************************
64  * vlc_mutex_init: initialize a mutex
65  *****************************************************************************/
66 #define vlc_mutex_init( P_THIS, P_MUTEX )                                   \
67     __vlc_mutex_init( VLC_OBJECT(P_THIS), P_MUTEX )
68
69 /*****************************************************************************
70  * vlc_mutex_lock: lock a mutex
71  *****************************************************************************/
72 #define vlc_mutex_lock( P_MUTEX )                                           \
73     __vlc_mutex_lock( __FILE__, __LINE__, P_MUTEX )
74
75 #ifdef __APPLE__
76 #   define CAST_PTHREAD_TO_INT(t) (unsigned long int)(uintptr_t)(void *)t
77 #else
78 #   define CAST_PTHREAD_TO_INT(t) (unsigned long int)t
79 #endif
80
81 static inline int __vlc_mutex_lock( const char * psz_file, int i_line,
82                                     vlc_mutex_t * p_mutex )
83 {
84     int i_result;
85     /* In case of error : */
86     unsigned long int i_thread = 0;
87     const char * psz_error = "";
88
89 #if defined( PTH_INIT_IN_PTH_H )
90     i_result = ( pth_mutex_acquire( &p_mutex->mutex, FALSE, NULL ) == FALSE );
91
92 #elif defined( ST_INIT_IN_ST_H )
93     i_result = st_mutex_lock( p_mutex->mutex );
94
95 #elif defined( UNDER_CE )
96     EnterCriticalSection( &p_mutex->csection );
97     i_result = 0;
98
99 #elif defined( WIN32 )
100     if( p_mutex->mutex )
101     {
102         WaitForSingleObject( p_mutex->mutex, INFINITE );
103     }
104     else
105     {
106         EnterCriticalSection( &p_mutex->csection );
107     }
108     i_result = 0;
109
110 #elif defined( HAVE_KERNEL_SCHEDULER_H )
111     if( p_mutex == NULL )
112     {
113         i_result = B_BAD_VALUE;
114     }
115     else if( p_mutex->init < 2000 )
116     {
117         i_result = B_NO_INIT;
118     }
119     else
120     {
121         i_result = acquire_sem( p_mutex->lock );
122     }
123
124 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
125 #   define vlc_assert_locked( m ) \
126            assert (pthread_mutex_lock (&((m)->mutex)) == EDEADLK)
127
128     i_result = pthread_mutex_lock( &p_mutex->mutex );
129     if ( i_result )
130     {
131         i_thread = CAST_PTHREAD_TO_INT(pthread_self());
132         psz_error = strerror(i_result);
133     }
134
135 #elif defined( HAVE_CTHREADS_H )
136     mutex_lock( p_mutex->mutex );
137     i_result = 0;
138
139 #endif
140
141     if( i_result )
142     {
143         msg_Err( p_mutex->p_this,
144                  "thread %li: mutex_lock failed at %s:%d (%d:%s)",
145                  i_thread, psz_file, i_line, i_result, psz_error );
146     }
147     return i_result;
148 }
149
150 #ifndef vlc_assert_locked
151 # define vlc_assert_locked( m ) (void)0
152 #endif
153
154 /*****************************************************************************
155  * vlc_mutex_unlock: unlock a mutex
156  *****************************************************************************/
157 #define vlc_mutex_unlock( P_MUTEX )                                         \
158     __vlc_mutex_unlock( __FILE__, __LINE__, P_MUTEX )
159
160 static inline int __vlc_mutex_unlock( const char * psz_file, int i_line,
161                                       vlc_mutex_t *p_mutex )
162 {
163     int i_result;
164     /* In case of error : */
165     unsigned long int i_thread = 0;
166     const char * psz_error = "";
167
168 #if defined( PTH_INIT_IN_PTH_H )
169     i_result = ( pth_mutex_release( &p_mutex->mutex ) == FALSE );
170
171 #elif defined( ST_INIT_IN_ST_H )
172     i_result = st_mutex_unlock( p_mutex->mutex );
173
174 #elif defined( UNDER_CE )
175     LeaveCriticalSection( &p_mutex->csection );
176     i_result = 0;
177
178 #elif defined( WIN32 )
179     if( p_mutex->mutex )
180     {
181         ReleaseMutex( p_mutex->mutex );
182     }
183     else
184     {
185         LeaveCriticalSection( &p_mutex->csection );
186     }
187     i_result = 0;
188
189 #elif defined( HAVE_KERNEL_SCHEDULER_H )
190     if( p_mutex == NULL )
191     {
192         i_result = B_BAD_VALUE;
193     }
194     else if( p_mutex->init < 2000 )
195     {
196         i_result = B_NO_INIT;
197     }
198     else
199     {
200         release_sem( p_mutex->lock );
201         return B_OK;
202     }
203
204 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
205     i_result = pthread_mutex_unlock( &p_mutex->mutex );
206     if ( i_result )
207     {
208         i_thread = CAST_PTHREAD_TO_INT(pthread_self());
209         psz_error = strerror(i_result);
210     }
211
212 #elif defined( HAVE_CTHREADS_H )
213     mutex_unlock( p_mutex );
214     i_result = 0;
215
216 #endif
217
218     if( i_result )
219     {
220         msg_Err( p_mutex->p_this,
221                  "thread %li: mutex_unlock failed at %s:%d (%d:%s)",
222                  i_thread, psz_file, i_line, i_result, psz_error );
223     }
224
225     return i_result;
226 }
227
228 /*****************************************************************************
229  * vlc_mutex_destroy: destroy a mutex
230  *****************************************************************************/
231 #define vlc_mutex_destroy( P_MUTEX )                                        \
232     __vlc_mutex_destroy( __FILE__, __LINE__, P_MUTEX )
233
234 /*****************************************************************************
235  * vlc_cond_init: initialize a condition
236  *****************************************************************************/
237 #define vlc_cond_init( P_THIS, P_COND )                                     \
238     __vlc_cond_init( VLC_OBJECT(P_THIS), P_COND )
239
240 /*****************************************************************************
241  * vlc_cond_signal: start a thread on condition completion
242  *****************************************************************************/
243 #define vlc_cond_signal( P_COND )                                           \
244     __vlc_cond_signal( __FILE__, __LINE__, P_COND )
245
246 static inline int __vlc_cond_signal( const char * psz_file, int i_line,
247                                      vlc_cond_t *p_condvar )
248 {
249     int i_result;
250     /* In case of error : */
251     unsigned long int i_thread = 0;
252     const char * psz_error = "";
253
254 #if defined( PTH_INIT_IN_PTH_H )
255     i_result = ( pth_cond_notify( &p_condvar->cond, FALSE ) == FALSE );
256
257 #elif defined( ST_INIT_IN_ST_H )
258     i_result = st_cond_signal( p_condvar->cond );
259
260 #elif defined( UNDER_CE )
261     PulseEvent( p_condvar->event );
262     i_result = 0;
263
264 #elif defined( WIN32 )
265     /* Release one waiting thread if one is available. */
266     /* For this trick to work properly, the vlc_cond_signal must be surrounded
267      * by a mutex. This will prevent another thread from stealing the signal */
268     if( !p_condvar->semaphore )
269     {
270         PulseEvent( p_condvar->event );
271     }
272     else if( p_condvar->i_win9x_cv == 1 )
273     {
274         /* Wait for the gate to be open */
275         WaitForSingleObject( p_condvar->event, INFINITE );
276
277         if( p_condvar->i_waiting_threads )
278         {
279             /* Using a semaphore exposes us to a race condition. It is
280              * possible for another thread to start waiting on the semaphore
281              * just after we signaled it and thus steal the signal.
282              * We have to prevent new threads from entering the cond_wait(). */
283             ResetEvent( p_condvar->event );
284
285             /* A semaphore is used here because Win9x doesn't have
286              * SignalObjectAndWait() and thus a race condition exists
287              * during the time we release the mutex and the time we start
288              * waiting on the event (more precisely, the signal can sometimes
289              * be missed by the waiting thread if we use PulseEvent()). */
290             ReleaseSemaphore( p_condvar->semaphore, 1, 0 );
291         }
292     }
293     else
294     {
295         if( p_condvar->i_waiting_threads )
296         {
297             ReleaseSemaphore( p_condvar->semaphore, 1, 0 );
298
299             /* Wait for the last thread to be awakened */
300             WaitForSingleObject( p_condvar->event, INFINITE );
301         }
302     }
303     i_result = 0;
304
305 #elif defined( HAVE_KERNEL_SCHEDULER_H )
306     if( p_condvar == NULL )
307     {
308         i_result = B_BAD_VALUE;
309     }
310     else if( p_condvar->init < 2000 )
311     {
312         i_result = B_NO_INIT;
313     }
314     else
315     {
316         while( p_condvar->thread != -1 )
317         {
318             thread_info info;
319             if( get_thread_info(p_condvar->thread, &info) == B_BAD_VALUE )
320             {
321                 return 0;
322             }
323
324             if( info.state != B_THREAD_SUSPENDED )
325             {
326                 /* The  waiting thread is not suspended so it could
327                  * have been interrupted beetwen the unlock and the
328                  * suspend_thread line. That is why we sleep a little
329                  * before retesting p_condver->thread. */
330                 snooze( 10000 );
331             }
332             else
333             {
334                 /* Ok, we have to wake up that thread */
335                 resume_thread( p_condvar->thread );
336                 return 0;
337             }
338         }
339         i_result = 0;
340     }
341
342 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
343     i_result = pthread_cond_signal( &p_condvar->cond );
344     if ( i_result )
345     {
346         i_thread = CAST_PTHREAD_TO_INT(pthread_self());
347         psz_error = strerror(i_result);
348     }
349
350 #elif defined( HAVE_CTHREADS_H )
351     /* condition_signal() */
352     if ( p_condvar->queue.head || p_condvar->implications )
353     {
354         cond_signal( (condition_t)p_condvar );
355     }
356     i_result = 0;
357
358 #endif
359
360     if( i_result )
361     {
362         msg_Err( p_condvar->p_this,
363                  "thread %li: cond_signal failed at %s:%d (%d:%s)",
364                  i_thread, psz_file, i_line, i_result, psz_error );
365     }
366
367     return i_result;
368 }
369
370 /*****************************************************************************
371  * vlc_cond_wait: wait until condition completion
372  *****************************************************************************/
373 #define vlc_cond_wait( P_COND, P_MUTEX )                                     \
374     __vlc_cond_wait( __FILE__, __LINE__, P_COND, P_MUTEX  )
375
376 static inline int __vlc_cond_wait( const char * psz_file, int i_line,
377                                    vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex )
378 {
379     int i_result;
380     /* In case of error : */
381     unsigned long int i_thread = 0;
382     const char * psz_error = "";
383
384 #if defined( PTH_INIT_IN_PTH_H )
385     i_result = ( pth_cond_await( &p_condvar->cond, &p_mutex->mutex, NULL )
386                  == FALSE );
387
388 #elif defined( ST_INIT_IN_ST_H )
389     st_mutex_unlock( p_mutex->mutex );
390     i_result = st_cond_wait( p_condvar->cond );
391     st_mutex_lock( p_mutex->mutex );
392
393 #elif defined( UNDER_CE )
394     p_condvar->i_waiting_threads++;
395     LeaveCriticalSection( &p_mutex->csection );
396     WaitForSingleObject( p_condvar->event, INFINITE );
397     p_condvar->i_waiting_threads--;
398
399     /* Reacquire the mutex before returning. */
400     vlc_mutex_lock( p_mutex );
401
402     i_result = 0;
403
404 #elif defined( WIN32 )
405     if( !p_condvar->semaphore )
406     {
407         /* Increase our wait count */
408         p_condvar->i_waiting_threads++;
409
410         if( p_mutex->mutex )
411         {
412             /* It is only possible to atomically release the mutex and
413              * initiate the waiting on WinNT/2K/XP. Win9x doesn't have
414              * SignalObjectAndWait(). */
415             p_condvar->SignalObjectAndWait( p_mutex->mutex,
416                                             p_condvar->event,
417                                             INFINITE, FALSE );
418         }
419         else
420         {
421             LeaveCriticalSection( &p_mutex->csection );
422             WaitForSingleObject( p_condvar->event, INFINITE );
423         }
424
425         p_condvar->i_waiting_threads--;
426     }
427     else if( p_condvar->i_win9x_cv == 1 )
428     {
429         int i_waiting_threads;
430
431         /* Wait for the gate to be open */
432         WaitForSingleObject( p_condvar->event, INFINITE );
433
434         /* Increase our wait count */
435         p_condvar->i_waiting_threads++;
436
437         LeaveCriticalSection( &p_mutex->csection );
438         WaitForSingleObject( p_condvar->semaphore, INFINITE );
439
440         /* Decrement and test must be atomic */
441         EnterCriticalSection( &p_condvar->csection );
442
443         /* Decrease our wait count */
444         i_waiting_threads = --p_condvar->i_waiting_threads;
445
446         LeaveCriticalSection( &p_condvar->csection );
447
448         /* Reopen the gate if we were the last waiting thread */
449         if( !i_waiting_threads )
450             SetEvent( p_condvar->event );
451     }
452     else
453     {
454         int i_waiting_threads;
455
456         /* Increase our wait count */
457         p_condvar->i_waiting_threads++;
458
459         LeaveCriticalSection( &p_mutex->csection );
460         WaitForSingleObject( p_condvar->semaphore, INFINITE );
461
462         /* Decrement and test must be atomic */
463         EnterCriticalSection( &p_condvar->csection );
464
465         /* Decrease our wait count */
466         i_waiting_threads = --p_condvar->i_waiting_threads;
467
468         LeaveCriticalSection( &p_condvar->csection );
469
470         /* Signal that the last waiting thread just went through */
471         if( !i_waiting_threads )
472             SetEvent( p_condvar->event );
473     }
474
475     /* Reacquire the mutex before returning. */
476     vlc_mutex_lock( p_mutex );
477
478     i_result = 0;
479
480 #elif defined( HAVE_KERNEL_SCHEDULER_H )
481     if( p_condvar == NULL )
482     {
483         i_result = B_BAD_VALUE;
484     }
485     else if( p_mutex == NULL )
486     {
487         i_result = B_BAD_VALUE;
488     }
489     else if( p_condvar->init < 2000 )
490     {
491         i_result = B_NO_INIT;
492     }
493
494     /* The p_condvar->thread var is initialized before the unlock because
495      * it enables to identify when the thread is interrupted beetwen the
496      * unlock line and the suspend_thread line */
497     p_condvar->thread = find_thread( NULL );
498     vlc_mutex_unlock( p_mutex );
499     suspend_thread( p_condvar->thread );
500     p_condvar->thread = -1;
501
502     vlc_mutex_lock( p_mutex );
503     i_result = 0;
504
505 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
506
507 #   ifdef DEBUG
508     /* In debug mode, timeout */
509     struct timeval now;
510     struct timespec timeout;
511
512     gettimeofday( &now, NULL );
513     timeout.tv_sec = now.tv_sec + THREAD_COND_TIMEOUT;
514     timeout.tv_nsec = now.tv_usec * 1000;
515
516     i_result = pthread_cond_timedwait( &p_condvar->cond, &p_mutex->mutex,
517                                        &timeout );
518
519     if( i_result == ETIMEDOUT )
520     {
521         msg_Dbg( p_condvar->p_this,
522                   "thread %li: possible condition deadlock "
523                   "at %s:%d (%s)", CAST_PTHREAD_TO_INT(pthread_self()),
524                   psz_file, i_line, strerror(i_result) );
525
526         i_result = pthread_cond_wait( &p_condvar->cond, &p_mutex->mutex );
527     }
528
529 #   else
530     i_result = pthread_cond_wait( &p_condvar->cond, &p_mutex->mutex );
531 #   endif
532
533     if ( i_result )
534     {
535         i_thread = CAST_PTHREAD_TO_INT(pthread_self());
536         psz_error = strerror(i_result);
537     }
538
539 #elif defined( HAVE_CTHREADS_H )
540     condition_wait( (condition_t)p_condvar, (mutex_t)p_mutex );
541     i_result = 0;
542
543 #endif
544
545     if( i_result )
546     {
547         msg_Err( p_condvar->p_this,
548                  "thread %li: cond_wait failed at %s:%d (%d:%s)",
549                  i_thread, psz_file, i_line, i_result, psz_error );
550     }
551
552     return i_result;
553 }
554
555 /*****************************************************************************
556  * vlc_cond_destroy: destroy a condition
557  *****************************************************************************/
558 #define vlc_cond_destroy( P_COND )                                          \
559     __vlc_cond_destroy( __FILE__, __LINE__, P_COND )
560
561 /*****************************************************************************
562  * vlc_threadvar_create: create a thread-local variable
563  *****************************************************************************/
564 #define vlc_threadvar_create( PTHIS, P_TLS )                                 \
565    __vlc_threadvar_create( PTHIS, P_TLS )
566
567 /*****************************************************************************
568  * vlc_threadvar_set: create: set the value of a thread-local variable
569  *****************************************************************************/
570 static inline int vlc_threadvar_set( vlc_threadvar_t * p_tls, void *p_value )
571 {
572     int i_ret;
573
574 #if defined( PTH_INIT_IN_PTH_H )
575     return pth_key_setdata( p_tls->handle, p_value );
576 #elif  defined( ST_INIT_IN_ST_H )
577     return st_thread_setspecific( p_tls->handle, p_value );
578 #elif defined( HAVE_KERNEL_SCHEDULER_H )
579     return -1;
580
581 #elif defined( UNDER_CE ) || defined( WIN32 )
582     i_ret = ( TlsSetValue( p_tls->handle, p_value ) != 0 );
583
584 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
585     i_ret = pthread_setspecific( p_tls->handle, p_value );
586
587 #elif defined( HAVE_CTHREADS_H )
588     i_ret = cthread_setspecific( p_tls->handle, p_value );
589 #endif
590
591     return i_ret;
592 }
593
594 /*****************************************************************************
595  * vlc_threadvar_get: create: get the value of a thread-local variable
596  *****************************************************************************/
597 static inline void* vlc_threadvar_get( vlc_threadvar_t * p_tls )
598 {
599     void* p_ret;
600
601 #if defined( PTH_INIT_IN_PTH_H )
602     p_ret = pth_key_getdata( p_handle->key );
603 #elif defined( ST_INIT_IN_ST_H )
604     p_ret = st_thread_getspecific( p_handle->key );
605 #elif defined( HAVE_KERNEL_SCHEDULER_H )
606     p_ret = NULL;
607 #elif defined( UNDER_CE ) || defined( WIN32 )
608     p_ret = TlsGetValue( p_tls->handle );
609
610 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
611     p_ret = pthread_getspecific( p_tls->handle );
612
613 #elif defined( HAVE_CTHREADS_H )
614     if ( !cthread_getspecific( p_tls->handle, &p_ret ) )
615     {
616         p_ret = NULL;
617     }
618 #endif
619
620     return p_ret;
621 }
622
623 /*****************************************************************************
624  * vlc_thread_create: create a thread
625  *****************************************************************************/
626 #define vlc_thread_create( P_THIS, PSZ_NAME, FUNC, PRIORITY, WAIT )         \
627     __vlc_thread_create( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PSZ_NAME, (void * ( * ) ( void * ))FUNC, PRIORITY, WAIT )
628
629 /*****************************************************************************
630  * vlc_thread_set_priority: set the priority of the calling thread
631  *****************************************************************************/
632 #define vlc_thread_set_priority( P_THIS, PRIORITY )                         \
633     __vlc_thread_set_priority( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PRIORITY )
634
635 /*****************************************************************************
636  * vlc_thread_ready: tell the parent thread we were successfully spawned
637  *****************************************************************************/
638 #define vlc_thread_ready( P_THIS )                                          \
639     __vlc_thread_ready( VLC_OBJECT(P_THIS) )
640
641 /*****************************************************************************
642  * vlc_thread_join: wait until a thread exits
643  *****************************************************************************/
644 #define vlc_thread_join( P_THIS )                                           \
645     __vlc_thread_join( VLC_OBJECT(P_THIS), __FILE__, __LINE__ )
646
647 #endif