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 = NULL;
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] = { 0 };
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] = { 0 };
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] = { 0 };
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] = { 0 };
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, NULL))
226 if (!run_cond_init(pthread_cond_init, &bundle->cond, NULL))
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] = { 0 };
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, NULL);
336 char ebuffer[256] = { 0 };
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))
376static void dump_thread(WINPR_THREAD* thread)
378#if defined(WITH_DEBUG_THREADS)
379 void* stack = winpr_backtrace(20);
382 WLog_DBG(TAG,
"Called from:");
383 msg = winpr_backtrace_symbols(stack, &used);
385 for (
size_t i = 0; i < used; i++)
386 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
389 winpr_backtrace_free(stack);
390 WLog_DBG(TAG,
"Thread handle created still not closed!");
391 msg = winpr_backtrace_symbols(thread->create_stack, &used);
393 for (
size_t i = 0; i < used; i++)
394 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
400 WLog_DBG(TAG,
"Thread still running!");
402 else if (!thread->exit_stack)
404 WLog_DBG(TAG,
"Thread suspended.");
408 WLog_DBG(TAG,
"Thread exited at:");
409 msg = winpr_backtrace_symbols(thread->exit_stack, &used);
411 for (
size_t i = 0; i < used; i++)
412 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
417 WINPR_UNUSED(thread);
425static BOOL set_event(WINPR_THREAD* thread)
427 return winpr_event_set(&thread->event);
430static BOOL reset_event(WINPR_THREAD* thread)
432 return winpr_event_reset(&thread->event);
435#if defined(WITH_THREAD_LIST)
436static BOOL thread_compare(
const void* a,
const void* b)
438 const pthread_t* p1 = a;
439 const pthread_t* p2 = b;
440 BOOL rc = pthread_equal(*p1, *p2);
445static INIT_ONCE threads_InitOnce = INIT_ONCE_STATIC_INIT;
446static pthread_t mainThreadId;
447static DWORD currentThreadTlsIndex = TLS_OUT_OF_INDEXES;
449static BOOL initializeThreads(WINPR_ATTR_UNUSED
PINIT_ONCE InitOnce,
450 WINPR_ATTR_UNUSED PVOID Parameter, WINPR_ATTR_UNUSED PVOID* Context)
452 if (!apc_init(&mainThread.apc))
454 WLog_ERR(TAG,
"failed to initialize APC");
458 mainThread.common.Type = HANDLE_TYPE_THREAD;
459 mainThreadId = pthread_self();
461 currentThreadTlsIndex = TlsAlloc();
462 if (currentThreadTlsIndex == TLS_OUT_OF_INDEXES)
464 WLog_ERR(TAG,
"Major bug, unable to allocate a TLS value for currentThread");
467#if defined(WITH_THREAD_LIST)
468 thread_list = ListDictionary_New(TRUE);
472 WLog_ERR(TAG,
"Couldn't create global thread list");
473 goto error_thread_list;
476 thread_list->objectKey.fnObjectEquals = thread_compare;
483static BOOL signal_and_wait_for_ready(WINPR_THREAD* thread)
487 WINPR_ASSERT(thread);
489 if (!mux_condition_bundle_lock(&thread->isRunning))
492 if (!signal_thread_ready(thread))
495 if (!mux_condition_bundle_wait(&thread->isRunning,
"threadIsRunning"))
498#if defined(WITH_THREAD_LIST)
499 if (!ListDictionary_Contains(thread_list, &thread->thread))
501 WLog_ERR(TAG,
"Thread not in thread_list, startup failed!");
509 if (!mux_condition_bundle_unlock(&thread->isRunning))
518static void* thread_launcher(
void* arg)
521 WINPR_THREAD* thread = (WINPR_THREAD*)arg;
522 LPTHREAD_START_ROUTINE fkt = NULL;
526 WLog_ERR(TAG,
"Called with invalid argument %p", arg);
530 if (!TlsSetValue(currentThreadTlsIndex, thread))
532 WLog_ERR(TAG,
"thread %" PRIu64
", unable to set current thread value",
533 WINPR_CXX_COMPAT_CAST(uint64_t, pthread_self()));
537 if (!(fkt = thread->lpStartAddress))
541 LPTHREAD_START_ROUTINE fkt;
545 WLog_ERR(TAG,
"Thread function argument is %p", cnv.pv);
549 if (!signal_and_wait_for_ready(thread))
552 rc = fkt(thread->lpParameter);
557 apc_cleanupThread(thread);
560 thread->dwExitCode = rc;
564 (void)signal_thread_ready(thread);
566 if (thread->detached || !thread->started)
567 cleanup_handle(thread);
573static BOOL winpr_StartThread(WINPR_THREAD* thread)
577 pthread_attr_t attr = { 0 };
579 if (!mux_condition_bundle_lock(&thread->isCreated))
583 pthread_attr_init(&attr);
584 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
586 if (thread->dwStackSize > 0)
587 pthread_attr_setstacksize(&attr, thread->dwStackSize);
589 thread->started = TRUE;
592#if defined(WITH_THREAD_LIST)
593 if (!ListDictionary_Add(thread_list, &thread->thread, thread))
595 WLog_ERR(TAG,
"failed to add the thread to the thread list");
600 if (pthread_create(&thread->thread, &attr, thread_launcher, thread))
603 if (!mux_condition_bundle_wait(&thread->isCreated,
"threadIsCreated"))
607 if (!mux_condition_bundle_unlock(&thread->isCreated))
610 if (!signal_thread_is_running(thread))
612 WLog_ERR(TAG,
"failed to signal the thread was ready");
620 if (!mux_condition_bundle_unlock(&thread->isCreated))
624 pthread_attr_destroy(&attr);
632BOOL SetThreadPriority(HANDLE hThread,
int nPriority)
637 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
642 const int diff = (max - min);
643 const int normal = min + diff / 2;
644 const int off = MIN(1, diff / 4);
645 int sched_priority = -1;
647 switch (nPriority & ~(THREAD_MODE_BACKGROUND_BEGIN | THREAD_MODE_BACKGROUND_END))
649 case THREAD_PRIORITY_ABOVE_NORMAL:
650 sched_priority = MIN(normal + off, max);
652 case THREAD_PRIORITY_BELOW_NORMAL:
653 sched_priority = MAX(normal - off, min);
655 case THREAD_PRIORITY_HIGHEST:
656 sched_priority = max;
658 case THREAD_PRIORITY_IDLE:
659 sched_priority = min;
661 case THREAD_PRIORITY_LOWEST:
662 sched_priority = min;
664 case THREAD_PRIORITY_TIME_CRITICAL:
665 sched_priority = max;
668 case THREAD_PRIORITY_NORMAL:
669 sched_priority = normal;
672#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L) && defined(PTHREAD_SETSCHEDPRIO)
673 WINPR_THREAD* thread = (WINPR_THREAD*)Object;
674 const int rc = pthread_setschedprio(thread->thread, sched_priority);
677 char buffer[256] = { 0 };
678 WLog_ERR(TAG,
"pthread_setschedprio(%d) %s [%d]", sched_priority,
679 winpr_strerror(rc, buffer,
sizeof(buffer)), rc);
683 WLog_WARN(TAG,
"pthread_setschedprio(%d) not implemented, requires POSIX 2008 or later",
689HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,
size_t dwStackSize,
690 LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter,
691 DWORD dwCreationFlags, WINPR_ATTR_UNUSED LPDWORD lpThreadId)
693 HANDLE handle = NULL;
694 WINPR_THREAD* thread = (WINPR_THREAD*)calloc(1,
sizeof(WINPR_THREAD));
699 thread->dwStackSize = dwStackSize;
700 thread->lpParameter = lpParameter;
701 thread->lpStartAddress = lpStartAddress;
702 thread->lpThreadAttributes = lpThreadAttributes;
703 thread->common.ops = &ops;
704#if defined(WITH_DEBUG_THREADS)
705 thread->create_stack = winpr_backtrace(20);
709 if (!winpr_event_init(&thread->event))
711 WLog_ERR(TAG,
"failed to create event");
715 if (!run_mutex_init(pthread_mutex_init, &thread->mutex, NULL))
717 WLog_ERR(TAG,
"failed to initialize thread mutex");
721 if (!apc_init(&thread->apc))
723 WLog_ERR(TAG,
"failed to initialize APC");
727 if (!mux_condition_bundle_init(&thread->isCreated))
729 if (!mux_condition_bundle_init(&thread->isRunning))
732 WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ);
733 handle = (HANDLE)thread;
735 InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
737 if (!(dwCreationFlags & CREATE_SUSPENDED))
739 if (!winpr_StartThread(thread))
744 if (!set_event(thread))
750 cleanup_handle(thread);
754void cleanup_handle(
void* obj)
756 WINPR_THREAD* thread = (WINPR_THREAD*)obj;
760 if (!apc_uninit(&thread->apc))
761 WLog_ERR(TAG,
"failed to destroy APC");
763 mux_condition_bundle_uninit(&thread->isCreated);
764 mux_condition_bundle_uninit(&thread->isRunning);
765 run_mutex_fkt(pthread_mutex_destroy, &thread->mutex);
767 winpr_event_uninit(&thread->event);
769#if defined(WITH_THREAD_LIST)
770 ListDictionary_Remove(thread_list, &thread->thread);
772#if defined(WITH_DEBUG_THREADS)
774 if (thread->create_stack)
775 winpr_backtrace_free(thread->create_stack);
777 if (thread->exit_stack)
778 winpr_backtrace_free(thread->exit_stack);
784BOOL ThreadCloseHandle(HANDLE handle)
786 WINPR_THREAD* thread = (WINPR_THREAD*)handle;
788#if defined(WITH_THREAD_LIST)
791 WLog_ERR(TAG,
"Thread list does not exist, check call!");
794 else if (!ListDictionary_Contains(thread_list, &thread->thread))
796 WLog_ERR(TAG,
"Thread list does not contain this thread! check call!");
801 ListDictionary_Lock(thread_list);
805 if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0))
807 WLog_DBG(TAG,
"Thread running, setting to detached state!");
808 thread->detached = TRUE;
809 pthread_detach(thread->thread);
813 cleanup_handle(thread);
816#if defined(WITH_THREAD_LIST)
817 ListDictionary_Unlock(thread_list);
824HANDLE CreateRemoteThread(WINPR_ATTR_UNUSED HANDLE hProcess,
825 WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpThreadAttributes,
826 WINPR_ATTR_UNUSED
size_t dwStackSize,
827 WINPR_ATTR_UNUSED LPTHREAD_START_ROUTINE lpStartAddress,
828 WINPR_ATTR_UNUSED LPVOID lpParameter,
829 WINPR_ATTR_UNUSED DWORD dwCreationFlags,
830 WINPR_ATTR_UNUSED LPDWORD lpThreadId)
832 WLog_ERR(TAG,
"not implemented");
833 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
837VOID ExitThread(DWORD dwExitCode)
839#if defined(WITH_THREAD_LIST)
841 pthread_t tid = pthread_self();
845 WLog_ERR(TAG,
"function called without existing thread list!");
846#if defined(WITH_DEBUG_THREADS)
851 else if (!ListDictionary_Contains(thread_list, &tid))
853 WLog_ERR(TAG,
"function called, but no matching entry in thread list!");
854#if defined(WITH_DEBUG_THREADS)
861 WINPR_THREAD* thread;
862 ListDictionary_Lock(thread_list);
863 thread = ListDictionary_GetItemValue(thread_list, &tid);
864 WINPR_ASSERT(thread);
865 thread->exited = TRUE;
866 thread->dwExitCode = dwExitCode;
867#if defined(WITH_DEBUG_THREADS)
868 thread->exit_stack = winpr_backtrace(20);
870 ListDictionary_Unlock(thread_list);
872 rc = thread->dwExitCode;
874 if (thread->detached || !thread->started)
875 cleanup_handle(thread);
877 pthread_exit((
void*)(
size_t)rc);
880 WINPR_UNUSED(dwExitCode);
884BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)
888 WINPR_THREAD* thread = NULL;
890 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
892 WLog_ERR(TAG,
"hThread is not a thread");
893 SetLastError(ERROR_INVALID_PARAMETER);
897 thread = (WINPR_THREAD*)Object;
898 *lpExitCode = thread->dwExitCode;
902WINPR_THREAD* winpr_GetCurrentThread(VOID)
904 WINPR_THREAD* ret = NULL;
906 InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
907 if (mainThreadId == pthread_self())
908 return (HANDLE)&mainThread;
910 ret = TlsGetValue(currentThreadTlsIndex);
914HANDLE _GetCurrentThread(VOID)
916 return (HANDLE)winpr_GetCurrentThread();
919DWORD GetCurrentThreadId(VOID)
921#if defined(__FreeBSD__)
922 return WINPR_CXX_COMPAT_CAST(DWORD, pthread_getthreadid_np());
923#elif defined(__linux__)
924 return WINPR_CXX_COMPAT_CAST(DWORD, syscall(SYS_gettid));
926 pthread_t tid = pthread_self();
929 uintptr_t ptid = WINPR_REINTERPRET_CAST(tid, pthread_t, uintptr_t);
930 return (ptid & UINT32_MAX) ^ (ptid >> 32);
938 ULONG_PTR completionArg;
941static void userAPC(LPVOID arg)
943 UserApcItem* userApc = (UserApcItem*)arg;
945 userApc->completion(userApc->completionArg);
947 userApc->apc.markedForRemove = TRUE;
950DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData)
954 WINPR_APC_ITEM* apc = NULL;
955 UserApcItem* apcItem = NULL;
960 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
962 WLog_ERR(TAG,
"hThread is not a thread");
963 SetLastError(ERROR_INVALID_PARAMETER);
967 apcItem = calloc(1,
sizeof(*apcItem));
970 SetLastError(ERROR_INVALID_PARAMETER);
975 apc->type = APC_TYPE_USER;
976 apc->markedForFree = TRUE;
977 apc->alwaysSignaled = TRUE;
978 apc->completion = userAPC;
979 apc->completionArgs = apc;
980 apcItem->completion = pfnAPC;
981 apcItem->completionArg = dwData;
982 apc_register(hThread, apc);
986DWORD ResumeThread(HANDLE hThread)
990 WINPR_THREAD* thread = NULL;
992 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
994 WLog_ERR(TAG,
"hThread is not a thread");
995 SetLastError(ERROR_INVALID_PARAMETER);
999 thread = (WINPR_THREAD*)Object;
1001 if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
1004 if (!thread->started)
1006 if (!winpr_StartThread(thread))
1008 run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex);
1013 WLog_WARN(TAG,
"Thread already started!");
1015 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1021DWORD SuspendThread(WINPR_ATTR_UNUSED HANDLE hThread)
1023 WLog_ERR(TAG,
"not implemented");
1024 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1028BOOL SwitchToThread(VOID)
1034 if (sched_yield() != 0)
1040BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode)
1044 WINPR_THREAD* thread = NULL;
1046 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
1049 thread = (WINPR_THREAD*)Object;
1050 thread->exited = TRUE;
1051 thread->dwExitCode = dwExitCode;
1053 if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
1057 pthread_cancel(thread->thread);
1059 WLog_ERR(TAG,
"Function not supported on this platform!");
1062 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1069VOID DumpThreadHandles(
void)
1071#if defined(WITH_DEBUG_THREADS)
1074 void* stack = winpr_backtrace(20);
1075 WLog_DBG(TAG,
"---------------- Called from ----------------------------");
1076 msg = winpr_backtrace_symbols(stack, &used);
1078 for (
size_t i = 0; i < used; i++)
1080 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
1084 winpr_backtrace_free(stack);
1085 WLog_DBG(TAG,
"---------------- Start Dumping thread handles -----------");
1087#if defined(WITH_THREAD_LIST)
1090 WLog_DBG(TAG,
"All threads properly shut down and disposed of.");
1094 ULONG_PTR* keys = NULL;
1095 ListDictionary_Lock(thread_list);
1096 int x, count = ListDictionary_GetKeys(thread_list, &keys);
1097 WLog_DBG(TAG,
"Dumping %d elements", count);
1099 for (
size_t x = 0; x < count; x++)
1101 WINPR_THREAD* thread = ListDictionary_GetItemValue(thread_list, (
void*)keys[x]);
1102 WLog_DBG(TAG,
"Thread [%d] handle created still not closed!", x);
1103 msg = winpr_backtrace_symbols(thread->create_stack, &used);
1105 for (
size_t i = 0; i < used; i++)
1107 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
1112 if (thread->started)
1114 WLog_DBG(TAG,
"Thread [%d] still running!", x);
1118 WLog_DBG(TAG,
"Thread [%d] exited at:", x);
1119 msg = winpr_backtrace_symbols(thread->exit_stack, &used);
1121 for (
size_t i = 0; i < used; i++)
1122 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
1129 ListDictionary_Unlock(thread_list);
1133 WLog_DBG(TAG,
"---------------- End Dumping thread handles -------------");