Coverage Report

Created: 2025-10-10 06:50

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 = 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
69.9k
{
125
69.9k
  return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_THREAD, FALSE);
126
69.9k
}
127
128
static int ThreadGetFd(HANDLE handle)
129
34.9k
{
130
34.9k
  WINPR_THREAD* pThread = (WINPR_THREAD*)handle;
131
132
34.9k
  if (!ThreadIsHandled(handle))
133
0
    return -1;
134
135
34.9k
  return pThread->event.fds[0];
136
34.9k
}
137
138
52.4k
#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
52.4k
{
143
52.4k
  int rc = 0;
144
145
52.4k
  WINPR_ASSERT(fkt);
146
52.4k
  WINPR_ASSERT(mutex);
147
148
52.4k
  rc = fkt(mutex, mutexattr);
149
52.4k
  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
52.4k
  return rc == 0;
155
52.4k
}
156
157
297k
#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
297k
{
161
297k
  int rc = 0;
162
163
297k
  WINPR_ASSERT(fkt);
164
297k
  WINPR_ASSERT(mutex);
165
166
297k
  rc = fkt(mutex);
167
297k
  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
297k
  return rc == 0;
173
297k
}
174
175
34.9k
#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
34.9k
{
179
34.9k
  int rc = 0;
180
181
34.9k
  WINPR_ASSERT(fkt);
182
34.9k
  WINPR_ASSERT(condition);
183
184
34.9k
  rc = fkt(condition, conditionattr);
185
34.9k
  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
34.9k
  return rc == 0;
191
34.9k
}
192
193
87.4k
#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
87.4k
{
197
87.4k
  int rc = 0;
198
199
87.4k
  WINPR_ASSERT(fkt);
200
87.4k
  WINPR_ASSERT(condition);
201
202
87.4k
  rc = fkt(condition);
203
87.4k
  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
87.4k
  return rc == 0;
209
87.4k
}
210
211
static int pthread_mutex_checked_unlock(pthread_mutex_t* mutex)
212
122k
{
213
122k
  WINPR_ASSERT(mutex);
214
122k
  WINPR_ASSERT(pthread_mutex_trylock(mutex) == EBUSY);
215
122k
  return pthread_mutex_unlock(mutex);
216
122k
}
217
218
static BOOL mux_condition_bundle_init(mux_condition_bundle* bundle)
219
34.9k
{
220
34.9k
  WINPR_ASSERT(bundle);
221
222
34.9k
  bundle->val = FALSE;
223
34.9k
  if (!run_mutex_init(pthread_mutex_init, &bundle->mux, NULL))
224
0
    return FALSE;
225
226
34.9k
  if (!run_cond_init(pthread_cond_init, &bundle->cond, NULL))
227
0
    return FALSE;
228
34.9k
  return TRUE;
229
34.9k
}
230
231
static void mux_condition_bundle_uninit(mux_condition_bundle* bundle)
232
34.9k
{
233
34.9k
  mux_condition_bundle empty = { 0 };
234
235
34.9k
  WINPR_ASSERT(bundle);
236
237
34.9k
  run_cond_fkt(pthread_cond_destroy, &bundle->cond);
238
34.9k
  run_mutex_fkt(pthread_mutex_destroy, &bundle->mux);
239
34.9k
  *bundle = empty;
240
34.9k
}
241
242
static BOOL mux_condition_bundle_signal(mux_condition_bundle* bundle)
243
52.4k
{
244
52.4k
  BOOL rc = TRUE;
245
52.4k
  WINPR_ASSERT(bundle);
246
247
52.4k
  if (!run_mutex_fkt(pthread_mutex_lock, &bundle->mux))
248
0
    return FALSE;
249
52.4k
  bundle->val = TRUE;
250
52.4k
  if (!run_cond_fkt(pthread_cond_signal, &bundle->cond))
251
0
    rc = FALSE;
252
52.4k
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux))
253
0
    rc = FALSE;
254
52.4k
  return rc;
255
52.4k
}
256
257
static BOOL mux_condition_bundle_lock(mux_condition_bundle* bundle)
258
34.9k
{
259
34.9k
  WINPR_ASSERT(bundle);
260
34.9k
  return run_mutex_fkt(pthread_mutex_lock, &bundle->mux);
261
34.9k
}
262
263
static BOOL mux_condition_bundle_unlock(mux_condition_bundle* bundle)
264
34.9k
{
265
34.9k
  WINPR_ASSERT(bundle);
266
34.9k
  return run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux);
