]> git.sesse.net Git - vlc/blob - src/misc/threads.c
Move psz_vlcpath out of p_root
[vlc] / src / misc / threads.c
1 /*****************************************************************************
2  * threads.c : threads implementation for the VideoLAN client
3  *****************************************************************************
4  * Copyright (C) 1999-2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
9  *          Gildas Bazin <gbazin@netcourrier.com>
10  *          Clément Sténac
11  *          Rémi Denis-Courmont
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 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33
34 #include "libvlc.h"
35 #include <stdarg.h>
36 #include <assert.h>
37 #ifdef HAVE_UNISTD_H
38 # include <unistd.h>
39 #endif
40 #include <signal.h>
41
42 #define VLC_THREADS_UNINITIALIZED  0
43 #define VLC_THREADS_PENDING        1
44 #define VLC_THREADS_ERROR          2
45 #define VLC_THREADS_READY          3
46
47 /*****************************************************************************
48  * Global mutex for lazy initialization of the threads system
49  *****************************************************************************/
50 static volatile unsigned i_initializations = 0;
51
52 #if defined( LIBVLC_USE_PTHREAD )
53 # include <sched.h>
54
55 static pthread_mutex_t once_mutex = PTHREAD_MUTEX_INITIALIZER;
56 #else
57 static vlc_threadvar_t cancel_key;
58 #endif
59
60 /**
61  * Global process-wide VLC object.
62  * Contains the global named mutexes.
63  * TODO: remove it.
64  */
65 static vlc_object_t *p_root;
66
67 vlc_object_t *vlc_global( void )
68 {
69     assert( i_initializations > 0 );
70     return p_root;
71 }
72
73 #ifdef HAVE_EXECINFO_H
74 # include <execinfo.h>
75 #endif
76
77 /**
78  * Print a backtrace to the standard error for debugging purpose.
79  */
80 void vlc_trace (const char *fn, const char *file, unsigned line)
81 {
82      fprintf (stderr, "at %s:%u in %s\n", file, line, fn);
83      fflush (stderr); /* needed before switch to low-level I/O */
84 #ifdef HAVE_BACKTRACE
85      void *stack[20];
86      int len = backtrace (stack, sizeof (stack) / sizeof (stack[0]));
87      backtrace_symbols_fd (stack, len, 2);
88 #endif
89 #ifndef WIN32
90      fsync (2);
91 #endif
92 }
93
94 static inline unsigned long vlc_threadid (void)
95 {
96 #if defined(LIBVLC_USE_PTHREAD)
97      union { pthread_t th; unsigned long int i; } v = { };
98      v.th = pthread_self ();
99      return v.i;
100
101 #elif defined (WIN32)
102      return GetCurrentThreadId ();
103
104 #else
105      return 0;
106
107 #endif
108 }
109
110 /*****************************************************************************
111  * vlc_thread_fatal: Report an error from the threading layer
112  *****************************************************************************
113  * This is mostly meant for debugging.
114  *****************************************************************************/
115 static void
116 vlc_thread_fatal (const char *action, int error,
117                   const char *function, const char *file, unsigned line)
118 {
119     fprintf (stderr, "LibVLC fatal error %s (%d) in thread %lu ",
120              action, error, vlc_threadid ());
121     vlc_trace (function, file, line);
122
123     /* Sometimes strerror_r() crashes too, so make sure we print an error
124      * message before we invoke it */
125 #ifdef __GLIBC__
126     /* Avoid the strerror_r() prototype brain damage in glibc */
127     errno = error;
128     fprintf (stderr, " Error message: %m\n");
129 #elif !defined (WIN32)
130     char buf[1000];
131     const char *msg;
132
133     switch (strerror_r (error, buf, sizeof (buf)))
134     {
135         case 0:
136             msg = buf;
137             break;
138         case ERANGE: /* should never happen */
139             msg = "unknwon (too big to display)";
140             break;
141         default:
142             msg = "unknown (invalid error number)";
143             break;
144     }
145     fprintf (stderr, " Error message: %s\n", msg);
146 #endif
147     fflush (stderr);
148
149     abort ();
150 }
151
152 #ifndef NDEBUG
153 # define VLC_THREAD_ASSERT( action ) \
154     if (val) vlc_thread_fatal (action, val, __func__, __FILE__, __LINE__)
155 #else
156 # define VLC_THREAD_ASSERT( action ) ((void)val)
157 #endif
158
159 /**
160  * Per-thread cancellation data
161  */
162 #ifndef LIBVLC_USE_PTHREAD_CANCEL
163 typedef struct vlc_cancel_t
164 {
165     vlc_cleanup_t *cleaners;
166     bool           killable;
167     bool           killed;
168 } vlc_cancel_t;
169
170 # define VLC_CANCEL_INIT { NULL, true, false }
171 #endif
172
173 /*****************************************************************************
174  * vlc_threads_init: initialize threads system
175  *****************************************************************************
176  * This function requires lazy initialization of a global lock in order to
177  * keep the library really thread-safe. Some architectures don't support this
178  * and thus do not guarantee the complete reentrancy.
179  *****************************************************************************/
180 int vlc_threads_init( void )
181 {
182     int i_ret = VLC_SUCCESS;
183
184     /* If we have lazy mutex initialization, use it. Otherwise, we just
185      * hope nothing wrong happens. */
186 #if defined( LIBVLC_USE_PTHREAD )
187     pthread_mutex_lock( &once_mutex );
188 #endif
189
190     if( i_initializations == 0 )
191     {
192         p_root = vlc_custom_create( (vlc_object_t *)NULL, sizeof( *p_root ),
193                                     VLC_OBJECT_GENERIC, "root" );
194         if( p_root == NULL )
195         {
196             i_ret = VLC_ENOMEM;
197             goto out;
198         }
199
200         /* We should be safe now. Do all the initialization stuff we want. */
201 #ifndef LIBVLC_USE_PTHREAD_CANCEL
202         vlc_threadvar_create( &cancel_key, free );
203 #endif
204     }
205     i_initializations++;
206
207 out:
208     /* If we have lazy mutex initialization support, unlock the mutex.
209      * Otherwize, we are screwed. */
210 #if defined( LIBVLC_USE_PTHREAD )
211     pthread_mutex_unlock( &once_mutex );
212 #endif
213
214     return i_ret;
215 }
216
217 /*****************************************************************************
218  * vlc_threads_end: stop threads system
219  *****************************************************************************
220  * FIXME: This function is far from being threadsafe.
221  *****************************************************************************/
222 void vlc_threads_end( void )
223 {
224 #if defined( LIBVLC_USE_PTHREAD )
225     pthread_mutex_lock( &once_mutex );
226 #endif
227
228     assert( i_initializations > 0 );
229
230     if( i_initializations == 1 )
231     {
232         vlc_object_release( p_root );
233 #ifndef LIBVLC_USE_PTHREAD
234         vlc_threadvar_delete( &cancel_key );
235 #endif
236     }
237     i_initializations--;
238
239 #if defined( LIBVLC_USE_PTHREAD )
240     pthread_mutex_unlock( &once_mutex );
241 #endif
242 }
243
244 #if defined (__GLIBC__) && (__GLIBC_MINOR__ < 6)
245 /* This is not prototyped under glibc, though it exists. */
246 int pthread_mutexattr_setkind_np( pthread_mutexattr_t *attr, int kind );
247 #endif
248
249 /*****************************************************************************
250  * vlc_mutex_init: initialize a mutex
251  *****************************************************************************/
252 int vlc_mutex_init( vlc_mutex_t *p_mutex )
253 {
254 #if defined( LIBVLC_USE_PTHREAD )
255     pthread_mutexattr_t attr;
256     int                 i_result;
257
258     pthread_mutexattr_init( &attr );
259
260 # ifndef NDEBUG
261     /* Create error-checking mutex to detect problems more easily. */
262 #  if defined (__GLIBC__) && (__GLIBC_MINOR__ < 6)
263     pthread_mutexattr_setkind_np( &attr, PTHREAD_MUTEX_ERRORCHECK_NP );
264 #  else
265     pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK );
266 #  endif
267 # endif
268     i_result = pthread_mutex_init( p_mutex, &attr );
269     pthread_mutexattr_destroy( &attr );
270     return i_result;
271
272 #elif defined( WIN32 )
273     InitializeCriticalSection (&p_mutex->mutex);
274     p_mutex->recursive = false;
275     return 0;
276
277 #endif
278 }
279
280 /*****************************************************************************
281  * vlc_mutex_init: initialize a recursive mutex (Do not use)
282  *****************************************************************************/
283 int vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
284 {
285 #if defined( LIBVLC_USE_PTHREAD )
286     pthread_mutexattr_t attr;
287     int                 i_result;
288
289     pthread_mutexattr_init( &attr );
290 #  if defined (__GLIBC__) && (__GLIBC_MINOR__ < 6)
291     pthread_mutexattr_setkind_np( &attr, PTHREAD_MUTEX_RECURSIVE_NP );
292 #  else
293     pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
294 #  endif
295     i_result = pthread_mutex_init( p_mutex, &attr );
296     pthread_mutexattr_destroy( &attr );
297     return( i_result );
298
299 #elif defined( WIN32 )
300     InitializeCriticalSection( &p_mutex->mutex );
301     p_mutex->owner = 0; /* the error value for GetThreadId()! */
302     p_mutex->recursion = 0;
303     p_mutex->recursive = true;
304     return 0;
305
306 #endif
307 }
308
309
310 /**
311  * Destroys a mutex. The mutex must not be locked.
312  *
313  * @param p_mutex mutex to destroy
314  * @return always succeeds
315  */
316 void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
317 {
318 #if defined( LIBVLC_USE_PTHREAD )
319     int val = pthread_mutex_destroy( p_mutex );
320     VLC_THREAD_ASSERT ("destroying mutex");
321
322 #elif defined( WIN32 )
323     DeleteCriticalSection (&p_mutex->mutex);
324
325 #endif
326 }
327
328 /**
329  * Acquires a mutex. If needed, waits for any other thread to release it.
330  * Beware of deadlocks when locking multiple mutexes at the same time,
331  * or when using mutexes from callbacks.
332  *
333  * @param p_mutex mutex initialized with vlc_mutex_init() or
334  *                vlc_mutex_init_recursive()
335  */
336 void vlc_mutex_lock (vlc_mutex_t *p_mutex)
337 {
338 #if defined(LIBVLC_USE_PTHREAD)
339     int val = pthread_mutex_lock( p_mutex );
340     VLC_THREAD_ASSERT ("locking mutex");
341
342 #elif defined( WIN32 )
343     if (p_mutex->recursive)
344     {
345         DWORD self = GetCurrentThreadId ();
346
347         if ((DWORD)InterlockedCompareExchange (&p_mutex->owner, self,
348                                                self) == self)
349         {   /* We already locked this recursive mutex */
350             p_mutex->recursion++;
351             return;
352         }
353
354         /* We need to lock this recursive mutex */
355         EnterCriticalSection (&p_mutex->mutex);
356         self = InterlockedExchange (&p_mutex->owner, self);
357         assert (self == 0); /* no previous owner */
358         return;
359     }
360     /* Non-recursive mutex */
361     EnterCriticalSection (&p_mutex->mutex);
362
363 #endif
364 }
365
366
367 /**
368  * Releases a mutex (or crashes if the mutex is not locked by the caller).
369  * @param p_mutex mutex locked with vlc_mutex_lock().
370  */
371 void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
372 {
373 #if defined(LIBVLC_USE_PTHREAD)
374     int val = pthread_mutex_unlock( p_mutex );
375     VLC_THREAD_ASSERT ("unlocking mutex");
376
377 #elif defined( WIN32 )
378     if (p_mutex->recursive)
379     {
380         if (p_mutex->recursion != 0)
381         {
382             p_mutex->recursion--;
383             return; /* We still own this mutex */
384         }
385
386         /* We release the mutex */
387         InterlockedExchange (&p_mutex->owner, 0);
388         /* fall through */
389     }
390     LeaveCriticalSection (&p_mutex->mutex);
391
392 #endif
393 }
394
395 /*****************************************************************************
396  * vlc_cond_init: initialize a condition variable
397  *****************************************************************************/
398 int vlc_cond_init( vlc_cond_t *p_condvar )
399 {
400 #if defined( LIBVLC_USE_PTHREAD )
401     pthread_condattr_t attr;
402     int ret;
403
404     ret = pthread_condattr_init (&attr);
405     if (ret)
406         return ret;
407
408 # if !defined (_POSIX_CLOCK_SELECTION)
409    /* Fairly outdated POSIX support (that was defined in 2001) */
410 #  define _POSIX_CLOCK_SELECTION (-1)
411 # endif
412 # if (_POSIX_CLOCK_SELECTION >= 0)
413     /* NOTE: This must be the same clock as the one in mtime.c */
414     pthread_condattr_setclock (&attr, CLOCK_MONOTONIC);
415 # endif
416
417     ret = pthread_cond_init (p_condvar, &attr);
418     pthread_condattr_destroy (&attr);
419     return ret;
420
421 #elif defined( WIN32 )
422     /* Create a manual-reset event (manual reset is needed for broadcast). */
423     *p_condvar = CreateEvent( NULL, TRUE, FALSE, NULL );
424     return *p_condvar ? 0 : ENOMEM;
425
426 #endif
427 }
428
429 /**
430  * Destroys a condition variable. No threads shall be waiting or signaling the
431  * condition.
432  * @param p_condvar condition variable to destroy
433  */
434 void vlc_cond_destroy (vlc_cond_t *p_condvar)
435 {
436 #if defined( LIBVLC_USE_PTHREAD )
437     int val = pthread_cond_destroy( p_condvar );
438     VLC_THREAD_ASSERT ("destroying condition");
439
440 #elif defined( WIN32 )
441     CloseHandle( *p_condvar );
442
443 #endif
444 }
445
446 /**
447  * Wakes up one thread waiting on a condition variable, if any.
448  * @param p_condvar condition variable
449  */
450 void vlc_cond_signal (vlc_cond_t *p_condvar)
451 {
452 #if defined(LIBVLC_USE_PTHREAD)
453     int val = pthread_cond_signal( p_condvar );
454     VLC_THREAD_ASSERT ("signaling condition variable");
455
456 #elif defined( WIN32 )
457     /* NOTE: This will cause a broadcast, that is wrong.
458      * This will also wake up the next waiting thread if no thread are yet
459      * waiting, which is also wrong. However both of these issues are allowed
460      * by the provision for spurious wakeups. Better have too many wakeups
461      * than too few (= deadlocks). */
462     SetEvent (*p_condvar);
463
464 #endif
465 }
466
467 /**
468  * Wakes up all threads (if any) waiting on a condition variable.
469  * @param p_cond condition variable
470  */
471 void vlc_cond_broadcast (vlc_cond_t *p_condvar)
472 {
473 #if defined (LIBVLC_USE_PTHREAD)
474     pthread_cond_broadcast (p_condvar);
475
476 #elif defined (WIN32)
477     SetEvent (*p_condvar);
478
479 #endif
480 }
481
482 #ifdef UNDER_CE
483 # warning FIXME
484 # define WaitForMultipleObjectsEx(a,b,c) WaitForMultipleObjects(a,b)
485 #endif
486
487 /**
488  * Waits for a condition variable. The calling thread will be suspended until
489  * another thread calls vlc_cond_signal() or vlc_cond_broadcast() on the same
490  * condition variable, the thread is cancelled with vlc_cancel(), or the
491  * system causes a "spurious" unsolicited wake-up.
492  *
493  * A mutex is needed to wait on a condition variable. It must <b>not</b> be
494  * a recursive mutex. Although it is possible to use the same mutex for
495  * multiple condition, it is not valid to use different mutexes for the same
496  * condition variable at the same time from different threads.
497  *
498  * In case of thread cancellation, the mutex is always locked before
499  * cancellation proceeds.
500  *
501  * The canonical way to use a condition variable to wait for event foobar is:
502  @code
503    vlc_mutex_lock (&lock);
504    mutex_cleanup_push (&lock); // release the mutex in case of cancellation
505
506    while (!foobar)
507        vlc_cond_wait (&wait, &lock);
508
509    --- foobar is now true, do something about it here --
510
511    vlc_cleanup_run (); // release the mutex
512   @endcode
513  *
514  * @param p_condvar condition variable to wait on
515  * @param p_mutex mutex which is unlocked while waiting,
516  *                then locked again when waking up.
517  * @param deadline <b>absolute</b> timeout
518  *
519  * @return 0 if the condition was signaled, an error code in case of timeout.
520  */
521 void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
522 {
523 #if defined(LIBVLC_USE_PTHREAD)
524     int val = pthread_cond_wait( p_condvar, p_mutex );
525     VLC_THREAD_ASSERT ("waiting on condition");
526
527 #elif defined( WIN32 )
528     DWORD result;
529
530     assert (!p_mutex->recursive);
531     do
532     {
533         vlc_testcancel ();
534         LeaveCriticalSection (&p_mutex->mutex);
535         result = WaitForSingleObjectEx (*p_condvar, INFINITE, TRUE);
536         EnterCriticalSection (&p_mutex->mutex);
537     }
538     while (result == WAIT_IO_COMPLETION);
539
540     ResetEvent (*p_condvar);
541
542 #endif
543 }
544
545 /**
546  * Waits for a condition variable up to a certain date.
547  * This works like vlc_cond_wait(), except for the additional timeout.
548  *
549  * @param p_condvar condition variable to wait on
550  * @param p_mutex mutex which is unlocked while waiting,
551  *                then locked again when waking up.
552  * @param deadline <b>absolute</b> timeout
553  *
554  * @return 0 if the condition was signaled, an error code in case of timeout.
555  */
556 int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
557                         mtime_t deadline)
558 {
559 #if defined(LIBVLC_USE_PTHREAD)
560     lldiv_t d = lldiv( deadline, CLOCK_FREQ );
561     struct timespec ts = { d.quot, d.rem * (1000000000 / CLOCK_FREQ) };
562
563     int val = pthread_cond_timedwait (p_condvar, p_mutex, &ts);
564     if (val != ETIMEDOUT)
565         VLC_THREAD_ASSERT ("timed-waiting on condition");
566     return val;
567
568 #elif defined( WIN32 )
569     DWORD result;
570
571     assert (!p_mutex->recursive);
572     do
573     {
574         vlc_testcancel ();
575
576         mtime_t total = (deadline - mdate ())/1000;
577         if( total < 0 )
578             total = 0;
579
580         DWORD delay = (total > 0x7fffffff) ? 0x7fffffff : total;
581         LeaveCriticalSection (&p_mutex->mutex);
582         result = WaitForSingleObjectEx (*p_condvar, delay, TRUE);
583         EnterCriticalSection (&p_mutex->mutex);
584     }
585     while (result == WAIT_IO_COMPLETION);
586
587     ResetEvent (*p_condvar);
588
589     return (result == WAIT_OBJECT_0) ? 0 : ETIMEDOUT;
590
591 #endif
592 }
593
594 /*****************************************************************************
595  * vlc_tls_create: create a thread-local variable
596  *****************************************************************************/
597 int vlc_threadvar_create( vlc_threadvar_t *p_tls, void (*destr) (void *) )
598 {
599     int i_ret;
600
601 #if defined( LIBVLC_USE_PTHREAD )
602     i_ret =  pthread_key_create( p_tls, destr );
603 #elif defined( UNDER_CE )
604     i_ret = ENOSYS;
605 #elif defined( WIN32 )
606     /* FIXME: remember/use the destr() callback and stop leaking whatever */
607     *p_tls = TlsAlloc();
608     i_ret = (*p_tls == TLS_OUT_OF_INDEXES) ? EAGAIN : 0;
609 #else
610 # error Unimplemented!
611 #endif
612     return i_ret;
613 }
614
615 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
616 {
617 #if defined( LIBVLC_USE_PTHREAD )
618     pthread_key_delete (*p_tls);
619 #elif defined( UNDER_CE )
620 #elif defined( WIN32 )
621     TlsFree (*p_tls);
622 #else
623 # error Unimplemented!
624 #endif
625 }
626
627 #if defined (LIBVLC_USE_PTHREAD)
628 #elif defined (WIN32)
629 static unsigned __stdcall vlc_entry (void *data)
630 {
631     vlc_cancel_t cancel_data = VLC_CANCEL_INIT;
632     vlc_thread_t self = data;
633
634     vlc_threadvar_set (&cancel_key, &cancel_data);
635     self->data = self->entry (self->data);
636     return 0;
637 }
638 #endif
639
640 /**
641  * Creates and starts new thread.
642  *
643  * @param p_handle [OUT] pointer to write the handle of the created thread to
644  * @param entry entry point for the thread
645  * @param data data parameter given to the entry point
646  * @param priority thread priority value
647  * @return 0 on success, a standard error code on error.
648  */
649 int vlc_clone (vlc_thread_t *p_handle, void * (*entry) (void *), void *data,
650                int priority)
651 {
652     int ret;
653
654 #if defined( LIBVLC_USE_PTHREAD )
655     pthread_attr_t attr;
656     pthread_attr_init (&attr);
657
658     /* Block the signals that signals interface plugin handles.
659      * If the LibVLC caller wants to handle some signals by itself, it should
660      * block these before whenever invoking LibVLC. And it must obviously not
661      * start the VLC signals interface plugin.
662      *
663      * LibVLC will normally ignore any interruption caused by an asynchronous
664      * signal during a system call. But there may well be some buggy cases
665      * where it fails to handle EINTR (bug reports welcome). Some underlying
666      * libraries might also not handle EINTR properly.
667      */
668     sigset_t oldset;
669     {
670         sigset_t set;
671         sigemptyset (&set);
672         sigdelset (&set, SIGHUP);
673         sigaddset (&set, SIGINT);
674         sigaddset (&set, SIGQUIT);
675         sigaddset (&set, SIGTERM);
676
677         sigaddset (&set, SIGPIPE); /* We don't want this one, really! */
678         pthread_sigmask (SIG_BLOCK, &set, &oldset);
679     }
680     {
681         struct sched_param sp = { .sched_priority = priority, };
682         int policy;
683
684         if (sp.sched_priority <= 0)
685             sp.sched_priority += sched_get_priority_max (policy = SCHED_OTHER);
686         else
687             sp.sched_priority += sched_get_priority_min (policy = SCHED_RR);
688
689         pthread_attr_setschedpolicy (&attr, policy);
690         pthread_attr_setschedparam (&attr, &sp);
691     }
692
693     /* The thread stack size.
694      * The lower the value, the less address space per thread, the highest
695      * maximum simultaneous threads per process. Too low values will cause
696      * stack overflows and weird crashes. Set with caution. Also keep in mind
697      * that 64-bits platforms consume more stack than 32-bits one.
698      *
699      * Thanks to on-demand paging, thread stack size only affects address space
700      * consumption. In terms of memory, threads only use what they need
701      * (rounded up to the page boundary).
702      *
703      * For example, on Linux i386, the default is 2 mega-bytes, which supports
704      * about 320 threads per processes. */
705 #define VLC_STACKSIZE (128 * sizeof (void *) * 1024)
706
707 #ifdef VLC_STACKSIZE
708     ret = pthread_attr_setstacksize (&attr, VLC_STACKSIZE);
709     assert (ret == 0); /* fails iif VLC_STACKSIZE is invalid */
710 #endif
711
712     ret = pthread_create (p_handle, &attr, entry, data);
713     pthread_sigmask (SIG_SETMASK, &oldset, NULL);
714     pthread_attr_destroy (&attr);
715
716 #elif defined( WIN32 ) || defined( UNDER_CE )
717     /* When using the MSVCRT C library you have to use the _beginthreadex
718      * function instead of CreateThread, otherwise you'll end up with
719      * memory leaks and the signal functions not working (see Microsoft
720      * Knowledge Base, article 104641) */
721     HANDLE hThread;
722     vlc_thread_t th = malloc (sizeof (*th));
723
724     if (th == NULL)
725         return ENOMEM;
726
727     th->data = data;
728     th->entry = entry;
729 #if defined( UNDER_CE )
730     hThread = CreateThread (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL);
731 #else
732     hThread = (HANDLE)(uintptr_t)
733         _beginthreadex (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL);
734 #endif
735
736     if (hThread)
737     {
738         /* Thread closes the handle when exiting, duplicate it here
739          * to be on the safe side when joining. */
740         if (!DuplicateHandle (GetCurrentProcess (), hThread,
741                               GetCurrentProcess (), &th->handle, 0, FALSE,
742                               DUPLICATE_SAME_ACCESS))
743         {
744             CloseHandle (hThread);
745             free (th);
746             return ENOMEM;
747         }
748
749         ResumeThread (hThread);
750         if (priority)
751             SetThreadPriority (hThread, priority);
752
753         ret = 0;
754         *p_handle = th;
755     }
756     else
757     {
758         ret = errno;
759         free (th);
760     }
761
762 #endif
763     return ret;
764 }
765
766 #if defined (WIN32)
767 /* APC procedure for thread cancellation */
768 static void CALLBACK vlc_cancel_self (ULONG_PTR dummy)
769 {
770     (void)dummy;
771     vlc_control_cancel (VLC_DO_CANCEL);
772 }
773 #endif
774
775 /**
776  * Marks a thread as cancelled. Next time the target thread reaches a
777  * cancellation point (while not having disabled cancellation), it will
778  * run its cancellation cleanup handler, the thread variable destructors, and
779  * terminate. vlc_join() must be used afterward regardless of a thread being
780  * cancelled or not.
781  */
782 void vlc_cancel (vlc_thread_t thread_id)
783 {
784 #if defined (LIBVLC_USE_PTHREAD_CANCEL)
785     pthread_cancel (thread_id);
786 #elif defined (WIN32)
787     QueueUserAPC (vlc_cancel_self, thread_id->handle, 0);
788 #else
789 #   warning vlc_cancel is not implemented!
790 #endif
791 }
792
793 /**
794  * Waits for a thread to complete (if needed), and destroys it.
795  * This is a cancellation point; in case of cancellation, the join does _not_
796  * occur.
797  *
798  * @param handle thread handle
799  * @param p_result [OUT] pointer to write the thread return value or NULL
800  * @return 0 on success, a standard error code otherwise.
801  */
802 void vlc_join (vlc_thread_t handle, void **result)
803 {
804 #if defined( LIBVLC_USE_PTHREAD )
805     int val = pthread_join (handle, result);
806     if (val)
807         vlc_thread_fatal ("joining thread", val, __func__, __FILE__, __LINE__);
808
809 #elif defined( UNDER_CE ) || defined( WIN32 )
810     do
811         vlc_testcancel ();
812     while (WaitForSingleObjectEx (handle->handle, INFINITE, TRUE)
813                                                         == WAIT_IO_COMPLETION);
814
815     CloseHandle (handle->handle);
816     if (result)
817         *result = handle->data;
818     free (handle);
819
820 #endif
821 }
822
823
824 struct vlc_thread_boot
825 {
826     void * (*entry) (vlc_object_t *);
827     vlc_object_t *object;
828 };
829
830 static void *thread_entry (void *data)
831 {
832     vlc_object_t *obj = ((struct vlc_thread_boot *)data)->object;
833     void *(*func) (vlc_object_t *) = ((struct vlc_thread_boot *)data)->entry;
834
835     free (data);
836     msg_Dbg (obj, "thread started");
837     func (obj);
838     msg_Dbg (obj, "thread ended");
839
840     return NULL;
841 }
842
843 /*****************************************************************************
844  * vlc_thread_create: create a thread, inner version
845  *****************************************************************************
846  * Note that i_priority is only taken into account on platforms supporting
847  * userland real-time priority threads.
848  *****************************************************************************/
849 int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line,
850                          const char *psz_name, void * ( *func ) ( vlc_object_t * ),
851                          int i_priority, bool b_wait )
852 {
853     int i_ret;
854     vlc_object_internals_t *p_priv = vlc_internals( p_this );
855
856     struct vlc_thread_boot *boot = malloc (sizeof (*boot));
857     if (boot == NULL)
858         return errno;
859     boot->entry = func;
860     boot->object = p_this;
861
862     vlc_object_lock( p_this );
863
864     /* Make sure we don't re-create a thread if the object has already one */
865     assert( !p_priv->b_thread );
866
867 #if defined( LIBVLC_USE_PTHREAD )
868 #ifndef __APPLE__
869     if( config_GetInt( p_this, "rt-priority" ) > 0 )
870 #endif
871     {
872         /* Hack to avoid error msg */
873         if( config_GetType( p_this, "rt-offset" ) )
874             i_priority += config_GetInt( p_this, "rt-offset" );
875     }
876 #endif
877
878     i_ret = vlc_clone( &p_priv->thread_id, thread_entry, boot, i_priority );
879     if( i_ret == 0 )
880     {
881         if( b_wait )
882         {
883             msg_Dbg( p_this, "waiting for thread initialization" );
884             vlc_object_wait( p_this );
885         }
886
887         p_priv->b_thread = true;
888         msg_Dbg( p_this, "thread %lu (%s) created at priority %d (%s:%d)",
889                  (unsigned long)p_priv->thread_id, psz_name, i_priority,
890                  psz_file, i_line );
891     }
892     else
893     {
894         errno = i_ret;
895         msg_Err( p_this, "%s thread could not be created at %s:%d (%m)",
896                          psz_name, psz_file, i_line );
897     }
898
899     vlc_object_unlock( p_this );
900     return i_ret;
901 }
902
903 /*****************************************************************************
904  * vlc_thread_set_priority: set the priority of the current thread when we
905  * couldn't set it in vlc_thread_create (for instance for the main thread)
906  *****************************************************************************/
907 int __vlc_thread_set_priority( vlc_object_t *p_this, const char * psz_file,
908                                int i_line, int i_priority )
909 {
910     vlc_object_internals_t *p_priv = vlc_internals( p_this );
911
912     if( !p_priv->b_thread )
913     {
914         msg_Err( p_this, "couldn't set priority of non-existent thread" );
915         return ESRCH;
916     }
917
918 #if defined( LIBVLC_USE_PTHREAD )
919 # ifndef __APPLE__
920     if( config_GetInt( p_this, "rt-priority" ) > 0 )
921 # endif
922     {
923         int i_error, i_policy;
924         struct sched_param param;
925
926         memset( &param, 0, sizeof(struct sched_param) );
927         if( config_GetType( p_this, "rt-offset" ) )
928             i_priority += config_GetInt( p_this, "rt-offset" );
929         if( i_priority <= 0 )
930         {
931             param.sched_priority = (-1) * i_priority;
932             i_policy = SCHED_OTHER;
933         }
934         else
935         {
936             param.sched_priority = i_priority;
937             i_policy = SCHED_RR;
938         }
939         if( (i_error = pthread_setschedparam( p_priv->thread_id,
940                                               i_policy, &param )) )
941         {
942             errno = i_error;
943             msg_Warn( p_this, "couldn't set thread priority (%s:%d): %m",
944                       psz_file, i_line );
945             i_priority = 0;
946         }
947     }
948
949 #elif defined( WIN32 ) || defined( UNDER_CE )
950     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
951
952     if( !SetThreadPriority(p_priv->thread_id->handle, i_priority) )
953     {
954         msg_Warn( p_this, "couldn't set a faster priority" );
955         return 1;
956     }
957
958 #endif
959
960     return 0;
961 }
962
963 /*****************************************************************************
964  * vlc_thread_join: wait until a thread exits, inner version
965  *****************************************************************************/
966 void __vlc_thread_join( vlc_object_t *p_this )
967 {
968     vlc_object_internals_t *p_priv = vlc_internals( p_this );
969
970 #if defined( LIBVLC_USE_PTHREAD )
971     vlc_join (p_priv->thread_id, NULL);
972
973 #elif defined( UNDER_CE ) || defined( WIN32 )
974     HANDLE hThread;
975     FILETIME create_ft, exit_ft, kernel_ft, user_ft;
976     int64_t real_time, kernel_time, user_time;
977
978     if( ! DuplicateHandle(GetCurrentProcess(),
979             p_priv->thread_id->handle,
980             GetCurrentProcess(),
981             &hThread,
982             0,
983             FALSE,
984             DUPLICATE_SAME_ACCESS) )
985     {
986         p_priv->b_thread = false;
987         return; /* We have a problem! */
988     }
989
990     vlc_join( p_priv->thread_id, NULL );
991
992     if( GetThreadTimes( hThread, &create_ft, &exit_ft, &kernel_ft, &user_ft ) )
993     {
994         real_time =
995           ((((int64_t)exit_ft.dwHighDateTime)<<32)| exit_ft.dwLowDateTime) -
996           ((((int64_t)create_ft.dwHighDateTime)<<32)| create_ft.dwLowDateTime);
997         real_time /= 10;
998
999         kernel_time =
1000           ((((int64_t)kernel_ft.dwHighDateTime)<<32)|
1001            kernel_ft.dwLowDateTime) / 10;
1002
1003         user_time =
1004           ((((int64_t)user_ft.dwHighDateTime)<<32)|
1005            user_ft.dwLowDateTime) / 10;
1006
1007         msg_Dbg( p_this, "thread times: "
1008                  "real %"PRId64"m%fs, kernel %"PRId64"m%fs, user %"PRId64"m%fs",
1009                  real_time/60/1000000,
1010                  (double)((real_time%(60*1000000))/1000000.0),
1011                  kernel_time/60/1000000,
1012                  (double)((kernel_time%(60*1000000))/1000000.0),
1013                  user_time/60/1000000,
1014                  (double)((user_time%(60*1000000))/1000000.0) );
1015     }
1016     CloseHandle( hThread );
1017
1018 #else
1019     vlc_join( p_priv->thread_id, NULL );
1020
1021 #endif
1022
1023     p_priv->b_thread = false;
1024 }
1025
1026 void vlc_thread_cancel (vlc_object_t *obj)
1027 {
1028     vlc_object_internals_t *priv = vlc_internals (obj);
1029
1030     if (priv->b_thread)
1031         vlc_cancel (priv->thread_id);
1032 }
1033
1034 void vlc_control_cancel (int cmd, ...)
1035 {
1036     /* NOTE: This function only modifies thread-specific data, so there is no
1037      * need to lock anything. */
1038 #ifdef LIBVLC_USE_PTHREAD_CANCEL
1039     (void) cmd;
1040     assert (0);
1041 #else
1042     va_list ap;
1043
1044     vlc_cancel_t *nfo = vlc_threadvar_get (&cancel_key);
1045     if (nfo == NULL)
1046     {
1047 #ifdef WIN32
1048         /* Main thread - cannot be cancelled anyway */
1049         return;
1050 #else
1051         nfo = malloc (sizeof (*nfo));
1052         if (nfo == NULL)
1053             return; /* Uho! Expect problems! */
1054         *nfo = VLC_CANCEL_INIT;
1055         vlc_threadvar_set (&cancel_key, nfo);
1056 #endif
1057     }
1058
1059     va_start (ap, cmd);
1060     switch (cmd)
1061     {
1062         case VLC_SAVE_CANCEL:
1063         {
1064             int *p_state = va_arg (ap, int *);
1065             *p_state = nfo->killable;
1066             nfo->killable = false;
1067             break;
1068         }
1069
1070         case VLC_RESTORE_CANCEL:
1071         {
1072             int state = va_arg (ap, int);
1073             nfo->killable = state != 0;
1074             break;
1075         }
1076
1077         case VLC_TEST_CANCEL:
1078             if (nfo->killable && nfo->killed)
1079             {
1080                 for (vlc_cleanup_t *p = nfo->cleaners; p != NULL; p = p->next)
1081                      p->proc (p->data);
1082 #ifndef WIN32
1083                 free (nfo);
1084 #endif
1085 #if defined (LIBVLC_USE_PTHREAD)
1086                 pthread_exit (PTHREAD_CANCELLED);
1087 #elif defined (WIN32)
1088                 _endthread ();
1089 #else
1090 # error Not implemented!
1091 #endif
1092             }
1093             break;
1094
1095         case VLC_DO_CANCEL:
1096             nfo->killed = true;
1097             break;
1098
1099         case VLC_CLEANUP_PUSH:
1100         {
1101             /* cleaner is a pointer to the caller stack, no need to allocate
1102              * and copy anything. As a nice side effect, this cannot fail. */
1103             vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
1104             cleaner->next = nfo->cleaners;
1105             nfo->cleaners = cleaner;
1106             break;
1107         }
1108
1109         case VLC_CLEANUP_POP:
1110         {
1111             nfo->cleaners = nfo->cleaners->next;
1112             break;
1113         }
1114     }
1115     va_end (ap);
1116 #endif
1117 }