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