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 size_t stackSize = thread->dwStackSize;
575 if (stackSize == 0)
576 {
577 size_t platformDefault = 0;
578 if ((pthread_attr_getstacksize(&attr, &platformDefault) == 0) &&
579 (platformDefault < WINPR_THREAD_DEFAULT_STACK_SIZE))
580 stackSize = WINPR_THREAD_DEFAULT_STACK_SIZE;
581 }
582
583 if (stackSize > 0)
584 {
585 const int err = pthread_attr_setstacksize(&attr, stackSize);
586 if (err != 0)
587 {
588 char ebuffer[256] = WINPR_C_ARRAY_INIT;
589 WLog_WARN(TAG, "pthread_attr_setstacksize(%" PRIuz ") failed: %s", stackSize,
590 winpr_strerror(err, ebuffer, sizeof(ebuffer)));
591 }
592 }
593
594 thread->started = TRUE;
595 reset_event(thread);
596
597#if defined(WITH_THREAD_LIST)
598 if (!ListDictionary_Add(thread_list, &thread->thread, thread))
599 {
600 WLog_ERR(TAG, "failed to add the thread to the thread list");
601 goto error;
602 }
603#endif
604
605 if (pthread_create(&thread->thread, &attr, thread_launcher, thread))
606 goto error;
607
608 if (!mux_condition_bundle_wait(&thread->isCreated, "threadIsCreated"))
609 goto error;
610
611 locked = FALSE;
612 if (!mux_condition_bundle_unlock(&thread->isCreated))
613 goto error;
614
615 if (!signal_thread_is_running(thread))
616 {
617 WLog_ERR(TAG, "failed to signal the thread was ready");
618 goto error;
619 }
620
621 rc = TRUE;
622error:
623 if (locked)
624 {
625 if (!mux_condition_bundle_unlock(&thread->isCreated))
626 rc = FALSE;
627 }
628
629 pthread_attr_destroy(&attr);
630
631 if (rc)
632 dump_thread(thread);
633
634 return rc;
635}
636
637BOOL SetThreadPriority(HANDLE hThread, int nPriority)
638{
639 ULONG Type = 0;
640 WINPR_HANDLE* Object = nullptr;
641
642 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
643 return FALSE;
644
645 const int min = 19;
646 const int max = 0;
647 const int diff = (max - min);
648 const int normal = min + diff / 2;
649 const int off = MIN(1, diff / 4);
650 int sched_priority = -1;
651
652 switch (nPriority & ~(THREAD_MODE_BACKGROUND_BEGIN | THREAD_MODE_BACKGROUND_END))
653 {
654 case THREAD_PRIORITY_ABOVE_NORMAL:
655 sched_priority = MIN(normal + off, max);
656 break;
657 case THREAD_PRIORITY_BELOW_NORMAL:
658 sched_priority = MAX(normal - off, min);
659 break;
660 case THREAD_PRIORITY_HIGHEST:
661 sched_priority = max;
662 break;
663 case THREAD_PRIORITY_IDLE:
664 sched_priority = min;
665 break;
666 case THREAD_PRIORITY_LOWEST:
667 sched_priority = min;
668 break;
669 case THREAD_PRIORITY_TIME_CRITICAL:
670 sched_priority = max;
671 break;
672 default:
673 case THREAD_PRIORITY_NORMAL:
674 sched_priority = normal;
675 break;
676 }
677#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L) && defined(PTHREAD_SETSCHEDPRIO)
678 WINPR_THREAD* thread = (WINPR_THREAD*)Object;
679 const int rc = pthread_setschedprio(thread->thread, sched_priority);
680 if (rc != 0)
681 {
682 char buffer[256] = WINPR_C_ARRAY_INIT;
683 WLog_ERR(TAG, "pthread_setschedprio(%d) %s [%d]", sched_priority,
684 winpr_strerror(rc, buffer, sizeof(buffer)), rc);
685 }
686 return rc == 0;
687#else
688 WLog_WARN(TAG, "pthread_setschedprio(%d) not implemented, requires POSIX 2008 or later",
689 sched_priority);
690 return TRUE;
691#endif
692}
693
694HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, size_t dwStackSize,
695 LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter,
696 DWORD dwCreationFlags, WINPR_ATTR_UNUSED LPDWORD lpThreadId)
697{
698 HANDLE handle = nullptr;
699 WINPR_THREAD* thread = (WINPR_THREAD*)calloc(1, sizeof(WINPR_THREAD));
700
701 if (!thread)
702 return nullptr;
703
704 thread->dwStackSize = dwStackSize;
705 thread->lpParameter = lpParameter;
706 thread->lpStartAddress = lpStartAddress;
707 thread->lpThreadAttributes = lpThreadAttributes;
708 thread->common.ops = &ops;
709#if defined(WITH_DEBUG_THREADS)
710 thread->create_stack = winpr_backtrace(20);
711 dump_thread(thread);
712#endif
713
714 if (!winpr_event_init(&thread->event))
715 {
716 WLog_ERR(TAG, "failed to create event");
717 goto fail;
718 }
719
720 if (!run_mutex_init(pthread_mutex_init, &thread->mutex, nullptr))
721 {
722 WLog_ERR(TAG, "failed to initialize thread mutex");
723 goto fail;
724 }
725
726 if (!apc_init(&thread->apc))
727 {
728 WLog_ERR(TAG, "failed to initialize APC");
729 goto fail;
730 }
731
732 if (!mux_condition_bundle_init(&thread->isCreated))
733 goto fail;
734 if (!mux_condition_bundle_init(&thread->isRunning))
735 goto fail;
736
737 WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ);
738 handle = (HANDLE)thread;
739
740 if (!InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, nullptr, nullptr))
741 goto fail;
742
743 if (!(dwCreationFlags & CREATE_SUSPENDED))
744 {
745 if (!winpr_StartThread(thread))
746 goto fail;
747 }
748 else
749 {
750 if (!set_event(thread))
751 goto fail;
752 }
753
754 return handle;
755fail:
756 cleanup_handle(thread);
757 return nullptr;
758}
759
760void cleanup_handle(void* obj)
761{
762 WINPR_THREAD* thread = (WINPR_THREAD*)obj;
763 if (!thread)
764 return;
765
766 if (!apc_uninit(&thread->apc))
767 WLog_ERR(TAG, "failed to destroy APC");
768
769 mux_condition_bundle_uninit(&thread->isCreated);
770 mux_condition_bundle_uninit(&thread->isRunning);
771 run_mutex_fkt(pthread_mutex_destroy, &thread->mutex);
772
773 winpr_event_uninit(&thread->event);
774
775#if defined(WITH_THREAD_LIST)
776 ListDictionary_Remove(thread_list, &thread->thread);
777#endif
778#if defined(WITH_DEBUG_THREADS)
779
780 if (thread->create_stack)
781 winpr_backtrace_free(thread->create_stack);
782
783 if (thread->exit_stack)
784 winpr_backtrace_free(thread->exit_stack);
785
786#endif
787 free(thread);
788}
789
790BOOL ThreadCloseHandle(HANDLE handle)
791{
792 WINPR_THREAD* thread = (WINPR_THREAD*)handle;
793
794#if defined(WITH_THREAD_LIST)
795 if (!thread_list)
796 {
797 WLog_ERR(TAG, "Thread list does not exist, check call!");
798 dump_thread(thread);
799 }
800 else if (!ListDictionary_Contains(thread_list, &thread->thread))
801 {
802 WLog_ERR(TAG, "Thread list does not contain this thread! check call!");
803 dump_thread(thread);
804 }
805 else
806 {
807 ListDictionary_Lock(thread_list);
808#endif
809 dump_thread(thread);
810
811 if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0))
812 {
813 WLog_DBG(TAG, "Thread running, setting to detached state!");
814 thread->detached = TRUE;
815 pthread_detach(thread->thread);
816 }
817 else
818 {
819 cleanup_handle(thread);
820 }
821
822#if defined(WITH_THREAD_LIST)
823 ListDictionary_Unlock(thread_list);
824 }
825#endif
826
827 return TRUE;
828}
829
830HANDLE CreateRemoteThread(WINPR_ATTR_UNUSED HANDLE hProcess,
831 WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpThreadAttributes,
832 WINPR_ATTR_UNUSED size_t dwStackSize,
833 WINPR_ATTR_UNUSED LPTHREAD_START_ROUTINE lpStartAddress,
834 WINPR_ATTR_UNUSED LPVOID lpParameter,
835 WINPR_ATTR_UNUSED DWORD dwCreationFlags,
836 WINPR_ATTR_UNUSED LPDWORD lpThreadId)
837{
838 WLog_ERR(TAG, "not implemented");
839 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
840 return nullptr;
841}
842
843VOID ExitThread(DWORD dwExitCode)
844{
845#if defined(WITH_THREAD_LIST)
846 DWORD rc;
847 pthread_t tid = pthread_self();
848
849 if (!thread_list)
850 {
851 WLog_ERR(TAG, "function called without existing thread list!");
852#if defined(WITH_DEBUG_THREADS)
853 DumpThreadHandles();
854#endif
855 pthread_exit(0);
856 }
857 else if (!ListDictionary_Contains(thread_list, &tid))
858 {
859 WLog_ERR(TAG, "function called, but no matching entry in thread list!");
860#if defined(WITH_DEBUG_THREADS)
861 DumpThreadHandles();
862#endif
863 pthread_exit(0);
864 }
865 else
866 {
867 WINPR_THREAD* thread;
868 ListDictionary_Lock(thread_list);
869 thread = ListDictionary_GetItemValue(thread_list, &tid);
870 WINPR_ASSERT(thread);
871 thread->exited = TRUE;
872 thread->dwExitCode = dwExitCode;
873#if defined(WITH_DEBUG_THREADS)
874 thread->exit_stack = winpr_backtrace(20);
875#endif
876 ListDictionary_Unlock(thread_list);
877 set_event(thread);
878 rc = thread->dwExitCode;
879
880 if (thread->detached || !thread->started)
881 cleanup_handle(thread);
882
883 pthread_exit((void*)(size_t)rc);
884 }
885#else
886 WINPR_UNUSED(dwExitCode);
887#endif
888}
889
890BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)
891{
892 ULONG Type = 0;
893 WINPR_HANDLE* Object = nullptr;
894 WINPR_THREAD* thread = nullptr;
895
896 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
897 {
898 WLog_ERR(TAG, "hThread is not a thread");
899 SetLastError(ERROR_INVALID_PARAMETER);
900 return FALSE;
901 }
902
903 thread = (WINPR_THREAD*)Object;
904 *lpExitCode = thread->dwExitCode;
905 return TRUE;
906}
907
908WINPR_THREAD* winpr_GetCurrentThread(VOID)
909{
910 WINPR_THREAD* ret = nullptr;
911
912 if (!InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, nullptr, nullptr))
913 return nullptr;
914 if (mainThreadId == pthread_self())
915 return (HANDLE)&mainThread;
916
917 ret = pthread_getspecific(currentThreadTlsIndex);
918 return ret;
919}
920
921HANDLE _GetCurrentThread(VOID)
922{
923 return (HANDLE)winpr_GetCurrentThread();
924}
925
926DWORD GetCurrentThreadId(VOID)
927{
928#if defined(__FreeBSD__)
929 return WINPR_CXX_COMPAT_CAST(DWORD, pthread_getthreadid_np());
930#elif defined(__OpenBSD__)
931 return WINPR_CXX_COMPAT_CAST(DWORD, getthrid());
932#elif defined(__linux__)
933 return WINPR_CXX_COMPAT_CAST(DWORD, syscall(SYS_gettid));
934#else
935 pthread_t tid = pthread_self();
936 /* Since pthread_t can be 64-bits on some systems, take just the */
937 /* lower 32-bits of it for the thread ID returned by this function. */
938 uintptr_t ptid = WINPR_REINTERPRET_CAST(tid, pthread_t, uintptr_t);
939#if UINTPTR_MAX > UINT32_MAX
940 return (ptid & UINT32_MAX) ^ (ptid >> 32);
941#else
942 /* A 32-bit pthread_t already fits. Shifting it by 32 is undefined behaviour and
943 * WebAssembly masks the shift count to 0, so the expression above returned
944 * ptid ^ ptid == 0 for every thread, and critical sections took any thread for
945 * the owner. */
946 return WINPR_CXX_COMPAT_CAST(DWORD, ptid);
947#endif
948#endif
949}
950
951typedef struct
952{
953 WINPR_APC_ITEM apc;
954 PAPCFUNC completion;
955 ULONG_PTR completionArg;
956} UserApcItem;
957
958static void userAPC(LPVOID arg)
959{
960 UserApcItem* userApc = (UserApcItem*)arg;
961
962 userApc->completion(userApc->completionArg);
963
964 userApc->apc.markedForRemove = TRUE;
965}
966
967DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData)
968{
969 ULONG Type = 0;
970 WINPR_HANDLE* Object = nullptr;
971 WINPR_APC_ITEM* apc = nullptr;
972 UserApcItem* apcItem = nullptr;
973
974 if (!pfnAPC)
975 return 1;
976
977 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
978 {
979 WLog_ERR(TAG, "hThread is not a thread");
980 SetLastError(ERROR_INVALID_PARAMETER);
981 return (DWORD)0;
982 }
983
984 apcItem = calloc(1, sizeof(*apcItem));
985 if (!apcItem)
986 {
987 SetLastError(ERROR_INVALID_PARAMETER);
988 return (DWORD)0;
989 }
990
991 apc = &apcItem->apc;
992 apc->type = APC_TYPE_USER;
993 apc->markedForFree = TRUE;
994 apc->alwaysSignaled = TRUE;
995 apc->completion = userAPC;
996 apc->completionArgs = apc;
997 apcItem->completion = pfnAPC;
998 apcItem->completionArg = dwData;
999 apc_register(hThread, apc);
1000 return 1;
1001}
1002
1003DWORD ResumeThread(HANDLE hThread)
1004{
1005 ULONG Type = 0;
1006 WINPR_HANDLE* Object = nullptr;
1007 WINPR_THREAD* thread = nullptr;
1008
1009 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
1010 {
1011 WLog_ERR(TAG, "hThread is not a thread");
1012 SetLastError(ERROR_INVALID_PARAMETER);
1013 return (DWORD)-1;
1014 }
1015
1016 thread = (WINPR_THREAD*)Object;
1017
1018 if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
1019 return (DWORD)-1;
1020
1021 if (!thread->started)
1022 {
1023 if (!winpr_StartThread(thread))
1024 {
1025 run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex);
1026 return (DWORD)-1;
1027 }
1028 }
1029 else
1030 WLog_WARN(TAG, "Thread already started!");
1031
1032 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1033 return (DWORD)-1;
1034
1035 return 0;
1036}
1037
1038DWORD SuspendThread(WINPR_ATTR_UNUSED HANDLE hThread)
1039{
1040 WLog_ERR(TAG, "not implemented");
1041 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1042 return (DWORD)-1;
1043}
1044
1045BOOL SwitchToThread(VOID)
1046{
1051 if (sched_yield() != 0)
1052 usleep(1);
1053
1054 return TRUE;
1055}
1056
1057BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode)
1058{
1059 ULONG Type = 0;
1060 WINPR_HANDLE* Object = nullptr;
1061 WINPR_THREAD* thread = nullptr;
1062
1063 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
1064 return FALSE;
1065
1066 thread = (WINPR_THREAD*)Object;
1067 thread->exited = TRUE;
1068 thread->dwExitCode = dwExitCode;
1069
1070 if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
1071 return FALSE;
1072
1073#ifndef ANDROID
1074 pthread_cancel(thread->thread);
1075#else
1076 WLog_ERR(TAG, "Function not supported on this platform!");
1077#endif
1078
1079 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1080 return FALSE;
1081
1082 set_event(thread);
1083 return TRUE;
1084}
1085
1086VOID DumpThreadHandles(void)
1087{
1088#if defined(WITH_DEBUG_THREADS)
1089 char** msg = nullptr;
1090 size_t used = 0;
1091 void* stack = winpr_backtrace(20);
1092 WLog_DBG(TAG, "---------------- Called from ----------------------------");
1093 msg = winpr_backtrace_symbols(stack, &used);
1094
1095 for (size_t i = 0; i < used; i++)
1096 {
1097 WLog_DBG(TAG, "[%" PRIuz "]: %s", i, msg[i]);
1098 }
1099
1100 free((void*)msg);
1101 winpr_backtrace_free(stack);
1102 WLog_DBG(TAG, "---------------- Start Dumping thread handles -----------");
1103
1104#if defined(WITH_THREAD_LIST)
1105 if (!thread_list)
1106 {
1107 WLog_DBG(TAG, "All threads properly shut down and disposed of.");
1108 }
1109 else
1110 {
1111 ULONG_PTR* keys = nullptr;
1112 ListDictionary_Lock(thread_list);
1113 int x, count = ListDictionary_GetKeys(thread_list, &keys);
1114 WLog_DBG(TAG, "Dumping %d elements", count);
1115
1116 for (size_t x = 0; x < count; x++)
1117 {
1118 WINPR_THREAD* thread = ListDictionary_GetItemValue(thread_list, (void*)keys[x]);
1119 WLog_DBG(TAG, "Thread [%d] handle created still not closed!", x);
1120 msg = winpr_backtrace_symbols(thread->create_stack, &used);
1121
1122 for (size_t i = 0; i < used; i++)
1123 {
1124 WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1125 }
1126
1127 free(msg);
1128
1129 if (thread->started)
1130 {
1131 WLog_DBG(TAG, "Thread [%d] still running!", x);
1132 }
1133 else
1134 {
1135 WLog_DBG(TAG, "Thread [%d] exited at:", x);
1136 msg = winpr_backtrace_symbols(thread->exit_stack, &used);
1137
1138 for (size_t i = 0; i < used; i++)
1139 WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1140
1141 free(msg);
1142 }
1143 }
1144
1145 free(keys);
1146 ListDictionary_Unlock(thread_list);
1147 }
1148#endif
1149
1150 WLog_DBG(TAG, "---------------- End Dumping thread handles -------------");
1151#endif
1152}
1153#endif