1 /*****************************************************************************
2 * threads.h : thread implementation for VideoLAN client
3 * This header is supposed to provide a portable threads implementation.
4 * Currently, it is a wrapper to either the POSIX pthreads library, or
5 * the Mach cthreads (for the GNU/Hurd).
6 *****************************************************************************
7 * Copyright (C) 1999, 2000 VideoLAN
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 GNU
19 * General Public License for more details.
21 * You should have received a copy of the GNU General Public
22 * License along with this program; if not, write to the
23 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 * Boston, MA 02111-1307, USA.
25 *****************************************************************************/
33 #include <kernel/OS.h>
34 #include <kernel/scheduler.h>
37 #if defined(SYS_LINUX) || defined(SYS_BSD)
41 /*****************************************************************************
43 *****************************************************************************
44 * These constants are used by all threads in *_CreateThread() and
45 * *_DestroyThreads() functions. Since those calls are non-blocking, an integer
46 * value is used as a shared flag to represent the status of the thread.
47 *****************************************************************************/
49 /* Void status - this value can be used to be sure, in an array of recorded
50 * threads, that no operation is currently in progress on the concerned thread */
51 #define THREAD_NOP 0 /* nothing happened */
54 #define THREAD_CREATE 10 /* thread is initializing */
55 #define THREAD_START 11 /* thread has forked */
56 #define THREAD_READY 19 /* thread is ready */
58 /* Destructions status */
59 #define THREAD_DESTROY 20 /* destruction order has been sent */
60 #define THREAD_END 21 /* destruction order has been received */
61 #define THREAD_OVER 29 /* thread does not exist any more */
64 #define THREAD_ERROR 30 /* an error occured */
65 #define THREAD_FATAL 31 /* an fatal error occured - program must end */
67 /*****************************************************************************
69 *****************************************************************************/
73 typedef cthread_t vlc_thread_t;
75 /* those structs are the ones defined in /include/cthreads.h but we need
76 * * to handle *foo where foo is a mutex_t */
77 typedef struct s_mutex {
81 struct cthread_queue queue;
84 typedef struct s_condition {
86 struct cthread_queue queue;
88 struct cond_imp *implications;
95 typedef thread_id vlc_thread_t;
114 #endif /* SYS_BEOS */
116 #if defined(SYS_LINUX) || defined(SYS_BSD)
118 typedef pthread_t vlc_thread_t;
119 typedef pthread_mutex_t vlc_mutex_t;
120 typedef pthread_cond_t vlc_cond_t;
122 #endif /* SYS_LINUX || SYS_BSD */
124 typedef void *(*vlc_thread_func_t)(void *p_data);
126 /*****************************************************************************
128 *****************************************************************************/
130 static __inline__ int vlc_thread_create( vlc_thread_t *p_thread, char *psz_name,
131 vlc_thread_func_t func, void *p_data );
132 static __inline__ void vlc_thread_exit ( void );
133 static __inline__ void vlc_thread_join ( vlc_thread_t thread );
135 static __inline__ int vlc_mutex_init ( vlc_mutex_t *p_mutex );
136 static __inline__ int vlc_mutex_lock ( vlc_mutex_t *p_mutex );
137 static __inline__ int vlc_mutex_unlock ( vlc_mutex_t *p_mutex );
139 static __inline__ int vlc_cond_init ( vlc_cond_t *p_condvar );
140 static __inline__ int vlc_cond_signal ( vlc_cond_t *p_condvar );
141 static __inline__ int vlc_cond_wait ( vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex );
144 static _inline__ int vlc_cond_timedwait ( vlc_cond_t * condvar, vlc_mutex_t * mutex,
145 mtime_t absoute_timeout_time );
148 /*****************************************************************************
149 * vlc_thread_create: create a thread
150 *****************************************************************************/
151 static __inline__ int vlc_thread_create( vlc_thread_t *p_thread,
152 char *psz_name, vlc_thread_func_t func,
156 *p_thread = cthread_fork( (cthread_fn_t)func, (any_t)p_data );
161 *p_thread = spawn_thread( (thread_func)func, psz_name, B_NORMAL_PRIORITY, p_data );
162 return resume_thread( *p_thread );
165 #if defined(SYS_LINUX) || defined(SYS_BSD)
166 return pthread_create( p_thread, NULL, func, p_data );
170 /*****************************************************************************
171 * vlc_thread_exit: terminate a thread
172 *****************************************************************************/
173 static __inline__ void vlc_thread_exit( void )
177 cthread_exit( &result );
184 #if defined(SYS_LINUX) || defined(SYS_BSD)
189 /*****************************************************************************
190 * vlc_thread_join: wait until a thread exits
191 *****************************************************************************/
192 static __inline__ void vlc_thread_join( vlc_thread_t thread )
195 cthread_join( thread );
200 wait_for_thread( thread, &exit_value );
203 #if defined(SYS_LINUX) || defined(SYS_BSD)
204 pthread_join( thread, NULL );
209 /* lazy_init_mutex */
210 static __inline__ void lazy_init_mutex(vlc_mutex_t* p_mutex)
212 int32 v = atomic_or( &p_mutex->init, 1 );
213 if( 2000 == v ) // we're the first, so do the init
215 vlc_mutex_init( p_mutex );
217 else // we're not the first, so wait until the init is finished
219 while( p_mutex->init != 9999 ) snooze( 10000 );
224 /*****************************************************************************
225 * vlc_mutex_init: initialize a mutex
226 *****************************************************************************/
227 static __inline__ int vlc_mutex_init( vlc_mutex_t *p_mutex )
230 mutex_init( p_mutex );
235 // check the arguments and whether it's already been initialized
236 if( !p_mutex ) return B_BAD_VALUE;
237 if( p_mutex->init == 9999 ) return EALREADY;
239 p_mutex->lock = create_sem( 1, "BeMutex" );
241 p_mutex->init = 9999;
245 #if defined(SYS_LINUX) || defined(SYS_BSD)
246 return pthread_mutex_init( p_mutex, NULL );
250 /*****************************************************************************
251 * vlc_mutex_lock: lock a mutex
252 *****************************************************************************/
253 static __inline__ int vlc_mutex_lock( vlc_mutex_t *p_mutex )
256 mutex_lock( p_mutex );
263 if( !p_mutex ) return B_BAD_VALUE;
264 if( p_mutex->init < 2000 ) return B_NO_INIT;
265 lazy_init_mutex( p_mutex );
267 err = acquire_sem( p_mutex->lock );
268 if( !err ) p_mutex->owner = find_thread( NULL );
272 #if defined(SYS_LINUX) || defined(SYS_BSD)
273 return pthread_mutex_lock( p_mutex );
277 /*****************************************************************************
278 * vlc_mutex_unlock: unlock a mutex
279 *****************************************************************************/
280 static __inline__ int vlc_mutex_unlock( vlc_mutex_t *p_mutex )
283 mutex_unlock( p_mutex );
288 if(! p_mutex) return B_BAD_VALUE;
289 if( p_mutex->init < 2000 ) return B_NO_INIT;
290 lazy_init_mutex( p_mutex );
292 if( p_mutex->owner != find_thread(NULL) ) return ENOLCK;
294 release_sem( p_mutex->lock );
298 #if defined(SYS_LINUX) || defined(SYS_BSD)
299 return pthread_mutex_unlock( p_mutex );
305 static __inline__ void lazy_init_cond( vlc_cond_t* p_condvar )
307 int32 v = atomic_or( &p_condvar->init, 1 );
308 if( 2000 == v ) // we're the first, so do the init
310 vlc_cond_init( p_condvar );
312 else // we're not the first, so wait until the init is finished
314 while( p_condvar->init != 9999 ) snooze( 10000 );
319 /*****************************************************************************
320 * vlc_cond_init: initialize a condition
321 *****************************************************************************/
322 static __inline__ int vlc_cond_init( vlc_cond_t *p_condvar )
325 /* condition_init() */
326 spin_lock_init( &p_condvar->lock );
327 cthread_queue_init( &p_condvar->queue );
329 p_condvar->implications = 0;
335 if( !p_condvar ) return B_BAD_VALUE;
336 if( p_condvar->init == 9999 ) return EALREADY;
338 p_condvar->sem = create_sem( 0, "CVSem" );
339 p_condvar->handshakeSem = create_sem( 0, "CVHandshake" );
340 p_condvar->signalSem = create_sem( 1, "CVSignal" );
341 p_condvar->ns = p_condvar->nw = 0;
342 p_condvar->init = 9999;
346 #if defined(SYS_LINUX) || defined(SYS_BSD)
347 return pthread_cond_init( p_condvar, NULL );
351 /*****************************************************************************
352 * vlc_cond_signal: start a thread on condition completion
353 *****************************************************************************/
354 static __inline__ int vlc_cond_signal( vlc_cond_t *p_condvar )
357 /* condition_signal() */
358 if ( p_condvar->queue.head || p_condvar->implications )
360 cond_signal( (condition_t)p_condvar );
368 if( !p_condvar ) return B_BAD_VALUE;
369 if( p_condvar->init < 2000 ) return B_NO_INIT;
370 lazy_init_cond( p_condvar );
372 if( acquire_sem(p_condvar->signalSem) == B_INTERRUPTED) return B_INTERRUPTED;
374 if( p_condvar->nw > p_condvar->ns )
377 release_sem( p_condvar->sem );
378 release_sem( p_condvar->signalSem );
379 while( acquire_sem(p_condvar->handshakeSem) == B_INTERRUPTED )
380 { err = B_INTERRUPTED; }
384 release_sem( p_condvar->signalSem );
389 #if defined(SYS_LINUX) || defined(SYS_BSD)
390 return pthread_cond_signal( p_condvar );
394 /*****************************************************************************
395 * vlc_cond_wait: wait until condition completion
396 *****************************************************************************/
397 static __inline__ int vlc_cond_wait( vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex )
400 condition_wait( (condition_t)p_condvar, (mutex_t)p_mutex );
407 if( !p_condvar ) return B_BAD_VALUE;
408 if( !p_mutex ) return B_BAD_VALUE;
409 if( p_condvar->init < 2000 ) return B_NO_INIT;
410 lazy_init_cond( p_condvar );
412 if( acquire_sem(p_condvar->signalSem) == B_INTERRUPTED ) return B_INTERRUPTED;
414 release_sem( p_condvar->signalSem );
416 vlc_mutex_unlock( p_mutex );
417 err = acquire_sem( p_condvar->sem );
419 while( acquire_sem(p_condvar->signalSem) == B_INTERRUPTED)
420 { err = B_INTERRUPTED; }
421 if( p_condvar->ns > 0 )
423 release_sem( p_condvar->handshakeSem );
427 release_sem( p_condvar->signalSem );
429 while( vlc_mutex_lock(p_mutex) == B_INTERRUPTED)
430 { err = B_INTERRUPTED; }
434 #if defined(SYS_LINUX) || defined(SYS_BSD)
435 return pthread_cond_wait( p_condvar, p_mutex );