summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/thread/thread.c
diff options
context:
space:
mode:
Diffstat (limited to 'winpr/libwinpr/thread/thread.c')
-rw-r--r--winpr/libwinpr/thread/thread.c1045
1 files changed, 1045 insertions, 0 deletions
diff --git a/winpr/libwinpr/thread/thread.c b/winpr/libwinpr/thread/thread.c
new file mode 100644
index 0000000..edf5639
--- /dev/null
+++ b/winpr/libwinpr/thread/thread.c
@@ -0,0 +1,1045 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Process Thread Functions
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2015 Hewlett-Packard Development Company, L.P.
+ * Copyright 2021 David Fort <contact@hardening-consulting.com>
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/assert.h>
+
+#include <winpr/handle.h>
+
+#include <winpr/thread.h>
+
+/**
+ * api-ms-win-core-processthreads-l1-1-1.dll
+ *
+ * CreateRemoteThread
+ * CreateRemoteThreadEx
+ * CreateThread
+ * DeleteProcThreadAttributeList
+ * ExitThread
+ * FlushInstructionCache
+ * FlushProcessWriteBuffers
+ * GetCurrentThread
+ * GetCurrentThreadId
+ * GetCurrentThreadStackLimits
+ * GetExitCodeThread
+ * GetPriorityClass
+ * GetStartupInfoW
+ * GetThreadContext
+ * GetThreadId
+ * GetThreadIdealProcessorEx
+ * GetThreadPriority
+ * GetThreadPriorityBoost
+ * GetThreadTimes
+ * InitializeProcThreadAttributeList
+ * OpenThread
+ * OpenThreadToken
+ * QueryProcessAffinityUpdateMode
+ * QueueUserAPC
+ * ResumeThread
+ * SetPriorityClass
+ * SetThreadContext
+ * SetThreadPriority
+ * SetThreadPriorityBoost
+ * SetThreadStackGuarantee
+ * SetThreadToken
+ * SuspendThread
+ * SwitchToThread
+ * TerminateThread
+ * UpdateProcThreadAttribute
+ */
+
+#ifndef _WIN32
+
+#include <winpr/crt.h>
+#include <winpr/platform.h>
+
+#ifdef WINPR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef WINPR_HAVE_SYS_EVENTFD_H
+#include <sys/eventfd.h>
+#endif
+
+#include <winpr/debug.h>
+
+#include <errno.h>
+#include <fcntl.h>
+
+#include <winpr/collections.h>
+
+#include "thread.h"
+#include "apc.h"
+
+#include "../handle/handle.h"
+#include "../log.h"
+#define TAG WINPR_TAG("thread")
+
+static WINPR_THREAD mainThread;
+
+#if defined(WITH_THREAD_LIST)
+static wListDictionary* thread_list = NULL;
+#endif
+
+static BOOL ThreadCloseHandle(HANDLE handle);
+static void cleanup_handle(void* obj);
+
+static BOOL ThreadIsHandled(HANDLE handle)
+{
+ return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_THREAD, FALSE);
+}
+
+static int ThreadGetFd(HANDLE handle)
+{
+ WINPR_THREAD* pThread = (WINPR_THREAD*)handle;
+
+ if (!ThreadIsHandled(handle))
+ return -1;
+
+ return pThread->event.fds[0];
+}
+
+#define run_mutex_init(fkt, mux, arg) run_mutex_init_(fkt, #fkt, mux, arg)
+static BOOL run_mutex_init_(int (*fkt)(pthread_mutex_t*, const pthread_mutexattr_t*),
+ const char* name, pthread_mutex_t* mutex,
+ const pthread_mutexattr_t* mutexattr)
+{
+ int rc = 0;
+
+ WINPR_ASSERT(fkt);
+ WINPR_ASSERT(mutex);
+
+ rc = fkt(mutex, mutexattr);
+ if (rc != 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
+ }
+ return rc == 0;
+}
+
+#define run_mutex_fkt(fkt, mux) run_mutex_fkt_(fkt, #fkt, mux)
+static BOOL run_mutex_fkt_(int (*fkt)(pthread_mutex_t* mux), const char* name,
+ pthread_mutex_t* mutex)
+{
+ int rc = 0;
+
+ WINPR_ASSERT(fkt);
+ WINPR_ASSERT(mutex);
+
+ rc = fkt(mutex);
+ if (rc != 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
+ }
+ return rc == 0;
+}
+
+#define run_cond_init(fkt, cond, arg) run_cond_init_(fkt, #fkt, cond, arg)
+static BOOL run_cond_init_(int (*fkt)(pthread_cond_t*, const pthread_condattr_t*), const char* name,
+ pthread_cond_t* condition, const pthread_condattr_t* conditionattr)
+{
+ int rc = 0;
+
+ WINPR_ASSERT(fkt);
+ WINPR_ASSERT(condition);
+
+ rc = fkt(condition, conditionattr);
+ if (rc != 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
+ }
+ return rc == 0;
+}
+
+#define run_cond_fkt(fkt, cond) run_cond_fkt_(fkt, #fkt, cond)
+static BOOL run_cond_fkt_(int (*fkt)(pthread_cond_t* mux), const char* name,
+ pthread_cond_t* condition)
+{
+ int rc = 0;
+
+ WINPR_ASSERT(fkt);
+ WINPR_ASSERT(condition);
+
+ rc = fkt(condition);
+ if (rc != 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
+ }
+ return rc == 0;
+}
+
+static int pthread_mutex_checked_unlock(pthread_mutex_t* mutex)
+{
+ WINPR_ASSERT(mutex);
+ WINPR_ASSERT(pthread_mutex_trylock(mutex) == EBUSY);
+ return pthread_mutex_unlock(mutex);
+}
+
+static BOOL mux_condition_bundle_init(mux_condition_bundle* bundle)
+{
+ WINPR_ASSERT(bundle);
+
+ bundle->val = FALSE;
+ if (!run_mutex_init(pthread_mutex_init, &bundle->mux, NULL))
+ return FALSE;
+
+ if (!run_cond_init(pthread_cond_init, &bundle->cond, NULL))
+ return FALSE;
+ return TRUE;
+}
+
+static void mux_condition_bundle_uninit(mux_condition_bundle* bundle)
+{
+ mux_condition_bundle empty = { 0 };
+
+ WINPR_ASSERT(bundle);
+
+ run_cond_fkt(pthread_cond_destroy, &bundle->cond);
+ run_mutex_fkt(pthread_mutex_destroy, &bundle->mux);
+ *bundle = empty;
+}
+
+static BOOL mux_condition_bundle_signal(mux_condition_bundle* bundle)
+{
+ BOOL rc = TRUE;
+ WINPR_ASSERT(bundle);
+
+ if (!run_mutex_fkt(pthread_mutex_lock, &bundle->mux))
+ return FALSE;
+ bundle->val = TRUE;
+ if (!run_cond_fkt(pthread_cond_signal, &bundle->cond))
+ rc = FALSE;
+ if (!run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux))
+ rc = FALSE;
+ return rc;
+}
+
+static BOOL mux_condition_bundle_lock(mux_condition_bundle* bundle)
+{
+ WINPR_ASSERT(bundle);
+ return run_mutex_fkt(pthread_mutex_lock, &bundle->mux);
+}
+
+static BOOL mux_condition_bundle_unlock(mux_condition_bundle* bundle)
+{
+ WINPR_ASSERT(bundle);
+ return run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux);
+}
+
+static BOOL mux_condition_bundle_wait(mux_condition_bundle* bundle, const char* name)
+{
+ BOOL rc = FALSE;
+
+ WINPR_ASSERT(bundle);
+ WINPR_ASSERT(name);
+ WINPR_ASSERT(pthread_mutex_trylock(&bundle->mux) == EBUSY);
+
+ while (!bundle->val)
+ {
+ int r = pthread_cond_wait(&bundle->cond, &bundle->mux);
+ if (r != 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "failed to wait for %s [%s]", name,
+ winpr_strerror(r, ebuffer, sizeof(ebuffer)));
+ switch (r)
+ {
+ case ENOTRECOVERABLE:
+ case EPERM:
+ case ETIMEDOUT:
+ case EINVAL:
+ goto fail;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ rc = bundle->val;
+
+fail:
+ return rc;
+}
+
+static BOOL signal_thread_ready(WINPR_THREAD* thread)
+{
+ WINPR_ASSERT(thread);
+
+ return mux_condition_bundle_signal(&thread->isCreated);
+}
+
+static BOOL signal_thread_is_running(WINPR_THREAD* thread)
+{
+ WINPR_ASSERT(thread);
+
+ return mux_condition_bundle_signal(&thread->isRunning);
+}
+
+static DWORD ThreadCleanupHandle(HANDLE handle)
+{
+ DWORD status = WAIT_FAILED;
+ WINPR_THREAD* thread = (WINPR_THREAD*)handle;
+
+ if (!ThreadIsHandled(handle))
+ return WAIT_FAILED;
+
+ if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
+ return WAIT_FAILED;
+
+ if (!thread->joined)
+ {
+ int rc = pthread_join(thread->thread, NULL);
+
+ if (rc != 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "pthread_join failure: [%d] %s", rc,
+ winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
+ goto fail;
+ }
+ else
+ thread->joined = TRUE;
+ }
+
+ status = WAIT_OBJECT_0;
+
+fail:
+ if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
+ return WAIT_FAILED;
+
+ return status;
+}
+
+static HANDLE_OPS ops = { ThreadIsHandled,
+ ThreadCloseHandle,
+ ThreadGetFd,
+ ThreadCleanupHandle,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL };
+
+static void dump_thread(WINPR_THREAD* thread)
+{
+#if defined(WITH_DEBUG_THREADS)
+ void* stack = winpr_backtrace(20);
+ char** msg = NULL;
+ size_t used = 0;
+ WLog_DBG(TAG, "Called from:");
+ msg = winpr_backtrace_symbols(stack, &used);
+
+ for (size_t i = 0; i < used; i++)
+ WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
+
+ free(msg);
+ winpr_backtrace_free(stack);
+ WLog_DBG(TAG, "Thread handle created still not closed!");
+ msg = winpr_backtrace_symbols(thread->create_stack, &used);
+
+ for (size_t i = 0; i < used; i++)
+ WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
+
+ free(msg);
+
+ if (thread->started)
+ {
+ WLog_DBG(TAG, "Thread still running!");
+ }
+ else if (!thread->exit_stack)
+ {
+ WLog_DBG(TAG, "Thread suspended.");
+ }
+ else
+ {
+ WLog_DBG(TAG, "Thread exited at:");
+ msg = winpr_backtrace_symbols(thread->exit_stack, &used);
+
+ for (size_t i = 0; i < used; i++)
+ WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
+
+ free(msg);
+ }
+#else
+ WINPR_UNUSED(thread);
+#endif
+}
+
+/**
+ * TODO: implement thread suspend/resume using pthreads
+ * http://stackoverflow.com/questions/3140867/suspend-pthreads-without-using-condition
+ */
+static BOOL set_event(WINPR_THREAD* thread)
+{
+ return winpr_event_set(&thread->event);
+}
+
+static BOOL reset_event(WINPR_THREAD* thread)
+{
+ return winpr_event_reset(&thread->event);
+}
+
+#if defined(WITH_THREAD_LIST)
+static BOOL thread_compare(const void* a, const void* b)
+{
+ const pthread_t* p1 = a;
+ const pthread_t* p2 = b;
+ BOOL rc = pthread_equal(*p1, *p2);
+ return rc;
+}
+#endif
+
+static INIT_ONCE threads_InitOnce = INIT_ONCE_STATIC_INIT;
+static pthread_t mainThreadId;
+static DWORD currentThreadTlsIndex = TLS_OUT_OF_INDEXES;
+
+static BOOL initializeThreads(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context)
+{
+ if (!apc_init(&mainThread.apc))
+ {
+ WLog_ERR(TAG, "failed to initialize APC");
+ goto out;
+ }
+
+ mainThread.common.Type = HANDLE_TYPE_THREAD;
+ mainThreadId = pthread_self();
+
+ currentThreadTlsIndex = TlsAlloc();
+ if (currentThreadTlsIndex == TLS_OUT_OF_INDEXES)
+ {
+ WLog_ERR(TAG, "Major bug, unable to allocate a TLS value for currentThread");
+ }
+
+#if defined(WITH_THREAD_LIST)
+ thread_list = ListDictionary_New(TRUE);
+
+ if (!thread_list)
+ {
+ WLog_ERR(TAG, "Couldn't create global thread list");
+ goto error_thread_list;
+ }
+
+ thread_list->objectKey.fnObjectEquals = thread_compare;
+#endif
+
+out:
+ return TRUE;
+}
+
+static BOOL signal_and_wait_for_ready(WINPR_THREAD* thread)
+{
+ BOOL res = FALSE;
+
+ WINPR_ASSERT(thread);
+
+ if (!mux_condition_bundle_lock(&thread->isRunning))
+ return FALSE;
+
+ if (!signal_thread_ready(thread))
+ goto fail;
+
+ if (!mux_condition_bundle_wait(&thread->isRunning, "threadIsRunning"))
+ goto fail;
+
+#if defined(WITH_THREAD_LIST)
+ if (!ListDictionary_Contains(thread_list, &thread->thread))
+ {
+ WLog_ERR(TAG, "Thread not in thread_list, startup failed!");
+ goto fail;
+ }
+#endif
+
+ res = TRUE;
+
+fail:
+ if (!mux_condition_bundle_unlock(&thread->isRunning))
+ return FALSE;
+
+ return res;
+}
+
+/* Thread launcher function responsible for registering
+ * cleanup handlers and calling pthread_exit, if not done
+ * in thread function. */
+static void* thread_launcher(void* arg)
+{
+ DWORD rc = 0;
+ WINPR_THREAD* thread = (WINPR_THREAD*)arg;
+ LPTHREAD_START_ROUTINE fkt = NULL;
+
+ if (!thread)
+ {
+ WLog_ERR(TAG, "Called with invalid argument %p", arg);
+ goto exit;
+ }
+
+ if (!TlsSetValue(currentThreadTlsIndex, thread))
+ {
+ WLog_ERR(TAG, "thread %d, unable to set current thread value", pthread_self());
+ goto exit;
+ }
+
+ if (!(fkt = thread->lpStartAddress))
+ {
+ WLog_ERR(TAG, "Thread function argument is %p", (void*)fkt);
+ goto exit;
+ }
+
+ if (!signal_and_wait_for_ready(thread))
+ goto exit;
+
+ rc = fkt(thread->lpParameter);
+exit:
+
+ if (thread)
+ {
+ apc_cleanupThread(thread);
+
+ if (!thread->exited)
+ thread->dwExitCode = rc;
+
+ set_event(thread);
+
+ signal_thread_ready(thread);
+
+ if (thread->detached || !thread->started)
+ cleanup_handle(thread);
+ }
+
+ return NULL;
+}
+
+static BOOL winpr_StartThread(WINPR_THREAD* thread)
+{
+ BOOL rc = FALSE;
+ BOOL locked = FALSE;
+ pthread_attr_t attr = { 0 };
+
+ if (!mux_condition_bundle_lock(&thread->isCreated))
+ return FALSE;
+ locked = TRUE;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ if (thread->dwStackSize > 0)
+ pthread_attr_setstacksize(&attr, (size_t)thread->dwStackSize);
+
+ thread->started = TRUE;
+ reset_event(thread);
+
+#if defined(WITH_THREAD_LIST)
+ if (!ListDictionary_Add(thread_list, &thread->thread, thread))
+ {
+ WLog_ERR(TAG, "failed to add the thread to the thread list");
+ goto error;
+ }
+#endif
+
+ if (pthread_create(&thread->thread, &attr, thread_launcher, thread))
+ goto error;
+
+ if (!mux_condition_bundle_wait(&thread->isCreated, "threadIsCreated"))
+ goto error;
+
+ locked = FALSE;
+ if (!mux_condition_bundle_unlock(&thread->isCreated))
+ goto error;
+
+ if (!signal_thread_is_running(thread))
+ {
+ WLog_ERR(TAG, "failed to signal the thread was ready");
+ goto error;
+ }
+
+ rc = TRUE;
+error:
+ if (locked)
+ {
+ if (!mux_condition_bundle_unlock(&thread->isCreated))
+ rc = FALSE;
+ }
+
+ pthread_attr_destroy(&attr);
+
+ if (rc)
+ dump_thread(thread);
+
+ return rc;
+}
+
+HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize,
+ LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter,
+ DWORD dwCreationFlags, LPDWORD lpThreadId)
+{
+ HANDLE handle = NULL;
+ WINPR_THREAD* thread = (WINPR_THREAD*)calloc(1, sizeof(WINPR_THREAD));
+
+ if (!thread)
+ return NULL;
+
+ thread->dwStackSize = dwStackSize;
+ thread->lpParameter = lpParameter;
+ thread->lpStartAddress = lpStartAddress;
+ thread->lpThreadAttributes = lpThreadAttributes;
+ thread->common.ops = &ops;
+#if defined(WITH_DEBUG_THREADS)
+ thread->create_stack = winpr_backtrace(20);
+ dump_thread(thread);
+#endif
+
+ if (!winpr_event_init(&thread->event))
+ {
+ WLog_ERR(TAG, "failed to create event");
+ goto fail;
+ }
+
+ if (!run_mutex_init(pthread_mutex_init, &thread->mutex, NULL))
+ {
+ WLog_ERR(TAG, "failed to initialize thread mutex");
+ goto fail;
+ }
+
+ if (!apc_init(&thread->apc))
+ {
+ WLog_ERR(TAG, "failed to initialize APC");
+ goto fail;
+ }
+
+ if (!mux_condition_bundle_init(&thread->isCreated))
+ goto fail;
+ if (!mux_condition_bundle_init(&thread->isRunning))
+ goto fail;
+
+ WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ);
+ handle = (HANDLE)thread;
+
+ InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
+
+ if (!(dwCreationFlags & CREATE_SUSPENDED))
+ {
+ if (!winpr_StartThread(thread))
+ goto fail;
+ }
+ else
+ {
+ if (!set_event(thread))
+ goto fail;
+ }
+
+ return handle;
+fail:
+ cleanup_handle(thread);
+ return NULL;
+}
+
+void cleanup_handle(void* obj)
+{
+ WINPR_THREAD* thread = (WINPR_THREAD*)obj;
+ if (!thread)
+ return;
+
+ if (!apc_uninit(&thread->apc))
+ WLog_ERR(TAG, "failed to destroy APC");
+
+ mux_condition_bundle_uninit(&thread->isCreated);
+ mux_condition_bundle_uninit(&thread->isRunning);
+ run_mutex_fkt(pthread_mutex_destroy, &thread->mutex);
+
+ winpr_event_uninit(&thread->event);
+
+#if defined(WITH_THREAD_LIST)
+ ListDictionary_Remove(thread_list, &thread->thread);
+#endif
+#if defined(WITH_DEBUG_THREADS)
+
+ if (thread->create_stack)
+ winpr_backtrace_free(thread->create_stack);
+
+ if (thread->exit_stack)
+ winpr_backtrace_free(thread->exit_stack);
+
+#endif
+ free(thread);
+}
+
+BOOL ThreadCloseHandle(HANDLE handle)
+{
+ WINPR_THREAD* thread = (WINPR_THREAD*)handle;
+
+#if defined(WITH_THREAD_LIST)
+ if (!thread_list)
+ {
+ WLog_ERR(TAG, "Thread list does not exist, check call!");
+ dump_thread(thread);
+ }
+ else if (!ListDictionary_Contains(thread_list, &thread->thread))
+ {
+ WLog_ERR(TAG, "Thread list does not contain this thread! check call!");
+ dump_thread(thread);
+ }
+ else
+ {
+ ListDictionary_Lock(thread_list);
+#endif
+ dump_thread(thread);
+
+ if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0))
+ {
+ WLog_DBG(TAG, "Thread running, setting to detached state!");
+ thread->detached = TRUE;
+ pthread_detach(thread->thread);
+ }
+ else
+ {
+ cleanup_handle(thread);
+ }
+
+#if defined(WITH_THREAD_LIST)
+ ListDictionary_Unlock(thread_list);
+ }
+#endif
+
+ return TRUE;
+}
+
+HANDLE CreateRemoteThread(HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
+ LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId)
+{
+ WLog_ERR(TAG, "not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return NULL;
+}
+
+VOID ExitThread(DWORD dwExitCode)
+{
+#if defined(WITH_THREAD_LIST)
+ DWORD rc;
+ pthread_t tid = pthread_self();
+
+ if (!thread_list)
+ {
+ WLog_ERR(TAG, "function called without existing thread list!");
+#if defined(WITH_DEBUG_THREADS)
+ DumpThreadHandles();
+#endif
+ pthread_exit(0);
+ }
+ else if (!ListDictionary_Contains(thread_list, &tid))
+ {
+ WLog_ERR(TAG, "function called, but no matching entry in thread list!");
+#if defined(WITH_DEBUG_THREADS)
+ DumpThreadHandles();
+#endif
+ pthread_exit(0);
+ }
+ else
+ {
+ WINPR_THREAD* thread;
+ ListDictionary_Lock(thread_list);
+ thread = ListDictionary_GetItemValue(thread_list, &tid);
+ WINPR_ASSERT(thread);
+ thread->exited = TRUE;
+ thread->dwExitCode = dwExitCode;
+#if defined(WITH_DEBUG_THREADS)
+ thread->exit_stack = winpr_backtrace(20);
+#endif
+ ListDictionary_Unlock(thread_list);
+ set_event(thread);
+ rc = thread->dwExitCode;
+
+ if (thread->detached || !thread->started)
+ cleanup_handle(thread);
+
+ pthread_exit((void*)(size_t)rc);
+ }
+#else
+ WINPR_UNUSED(dwExitCode);
+#endif
+}
+
+BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* Object = NULL;
+ WINPR_THREAD* thread = NULL;
+
+ if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
+ {
+ WLog_ERR(TAG, "hThread is not a thread");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ thread = (WINPR_THREAD*)Object;
+ *lpExitCode = thread->dwExitCode;
+ return TRUE;
+}
+
+WINPR_THREAD* winpr_GetCurrentThread(VOID)
+{
+ WINPR_THREAD* ret = NULL;
+
+ InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
+ if (mainThreadId == pthread_self())
+ return (HANDLE)&mainThread;
+
+ ret = TlsGetValue(currentThreadTlsIndex);
+ return ret;
+}
+
+HANDLE _GetCurrentThread(VOID)
+{
+ return (HANDLE)winpr_GetCurrentThread();
+}
+
+DWORD GetCurrentThreadId(VOID)
+{
+ pthread_t tid = 0;
+ tid = pthread_self();
+ /* Since pthread_t can be 64-bits on some systems, take just the */
+ /* lower 32-bits of it for the thread ID returned by this function. */
+ return (DWORD)tid & 0xffffffffUL;
+}
+
+typedef struct
+{
+ WINPR_APC_ITEM apc;
+ PAPCFUNC completion;
+ ULONG_PTR completionArg;
+} UserApcItem;
+
+static void userAPC(LPVOID arg)
+{
+ UserApcItem* userApc = (UserApcItem*)arg;
+
+ userApc->completion(userApc->completionArg);
+
+ userApc->apc.markedForRemove = TRUE;
+}
+
+DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* Object = NULL;
+ WINPR_APC_ITEM* apc = NULL;
+ UserApcItem* apcItem = NULL;
+
+ if (!pfnAPC)
+ return 1;
+
+ if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
+ {
+ WLog_ERR(TAG, "hThread is not a thread");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return (DWORD)0;
+ }
+
+ apcItem = calloc(1, sizeof(*apcItem));
+ if (!apcItem)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return (DWORD)0;
+ }
+
+ apc = &apcItem->apc;
+ apc->type = APC_TYPE_USER;
+ apc->markedForFree = TRUE;
+ apc->alwaysSignaled = TRUE;
+ apc->completion = userAPC;
+ apc->completionArgs = apc;
+ apcItem->completion = pfnAPC;
+ apcItem->completionArg = dwData;
+ apc_register(hThread, apc);
+ return 1;
+}
+
+DWORD ResumeThread(HANDLE hThread)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* Object = NULL;
+ WINPR_THREAD* thread = NULL;
+
+ if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
+ {
+ WLog_ERR(TAG, "hThread is not a thread");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return (DWORD)-1;
+ }
+
+ thread = (WINPR_THREAD*)Object;
+
+ if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
+ return (DWORD)-1;
+
+ if (!thread->started)
+ {
+ if (!winpr_StartThread(thread))
+ {
+ run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex);
+ return (DWORD)-1;
+ }
+ }
+ else
+ WLog_WARN(TAG, "Thread already started!");
+
+ if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
+ return (DWORD)-1;
+
+ return 0;
+}
+
+DWORD SuspendThread(HANDLE hThread)
+{
+ WLog_ERR(TAG, "not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return (DWORD)-1;
+}
+
+BOOL SwitchToThread(VOID)
+{
+ /**
+ * Note: on some operating systems sched_yield is a stub returning -1.
+ * usleep should at least trigger a context switch if any thread is waiting.
+ */
+ if (sched_yield() != 0)
+ usleep(1);
+
+ return TRUE;
+}
+
+BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* Object = NULL;
+ WINPR_THREAD* thread = NULL;
+
+ if (!winpr_Handle_GetInfo(hThread, &Type, &Object))
+ return FALSE;
+
+ thread = (WINPR_THREAD*)Object;
+ thread->exited = TRUE;
+ thread->dwExitCode = dwExitCode;
+
+ if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
+ return FALSE;
+
+#ifndef ANDROID
+ pthread_cancel(thread->thread);
+#else
+ WLog_ERR(TAG, "Function not supported on this platform!");
+#endif
+
+ if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
+ return FALSE;
+
+ set_event(thread);
+ return TRUE;
+}
+
+VOID DumpThreadHandles(void)
+{
+#if defined(WITH_DEBUG_THREADS)
+ char** msg = NULL;
+ size_t used = 0;
+ void* stack = winpr_backtrace(20);
+ WLog_DBG(TAG, "---------------- Called from ----------------------------");
+ msg = winpr_backtrace_symbols(stack, &used);
+
+ for (size_t i = 0; i < used; i++)
+ {
+ WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
+ }
+
+ free(msg);
+ winpr_backtrace_free(stack);
+ WLog_DBG(TAG, "---------------- Start Dumping thread handles -----------");
+
+#if defined(WITH_THREAD_LIST)
+ if (!thread_list)
+ {
+ WLog_DBG(TAG, "All threads properly shut down and disposed of.");
+ }
+ else
+ {
+ ULONG_PTR* keys = NULL;
+ ListDictionary_Lock(thread_list);
+ int x, count = ListDictionary_GetKeys(thread_list, &keys);
+ WLog_DBG(TAG, "Dumping %d elements", count);
+
+ for (size_t x = 0; x < count; x++)
+ {
+ WINPR_THREAD* thread = ListDictionary_GetItemValue(thread_list, (void*)keys[x]);
+ WLog_DBG(TAG, "Thread [%d] handle created still not closed!", x);
+ msg = winpr_backtrace_symbols(thread->create_stack, &used);
+
+ for (size_t i = 0; i < used; i++)
+ {
+ WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
+ }
+
+ free(msg);
+
+ if (thread->started)
+ {
+ WLog_DBG(TAG, "Thread [%d] still running!", x);
+ }
+ else
+ {
+ WLog_DBG(TAG, "Thread [%d] exited at:", x);
+ msg = winpr_backtrace_symbols(thread->exit_stack, &used);
+
+ for (size_t i = 0; i < used; i++)
+ WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
+
+ free(msg);
+ }
+ }
+
+ free(keys);
+ ListDictionary_Unlock(thread_list);
+ }
+#endif
+
+ WLog_DBG(TAG, "---------------- End Dumping thread handles -------------");
+#endif
+}
+#endif