FreeRDP/winpr/libwinpr/thread/thread.c

852 lines
19 KiB
C
Raw Normal View History

/**
* WinPR: Windows Portable Runtime
* Process Thread Functions
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
2015-05-27 19:24:56 +03:00
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
2021-06-09 15:03:34 +03:00
#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 HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_EVENTFD_H
#include <sys/eventfd.h>
#endif
2014-10-27 13:39:02 +03:00
#include <winpr/debug.h>
#include <errno.h>
#include <fcntl.h>
#include <winpr/collections.h>
#include "thread.h"
#include "apc.h"
2013-05-17 01:32:58 +04:00
#include "../handle/handle.h"
2014-10-27 13:45:39 +03:00
#include "../log.h"
#define TAG WINPR_TAG("thread")
2013-05-17 01:32:58 +04:00
static WINPR_THREAD mainThread;
static wListDictionary* thread_list = NULL;
static BOOL ThreadCloseHandle(HANDLE handle);
static void cleanup_handle(void* obj);
static BOOL ThreadIsHandled(HANDLE handle)
{
2019-11-06 17:24:51 +03:00
WINPR_THREAD* pThread = (WINPR_THREAD*)handle;
if (!pThread || (pThread->Type != HANDLE_TYPE_THREAD))
{
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
return TRUE;
}
2015-03-11 19:57:01 +03:00
static int ThreadGetFd(HANDLE handle)
{
2019-11-06 17:24:51 +03:00
WINPR_THREAD* pThread = (WINPR_THREAD*)handle;
2015-03-11 19:57:01 +03:00
if (!ThreadIsHandled(handle))
return -1;
return pThread->event.fds[0];
2015-03-11 19:57:01 +03:00
}
static DWORD ThreadCleanupHandle(HANDLE handle)
{
2019-11-06 17:24:51 +03:00
WINPR_THREAD* thread = (WINPR_THREAD*)handle;
2015-03-11 19:57:01 +03:00
if (!ThreadIsHandled(handle))
return WAIT_FAILED;
if (pthread_mutex_lock(&thread->mutex))
return WAIT_FAILED;
2015-03-11 19:57:01 +03:00
if (!thread->joined)
{
int status;
status = pthread_join(thread->thread, NULL);
if (status != 0)
{
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG, "pthread_join failure: [%d] %s", status, strerror(status));
2015-03-11 19:57:01 +03:00
pthread_mutex_unlock(&thread->mutex);
return WAIT_FAILED;
}
else
thread->joined = TRUE;
}
if (pthread_mutex_unlock(&thread->mutex))
return WAIT_FAILED;
2015-03-11 19:57:01 +03:00
return WAIT_OBJECT_0;
}
2019-11-06 17:24:51 +03:00
static HANDLE_OPS ops = { ThreadIsHandled,
ThreadCloseHandle,
ThreadGetFd,
ThreadCleanupHandle,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL };
2014-10-27 13:39:02 +03:00
static void dump_thread(WINPR_THREAD* thread)
{
2014-10-27 13:39:02 +03:00
#if defined(WITH_DEBUG_THREADS)
void* stack = winpr_backtrace(20);
char** msg;
size_t used, i;
WLog_DBG(TAG, "Called from:");
msg = winpr_backtrace_symbols(stack, &used);
for (i = 0; i < used; i++)
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
2014-10-27 13:39:02 +03:00
free(msg);
winpr_backtrace_free(stack);
WLog_DBG(TAG, "Thread handle created still not closed!");
msg = winpr_backtrace_symbols(thread->create_stack, &used);
for (i = 0; i < used; i++)
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
2014-10-27 13:39:02 +03:00
free(msg);
if (thread->started)
{
2014-10-27 13:39:02 +03:00
WLog_DBG(TAG, "Thread still running!");
}
else if (!thread->exit_stack)
{
2014-10-27 13:39:02 +03:00
WLog_DBG(TAG, "Thread suspended.");
}
else
{
2014-10-27 13:39:02 +03:00
WLog_DBG(TAG, "Thread exited at:");
msg = winpr_backtrace_symbols(thread->exit_stack, &used);
for (i = 0; i < used; i++)
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
2014-10-27 13:39:02 +03:00
free(msg);
}
#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);
}
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;
}
static INIT_ONCE threads_InitOnce = INIT_ONCE_STATIC_INIT;
static pthread_t mainThreadId;
static DWORD currentThreadTlsIndex = TLS_OUT_OF_INDEXES;
BOOL initializeThreads(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context)
{
if (!apc_init(&mainThread.apc))
{
WLog_ERR(TAG, "failed to initialize APC");
goto out;
}
mainThread.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");
}
out:
return TRUE;
}
/* Thread launcher function responsible for registering
* cleanup handlers and calling pthread_exit, if not done
* in thread function. */
static void* thread_launcher(void* arg)
{
2018-10-24 14:17:04 +03:00
DWORD rc = 0;
2019-11-06 17:24:51 +03:00
WINPR_THREAD* thread = (WINPR_THREAD*)arg;
2018-10-24 14:17:04 +03:00
LPTHREAD_START_ROUTINE fkt;
if (!thread)
{
2014-10-27 13:39:02 +03:00
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;
}
2018-10-24 14:17:04 +03:00
if (!(fkt = thread->lpStartAddress))
{
2017-11-14 15:54:59 +03:00
WLog_ERR(TAG, "Thread function argument is %p", (void*)fkt);
goto exit;
}
if (pthread_mutex_lock(&thread->threadIsReadyMutex))
goto exit;
if (!ListDictionary_Contains(thread_list, &thread->thread))
{
if (pthread_cond_wait(&thread->threadIsReady, &thread->threadIsReadyMutex) != 0)
{
WLog_ERR(TAG, "The thread could not be made ready");
pthread_mutex_unlock(&thread->threadIsReadyMutex);
goto exit;
}
}
if (pthread_mutex_unlock(&thread->threadIsReadyMutex))
goto exit;
2021-06-09 15:03:34 +03:00
WINPR_ASSERT(ListDictionary_Contains(thread_list, &thread->thread));
rc = fkt(thread->lpParameter);
exit:
2014-11-16 18:51:04 +03:00
if (thread)
{
apc_cleanupThread(thread);
2014-11-16 18:51:04 +03:00
if (!thread->exited)
2018-10-24 14:17:04 +03:00
thread->dwExitCode = rc;
2014-11-16 18:51:04 +03:00
set_event(thread);
2014-11-16 18:51:04 +03:00
if (thread->detached || !thread->started)
cleanup_handle(thread);
}
2018-10-24 14:17:04 +03:00
return NULL;
}
static BOOL winpr_StartThread(WINPR_THREAD* thread)
{
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
if (thread->dwStackSize > 0)
2019-11-06 17:24:51 +03:00
pthread_attr_setstacksize(&attr, (size_t)thread->dwStackSize);
thread->started = TRUE;
reset_event(thread);
if (pthread_create(&thread->thread, &attr, thread_launcher, thread))
goto error;
if (pthread_mutex_lock(&thread->threadIsReadyMutex))
goto error;
if (!ListDictionary_Add(thread_list, &thread->thread, thread))
{
WLog_ERR(TAG, "failed to add the thread to the thread list");
pthread_mutex_unlock(&thread->threadIsReadyMutex);
goto error;
}
if (pthread_cond_signal(&thread->threadIsReady) != 0)
{
WLog_ERR(TAG, "failed to signal the thread was ready");
pthread_mutex_unlock(&thread->threadIsReadyMutex);
goto error;
}
if (pthread_mutex_unlock(&thread->threadIsReadyMutex))
goto error;
pthread_attr_destroy(&attr);
dump_thread(thread);
return TRUE;
error:
pthread_attr_destroy(&attr);
return FALSE;
}
2019-11-06 17:24:51 +03:00
HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter,
DWORD dwCreationFlags, LPDWORD lpThreadId)
{
HANDLE handle;
WINPR_THREAD* thread;
2019-11-06 17:24:51 +03:00
thread = (WINPR_THREAD*)calloc(1, sizeof(WINPR_THREAD));
2014-04-09 18:07:06 +04:00
if (!thread)
return NULL;
thread->dwStackSize = dwStackSize;
thread->lpParameter = lpParameter;
thread->lpStartAddress = lpStartAddress;
thread->lpThreadAttributes = lpThreadAttributes;
thread->ops = &ops;
2014-10-27 13:39:02 +03:00
#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 error_event;
}
if (pthread_mutex_init(&thread->mutex, NULL) != 0)
2014-11-21 23:12:49 +03:00
{
WLog_ERR(TAG, "failed to initialize thread mutex");
goto error_mutex;
2014-11-21 23:12:49 +03:00
}
if (!apc_init(&thread->apc))
{
WLog_ERR(TAG, "failed to initialize APC");
goto error_APC;
}
if (pthread_mutex_init(&thread->threadIsReadyMutex, NULL) != 0)
{
WLog_ERR(TAG, "failed to initialize a mutex for a condition variable");
goto error_thread_ready_mutex;
}
if (pthread_cond_init(&thread->threadIsReady, NULL) != 0)
{
WLog_ERR(TAG, "failed to initialize a condition variable");
goto error_thread_ready;
}
WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ);
2019-11-06 17:24:51 +03:00
handle = (HANDLE)thread;
if (!thread_list)
{
InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
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;
}
if (!(dwCreationFlags & CREATE_SUSPENDED))
{
if (!winpr_StartThread(thread))
goto error_thread_list;
}
else
{
if (!set_event(thread))
goto error_thread_list;
}
return handle;
error_thread_list:
pthread_cond_destroy(&thread->threadIsReady);
error_thread_ready:
pthread_mutex_destroy(&thread->threadIsReadyMutex);
error_thread_ready_mutex:
apc_uninit(&thread->apc);
error_APC:
pthread_mutex_destroy(&thread->mutex);
error_mutex:
winpr_event_uninit(&thread->event);
error_event:
free(thread);
return NULL;
}
void cleanup_handle(void* obj)
{
int rc;
2019-11-06 17:24:51 +03:00
WINPR_THREAD* thread = (WINPR_THREAD*)obj;
if (!apc_uninit(&thread->apc))
WLog_ERR(TAG, "failed to destroy APC");
rc = pthread_cond_destroy(&thread->threadIsReady);
if (rc)
WLog_ERR(TAG, "failed to destroy thread->threadIsReady [%d] %s (%d)", rc, strerror(errno),
2019-11-06 17:24:51 +03:00
errno);
rc = pthread_mutex_destroy(&thread->threadIsReadyMutex);
if (rc)
WLog_ERR(TAG, "failed to destroy thread->threadIsReadyMutex [%d] %s (%d)", rc,
2019-11-06 17:24:51 +03:00
strerror(errno), errno);
rc = pthread_mutex_destroy(&thread->mutex);
if (rc)
WLog_ERR(TAG, "failed to destroy thread->mutex [%d] %s (%d)", rc, strerror(errno), errno);
winpr_event_uninit(&thread->event);
if (thread_list && ListDictionary_Contains(thread_list, &thread->thread))
ListDictionary_Remove(thread_list, &thread->thread);
2014-10-27 13:39:02 +03:00
#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)
{
2019-11-06 17:24:51 +03:00
WINPR_THREAD* thread = (WINPR_THREAD*)handle;
if (!thread_list)
{
2014-10-27 13:39:02 +03:00
WLog_ERR(TAG, "Thread list does not exist, check call!");
dump_thread(thread);
}
else if (!ListDictionary_Contains(thread_list, &thread->thread))
{
2014-10-27 13:39:02 +03:00
WLog_ERR(TAG, "Thread list does not contain this thread! check call!");
dump_thread(thread);
}
else
{
ListDictionary_Lock(thread_list);
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);
}
ListDictionary_Unlock(thread_list);
if (ListDictionary_Count(thread_list) < 1)
{
ListDictionary_Free(thread_list);
thread_list = NULL;
}
}
return TRUE;
}
2019-11-06 17:24:51 +03:00
HANDLE CreateRemoteThread(HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId)
{
WLog_ERR(TAG, "%s: not implemented", __FUNCTION__);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return NULL;
}
VOID ExitThread(DWORD dwExitCode)
{
2014-11-16 17:21:13 +03:00
DWORD rc;
pthread_t tid = pthread_self();
2014-11-11 19:21:34 +03:00
if (!thread_list)
{
2014-10-27 13:39:02 +03:00
WLog_ERR(TAG, "function called without existing thread list!");
#if defined(WITH_DEBUG_THREADS)
DumpThreadHandles();
#endif
2014-11-11 19:21:34 +03:00
pthread_exit(0);
}
else if (!ListDictionary_Contains(thread_list, &tid))
{
2014-10-27 13:39:02 +03:00
WLog_ERR(TAG, "function called, but no matching entry in thread list!");
#if defined(WITH_DEBUG_THREADS)
DumpThreadHandles();
#endif
2014-11-11 19:21:34 +03:00
pthread_exit(0);
}
else
{
2014-11-11 19:21:34 +03:00
WINPR_THREAD* thread;
ListDictionary_Lock(thread_list);
thread = ListDictionary_GetItemValue(thread_list, &tid);
2021-06-09 15:03:34 +03:00
WINPR_ASSERT(thread);
thread->exited = TRUE;
thread->dwExitCode = dwExitCode;
2014-10-27 13:39:02 +03:00
#if defined(WITH_DEBUG_THREADS)
thread->exit_stack = winpr_backtrace(20);
#endif
ListDictionary_Unlock(thread_list);
set_event(thread);
2014-11-16 17:21:13 +03:00
rc = thread->dwExitCode;
if (thread->detached || !thread->started)
cleanup_handle(thread);
2019-11-06 17:24:51 +03:00
pthread_exit((void*)(size_t)rc);
}
}
BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)
{
ULONG Type;
2015-07-03 10:36:58 +03:00
WINPR_HANDLE* Object;
WINPR_THREAD* thread;
if (!winpr_Handle_GetInfo(hThread, &Type, &Object))
return FALSE;
2019-11-06 17:24:51 +03:00
thread = (WINPR_THREAD*)Object;
*lpExitCode = thread->dwExitCode;
return TRUE;
}
WINPR_THREAD* winpr_GetCurrentThread(VOID)
{
WINPR_THREAD* ret;
InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
if (mainThreadId == pthread_self())
return (HANDLE)&mainThread;
ret = TlsGetValue(currentThreadTlsIndex);
if (!ret)
{
2014-10-27 13:39:02 +03:00
WLog_ERR(TAG, "function called, but no matching entry in thread list!");
#if defined(WITH_DEBUG_THREADS)
DumpThreadHandles();
#endif
}
return ret;
}
HANDLE _GetCurrentThread(VOID)
{
return (HANDLE)winpr_GetCurrentThread();
}
DWORD GetCurrentThreadId(VOID)
{
pthread_t tid;
tid = pthread_self();
2015-05-20 17:52:24 +03:00
/* 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. */
2016-02-04 14:57:21 +03:00
return (DWORD)tid & 0xffffffffUL;
}
typedef struct
{
WINPR_APC_ITEM apc;
PAPCFUNC completion;
ULONG_PTR completionArg;
} UserApcItem;
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;
WINPR_HANDLE* Object;
WINPR_THREAD* thread;
WINPR_APC_ITEM* apc;
UserApcItem* apcItem;
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;
}
thread = (WINPR_THREAD*)Object;
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;
2015-07-03 10:36:58 +03:00
WINPR_HANDLE* Object;
WINPR_THREAD* thread;
if (!winpr_Handle_GetInfo(hThread, &Type, &Object))
2019-11-06 17:24:51 +03:00
return (DWORD)-1;
2019-11-06 17:24:51 +03:00
thread = (WINPR_THREAD*)Object;
if (pthread_mutex_lock(&thread->mutex))
2019-11-06 17:24:51 +03:00
return (DWORD)-1;
if (!thread->started)
{
if (!winpr_StartThread(thread))
{
pthread_mutex_unlock(&thread->mutex);
2019-11-06 17:24:51 +03:00
return (DWORD)-1;
}
}
else
2014-10-27 13:39:02 +03:00
WLog_WARN(TAG, "Thread already started!");
if (pthread_mutex_unlock(&thread->mutex))
2019-11-06 17:24:51 +03:00
return (DWORD)-1;
return 0;
}
DWORD SuspendThread(HANDLE hThread)
{
WLog_ERR(TAG, "%s: not implemented", __FUNCTION__);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2019-11-06 17:24:51 +03:00
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;
2015-07-03 10:36:58 +03:00
WINPR_HANDLE* Object;
WINPR_THREAD* thread;
if (!winpr_Handle_GetInfo(hThread, &Type, &Object))
return FALSE;
2019-11-06 17:24:51 +03:00
thread = (WINPR_THREAD*)Object;
thread->exited = TRUE;
thread->dwExitCode = dwExitCode;
if (pthread_mutex_lock(&thread->mutex))
return FALSE;
#ifndef ANDROID
pthread_cancel(thread->thread);
#else
2014-10-27 13:39:02 +03:00
WLog_ERR(TAG, "Function not supported on this platform!");
#endif
if (pthread_mutex_unlock(&thread->mutex))
return FALSE;
2015-01-28 14:43:20 +03:00
set_event(thread);
return TRUE;
}
#if defined(WITH_DEBUG_THREADS)
VOID DumpThreadHandles(void)
{
2014-10-27 13:39:02 +03:00
char** msg;
size_t used, i;
void* stack = winpr_backtrace(20);
WLog_DBG(TAG, "---------------- Called from ----------------------------");
msg = winpr_backtrace_symbols(stack, &used);
for (i = 0; i < used; i++)
{
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
}
2014-10-27 13:39:02 +03:00
free(msg);
winpr_backtrace_free(stack);
WLog_DBG(TAG, "---------------- Start Dumping thread handles -----------");
if (!thread_list)
{
2014-10-27 13:39:02 +03:00
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);
2015-01-20 13:04:23 +03:00
WLog_DBG(TAG, "Dumping %d elements", count);
for (x = 0; x < count; x++)
{
2019-11-06 17:24:51 +03:00
WINPR_THREAD* thread = ListDictionary_GetItemValue(thread_list, (void*)keys[x]);
2014-10-27 13:39:02 +03:00
WLog_DBG(TAG, "Thread [%d] handle created still not closed!", x);
msg = winpr_backtrace_symbols(thread->create_stack, &used);
for (i = 0; i < used; i++)
{
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
}
2014-10-27 13:39:02 +03:00
free(msg);
if (thread->started)
{
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "Thread [%d] still running!", x);
}
else
{
2014-10-27 13:39:02 +03:00
WLog_DBG(TAG, "Thread [%d] exited at:", x);
msg = winpr_backtrace_symbols(thread->exit_stack, &used);
for (i = 0; i < used; i++)
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
2014-10-27 13:39:02 +03:00
free(msg);
}
}
2015-05-11 10:07:39 +03:00
free(keys);
ListDictionary_Unlock(thread_list);
}
2014-10-27 13:39:02 +03:00
WLog_DBG(TAG, "---------------- End Dumping thread handles -------------");
}
#endif
#endif