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