267
34.9k
}
268
269
static BOOL mux_condition_bundle_wait(mux_condition_bundle* bundle, const char* name)
270
34.9k
{
271
34.9k
  BOOL rc = FALSE;
272
273
34.9k
  WINPR_ASSERT(bundle);
274
34.9k
  WINPR_ASSERT(name);
275
34.9k
  WINPR_ASSERT(pthread_mutex_trylock(&bundle->mux) == EBUSY);
276
277
69.9k
  while (!bundle->val)
278
34.9k
  {
279
34.9k
    int r = pthread_cond_wait(&bundle->cond, &bundle->mux);
280
34.9k
    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
34.9k
  }
298
299
34.9k
  rc = bundle->val;
300
301
34.9k
fail:
302
34.9k
  return rc;
303
34.9k
}
304
305
static BOOL signal_thread_ready(WINPR_THREAD* thread)
306
34.9k
{
307
34.9k
  WINPR_ASSERT(thread);
308
309
34.9k
  return mux_condition_bundle_signal(&thread->isCreated);
310
34.9k
}
311
312
static BOOL signal_thread_is_running(WINPR_THREAD* thread)
313
17.4k
{
314
17.4k
  WINPR_ASSERT(thread);
315
316
17.4k
  return mux_condition_bundle_signal(&thread->isRunning);
317
17.4k
}
318
319
static DWORD ThreadCleanupHandle(HANDLE handle)
320
34.9k
{
321
34.9k
  DWORD status = WAIT_FAILED;
322
34.9k
  WINPR_THREAD* thread = (WINPR_THREAD*)handle;
323
324
34.9k
  if (!ThreadIsHandled(handle))
325
0
    return WAIT_FAILED;
326
327
34.9k
  if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
328
0
    return WAIT_FAILED;
329
330
34.9k
  if (!thread->joined)
331
17.4k
  {
332
17.4k
    int rc = pthread_join(thread->thread, NULL);
333
334
17.4k
    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
17.4k
    else
342
17.4k
      thread->joined = TRUE;
343
17.4k
  }
344
345
34.9k
  status = WAIT_OBJECT_0;
346
347
34.9k
fail:
348
34.9k
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
349
0
    return WAIT_FAILED;
350
351
34.9k
  return status;
352
34.9k
}
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
34.9k
{
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
34.9k
  WINPR_UNUSED(thread);
418
34.9k
#endif
419
34.9k
}
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
17.4k
{
427
17.4k
  return winpr_event_set(&thread->event);
428
17.4k
}
429
430
static BOOL reset_event(WINPR_THREAD* thread)
431
17.4k
{
432
17.4k
  return winpr_event_reset(&thread->event);
433
17.4k
}
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
2
{
452
2
  if (!apc_init(&mainThread.apc))
453
0
  {
454
0
    WLog_ERR(TAG, "failed to initialize APC");
455
0
    goto out;
456
0
  }
457
458
2
  mainThread.common.Type = HANDLE_TYPE_THREAD;
459
2
  mainThreadId = pthread_self();
460
461
2
  currentThreadTlsIndex = TlsAlloc();
462
2
  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
2
out:
480
2
  return TRUE;
481
2
}
482
483
static BOOL signal_and_wait_for_ready(WINPR_THREAD* thread)
484
17.4k
{
485
17.4k
  BOOL res = FALSE;
486
487
17.4k
  WINPR_ASSERT(thread);
488
489
17.4k
  if (!mux_condition_bundle_lock(&thread->isRunning))
490
0
    return FALSE;
491
492
17.4k
  if (!signal_thread_ready(thread))
493
0
    goto fail;
494
495
17.4k
  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
17.4k
  res = TRUE;
507
508
17.4k
fail:
509
17.4k
  if (!mux_condition_bundle_unlock(&thread->isRunning))
510
0
    return FALSE;
511
512
17.4k
  return res;
513
17.4k
}
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
17.4k
{
520
17.4k
  DWORD rc = 0;
521
17.4k
  WINPR_THREAD* thread = (WINPR_THREAD*)arg;
522
17.4k
  LPTHREAD_START_ROUTINE fkt = NULL;
523
524
17.4k
  if (!thread)
525
0
  {
526
0
    WLog_ERR(TAG, "Called with invalid argument %p", arg);
527
0
    goto exit;
528
0
  }
529
530
17.4k
  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
17.4k
  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
17.4k
  if (!signal_and_wait_for_ready(thread))
549
0
    goto exit;
550
551
17.4k
  rc = fkt(thread->lpParameter);
552
17.4k
exit:
553
554
17.4k
  if (thread)
555
17.4k
  {
556
17.4k
    apc_cleanupThread(thread);
557
558
17.4k
    if (!thread->exited)
559
17.4k
      thread->dwExitCode = rc;
560
561
17.4k
    set_event(thread);
562
563
17.4k
    (void)signal_thread_ready(thread);
564
565
17.4k
    if (thread->detached || !thread->started)
566
0
      cleanup_handle(thread);
567
17.4k
  }
568
569
17.4k
  return NULL;
570
17.4k
}
571
572
static BOOL winpr_StartThread(WINPR_THREAD* thread)
573
17.4k
{
574
17.4k
  BOOL rc = FALSE;
575
17.4k
  BOOL locked = FALSE;
576
17.4k
  pthread_attr_t attr = { 0 };
577
578
17.4k
  if (!mux_condition_bundle_lock(&thread->isCreated))
579
0
    return FALSE;
580
17.4k
  locked = TRUE;
581
582
17.4k
  pthread_attr_init(&attr);
583
17.4k
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
584
585
17.4k
  if (thread->dwStackSize > 0)
586
0
    pthread_attr_setstacksize(&attr, thread->dwStackSize);
587
588
17.4k
  thread->started = TRUE;
589
17.4k
  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
17.4k
  if (pthread_create(&thread->thread, &attr, thread_launcher, thread))
600
0
    goto error;
601
602
17.4k
  if (!mux_condition_bundle_wait(&thread->isCreated, "threadIsCreated"))
603
0
    goto error;
604
605
17.4k
  locked = FALSE;
606
17.4k
  if (!mux_condition_bundle_unlock(&thread->isCreated))
607
0
    goto error;
608
609
17.4k
  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
17.4k
  rc = TRUE;
616
17.4k
error:
617
17.4k
  if (locked)
618
0
  {
619
0
    if (!mux_condition_bundle_unlock(&thread->isCreated))
620
0
      rc = FALSE;
621
0
  }
622
623
17.4k
  pthread_attr_destroy(&attr);
624
625
17.4k
  if (rc)
626
17.4k
    dump_thread(thread);
627
628
17.4k
  return rc;
629
17.4k
}
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
17.4k
{
692
17.4k
  HANDLE handle = NULL;
693
17.4k
  WINPR_THREAD* thread = (WINPR_THREAD*)calloc(1, sizeof(WINPR_THREAD));
694
695
17.4k
  if (!thread)
696
0
    return NULL;
697
698
17.4k
  thread->dwStackSize = dwStackSize;
699
17.4k
  thread->lpParameter = lpParameter;
700
17.4k
  thread->lpStartAddress = lpStartAddress;
701
17.4k
  thread->lpThreadAttributes = lpThreadAttributes;
702
17.4k
  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
17.4k
  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
17.4k
  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
17.4k
  if (!apc_init(&thread->apc))
721
0
  {
722
0
    WLog_ERR(TAG, "failed to initialize APC");
723
0
    goto fail;
724
0
  }
725
726
17.4k
  if (!mux_condition_bundle_init(&thread->isCreated))
727
0
    goto fail;
728
17.4k
  if (!mux_condition_bundle_init(&thread->isRunning))
729
0
    goto fail;
730
731
17.4k
  WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ);
