Coverage Report

Created: 2026-08-31 06:25

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