Coverage Report

Created: 2025-08-26 06:31

/src/FreeRDP/winpr/libwinpr/thread/thread.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Process Thread Functions
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2015 Hewlett-Packard Development Company, L.P.
7
 * Copyright 2021 David Fort <contact@hardening-consulting.com>
8
 *
9
 *
10
 * Licensed under the Apache License, Version 2.0 (the "License");
11
 * you may not use this file except in compliance with the License.
12
 * You may obtain a copy of the License at
13
 *
14
 *     http://www.apache.org/licenses/LICENSE-2.0
15
 *
16
 * Unless required by applicable law or agreed to in writing, software
17
 * distributed under the License is distributed on an "AS IS" BASIS,
18
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
 * See the License for the specific language governing permissions and
20
 * limitations under the License.
21
 */
22
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
0
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
40
#endif
41
42
#ifndef MAX
43
0
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
44
#endif
45
46
/**
47
 * api-ms-win-core-processthreads-l1-1-1.dll
48
 *
49
 * CreateRemoteThread
50
 * CreateRemoteThreadEx
51
 * CreateThread
52
 * DeleteProcThreadAttributeList
53
 * ExitThread
54
 * FlushInstructionCache
55
 * FlushProcessWriteBuffers
56
 * GetCurrentThread
57
 * GetCurrentThreadId
58
 * GetCurrentThreadStackLimits
59
 * GetExitCodeThread
60
 * GetPriorityClass
61
 * GetStartupInfoW
62
 * GetThreadContext
63
 * GetThreadId
64
 * GetThreadIdealProcessorEx
65
 * GetThreadPriority
66
 * GetThreadPriorityBoost
67
 * GetThreadTimes
68
 * InitializeProcThreadAttributeList
69
 * OpenThread
70
 * OpenThreadToken
71
 * QueryProcessAffinityUpdateMode
72
 * QueueUserAPC
73
 * ResumeThread
74
 * SetPriorityClass
75
 * SetThreadContext
76
 * SetThreadPriority
77
 * SetThreadPriorityBoost
78
 * SetThreadStackGuarantee
79
 * SetThreadToken
80
 * SuspendThread
81
 * SwitchToThread
82
 * TerminateThread
83
 * UpdateProcThreadAttribute
84
 */
85
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
114
static WINPR_THREAD mainThread;
115
116
#if defined(WITH_THREAD_LIST)
117
static wListDictionary* thread_list = NULL;
118
#endif
119
120
static BOOL ThreadCloseHandle(HANDLE handle);
121
static void cleanup_handle(void* obj);
122
123
static BOOL ThreadIsHandled(HANDLE handle)
124
0
{
125
0
  return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_THREAD, FALSE);
126
0
}
127
128
static int ThreadGetFd(HANDLE handle)
129
0
{
130
0
  WINPR_THREAD* pThread = (WINPR_THREAD*)handle;
131
132
0
  if (!ThreadIsHandled(handle))
133
0
    return -1;
134
135
0
  return pThread->event.fds[0];
136
0
}
137
138
0
#define run_mutex_init(fkt, mux, arg) run_mutex_init_(fkt, #fkt, mux, arg)
139
static 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
0
{
143
0
  int rc = 0;
144
145
0
  WINPR_ASSERT(fkt);
146
0
  WINPR_ASSERT(mutex);
147
148
0
  rc = fkt(mutex, mutexattr);
149
0
  if (rc != 0)
150
0
  {
151
0
    char ebuffer[256] = { 0 };
152
0
    WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
153
0
  }
154
0
  return rc == 0;
155
0
}
156
157
0
#define run_mutex_fkt(fkt, mux) run_mutex_fkt_(fkt, #fkt, mux)
158
static BOOL run_mutex_fkt_(int (*fkt)(pthread_mutex_t* mux), const char* name,
159
                           pthread_mutex_t* mutex)
