FreeRDP
Loading...
Searching...
No Matches
thread.c
1
23#include <winpr/config.h>
24
25#include <winpr/winpr.h>
26#include <winpr/assert.h>
27
28#include <winpr/handle.h>
29
30#include <winpr/thread.h>
31
32#ifndef MIN
33#define MIN(x, y) (((x) < (y)) ? (x) : (y))
34#endif
35
36#ifndef MAX
37#define MAX(x, y) (((x) > (y)) ? (x) : (y))
38#endif
39
80#ifndef _WIN32
81
82#include <winpr/crt.h>
83#include <winpr/platform.h>
84
85#include <string.h>
86#ifdef WINPR_HAVE_UNISTD_H
87#include <unistd.h>
88#endif
89
90#ifdef WINPR_HAVE_SYS_EVENTFD_H
91#include <sys/eventfd.h>
92#endif
93
94#include <winpr/debug.h>
95
96#include <errno.h>
97#include <fcntl.h>
98
99#include <winpr/collections.h>
100
101#include "thread.h"
102#include "apc.h"
103
104#include "../handle/handle.h"
105#include "../log.h"
106#define TAG WINPR_TAG("thread")
107
108static WINPR_THREAD mainThread;
109
110#if defined(WITH_THREAD_LIST)
111static wListDictionary* thread_list = NULL;
112#endif
113
114static BOOL ThreadCloseHandle(HANDLE handle);
115static void cleanup_handle(void* obj);
116
117static BOOL ThreadIsHandled(HANDLE handle)
118{
119 return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_THREAD, FALSE);
120}
121
122static int ThreadGetFd(HANDLE handle)
123{
124 WINPR_THREAD* pThread = (WINPR_THREAD*)handle;
125
126 if (!ThreadIsHandled(handle))
127 return -1;
128
129 return pThread->event.fds[0];
130}
131
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)
136{
137 int rc = 0;
138
139 WINPR_ASSERT(fkt);
140 WINPR_ASSERT(mutex);
141
142 rc = fkt(mutex, mutexattr);
143 if (rc != 0)
144 {
145 char ebuffer[256] = { 0 };
146 WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
147 }
148 return rc == 0;
149}
150
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)
154{
155 int rc = 0;
156
157 WINPR_ASSERT(fkt);
158 WINPR_ASSERT(mutex);
159
160 rc = fkt(mutex);
161 if (rc != 0)
162 {
163 char ebuffer[256] = { 0 };
164 WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
165 }
166 return rc == 0;
167}
168
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)
172{
173 int rc = 0;
174
175 WINPR_ASSERT(fkt);
176 WINPR_ASSERT(condition);
177
178 rc = fkt(condition, conditionattr);
179 if (rc != 0)
180 {
181 char ebuffer[256] = { 0 };
182 WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
183 }
184 return rc == 0;
185}
186
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)
190{
191 int rc = 0;
192
193 WINPR_ASSERT(fkt);
194 WINPR_ASSERT(condition);
195
196 rc = fkt(condition);
197 if (rc != 0)
198 {
199 char ebuffer[256] = { 0 };
200 WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
201 }
202 return rc == 0;
203}
204
205static int pthread_mutex_checked_unlock(pthread_mutex_t* mutex)
206{
207 WINPR_ASSERT(mutex);
208 WINPR_ASSERT(pthread_mutex_trylock(mutex) == EBUSY);
209 return pthread_mutex_unlock(mutex);
210}
211
212static BOOL mux_condition_bundle_init(mux_condition_bundle* bundle)
213{
214 WINPR_ASSERT(bundle);
215
216 bundle->val = FALSE;
217 if (!run_mutex_init(pthread_mutex_init, &bundle->mux, NULL))
218 return FALSE;
219
220 if (!run_cond_init(pthread_cond_init, &bundle->cond, NULL))
221 return FALSE;
222 return TRUE;
223}
224
225static void mux_condition_bundle_uninit(mux_condition_bundle* bundle)
226{
227 mux_condition_bundle empty = { 0 };
228
229 WINPR_ASSERT(bundle);
230
231 run_cond_fkt(pthread_cond_destroy, &bundle->cond);
232 run_mutex_fkt(pthread_mutex_destroy, &bundle->mux);
233 *bundle = empty;
234}
235
236static BOOL mux_condition_bundle_signal(mux_condition_bundle* bundle)
237{
238 BOOL rc = TRUE;
239 WINPR_ASSERT(bundle);
240
241 if (!run_mutex_fkt(pthread_mutex_lock, &bundle->mux))
242 return FALSE;
243 bundle->val = TRUE;
244 if (!run_cond_fkt(pthread_cond_signal, &bundle->cond))
245 rc = FALSE;
246 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux))
247 rc = FALSE;
248 return rc;
249}
250
251static BOOL mux_condition_bundle_lock(mux_condition_bundle* bundle)
252{
253 WINPR_ASSERT(bundle);
254 return run_mutex_fkt(pthread_mutex_lock, &bundle->mux);
255}
256
257static BOOL mux_condition_bundle_unlock(mux_condition_bundle* bundle)
258{
259 WINPR_ASSERT(bundle);
260 return run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux);
261}
262
263static BOOL mux_condition_bundle_wait(mux_condition_bundle* bundle, const char* name)
264{
265 BOOL rc = FALSE;
266
267 WINPR_ASSERT(bundle);
268 WINPR_ASSERT(name);
269 WINPR_ASSERT(pthread_mutex_trylock(&bundle->mux) == EBUSY);
270
271 while (!bundle->val)
272 {
273 int r = pthread_cond_wait(&bundle->cond, &bundle->mux);
274 if (r != 0)
275 {
276 char ebuffer[256] = { 0 };
277 WLog_ERR(TAG, "failed to wait for %s [%s]", name,
278 winpr_strerror(r, ebuffer, sizeof(ebuffer)));
279 switch (r)
280 {
281 case ENOTRECOVERABLE:
282 case EPERM:
283 case ETIMEDOUT:
284 case EINVAL:
285 goto fail;
286
287 default:
288 break;
289 }
290 }
291 }
292
293 rc = bundle->val;
294
295fail:
296 return rc;
297}
298
299static BOOL signal_thread_ready(WINPR_THREAD* thread)
300{
301 WINPR_ASSERT(thread);
302
303 return mux_condition_bundle_signal(&thread->isCreated);
304}
305
306static BOOL signal_thread_is_running(WINPR_THREAD* thread)
307{
308 WINPR_ASSERT(thread);
309
310 return mux_condition_bundle_signal(&thread->isRunning);
311}
312
313static DWORD ThreadCleanupHandle(HANDLE handle)
314{
315 DWORD status = WAIT_FAILED;
316 WINPR_THREAD* thread = (WINPR_THREAD*)handle;
317
318 if (!ThreadIsHandled(handle))
319 return WAIT_FAILED;
320
321 if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
322 return WAIT_FAILED;
323
324 if (!thread->joined)
325 {
326 int rc = pthread_join(thread->thread, NULL);
327
328 if (rc != 0)
329 {
330 char ebuffer[256] = { 0 };
331 WLog_ERR(TAG, "pthread_join failure: [%d] %s", rc,
332 winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
333 goto fail;
334 }
335 else
336 thread->joined = TRUE;
337 }
338
339 status = WAIT_OBJECT_0;
340
341fail:
342 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
343 return WAIT_FAILED;
344
345 return status;
346}
347
348static HANDLE_OPS ops = { ThreadIsHandled,
349 ThreadCloseHandle,
350 ThreadGetFd,
351 ThreadCleanupHandle,
352 NULL,
353 NULL,
354 NULL,
355 NULL,
356 NULL,
357 NULL,
358 NULL,
359 NULL,
360 NULL,
361 NULL,
362 NULL,
363 NULL,
364 NULL,
365 NULL,
366 NULL,
367 NULL,
368 NULL };
369
370static void dump_thread(WINPR_THREAD* thread)
371{
372#if defined(WITH_DEBUG_THREADS)
373 void* stack = winpr_backtrace(20);
374 char** msg = NULL;
375 size_t used = 0;
376 WLog_DBG(TAG, "Called from:");
377 msg = winpr_backtrace_symbols(stack, &used);
378
379 for (size_t i = 0; i < used; i++)
380 WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
381
382 free(msg);
383 winpr_backtrace_free(stack);
384 WLog_DBG(TAG, "Thread handle created still not closed!");
385 msg = winpr_backtrace_symbols(thread->create_stack, &used);
386
387 for (size_t i = 0; i < used; i++)
388 WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
389
390 free(msg);
391
392 if (thread->started)
393 {
394 WLog_DBG(TAG, "Thread still running!");
395 }
396 else if (!thread->exit_stack)
397 {
398 WLog_DBG(TAG, "Thread suspended.");
399 }
400 else
401 {
402 WLog_DBG(TAG, "Thread exited at:");
403 msg = winpr_backtrace_symbols(thread->exit_stack, &used);
404
405 for (size_t i = 0; i < used; i++)
406 WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
407
408 free(msg);
409 }
410#else
411 WINPR_UNUSED(thread);
412#endif
413}
414
419static BOOL set_event(WINPR_THREAD* thread)
420{
421 return winpr_event_set(&thread->event);
422}
423
424static BOOL reset_event(WINPR_THREAD* thread)
425{
426 return winpr_event_reset(&thread->event);
427}
428
429#if defined(WITH_THREAD_LIST)
430static BOOL thread_compare(const void* a, const void* b)
431{
432 const pthread_t* p1 = a;
433 const pthread_t* p2 = b;
434 BOOL rc = pthread_equal(*p1, *p2);
435 return rc;
436}
437#endif
438
439static INIT_ONCE threads_InitOnce = INIT_ONCE_STATIC_INIT;
440static pthread_t mainThreadId;
441static DWORD currentThreadTlsIndex = TLS_OUT_OF_INDEXES;
442
443static BOOL initializeThreads(WINPR_ATTR_UNUSED PINIT_ONCE InitOnce,
444 WINPR_ATTR_UNUSED PVOID Parameter, WINPR_ATTR_UNUSED PVOID* Context)
445{
446 if (!apc_init(&mainThread.apc))
447 {
448 WLog_ERR(TAG, "failed to initialize APC");
449 goto out;
450 }
451
452 mainThread.common.Type = HANDLE_TYPE_THREAD;
453 mainThreadId = pthread_self();
454
455 currentThreadTlsIndex = TlsAlloc();
456 if (currentThreadTlsIndex == TLS_OUT_OF_INDEXES)
457 {
458 WLog_ERR(TAG, "Major bug, unable to allocate a TLS value for currentThread");
459 }
460
461#if defined(WITH_THREAD_LIST)
462 thread_list = ListDictionary_New(TRUE);
463
464 if (!thread_list)
465 {
466 WLog_ERR(TAG, "Couldn't create global thread list");
467 goto error_thread_list;
468 }
469
470 thread_list->objectKey.fnObjectEquals = thread_compare;
471#endif
472
473out:
474 return TRUE;
475}
476
477static BOOL signal_and_wait_for_ready(WINPR_THREAD* thread)
478{
479 BOOL res = FALSE;
480
481 WINPR_ASSERT(thread);
482
483 if (!mux_condition_bundle_lock(&thread->isRunning))
484 return FALSE;
485
486 if (!signal_thread_ready(thread))
487 goto fail;
488
489 if (!mux_condition_bundle_wait(&thread->isRunning, "threadIsRunning"))
490 goto fail;
491
492#if defined(WITH_THREAD_LIST)
493 if (!ListDictionary_Contains(thread_list, &thread->thread))
494 {
495 WLog_ERR(TAG, "Thread not in thread_list, startup failed!");
496 goto fail;
497 }
498#endif
499
500 res = TRUE;
501
502fail:
503 if (!mux_condition_bundle_unlock(&thread->isRunning))
504 return FALSE;
505
506 return res;
507}
508
509/* Thread launcher function responsible for registering
510 * cleanup handlers and calling pthread_exit, if not done
511 * in thread function. */
512static void* thread_launcher(void* arg)
513{
514 DWORD rc = 0;
515 WINPR_THREAD* thread = (WINPR_THREAD*)arg;
516 LPTHREAD_START_ROUTINE fkt = NULL;
517
518 if (!thread)
519 {
520 WLog_ERR(TAG, "Called with invalid argument %p", arg);
521 goto exit;
522 }
523
524 if (!TlsSetValue(currentThreadTlsIndex, thread))
525 {
526 WLog_ERR(TAG, "thread %d, unable to set current thread value", pthread_self());
527 goto exit;
528 }
529
530 if (!(fkt = thread->lpStartAddress))
531 {
532 union
533 {
534 LPTHREAD_START_ROUTINE fkt;
535 void* pv;
536 } cnv;
537 cnv.fkt = fkt;
538 WLog_ERR(TAG, "Thread function argument is %p", cnv.pv);
539 goto exit;
540 }
541
542 if (!signal_and_wait_for_ready(thread))
543 goto exit;
544
545 rc = fkt(thread->lpParameter);
546exit:
547
548 if (thread)
549 {
550 apc_cleanupThread(thread);
551
552 if (!thread->exited)
553 thread->dwExitCode = rc;
554
555 set_event(thread);
556
557 (void)signal_thread_ready(thread);
558
559 if (thread->detached || !thread->started)
560 cleanup_handle(thread);
561 }
562
563 return NULL;
564}
565
566static BOOL winpr_StartThread(WINPR_THREAD* thread)
567{
568 BOOL rc = FALSE;
569 BOOL locked = FALSE;
570 pthread_attr_t attr = { 0 };
571
572 if (!mux_condition_bundle_lock(&thread->isCreated))
573 return FALSE;
574 locked = TRUE;
575
576 pthread_attr_init(&attr);
577 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
578
579 if (thread->dwStackSize > 0)
580 pthread_attr_setstacksize(&attr, thread->dwStackSize);
581
582 thread->started = TRUE;
583 reset_event(thread);
584
585#if defined(WITH_THREAD_LIST)
586 if (!ListDictionary_Add(thread_list, &thread->thread, thread))
587 {
588 WLog_ERR(TAG, "failed to add the thread to the thread list");
589 goto error;
590 }
591#endif
592
593 if (pthread_create(&thread->thread, &attr, thread_launcher, thread))
594 goto error;
595
596 if (!mux_condition_bundle_wait(&thread->isCreated, "threadIsCreated"))
597 goto error;
598
599 locked = FALSE;
600 if (!mux_condition_bundle_unlock(&thread->isCreated))
601 goto error;
602
603 if (!signal_thread_is_running(thread))
604 {
605 WLog_ERR(TAG, "failed to signal the thread was ready");
606 goto error;
607 }
608
609 rc = TRUE;
610error:
611 if (locked)
612 {
613 if (!mux_condition_bundle_unlock(&thread->isCreated))
614 rc = FALSE;
615 }
616
617 pthread_attr_destroy(&attr);
618
619 if (rc)
620 dump_thread(thread);
621
622 return rc;
623}
624
625BOOL SetThreadPriority(HANDLE hThread, int nPriority)
626{
627 ULONG Type = 0;
628 WINPR_HANDLE* Object = NULL;
629
630 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
631 return FALSE;
632
633 const int min = 19;
634 const int max = 0;
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;
639
640 switch (nPriority & ~(THREAD_MODE_BACKGROUND_BEGIN | THREAD_MODE_BACKGROUND_END))
641 {
642 case THREAD_PRIORITY_ABOVE_NORMAL:
643 sched_priority = MIN(normal + off, max);
644 break;
645 case THREAD_PRIORITY_BELOW_NORMAL:
646 sched_priority = MAX(normal - off, min);
647 break;
648 case THREAD_PRIORITY_HIGHEST:
649 sched_priority = max;
650 break;
651 case THREAD_PRIORITY_IDLE:
652 sched_priority = min;
653 break;
654 case THREAD_PRIORITY_LOWEST:
655 sched_priority = min;
656 break;
657 case THREAD_PRIORITY_TIME_CRITICAL:
658 sched_priority = max;
659 break;
660 default:
661 case THREAD_PRIORITY_NORMAL:
662 sched_priority = normal;
663 break;
664 }
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);
668 if (rc != 0)
669 {
670 char buffer[256] = { 0 };
671 WLog_ERR(TAG, "pthread_setschedprio(%d) %s [%d]", sched_priority,
672 winpr_strerror(rc, buffer, sizeof(buffer)), rc);
673 }
674 return rc == 0;
675#else
676 WLog_WARN(TAG, "pthread_setschedprio(%d) not implemented, requires POSIX 2008 or later",
677 sched_priority);
678 return TRUE;
679#endif
680}
681
682HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, size_t dwStackSize,
683 LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter,
684 DWORD dwCreationFlags, WINPR_ATTR_UNUSED LPDWORD lpThreadId)
685{
686 HANDLE handle = NULL;
687 WINPR_THREAD* thread = (WINPR_THREAD*)calloc(1, sizeof(WINPR_THREAD));
688
689 if (!thread)
690 return NULL;
691
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);
699 dump_thread(thread);
700#endif
701
702 if (!winpr_event_init(&thread->event))
703 {
704 WLog_ERR(TAG, "failed to create event");
705 goto fail;
706 }
707
708 if (!run_mutex_init(pthread_mutex_init, &thread->mutex, NULL))
709 {
710 WLog_ERR(TAG, "failed to initialize thread mutex");
711 goto fail;
712 }
713
714 if (!apc_init(&thread->apc))
715 {
716 WLog_ERR(TAG, "failed to initialize APC");
717 goto fail;
718 }
719
720 if (!mux_condition_bundle_init(&thread->isCreated))
721 goto fail;
722 if (!mux_condition_bundle_init(&thread->isRunning))
723 goto fail;
724
725 WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ);
726 handle = (HANDLE)thread;
727
728 InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
729
730 if (!(dwCreationFlags & CREATE_SUSPENDED))
731 {
732 if (!winpr_StartThread(thread))
733 goto fail;
734 }
735 else
736 {
737 if (!set_event(thread))
738 goto fail;
739 }
740
741 return handle;
742fail:
743 cleanup_handle(thread);
744 return NULL;
745}
746
747void cleanup_handle(void* obj)
748{
749 WINPR_THREAD* thread = (WINPR_THREAD*)obj;
750 if (!thread)
751 return;
752
753 if (!apc_uninit(&thread->apc))
754 WLog_ERR(TAG, "failed to destroy APC");
755
756 mux_condition_bundle_uninit(&thread->isCreated);
757 mux_condition_bundle_uninit(&thread->isRunning);
758 run_mutex_fkt(pthread_mutex_destroy, &thread->mutex);
759
760 winpr_event_uninit(&thread->event);
761
762#if defined(WITH_THREAD_LIST)
763 ListDictionary_Remove(thread_list, &thread->thread);
764#endif
765#if defined(WITH_DEBUG_THREADS)
766
767 if (thread->create_stack)
768 winpr_backtrace_free(thread->create_stack);
769
770 if (thread->exit_stack)
771 winpr_backtrace_free(thread->exit_stack);
772
773#endif
774 free(thread);
775}
776
777BOOL ThreadCloseHandle(HANDLE handle)
778{
779 WINPR_THREAD* thread = (WINPR_THREAD*)handle;
780
781#if defined(WITH_THREAD_LIST)
782 if (!thread_list)
783 {
784 WLog_ERR(TAG, "Thread list does not exist, check call!");
785 dump_thread(thread);
786 }
787 else if (!ListDictionary_Contains(thread_list, &thread->thread))
788 {
789 WLog_ERR(TAG, "Thread list does not contain this thread! check call!");
790 dump_thread(thread);
791 }
792 else
793 {
794 ListDictionary_Lock(thread_list);
795#endif
796 dump_thread(thread);
797
798 if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0))
799 {
800 WLog_DBG(TAG, "Thread running, setting to detached state!");
801 thread->detached = TRUE;
802 pthread_detach(thread->thread);
803 }
804 else
805 {
806 cleanup_handle(thread);
807 }
808
809#if defined(WITH_THREAD_LIST)
810 ListDictionary_Unlock(thread_list);
811 }
812#endif
813
814 return TRUE;
815}
816
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)
824{
825 WLog_ERR(TAG, "not implemented");
826 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
827 return NULL;
828}
829
830VOID ExitThread(DWORD dwExitCode)
831{
832#if defined(WITH_THREAD_LIST)
833 DWORD rc;
834 pthread_t tid = pthread_self();
835
836 if (!thread_list)
837 {
838 WLog_ERR(TAG, "function called without existing thread list!");
839#if defined(WITH_DEBUG_THREADS)
840 DumpThreadHandles();
841#endif
842 pthread_exit(0);
843 }
844 else if (!ListDictionary_Contains(thread_list, &tid))
845 {
846 WLog_ERR(TAG, "function called, but no matching entry in thread list!");
847#if defined(WITH_DEBUG_THREADS)
848 DumpThreadHandles();
849#endif
850 pthread_exit(0);
851 }
852 else
853 {
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);
862#endif
863 ListDictionary_Unlock(thread_list);
864 set_event(thread);
865 rc = thread->dwExitCode;
866
867 if (thread->detached || !thread->started)
868 cleanup_handle(thread);
869
870 pthread_exit((void*)(size_t)rc);
871 }
872#else
873 WINPR_UNUSED(dwExitCode);
874#endif
875}
876
877BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)
878{
879 ULONG Type = 0;
880 WINPR_HANDLE* Object = NULL;
881 WINPR_THREAD* thread = NULL;
882
883 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
884 {
885 WLog_ERR(TAG, "hThread is not a thread");
886 SetLastError(ERROR_INVALID_PARAMETER);
887 return FALSE;
888 }
889
890 thread = (WINPR_THREAD*)Object;
891 *lpExitCode = thread->dwExitCode;
892 return TRUE;
893}
894
895WINPR_THREAD* winpr_GetCurrentThread(VOID)
896{
897 WINPR_THREAD* ret = NULL;
898
899 InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
900 if (mainThreadId == pthread_self())
901 return (HANDLE)&mainThread;
902
903 ret = TlsGetValue(currentThreadTlsIndex);
904 return ret;
905}
906
907HANDLE _GetCurrentThread(VOID)
908{
909 return (HANDLE)winpr_GetCurrentThread();
910}
911
912DWORD GetCurrentThreadId(VOID)
913{
914 pthread_t tid = pthread_self();
915 /* Since pthread_t can be 64-bits on some systems, take just the */
916 /* lower 32-bits of it for the thread ID returned by this function. */
917 uintptr_t ptid = WINPR_REINTERPRET_CAST(tid, pthread_t, uintptr_t);
918 return ptid & UINT32_MAX;
919}
920
921typedef struct
922{
923 WINPR_APC_ITEM apc;
924 PAPCFUNC completion;
925 ULONG_PTR completionArg;
926} UserApcItem;
927
928static void userAPC(LPVOID arg)
929{
930 UserApcItem* userApc = (UserApcItem*)arg;
931
932 userApc->completion(userApc->completionArg);
933
934 userApc->apc.markedForRemove = TRUE;
935}
936
937DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData)
938{
939 ULONG Type = 0;
940 WINPR_HANDLE* Object = NULL;
941 WINPR_APC_ITEM* apc = NULL;
942 UserApcItem* apcItem = NULL;
943
944 if (!pfnAPC)
945 return 1;
946
947 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
948 {
949 WLog_ERR(TAG, "hThread is not a thread");
950 SetLastError(ERROR_INVALID_PARAMETER);
951 return (DWORD)0;
952 }
953
954 apcItem = calloc(1, sizeof(*apcItem));
955 if (!apcItem)
956 {
957 SetLastError(ERROR_INVALID_PARAMETER);
958 return (DWORD)0;
959 }
960
961 apc = &apcItem->apc;
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);
970 return 1;
971}
972
973DWORD ResumeThread(HANDLE hThread)
974{
975 ULONG Type = 0;
976 WINPR_HANDLE* Object = NULL;
977 WINPR_THREAD* thread = NULL;
978
979 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
980 {
981 WLog_ERR(TAG, "hThread is not a thread");
982 SetLastError(ERROR_INVALID_PARAMETER);
983 return (DWORD)-1;
984 }
985
986 thread = (WINPR_THREAD*)Object;
987
988 if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
989 return (DWORD)-1;
990
991 if (!thread->started)
992 {
993 if (!winpr_StartThread(thread))
994 {
995 run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex);
996 return (DWORD)-1;
997 }
998 }
999 else
1000 WLog_WARN(TAG, "Thread already started!");
1001
1002 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1003 return (DWORD)-1;
1004
1005 return 0;
1006}
1007
1008DWORD SuspendThread(WINPR_ATTR_UNUSED HANDLE hThread)
1009{
1010 WLog_ERR(TAG, "not implemented");
1011 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1012 return (DWORD)-1;
1013}
1014
1015BOOL SwitchToThread(VOID)
1016{
1021 if (sched_yield() != 0)
1022 usleep(1);
1023
1024 return TRUE;
1025}
1026
1027BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode)
1028{
1029 ULONG Type = 0;
1030 WINPR_HANDLE* Object = NULL;
1031 WINPR_THREAD* thread = NULL;
1032
1033 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
1034 return FALSE;
1035
1036 thread = (WINPR_THREAD*)Object;
1037 thread->exited = TRUE;
1038 thread->dwExitCode = dwExitCode;
1039
1040 if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
1041 return FALSE;
1042
1043#ifndef ANDROID
1044 pthread_cancel(thread->thread);
1045#else
1046 WLog_ERR(TAG, "Function not supported on this platform!");
1047#endif
1048
1049 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1050 return FALSE;
1051
1052 set_event(thread);
1053 return TRUE;
1054}
1055
1056VOID DumpThreadHandles(void)
1057{
1058#if defined(WITH_DEBUG_THREADS)
1059 char** msg = NULL;
1060 size_t used = 0;
1061 void* stack = winpr_backtrace(20);
1062 WLog_DBG(TAG, "---------------- Called from ----------------------------");
1063 msg = winpr_backtrace_symbols(stack, &used);
1064
1065 for (size_t i = 0; i < used; i++)
1066 {
1067 WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1068 }
1069
1070 free(msg);
1071 winpr_backtrace_free(stack);
1072 WLog_DBG(TAG, "---------------- Start Dumping thread handles -----------");
1073
1074#if defined(WITH_THREAD_LIST)
1075 if (!thread_list)
1076 {
1077 WLog_DBG(TAG, "All threads properly shut down and disposed of.");
1078 }
1079 else
1080 {
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);
1085
1086 for (size_t x = 0; x < count; x++)
1087 {
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);
1091
1092 for (size_t i = 0; i < used; i++)
1093 {
1094 WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1095 }
1096
1097 free(msg);
1098
1099 if (thread->started)
1100 {
1101 WLog_DBG(TAG, "Thread [%d] still running!", x);
1102 }
1103 else
1104 {
1105 WLog_DBG(TAG, "Thread [%d] exited at:", x);
1106 msg = winpr_backtrace_symbols(thread->exit_stack, &used);
1107
1108 for (size_t i = 0; i < used; i++)
1109 WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1110
1111 free(msg);
1112 }
1113 }
1114
1115 free(keys);
1116 ListDictionary_Unlock(thread_list);
1117 }
1118#endif
1119
1120 WLog_DBG(TAG, "---------------- End Dumping thread handles -------------");
1121#endif
1122}
1123#endif