#include <vlc_common.h>
#include "libvlc.h"
+#include <stdarg.h>
#include <assert.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
(void)action; (void)error; (void)file; (void)line;
abort();
}
+
+static vlc_threadvar_t cancel_key;
#endif
/*****************************************************************************
vlc_threadvar_create( &thread_object_key, NULL );
#endif
vlc_threadvar_create( &msg_context_global_key, msg_StackDestroy );
+#ifndef LIBVLC_USE_PTHREAD
+ vlc_threadvar_create( &cancel_key, free );
+#endif
}
i_initializations++;
if( i_initializations == 1 )
{
vlc_object_release( p_root );
+#ifndef LIBVLC_USE_PTHREAD
+ vlc_threadvar_delete( &cancel_key );
+#endif
vlc_threadvar_delete( &msg_context_global_key );
#ifndef NDEBUG
vlc_threadvar_delete( &thread_object_key );
return ret;
}
+#if defined (WIN32)
+/* APC procedure for thread cancellation */
+static void CALLBACK vlc_cancel_self (ULONG_PTR dummy)
+{
+ (void)dummy;
+ vlc_control_cancel (VLC_DO_CANCEL);
+}
+#endif
+
/**
* Marks a thread as cancelled. Next time the target thread reaches a
* cancellation point (while not having disabled cancellation), it will
{
#if defined (LIBVLC_USE_PTHREAD)
pthread_cancel (thread_id);
+#elif defined (WIN32)
+ QueueUserAPC (vlc_cancel_self, thread_id->handle, 0);
#endif
}
/**
* Waits for a thread to complete (if needed), and destroys it.
+ * This is a cancellation point; in case of cancellation, the join does _not_
+ * occur.
+ *
* @param handle thread handle
* @param p_result [OUT] pointer to write the thread return value or NULL
* @return 0 on success, a standard error code otherwise.
return pthread_join (handle, result);
#elif defined( UNDER_CE ) || defined( WIN32 )
- WaitForSingleObject (handle->handle, INFINITE);
+ do
+ vlc_testcancel ();
+ while (WaitForSingleObjectEx (handle->handle, INFINITE, TRUE)
+ == WAIT_IO_COMPLETION);
+
CloseHandle (handle->handle);
if (result)
*result = handle->data;
if (priv->b_thread)
vlc_cancel (priv->thread_id);
}
+
+#ifndef LIBVLC_USE_PTHREAD
+typedef struct vlc_cancel_t
+{
+ vlc_cleanup_t *cleaners;
+ bool killable;
+ bool killed;
+} vlc_cancel_t;
+#endif
+
+void vlc_control_cancel (int cmd, ...)
+{
+ /* NOTE: This function only modifies thread-specific data, so there is no
+ * need to lock anything. */
+#ifdef LIBVLC_USE_PTHREAD
+ (void) cmd;
+ assert (0);
+#else
+ va_list ap;
+
+ va_start (ap, cmd);
+
+ vlc_cancel_t *nfo = vlc_threadvar_get (&cancel_key);
+ if (nfo == NULL)
+ {
+ nfo = malloc (sizeof (*nfo));
+ if (nfo == NULL)
+ abort ();
+ nfo->cleaners = NULL;
+ nfo->killed = false;
+ nfo->killable = true;
+ }
+
+ switch (cmd)
+ {
+ case VLC_SAVE_CANCEL:
+ {
+ int *p_state = va_arg (ap, int *);
+ *p_state = nfo->killable;
+ nfo->killable = false;
+ break;
+ }
+
+ case VLC_RESTORE_CANCEL:
+ {
+ int state = va_arg (ap, int);
+ nfo->killable = state != 0;
+ break;
+ }
+
+ case VLC_TEST_CANCEL:
+ if (nfo->killable && nfo->killed)
+ {
+ for (vlc_cleanup_t *p = nfo->cleaners; p != NULL; p = p->next)
+ p->proc (p->data);
+ free (nfo);
+#if defined (LIBVLC_USE_PTHREAD)
+ pthread_exit (PTHREAD_CANCELLED);
+#elif defined (WIN32)
+ _endthread ();
+#else
+# error Not implemented!
+#endif
+ }
+ break;
+
+ case VLC_DO_CANCEL:
+ nfo->killed = true;
+ break;
+
+ case VLC_CLEANUP_PUSH:
+ {
+ /* cleaner is a pointer to the caller stack, no need to allocate
+ * and copy anything. As a nice side effect, this cannot fail. */
+ vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
+ cleaner->next = nfo->cleaners;
+ nfo->cleaners = cleaner;
+ break;
+ }
+
+ case VLC_CLEANUP_POP:
+ {
+ nfo->cleaners = nfo->cleaners->next;
+ break;
+ }
+ }
+ va_end (ap);
+#endif
+}