160
0
{
161
0
  int rc = 0;
162
163
0
  WINPR_ASSERT(fkt);
164
0
  WINPR_ASSERT(mutex);
165
166
0
  rc = fkt(mutex);
167
0
  if (rc != 0)
168
0
  {
169
0
    char ebuffer[256] = { 0 };
170
0
    WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
171
0
  }
172
0
  return rc == 0;
173
0
}
174
175
0
#define run_cond_init(fkt, cond, arg) run_cond_init_(fkt, #fkt, cond, arg)
176
static 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
0
{
179
0
  int rc = 0;
180
181
0
  WINPR_ASSERT(fkt);
182
0
  WINPR_ASSERT(condition);
183
184
0
  rc = fkt(condition, conditionattr);
185
0
  if (rc != 0)
186
0
  {
187
0
    char ebuffer[256] = { 0 };
188
0
    WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
189
0
  }
190
0
  return rc == 0;
191
0
}
192
193
0
#define run_cond_fkt(fkt, cond) run_cond_fkt_(fkt, #fkt, cond)
194
static BOOL run_cond_fkt_(int (*fkt)(pthread_cond_t* mux), const char* name,
195
                          pthread_cond_t* condition)
196
0
{
197
0
  int rc = 0;
198
199
0
  WINPR_ASSERT(fkt);
200
0
  WINPR_ASSERT(condition);
201
202
0
  rc = fkt(condition);
203
0
  if (rc != 0)
204
0
  {
205
0
    char ebuffer[256] = { 0 };
206
0
    WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
207
0
  }
208
0
  return rc == 0;
209
0
}
210
211
static int pthread_mutex_checked_unlock(pthread_mutex_t* mutex)
212
0
{
213
0
  WINPR_ASSERT(mutex);
214
0
  WINPR_ASSERT(pthread_mutex_trylock(mutex) == EBUSY);
215
0
  return pthread_mutex_unlock(mutex);
216
0
}
217
218
static BOOL mux_condition_bundle_init(mux_condition_bundle* bundle)
219
0
{
220
0
  WINPR_ASSERT(bundle);
221
222
0
  bundle->val = FALSE;
223
0
  if (!run_mutex_init(pthread_mutex_init, &bundle->mux, NULL))
224
0
    return FALSE;
225
226
0
  if (!run_cond_init(pthread_cond_init, &bundle->cond, NULL))
227
0
    return FALSE;
228
0
  return TRUE;
229
0
}
230
231
static void mux_condition_bundle_uninit(mux_condition_bundle* bundle)
232
0
{
233
0
  mux_condition_bundle empty = { 0 };
234
235
0
  WINPR_ASSERT(bundle);
236
237
0
  run_cond_fkt(pthread_cond_destroy, &bundle->cond);
238
0
  run_mutex_fkt(pthread_mutex_destroy, &bundle->mux);
239
0
  *bundle = empty;
240
0
}
241
242
static BOOL mux_condition_bundle_signal(mux_condition_bundle* bundle)
243
0
{
244
0
  BOOL rc = TRUE;
245
0
  WINPR_ASSERT(bundle);
246
247
0
  if (!run_mutex_fkt(pthread_mutex_lock, &bundle->mux))
248
0
    return FALSE;
249
0
  bundle->val = TRUE;
250
0
  if (!run_cond_fkt(pthread_cond_signal, &bundle->cond))
251
0
    rc = FALSE;
252
0
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux))
253
0
    rc = FALSE;
254
0
  return rc;
255
0
}
256
257
static BOOL mux_condition_bundle_lock(mux_condition_bundle* bundle)
258
0
{
259
0
  WINPR_ASSERT(bundle);
260
0
  return run_mutex_fkt(pthread_mutex_lock, &bundle->mux);
261
0
}
262
263
static BOOL mux_condition_bundle_unlock(mux_condition_bundle* bundle)
264
0
{
265
0
  WINPR_ASSERT(bundle);
266
0
  return run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux);
