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>
33#define MIN(x, y) (((x) < (y)) ? (x) : (y))
37#define MAX(x, y) (((x) > (y)) ? (x) : (y))
83#include <winpr/platform.h>
86#ifdef WINPR_HAVE_UNISTD_H
90#ifdef WINPR_HAVE_SYS_EVENTFD_H
91#include <sys/eventfd.h>
94#include <winpr/debug.h>
99#include <winpr/collections.h>
104#include "../handle/handle.h"
106#define TAG WINPR_TAG("thread")
108static WINPR_THREAD mainThread;
110#if defined(WITH_THREAD_LIST)
111static wListDictionary* thread_list = NULL;
114static BOOL ThreadCloseHandle(HANDLE handle);
115static void cleanup_handle(
void* obj);
117static BOOL ThreadIsHandled(HANDLE handle)
119 return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_THREAD, FALSE);
122static int ThreadGetFd(HANDLE handle)
124 WINPR_THREAD* pThread = (WINPR_THREAD*)handle;
126 if (!ThreadIsHandled(handle))
129 return pThread->event.fds[0];
132#define run_mutex_init(fkt, mux, arg) run_mutex_init_(fkt, #fkt, mux, arg)
133static BOOL run_mutex_init_(
int (*fkt)(pthread_mutex_t*,
const pthread_mutexattr_t*),
134 const char* name, pthread_mutex_t* mutex,
135 const pthread_mutexattr_t* mutexattr)
142 rc = fkt(mutex, mutexattr);
145 char ebuffer[256] = { 0 };
146 WLog_WARN(TAG,
"[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer,
sizeof(ebuffer)));
151#define run_mutex_fkt(fkt, mux) run_mutex_fkt_(fkt, #fkt, mux)
152static BOOL run_mutex_fkt_(
int (*fkt)(pthread_mutex_t* mux),
const char* name,
153 pthread_mutex_t* mutex)
163 char ebuffer[256] = { 0 };
164 WLog_WARN(TAG,
"[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer,
sizeof(ebuffer)));
169#define run_cond_init(fkt, cond, arg) run_cond_init_(fkt, #fkt, cond, arg)
170static BOOL run_cond_init_(
int (*fkt)(pthread_cond_t*,
const pthread_condattr_t*),
const char* name,
171 pthread_cond_t* condition,
const pthread_condattr_t* conditionattr)
176 WINPR_ASSERT(condition);
178 rc = fkt(condition, conditionattr);
181 char ebuffer[256] = { 0 };
182 WLog_WARN(TAG,
"[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer,
sizeof(ebuffer)));
187#define run_cond_fkt(fkt, cond) run_cond_fkt_(fkt, #fkt, cond)
188static BOOL run_cond_fkt_(
int (*fkt)(pthread_cond_t* mux),
const char* name,
189 pthread_cond_t* condition)
194 WINPR_ASSERT(condition);
199 char ebuffer[256] = { 0 };
200 WLog_WARN(TAG,
"[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer,
sizeof(ebuffer)));
205static int pthread_mutex_checked_unlock(pthread_mutex_t* mutex)
208 WINPR_ASSERT(pthread_mutex_trylock(mutex) == EBUSY);
209 return pthread_mutex_unlock(mutex);
214 WINPR_ASSERT(bundle);
217 if (!run_mutex_init(pthread_mutex_init, &bundle->mux, NULL))
220 if (!run_cond_init(pthread_cond_init, &bundle->cond, NULL))
229 WINPR_ASSERT(bundle);
231 run_cond_fkt(pthread_cond_destroy, &bundle->cond);
232 run_mutex_fkt(pthread_mutex_destroy, &bundle->mux);
239 WINPR_ASSERT(bundle);
241 if (!run_mutex_fkt(pthread_mutex_lock, &bundle->mux))
244 if (!run_cond_fkt(pthread_cond_signal, &bundle->cond))
246 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux))
253 WINPR_ASSERT(bundle);
254 return run_mutex_fkt(pthread_mutex_lock, &bundle->mux);
259 WINPR_ASSERT(bundle);
260 return run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux);
267 WINPR_ASSERT(bundle);
269 WINPR_ASSERT(pthread_mutex_trylock(&bundle->mux) == EBUSY);
273 int r = pthread_cond_wait(&bundle->cond, &bundle->mux);
276 char ebuffer[256] = { 0 };
277 WLog_ERR(TAG,
"failed to wait for %s [%s]", name,
278 winpr_strerror(r, ebuffer,
sizeof(ebuffer)));
281 case ENOTRECOVERABLE:
299static BOOL signal_thread_ready(WINPR_THREAD* thread)
301 WINPR_ASSERT(thread);
303 return mux_condition_bundle_signal(&thread->isCreated);
306static BOOL signal_thread_is_running(WINPR_THREAD* thread)
308 WINPR_ASSERT(thread);
310 return mux_condition_bundle_signal(&thread->isRunning);
313static DWORD ThreadCleanupHandle(HANDLE handle)
315 DWORD status = WAIT_FAILED;
316 WINPR_THREAD* thread = (WINPR_THREAD*)handle;
318 if (!ThreadIsHandled(handle))
321 if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
326 int rc = pthread_join(thread->thread, NULL);
330 char ebuffer[256] = { 0 };
331 WLog_ERR(TAG,
"pthread_join failure: [%d] %s", rc,
332 winpr_strerror(rc, ebuffer,
sizeof(ebuffer)));
336 thread->joined = TRUE;
339 status = WAIT_OBJECT_0;
342 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
370static void dump_thread(WINPR_THREAD* thread)
372#if defined(WITH_DEBUG_THREADS)
373 void* stack = winpr_backtrace(20);
376 WLog_DBG(TAG,
"Called from:");
377 msg = winpr_backtrace_symbols(stack, &used);
379 for (
size_t i = 0; i < used; i++)
380 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
383 winpr_backtrace_free(stack);
384 WLog_DBG(TAG,
"Thread handle created still not closed!");
385 msg = winpr_backtrace_symbols(thread->create_stack, &used);
387 for (
size_t i = 0; i < used; i++)
388 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
394 WLog_DBG(TAG,
"Thread still running!");
396 else if (!thread->exit_stack)
398 WLog_DBG(TAG,
"Thread suspended.");
402 WLog_DBG(TAG,
"Thread exited at:");
403 msg = winpr_backtrace_symbols(thread->exit_stack, &used);
405 for (
size_t i = 0; i < used; i++)
406 WLog_DBG(TAG,
"[%" PRIdz
"]: %s", i, msg[i]);
411 WINPR_UNUSED(thread);
419static BOOL set_event(WINPR_THREAD* thread)
421 return winpr_event_set(&thread->event);
424static BOOL reset_event(WINPR_THREAD* thread)
426 return winpr_event_reset(&thread->event);
429#if defined(WITH_THREAD_LIST)
430static BOOL thread_compare(
const void* a,
const void* b)
432 const pthread_t* p1 = a;
433 const pthread_t* p2 = b;
434 BOOL rc = pthread_equal(*p1, *p2);
439static INIT_ONCE threads_InitOnce = INIT_ONCE_STATIC_INIT;
440static pthread_t mainThreadId;
441static DWORD currentThreadTlsIndex = TLS_OUT_OF_INDEXES;
443static BOOL initializeThreads(WINPR_ATTR_UNUSED
PINIT_ONCE InitOnce,
444 WINPR_ATTR_UNUSED PVOID Parameter, WINPR_ATTR_UNUSED PVOID* Context)
446 if (!apc_init(&mainThread.apc))
448 WLog_ERR(TAG,
"failed to initialize APC");
452 mainThread.common.Type = HANDLE_TYPE_THREAD;
453 mainThreadId = pthread_self();
455 currentThreadTlsIndex = TlsAlloc();
456 if (currentThreadTlsIndex == TLS_OUT_OF_INDEXES)
458 WLog_ERR(TAG,
"Major bug, unable to allocate a TLS value for currentThread");
461#if defined(WITH_THREAD_LIST)
462 thread_list = ListDictionary_New(TRUE);
466 WLog_ERR(TAG,
"Couldn't create global thread list");
467 goto error_thread_list;
470 thread_list->objectKey.fnObjectEquals = thread_compare;
477static BOOL signal_and_wait_for_ready(WINPR_THREAD* thread)
481 WINPR_ASSERT(thread);
483 if (!mux_condition_bundle_lock(&thread->isRunning))
486 if (!signal_thread_ready(thread))
489 if (!mux_condition_bundle_wait(&thread->isRunning,
"threadIsRunning"))
492#if defined(WITH_THREAD_LIST)
493 if (!ListDictionary_Contains(thread_list, &thread->thread))
495 WLog_ERR(TAG,
"Thread not in thread_list, startup failed!");
503 if (!mux_condition_bundle_unlock(&thread->isRunning))
512static void* thread_launcher(
void* arg)
515 WINPR_THREAD* thread = (WINPR_THREAD*)arg;
516 LPTHREAD_START_ROUTINE fkt = NULL;
520 WLog_ERR(TAG,
"Called with invalid argument %p", arg);
524 if (!TlsSetValue(currentThreadTlsIndex, thread))
526 WLog_ERR(TAG,
"thread %d, unable to set current thread value", pthread_self());
530 if (!(fkt = thread->lpStartAddress))
534 LPTHREAD_START_ROUTINE fkt;
538 WLog_ERR(TAG,
"Thread function argument is %p", cnv.pv);
542 if (!signal_and_wait_for_ready(thread))
545 rc = fkt(thread->lpParameter);
550 apc_cleanupThread(thread);
553 thread->dwExitCode = rc;
557 (void)signal_thread_ready(thread);
559 if (thread->detached || !thread->started)
560 cleanup_handle(thread);
566static BOOL winpr_StartThread(WINPR_THREAD* thread)
570 pthread_attr_t attr = { 0 };
572 if (!mux_condition_bundle_lock(&thread->isCreated))
576 pthread_attr_init(&attr);
577 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
579 if (thread->dwStackSize > 0)
580 pthread_attr_setstacksize(&attr, thread->dwStackSize);
582 thread->started = TRUE;
585#if defined(WITH_THREAD_LIST)
586 if (!ListDictionary_Add(thread_list, &thread->thread, thread))
588 WLog_ERR(TAG,
"failed to add the thread to the thread list");
593 if (pthread_create(&thread->thread, &attr, thread_launcher, thread))
596 if (!mux_condition_bundle_wait(&thread->isCreated,
"threadIsCreated"))
600 if (!mux_condition_bundle_unlock(&thread->isCreated))
603 if (!signal_thread_is_running(thread))
605 WLog_ERR(TAG,
"failed to signal the thread was ready");
613 if (!mux_condition_bundle_unlock(&thread->isCreated))
617 pthread_attr_destroy(&attr);
625BOOL SetThreadPriority(HANDLE hThread,
int nPriority)
630 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
635 const int diff = (max - min);
636 const int normal = min + diff / 2;
637 const int off = MIN(1, diff / 4);
638 int sched_priority = -1;
640 switch (nPriority & ~(THREAD_MODE_BACKGROUND_BEGIN | THREAD_MODE_BACKGROUND_END))
642 case THREAD_PRIORITY_ABOVE_NORMAL:
643 sched_priority = MIN(normal + off, max);
645 case THREAD_PRIORITY_BELOW_NORMAL:
646 sched_priority = MAX(normal - off, min);
648 case THREAD_PRIORITY_HIGHEST:
649 sched_priority = max;
651 case THREAD_PRIORITY_IDLE:
652 sched_priority = min;
654 case THREAD_PRIORITY_LOWEST:
655 sched_priority = min;
657 case THREAD_PRIORITY_TIME_CRITICAL:
658 sched_priority = max;
661 case THREAD_PRIORITY_NORMAL:
662 sched_priority = normal;
665#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L) && defined(PTHREAD_SETSCHEDPRIO)
666 WINPR_THREAD* thread = (WINPR_THREAD*)Object;
667 const int rc = pthread_setschedprio(thread->thread, sched_priority);
670 char buffer[256] = { 0 };
671 WLog_ERR(TAG,
"pthread_setschedprio(%d) %s [%d]", sched_priority,
672 winpr_strerror(rc, buffer,
sizeof(buffer)), rc);
676 WLog_WARN(TAG,
"pthread_setschedprio(%d) not implemented, requires POSIX 2008 or later",
682HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,
size_t dwStackSize,
683 LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter,
684 DWORD dwCreationFlags, WINPR_ATTR_UNUSED LPDWORD lpThreadId)
686 HANDLE handle = NULL;
687 WINPR_THREAD* thread = (WINPR_THREAD*)calloc(1,
sizeof(WINPR_THREAD));
692 thread->dwStackSize = dwStackSize;
693 thread->lpParameter = lpParameter;
694 thread->lpStartAddress = lpStartAddress;
695 thread->lpThreadAttributes = lpThreadAttributes;
696 thread->common.ops = &ops;
697#if defined(WITH_DEBUG_THREADS)
698 thread->create_stack = winpr_backtrace(20);
702 if (!winpr_event_init(&thread->event))
704 WLog_ERR(TAG,
"failed to create event");
708 if (!run_mutex_init(pthread_mutex_init, &thread->mutex, NULL))
710 WLog_ERR(TAG,
"failed to initialize thread mutex");
714 if (!apc_init(&thread->apc))
716 WLog_ERR(TAG,
"failed to initialize APC");
720 if (!mux_condition_bundle_init(&thread->isCreated))
722 if (!mux_condition_bundle_init(&thread->isRunning))
725 WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ);
726 handle = (HANDLE)thread;
728 InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
730 if (!(dwCreationFlags & CREATE_SUSPENDED))
732 if (!winpr_StartThread(thread))
737 if (!set_event(thread))
743 cleanup_handle(thread);
747void cleanup_handle(
void* obj)
749 WINPR_THREAD* thread = (WINPR_THREAD*)obj;
753 if (!apc_uninit(&thread->apc))
754 WLog_ERR(TAG,
"failed to destroy APC");
756 mux_condition_bundle_uninit(&thread->isCreated);
757 mux_condition_bundle_uninit(&thread->isRunning);
758 run_mutex_fkt(pthread_mutex_destroy, &thread->mutex);
760 winpr_event_uninit(&thread->event);
762#if defined(WITH_THREAD_LIST)
763 ListDictionary_Remove(thread_list, &thread->thread);
765#if defined(WITH_DEBUG_THREADS)
767 if (thread->create_stack)
768 winpr_backtrace_free(thread->create_stack);
770 if (thread->exit_stack)
771 winpr_backtrace_free(thread->exit_stack);
777BOOL ThreadCloseHandle(HANDLE handle)
779 WINPR_THREAD* thread = (WINPR_THREAD*)handle;
781#if defined(WITH_THREAD_LIST)
784 WLog_ERR(TAG,
"Thread list does not exist, check call!");
787 else if (!ListDictionary_Contains(thread_list, &thread->thread))
789 WLog_ERR(TAG,
"Thread list does not contain this thread! check call!");
794 ListDictionary_Lock(thread_list);
798 if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0))
800 WLog_DBG(TAG,
"Thread running, setting to detached state!");
801 thread->detached = TRUE;
802 pthread_detach(thread->thread);
806 cleanup_handle(thread);
809#if defined(WITH_THREAD_LIST)
810 ListDictionary_Unlock(thread_list);
817HANDLE CreateRemoteThread(WINPR_ATTR_UNUSED HANDLE hProcess,
818 WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpThreadAttributes,
819 WINPR_ATTR_UNUSED
size_t dwStackSize,
820 WINPR_ATTR_UNUSED LPTHREAD_START_ROUTINE lpStartAddress,
821 WINPR_ATTR_UNUSED LPVOID lpParameter,
822 WINPR_ATTR_UNUSED DWORD dwCreationFlags,
823 WINPR_ATTR_UNUSED LPDWORD lpThreadId)
825 WLog_ERR(TAG,
"not implemented");
826 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
830VOID ExitThread(DWORD dwExitCode)
832#if defined(WITH_THREAD_LIST)
834 pthread_t tid = pthread_self();
838 WLog_ERR(TAG,
"function called without existing thread list!");
839#if defined(WITH_DEBUG_THREADS)
844 else if (!ListDictionary_Contains(thread_list, &tid))
846 WLog_ERR(TAG,
"function called, but no matching entry in thread list!");
847#if defined(WITH_DEBUG_THREADS)
854 WINPR_THREAD* thread;
855 ListDictionary_Lock(thread_list);
856 thread = ListDictionary_GetItemValue(thread_list, &tid);
857 WINPR_ASSERT(thread);
858 thread->exited = TRUE;
859 thread->dwExitCode = dwExitCode;
860#if defined(WITH_DEBUG_THREADS)
861 thread->exit_stack = winpr_backtrace(20);
863 ListDictionary_Unlock(thread_list);
865 rc = thread->dwExitCode;
867 if (thread->detached || !thread->started)
868 cleanup_handle(thread);
870 pthread_exit((
void*)(
size_t)rc);
873 WINPR_UNUSED(dwExitCode);
877BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)
881 WINPR_THREAD* thread = NULL;
883 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
885 WLog_ERR(TAG,
"hThread is not a thread");
886 SetLastError(ERROR_INVALID_PARAMETER);
890 thread = (WINPR_THREAD*)Object;
891 *lpExitCode = thread->dwExitCode;
895WINPR_THREAD* winpr_GetCurrentThread(VOID)
897 WINPR_THREAD* ret = NULL;
899 InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
900 if (mainThreadId == pthread_self())
901 return (HANDLE)&mainThread;
903 ret = TlsGetValue(currentThreadTlsIndex);
907HANDLE _GetCurrentThread(VOID)
909 return (HANDLE)winpr_GetCurrentThread();
912DWORD GetCurrentThreadId(VOID)
914 pthread_t tid = pthread_self();
917 uintptr_t ptid = WINPR_REINTERPRET_CAST(tid, pthread_t, uintptr_t);
918 return ptid & UINT32_MAX;
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 = NULL;
942 UserApcItem* apcItem = NULL;
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 = NULL;
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 = NULL;
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)
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 = NULL;
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 -------------");