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