Coverage Report

Created: 2026-03-04 06:13

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 <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 = nullptr;
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] = WINPR_C_ARRAY_INIT;
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] = WINPR_C_ARRAY_INIT;
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] = WINPR_C_ARRAY_INIT;
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] = WINPR_C_ARRAY_INIT;
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, nullptr))
224
0
    return FALSE;
225
226
0
  if (!run_cond_init(pthread_cond_init, &bundle->cond, nullptr))
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 = WINPR_C_ARRAY_INIT;
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] = WINPR_C_ARRAY_INIT;
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, nullptr);
333
334
0
    if (rc != 0)
335
0
    {
336
0
      char ebuffer[256] = WINPR_C_ARRAY_INIT;
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, 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
361
static void dump_thread(WINPR_THREAD* thread)
362
0
{
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
0
  WINPR_UNUSED(thread);
403
0
#endif
404
0
}
405
406
/**
407
 * TODO: implement thread suspend/resume using pthreads
408
 * http://stackoverflow.com/questions/3140867/suspend-pthreads-without-using-condition
409
 */
410
static BOOL set_event(WINPR_THREAD* thread)
411
0
{
412
0
  return winpr_event_set(&thread->event);
413
0
}
414
415
static BOOL reset_event(WINPR_THREAD* thread)
416
0
{
417
0
  return winpr_event_reset(&thread->event);
418
0
}
419
420
#if defined(WITH_THREAD_LIST)
421
static 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
430
static INIT_ONCE threads_InitOnce = INIT_ONCE_STATIC_INIT;
431
static pthread_t mainThreadId;
432
static DWORD currentThreadTlsIndex = TLS_OUT_OF_INDEXES;
433
434
static BOOL initializeThreads(WINPR_ATTR_UNUSED PINIT_ONCE InitOnce,
435
                              WINPR_ATTR_UNUSED PVOID Parameter, WINPR_ATTR_UNUSED PVOID* Context)
436
0
{
437
0
  if (!apc_init(&mainThread.apc))
438
0
  {
439
0
    WLog_ERR(TAG, "failed to initialize APC");
440
0
    goto out;
441
0
  }
442
443
0
  mainThread.common.Type = HANDLE_TYPE_THREAD;
444
0
  mainThreadId = pthread_self();
445
446
0
  currentThreadTlsIndex = TlsAlloc();
447
0
  if (currentThreadTlsIndex == TLS_OUT_OF_INDEXES)
448
0
  {
449
0
    WLog_ERR(TAG, "Major bug, unable to allocate a TLS value for currentThread");
450
0
  }
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
464
0
out:
465
0
  return TRUE;
466
0
}
467
468
static BOOL signal_and_wait_for_ready(WINPR_THREAD* thread)
469
0
{
470
0
  BOOL res = FALSE;
471
472
0
  WINPR_ASSERT(thread);
473
474
0
  if (!mux_condition_bundle_lock(&thread->isRunning))
475
0
    return FALSE;
476
477
0
  if (!signal_thread_ready(thread))
478
0
    goto fail;
479
480
0
  if (!mux_condition_bundle_wait(&thread->isRunning, "threadIsRunning"))
481
0
    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
0
  res = TRUE;
492
493
0
fail:
494
0
  if (!mux_condition_bundle_unlock(&thread->isRunning))
495
0
    return FALSE;
496
497
0
  return res;
498
0
}
499
500
/* Thread launcher function responsible for registering
501
 * cleanup handlers and calling pthread_exit, if not done
502
 * in thread function. */
503
static void* thread_launcher(void* arg)
504
0
{
505
0
  DWORD rc = 0;
506
0
  WINPR_THREAD* thread = (WINPR_THREAD*)arg;
507
0
  LPTHREAD_START_ROUTINE fkt = nullptr;
508
509
0
  if (!thread)
510
0
  {
511
0
    WLog_ERR(TAG, "Called with invalid argument %p", arg);
512
0
    goto exit;
513
0
  }
514
515
0
  if (!TlsSetValue(currentThreadTlsIndex, thread))
516
0
  {
517
0
    WLog_ERR(TAG, "thread %" PRIu64 ", unable to set current thread value",
518
0
             WINPR_CXX_COMPAT_CAST(uint64_t, pthread_self()));
519
0
    goto exit;
520
0
  }
521
522
0
  if (!(fkt = thread->lpStartAddress))
523
0
  {
524
0
    union
525
0
    {
526
0
      LPTHREAD_START_ROUTINE fkt;
527
0
      void* pv;
528
0
    } cnv;
529
0
    cnv.fkt = fkt;
530
0
    WLog_ERR(TAG, "Thread function argument is %p", cnv.pv);
531
0
    goto exit;
532
0
  }
533
534
0
  if (!signal_and_wait_for_ready(thread))
535
0
    goto exit;
536
537
0
  rc = fkt(thread->lpParameter);
538
0
exit:
539
540
0
  if (thread)
541
0
  {
542
0
    apc_cleanupThread(thread);
543
544
0
    if (!thread->exited)
545
0
      thread->dwExitCode = rc;
546
547
0
    set_event(thread);
548
549
0
    (void)signal_thread_ready(thread);
550
551
0
    if (thread->detached || !thread->started)
552
0
      cleanup_handle(thread);
553
0
  }
554
555
0
  return nullptr;
556
0
}
557
558
static BOOL winpr_StartThread(WINPR_THREAD* thread)
559
0
{
560
0
  BOOL rc = FALSE;
561
0
  BOOL locked = FALSE;
562
0
  pthread_attr_t attr = WINPR_C_ARRAY_INIT;
563
564
0
  if (!mux_condition_bundle_lock(&thread->isCreated))
565
0
    return FALSE;
566
0
  locked = TRUE;
567
568
0
  pthread_attr_init(&attr);
569
0
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
570
571
0
  if (thread->dwStackSize > 0)
572
0
    pthread_attr_setstacksize(&attr, thread->dwStackSize);
573
574
0
  thread->started = TRUE;
575
0
  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
0
  if (pthread_create(&thread->thread, &attr, thread_launcher, thread))
586
0
    goto error;
587
588
0
  if (!mux_condition_bundle_wait(&thread->isCreated, "threadIsCreated"))
589
0
    goto error;
590
591
0
  locked = FALSE;
592
0
  if (!mux_condition_bundle_unlock(&thread->isCreated))
593
0
    goto error;
594
595
0
  if (!signal_thread_is_running(thread))
596
0
  {
597
0
    WLog_ERR(TAG, "failed to signal the thread was ready");
598
0
    goto error;
599
0
  }
600
601
0
  rc = TRUE;
602
0
error:
603
0
  if (locked)
604
0
  {
605
0
    if (!mux_condition_bundle_unlock(&thread->isCreated))
606
0
      rc = FALSE;
607
0
  }
608
609
0
  pthread_attr_destroy(&attr);
610
611
0
  if (rc)
612
0
    dump_thread(thread);
613
614
0
  return rc;
615
0
}
616
617
BOOL SetThreadPriority(HANDLE hThread, int nPriority)
618
0
{
619
0
  ULONG Type = 0;
620
0
  WINPR_HANDLE* Object = nullptr;
621
622
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
623
0
    return FALSE;
624
625
0
  const int min = 19;
626
0
  const int max = 0;
627
0
  const int diff = (max - min);
628
0
  const int normal = min + diff / 2;
629
0
  const int off = MIN(1, diff / 4);
630
0
  int sched_priority = -1;
631
632
0
  switch (nPriority & ~(THREAD_MODE_BACKGROUND_BEGIN | THREAD_MODE_BACKGROUND_END))
633
0
  {
634
0
    case THREAD_PRIORITY_ABOVE_NORMAL:
635
0
      sched_priority = MIN(normal + off, max);
636
0
      break;
637
0
    case THREAD_PRIORITY_BELOW_NORMAL:
638
0
      sched_priority = MAX(normal - off, min);
639
0
      break;
640
0
    case THREAD_PRIORITY_HIGHEST:
641
0
      sched_priority = max;
642
0
      break;
643
0
    case THREAD_PRIORITY_IDLE:
644
0
      sched_priority = min;
645
0
      break;
646
0
    case THREAD_PRIORITY_LOWEST:
647
0
      sched_priority = min;
648
0
      break;
649
0
    case THREAD_PRIORITY_TIME_CRITICAL:
650
0
      sched_priority = max;
651
0
      break;
652
0
    default:
653
0
    case THREAD_PRIORITY_NORMAL:
654
0
      sched_priority = normal;
655
0
      break;
656
0
  }
657
0
#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L) && defined(PTHREAD_SETSCHEDPRIO)
658
0
  WINPR_THREAD* thread = (WINPR_THREAD*)Object;
659
0
  const int rc = pthread_setschedprio(thread->thread, sched_priority);
660
0
  if (rc != 0)
661
0
  {
662
0
    char buffer[256] = WINPR_C_ARRAY_INIT;
663
0
    WLog_ERR(TAG, "pthread_setschedprio(%d) %s [%d]", sched_priority,
664
0
             winpr_strerror(rc, buffer, sizeof(buffer)), rc);
665
0
  }
666
0
  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
0
}
673
674
HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, size_t dwStackSize,
675
                    LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter,
676
                    DWORD dwCreationFlags, WINPR_ATTR_UNUSED LPDWORD lpThreadId)