267
0
}
268
269
static BOOL mux_condition_bundle_wait(mux_condition_bundle* bundle, const char* name)
270
0
{
271
0
  BOOL rc = FALSE;
272
273
0
  WINPR_ASSERT(bundle);
274
0
  WINPR_ASSERT(name);
275
0
  WINPR_ASSERT(pthread_mutex_trylock(&bundle->mux) == EBUSY);
276
277
0
  while (!bundle->val)
278
0
  {
279
0
    int r = pthread_cond_wait(&bundle->cond, &bundle->mux);
280
0
    if (r != 0)
281
0
    {
282
0
      char ebuffer[256] = { 0 };
283
0
      WLog_ERR(TAG, "failed to wait for %s [%s]", name,
284
0
               winpr_strerror(r, ebuffer, sizeof(ebuffer)));
285
0
      switch (r)
286
0
      {
287
0
        case ENOTRECOVERABLE:
288
0
        case EPERM:
289
0
        case ETIMEDOUT:
290
0
        case EINVAL:
291
0
          goto fail;
292
293
0
        default:
294
0
          break;
295
0
      }
296
0
    }
297
0
  }
298
299
0
  rc = bundle->val;
300
301
0
fail:
302
0
  return rc;
303
0
}
304
305
static BOOL signal_thread_ready(WINPR_THREAD* thread)
306
0
{
307
0
  WINPR_ASSERT(thread);
308
309
0
  return mux_condition_bundle_signal(&thread->isCreated);
310
0
}
311
312
static BOOL signal_thread_is_running(WINPR_THREAD* thread)
313
0
{
314
0
  WINPR_ASSERT(thread);
315
316
0
  return mux_condition_bundle_signal(&thread->isRunning);
317
0
}
318
319
static DWORD ThreadCleanupHandle(HANDLE handle)
320
0
{
321
0
  DWORD status = WAIT_FAILED;
322
0
  WINPR_THREAD* thread = (WINPR_THREAD*)handle;
323
324
0
  if (!ThreadIsHandled(handle))
325
0
    return WAIT_FAILED;
326
327
0
  if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
328
0
    return WAIT_FAILED;
329
330
0
  if (!thread->joined)
331
0
  {
332
0
    int rc = pthread_join(thread->thread, NULL);
333
334
0
    if (rc != 0)
335
0
    {
336
0
      char ebuffer[256] = { 0 };
337
0
      WLog_ERR(TAG, "pthread_join failure: [%d] %s", rc,
338
0
               winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
339
0
      goto fail;
340
0
    }
341
0
    else
342
0
      thread->joined = TRUE;
343
0
  }
344
345
0
  status = WAIT_OBJECT_0;
346
347
0
fail:
348
0
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
349
0
    return WAIT_FAILED;
350
351
0
  return status;
352
0
}
353
354
static 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
376
static void dump_thread(WINPR_THREAD* thread)
377
0
{
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
0
  WINPR_UNUSED(thread);
418
0
#endif
419
0
}
420
421
/**
422
 * TODO: implement thread suspend/resume using pthreads
423
 * http://stackoverflow.com/questions/3140867/suspend-pthreads-without-using-condition
424
 */
425
static BOOL set_event(WINPR_THREAD* thread)
426
0
{
427
0
  return winpr_event_set(&thread->event);
428
0
}
429
430
static BOOL reset_event(WINPR_THREAD* thread)
431
0
{
432
0
  return winpr_event_reset(&thread->event);
433
0
}
434
435
#if defined(WITH_THREAD_LIST)
436
static 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
445
static INIT_ONCE threads_InitOnce = INIT_ONCE_STATIC_INIT;
446
static pthread_t mainThreadId;
447
static DWORD currentThreadTlsIndex = TLS_OUT_OF_INDEXES;
448
449
static BOOL initializeThreads(WINPR_ATTR_UNUSED PINIT_ONCE InitOnce,
450
                              WINPR_ATTR_UNUSED PVOID Parameter, WINPR_ATTR_UNUSED PVOID* Context)
451
0
{
452
0
  if (!apc_init(&mainThread.apc))
453
0
  {
454
0
    WLog_ERR(TAG, "failed to initialize APC");
455
0
    goto out;
456
0
  }
457
458
0
  mainThread.common.Type = HANDLE_TYPE_THREAD;
459
0
  mainThreadId = pthread_self();
460
461
0
  currentThreadTlsIndex = TlsAlloc();
462
0
  if (currentThreadTlsIndex == TLS_OUT_OF_INDEXES)
463
0
  {
464
0
    WLog_ERR(TAG, "Major bug, unable to allocate a TLS value for currentThread");
465
0
  }
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
479
0
out:
480
0
  return TRUE;
481
0
}
482
483
static BOOL signal_and_wait_for_ready(WINPR_THREAD* thread)
484
0
{
485
0
  BOOL res = FALSE;
486
487
0
  WINPR_ASSERT(thread);
488
489
0
  if (!mux_condition_bundle_lock(&thread->isRunning))
490
0
    return FALSE;
491
492
0
  if (!signal_thread_ready(thread))
493
0
    goto fail;
494
495
0
  if (!mux_condition_bundle_wait(&thread->isRunning, "threadIsRunning"))
496
0
    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
0
  res = TRUE;
507
508
0
fail:
509
0
  if (!mux_condition_bundle_unlock(&thread->isRunning))
510
0
    return FALSE;
511
512
0
  return res;
513
0
}
514
515
/* Thread launcher function responsible for registering
516
 * cleanup handlers and calling pthread_exit, if not done
517
 * in thread function. */
518
static void* thread_launcher(void* arg)
519
0
{
520
0
  DWORD rc = 0;
521
0
  WINPR_THREAD* thread = (WINPR_THREAD*)arg;
522
0
  LPTHREAD_START_ROUTINE fkt = NULL;
523
524
0
  if (!thread)
525
0
  {
526
0
    WLog_ERR(TAG, "Called with invalid argument %p", arg);
527
0
    goto exit;
528
0
  }
529
530
0
  if (!TlsSetValue(currentThreadTlsIndex, thread))
531
0
  {
532
0
    WLog_ERR(TAG, "thread %d, unable to set current thread value", pthread_self());
533
0
    goto exit;
534
0
  }
535
536
0
  if (!(fkt = thread->lpStartAddress))
537
0
  {
538
0
    union
539
0
    {
540
0
      LPTHREAD_START_ROUTINE fkt;
541
0
      void* pv;
542
0
    } cnv;
543
0
    cnv.fkt = fkt;
544
0
    WLog_ERR(TAG, "Thread function argument is %p", cnv.pv);
545
0
    goto exit;
546
0
  }
547
548
0
  if (!signal_and_wait_for_ready(thread))
549
0
    goto exit;
550
551
0
  rc = fkt(thread->lpParameter);
552
0
exit:
553
554
0
  if (thread)
555
0
  {
556
0
    apc_cleanupThread(thread);
557
558
0
    if (!thread->exited)
559
0
      thread->dwExitCode = rc;
560
561
0
    set_event(thread);
562
563
0
    (void)signal_thread_ready(thread);
564
565
0
    if (thread->detached || !thread->started)
566
0
      cleanup_handle(thread);
567
0
  }
568
569
0
  return NULL;
570
0
}
571
572
static BOOL winpr_StartThread(WINPR_THREAD* thread)
573
0
{
574
0
  BOOL rc = FALSE;
575
0
  BOOL locked = FALSE;
576
0
  pthread_attr_t attr = { 0 };
577
578
0
  if (!mux_condition_bundle_lock(&thread->isCreated))
579
0
    return FALSE;
580
0
  locked = TRUE;
581
582
0
  pthread_attr_init(&attr);
583
0
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
584
585
0
  if (thread->dwStackSize > 0)
586
0
    pthread_attr_setstacksize(&attr, thread->dwStackSize);
587
588
0
  thread->started = TRUE;
589
0
  reset_event(thread);
590
591
#if defined(WITH_THREAD_LIST)
592
  if (!ListDictionary_Add(thread_list, &thread->thread, thread))
593
  {
594
    WLog_ERR(TAG, "failed to add the thread to the thread list");
595
    goto error;
596
  }
597
#endif
598
599
0
  if (pthread_create(&thread->thread, &attr, thread_launcher, thread))
600
0
    goto error;
601
602
0
  if (!mux_condition_bundle_wait(&thread->isCreated, "threadIsCreated"))
603
0
    goto error;
604
605
0
  locked = FALSE;
606
0
  if (!mux_condition_bundle_unlock(&thread->isCreated))
607
0
    goto error;
608
609
0
  if (!signal_thread_is_running(thread))
610
0
  {
611
0
    WLog_ERR(TAG, "failed to signal the thread was ready");
612
0
    goto error;
613
0
  }
614
615
0
  rc = TRUE;
616
0
error:
617
0
  if (locked)
618
0
  {
619
0
    if (!mux_condition_bundle_unlock(&thread->isCreated))
620
0
      rc = FALSE;
621
0
  }
622
623
0
  pthread_attr_destroy(&attr);
624
625
0
  if (rc)
626
0
    dump_thread(thread);
627
628
0
  return rc;
629
0
}
630
631
BOOL SetThreadPriority(HANDLE hThread, int nPriority)
632
0
{
633
0
  ULONG Type = 0;
634
0
  WINPR_HANDLE* Object = NULL;
635
636
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
637
0
    return FALSE;
638
639
0
  const int min = 19;
640
0
  const int max = 0;
641
0
  const int diff = (max - min);
642
0
  const int normal = min + diff / 2;
643
0
  const int off = MIN(1, diff / 4);
644
0
  int sched_priority = -1;
645
646
0
  switch (nPriority & ~(THREAD_MODE_BACKGROUND_BEGIN | THREAD_MODE_BACKGROUND_END))
647
0
  {
648
0
    case THREAD_PRIORITY_ABOVE_NORMAL:
649
0
      sched_priority = MIN(normal + off, max);
650
0
      break;
651
0
    case THREAD_PRIORITY_BELOW_NORMAL:
652
0
      sched_priority = MAX(normal - off, min);
653
0
      break;
654
0
    case THREAD_PRIORITY_HIGHEST:
655
0
      sched_priority = max;
656
0
      break;
657
0
    case THREAD_PRIORITY_IDLE:
658
0
      sched_priority = min;
659
0
      break;
660
0
    case THREAD_PRIORITY_LOWEST:
661
0
      sched_priority = min;
662
0
      break;
663
0
    case THREAD_PRIORITY_TIME_CRITICAL:
664
0
      sched_priority = max;
665
0
      break;
666
0
    default:
667
0
    case THREAD_PRIORITY_NORMAL:
668
0
      sched_priority = normal;
669
0
      break;
670
0
  }
671
0
#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L) && defined(PTHREAD_SETSCHEDPRIO)
672
0
  WINPR_THREAD* thread = (WINPR_THREAD*)Object;
673
0
  const int rc = pthread_setschedprio(thread->thread, sched_priority);
674
0
  if (rc != 0)
675
0
  {
676
0
    char buffer[256] = { 0 };
677
0
    WLog_ERR(TAG, "pthread_setschedprio(%d) %s [%d]", sched_priority,
678
0
             winpr_strerror(rc, buffer, sizeof(buffer)), rc);
679
0
  }
680
0
  return rc == 0;
681
#else
682
  WLog_WARN(TAG, "pthread_setschedprio(%d) not implemented, requires POSIX 2008 or later",
683
            sched_priority);
684
  return TRUE;
685
#endif
686
0
}
687
688
HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, size_t dwStackSize,
689
                    LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter,
690
                    DWORD dwCreationFlags, WINPR_ATTR_UNUSED LPDWORD lpThreadId)
