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