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 %" PRIu64 ", unable to set current thread value",
533 WINPR_CXX_COMPAT_CAST(uint64_t, pthread_self()));
534 goto exit;
535 }
536
537 if (!(fkt = thread->lpStartAddress))
538 {
539 union
540 {
541 LPTHREAD_START_ROUTINE fkt;
542 void* pv;
543 } cnv;
544 cnv.fkt = fkt;
545 WLog_ERR(TAG, "Thread function argument is %p", cnv.pv);
546 goto exit;
547 }
548
549 if (!signal_and_wait_for_ready(thread))
550 goto exit;
551
552 rc = fkt(thread->lpParameter);
553exit:
554
555 if (thread)
556 {
557 apc_cleanupThread(thread);
558
559 if (!thread->exited)
560 thread->dwExitCode = rc;
561
562 set_event(thread);
563
564 (void)signal_thread_ready(thread);
565
566 if (thread->detached || !thread->started)
567 cleanup_handle(thread);
568 }
569
570 return NULL;
571}
572
573static BOOL winpr_StartThread(WINPR_THREAD* thread)
574{
575 BOOL rc = FALSE;
576 BOOL locked = FALSE;
577 pthread_attr_t attr = { 0 };
578
579 if (!mux_condition_bundle_lock(&thread->isCreated))
580 return FALSE;
581 locked = TRUE;
582
583 pthread_attr_init(&attr);
584 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
585
586 if (thread->dwStackSize > 0)
587 pthread_attr_setstacksize(&attr, thread->dwStackSize);
588
589 thread->started = TRUE;
590 reset_event(thread);
591
592#if defined(WITH_THREAD_LIST)
593 if (!ListDictionary_Add(thread_list, &thread->thread, thread))
594 {
595 WLog_ERR(TAG, "failed to add the thread to the thread list");
596 goto error;
597 }
598#endif
599
600 if (pthread_create(&thread->thread, &attr, thread_launcher, thread))
601 goto error;
602
603 if (!mux_condition_bundle_wait(&thread->isCreated, "threadIsCreated"))
604 goto error;
605
606 locked = FALSE;
607 if (!mux_condition_bundle_unlock(&thread->isCreated))
608 goto error;
609
610 if (!signal_thread_is_running(thread))
611 {
612 WLog_ERR(TAG, "failed to signal the thread was ready");
613 goto error;
614 }
615
616 rc = TRUE;
617error:
618 if (locked)
619 {
620 if (!mux_condition_bundle_unlock(&thread->isCreated))
621 rc = FALSE;
622 }
623
624 pthread_attr_destroy(&attr);
625
626 if (rc)
627 dump_thread(thread);
628
629 return rc;
630}
631
632BOOL SetThreadPriority(HANDLE hThread, int nPriority)
633{
634 ULONG Type = 0;
635 WINPR_HANDLE* Object = NULL;
636
637 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
638 return FALSE;
639
640 const int min = 19;
641 const int max = 0;
642 const int diff = (max - min);
643 const int normal = min + diff / 2;
644 const int off = MIN(1, diff / 4);
645 int sched_priority = -1;
646
647 switch (nPriority & ~(THREAD_MODE_BACKGROUND_BEGIN | THREAD_MODE_BACKGROUND_END))
648 {
649 case THREAD_PRIORITY_ABOVE_NORMAL:
650 sched_priority = MIN(normal + off, max);
651 break;
652 case THREAD_PRIORITY_BELOW_NORMAL:
653 sched_priority = MAX(normal - off, min);
654 break;
655 case THREAD_PRIORITY_HIGHEST:
656 sched_priority = max;
657 break;
658 case THREAD_PRIORITY_IDLE:
659 sched_priority = min;
660 break;
661 case THREAD_PRIORITY_LOWEST:
662 sched_priority = min;
663 break;
664 case THREAD_PRIORITY_TIME_CRITICAL:
665 sched_priority = max;
666 break;
667 default:
668 case THREAD_PRIORITY_NORMAL:
669 sched_priority = normal;
670 break;
671 }
672#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L) && defined(PTHREAD_SETSCHEDPRIO)
673 WINPR_THREAD* thread = (WINPR_THREAD*)Object;
674 const int rc = pthread_setschedprio(thread->thread, sched_priority);
675 if (rc != 0)
676 {
677 char buffer[256] = { 0 };
678 WLog_ERR(TAG, "pthread_setschedprio(%d) %s [%d]", sched_priority,
679 winpr_strerror(rc, buffer, sizeof(buffer)), rc);
680 }
681 return rc == 0;
682#else
683 WLog_WARN(TAG, "pthread_setschedprio(%d) not implemented, requires POSIX 2008 or later",
684 sched_priority);
685 return TRUE;
686#endif
687}
688
689HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, size_t dwStackSize,
690 LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter,
691 DWORD dwCreationFlags, WINPR_ATTR_UNUSED LPDWORD lpThreadId)
692{
693 HANDLE handle = NULL;
694 WINPR_THREAD* thread = (WINPR_THREAD*)calloc(1, sizeof(WINPR_THREAD));
695
696 if (!thread)
697 return NULL;
698
699 thread->dwStackSize = dwStackSize;
700 thread->lpParameter = lpParameter;
701 thread->lpStartAddress = lpStartAddress;
702 thread->lpThreadAttributes = lpThreadAttributes;
703 thread->common.ops = &ops;
704#if defined(WITH_DEBUG_THREADS)
705 thread->create_stack = winpr_backtrace(20);
706 dump_thread(thread);
707#endif
708
709 if (!winpr_event_init(&thread->event))
710 {
711 WLog_ERR(TAG, "failed to create event");
712 goto fail;
713 }
714
715 if (!run_mutex_init(pthread_mutex_init, &thread->mutex, NULL))
716 {
717 WLog_ERR(TAG, "failed to initialize thread mutex");
718 goto fail;
719 }
720
721 if (!apc_init(&thread->apc))
722 {
723 WLog_ERR(TAG, "failed to initialize APC");
724 goto fail;
725 }
726
727 if (!mux_condition_bundle_init(&thread->isCreated))
728 goto fail;
729 if (!mux_condition_bundle_init(&thread->isRunning))
730 goto fail;
731
732 WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ);
733 handle = (HANDLE)thread;
734
735 InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
736
737 if (!(dwCreationFlags & CREATE_SUSPENDED))
738 {
739 if (!winpr_StartThread(thread))
740 goto fail;
741 }
742 else
743 {
744 if (!set_event(thread))
745 goto fail;
746 }
747
748 return handle;
749fail:
750 cleanup_handle(thread);
751 return NULL;
752}
753
754void cleanup_handle(void* obj)
755{
756 WINPR_THREAD* thread = (WINPR_THREAD*)obj;
757 if (!thread)
758 return;
759
760 if (!apc_uninit(&thread->apc))
761 WLog_ERR(TAG, "failed to destroy APC");
762
763 mux_condition_bundle_uninit(&thread->isCreated);
764 mux_condition_bundle_uninit(&thread->isRunning);
765 run_mutex_fkt(pthread_mutex_destroy, &thread->mutex);
766
767 winpr_event_uninit(&thread->event);
768
769#if defined(WITH_THREAD_LIST)
770 ListDictionary_Remove(thread_list, &thread->thread);
771#endif
772#if defined(WITH_DEBUG_THREADS)
773
774 if (thread->create_stack)
775 winpr_backtrace_free(thread->create_stack);
776
777 if (thread->exit_stack)
778 winpr_backtrace_free(thread->exit_stack);
779
780#endif
781 free(thread);
782}
783
784BOOL ThreadCloseHandle(HANDLE handle)
785{
786 WINPR_THREAD* thread = (WINPR_THREAD*)handle;
787
788#if defined(WITH_THREAD_LIST)
789 if (!thread_list)
790 {
791 WLog_ERR(TAG, "Thread list does not exist, check call!");
792 dump_thread(thread);
793 }
794 else if (!ListDictionary_Contains(thread_list, &thread->thread))
795 {
796 WLog_ERR(TAG, "Thread list does not contain this thread! check call!");
797 dump_thread(thread);
798 }
799 else
800 {
801 ListDictionary_Lock(thread_list);
802#endif
803 dump_thread(thread);
804
805 if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0))
806 {
807 WLog_DBG(TAG, "Thread running, setting to detached state!");
808 thread->detached = TRUE;
809 pthread_detach(thread->thread);
810 }
811 else
812 {
813 cleanup_handle(thread);
814 }
815
816#if defined(WITH_THREAD_LIST)
817 ListDictionary_Unlock(thread_list);
818 }
819#endif
820
821 return TRUE;
822}
823
824HANDLE CreateRemoteThread(WINPR_ATTR_UNUSED HANDLE hProcess,
825 WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpThreadAttributes,
826 WINPR_ATTR_UNUSED size_t dwStackSize,
827 WINPR_ATTR_UNUSED LPTHREAD_START_ROUTINE lpStartAddress,
828 WINPR_ATTR_UNUSED LPVOID lpParameter,
829 WINPR_ATTR_UNUSED DWORD dwCreationFlags,
830 WINPR_ATTR_UNUSED LPDWORD lpThreadId)
831{
832 WLog_ERR(TAG, "not implemented");
833 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
834 return NULL;
835}
836
837VOID ExitThread(DWORD dwExitCode)
838{
839#if defined(WITH_THREAD_LIST)
840 DWORD rc;
841 pthread_t tid = pthread_self();
842
843 if (!thread_list)
844 {
845 WLog_ERR(TAG, "function called without existing thread list!");
846#if defined(WITH_DEBUG_THREADS)
847 DumpThreadHandles();
848#endif
849 pthread_exit(0);
850 }
851 else if (!ListDictionary_Contains(thread_list, &tid))
852 {
853 WLog_ERR(TAG, "function called, but no matching entry in thread list!");
854#if defined(WITH_DEBUG_THREADS)
855 DumpThreadHandles();
856#endif
857 pthread_exit(0);
858 }
859 else
860 {
861 WINPR_THREAD* thread;
862 ListDictionary_Lock(thread_list);
863 thread = ListDictionary_GetItemValue(thread_list, &tid);
864 WINPR_ASSERT(thread);
865 thread->exited = TRUE;
866 thread->dwExitCode = dwExitCode;
867#if defined(WITH_DEBUG_THREADS)
868 thread->exit_stack = winpr_backtrace(20);
869#endif
870 ListDictionary_Unlock(thread_list);
871 set_event(thread);
872 rc = thread->dwExitCode;
873
874 if (thread->detached || !thread->started)
875 cleanup_handle(thread);
876
877 pthread_exit((void*)(size_t)rc);
878 }
879#else
880 WINPR_UNUSED(dwExitCode);
881#endif
882}
883
884BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)
885{
886 ULONG Type = 0;
887 WINPR_HANDLE* Object = NULL;
888 WINPR_THREAD* thread = NULL;
889
890 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
891 {
892 WLog_ERR(TAG, "hThread is not a thread");
893 SetLastError(ERROR_INVALID_PARAMETER);
894 return FALSE;
895 }
896
897 thread = (WINPR_THREAD*)Object;
898 *lpExitCode = thread->dwExitCode;
899 return TRUE;
900}
901
902WINPR_THREAD* winpr_GetCurrentThread(VOID)
903{
904 WINPR_THREAD* ret = NULL;
905
906 InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
907 if (mainThreadId == pthread_self())
908 return (HANDLE)&mainThread;
909
910 ret = TlsGetValue(currentThreadTlsIndex);
911 return ret;
912}
913
914HANDLE _GetCurrentThread(VOID)
915{
916 return (HANDLE)winpr_GetCurrentThread();
917}
918
919DWORD GetCurrentThreadId(VOID)
920{
921#if defined(__FreeBSD__)
922 return WINPR_CXX_COMPAT_CAST(DWORD, pthread_getthreadid_np());
923#elif defined(__linux__)
924 return WINPR_CXX_COMPAT_CAST(DWORD, syscall(SYS_gettid));
925#else
926 pthread_t tid = pthread_self();
927 /* Since pthread_t can be 64-bits on some systems, take just the */
928 /* lower 32-bits of it for the thread ID returned by this function. */
929 uintptr_t ptid = WINPR_REINTERPRET_CAST(tid, pthread_t, uintptr_t);
930 return (ptid & UINT32_MAX) ^ (ptid >> 32);
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 = NULL;
954 WINPR_APC_ITEM* apc = NULL;
955 UserApcItem* apcItem = NULL;
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 = NULL;
990 WINPR_THREAD* thread = NULL;
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 = NULL;
1044 WINPR_THREAD* thread = NULL;
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 = NULL;
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, "[%" PRIdz "]: %s", i, msg[i]);
1081 }
1082
1083 free(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 = NULL;
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