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))
90#include <winpr/platform.h>
93#ifdef WINPR_HAVE_UNISTD_H
97#ifdef WINPR_HAVE_SYS_EVENTFD_H
98#include <sys/eventfd.h>
101#include <winpr/debug.h>
106#include <winpr/collections.h>
111#include "../handle/handle.h"
113#define TAG WINPR_TAG("thread")
115static WINPR_THREAD mainThread;
117#if defined(WITH_THREAD_LIST)
118static wListDictionary* thread_list =
nullptr;
121static BOOL ThreadCloseHandle(HANDLE handle);
122static void cleanup_handle(
void* obj);
124static BOOL ThreadIsHandled(HANDLE handle)
126 return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_THREAD, FALSE);
129static int ThreadGetFd(HANDLE handle)
131 WINPR_THREAD* pThread = (WINPR_THREAD*)handle;
133 if (!ThreadIsHandled(handle))
136 return pThread->event.fds[0];
139#define run_mutex_init(fkt, mux, arg) run_mutex_init_(fkt, #fkt, mux, arg)
140static BOOL run_mutex_init_(
int (*fkt)(pthread_mutex_t*,
const pthread_mutexattr_t*),
141 const char* name, pthread_mutex_t* mutex,
142 const pthread_mutexattr_t* mutexattr)
149 rc = fkt(mutex, mutexattr);
152 char ebuffer[256] = WINPR_C_ARRAY_INIT;
153 WLog_WARN(TAG,
"[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer,
sizeof(ebuffer)));
158#define run_mutex_fkt(fkt, mux) run_mutex_fkt_(fkt, #fkt, mux)
159static BOOL run_mutex_fkt_(
int (*fkt)(pthread_mutex_t* mux),
const char* name,
160 pthread_mutex_t* mutex)
170 char ebuffer[256] = WINPR_C_ARRAY_INIT;
171 WLog_WARN(TAG,
"[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer,
sizeof(ebuffer)));
176#define run_cond_init(fkt, cond, arg) run_cond_init_(fkt, #fkt, cond, arg)
177static BOOL run_cond_init_(
int (*fkt)(pthread_cond_t*,
const pthread_condattr_t*),
const char* name,
178 pthread_cond_t* condition,
const pthread_condattr_t* conditionattr)
183 WINPR_ASSERT(condition);
185 rc = fkt(condition, conditionattr);
188 char ebuffer[256] = WINPR_C_ARRAY_INIT;
189 WLog_WARN(TAG,
"[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer,
sizeof(ebuffer)));
194#define run_cond_fkt(fkt, cond) run_cond_fkt_(fkt, #fkt, cond)
195static BOOL run_cond_fkt_(
int (*fkt)(pthread_cond_t* mux),
const char* name,
196 pthread_cond_t* condition)
201 WINPR_ASSERT(condition);
206 char ebuffer[256] = WINPR_C_ARRAY_INIT;
207 WLog_WARN(TAG,
"[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer,
sizeof(ebuffer)));
212static int pthread_mutex_checked_unlock(pthread_mutex_t* mutex)
215 WINPR_ASSERT(pthread_mutex_trylock(mutex) == EBUSY);
216 return pthread_mutex_unlock(mutex);
221 WINPR_ASSERT(bundle);
224 if (!run_mutex_init(pthread_mutex_init, &bundle->mux,
nullptr))
227 if (!run_cond_init(pthread_cond_init, &bundle->cond,
nullptr))
236 WINPR_ASSERT(bundle);
238 run_cond_fkt(pthread_cond_destroy, &bundle->cond);
239 run_mutex_fkt(pthread_mutex_destroy, &bundle->mux);
246 WINPR_ASSERT(bundle);
248 if (!run_mutex_fkt(pthread_mutex_lock, &bundle->mux))
251 if (!run_cond_fkt(pthread_cond_signal, &bundle->cond))
253 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux))
260 WINPR_ASSERT(bundle);
261 return run_mutex_fkt(pthread_mutex_lock, &bundle->mux);
266 WINPR_ASSERT(bundle);
267 return run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux);
274 WINPR_ASSERT(bundle);
276 WINPR_ASSERT(pthread_mutex_trylock(&bundle->mux) == EBUSY);
280 int r = pthread_cond_wait(&bundle->cond, &bundle->mux);
283 char ebuffer[256] = WINPR_C_ARRAY_INIT;
284 WLog_ERR(TAG,
"failed to wait for %s [%s]", name,
285 winpr_strerror(r, ebuffer,
sizeof(ebuffer)));
288 case ENOTRECOVERABLE:
306static BOOL signal_thread_ready(WINPR_THREAD* thread)
308 WINPR_ASSERT(thread);
310 return mux_condition_bundle_signal(&thread->isCreated);
313static BOOL signal_thread_is_running(WINPR_THREAD* thread)
315 WINPR_ASSERT(thread);
317 return mux_condition_bundle_signal(&thread->isRunning);
320static DWORD ThreadCleanupHandle(HANDLE handle)
322 DWORD status = WAIT_FAILED;
323 WINPR_THREAD* thread = (WINPR_THREAD*)handle;
325 if (!ThreadIsHandled(handle))
328 if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
333 int rc = pthread_join(thread->thread,
nullptr);
337 char ebuffer[256] = WINPR_C_ARRAY_INIT;
338 WLog_ERR(TAG,
"pthread_join failure: [%d] %s", rc,
339 winpr_strerror(rc, ebuffer,
sizeof(ebuffer)));
343 thread->joined = TRUE;
346 status = WAIT_OBJECT_0;
349 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
355static HANDLE_OPS ops = { ThreadIsHandled, ThreadCloseHandle, ThreadGetFd, ThreadCleanupHandle,
356 nullptr,
nullptr,
nullptr,
nullptr,
357 nullptr,
nullptr,
nullptr,
nullptr,
358 nullptr,
nullptr,
nullptr,
nullptr,
359 nullptr,
nullptr,
nullptr,
nullptr,
362static void dump_thread(WINPR_THREAD* thread)
364#if defined(WITH_DEBUG_THREADS)
365 void* stack = winpr_backtrace(20);
366 char** msg =
nullptr;
368 WLog_DBG(TAG,
"Called from:");
369 msg = winpr_backtrace_symbols(stack, &used);
371 for (
size_t i = 0; i < used; i++)
372 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
375 winpr_backtrace_free(stack);
376 WLog_DBG(TAG,
"Thread handle created still not closed!");
377 msg = winpr_backtrace_symbols(thread->create_stack, &used);
379 for (
size_t i = 0; i < used; i++)
380 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
386 WLog_DBG(TAG,
"Thread still running!");
388 else if (!thread->exit_stack)
390 WLog_DBG(TAG,
"Thread suspended.");
394 WLog_DBG(TAG,
"Thread exited at:");
395 msg = winpr_backtrace_symbols(thread->exit_stack, &used);
397 for (
size_t i = 0; i < used; i++)
398 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
403 WINPR_UNUSED(thread);
411static BOOL set_event(WINPR_THREAD* thread)
413 return winpr_event_set(&thread->event);
416static BOOL reset_event(WINPR_THREAD* thread)
418 return winpr_event_reset(&thread->event);
421#if defined(WITH_THREAD_LIST)
422static BOOL thread_compare(
const void* a,
const void* b)
424 const pthread_t* p1 = a;
425 const pthread_t* p2 = b;
426 BOOL rc = pthread_equal(*p1, *p2);
431static INIT_ONCE threads_InitOnce = INIT_ONCE_STATIC_INIT;
432static pthread_t mainThreadId;
433static pthread_key_t currentThreadTlsIndex = 0;
435static BOOL initializeThreads(WINPR_ATTR_UNUSED
PINIT_ONCE InitOnce,
436 WINPR_ATTR_UNUSED PVOID Parameter, WINPR_ATTR_UNUSED PVOID* Context)
438 if (!apc_init(&mainThread.apc))
440 WLog_ERR(TAG,
"failed to initialize APC");
444 mainThread.common.Type = HANDLE_TYPE_THREAD;
445 mainThreadId = pthread_self();
447 const int res = pthread_key_create(¤tThreadTlsIndex,
nullptr);
450 WLog_ERR(TAG,
"Major bug, unable to allocate a TLS value for currentThread");
454#if defined(WITH_THREAD_LIST)
455 thread_list = ListDictionary_New(TRUE);
459 WLog_ERR(TAG,
"Couldn't create global thread list");
460 goto error_thread_list;
463 thread_list->objectKey.fnObjectEquals = thread_compare;
470static BOOL signal_and_wait_for_ready(WINPR_THREAD* thread)
474 WINPR_ASSERT(thread);
476 if (!mux_condition_bundle_lock(&thread->isRunning))
479 if (!signal_thread_ready(thread))
482 if (!mux_condition_bundle_wait(&thread->isRunning,
"threadIsRunning"))
485#if defined(WITH_THREAD_LIST)
486 if (!ListDictionary_Contains(thread_list, &thread->thread))
488 WLog_ERR(TAG,
"Thread not in thread_list, startup failed!");
496 if (!mux_condition_bundle_unlock(&thread->isRunning))
505static void* thread_launcher(
void* arg)
508 WINPR_THREAD* thread = (WINPR_THREAD*)arg;
509 LPTHREAD_START_ROUTINE fkt =
nullptr;
513 WLog_ERR(TAG,
"Called with invalid argument %p", arg);
517 const int res = pthread_setspecific(currentThreadTlsIndex, thread);
520 WLog_ERR(TAG,
"thread %" PRIu64
", unable to set current thread value",
521 WINPR_CXX_COMPAT_CAST(uint64_t, pthread_self()));
525 if (!(fkt = thread->lpStartAddress))
529 LPTHREAD_START_ROUTINE fkt;
533 WLog_ERR(TAG,
"Thread function argument is %p", cnv.pv);
537 if (!signal_and_wait_for_ready(thread))
540 rc = fkt(thread->lpParameter);
545 apc_cleanupThread(thread);
548 thread->dwExitCode = rc;
552 (void)signal_thread_ready(thread);
554 if (thread->detached || !thread->started)
555 cleanup_handle(thread);
561static BOOL winpr_StartThread(WINPR_THREAD* thread)
565 pthread_attr_t attr = WINPR_C_ARRAY_INIT;
567 if (!mux_condition_bundle_lock(&thread->isCreated))
571 pthread_attr_init(&attr);
572 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
574 if (thread->dwStackSize > 0)
575 pthread_attr_setstacksize(&attr, thread->dwStackSize);
577 thread->started = TRUE;
580#if defined(WITH_THREAD_LIST)
581 if (!ListDictionary_Add(thread_list, &thread->thread, thread))
583 WLog_ERR(TAG,
"failed to add the thread to the thread list");
588 if (pthread_create(&thread->thread, &attr, thread_launcher, thread))
591 if (!mux_condition_bundle_wait(&thread->isCreated,
"threadIsCreated"))
595 if (!mux_condition_bundle_unlock(&thread->isCreated))
598 if (!signal_thread_is_running(thread))
600 WLog_ERR(TAG,
"failed to signal the thread was ready");
608 if (!mux_condition_bundle_unlock(&thread->isCreated))
612 pthread_attr_destroy(&attr);
620BOOL SetThreadPriority(HANDLE hThread,
int nPriority)
625 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
630 const int diff = (max - min);
631 const int normal = min + diff / 2;
632 const int off = MIN(1, diff / 4);
633 int sched_priority = -1;
635 switch (nPriority & ~(THREAD_MODE_BACKGROUND_BEGIN | THREAD_MODE_BACKGROUND_END))
637 case THREAD_PRIORITY_ABOVE_NORMAL:
638 sched_priority = MIN(normal + off, max);
640 case THREAD_PRIORITY_BELOW_NORMAL:
641 sched_priority = MAX(normal - off, min);
643 case THREAD_PRIORITY_HIGHEST:
644 sched_priority = max;
646 case THREAD_PRIORITY_IDLE:
647 sched_priority = min;
649 case THREAD_PRIORITY_LOWEST:
650 sched_priority = min;
652 case THREAD_PRIORITY_TIME_CRITICAL:
653 sched_priority = max;
656 case THREAD_PRIORITY_NORMAL:
657 sched_priority = normal;
660#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L) && defined(PTHREAD_SETSCHEDPRIO)
661 WINPR_THREAD* thread = (WINPR_THREAD*)Object;
662 const int rc = pthread_setschedprio(thread->thread, sched_priority);
665 char buffer[256] = WINPR_C_ARRAY_INIT;
666 WLog_ERR(TAG,
"pthread_setschedprio(%d) %s [%d]", sched_priority,
667 winpr_strerror(rc, buffer,
sizeof(buffer)), rc);
671 WLog_WARN(TAG,
"pthread_setschedprio(%d) not implemented, requires POSIX 2008 or later",
677HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,
size_t dwStackSize,
678 LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter,
679 DWORD dwCreationFlags, WINPR_ATTR_UNUSED LPDWORD lpThreadId)
681 HANDLE handle =
nullptr;
682 WINPR_THREAD* thread = (WINPR_THREAD*)calloc(1,
sizeof(WINPR_THREAD));
687 thread->dwStackSize = dwStackSize;
688 thread->lpParameter = lpParameter;
689 thread->lpStartAddress = lpStartAddress;
690 thread->lpThreadAttributes = lpThreadAttributes;
691 thread->common.ops = &ops;
692#if defined(WITH_DEBUG_THREADS)
693 thread->create_stack = winpr_backtrace(20);
697 if (!winpr_event_init(&thread->event))
699 WLog_ERR(TAG,
"failed to create event");
703 if (!run_mutex_init(pthread_mutex_init, &thread->mutex,
nullptr))
705 WLog_ERR(TAG,
"failed to initialize thread mutex");
709 if (!apc_init(&thread->apc))
711 WLog_ERR(TAG,
"failed to initialize APC");
715 if (!mux_condition_bundle_init(&thread->isCreated))
717 if (!mux_condition_bundle_init(&thread->isRunning))
720 WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ);
721 handle = (HANDLE)thread;
723 if (!InitOnceExecuteOnce(&threads_InitOnce, initializeThreads,
nullptr,
nullptr))
726 if (!(dwCreationFlags & CREATE_SUSPENDED))
728 if (!winpr_StartThread(thread))
733 if (!set_event(thread))
739 cleanup_handle(thread);
743void cleanup_handle(
void* obj)
745 WINPR_THREAD* thread = (WINPR_THREAD*)obj;
749 if (!apc_uninit(&thread->apc))
750 WLog_ERR(TAG,
"failed to destroy APC");
752 mux_condition_bundle_uninit(&thread->isCreated);
753 mux_condition_bundle_uninit(&thread->isRunning);
754 run_mutex_fkt(pthread_mutex_destroy, &thread->mutex);
756 winpr_event_uninit(&thread->event);
758#if defined(WITH_THREAD_LIST)
759 ListDictionary_Remove(thread_list, &thread->thread);
761#if defined(WITH_DEBUG_THREADS)
763 if (thread->create_stack)
764 winpr_backtrace_free(thread->create_stack);
766 if (thread->exit_stack)
767 winpr_backtrace_free(thread->exit_stack);
773BOOL ThreadCloseHandle(HANDLE handle)
775 WINPR_THREAD* thread = (WINPR_THREAD*)handle;
777#if defined(WITH_THREAD_LIST)
780 WLog_ERR(TAG,
"Thread list does not exist, check call!");
783 else if (!ListDictionary_Contains(thread_list, &thread->thread))
785 WLog_ERR(TAG,
"Thread list does not contain this thread! check call!");
790 ListDictionary_Lock(thread_list);
794 if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0))
796 WLog_DBG(TAG,
"Thread running, setting to detached state!");
797 thread->detached = TRUE;
798 pthread_detach(thread->thread);
802 cleanup_handle(thread);
805#if defined(WITH_THREAD_LIST)
806 ListDictionary_Unlock(thread_list);
813HANDLE CreateRemoteThread(WINPR_ATTR_UNUSED HANDLE hProcess,
814 WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpThreadAttributes,
815 WINPR_ATTR_UNUSED
size_t dwStackSize,
816 WINPR_ATTR_UNUSED LPTHREAD_START_ROUTINE lpStartAddress,
817 WINPR_ATTR_UNUSED LPVOID lpParameter,
818 WINPR_ATTR_UNUSED DWORD dwCreationFlags,
819 WINPR_ATTR_UNUSED LPDWORD lpThreadId)
821 WLog_ERR(TAG,
"not implemented");
822 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
826VOID ExitThread(DWORD dwExitCode)
828#if defined(WITH_THREAD_LIST)
830 pthread_t tid = pthread_self();
834 WLog_ERR(TAG,
"function called without existing thread list!");
835#if defined(WITH_DEBUG_THREADS)
840 else if (!ListDictionary_Contains(thread_list, &tid))
842 WLog_ERR(TAG,
"function called, but no matching entry in thread list!");
843#if defined(WITH_DEBUG_THREADS)
850 WINPR_THREAD* thread;
851 ListDictionary_Lock(thread_list);
852 thread = ListDictionary_GetItemValue(thread_list, &tid);
853 WINPR_ASSERT(thread);
854 thread->exited = TRUE;
855 thread->dwExitCode = dwExitCode;
856#if defined(WITH_DEBUG_THREADS)
857 thread->exit_stack = winpr_backtrace(20);
859 ListDictionary_Unlock(thread_list);
861 rc = thread->dwExitCode;
863 if (thread->detached || !thread->started)
864 cleanup_handle(thread);
866 pthread_exit((
void*)(
size_t)rc);
869 WINPR_UNUSED(dwExitCode);
873BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)
877 WINPR_THREAD* thread =
nullptr;
879 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
881 WLog_ERR(TAG,
"hThread is not a thread");
882 SetLastError(ERROR_INVALID_PARAMETER);
886 thread = (WINPR_THREAD*)Object;
887 *lpExitCode = thread->dwExitCode;
891WINPR_THREAD* winpr_GetCurrentThread(VOID)
893 WINPR_THREAD* ret =
nullptr;
895 if (!InitOnceExecuteOnce(&threads_InitOnce, initializeThreads,
nullptr,
nullptr))
897 if (mainThreadId == pthread_self())
898 return (HANDLE)&mainThread;
900 ret = pthread_getspecific(currentThreadTlsIndex);
904HANDLE _GetCurrentThread(VOID)
906 return (HANDLE)winpr_GetCurrentThread();
909DWORD GetCurrentThreadId(VOID)
911#if defined(__FreeBSD__)
912 return WINPR_CXX_COMPAT_CAST(DWORD, pthread_getthreadid_np());
913#elif defined(__OpenBSD__)
914 return WINPR_CXX_COMPAT_CAST(DWORD, getthrid());
915#elif defined(__linux__)
916 return WINPR_CXX_COMPAT_CAST(DWORD, syscall(SYS_gettid));
918 pthread_t tid = pthread_self();
921 uintptr_t ptid = WINPR_REINTERPRET_CAST(tid, pthread_t, uintptr_t);
922 return (ptid & UINT32_MAX) ^ (ptid >> 32);
930 ULONG_PTR completionArg;
933static void userAPC(LPVOID arg)
935 UserApcItem* userApc = (UserApcItem*)arg;
937 userApc->completion(userApc->completionArg);
939 userApc->apc.markedForRemove = TRUE;
942DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData)
946 WINPR_APC_ITEM* apc =
nullptr;
947 UserApcItem* apcItem =
nullptr;
952 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
954 WLog_ERR(TAG,
"hThread is not a thread");
955 SetLastError(ERROR_INVALID_PARAMETER);
959 apcItem = calloc(1,
sizeof(*apcItem));
962 SetLastError(ERROR_INVALID_PARAMETER);
967 apc->type = APC_TYPE_USER;
968 apc->markedForFree = TRUE;
969 apc->alwaysSignaled = TRUE;
970 apc->completion = userAPC;
971 apc->completionArgs = apc;
972 apcItem->completion = pfnAPC;
973 apcItem->completionArg = dwData;
974 apc_register(hThread, apc);
978DWORD ResumeThread(HANDLE hThread)
982 WINPR_THREAD* thread =
nullptr;
984 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
986 WLog_ERR(TAG,
"hThread is not a thread");
987 SetLastError(ERROR_INVALID_PARAMETER);
991 thread = (WINPR_THREAD*)Object;
993 if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
996 if (!thread->started)
998 if (!winpr_StartThread(thread))
1000 run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex);
1005 WLog_WARN(TAG,
"Thread already started!");
1007 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1013DWORD SuspendThread(WINPR_ATTR_UNUSED HANDLE hThread)
1015 WLog_ERR(TAG,
"not implemented");
1016 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1020BOOL SwitchToThread(VOID)
1026 if (sched_yield() != 0)
1032BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode)
1036 WINPR_THREAD* thread =
nullptr;
1038 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
1041 thread = (WINPR_THREAD*)Object;
1042 thread->exited = TRUE;
1043 thread->dwExitCode = dwExitCode;
1045 if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
1049 pthread_cancel(thread->thread);
1051 WLog_ERR(TAG,
"Function not supported on this platform!");
1054 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1061VOID DumpThreadHandles(
void)
1063#if defined(WITH_DEBUG_THREADS)
1064 char** msg =
nullptr;
1066 void* stack = winpr_backtrace(20);
1067 WLog_DBG(TAG,
"---------------- Called from ----------------------------");
1068 msg = winpr_backtrace_symbols(stack, &used);
1070 for (
size_t i = 0; i < used; i++)
1072 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
1076 winpr_backtrace_free(stack);
1077 WLog_DBG(TAG,
"---------------- Start Dumping thread handles -----------");
1079#if defined(WITH_THREAD_LIST)
1082 WLog_DBG(TAG,
"All threads properly shut down and disposed of.");
1086 ULONG_PTR* keys =
nullptr;
1087 ListDictionary_Lock(thread_list);
1088 int x, count = ListDictionary_GetKeys(thread_list, &keys);
1089 WLog_DBG(TAG,
"Dumping %d elements", count);
1091 for (
size_t x = 0; x < count; x++)
1093 WINPR_THREAD* thread = ListDictionary_GetItemValue(thread_list, (
void*)keys[x]);
1094 WLog_DBG(TAG,
"Thread [%d] handle created still not closed!", x);
1095 msg = winpr_backtrace_symbols(thread->create_stack, &used);
1097 for (
size_t i = 0; i < used; i++)
1099 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
1104 if (thread->started)
1106 WLog_DBG(TAG,
"Thread [%d] still running!", x);
1110 WLog_DBG(TAG,
"Thread [%d] exited at:", x);
1111 msg = winpr_backtrace_symbols(thread->exit_stack, &used);
1113 for (
size_t i = 0; i < used; i++)
1114 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
1121 ListDictionary_Unlock(thread_list);
1125 WLog_DBG(TAG,
"---------------- End Dumping thread handles -------------");