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