]> git.sesse.net Git - vlc/blob - src/misc/threads.c
* ./src/misc/beos_specific.cpp: BeOS fixes, removed a static variable.
[vlc] / src / misc / threads.c
1 /*****************************************************************************
2  * threads.c : threads implementation for the VideoLAN client
3  *****************************************************************************
4  * Copyright (C) 1999, 2000, 2001, 2002 VideoLAN
5  * $Id: threads.c,v 1.2 2002/06/01 14:31:32 sam Exp $
6  *
7  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
9  *          Gildas Bazin <gbazin@netcourrier.com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 #include <vlc/vlc.h>
27
28 #define VLC_THREADS_UNINITIALIZED  0
29 #define VLC_THREADS_PENDING        1
30 #define VLC_THREADS_ERROR          2
31 #define VLC_THREADS_READY          3
32
33 /*****************************************************************************
34  * Prototype for GPROF wrapper
35  *****************************************************************************/
36 #ifdef GPROF
37 /* Wrapper function for profiling */
38 static void *      vlc_thread_wrapper ( void *p_wrapper );
39
40 #   ifdef WIN32
41
42 #       define ITIMER_REAL 1
43 #       define ITIMER_PROF 2
44
45 struct itimerval
46 {
47     struct timeval it_value;
48     struct timeval it_interval;
49 };  
50
51 int setitimer(int kind, const struct itimerval* itnew, struct itimerval* itold);
52 #   endif /* WIN32 */
53
54 typedef struct wrapper_s
55 {
56     /* Data lock access */
57     vlc_mutex_t lock;
58     vlc_cond_t  wait;
59     
60     /* Data used to spawn the real thread */
61     vlc_thread_func_t func;
62     void *p_data;
63     
64     /* Profiling timer passed to the thread */
65     struct itimerval itimer;
66     
67 } wrapper_t;
68
69 #endif /* GPROF */
70
71 /*****************************************************************************
72  * vlc_threads_init: initialize threads system
73  *****************************************************************************/
74 int __vlc_threads_init( vlc_object_t *p_this )
75 {
76     /* FIXME: this is definitely _not_ threadsafe, but at least it works
77      * under all implementations. We should for instance use pthread_once
78      * for lazy initialization of the global lock. */
79     static int i_status = VLC_THREADS_UNINITIALIZED;
80     int i_ret;
81
82     if( i_status == VLC_THREADS_READY )
83     {
84         return 0;
85     }
86
87     if( i_status == VLC_THREADS_UNINITIALIZED )
88     {
89         i_status = VLC_THREADS_PENDING;
90
91 #if defined( PTH_INIT_IN_PTH_H )
92         i_ret = pth_init();
93
94 #elif defined( ST_INIT_IN_ST_H )
95         i_ret = st_init();
96
97 #elif defined( WIN32 )
98         i_ret = 0;
99
100 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
101         i_ret = 0;
102
103 #elif defined( HAVE_CTHREADS_H )
104         i_ret = 0;
105
106 #elif defined( HAVE_KERNEL_SCHEDULER_H )
107         i_ret = 0;
108
109 #endif
110         if( i_ret )
111         {
112             i_status = VLC_THREADS_ERROR;
113             return i_ret;
114         }
115
116         vlc_mutex_init( p_this, p_this->p_vlc->p_global_lock );
117
118         i_status = VLC_THREADS_READY;
119
120         return i_ret;
121     }
122
123     /* Wait until the other thread has initialized the thread library */
124     while( i_status == VLC_THREADS_PENDING )
125     {
126         msleep( THREAD_SLEEP );
127     }
128
129     return( i_status == VLC_THREADS_READY );
130 }
131
132 /*****************************************************************************
133  * vlc_threads_end: stop threads system
134  *****************************************************************************/
135 int vlc_threads_end( void )
136 {
137 #if defined( PTH_INIT_IN_PTH_H )
138     return pth_kill();
139
140 #elif defined( ST_INIT_IN_ST_H )
141     return 0;
142
143 #elif defined( WIN32 )
144     return 0;
145
146 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
147     return 0;
148
149 #elif defined( HAVE_CTHREADS_H )
150     return 0;
151
152 #elif defined( HAVE_KERNEL_SCHEDULER_H )
153     return 0;
154
155 #endif
156 }
157
158 static int mutexes = 0;
159 /*****************************************************************************
160  * vlc_mutex_init: initialize a mutex
161  *****************************************************************************/
162 int __vlc_mutex_init( vlc_object_t *p_this, vlc_mutex_t *p_mutex )
163 {
164 #if defined( PTH_INIT_IN_PTH_H )
165     return pth_mutex_init( p_mutex );
166
167 #elif defined( ST_INIT_IN_ST_H )
168     *p_mutex = st_mutex_new();
169     return ( *p_mutex == NULL ) ? errno : 0;
170
171 #elif defined( WIN32 )
172     /* We use mutexes on WinNT/2K/XP because we can use the SignalObjectAndWait
173      * function and have a 100% correct vlc_cond_wait() implementation.
174      * As this function is not available on Win9x, we can use the faster
175      * CriticalSections */
176     if( (GetVersion() < 0x80000000) && !p_this->p_vlc->b_fast_pthread )
177     {
178         /* We are running on NT/2K/XP, we can use SignalObjectAndWait */
179         p_mutex->mutex = CreateMutex( 0, FALSE, 0 );
180         p_mutex->SignalObjectAndWait = p_this->p_vlc->SignalObjectAndWait;
181         return ( p_mutex->mutex ? 0 : 1 );
182     }
183     else
184     {
185         InitializeCriticalSection( &p_mutex->csection );
186         p_mutex->mutex = NULL;
187         return 0;
188     }
189
190 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
191 #   if defined(DEBUG) && defined(SYS_LINUX)
192     /* Create error-checking mutex to detect threads problems more easily. */
193     pthread_mutexattr_t attr;
194     int                 i_result;
195
196     pthread_mutexattr_init( &attr );
197     pthread_mutexattr_setkind_np( &attr, PTHREAD_MUTEX_ERRORCHECK_NP );
198     i_result = pthread_mutex_init( p_mutex, &attr );
199     pthread_mutexattr_destroy( &attr );
200     return( i_result );
201 #   endif
202     return pthread_mutex_init( p_mutex, NULL );
203
204 #elif defined( HAVE_CTHREADS_H )
205     mutex_init( p_mutex );
206     return 0;
207
208 #elif defined( HAVE_KERNEL_SCHEDULER_H )
209
210     /* check the arguments and whether it's already been initialized */
211     if( p_mutex == NULL )
212     {
213         return B_BAD_VALUE;
214     }
215
216     if( p_mutex->init == 9999 )
217     {
218         return EALREADY;
219     }
220
221     p_mutex->lock = create_sem( 1, "BeMutex" );
222     if( p_mutex->lock < B_NO_ERROR )
223     {
224         return( -1 );
225     }
226
227     p_mutex->init = 9999;
228     return B_OK;
229
230 #endif
231 }
232
233 /*****************************************************************************
234  * vlc_mutex_destroy: destroy a mutex, inner version
235  *****************************************************************************/
236 int __vlc_mutex_destroy( char * psz_file, int i_line, vlc_mutex_t *p_mutex )
237 {
238 #if defined( PTH_INIT_IN_PTH_H )
239     return 0;
240
241 #elif defined( ST_INIT_IN_ST_H )
242     return st_mutex_destroy( *p_mutex );
243
244 #elif defined( WIN32 )
245     if( p_mutex->mutex )
246     {
247         CloseHandle( p_mutex->mutex );
248     }
249     else
250     {
251         DeleteCriticalSection( &p_mutex->csection );
252     }
253     return 0;
254
255 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )    
256     int i_return = pthread_mutex_destroy( p_mutex );
257     if( i_return )
258     {
259 //X        intf_ErrMsg( "thread %d error: mutex_destroy failed at %s:%d (%s)",
260 //X                     pthread_self(), psz_file, i_line, strerror(i_return) );
261     }
262     return i_return;
263
264 #elif defined( HAVE_CTHREADS_H )
265     return 0;
266
267 #elif defined( HAVE_KERNEL_SCHEDULER_H )
268     if( p_mutex->init == 9999 )
269     {
270         delete_sem( p_mutex->lock );
271     }
272
273     p_mutex->init = 0;
274     return B_OK;
275
276 #endif    
277 }
278
279 /*****************************************************************************
280  * vlc_cond_init: initialize a condition
281  *****************************************************************************/
282 int vlc_cond_init( vlc_cond_t *p_condvar )
283 {
284 #if defined( PTH_INIT_IN_PTH_H )
285     return pth_cond_init( p_condvar );
286
287 #elif defined( ST_INIT_IN_ST_H )
288     *p_condvar = st_cond_new();
289     return ( *p_condvar == NULL ) ? errno : 0;
290
291 #elif defined( WIN32 )
292     /* initialise counter */
293     p_condvar->i_waiting_threads = 0;
294
295     /* Create an auto-reset event. */
296     p_condvar->signal = CreateEvent( NULL, /* no security */
297                                      FALSE,  /* auto-reset event */
298                                      FALSE,  /* non-signaled initially */
299                                      NULL ); /* unnamed */
300
301     return( !p_condvar->signal );
302
303 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
304     return pthread_cond_init( p_condvar, NULL );
305
306 #elif defined( HAVE_CTHREADS_H )
307     /* condition_init() */
308     spin_lock_init( &p_condvar->lock );
309     cthread_queue_init( &p_condvar->queue );
310     p_condvar->name = 0;
311     p_condvar->implications = 0;
312
313     return 0;
314
315 #elif defined( HAVE_KERNEL_SCHEDULER_H )
316     if( !p_condvar )
317     {
318         return B_BAD_VALUE;
319     }
320
321     if( p_condvar->init == 9999 )
322     {
323         return EALREADY;
324     }
325
326     p_condvar->thread = -1;
327     p_condvar->init = 9999;
328     return 0;
329
330 #endif
331 }
332
333 /*****************************************************************************
334  * vlc_cond_destroy: destroy a condition, inner version
335  *****************************************************************************/
336 int __vlc_cond_destroy( char * psz_file, int i_line, vlc_cond_t *p_condvar )
337 {
338 #if defined( PTH_INIT_IN_PTH_H )
339     return 0;
340
341 #elif defined( ST_INIT_IN_ST_H )
342     return st_cond_destroy( *p_condvar );
343
344 #elif defined( WIN32 )
345     return( !CloseHandle( p_condvar->signal ) );
346
347 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
348     int i_result = pthread_cond_destroy( p_condvar );
349     if( i_result )
350     {
351 //X        intf_ErrMsg( "thread %d error: cond_destroy failed at %s:%d (%s)",
352 //X                     pthread_self(), psz_file, i_line, strerror(i_result) );
353     }
354     return i_result;
355
356 #elif defined( HAVE_CTHREADS_H )
357     return 0;
358
359 #elif defined( HAVE_KERNEL_SCHEDULER_H )
360     p_condvar->init = 0;
361     return 0;
362
363 #endif    
364 }
365
366 /*****************************************************************************
367  * vlc_thread_create: create a thread, inner version
368  *****************************************************************************/
369 int __vlc_thread_create( vlc_object_t *p_this, char * psz_file, int i_line,
370                          char *psz_name, void * ( *func ) ( void * ),
371                          vlc_bool_t b_wait )
372 {
373     int i_ret;
374
375     vlc_mutex_init( p_this, &p_this->thread_lock );
376     vlc_cond_init( &p_this->thread_wait );
377     vlc_mutex_lock( &p_this->thread_lock );
378
379 #ifdef GPROF
380     wrapper_t wrapper;
381
382     /* Initialize the wrapper structure */
383     wrapper.func = func;
384     wrapper.p_data = (void *)p_this;
385     getitimer( ITIMER_PROF, &wrapper.itimer );
386     vlc_mutex_init( p_this, &wrapper.lock );
387     vlc_cond_init( &wrapper.wait );
388     vlc_mutex_lock( &wrapper.lock );
389
390     /* Alter user-passed data so that we call the wrapper instead
391      * of the real function */
392     p_data = &wrapper;
393     func = vlc_thread_wrapper;
394 #endif
395
396 #if defined( PTH_INIT_IN_PTH_H )
397     p_this->thread_id = pth_spawn( PTH_ATTR_DEFAULT, func, (void *)p_this );
398     i_ret = 0;
399
400 #elif defined( ST_INIT_IN_ST_H )
401     p_this->thread_id = st_thread_create( func, (void *)p_this, 1, 0 );
402     i_ret = 0;
403     
404 #elif defined( WIN32 )
405     unsigned threadID;
406     /* When using the MSVCRT C library you have to use the _beginthreadex
407      * function instead of CreateThread, otherwise you'll end up with memory
408      * leaks and the signal functions not working */
409     p_this->thread_id = (HANDLE)_beginthreadex( NULL, 0, (PTHREAD_START) func, 
410                                                 (void *)p_this, 0, &threadID );
411     
412     i_ret = ( p_this->thread_id ? 0 : 1 );
413
414 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
415     i_ret = pthread_create( &p_this->thread_id, NULL, func, (void *)p_this );
416
417 #elif defined( HAVE_CTHREADS_H )
418     p_this->thread_id = cthread_fork( (cthread_fn_t)func, (any_t)p_this );
419     i_ret = 0;
420
421 #elif defined( HAVE_KERNEL_SCHEDULER_H )
422     p_this->thread_id = spawn_thread( (thread_func)func, psz_name,
423                                       B_NORMAL_PRIORITY, (void *)p_this );
424     i_ret = resume_thread( p_this->thread_id );
425
426 #endif
427
428 #ifdef GPROF
429     if( i_ret == 0 )
430     {
431         vlc_cond_wait( &wrapper.wait, &wrapper.lock );
432     }
433
434     vlc_mutex_unlock( &wrapper.lock );
435     vlc_mutex_destroy( &wrapper.lock );
436     vlc_cond_destroy( &wrapper.wait );
437 #endif
438
439     if( i_ret == 0 )
440     {
441         msg_Dbg( p_this, "thread %d (%s) created (%s:%d)",
442                          p_this->thread_id, psz_name, psz_file, i_line );
443
444         p_this->b_thread = 1;
445
446         if( b_wait )
447         {
448             msg_Dbg( p_this, "waiting for thread completion" );
449             vlc_cond_wait( &p_this->thread_wait, &p_this->thread_lock );
450         }
451
452         vlc_mutex_unlock( &p_this->thread_lock );
453     }
454     else
455     {
456         msg_Err( p_this, "%s thread could not be created at %s:%d (%s)",
457                          psz_name, psz_file, i_line, strerror(i_ret) );
458         vlc_mutex_unlock( &p_this->thread_lock );
459         vlc_mutex_destroy( &p_this->thread_lock );
460         vlc_cond_destroy( &p_this->thread_wait );
461     }
462
463     return i_ret;
464 }
465
466 /*****************************************************************************
467  * vlc_thread_ready: tell the parent thread we were successfully spawned
468  *****************************************************************************/
469 void __vlc_thread_ready( vlc_object_t *p_this )
470 {
471     vlc_mutex_lock( &p_this->thread_lock );
472     vlc_cond_signal( &p_this->thread_wait );
473     vlc_mutex_unlock( &p_this->thread_lock );
474 }
475
476 /*****************************************************************************
477  * vlc_thread_join: wait until a thread exits, inner version
478  *****************************************************************************/
479 void __vlc_thread_join( vlc_object_t *p_this, char * psz_file, int i_line )
480 {
481     int i_ret = 0;
482
483 #if defined( PTH_INIT_IN_PTH_H )
484     i_ret = pth_join( p_this->thread_id, NULL );
485
486 #elif defined( ST_INIT_IN_ST_H )
487     i_ret = st_thread_join( p_this->thread_id, NULL );
488     
489 #elif defined( WIN32 )
490     WaitForSingleObject( p_this->thread_id, INFINITE );
491
492 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
493     i_ret = pthread_join( p_this->thread_id, NULL );
494
495 #elif defined( HAVE_CTHREADS_H )
496     cthread_join( p_this->thread_id );
497     i_ret = 1;
498
499 #elif defined( HAVE_KERNEL_SCHEDULER_H )
500     int32 exit_value;
501     wait_for_thread( p_this->thread_id, &exit_value );
502
503 #endif
504
505     vlc_mutex_destroy( &p_this->thread_lock );
506     vlc_cond_destroy( &p_this->thread_wait );
507
508     if( i_ret )
509     {
510         msg_Err( p_this, "thread_join(%d) failed at %s:%d (%s)",
511                          p_this->thread_id, psz_file, i_line, strerror(i_ret) );
512     }
513     else
514     {
515         msg_Dbg( p_this, "thread %d joined (%s:%d)",
516                          p_this->thread_id, psz_file, i_line );
517     }
518
519     p_this->b_thread = 0;
520 }
521
522 /*****************************************************************************
523  * vlc_thread_wrapper: wrapper around thread functions used when profiling.
524  *****************************************************************************/
525 #ifdef GPROF
526 static void *vlc_thread_wrapper( void *p_wrapper )
527 {
528     /* Put user data in thread-local variables */
529     void *            p_data = ((wrapper_t*)p_wrapper)->p_data;
530     vlc_thread_func_t func   = ((wrapper_t*)p_wrapper)->func;
531
532     /* Set the profile timer value */
533     setitimer( ITIMER_PROF, &((wrapper_t*)p_wrapper)->itimer, NULL );
534
535     /* Tell the calling thread that we don't need its data anymore */
536     vlc_mutex_lock( &((wrapper_t*)p_wrapper)->lock );
537     vlc_cond_signal( &((wrapper_t*)p_wrapper)->wait );
538     vlc_mutex_unlock( &((wrapper_t*)p_wrapper)->lock );
539
540     /* Call the real function */
541     return func( p_data );
542 }
543 #endif