+#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
+ * run its cancellation cleanup handler, the thread variable destructors, and
+ * terminate. vlc_join() must be used afterward regardless of a thread being
+ * cancelled or not.
+ */
+void vlc_cancel (vlc_thread_t thread_id)
+{
+#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.
+ */
+int vlc_join (vlc_thread_t handle, void **result)
+{
+#if defined( LIBVLC_USE_PTHREAD )
+ return pthread_join (handle, result);
+
+#elif defined( UNDER_CE ) || defined( WIN32 )
+ do
+ vlc_testcancel ();
+ while (WaitForSingleObjectEx (handle->handle, INFINITE, TRUE)
+ == WAIT_IO_COMPLETION);
+
+ CloseHandle (handle->handle);
+ if (result)
+ *result = handle->data;
+ free (handle);
+ return 0;
+
+#elif defined( HAVE_KERNEL_SCHEDULER_H )
+ int32_t exit_value;
+ ret = (B_OK == wait_for_thread( p_priv->thread_id, &exit_value ));
+ if( !ret && result )
+ *result = (void *)exit_value;
+
+ return ret;
+#endif
+}
+
+
+struct vlc_thread_boot
+{
+ void * (*entry) (vlc_object_t *);
+ vlc_object_t *object;
+};
+
+static void *thread_entry (void *data)
+{
+ vlc_object_t *obj = ((struct vlc_thread_boot *)data)->object;
+ void *(*func) (vlc_object_t *) = ((struct vlc_thread_boot *)data)->entry;
+
+ free (data);
+#ifndef NDEBUG
+ vlc_threadvar_set (&thread_object_key, obj);
+#endif
+ msg_Dbg (obj, "thread started");
+ func (obj);
+ msg_Dbg (obj, "thread ended");
+
+ return NULL;
+}
+
+/*****************************************************************************
+ * vlc_thread_create: create a thread, inner version
+ *****************************************************************************
+ * Note that i_priority is only taken into account on platforms supporting
+ * userland real-time priority threads.
+ *****************************************************************************/
+int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line,
+ const char *psz_name, void * ( *func ) ( vlc_object_t * ),
+ int i_priority, bool b_wait )
+{
+ int i_ret;
+ vlc_object_internals_t *p_priv = vlc_internals( p_this );
+
+ struct vlc_thread_boot *boot = malloc (sizeof (*boot));
+ if (boot == NULL)
+ return errno;
+ boot->entry = func;
+ boot->object = p_this;
+
+ vlc_object_lock( p_this );
+
+ /* Make sure we don't re-create a thread if the object has already one */
+ assert( !p_priv->b_thread );
+
+#if defined( LIBVLC_USE_PTHREAD )
+#ifndef __APPLE__
+ if( config_GetInt( p_this, "rt-priority" ) > 0 )
+#endif
+ {
+ /* Hack to avoid error msg */
+ if( config_GetType( p_this, "rt-offset" ) )
+ i_priority += config_GetInt( p_this, "rt-offset" );
+ }