732
17.4k
  handle = (HANDLE)thread;
733
734
17.4k
  InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
735
736
17.4k
  if (!(dwCreationFlags & CREATE_SUSPENDED))
737
17.4k
  {
738
17.4k
    if (!winpr_StartThread(thread))
739
0
      goto fail;
740
17.4k
  }
741
0
  else
742
0
  {
743
0
    if (!set_event(thread))
744
0
      goto fail;
745
0
  }
746
747
17.4k
  return handle;
748
0
fail:
749
0
  cleanup_handle(thread);
750
0
  return NULL;
751
17.4k
}
752
753
void cleanup_handle(void* obj)
754
17.4k
{
755
17.4k
  WINPR_THREAD* thread = (WINPR_THREAD*)obj;
756
17.4k
  if (!thread)
757
0
    return;
758
759
17.4k
  if (!apc_uninit(&thread->apc))
760
0
    WLog_ERR(TAG, "failed to destroy APC");
761
762
17.4k
  mux_condition_bundle_uninit(&thread->isCreated);
763
17.4k
  mux_condition_bundle_uninit(&thread->isRunning);
764
17.4k
  run_mutex_fkt(pthread_mutex_destroy, &thread->mutex);
765
766
17.4k
  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
17.4k
  free(thread);
781
17.4k
}
782
783
BOOL ThreadCloseHandle(HANDLE handle)
784
17.4k
{
785
17.4k
  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
17.4k
    dump_thread(thread);
803
804
17.4k
    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
17.4k
    else
811
17.4k
    {
812
17.4k
      cleanup_handle(thread);
813
17.4k
    }
814
815
#if defined(WITH_THREAD_LIST)
816
    ListDictionary_Unlock(thread_list);
817
  }
818
#endif
819
820
17.4k
  return TRUE;
821
17.4k
}
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
12.3M
{
920
#if defined(__FreeBSD__)
921
  return WINPR_CXX_COMPAT_CAST(DWORD, pthread_getthreadid_np());
922
#elif defined(__linux__)
923
12.3M
  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
12.3M
}
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