691
0
{
692
0
  HANDLE handle = NULL;
693
0
  WINPR_THREAD* thread = (WINPR_THREAD*)calloc(1, sizeof(WINPR_THREAD));
694
695
0
  if (!thread)
696
0
    return NULL;
697
698
0
  thread->dwStackSize = dwStackSize;
699
0
  thread->lpParameter = lpParameter;
700
0
  thread->lpStartAddress = lpStartAddress;
701
0
  thread->lpThreadAttributes = lpThreadAttributes;
702
0
  thread->common.ops = &ops;
703
#if defined(WITH_DEBUG_THREADS)
704
  thread->create_stack = winpr_backtrace(20);
705
  dump_thread(thread);
706
#endif
707
708
0
  if (!winpr_event_init(&thread->event))
709
0
  {
710
0
    WLog_ERR(TAG, "failed to create event");
711
0
    goto fail;
712
0
  }
713
714
0
  if (!run_mutex_init(pthread_mutex_init, &thread->mutex, NULL))
715
0
  {
716
0
    WLog_ERR(TAG, "failed to initialize thread mutex");
717
0
    goto fail;
718
0
  }
719
720
0
  if (!apc_init(&thread->apc))
721
0
  {
722
0
    WLog_ERR(TAG, "failed to initialize APC");
723
0
    goto fail;
724
0
  }
725
726
0
  if (!mux_condition_bundle_init(&thread->isCreated))
727
0
    goto fail;
728
0
  if (!mux_condition_bundle_init(&thread->isRunning))
729
0
    goto fail;
730
731
0
  WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ);
