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 = nullptr;
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] = WINPR_C_ARRAY_INIT;
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] = WINPR_C_ARRAY_INIT;
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] = WINPR_C_ARRAY_INIT;
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] = WINPR_C_ARRAY_INIT;
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, nullptr))
224 return FALSE;
225
226 if (!run_cond_init(pthread_cond_init, &bundle->cond, nullptr))
227 return FALSE;
228 return TRUE;
229}
230
231static void mux_condition_bundle_uninit(mux_condition_bundle* bundle)
232{
233 mux_condition_bundle empty = WINPR_C_ARRAY_INIT;
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] = WINPR_C_ARRAY_INIT;
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, nullptr);
333
334 if (rc != 0)
335 {
336 char ebuffer[256] = WINPR_C_ARRAY_INIT;
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, ThreadCloseHandle, ThreadGetFd, ThreadCleanupHandle,
355 nullptr, nullptr, nullptr, nullptr,
356 nullptr, nullptr, nullptr, nullptr,
357 nullptr, nullptr, nullptr, nullptr,
358 nullptr, nullptr, nullptr, nullptr,
359 nullptr };
360
361static void dump_thread(WINPR_THREAD* thread)
362{
363#if defined(WITH_DEBUG_THREADS)
364 void* stack = winpr_backtrace(20);
365 char** msg = nullptr;
366 size_t used = 0;
367 WLog_DBG(TAG, "Called from:");
368 msg = winpr_backtrace_symbols(stack, &used);
369
370 for (size_t i = 0; i < used; i++)
371 WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
372
373 free(msg);
374 winpr_backtrace_free(stack);
375 WLog_DBG(TAG, "Thread handle created still not closed!");
376 msg = winpr_backtrace_symbols(thread->create_stack, &used);
377
378 for (size_t i = 0; i < used; i++)
379 WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
380
381 free(msg);
382
383 if (thread->started)
384 {
385 WLog_DBG(TAG, "Thread still running!");
386 }
387 else if (!thread->exit_stack)
388 {
389 WLog_DBG(TAG, "Thread suspended.");
390 }
391 else
392 {
393 WLog_DBG(TAG, "Thread exited at:");
394 msg = winpr_backtrace_symbols(thread->exit_stack, &used);
395
396 for (size_t i = 0; i < used; i++)
397 WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
398
399 free(msg);
400 }
401#else
402 WINPR_UNUSED(thread);
403#endif
404}
405
410static BOOL set_event(WINPR_THREAD* thread)
411{
412 return winpr_event_set(&thread->event);
413}
414
415static BOOL reset_event(WINPR_THREAD* thread)
416{
417 return winpr_event_reset(&thread->event);
418}
419
420#if defined(WITH_THREAD_LIST)
421static BOOL thread_compare(const void* a, const void* b)
422{
423 const pthread_t* p1 = a;
424 const pthread_t* p2 = b;
425 BOOL rc = pthread_equal(*p1, *p2);
426 return rc;
427}
428#endif
429
430static INIT_ONCE threads_InitOnce = INIT_ONCE_STATIC_INIT;
431static pthread_t mainThreadId;
432static DWORD currentThreadTlsIndex = TLS_OUT_OF_INDEXES;
433
434static BOOL initializeThreads(WINPR_ATTR_UNUSED PINIT_ONCE InitOnce,
435 WINPR_ATTR_UNUSED PVOID Parameter, WINPR_ATTR_UNUSED PVOID* Context)
436{
437 if (!apc_init(&mainThread.apc))
438 {
439 WLog_ERR(TAG, "failed to initialize APC");
440 goto out;
441 }
442
443 mainThread.common.Type = HANDLE_TYPE_THREAD;
444 mainThreadId = pthread_self();
445
446 currentThreadTlsIndex = TlsAlloc();
447 if (currentThreadTlsIndex == TLS_OUT_OF_INDEXES)
448 {
449 WLog_ERR(TAG, "Major bug, unable to allocate a TLS value for currentThread");
450 }
451
452#if defined(WITH_THREAD_LIST)
453 thread_list = ListDictionary_New(TRUE);
454
455 if (!thread_list)
456 {
457 WLog_ERR(TAG, "Couldn't create global thread list");
458 goto error_thread_list;
459 }
460
461 thread_list->objectKey.fnObjectEquals = thread_compare;
462#endif
463
464out:
465 return TRUE;
466}
467
468static BOOL signal_and_wait_for_ready(WINPR_THREAD* thread)
469{
470 BOOL res = FALSE;
471
472 WINPR_ASSERT(thread);
473
474 if (!mux_condition_bundle_lock(&thread->isRunning))
475 return FALSE;
476
477 if (!signal_thread_ready(thread))
478 goto fail;
479
480 if (!mux_condition_bundle_wait(&thread->isRunning, "threadIsRunning"))
481 goto fail;
482
483#if defined(WITH_THREAD_LIST)
484 if (!ListDictionary_Contains(thread_list, &thread->thread))
485 {
486 WLog_ERR(TAG, "Thread not in thread_list, startup failed!");
487 goto fail;
488 }
489#endif
490
491 res = TRUE;
492
493fail:
494 if (!mux_condition_bundle_unlock(&thread->isRunning))
495 return FALSE;
496
497 return res;
498}
499
500/* Thread launcher function responsible for registering
501 * cleanup handlers and calling pthread_exit, if not done
502 * in thread function. */
503static void* thread_launcher(void* arg)
504{
505 DWORD rc = 0;
506 WINPR_THREAD* thread = (WINPR_THREAD*)arg;
507 LPTHREAD_START_ROUTINE fkt = nullptr;
508
509 if (!thread)
510 {
511 WLog_ERR(TAG, "Called with invalid argument %p", arg);
512 goto exit;
513 }
514
515 if (!TlsSetValue(currentThreadTlsIndex, thread))
516 {
517 WLog_ERR(TAG, "thread %" PRIu64 ", unable to set current thread value",
518 WINPR_CXX_COMPAT_CAST(uint64_t, pthread_self()));
519 goto exit;
520 }
521
522 if (!(fkt = thread->lpStartAddress))
523 {
524 union
525 {
526 LPTHREAD_START_ROUTINE fkt;
527 void* pv;
528 } cnv;
529 cnv.fkt = fkt;
530 WLog_ERR(TAG, "Thread function argument is %p", cnv.pv);
531 goto exit;
532 }
533
534 if (!signal_and_wait_for_ready(thread))
535 goto exit;
536
537 rc = fkt(thread->lpParameter);
538exit:
539
540 if (thread)
541 {
542 apc_cleanupThread(thread);
543
544 if (!thread->exited)
545 thread->dwExitCode = rc;
546
547 set_event(thread);
548
549 (void)signal_thread_ready(thread);
550
551 if (thread->detached || !thread->started)
552 cleanup_handle(thread);
553 }
554
555 return nullptr;
556}
557
558static BOOL winpr_StartThread(WINPR_THREAD* thread)
559{
560 BOOL rc = FALSE;
561 BOOL locked = FALSE;
562 pthread_attr_t attr = WINPR_C_ARRAY_INIT;
563
564 if (!mux_condition_bundle_lock(&thread->isCreated))
565 return FALSE;
566 locked = TRUE;
567
568 pthread_attr_init(&attr);
569 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
570
571 if (thread->dwStackSize > 0)
572 pthread_attr_setstacksize(&attr, thread->dwStackSize);
573
574 thread->started = TRUE;
575 reset_event(thread);
576
577#if defined(WITH_THREAD_LIST)
578 if (!ListDictionary_Add(thread_list, &thread->thread, thread))
579 {
580 WLog_ERR(TAG, "failed to add the thread to the thread list");
581 goto error;
582 }
583#endif
584
585 if (pthread_create(&thread->thread, &attr, thread_launcher, thread))
586 goto error;
587
588 if (!mux_condition_bundle_wait(&thread->isCreated, "threadIsCreated"))
589 goto error;
590
591 locked = FALSE;
592 if (!mux_condition_bundle_unlock(&thread->isCreated))
593 goto error;
594
595 if (!signal_thread_is_running(thread))
596 {
597 WLog_ERR(TAG, "failed to signal the thread was ready");
598 goto error;
599 }
600
601 rc = TRUE;
602error:
603 if (locked)
604 {
605 if (!mux_condition_bundle_unlock(&thread->isCreated))
606 rc = FALSE;
607 }
608
609 pthread_attr_destroy(&attr);
610
611 if (rc)
612 dump_thread(thread);
613
614 return rc;
615}
616
617BOOL SetThreadPriority(HANDLE hThread, int nPriority)
618{
619 ULONG Type = 0;
620 WINPR_HANDLE* Object = nullptr;
621
622 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
623 return FALSE;
624
625 const int min = 19;
626 const int max = 0;
627 const int diff = (max - min);
628 const int normal = min + diff / 2;
629 const int off = MIN(1, diff / 4);
630 int sched_priority = -1;
631
632 switch (nPriority & ~(THREAD_MODE_BACKGROUND_BEGIN | THREAD_MODE_BACKGROUND_END))
633 {
634 case THREAD_PRIORITY_ABOVE_NORMAL:
635 sched_priority = MIN(normal + off, max);
636 break;
637 case THREAD_PRIORITY_BELOW_NORMAL:
638 sched_priority = MAX(normal - off, min);
639 break;
640 case THREAD_PRIORITY_HIGHEST:
641 sched_priority = max;
642 break;
643 case THREAD_PRIORITY_IDLE:
644 sched_priority = min;
645 break;
646 case THREAD_PRIORITY_LOWEST:
647 sched_priority = min;
648 break;
649 case THREAD_PRIORITY_TIME_CRITICAL:
650 sched_priority = max;
651 break;
652 default:
653 case THREAD_PRIORITY_NORMAL:
654 sched_priority = normal;
655 break;
656 }
657#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L) && defined(PTHREAD_SETSCHEDPRIO)
658 WINPR_THREAD* thread = (WINPR_THREAD*)Object;
659 const int rc = pthread_setschedprio(thread->thread, sched_priority);
660 if (rc != 0)
661 {
662 char buffer[256] = WINPR_C_ARRAY_INIT;
663 WLog_ERR(TAG, "pthread_setschedprio(%d) %s [%d]", sched_priority,
664 winpr_strerror(rc, buffer, sizeof(buffer)), rc);
665 }
666 return rc == 0;
667#else
668 WLog_WARN(TAG, "pthread_setschedprio(%d) not implemented, requires POSIX 2008 or later",
669 sched_priority);
670 return TRUE;
671#endif
672}
673
674HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, size_t dwStackSize,
675 LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter,
676 DWORD dwCreationFlags, WINPR_ATTR_UNUSED LPDWORD lpThreadId)
677{
678 HANDLE handle = nullptr;
679 WINPR_THREAD* thread = (WINPR_THREAD*)calloc(1, sizeof(WINPR_THREAD));
680
681 if (!thread)
682 return nullptr;
683
684 thread->dwStackSize = dwStackSize;
685 thread->lpParameter = lpParameter;
686 thread->lpStartAddress = lpStartAddress;
687 thread->lpThreadAttributes = lpThreadAttributes;
688 thread->common.ops = &ops;
689#if defined(WITH_DEBUG_THREADS)
690 thread->create_stack = winpr_backtrace(20);
691 dump_thread(thread);
692#endif
693
694 if (!winpr_event_init(&thread->event))
695 {
696 WLog_ERR(TAG, "failed to create event");
697 goto fail;
698 }
699
700 if (!run_mutex_init(pthread_mutex_init, &thread->mutex, nullptr))
701 {
702 WLog_ERR(TAG, "failed to initialize thread mutex");
703 goto fail;
704 }
705
706 if (!apc_init(&thread->apc))
707 {
708 WLog_ERR(TAG, "failed to initialize APC");
709 goto fail;
710 }
711
712 if (!mux_condition_bundle_init(&thread->isCreated))
713 goto fail;
714 if (!mux_condition_bundle_init(&thread->isRunning))
715 goto fail;
716
717 WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ);
718 handle = (HANDLE)thread;
719
720 if (!InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, nullptr, nullptr))
721 goto fail;
722
723 if (!(dwCreationFlags & CREATE_SUSPENDED))
724 {
725 if (!winpr_StartThread(thread))
726 goto fail;
727 }
728 else
729 {
730 if (!set_event(thread))
731 goto fail;
732 }
733
734 return handle;
735fail:
736 cleanup_handle(thread);
737 return nullptr;
738}
739
740void cleanup_handle(void* obj)
741{
742 WINPR_THREAD* thread = (WINPR_THREAD*)obj;
743 if (!thread)
744 return;
745
746 if (!apc_uninit(&thread->apc))
747 WLog_ERR(TAG, "failed to destroy APC");
748
749 mux_condition_bundle_uninit(&thread->isCreated);
750 mux_condition_bundle_uninit(&thread->isRunning);
751 run_mutex_fkt(pthread_mutex_destroy, &thread->mutex);
752
753 winpr_event_uninit(&thread->event);
754
755#if defined(WITH_THREAD_LIST)
756 ListDictionary_Remove(thread_list, &thread->thread);
757#endif
758#if defined(WITH_DEBUG_THREADS)
759
760 if (thread->create_stack)
761 winpr_backtrace_free(thread->create_stack);
762
763 if (thread->exit_stack)
764 winpr_backtrace_free(thread->exit_stack);
765
766#endif
767 free(thread);
768}
769
770BOOL ThreadCloseHandle(HANDLE handle)
771{
772 WINPR_THREAD* thread = (WINPR_THREAD*)handle;
773
774#if defined(WITH_THREAD_LIST)
775 if (!thread_list)
776 {
777 WLog_ERR(TAG, "Thread list does not exist, check call!");
778 dump_thread(thread);
779 }
780 else if (!ListDictionary_Contains(thread_list, &thread->thread))
781 {
782 WLog_ERR(TAG, "Thread list does not contain this thread! check call!");
783 dump_thread(thread);
784 }
785 else
786 {
787 ListDictionary_Lock(thread_list);
788#endif
789 dump_thread(thread);
790
791 if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0))
792 {
793 WLog_DBG(TAG, "Thread running, setting to detached state!");
794 thread->detached = TRUE;
795 pthread_detach(thread->thread);
796 }
797 else
798 {
799 cleanup_handle(thread);
800 }
801
802#if defined(WITH_THREAD_LIST)
803 ListDictionary_Unlock(thread_list);
804 }
805#endif
806
807 return TRUE;
808}
809
810HANDLE CreateRemoteThread(WINPR_ATTR_UNUSED HANDLE hProcess,
811 WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpThreadAttributes,
812 WINPR_ATTR_UNUSED size_t dwStackSize,
813 WINPR_ATTR_UNUSED LPTHREAD_START_ROUTINE lpStartAddress,
814 WINPR_ATTR_UNUSED LPVOID lpParameter,
815 WINPR_ATTR_UNUSED DWORD dwCreationFlags,
816 WINPR_ATTR_UNUSED LPDWORD lpThreadId)
817{
818 WLog_ERR(TAG, "not implemented");
819 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
820 return nullptr;
821}
822
823VOID ExitThread(DWORD dwExitCode)
824{
825#if defined(WITH_THREAD_LIST)
826 DWORD rc;
827 pthread_t tid = pthread_self();
828
829 if (!thread_list)
830 {
831 WLog_ERR(TAG, "function called without existing thread list!");
832#if defined(WITH_DEBUG_THREADS)
833 DumpThreadHandles();
834#endif
835 pthread_exit(0);
836 }
837 else if (!ListDictionary_Contains(thread_list, &tid))
838 {
839 WLog_ERR(TAG, "function called, but no matching entry in thread list!");
840#if defined(WITH_DEBUG_THREADS)
841 DumpThreadHandles();
842#endif
843 pthread_exit(0);
844 }
845 else
846 {
847 WINPR_THREAD* thread;
848 ListDictionary_Lock(thread_list);
849 thread = ListDictionary_GetItemValue(thread_list, &tid);
850 WINPR_ASSERT(thread);
851 thread->exited = TRUE;
852 thread->dwExitCode = dwExitCode;
853#if defined(WITH_DEBUG_THREADS)
854 thread->exit_stack = winpr_backtrace(20);
855#endif
856 ListDictionary_Unlock(thread_list);
857 set_event(thread);
858 rc = thread->dwExitCode;
859
860 if (thread->detached || !thread->started)
861 cleanup_handle(thread);
862
863 pthread_exit((void*)(size_t)rc);
864 }
865#else
866 WINPR_UNUSED(dwExitCode);
867#endif
868}
869
870BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)
871{
872 ULONG Type = 0;
873 WINPR_HANDLE* Object = nullptr;
874 WINPR_THREAD* thread = nullptr;
875
876 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
877 {
878 WLog_ERR(TAG, "hThread is not a thread");
879 SetLastError(ERROR_INVALID_PARAMETER);
880 return FALSE;
881 }
882
883 thread = (WINPR_THREAD*)Object;
884 *lpExitCode = thread->dwExitCode;
885 return TRUE;
886}
887
888WINPR_THREAD* winpr_GetCurrentThread(VOID)
889{
890 WINPR_THREAD* ret = nullptr;
891
892 if (!InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, nullptr, nullptr))
893 return nullptr;
894 if (mainThreadId == pthread_self())
895 return (HANDLE)&mainThread;
896
897 ret = TlsGetValue(currentThreadTlsIndex);
898 return ret;
899}
900
901HANDLE _GetCurrentThread(VOID)
902{
903 return (HANDLE)winpr_GetCurrentThread();
904}
905
906DWORD GetCurrentThreadId(VOID)
907{
908#if defined(__FreeBSD__)
909 return WINPR_CXX_COMPAT_CAST(DWORD, pthread_getthreadid_np());
910#elif defined(__linux__)
911 return WINPR_CXX_COMPAT_CAST(DWORD, syscall(SYS_gettid));
912#else
913 pthread_t tid = pthread_self();
914 /* Since pthread_t can be 64-bits on some systems, take just the */
915 /* lower 32-bits of it for the thread ID returned by this function. */
916 uintptr_t ptid = WINPR_REINTERPRET_CAST(tid, pthread_t, uintptr_t);
917 return (ptid & UINT32_MAX) ^ (ptid >> 32);
918#endif
919}
920
921typedef struct
922{
923 WINPR_APC_ITEM apc;
924 PAPCFUNC completion;
925 ULONG_PTR completionArg;
926} UserApcItem;
927
928static void userAPC(LPVOID arg)
929{
930 UserApcItem* userApc = (UserApcItem*)arg;
931
932 userApc->completion(userApc->completionArg);
933
934 userApc->apc.markedForRemove = TRUE;
935}
936
937DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData)
938{
939 ULONG Type = 0;
940 WINPR_HANDLE* Object = nullptr;
941 WINPR_APC_ITEM* apc = nullptr;
942 UserApcItem* apcItem = nullptr;
943
944 if (!pfnAPC)
945 return 1;
946
947 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
948 {
949 WLog_ERR(TAG, "hThread is not a thread");
950 SetLastError(ERROR_INVALID_PARAMETER);
951 return (DWORD)0;
952 }
953
954 apcItem = calloc(1, sizeof(*apcItem));
955 if (!apcItem)
956 {
957 SetLastError(ERROR_INVALID_PARAMETER);
958 return (DWORD)0;
959 }
960
961 apc = &apcItem->apc;
962 apc->type = APC_TYPE_USER;
963 apc->markedForFree = TRUE;
964 apc->alwaysSignaled = TRUE;
965 apc->completion = userAPC;
966 apc->completionArgs = apc;
967 apcItem->completion = pfnAPC;
968 apcItem->completionArg = dwData;
969 apc_register(hThread, apc);
970 return 1;
971}
972
973DWORD ResumeThread(HANDLE hThread)
974{
975 ULONG Type = 0;
976 WINPR_HANDLE* Object = nullptr;
977 WINPR_THREAD* thread = nullptr;
978
979 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
980 {
981 WLog_ERR(TAG, "hThread is not a thread");
982 SetLastError(ERROR_INVALID_PARAMETER);
983 return (DWORD)-1;
984 }
985
986 thread = (WINPR_THREAD*)Object;
987
988 if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
989 return (DWORD)-1;
990
991 if (!thread->started)
992 {
993 if (!winpr_StartThread(thread))
994 {
995 run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex);
996 return (DWORD)-1;
997 }
998 }
999 else
1000 WLog_WARN(TAG, "Thread already started!");
1001
1002 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1003 return (DWORD)-1;
1004
1005 return 0;
1006}
1007
1008DWORD SuspendThread(WINPR_ATTR_UNUSED HANDLE hThread)
1009{
1010 WLog_ERR(TAG, "not implemented");
1011 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1012 return (DWORD)-1;
1013}
1014
1015BOOL SwitchToThread(VOID)
1016{
1021 if (sched_yield() != 0)
1022 usleep(1);
1023
1024 return TRUE;
1025}
1026
1027BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode)
1028{
1029 ULONG Type = 0;
1030 WINPR_HANDLE* Object = nullptr;
1031 WINPR_THREAD* thread = nullptr;
1032
1033 if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
1034 return FALSE;
1035
1036 thread = (WINPR_THREAD*)Object;
1037 thread->exited = TRUE;
1038 thread->dwExitCode = dwExitCode;
1039
1040 if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
1041 return FALSE;
1042
1043#ifndef ANDROID
1044 pthread_cancel(thread->thread);
1045#else
1046 WLog_ERR(TAG, "Function not supported on this platform!");
1047#endif
1048
1049 if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1050 return FALSE;
1051
1052 set_event(thread);
1053 return TRUE;
1054}
1055
1056VOID DumpThreadHandles(void)
1057{
1058#if defined(WITH_DEBUG_THREADS)
1059 char** msg = nullptr;
1060 size_t used = 0;
1061 void* stack = winpr_backtrace(20);
1062 WLog_DBG(TAG, "---------------- Called from ----------------------------");
1063 msg = winpr_backtrace_symbols(stack, &used);
1064
1065 for (size_t i = 0; i < used; i++)
1066 {
1067 WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1068 }
1069
1070 free(msg);
1071 winpr_backtrace_free(stack);
1072 WLog_DBG(TAG, "---------------- Start Dumping thread handles -----------");
1073
1074#if defined(WITH_THREAD_LIST)
1075 if (!thread_list)
1076 {
1077 WLog_DBG(TAG, "All threads properly shut down and disposed of.");
1078 }
1079 else
1080 {
1081 ULONG_PTR* keys = nullptr;
1082 ListDictionary_Lock(thread_list);
1083 int x, count = ListDictionary_GetKeys(thread_list, &keys);
1084 WLog_DBG(TAG, "Dumping %d elements", count);
1085
1086 for (size_t x = 0; x < count; x++)
1087 {
1088 WINPR_THREAD* thread = ListDictionary_GetItemValue(thread_list, (void*)keys[x]);
1089 WLog_DBG(TAG, "Thread [%d] handle created still not closed!", x);
1090 msg = winpr_backtrace_symbols(thread->create_stack, &used);
1091
1092 for (size_t i = 0; i < used; i++)
1093 {
1094 WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1095 }
1096
1097 free(msg);
1098
1099 if (thread->started)
1100 {
1101 WLog_DBG(TAG, "Thread [%d] still running!", x);
1102 }
1103 else
1104 {
1105 WLog_DBG(TAG, "Thread [%d] exited at:", x);
1106 msg = winpr_backtrace_symbols(thread->exit_stack, &used);
1107
1108 for (size_t i = 0; i < used; i++)
1109 WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1110
1111 free(msg);
1112 }
1113 }
1114
1115 free(keys);
1116 ListDictionary_Unlock(thread_list);
1117 }
1118#endif
1119
1120 WLog_DBG(TAG, "---------------- End Dumping thread handles -------------");
1121#endif
1122}
1123#endif