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, "[%" PRIdz "]: %s", i, msg[i]);
373
374 free(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, "[%" PRIdz "]: %s", i, msg[i]);
381
382 free(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, "[%" PRIdz "]: %s", i, msg[i]);
399
400 free(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 return (ptid & UINT32_MAX) ^ (ptid >> 32);
923#endif
924}
925
926typedef struct
927{
928 WINPR_APC_ITEM apc;
929 PAPCFUNC completion;
930 ULONG_PTR completionArg;
931} UserApcItem;
932
933static void userAPC(LPVOID arg)
934{
935 UserApcItem* userApc = (UserApcItem*)arg;
936
937 userApc->completion(userApc->completionArg);
938
939 userApc->apc.markedForRemove = TRUE;
940}
941
942DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData)
943{
944 ULONG Type = 0;
945 WINPR_HANDLE* Object = nullptr;
946 WINPR_APC_ITEM* apc = nullptr;
947 UserApcItem* apcItem = nullptr;
948
949 if (!pfnAPC)
950 return 1;
951
952 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
953 {
954 WLog_ERR(TAG, "hThread is not a thread");
955 SetLastError(ERROR_INVALID_PARAMETER);
956 return (DWORD)0;
957 }
958
959 apcItem = calloc(1, sizeof(*apcItem));
960 if (!apcItem)
961 {
962 SetLastError(ERROR_INVALID_PARAMETER);
963 return (DWORD)0;
964 }
965
966 apc = &apcItem->apc;
967 apc->type = APC_TYPE_USER;
968 apc->markedForFree = TRUE;
969 apc->alwaysSignaled = TRUE;
970 apc->completion = userAPC;
971 apc->completionArgs = apc;
972 apcItem->completion = pfnAPC;
973 apcItem->completionArg = dwData;
974 apc_register(hThread, apc);
975 return 1;
976}
977
978DWORD ResumeThread(HANDLE hThread)
979{
980 ULONG Type = 0;
981 WINPR_HANDLE* Object = nullptr;
982 WINPR_THREAD* thread = nullptr;
983
984 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
985 {
986 WLog_ERR(TAG, "hThread is not a thread");
987 SetLastError(ERROR_INVALID_PARAMETER);
988 return (DWORD)-1;
989 }
990
991 thread = (WINPR_THREAD*)Object;
992
993 if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
994 return (DWORD)-1;
995
996 if (!thread->started)
997 {
998 if (!winpr_StartThread(thread))
999 {
1000 run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex);
1001 return (DWORD)-1;
1002 }
1003 }
1004 else
1005 WLog_WARN(TAG, "Thread already started!");
1006
1007 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1008 return (DWORD)-1;
1009
1010 return 0;
1011}
1012
1013DWORD SuspendThread(WINPR_ATTR_UNUSED HANDLE hThread)
1014{
1015 WLog_ERR(TAG, "not implemented");
1016 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1017 return (DWORD)-1;
1018}
1019
1020BOOL SwitchToThread(VOID)
1021{
1026 if (sched_yield() != 0)
1027 usleep(1);
1028
1029 return TRUE;
1030}
1031
1032BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode)
1033{
1034 ULONG Type = 0;
1035 WINPR_HANDLE* Object = nullptr;
1036 WINPR_THREAD* thread = nullptr;
1037
1038 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
1039 return FALSE;
1040
1041 thread = (WINPR_THREAD*)Object;
1042 thread->exited = TRUE;
1043 thread->dwExitCode = dwExitCode;
1044
1045 if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
1046 return FALSE;
1047
1048#ifndef ANDROID
1049 pthread_cancel(thread->thread);
1050#else
1051 WLog_ERR(TAG, "Function not supported on this platform!");
1052#endif
1053
1054 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1055 return FALSE;
1056
1057 set_event(thread);
1058 return TRUE;
1059}
1060
1061VOID DumpThreadHandles(void)
1062{
1063#if defined(WITH_DEBUG_THREADS)
1064 char** msg = nullptr;
1065 size_t used = 0;
1066 void* stack = winpr_backtrace(20);
1067 WLog_DBG(TAG, "---------------- Called from ----------------------------");
1068 msg = winpr_backtrace_symbols(stack, &used);
1069
1070 for (size_t i = 0; i < used; i++)
1071 {
1072 WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1073 }
1074
1075 free(msg);
1076 winpr_backtrace_free(stack);
1077 WLog_DBG(TAG, "---------------- Start Dumping thread handles -----------");
1078
1079#if defined(WITH_THREAD_LIST)
1080 if (!thread_list)
1081 {
1082 WLog_DBG(TAG, "All threads properly shut down and disposed of.");
1083 }
1084 else
1085 {
1086 ULONG_PTR* keys = nullptr;
1087 ListDictionary_Lock(thread_list);
1088 int x, count = ListDictionary_GetKeys(thread_list, &keys);
1089 WLog_DBG(TAG, "Dumping %d elements", count);
1090
1091 for (size_t x = 0; x < count; x++)
1092 {
1093 WINPR_THREAD* thread = ListDictionary_GetItemValue(thread_list, (void*)keys[x]);
1094 WLog_DBG(TAG, "Thread [%d] handle created still not closed!", x);
1095 msg = winpr_backtrace_symbols(thread->create_stack, &used);
1096
1097 for (size_t i = 0; i < used; i++)
1098 {
1099 WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1100 }
1101
1102 free(msg);
1103
1104 if (thread->started)
1105 {
1106 WLog_DBG(TAG, "Thread [%d] still running!", x);
1107 }
1108 else
1109 {
1110 WLog_DBG(TAG, "Thread [%d] exited at:", x);
1111 msg = winpr_backtrace_symbols(thread->exit_stack, &used);
1112
1113 for (size_t i = 0; i < used; i++)
1114 WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1115
1116 free(msg);
1117 }
1118 }
1119
1120 free(keys);
1121 ListDictionary_Unlock(thread_list);
1122 }
1123#endif
1124
1125 WLog_DBG(TAG, "---------------- End Dumping thread handles -------------");
1126#endif
1127}
1128#endif