677
0
{
678
0
  HANDLE handle = nullptr;
679
0
  WINPR_THREAD* thread = (WINPR_THREAD*)calloc(1, sizeof(WINPR_THREAD));
680
681
0
  if (!thread)
682
0
    return nullptr;
683
684
0
  thread->dwStackSize = dwStackSize;
685
0
  thread->lpParameter = lpParameter;
686
0
  thread->lpStartAddress = lpStartAddress;
687
0
  thread->lpThreadAttributes = lpThreadAttributes;
688
0
  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
0
  if (!winpr_event_init(&thread->event))
695
0
  {
696
0
    WLog_ERR(TAG, "failed to create event");
697
0
    goto fail;
698
0
  }
699
700
0
  if (!run_mutex_init(pthread_mutex_init, &thread->mutex, nullptr))
701
0
  {
702
0
    WLog_ERR(TAG, "failed to initialize thread mutex");
703
0
    goto fail;
704
0
  }
705
706
0
  if (!apc_init(&thread->apc))
707
0
  {
708
0
    WLog_ERR(TAG, "failed to initialize APC");
709
0
    goto fail;
710
0
  }
711
712
0
  if (!mux_condition_bundle_init(&thread->isCreated))
713
0
    goto fail;
714
0
  if (!mux_condition_bundle_init(&thread->isRunning))
715
0
    goto fail;
716
717
0
  WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ);
