23#include <winpr/config.h>
25#include <winpr/winpr.h>
26#include <winpr/assert.h>
28#include <winpr/handle.h>
30#include <winpr/thread.h>
32#if defined(__FreeBSD__)
33#include <pthread_np.h>
34#elif defined(__linux__)
35#include <sys/syscall.h>
39#define MIN(x, y) (((x) < (y)) ? (x) : (y))
43#define MAX(x, y) (((x) > (y)) ? (x) : (y))
89#include <winpr/platform.h>
92#ifdef WINPR_HAVE_UNISTD_H
96#ifdef WINPR_HAVE_SYS_EVENTFD_H
97#include <sys/eventfd.h>
100#include <winpr/debug.h>
105#include <winpr/collections.h>
110#include "../handle/handle.h"
112#define TAG WINPR_TAG("thread")
114static WINPR_THREAD mainThread;
116#if defined(WITH_THREAD_LIST)
117static wListDictionary* thread_list =
nullptr;
120static BOOL ThreadCloseHandle(HANDLE handle);
121static void cleanup_handle(
void* obj);
123static BOOL ThreadIsHandled(HANDLE handle)
125 return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_THREAD, FALSE);
128static int ThreadGetFd(HANDLE handle)
130 WINPR_THREAD* pThread = (WINPR_THREAD*)handle;
132 if (!ThreadIsHandled(handle))
135 return pThread->event.fds[0];
138#define run_mutex_init(fkt, mux, arg) run_mutex_init_(fkt, #fkt, mux, arg)
139static BOOL run_mutex_init_(
int (*fkt)(pthread_mutex_t*,
const pthread_mutexattr_t*),
140 const char* name, pthread_mutex_t* mutex,
141 const pthread_mutexattr_t* mutexattr)
148 rc = fkt(mutex, mutexattr);
151 char ebuffer[256] = WINPR_C_ARRAY_INIT;
152 WLog_WARN(TAG,
"[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer,
sizeof(ebuffer)));
157#define run_mutex_fkt(fkt, mux) run_mutex_fkt_(fkt, #fkt, mux)
158static BOOL run_mutex_fkt_(
int (*fkt)(pthread_mutex_t* mux),
const char* name,
159 pthread_mutex_t* mutex)
169 char ebuffer[256] = WINPR_C_ARRAY_INIT;
170 WLog_WARN(TAG,
"[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer,
sizeof(ebuffer)));
175#define run_cond_init(fkt, cond, arg) run_cond_init_(fkt, #fkt, cond, arg)
176static BOOL run_cond_init_(
int (*fkt)(pthread_cond_t*,
const pthread_condattr_t*),
const char* name,
177 pthread_cond_t* condition,
const pthread_condattr_t* conditionattr)
182 WINPR_ASSERT(condition);
184 rc = fkt(condition, conditionattr);
187 char ebuffer[256] = WINPR_C_ARRAY_INIT;
188 WLog_WARN(TAG,
"[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer,
sizeof(ebuffer)));
193#define run_cond_fkt(fkt, cond) run_cond_fkt_(fkt, #fkt, cond)
194static BOOL run_cond_fkt_(
int (*fkt)(pthread_cond_t* mux),
const char* name,
195 pthread_cond_t* condition)
200 WINPR_ASSERT(condition);
205 char ebuffer[256] = WINPR_C_ARRAY_INIT;
206 WLog_WARN(TAG,
"[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer,
sizeof(ebuffer)));
211static int pthread_mutex_checked_unlock(pthread_mutex_t* mutex)
214 WINPR_ASSERT(pthread_mutex_trylock(mutex) == EBUSY);
215 return pthread_mutex_unlock(mutex);
220 WINPR_ASSERT(bundle);
223 if (!run_mutex_init(pthread_mutex_init, &bundle->mux,
nullptr))
226 if (!run_cond_init(pthread_cond_init, &bundle->cond,
nullptr))
235 WINPR_ASSERT(bundle);
237 run_cond_fkt(pthread_cond_destroy, &bundle->cond);
238 run_mutex_fkt(pthread_mutex_destroy, &bundle->mux);
245 WINPR_ASSERT(bundle);
247 if (!run_mutex_fkt(pthread_mutex_lock, &bundle->mux))
250 if (!run_cond_fkt(pthread_cond_signal, &bundle->cond))
252 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux))
259 WINPR_ASSERT(bundle);
260 return run_mutex_fkt(pthread_mutex_lock, &bundle->mux);
265 WINPR_ASSERT(bundle);
266 return run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux);
273 WINPR_ASSERT(bundle);
275 WINPR_ASSERT(pthread_mutex_trylock(&bundle->mux) == EBUSY);
279 int r = pthread_cond_wait(&bundle->cond, &bundle->mux);
282 char ebuffer[256] = WINPR_C_ARRAY_INIT;
283 WLog_ERR(TAG,
"failed to wait for %s [%s]", name,
284 winpr_strerror(r, ebuffer,
sizeof(ebuffer)));
287 case ENOTRECOVERABLE:
305static BOOL signal_thread_ready(WINPR_THREAD* thread)
307 WINPR_ASSERT(thread);
309 return mux_condition_bundle_signal(&thread->isCreated);
312static BOOL signal_thread_is_running(WINPR_THREAD* thread)
314 WINPR_ASSERT(thread);
316 return mux_condition_bundle_signal(&thread->isRunning);
319static DWORD ThreadCleanupHandle(HANDLE handle)
321 DWORD status = WAIT_FAILED;
322 WINPR_THREAD* thread = (WINPR_THREAD*)handle;
324 if (!ThreadIsHandled(handle))
327 if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
332 int rc = pthread_join(thread->thread,
nullptr);
336 char ebuffer[256] = WINPR_C_ARRAY_INIT;
337 WLog_ERR(TAG,
"pthread_join failure: [%d] %s", rc,
338 winpr_strerror(rc, ebuffer,
sizeof(ebuffer)));
342 thread->joined = TRUE;
345 status = WAIT_OBJECT_0;
348 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
354static HANDLE_OPS ops = { ThreadIsHandled, ThreadCloseHandle, ThreadGetFd, ThreadCleanupHandle,
355 nullptr,
nullptr,
nullptr,
nullptr,
356 nullptr,
nullptr,
nullptr,
nullptr,
357 nullptr,
nullptr,
nullptr,
nullptr,
358 nullptr,
nullptr,
nullptr,
nullptr,
361static void dump_thread(WINPR_THREAD* thread)
363#if defined(WITH_DEBUG_THREADS)
364 void* stack = winpr_backtrace(20);
365 char** msg =
nullptr;
367 WLog_DBG(TAG,
"Called from:");
368 msg = winpr_backtrace_symbols(stack, &used);
370 for (
size_t i = 0; i < used; i++)
371 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
374 winpr_backtrace_free(stack);
375 WLog_DBG(TAG,
"Thread handle created still not closed!");
376 msg = winpr_backtrace_symbols(thread->create_stack, &used);
378 for (
size_t i = 0; i < used; i++)
379 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
385 WLog_DBG(TAG,
"Thread still running!");
387 else if (!thread->exit_stack)
389 WLog_DBG(TAG,
"Thread suspended.");
393 WLog_DBG(TAG,
"Thread exited at:");
394 msg = winpr_backtrace_symbols(thread->exit_stack, &used);
396 for (
size_t i = 0; i < used; i++)
397 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
402 WINPR_UNUSED(thread);
410static BOOL set_event(WINPR_THREAD* thread)
412 return winpr_event_set(&thread->event);
415static BOOL reset_event(WINPR_THREAD* thread)
417 return winpr_event_reset(&thread->event);
420#if defined(WITH_THREAD_LIST)
421static BOOL thread_compare(
const void* a,
const void* b)
423 const pthread_t* p1 = a;
424 const pthread_t* p2 = b;
425 BOOL rc = pthread_equal(*p1, *p2);
430static INIT_ONCE threads_InitOnce = INIT_ONCE_STATIC_INIT;
431static pthread_t mainThreadId;
432static DWORD currentThreadTlsIndex = TLS_OUT_OF_INDEXES;
434static BOOL initializeThreads(WINPR_ATTR_UNUSED
PINIT_ONCE InitOnce,
435 WINPR_ATTR_UNUSED PVOID Parameter, WINPR_ATTR_UNUSED PVOID* Context)
437 if (!apc_init(&mainThread.apc))
439 WLog_ERR(TAG,
"failed to initialize APC");
443 mainThread.common.Type = HANDLE_TYPE_THREAD;
444 mainThreadId = pthread_self();
446 currentThreadTlsIndex = TlsAlloc();
447 if (currentThreadTlsIndex == TLS_OUT_OF_INDEXES)
449 WLog_ERR(TAG,
"Major bug, unable to allocate a TLS value for currentThread");
452#if defined(WITH_THREAD_LIST)
453 thread_list = ListDictionary_New(TRUE);
457 WLog_ERR(TAG,
"Couldn't create global thread list");
458 goto error_thread_list;
461 thread_list->objectKey.fnObjectEquals = thread_compare;
468static BOOL signal_and_wait_for_ready(WINPR_THREAD* thread)
472 WINPR_ASSERT(thread);
474 if (!mux_condition_bundle_lock(&thread->isRunning))
477 if (!signal_thread_ready(thread))
480 if (!mux_condition_bundle_wait(&thread->isRunning,
"threadIsRunning"))
483#if defined(WITH_THREAD_LIST)
484 if (!ListDictionary_Contains(thread_list, &thread->thread))
486 WLog_ERR(TAG,
"Thread not in thread_list, startup failed!");
494 if (!mux_condition_bundle_unlock(&thread->isRunning))
503static void* thread_launcher(
void* arg)
506 WINPR_THREAD* thread = (WINPR_THREAD*)arg;
507 LPTHREAD_START_ROUTINE fkt =
nullptr;
511 WLog_ERR(TAG,
"Called with invalid argument %p", arg);
515 if (!TlsSetValue(currentThreadTlsIndex, thread))
517 WLog_ERR(TAG,
"thread %" PRIu64
", unable to set current thread value",
518 WINPR_CXX_COMPAT_CAST(uint64_t, pthread_self()));
522 if (!(fkt = thread->lpStartAddress))
526 LPTHREAD_START_ROUTINE fkt;
530 WLog_ERR(TAG,
"Thread function argument is %p", cnv.pv);
534 if (!signal_and_wait_for_ready(thread))
537 rc = fkt(thread->lpParameter);
542 apc_cleanupThread(thread);
545 thread->dwExitCode = rc;
549 (void)signal_thread_ready(thread);
551 if (thread->detached || !thread->started)
552 cleanup_handle(thread);
558static BOOL winpr_StartThread(WINPR_THREAD* thread)
562 pthread_attr_t attr = WINPR_C_ARRAY_INIT;
564 if (!mux_condition_bundle_lock(&thread->isCreated))
568 pthread_attr_init(&attr);
569 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
571 if (thread->dwStackSize > 0)
572 pthread_attr_setstacksize(&attr, thread->dwStackSize);
574 thread->started = TRUE;
577#if defined(WITH_THREAD_LIST)
578 if (!ListDictionary_Add(thread_list, &thread->thread, thread))
580 WLog_ERR(TAG,
"failed to add the thread to the thread list");
585 if (pthread_create(&thread->thread, &attr, thread_launcher, thread))
588 if (!mux_condition_bundle_wait(&thread->isCreated,
"threadIsCreated"))
592 if (!mux_condition_bundle_unlock(&thread->isCreated))
595 if (!signal_thread_is_running(thread))
597 WLog_ERR(TAG,
"failed to signal the thread was ready");
605 if (!mux_condition_bundle_unlock(&thread->isCreated))
609 pthread_attr_destroy(&attr);
617BOOL SetThreadPriority(HANDLE hThread,
int nPriority)
622 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
627 const int diff = (max - min);
628 const int normal = min + diff / 2;
629 const int off = MIN(1, diff / 4);
630 int sched_priority = -1;
632 switch (nPriority & ~(THREAD_MODE_BACKGROUND_BEGIN | THREAD_MODE_BACKGROUND_END))
634 case THREAD_PRIORITY_ABOVE_NORMAL:
635 sched_priority = MIN(normal + off, max);
637 case THREAD_PRIORITY_BELOW_NORMAL:
638 sched_priority = MAX(normal - off, min);
640 case THREAD_PRIORITY_HIGHEST:
641 sched_priority = max;
643 case THREAD_PRIORITY_IDLE:
644 sched_priority = min;
646 case THREAD_PRIORITY_LOWEST:
647 sched_priority = min;
649 case THREAD_PRIORITY_TIME_CRITICAL:
650 sched_priority = max;
653 case THREAD_PRIORITY_NORMAL:
654 sched_priority = normal;
657#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L) && defined(PTHREAD_SETSCHEDPRIO)
658 WINPR_THREAD* thread = (WINPR_THREAD*)Object;
659 const int rc = pthread_setschedprio(thread->thread, sched_priority);
662 char buffer[256] = WINPR_C_ARRAY_INIT;
663 WLog_ERR(TAG,
"pthread_setschedprio(%d) %s [%d]", sched_priority,
664 winpr_strerror(rc, buffer,
sizeof(buffer)), rc);
668 WLog_WARN(TAG,
"pthread_setschedprio(%d) not implemented, requires POSIX 2008 or later",
674HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,
size_t dwStackSize,
675 LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter,
676 DWORD dwCreationFlags, WINPR_ATTR_UNUSED LPDWORD lpThreadId)
678 HANDLE handle =
nullptr;
679 WINPR_THREAD* thread = (WINPR_THREAD*)calloc(1,
sizeof(WINPR_THREAD));
684 thread->dwStackSize = dwStackSize;
685 thread->lpParameter = lpParameter;
686 thread->lpStartAddress = lpStartAddress;
687 thread->lpThreadAttributes = lpThreadAttributes;
688 thread->common.ops = &ops;
689#if defined(WITH_DEBUG_THREADS)
690 thread->create_stack = winpr_backtrace(20);
694 if (!winpr_event_init(&thread->event))
696 WLog_ERR(TAG,
"failed to create event");
700 if (!run_mutex_init(pthread_mutex_init, &thread->mutex,
nullptr))
702 WLog_ERR(TAG,
"failed to initialize thread mutex");
706 if (!apc_init(&thread->apc))
708 WLog_ERR(TAG,
"failed to initialize APC");
712 if (!mux_condition_bundle_init(&thread->isCreated))
714 if (!mux_condition_bundle_init(&thread->isRunning))
717 WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ);
718 handle = (HANDLE)thread;
720 if (!InitOnceExecuteOnce(&threads_InitOnce, initializeThreads,
nullptr,
nullptr))
723 if (!(dwCreationFlags & CREATE_SUSPENDED))
725 if (!winpr_StartThread(thread))
730 if (!set_event(thread))
736 cleanup_handle(thread);
740void cleanup_handle(
void* obj)
742 WINPR_THREAD* thread = (WINPR_THREAD*)obj;
746 if (!apc_uninit(&thread->apc))
747 WLog_ERR(TAG,
"failed to destroy APC");
749 mux_condition_bundle_uninit(&thread->isCreated);
750 mux_condition_bundle_uninit(&thread->isRunning);
751 run_mutex_fkt(pthread_mutex_destroy, &thread->mutex);
753 winpr_event_uninit(&thread->event);
755#if defined(WITH_THREAD_LIST)
756 ListDictionary_Remove(thread_list, &thread->thread);
758#if defined(WITH_DEBUG_THREADS)
760 if (thread->create_stack)
761 winpr_backtrace_free(thread->create_stack);
763 if (thread->exit_stack)
764 winpr_backtrace_free(thread->exit_stack);
770BOOL ThreadCloseHandle(HANDLE handle)
772 WINPR_THREAD* thread = (WINPR_THREAD*)handle;
774#if defined(WITH_THREAD_LIST)
777 WLog_ERR(TAG,
"Thread list does not exist, check call!");
780 else if (!ListDictionary_Contains(thread_list, &thread->thread))
782 WLog_ERR(TAG,
"Thread list does not contain this thread! check call!");
787 ListDictionary_Lock(thread_list);
791 if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0))
793 WLog_DBG(TAG,
"Thread running, setting to detached state!");
794 thread->detached = TRUE;
795 pthread_detach(thread->thread);
799 cleanup_handle(thread);
802#if defined(WITH_THREAD_LIST)
803 ListDictionary_Unlock(thread_list);
810HANDLE CreateRemoteThread(WINPR_ATTR_UNUSED HANDLE hProcess,
811 WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpThreadAttributes,
812 WINPR_ATTR_UNUSED
size_t dwStackSize,
813 WINPR_ATTR_UNUSED LPTHREAD_START_ROUTINE lpStartAddress,
814 WINPR_ATTR_UNUSED LPVOID lpParameter,
815 WINPR_ATTR_UNUSED DWORD dwCreationFlags,
816 WINPR_ATTR_UNUSED LPDWORD lpThreadId)
818 WLog_ERR(TAG,
"not implemented");
819 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
823VOID ExitThread(DWORD dwExitCode)
825#if defined(WITH_THREAD_LIST)
827 pthread_t tid = pthread_self();
831 WLog_ERR(TAG,
"function called without existing thread list!");
832#if defined(WITH_DEBUG_THREADS)
837 else if (!ListDictionary_Contains(thread_list, &tid))
839 WLog_ERR(TAG,
"function called, but no matching entry in thread list!");
840#if defined(WITH_DEBUG_THREADS)
847 WINPR_THREAD* thread;
848 ListDictionary_Lock(thread_list);
849 thread = ListDictionary_GetItemValue(thread_list, &tid);
850 WINPR_ASSERT(thread);
851 thread->exited = TRUE;
852 thread->dwExitCode = dwExitCode;
853#if defined(WITH_DEBUG_THREADS)
854 thread->exit_stack = winpr_backtrace(20);
856 ListDictionary_Unlock(thread_list);
858 rc = thread->dwExitCode;
860 if (thread->detached || !thread->started)
861 cleanup_handle(thread);
863 pthread_exit((
void*)(
size_t)rc);
866 WINPR_UNUSED(dwExitCode);
870BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)
874 WINPR_THREAD* thread =
nullptr;
876 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
878 WLog_ERR(TAG,
"hThread is not a thread");
879 SetLastError(ERROR_INVALID_PARAMETER);
883 thread = (WINPR_THREAD*)Object;
884 *lpExitCode = thread->dwExitCode;
888WINPR_THREAD* winpr_GetCurrentThread(VOID)
890 WINPR_THREAD* ret =
nullptr;
892 if (!InitOnceExecuteOnce(&threads_InitOnce, initializeThreads,
nullptr,
nullptr))
894 if (mainThreadId == pthread_self())
895 return (HANDLE)&mainThread;
897 ret = TlsGetValue(currentThreadTlsIndex);
901HANDLE _GetCurrentThread(VOID)
903 return (HANDLE)winpr_GetCurrentThread();
906DWORD GetCurrentThreadId(VOID)
908#if defined(__FreeBSD__)
909 return WINPR_CXX_COMPAT_CAST(DWORD, pthread_getthreadid_np());
910#elif defined(__linux__)
911 return WINPR_CXX_COMPAT_CAST(DWORD, syscall(SYS_gettid));
913 pthread_t tid = pthread_self();
916 uintptr_t ptid = WINPR_REINTERPRET_CAST(tid, pthread_t, uintptr_t);
917 return (ptid & UINT32_MAX) ^ (ptid >> 32);
925 ULONG_PTR completionArg;
928static void userAPC(LPVOID arg)
930 UserApcItem* userApc = (UserApcItem*)arg;
932 userApc->completion(userApc->completionArg);
934 userApc->apc.markedForRemove = TRUE;
937DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData)
941 WINPR_APC_ITEM* apc =
nullptr;
942 UserApcItem* apcItem =
nullptr;
947 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
949 WLog_ERR(TAG,
"hThread is not a thread");
950 SetLastError(ERROR_INVALID_PARAMETER);
954 apcItem = calloc(1,
sizeof(*apcItem));
957 SetLastError(ERROR_INVALID_PARAMETER);
962 apc->type = APC_TYPE_USER;
963 apc->markedForFree = TRUE;
964 apc->alwaysSignaled = TRUE;
965 apc->completion = userAPC;
966 apc->completionArgs = apc;
967 apcItem->completion = pfnAPC;
968 apcItem->completionArg = dwData;
969 apc_register(hThread, apc);
973DWORD ResumeThread(HANDLE hThread)
977 WINPR_THREAD* thread =
nullptr;
979 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
981 WLog_ERR(TAG,
"hThread is not a thread");
982 SetLastError(ERROR_INVALID_PARAMETER);
986 thread = (WINPR_THREAD*)Object;
988 if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
991 if (!thread->started)
993 if (!winpr_StartThread(thread))
995 run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex);
1000 WLog_WARN(TAG,
"Thread already started!");
1002 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1008DWORD SuspendThread(WINPR_ATTR_UNUSED HANDLE hThread)
1010 WLog_ERR(TAG,
"not implemented");
1011 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1015BOOL SwitchToThread(VOID)
1021 if (sched_yield() != 0)
1027BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode)
1031 WINPR_THREAD* thread =
nullptr;
1033 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
1036 thread = (WINPR_THREAD*)Object;
1037 thread->exited = TRUE;
1038 thread->dwExitCode = dwExitCode;
1040 if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
1044 pthread_cancel(thread->thread);
1046 WLog_ERR(TAG,
"Function not supported on this platform!");
1049 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1056VOID DumpThreadHandles(
void)
1058#if defined(WITH_DEBUG_THREADS)
1059 char** msg =
nullptr;
1061 void* stack = winpr_backtrace(20);
1062 WLog_DBG(TAG,
"---------------- Called from ----------------------------");
1063 msg = winpr_backtrace_symbols(stack, &used);
1065 for (
size_t i = 0; i < used; i++)
1067 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
1071 winpr_backtrace_free(stack);
1072 WLog_DBG(TAG,
"---------------- Start Dumping thread handles -----------");
1074#if defined(WITH_THREAD_LIST)
1077 WLog_DBG(TAG,
"All threads properly shut down and disposed of.");
1081 ULONG_PTR* keys =
nullptr;
1082 ListDictionary_Lock(thread_list);
1083 int x, count = ListDictionary_GetKeys(thread_list, &keys);
1084 WLog_DBG(TAG,
"Dumping %d elements", count);
1086 for (
size_t x = 0; x < count; x++)
1088 WINPR_THREAD* thread = ListDictionary_GetItemValue(thread_list, (
void*)keys[x]);
1089 WLog_DBG(TAG,
"Thread [%d] handle created still not closed!", x);
1090 msg = winpr_backtrace_symbols(thread->create_stack, &used);
1092 for (
size_t i = 0; i < used; i++)
1094 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
1099 if (thread->started)
1101 WLog_DBG(TAG,
"Thread [%d] still running!", x);
1105 WLog_DBG(TAG,
"Thread [%d] exited at:", x);
1106 msg = winpr_backtrace_symbols(thread->exit_stack, &used);
1108 for (
size_t i = 0; i < used; i++)
1109 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
1116 ListDictionary_Unlock(thread_list);
1120 WLog_DBG(TAG,
"---------------- End Dumping thread handles -------------");