]> git.sesse.net Git - vlc/blob - src/misc/threads.c
* ./include/vlc_threads_funcs.h, ./src/misc/threads.c: eradicated
[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.22 2002/10/15 08:35:24 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 #include <stdlib.h>
29
30 #define VLC_THREADS_UNINITIALIZED  0
31 #define VLC_THREADS_PENDING        1
32 #define VLC_THREADS_ERROR          2
33 #define VLC_THREADS_READY          3
34
35 /*****************************************************************************
36  * Global mutex for lazy initialization of the threads system
37  *****************************************************************************/
38 static volatile int i_initializations = 0;
39
40 #if defined( PTH_INIT_IN_PTH_H )
41 #elif defined( ST_INIT_IN_ST_H )
42 #elif defined( WIN32 )
43 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
44     static pthread_mutex_t once_mutex = PTHREAD_MUTEX_INITIALIZER;
45 #elif defined( HAVE_CTHREADS_H )
46 #elif defined( HAVE_KERNEL_SCHEDULER_H )
47 #endif
48
49 /*****************************************************************************
50  * Global variable for named mutexes
51  *****************************************************************************/
52 typedef struct vlc_namedmutex_t vlc_namedmutex_t;
53 struct vlc_namedmutex_t
54 {
55     vlc_mutex_t lock;
56
57     char *psz_name;
58     int i_usage;
59     vlc_namedmutex_t *p_next;
60 };
61
62 static vlc_namedmutex_t *p_named_list = NULL;
63 static vlc_mutex_t named_lock;
64
65 /*****************************************************************************
66  * vlc_threads_init: initialize threads system
67  *****************************************************************************
68  * This function requires lazy initialization of a global lock in order to
69  * keep the library really thread-safe. Some architectures don't support this
70  * and thus do not guarantee the complete reentrancy.
71  *****************************************************************************/
72 int __vlc_threads_init( vlc_object_t *p_this )
73 {
74     static volatile int i_status = VLC_THREADS_UNINITIALIZED;
75
76     libvlc_t *p_libvlc = (libvlc_t *)p_this;
77     int i_ret = VLC_SUCCESS;
78
79     /* If we have lazy mutex initialization, use it. Otherwise, we just
80      * hope nothing wrong happens. */
81 #if defined( PTH_INIT_IN_PTH_H )
82 #elif defined( ST_INIT_IN_ST_H )
83 #elif defined( WIN32 )
84     HINSTANCE hInstLib;
85 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
86     pthread_mutex_lock( &once_mutex );
87 #elif defined( HAVE_CTHREADS_H )
88 #elif defined( HAVE_KERNEL_SCHEDULER_H )
89 #endif
90
91     if( i_status == VLC_THREADS_UNINITIALIZED )
92     {
93         i_status = VLC_THREADS_PENDING;
94
95         /* We should be safe now. Do all the initialization stuff we want. */
96         vlc_object_create( p_libvlc, VLC_OBJECT_ROOT );
97         p_libvlc->b_ready = VLC_FALSE;
98
99 #if defined( PTH_INIT_IN_PTH_H )
100         i_ret = pth_init();
101
102 #elif defined( ST_INIT_IN_ST_H )
103         i_ret = st_init();
104
105 #elif defined( WIN32 )
106         /* Dynamically get the address of SignalObjectAndWait */
107         if( GetVersion() < 0x80000000 )
108         {
109             /* We are running on NT/2K/XP, we can use SignalObjectAndWait */
110             hInstLib = LoadLibrary( "kernel32" );
111             if( hInstLib )
112             {
113                 p_libvlc->SignalObjectAndWait =
114                     (SIGNALOBJECTANDWAIT)GetProcAddress( hInstLib,
115                                                      "SignalObjectAndWait" );
116             }
117         }
118         else
119         {
120             p_libvlc->SignalObjectAndWait = NULL;
121         }
122
123         p_libvlc->b_fast_mutex = 0;
124         p_libvlc->i_win9x_cv = 0;
125
126 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
127 #elif defined( HAVE_CTHREADS_H )
128 #elif defined( HAVE_KERNEL_SCHEDULER_H )
129 #endif
130
131         vlc_mutex_init( p_libvlc, &named_lock );
132
133         if( i_ret )
134         {
135             i_status = VLC_THREADS_ERROR;
136         }
137         else
138         {
139             i_initializations++;
140             i_status = VLC_THREADS_READY;
141         }
142     }
143     else
144     {
145         /* Just increment the initialization count */
146         i_initializations++;
147     }
148
149     /* If we have lazy mutex initialization support, unlock the mutex;
150      * otherwize, do a naive wait loop. */
151 #if defined( PTH_INIT_IN_PTH_H )
152     while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP );
153 #elif defined( ST_INIT_IN_ST_H )
154     while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP );
155 #elif defined( WIN32 )
156     while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP );
157 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
158     pthread_mutex_unlock( &once_mutex );
159 #elif defined( HAVE_CTHREADS_H )
160     while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP );
161 #elif defined( HAVE_KERNEL_SCHEDULER_H )
162     while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP );
163 #endif
164
165     if( i_status != VLC_THREADS_READY )
166     {
167         return VLC_ETHREAD;
168     }
169
170     return i_ret;
171 }
172
173 /*****************************************************************************
174  * vlc_threads_end: stop threads system
175  *****************************************************************************
176  * FIXME: This function is far from being threadsafe. We should undo exactly
177  * what we did above in vlc_threads_init.
178  *****************************************************************************/
179 int __vlc_threads_end( vlc_object_t *p_this )
180 {
181 #if defined( PTH_INIT_IN_PTH_H )
182     i_initializations--;
183     if( i_initializations == 0 )
184     {
185         return pth_kill();
186     }
187
188 #elif defined( ST_INIT_IN_ST_H )
189     i_initializations--;
190
191 #elif defined( WIN32 )
192     i_initializations--;
193
194 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
195     pthread_mutex_lock( &once_mutex );
196     i_initializations--;
197     pthread_mutex_unlock( &once_mutex );
198
199 #elif defined( HAVE_CTHREADS_H )
200     i_initializations--;
201
202 #elif defined( HAVE_KERNEL_SCHEDULER_H )
203     i_initializations--;
204
205 #endif
206     return VLC_SUCCESS;
207 }
208
209 /*****************************************************************************
210  * Prototype for GPROF wrapper
211  *****************************************************************************/
212 #ifdef GPROF
213 /* Wrapper function for profiling */
214 static void *      vlc_thread_wrapper ( void *p_wrapper );
215
216 #   ifdef WIN32
217
218 #       define ITIMER_REAL 1
219 #       define ITIMER_PROF 2
220
221 struct itimerval
222 {
223     struct timeval it_value;
224     struct timeval it_interval;
225 };  
226
227 int setitimer(int kind, const struct itimerval* itnew, struct itimerval* itold);
228 #   endif /* WIN32 */
229
230 typedef struct wrapper_t
231 {
232     /* Data lock access */
233     vlc_mutex_t lock;
234     vlc_cond_t  wait;
235     
236     /* Data used to spawn the real thread */
237     vlc_thread_func_t func;
238     void *p_data;
239     
240     /* Profiling timer passed to the thread */
241     struct itimerval itimer;
242     
243 } wrapper_t;
244
245 #endif /* GPROF */
246
247 /*****************************************************************************
248  * vlc_mutex_init: initialize a mutex
249  *****************************************************************************/
250 int __vlc_mutex_init( vlc_object_t *p_this, vlc_mutex_t *p_mutex )
251 {
252     p_mutex->p_this = p_this;
253
254 #if defined( PTH_INIT_IN_PTH_H )
255     return pth_mutex_init( &p_mutex->mutex );
256
257 #elif defined( ST_INIT_IN_ST_H )
258     p_mutex->mutex = st_mutex_new();
259     return ( p_mutex->mutex == NULL ) ? errno : 0;
260
261 #elif defined( WIN32 )
262     /* We use mutexes on WinNT/2K/XP because we can use the SignalObjectAndWait
263      * function and have a 100% correct vlc_cond_wait() implementation.
264      * As this function is not available on Win9x, we can use the faster
265      * CriticalSections */
266     if( p_this->p_libvlc->SignalObjectAndWait &&
267         !p_this->p_libvlc->b_fast_mutex )
268     {
269         /* We are running on NT/2K/XP, we can use SignalObjectAndWait */
270         p_mutex->mutex = CreateMutex( 0, FALSE, 0 );
271         return ( p_mutex->mutex != NULL ? 0 : 1 );
272     }
273     else
274     {
275         p_mutex->mutex = NULL;
276         InitializeCriticalSection( &p_mutex->csection );
277         return 0;
278     }
279
280 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
281 #   if defined(DEBUG) && defined(SYS_LINUX)
282     {
283         /* Create error-checking mutex to detect problems more easily. */
284         pthread_mutexattr_t attr;
285         int                 i_result;
286
287         pthread_mutexattr_init( &attr );
288         pthread_mutexattr_setkind_np( &attr, PTHREAD_MUTEX_ERRORCHECK_NP );
289         i_result = pthread_mutex_init( &p_mutex->mutex, &attr );
290         pthread_mutexattr_destroy( &attr );
291         return( i_result );
292     }
293 #   endif
294     return pthread_mutex_init( &p_mutex->mutex, NULL );
295
296 #elif defined( HAVE_CTHREADS_H )
297     mutex_init( p_mutex );
298     return 0;
299
300 #elif defined( HAVE_KERNEL_SCHEDULER_H )
301     /* check the arguments and whether it's already been initialized */
302     if( p_mutex == NULL )
303     {
304         return B_BAD_VALUE;
305     }
306
307     if( p_mutex->init == 9999 )
308     {
309         return EALREADY;
310     }
311
312     p_mutex->lock = create_sem( 1, "BeMutex" );
313     if( p_mutex->lock < B_NO_ERROR )
314     {
315         return( -1 );
316     }
317
318     p_mutex->init = 9999;
319     return B_OK;
320
321 #endif
322 }
323
324 /*****************************************************************************
325  * vlc_mutex_destroy: destroy a mutex, inner version
326  *****************************************************************************/
327 int __vlc_mutex_destroy( char * psz_file, int i_line, vlc_mutex_t *p_mutex )
328 {
329     int i_result;
330     /* In case of error : */
331     int i_thread = -1;
332     const char * psz_error = "";
333
334 #if defined( PTH_INIT_IN_PTH_H )
335     return 0;
336
337 #elif defined( ST_INIT_IN_ST_H )
338     i_result = st_mutex_destroy( p_mutex->mutex );
339
340 #elif defined( WIN32 )
341     if( p_mutex->mutex )
342     {
343         CloseHandle( p_mutex->mutex );
344     }
345     else
346     {
347         DeleteCriticalSection( &p_mutex->csection );
348     }
349     return 0;
350
351 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )    
352     i_result = pthread_mutex_destroy( &p_mutex->mutex );
353     if ( i_result )
354     {
355         i_thread = (int)pthread_self();
356         psz_error = strerror(i_result);
357     }
358
359 #elif defined( HAVE_CTHREADS_H )
360     return 0;
361
362 #elif defined( HAVE_KERNEL_SCHEDULER_H )
363     if( p_mutex->init == 9999 )
364     {
365         delete_sem( p_mutex->lock );
366     }
367
368     p_mutex->init = 0;
369     return B_OK;
370 #endif    
371
372     if( i_result )
373     {
374         msg_Err( p_mutex->p_this,
375                  "thread %d: mutex_destroy failed at %s:%d (%d:%s)",
376                  i_thread, psz_file, i_line, i_result, psz_error );
377     }
378     return i_result;
379 }
380
381 /*****************************************************************************
382  * vlc_cond_init: initialize a condition
383  *****************************************************************************/
384 int __vlc_cond_init( vlc_object_t *p_this, vlc_cond_t *p_condvar )
385 {
386     p_condvar->p_this = p_this;
387
388 #if defined( PTH_INIT_IN_PTH_H )
389     return pth_cond_init( &p_condvar->cond );
390
391 #elif defined( ST_INIT_IN_ST_H )
392     p_condvar->cond = st_cond_new();
393     return ( p_condvar->cond == NULL ) ? errno : 0;
394
395 #elif defined( WIN32 )
396     /* Initialize counter */
397     p_condvar->i_waiting_threads = 0;
398
399     /* Misc init */
400     p_condvar->i_win9x_cv = p_this->p_libvlc->i_win9x_cv;
401     p_condvar->SignalObjectAndWait = p_this->p_libvlc->SignalObjectAndWait;
402
403     if( (p_condvar->SignalObjectAndWait && !p_this->p_libvlc->b_fast_mutex)
404         || p_condvar->i_win9x_cv == 0 )
405     {
406         /* Create an auto-reset event. */
407         p_condvar->event = CreateEvent( NULL,   /* no security */
408                                         FALSE,  /* auto-reset event */
409                                         FALSE,  /* start non-signaled */
410                                         NULL ); /* unnamed */
411
412         p_condvar->semaphore = NULL;
413         return !p_condvar->event;
414     }
415     else
416     {
417         p_condvar->semaphore = CreateSemaphore( NULL,       /* no security */
418                                                 0,          /* initial count */
419                                                 0x7fffffff, /* max count */
420                                                 NULL );     /* unnamed */
421
422         if( p_condvar->i_win9x_cv == 1 )
423             /* Create a manual-reset event initially signaled. */
424             p_condvar->event = CreateEvent( NULL, TRUE, TRUE, NULL );
425         else
426             /* Create a auto-reset event. */
427             p_condvar->event = CreateEvent( NULL, FALSE, FALSE, NULL );
428
429         InitializeCriticalSection( &p_condvar->csection );
430
431         return !p_condvar->semaphore || !p_condvar->event;
432     }
433
434 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
435     return pthread_cond_init( &p_condvar->cond, NULL );
436
437 #elif defined( HAVE_CTHREADS_H )
438     /* condition_init() */
439     spin_lock_init( &p_condvar->lock );
440     cthread_queue_init( &p_condvar->queue );
441     p_condvar->name = 0;
442     p_condvar->implications = 0;
443
444     return 0;
445
446 #elif defined( HAVE_KERNEL_SCHEDULER_H )
447     if( !p_condvar )
448     {
449         return B_BAD_VALUE;
450     }
451
452     if( p_condvar->init == 9999 )
453     {
454         return EALREADY;
455     }
456
457     p_condvar->thread = -1;
458     p_condvar->init = 9999;
459     return 0;
460 #endif
461 }
462
463 /*****************************************************************************
464  * vlc_cond_destroy: destroy a condition, inner version
465  *****************************************************************************/
466 int __vlc_cond_destroy( char * psz_file, int i_line, vlc_cond_t *p_condvar )
467 {
468     int i_result;
469     /* In case of error : */
470     int i_thread = -1;
471     const char * psz_error = "";
472
473 #if defined( PTH_INIT_IN_PTH_H )
474     return 0;
475
476 #elif defined( ST_INIT_IN_ST_H )
477     i_result = st_cond_destroy( p_condvar->cond );
478
479 #elif defined( WIN32 )
480     if( !p_condvar->semaphore )
481         i_result = !CloseHandle( p_condvar->event );
482     else
483         i_result = !CloseHandle( p_condvar->event )
484           || !CloseHandle( p_condvar->semaphore );
485
486 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
487     i_result = pthread_cond_destroy( &p_condvar->cond );
488     if ( i_result )
489     {
490         i_thread = (int)pthread_self();
491         psz_error = strerror(i_result);
492     }
493
494 #elif defined( HAVE_CTHREADS_H )
495     return 0;
496
497 #elif defined( HAVE_KERNEL_SCHEDULER_H )
498     p_condvar->init = 0;
499     return 0;
500 #endif
501
502     if( i_result )
503     {
504         msg_Err( p_condvar->p_this,
505                  "thread %d: cond_destroy failed at %s:%d (%d:%s)",
506                  i_thread, psz_file, i_line, i_result, psz_error );
507     }
508     return i_result;
509 }
510
511 /*****************************************************************************
512  * vlc_thread_create: create a thread, inner version
513  *****************************************************************************
514  * Note that i_priority is only taken into account on platforms supporting
515  * userland real-time priority threads.
516  *****************************************************************************/
517 int __vlc_thread_create( vlc_object_t *p_this, char * psz_file, int i_line,
518                          char *psz_name, void * ( *func ) ( void * ),
519                          int i_priority, vlc_bool_t b_wait )
520 {
521     int i_ret;
522
523     vlc_mutex_lock( &p_this->object_lock );
524
525 #ifdef GPROF
526     wrapper_t wrapper;
527
528     /* Initialize the wrapper structure */
529     wrapper.func = func;
530     wrapper.p_data = (void *)p_this;
531     getitimer( ITIMER_PROF, &wrapper.itimer );
532     vlc_mutex_init( p_this, &wrapper.lock );
533     vlc_cond_init( p_this, &wrapper.wait );
534     vlc_mutex_lock( &wrapper.lock );
535
536     /* Alter user-passed data so that we call the wrapper instead
537      * of the real function */
538     p_data = &wrapper;
539     func = vlc_thread_wrapper;
540 #endif
541
542 #if defined( PTH_INIT_IN_PTH_H )
543     p_this->thread_id = pth_spawn( PTH_ATTR_DEFAULT, func, (void *)p_this );
544     i_ret = 0;
545
546 #elif defined( ST_INIT_IN_ST_H )
547     p_this->thread_id = st_thread_create( func, (void *)p_this, 1, 0 );
548     i_ret = 0;
549     
550 #elif defined( WIN32 )
551     {
552         unsigned threadID;
553         /* When using the MSVCRT C library you have to use the _beginthreadex
554          * function instead of CreateThread, otherwise you'll end up with
555          * memory leaks and the signal functions not working */
556         p_this->thread_id =
557                 (HANDLE)_beginthreadex( NULL, 0, (PTHREAD_START) func, 
558                                         (void *)p_this, 0, &threadID );
559     }
560
561     if ( p_this->thread_id && i_priority )
562     {
563         if ( !SetThreadPriority(p_this->thread_id, i_priority) )
564         {
565             msg_Warn( p_this, "couldn't set a faster priority" );
566             i_priority = 0;
567         }
568     }
569
570     i_ret = ( p_this->thread_id ? 0 : 1 );
571
572 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
573     i_ret = pthread_create( &p_this->thread_id, NULL, func, (void *)p_this );
574
575     if ( i_priority )
576     {
577         struct sched_param param;
578         memset( &param, 0, sizeof(struct sched_param) );
579         param.sched_priority = i_priority;
580         if ( pthread_setschedparam( p_this->thread_id, SCHED_RR, &param ) )
581         {
582             msg_Warn( p_this, "couldn't go to real-time priority" );
583             i_priority = 0;
584         }
585     }
586
587 #elif defined( HAVE_CTHREADS_H )
588     p_this->thread_id = cthread_fork( (cthread_fn_t)func, (any_t)p_this );
589     i_ret = 0;
590
591 #elif defined( HAVE_KERNEL_SCHEDULER_H )
592     p_this->thread_id = spawn_thread( (thread_func)func, psz_name,
593                                       B_NORMAL_PRIORITY, (void *)p_this );
594     i_ret = resume_thread( p_this->thread_id );
595
596 #endif
597
598 #ifdef GPROF
599     if( i_ret == 0 )
600     {
601         vlc_cond_wait( &wrapper.wait, &wrapper.lock );
602     }
603
604     vlc_mutex_unlock( &wrapper.lock );
605     vlc_mutex_destroy( &wrapper.lock );
606     vlc_cond_destroy( &wrapper.wait );
607 #endif
608
609     if( i_ret == 0 )
610     {
611         if( b_wait )
612         {
613             msg_Dbg( p_this, "waiting for thread completion" );
614             vlc_cond_wait( &p_this->object_wait, &p_this->object_lock );
615         }
616
617         p_this->b_thread = 1;
618
619         msg_Dbg( p_this, "thread %d (%s) created at priority %d (%s:%d)",
620                  p_this->thread_id, psz_name, i_priority,
621                  psz_file, i_line );
622
623         vlc_mutex_unlock( &p_this->object_lock );
624     }
625     else
626     {
627         msg_Err( p_this, "%s thread could not be created at %s:%d (%s)",
628                          psz_name, psz_file, i_line, strerror(i_ret) );
629         vlc_mutex_unlock( &p_this->object_lock );
630     }
631
632     return i_ret;
633 }
634
635 /*****************************************************************************
636  * vlc_thread_ready: tell the parent thread we were successfully spawned
637  *****************************************************************************/
638 void __vlc_thread_ready( vlc_object_t *p_this )
639 {
640     vlc_mutex_lock( &p_this->object_lock );
641     vlc_cond_signal( &p_this->object_wait );
642     vlc_mutex_unlock( &p_this->object_lock );
643 }
644
645 /*****************************************************************************
646  * vlc_thread_join: wait until a thread exits, inner version
647  *****************************************************************************/
648 void __vlc_thread_join( vlc_object_t *p_this, char * psz_file, int i_line )
649 {
650     int i_ret = 0;
651
652 #if defined( PTH_INIT_IN_PTH_H )
653     i_ret = pth_join( p_this->thread_id, NULL );
654
655 #elif defined( ST_INIT_IN_ST_H )
656     i_ret = st_thread_join( p_this->thread_id, NULL );
657     
658 #elif defined( WIN32 )
659     WaitForSingleObject( p_this->thread_id, INFINITE );
660
661 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
662     i_ret = pthread_join( p_this->thread_id, NULL );
663
664 #elif defined( HAVE_CTHREADS_H )
665     cthread_join( p_this->thread_id );
666     i_ret = 1;
667
668 #elif defined( HAVE_KERNEL_SCHEDULER_H )
669     int32 exit_value;
670     wait_for_thread( p_this->thread_id, &exit_value );
671
672 #endif
673
674     if( i_ret )
675     {
676         msg_Err( p_this, "thread_join(%d) failed at %s:%d (%s)",
677                          p_this->thread_id, psz_file, i_line, strerror(i_ret) );
678     }
679     else
680     {
681         msg_Dbg( p_this, "thread %d joined (%s:%d)",
682                          p_this->thread_id, psz_file, i_line );
683     }
684
685     p_this->b_thread = 0;
686 }
687
688 /*****************************************************************************
689  * vlc_thread_wrapper: wrapper around thread functions used when profiling.
690  *****************************************************************************/
691 #ifdef GPROF
692 static void *vlc_thread_wrapper( void *p_wrapper )
693 {
694     /* Put user data in thread-local variables */
695     void *            p_data = ((wrapper_t*)p_wrapper)->p_data;
696     vlc_thread_func_t func   = ((wrapper_t*)p_wrapper)->func;
697
698     /* Set the profile timer value */
699     setitimer( ITIMER_PROF, &((wrapper_t*)p_wrapper)->itimer, NULL );
700
701     /* Tell the calling thread that we don't need its data anymore */
702     vlc_mutex_lock( &((wrapper_t*)p_wrapper)->lock );
703     vlc_cond_signal( &((wrapper_t*)p_wrapper)->wait );
704     vlc_mutex_unlock( &((wrapper_t*)p_wrapper)->lock );
705
706     /* Call the real function */
707     return func( p_data );
708 }
709 #endif