718
0
  handle = (HANDLE)thread;
719
720
0
  if (!InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, nullptr, nullptr))
721
0
    goto fail;
722
723
0
  if (!(dwCreationFlags & CREATE_SUSPENDED))
724
0
  {
725
0
    if (!winpr_StartThread(thread))
726
0
      goto fail;
727
0
  }
728
0
  else
729
0
  {
730
0
    if (!set_event(thread))
731
0
      goto fail;
732
0
  }
733
734
0
  return handle;
735
0
fail:
736
0
  cleanup_handle(thread);
737
0
  return nullptr;
738
0
}
739
740
void cleanup_handle(void* obj)
741
0
{
742
0
  WINPR_THREAD* thread = (WINPR_THREAD*)obj;
743
0
  if (!thread)
744
0
    return;
745
746
0
  if (!apc_uninit(&thread->apc))
747
0
    WLog_ERR(TAG, "failed to destroy APC");
748
749
0
  mux_condition_bundle_uninit(&thread->isCreated);
750
0
  mux_condition_bundle_uninit(&thread->isRunning);
751
0
  run_mutex_fkt(pthread_mutex_destroy, &thread->mutex);
752
753
0
  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
0
  free(thread);
768
0
}
769
770
BOOL ThreadCloseHandle(HANDLE handle)
771
0
{
772
0
  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
0
    dump_thread(thread);
790
791
0
    if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0))
792
0
    {
793
0
      WLog_DBG(TAG, "Thread running, setting to detached state!");
794
0
      thread->detached = TRUE;
795
0
      pthread_detach(thread->thread);
796
0
    }
797
0
    else
798
0
    {
799
0
      cleanup_handle(thread);
800
0
    }
801
802
#if defined(WITH_THREAD_LIST)
803
    ListDictionary_Unlock(thread_list);
804
  }
805
#endif
806
807
0
  return TRUE;
808
0
}
809
810
HANDLE 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
0
{
818
0
  WLog_ERR(TAG, "not implemented");
819
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
820
0
  return nullptr;
821
0
}
822
823
VOID ExitThread(DWORD dwExitCode)
824
0
{
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
0
  WINPR_UNUSED(dwExitCode);
867
0
#endif
868
0
}
869
870
BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)
871
0
{
872
0
  ULONG Type = 0;
873
0
  WINPR_HANDLE* Object = nullptr;
874
0
  WINPR_THREAD* thread = nullptr;
875
876
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
877
0
  {
878
0
    WLog_ERR(TAG, "hThread is not a thread");
879
0
    SetLastError(ERROR_INVALID_PARAMETER);
880
0
    return FALSE;
881
0
  }
882
883
0
  thread = (WINPR_THREAD*)Object;
884
0
  *lpExitCode = thread->dwExitCode;
885
0
  return TRUE;
886
0
}
887
888
WINPR_THREAD* winpr_GetCurrentThread(VOID)
889
0
{
890
0
  WINPR_THREAD* ret = nullptr;
891
892
0
  if (!InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, nullptr, nullptr))
893
0
    return nullptr;
894
0
  if (mainThreadId == pthread_self())
895
0
    return (HANDLE)&mainThread;
896
897
0
  ret = TlsGetValue(currentThreadTlsIndex);
898
0
  return ret;