732
0
  handle = (HANDLE)thread;
733
734
0
  InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
735
736
0
  if (!(dwCreationFlags & CREATE_SUSPENDED))
737
0
  {
738
0
    if (!winpr_StartThread(thread))
739
0
      goto fail;
740
0
  }
741
0
  else
742
0
  {
743
0
    if (!set_event(thread))
744
0
      goto fail;
745
0
  }
746
747
0
  return handle;
748
0
fail:
749
0
  cleanup_handle(thread);
750
0
  return NULL;
751
0
}
752
753
void cleanup_handle(void* obj)
754
0
{
755
0
  WINPR_THREAD* thread = (WINPR_THREAD*)obj;
756
0
  if (!thread)
757
0
    return;
758
759
0
  if (!apc_uninit(&thread->apc))
760
0
    WLog_ERR(TAG, "failed to destroy APC");
761
762
0
  mux_condition_bundle_uninit(&thread->isCreated);
763
0
  mux_condition_bundle_uninit(&thread->isRunning);
764
0
  run_mutex_fkt(pthread_mutex_destroy, &thread->mutex);
765
766
0
  winpr_event_uninit(&thread->event);
767
768
#if defined(WITH_THREAD_LIST)
769
  ListDictionary_Remove(thread_list, &thread->thread);
770
#endif
771
#if defined(WITH_DEBUG_THREADS)
772
773
  if (thread->create_stack)
774
    winpr_backtrace_free(thread->create_stack);
775
776
  if (thread->exit_stack)
777
    winpr_backtrace_free(thread->exit_stack);
778
779
#endif
780
0
  free(thread);
781
0
}
782
783
BOOL ThreadCloseHandle(HANDLE handle)
784
0
{
785
0
  WINPR_THREAD* thread = (WINPR_THREAD*)handle;
786
787
#if defined(WITH_THREAD_LIST)
788
  if (!thread_list)
789
  {
790
    WLog_ERR(TAG, "Thread list does not exist, check call!");
791
    dump_thread(thread);
792
  }
793
  else if (!ListDictionary_Contains(thread_list, &thread->thread))
794
  {
795
    WLog_ERR(TAG, "Thread list does not contain this thread! check call!");
796
    dump_thread(thread);
797
  }
798
  else
799
  {
800
    ListDictionary_Lock(thread_list);
801
#endif
802
0
    dump_thread(thread);
803
804
0
    if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0))
