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 * $Id: threads.h,v 1.17 2001/04/28 03:36:25 sam Exp $
8 * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
9 * Samuel Hocevar <sam@via.ecp.fr>
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.
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.
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 *****************************************************************************/
28 #if defined(PTHREAD_COND_T_IN_PTHREAD_H) /* pthreads (like Linux & BSD) */
31 #elif defined(HAVE_CTHREADS_H) /* GNUMach */
34 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H) /* BeOS */
37 #include <kernel/OS.h>
38 #include <kernel/scheduler.h>
39 #include <byteorder.h>
41 #elif defined(WIN32) /* Win32 with MinGW32 compiler */
46 #error no threads available on your system !
49 /*****************************************************************************
51 *****************************************************************************
52 * These constants are used by all threads in *_CreateThread() and
53 * *_DestroyThreads() functions. Since those calls are non-blocking, an integer
54 * value is used as a shared flag to represent the status of the thread.
55 *****************************************************************************/
57 /* Void status - this value can be used to make sure no operation is currently
58 * in progress on the concerned thread in an array of recorded threads */
59 #define THREAD_NOP 0 /* nothing happened */
62 #define THREAD_CREATE 10 /* thread is initializing */
63 #define THREAD_START 11 /* thread has forked */
64 #define THREAD_READY 19 /* thread is ready */
66 /* Destructions status */
67 #define THREAD_DESTROY 20 /* destruction order has been sent */
68 #define THREAD_END 21 /* destruction order has been received */
69 #define THREAD_OVER 29 /* thread does not exist any more */
72 #define THREAD_ERROR 30 /* an error occured */
73 #define THREAD_FATAL 31 /* an fatal error occured - program must end */
75 /*****************************************************************************
77 *****************************************************************************/
79 #if defined(PTHREAD_COND_T_IN_PTHREAD_H)
80 typedef pthread_t vlc_thread_t;
81 typedef pthread_mutex_t vlc_mutex_t;
82 typedef pthread_cond_t vlc_cond_t;
84 #elif defined(HAVE_CTHREADS_H)
85 typedef cthread_t vlc_thread_t;
87 /* Those structs are the ones defined in /include/cthreads.h but we need
88 * to handle (*foo) where foo is a (mutex_t) while they handle (foo) where
89 * foo is a (mutex_t*) */
90 typedef struct s_mutex {
94 struct cthread_queue queue;
97 typedef struct s_condition {
99 struct cthread_queue queue;
101 struct cond_imp *implications;
104 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
105 /* This is the BeOS implementation of the vlc threads, note that the mutex is
106 * not a real mutex and the cond_var is not like a pthread cond_var but it is
107 * enough for what wee need */
109 typedef thread_id vlc_thread_t;
124 typedef HANDLE vlc_thread_t;
125 typedef HANDLE vlc_mutex_t;
126 typedef HANDLE vlc_cond_t;
127 typedef unsigned (__stdcall *PTHREAD_START) (void *);
131 typedef void *(*vlc_thread_func_t)(void *p_data);
133 /*****************************************************************************
135 *****************************************************************************/
137 static __inline__ int vlc_thread_create ( vlc_thread_t *, char *,
138 vlc_thread_func_t, void * );
139 static __inline__ void vlc_thread_exit ( void );
140 static __inline__ void vlc_thread_join ( vlc_thread_t );
142 static __inline__ int vlc_mutex_init ( vlc_mutex_t * );
143 static __inline__ int vlc_mutex_lock ( vlc_mutex_t * );
144 static __inline__ int vlc_mutex_unlock ( vlc_mutex_t * );
145 static __inline__ int vlc_mutex_destroy ( vlc_mutex_t * );
147 static __inline__ int vlc_cond_init ( vlc_cond_t * );
148 static __inline__ int vlc_cond_signal ( vlc_cond_t * );
149 static __inline__ int vlc_cond_wait ( vlc_cond_t *, vlc_mutex_t * );
150 static __inline__ int vlc_cond_destroy ( vlc_cond_t * );
153 static __inline__ int vlc_cond_timedwait( vlc_cond_t *, vlc_mutex_t *,
157 /*****************************************************************************
158 * vlc_thread_create: create a thread
159 *****************************************************************************/
160 static __inline__ int vlc_thread_create( vlc_thread_t *p_thread,
161 char *psz_name, vlc_thread_func_t func,
164 #if defined(PTHREAD_COND_T_IN_PTHREAD_H)
165 return pthread_create( p_thread, NULL, func, p_data );
167 #elif defined(HAVE_CTHREADS_H)
168 *p_thread = cthread_fork( (cthread_fn_t)func, (any_t)p_data );
171 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
172 *p_thread = spawn_thread( (thread_func)func, psz_name,
173 B_NORMAL_PRIORITY, p_data );
174 return resume_thread( *p_thread );
179 /* This method is not recommended when using the MSVCRT C library,
180 * so we'll have to use _beginthreadex instead */
181 *p_thread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) func,
182 p_data, 0, &threadID);
185 /* When using the MSVCRT C library you have to use the _beginthreadex
186 * function instead of CreateThread, otherwise you'll end up with memory
187 * leaks and the signal function not working */
188 *p_thread = (HANDLE)_beginthreadex(NULL, 0, (PTHREAD_START) func,
189 p_data, 0, &threadID);
191 return( *p_thread ? 0 : 1 );
196 /*****************************************************************************
197 * vlc_thread_exit: terminate a thread
198 *****************************************************************************/
199 static __inline__ void vlc_thread_exit( void )
201 #if defined(PTHREAD_COND_T_IN_PTHREAD_H)
204 #elif defined(HAVE_CTHREADS_H)
206 cthread_exit( &result );
208 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
215 /* For now we don't close the thread handles (because of race conditions).
216 * Need to be looked at. */
222 /*****************************************************************************
223 * vlc_thread_join: wait until a thread exits
224 *****************************************************************************/
225 static __inline__ void vlc_thread_join( vlc_thread_t thread )
227 #if defined(PTHREAD_COND_T_IN_PTHREAD_H)
228 pthread_join( thread, NULL );
230 #elif defined(HAVE_CTHREADS_H)
231 cthread_join( thread );
233 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
235 wait_for_thread( thread, &exit_value );
238 WaitForSingleObject( thread, INFINITE);
243 /*****************************************************************************
244 * vlc_mutex_init: initialize a mutex
245 *****************************************************************************/
246 static __inline__ int vlc_mutex_init( vlc_mutex_t *p_mutex )
248 #if defined(PTHREAD_COND_T_IN_PTHREAD_H)
249 return pthread_mutex_init( p_mutex, NULL );
251 #elif defined(HAVE_CTHREADS_H)
252 mutex_init( p_mutex );
255 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
257 /* check the arguments and whether it's already been initialized */
258 if( p_mutex == NULL )
263 if( p_mutex->init == 9999 )
268 p_mutex->lock = create_sem( 1, "BeMutex" );
269 if( p_mutex->lock < B_NO_ERROR )
274 p_mutex->init = 9999;
278 *p_mutex = CreateMutex(0,FALSE,0);
279 return (*p_mutex?0:1);
284 /*****************************************************************************
285 * vlc_mutex_lock: lock a mutex
286 *****************************************************************************/
287 static __inline__ int vlc_mutex_lock( vlc_mutex_t *p_mutex )
289 #if defined(PTHREAD_COND_T_IN_PTHREAD_H)
290 return pthread_mutex_lock( p_mutex );
292 #elif defined(HAVE_CTHREADS_H)
293 mutex_lock( p_mutex );
296 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
304 if( p_mutex->init < 2000 )
309 err = acquire_sem( p_mutex->lock );
313 WaitForSingleObject( *p_mutex, INFINITE );
319 /*****************************************************************************
320 * vlc_mutex_unlock: unlock a mutex
321 *****************************************************************************/
322 static __inline__ int vlc_mutex_unlock( vlc_mutex_t *p_mutex )
324 #if defined(PTHREAD_COND_T_IN_PTHREAD_H)
325 return pthread_mutex_unlock( p_mutex );
327 #elif defined(HAVE_CTHREADS_H)
328 mutex_unlock( p_mutex );
331 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
337 if( p_mutex->init < 2000 )
342 release_sem( p_mutex->lock );
346 ReleaseMutex( *p_mutex );
352 /*****************************************************************************
353 * vlc_mutex_destroy: destroy a mutex
354 *****************************************************************************/
355 static __inline__ int vlc_mutex_destroy( vlc_mutex_t *p_mutex )
357 #if defined(PTHREAD_COND_T_IN_PTHREAD_H)
358 return pthread_mutex_destroy( p_mutex );
360 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
361 if( p_mutex->init == 9999 )
363 delete_sem( p_mutex->lock );
370 CloseHandle(*p_mutex);
376 /*****************************************************************************
377 * vlc_cond_init: initialize a condition
378 *****************************************************************************/
379 static __inline__ int vlc_cond_init( vlc_cond_t *p_condvar )
381 #if defined(PTHREAD_COND_T_IN_PTHREAD_H)
382 return pthread_cond_init( p_condvar, NULL );
384 #elif defined(HAVE_CTHREADS_H)
385 /* condition_init() */
386 spin_lock_init( &p_condvar->lock );
387 cthread_queue_init( &p_condvar->queue );
389 p_condvar->implications = 0;
393 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
399 if( p_condvar->init == 9999 )
404 p_condvar->thread = -1;
405 p_condvar->init = 9999;
409 /* Create an auto-reset event. */
410 *p_condvar = CreateEvent( NULL, /* no security */
411 FALSE, /* auto-reset event */
412 FALSE, /* non-signaled initially */
413 NULL ); /* unnamed */
415 return( *p_condvar ? 0 : 1 );
420 /*****************************************************************************
421 * vlc_cond_signal: start a thread on condition completion
422 *****************************************************************************/
423 static __inline__ int vlc_cond_signal( vlc_cond_t *p_condvar )
425 #if defined(PTHREAD_COND_T_IN_PTHREAD_H)
426 return pthread_cond_signal( p_condvar );
428 #elif defined(HAVE_CTHREADS_H)
429 /* condition_signal() */
430 if ( p_condvar->queue.head || p_condvar->implications )
432 cond_signal( (condition_t)p_condvar );
436 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
442 if( p_condvar->init < 2000 )
447 while( p_condvar->thread != -1 )
450 if( get_thread_info(p_condvar->thread, &info) == B_BAD_VALUE )
455 if( info.state != B_THREAD_SUSPENDED )
457 /* The waiting thread is not suspended so it could
458 * have been interrupted beetwen the unlock and the
459 * suspend_thread line. That is why we sleep a little
460 * before retesting p_condver->thread. */
465 /* Ok, we have to wake up that thread */
466 resume_thread( p_condvar->thread );
473 /* Try to release one waiting thread. */
474 PulseEvent ( *p_condvar );
480 /*****************************************************************************
481 * vlc_cond_wait: wait until condition completion
482 *****************************************************************************/
483 static __inline__ int vlc_cond_wait( vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex )
485 #if defined(PTHREAD_COND_T_IN_PTHREAD_H)
486 return pthread_cond_wait( p_condvar, p_mutex );
488 #elif defined(HAVE_CTHREADS_H)
489 condition_wait( (condition_t)p_condvar, (mutex_t)p_mutex );
492 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
503 if( p_condvar->init < 2000 )
508 /* The p_condvar->thread var is initialized before the unlock because
509 * it enables to identify when the thread is interrupted beetwen the
510 * unlock line and the suspend_thread line */
511 p_condvar->thread = find_thread( NULL );
512 vlc_mutex_unlock( p_mutex );
513 suspend_thread( p_condvar->thread );
514 p_condvar->thread = -1;
516 vlc_mutex_lock( p_mutex );
520 /* Release the <external_mutex> here and wait for the event
521 * to become signaled, due to <pthread_cond_signal> being
523 vlc_mutex_unlock( p_mutex );
525 WaitForSingleObject( *p_condvar, INFINITE );
527 /* Reacquire the mutex before returning. */
528 vlc_mutex_lock( p_mutex );
534 /*****************************************************************************
535 * vlc_cond_destroy: destroy a condition
536 *****************************************************************************/
537 static __inline__ int vlc_cond_destroy( vlc_cond_t *p_condvar )
539 #if defined(PTHREAD_COND_T_IN_PTHREAD_H)
540 return pthread_cond_destroy( p_condvar );
542 #elif defined(HAVE_KERNEL_SCHEDULER_H) && defined(HAVE_KERNEL_OS_H)
547 CloseHandle( *p_condvar );