899
0
}
900
901
HANDLE _GetCurrentThread(VOID)
902
0
{
903
0
  return (HANDLE)winpr_GetCurrentThread();
904
0
}
905
906
DWORD GetCurrentThreadId(VOID)
907
63.9k
{
908
#if defined(__FreeBSD__)
909
  return WINPR_CXX_COMPAT_CAST(DWORD, pthread_getthreadid_np());
910
#elif defined(__linux__)
911
63.9k
  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
63.9k
}
920
921
typedef struct
922
{
923
  WINPR_APC_ITEM apc;
924
  PAPCFUNC completion;
925
  ULONG_PTR completionArg;
926
} UserApcItem;
927
928
static void userAPC(LPVOID arg)
929
0
{
930
0
  UserApcItem* userApc = (UserApcItem*)arg;
931
932
0
  userApc->completion(userApc->completionArg);
933
934
0
  userApc->apc.markedForRemove = TRUE;
935
0
}
936
937
DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData)
938
0
{
939
0
  ULONG Type = 0;
940
0
  WINPR_HANDLE* Object = nullptr;
941
0
  WINPR_APC_ITEM* apc = nullptr;
942
0
  UserApcItem* apcItem = nullptr;
943
944
0
  if (!pfnAPC)
945
0
    return 1;
946
947
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
948
0
  {
949
0
    WLog_ERR(TAG, "hThread is not a thread");
950
0
    SetLastError(ERROR_INVALID_PARAMETER);
951
0
    return (DWORD)0;
952
0
  }
953
954
0
  apcItem = calloc(1, sizeof(*apcItem));
955
0
  if (!apcItem)
956
0
  {
957
0
    SetLastError(ERROR_INVALID_PARAMETER);
958
0
    return (DWORD)0;
959
0
  }
960
961
0
  apc = &apcItem->apc;
962
0
  apc->type = APC_TYPE_USER;
963
0
  apc->markedForFree = TRUE;
964
0
  apc->alwaysSignaled = TRUE;
965
0
  apc->completion = userAPC;
966
0
  apc->completionArgs = apc;
967
0
  apcItem->completion = pfnAPC;
968
0
  apcItem->completionArg = dwData;
969
0
  apc_register(hThread, apc);
970
0
  return 1;
971
0
}
972
973
DWORD ResumeThread(HANDLE hThread)
974
0
{
975
0
  ULONG Type = 0;
976
0
  WINPR_HANDLE* Object = nullptr;
977
0
  WINPR_THREAD* thread = nullptr;
978
979
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
980
0
  {
981
0
    WLog_ERR(TAG, "hThread is not a thread");
982
0
    SetLastError(ERROR_INVALID_PARAMETER);
983
0
    return (DWORD)-1;
984
0
  }
985
986
0
  thread = (WINPR_THREAD*)Object;
987
988
0
  if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
989
0
    return (DWORD)-1;
990
991
0
  if (!thread->started)
992
0
  {
993
0
    if (!winpr_StartThread(thread))
994
0
    {
995
0
      run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex);
996
0
      return (DWORD)-1;
997
0
    }
998
0
  }
999
0
  else
1000
0
    WLog_WARN(TAG, "Thread already started!");
1001
1002
0
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1003
0
    return (DWORD)-1;
1004
1005
0
  return 0;
1006
0
}
1007
1008
DWORD SuspendThread(WINPR_ATTR_UNUSED HANDLE hThread)
1009
0
{
1010
0
  WLog_ERR(TAG, "not implemented");
1011
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1012
0
  return (DWORD)-1;
1013
0
}
1014
1015
BOOL SwitchToThread(VOID)
1016
0
{
1017
  /**
1018
   * Note: on some operating systems sched_yield is a stub returning -1.
1019
   * usleep should at least trigger a context switch if any thread is waiting.
1020
   */
1021
0
  if (sched_yield() != 0)
1022
0
    usleep(1);
1023
1024
0
  return TRUE;
1025
0
}
1026
1027
BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode)
1028
0
{
1029
0
  ULONG Type = 0;
1030
0
  WINPR_HANDLE* Object = nullptr;
1031
0
  WINPR_THREAD* thread = nullptr;
1032
1033
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
1034
0
    return FALSE;
1035
1036
0
  thread = (WINPR_THREAD*)Object;
1037
0
  thread->exited = TRUE;
1038
0
  thread->dwExitCode = dwExitCode;
1039
1040
0
  if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
1041
0
    return FALSE;
1042
1043
0
#ifndef ANDROID
1044
0
  pthread_cancel(thread->thread);
1045
#else
1046
  WLog_ERR(TAG, "Function not supported on this platform!");
1047
#endif
1048
1049
0
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1050
0
    return FALSE;
1051
1052
0
  set_event(thread);
1053
0
  return TRUE;
1054
0
}
1055
1056
VOID DumpThreadHandles(void)
1057
0
{
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
0
}
1123
#endif