805
0
    {
806
0
      WLog_DBG(TAG, "Thread running, setting to detached state!");
807
0
      thread->detached = TRUE;
808
0
      pthread_detach(thread->thread);
809
0
    }
810
0
    else
811
0
    {
812
0
      cleanup_handle(thread);
813
0
    }
814
815
#if defined(WITH_THREAD_LIST)
816
    ListDictionary_Unlock(thread_list);
817
  }
818
#endif
819
820
0
  return TRUE;
821
0
}
822
823
HANDLE CreateRemoteThread(WINPR_ATTR_UNUSED HANDLE hProcess,
824
                          WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpThreadAttributes,
825
                          WINPR_ATTR_UNUSED size_t dwStackSize,
826
                          WINPR_ATTR_UNUSED LPTHREAD_START_ROUTINE lpStartAddress,
827
                          WINPR_ATTR_UNUSED LPVOID lpParameter,
828
                          WINPR_ATTR_UNUSED DWORD dwCreationFlags,
829
                          WINPR_ATTR_UNUSED LPDWORD lpThreadId)
830
0
{
831
0
  WLog_ERR(TAG, "not implemented");
832
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
833
0
  return NULL;
834
0
}
835
836
VOID ExitThread(DWORD dwExitCode)
837
0
{
838
#if defined(WITH_THREAD_LIST)
839
  DWORD rc;
840
  pthread_t tid = pthread_self();
841
842
  if (!thread_list)
843
  {
844
    WLog_ERR(TAG, "function called without existing thread list!");
845
#if defined(WITH_DEBUG_THREADS)
846
    DumpThreadHandles();
847
#endif
848
    pthread_exit(0);
849
  }
850
  else if (!ListDictionary_Contains(thread_list, &tid))
851
  {
852
    WLog_ERR(TAG, "function called, but no matching entry in thread list!");
853
#if defined(WITH_DEBUG_THREADS)
854
    DumpThreadHandles();
855
#endif
856
    pthread_exit(0);
857
  }
858
  else
859
  {
860
    WINPR_THREAD* thread;
861
    ListDictionary_Lock(thread_list);
862
    thread = ListDictionary_GetItemValue(thread_list, &tid);
863
    WINPR_ASSERT(thread);
864
    thread->exited = TRUE;
865
    thread->dwExitCode = dwExitCode;
866
#if defined(WITH_DEBUG_THREADS)
867
    thread->exit_stack = winpr_backtrace(20);
868
#endif
869
    ListDictionary_Unlock(thread_list);
870
    set_event(thread);
871
    rc = thread->dwExitCode;
872
873
    if (thread->detached || !thread->started)
874
      cleanup_handle(thread);
875
876
    pthread_exit((void*)(size_t)rc);
877
  }
878
#else
879
0
  WINPR_UNUSED(dwExitCode);
880
0
#endif
881
0
}
882
883
BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)
884
0
{
885
0
  ULONG Type = 0;
886
0
  WINPR_HANDLE* Object = NULL;
887
0
  WINPR_THREAD* thread = NULL;
888
889
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
890
0
  {
891
0
    WLog_ERR(TAG, "hThread is not a thread");
892
0
    SetLastError(ERROR_INVALID_PARAMETER);
893
0
    return FALSE;
894
0
  }
895
896
0
  thread = (WINPR_THREAD*)Object;
897
0
  *lpExitCode = thread->dwExitCode;
898
0
  return TRUE;
899
0
}
900
901
WINPR_THREAD* winpr_GetCurrentThread(VOID)
902
0
{
903
0
  WINPR_THREAD* ret = NULL;
904
905
0
  InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
906
0
  if (mainThreadId == pthread_self())
907
0
    return (HANDLE)&mainThread;
908
909
0
  ret = TlsGetValue(currentThreadTlsIndex);
910
0
  return ret;
911
0
}
912
913
HANDLE _GetCurrentThread(VOID)
914
0
{
915
0
  return (HANDLE)winpr_GetCurrentThread();
916
0
}
917
918
DWORD GetCurrentThreadId(VOID)
919
63.9k
{
920
#if defined(__FreeBSD__)
921
  return WINPR_CXX_COMPAT_CAST(DWORD, pthread_getthreadid_np());
922
#elif defined(__linux__)
923
63.9k
  return WINPR_CXX_COMPAT_CAST(DWORD, syscall(SYS_gettid));
924
#else
925
  pthread_t tid = pthread_self();
926
  /* Since pthread_t can be 64-bits on some systems, take just the    */
927
  /* lower 32-bits of it for the thread ID returned by this function. */
928
  uintptr_t ptid = WINPR_REINTERPRET_CAST(tid, pthread_t, uintptr_t);
929
  return (ptid & UINT32_MAX) ^ (ptid >> 32);
930
#endif
931
63.9k
}
932
933
typedef struct
934
{
935
  WINPR_APC_ITEM apc;
936
  PAPCFUNC completion;
937
  ULONG_PTR completionArg;
938
} UserApcItem;
939
940
static void userAPC(LPVOID arg)
941
0
{
942
0
  UserApcItem* userApc = (UserApcItem*)arg;
943
944
0
  userApc->completion(userApc->completionArg);
945
946
0
  userApc->apc.markedForRemove = TRUE;
947
0
}
948
949
DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData)
950
0
{
951
0
  ULONG Type = 0;
952
0
  WINPR_HANDLE* Object = NULL;
953
0
  WINPR_APC_ITEM* apc = NULL;
954
0
  UserApcItem* apcItem = NULL;
955
956
0
  if (!pfnAPC)
957
0
    return 1;
958
959
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
960
0
  {
961
0
    WLog_ERR(TAG, "hThread is not a thread");
962
0
    SetLastError(ERROR_INVALID_PARAMETER);
963
0
    return (DWORD)0;
964
0
  }
965
966
0
  apcItem = calloc(1, sizeof(*apcItem));
967
0
  if (!apcItem)
968
0
  {
969
0
    SetLastError(ERROR_INVALID_PARAMETER);
970
0
    return (DWORD)0;
971
0
  }
972
973
0
  apc = &apcItem->apc;
974
0
  apc->type = APC_TYPE_USER;
975
0
  apc->markedForFree = TRUE;
976
0
  apc->alwaysSignaled = TRUE;
977
0
  apc->completion = userAPC;
978
0
  apc->completionArgs = apc;
979
0
  apcItem->completion = pfnAPC;
980
0
  apcItem->completionArg = dwData;
981
0
  apc_register(hThread, apc);
982
0
  return 1;
983
0
}
984
985
DWORD ResumeThread(HANDLE hThread)
986
0
{
987
0
  ULONG Type = 0;
988
0
  WINPR_HANDLE* Object = NULL;
989
0
  WINPR_THREAD* thread = NULL;
990
991
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
992
0
  {
993
0
    WLog_ERR(TAG, "hThread is not a thread");
994
0
    SetLastError(ERROR_INVALID_PARAMETER);
995
0
    return (DWORD)-1;
996
0
  }
997
998
0
  thread = (WINPR_THREAD*)Object;
999
1000
0
  if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
1001
0
    return (DWORD)-1;
1002
1003
0
  if (!thread->started)
1004
0
  {
1005
0
    if (!winpr_StartThread(thread))
1006
0
    {
1007
0
      run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex);
1008
0
      return (DWORD)-1;
1009
0
    }
1010
0
  }
1011
0
  else
1012
0
    WLog_WARN(TAG, "Thread already started!");
1013
1014
0
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1015
0
    return (DWORD)-1;
1016
1017
0
  return 0;
1018
0
}
1019
1020
DWORD SuspendThread(WINPR_ATTR_UNUSED HANDLE hThread)
1021
0
{
1022
0
  WLog_ERR(TAG, "not implemented");
1023
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1024
0
  return (DWORD)-1;
1025
0
}
1026
1027
BOOL SwitchToThread(VOID)
1028
0
{
1029
  /**
1030
   * Note: on some operating systems sched_yield is a stub returning -1.
1031
   * usleep should at least trigger a context switch if any thread is waiting.
1032
   */
1033
0
  if (sched_yield() != 0)
1034
0
    usleep(1);
1035
1036
0
  return TRUE;
1037
0
}
1038
1039
BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode)
1040
0
{
1041
0
  ULONG Type = 0;
1042
0
  WINPR_HANDLE* Object = NULL;
1043
0
  WINPR_THREAD* thread = NULL;
1044
1045
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
1046
0
    return FALSE;
1047
1048
0
  thread = (WINPR_THREAD*)Object;
1049
0
  thread->exited = TRUE;
1050
0
  thread->dwExitCode = dwExitCode;
1051
1052
0
  if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
1053
0
    return FALSE;
1054
1055
0
#ifndef ANDROID
1056
0
  pthread_cancel(thread->thread);
1057
#else
1058
  WLog_ERR(TAG, "Function not supported on this platform!");
1059
#endif
1060
1061
0
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1062
0
    return FALSE;
1063
1064
0
  set_event(thread);
1065
0
  return TRUE;
1066
0
}
1067
1068
VOID DumpThreadHandles(void)
1069
0
{
1070
#if defined(WITH_DEBUG_THREADS)
1071
  char** msg = NULL;
1072
  size_t used = 0;
1073
  void* stack = winpr_backtrace(20);
1074
  WLog_DBG(TAG, "---------------- Called from ----------------------------");
1075
  msg = winpr_backtrace_symbols(stack, &used);
1076
1077
  for (size_t i = 0; i < used; i++)
1078
  {
1079
    WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1080
  }
1081
1082
  free(msg);
1083
  winpr_backtrace_free(stack);
1084
  WLog_DBG(TAG, "---------------- Start Dumping thread handles -----------");
1085
1086
#if defined(WITH_THREAD_LIST)
1087
  if (!thread_list)
1088
  {
1089
    WLog_DBG(TAG, "All threads properly shut down and disposed of.");
1090
  }
1091
  else
1092
  {
1093
    ULONG_PTR* keys = NULL;
1094
    ListDictionary_Lock(thread_list);
1095
    int x, count = ListDictionary_GetKeys(thread_list, &keys);
1096
    WLog_DBG(TAG, "Dumping %d elements", count);
1097
1098
    for (size_t x = 0; x < count; x++)
1099
    {
1100
      WINPR_THREAD* thread = ListDictionary_GetItemValue(thread_list, (void*)keys[x]);
1101
      WLog_DBG(TAG, "Thread [%d] handle created still not closed!", x);
1102
      msg = winpr_backtrace_symbols(thread->create_stack, &used);
1103
1104
      for (size_t i = 0; i < used; i++)
1105
      {
1106
        WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1107
      }
1108
1109
      free(msg);
1110
1111
      if (thread->started)
1112
      {
1113
        WLog_DBG(TAG, "Thread [%d] still running!", x);
1114
      }
1115
      else
1116
      {
1117
        WLog_DBG(TAG, "Thread [%d] exited at:", x);
1118
        msg = winpr_backtrace_symbols(thread->exit_stack, &used);
1119
1120
        for (size_t i = 0; i < used; i++)
1121
          WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1122
1123
        free(msg);
1124
      }
1125
    }
1126
1127
    free(keys);
1128
    ListDictionary_Unlock(thread_list);
1129
  }
1130
#endif
1131
1132
  WLog_DBG(TAG, "---------------- End Dumping thread handles -------------");
1133
#endif
1134
0
}
1135
#endif