Coverage Report

Created: 2026-01-17 07:16

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
67.0k
{
125
67.0k
  return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_THREAD, FALSE);
126
67.0k
}
127
128
static int ThreadGetFd(HANDLE handle)
129
33.5k
{
130
33.5k
  WINPR_THREAD* pThread = (WINPR_THREAD*)handle;
131
132
33.5k
  if (!ThreadIsHandled(handle))
133
0
    return -1;
134
135
33.5k
  return pThread->event.fds[0];
136
33.5k
}
137
138
50.3k
#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
50.3k
{
143
50.3k
  int rc = 0;
144
145
50.3k
  WINPR_ASSERT(fkt);
146
50.3k
  WINPR_ASSERT(mutex);
147
148
50.3k
  rc = fkt(mutex, mutexattr);
149
50.3k
  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
50.3k
  return rc == 0;
155
50.3k
}
156
157
285k
#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
285k
{
161
285k
  int rc = 0;
162
163
285k
  WINPR_ASSERT(fkt);
164
285k
  WINPR_ASSERT(mutex);
165
166
285k
  rc = fkt(mutex);
167
285k
  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
285k
  return rc == 0;
173
285k
}
174
175
33.5k
#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
33.5k
{
179
33.5k
  int rc = 0;
180
181
33.5k
  WINPR_ASSERT(fkt);
182
33.5k
  WINPR_ASSERT(condition);
183
184
33.5k
  rc = fkt(condition, conditionattr);
185
33.5k
  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
33.5k
  return rc == 0;
191
33.5k
}
192
193
83.8k
#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
83.8k
{
197
83.8k
  int rc = 0;
198
199
83.8k
  WINPR_ASSERT(fkt);
200
83.8k
  WINPR_ASSERT(condition);
201
202
83.8k
  rc = fkt(condition);
203
83.8k
  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
83.8k
  return rc == 0;
209
83.8k
}
210
211
static int pthread_mutex_checked_unlock(pthread_mutex_t* mutex)
212
117k
{
213
117k
  WINPR_ASSERT(mutex);
214
117k
  WINPR_ASSERT(pthread_mutex_trylock(mutex) == EBUSY);
215
117k
  return pthread_mutex_unlock(mutex);
216
117k
}
217
218
static BOOL mux_condition_bundle_init(mux_condition_bundle* bundle)
219
33.5k
{
220
33.5k
  WINPR_ASSERT(bundle);
221
222
33.5k
  bundle->val = FALSE;
223
33.5k
  if (!run_mutex_init(pthread_mutex_init, &bundle->mux, NULL))
224
0
    return FALSE;
225
226
33.5k
  if (!run_cond_init(pthread_cond_init, &bundle->cond, NULL))
227
0
    return FALSE;
228
33.5k
  return TRUE;
229
33.5k
}
230
231
static void mux_condition_bundle_uninit(mux_condition_bundle* bundle)
232
33.5k
{
233
33.5k
  mux_condition_bundle empty = { 0 };
234
235
33.5k
  WINPR_ASSERT(bundle);
236
237
33.5k
  run_cond_fkt(pthread_cond_destroy, &bundle->cond);
238
33.5k
  run_mutex_fkt(pthread_mutex_destroy, &bundle->mux);
239
33.5k
  *bundle = empty;
240
33.5k
}
241
242
static BOOL mux_condition_bundle_signal(mux_condition_bundle* bundle)
243
50.3k
{
244
50.3k
  BOOL rc = TRUE;
245
50.3k
  WINPR_ASSERT(bundle);
246
247
50.3k
  if (!run_mutex_fkt(pthread_mutex_lock, &bundle->mux))
248
0
    return FALSE;
249
50.3k
  bundle->val = TRUE;
250
50.3k
  if (!run_cond_fkt(pthread_cond_signal, &bundle->cond))
251
0
    rc = FALSE;
252
50.3k
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux))
253
0
    rc = FALSE;
254
50.3k
  return rc;
255
50.3k
}
256
257
static BOOL mux_condition_bundle_lock(mux_condition_bundle* bundle)
258
33.5k
{
259
33.5k
  WINPR_ASSERT(bundle);
260
33.5k
  return run_mutex_fkt(pthread_mutex_lock, &bundle->mux);
261
33.5k
}
262
263
static BOOL mux_condition_bundle_unlock(mux_condition_bundle* bundle)
264
33.5k
{
265
33.5k
  WINPR_ASSERT(bundle);
266
33.5k
  return run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux);
267
33.5k
}
268
269
static BOOL mux_condition_bundle_wait(mux_condition_bundle* bundle, const char* name)
270
33.5k
{
271
33.5k
  BOOL rc = FALSE;
272
273
33.5k
  WINPR_ASSERT(bundle);
274
33.5k
  WINPR_ASSERT(name);
275
33.5k
  WINPR_ASSERT(pthread_mutex_trylock(&bundle->mux) == EBUSY);
276
277
67.0k
  while (!bundle->val)
278
33.5k
  {
279
33.5k
    int r = pthread_cond_wait(&bundle->cond, &bundle->mux);
280
33.5k
    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
33.5k
  }
298
299
33.5k
  rc = bundle->val;
300
301
33.5k
fail:
302
33.5k
  return rc;
303
33.5k
}
304
305
static BOOL signal_thread_ready(WINPR_THREAD* thread)
306
33.5k
{
307
33.5k
  WINPR_ASSERT(thread);
308
309
33.5k
  return mux_condition_bundle_signal(&thread->isCreated);
310
33.5k
}
311
312
static BOOL signal_thread_is_running(WINPR_THREAD* thread)
313
16.7k
{
314
16.7k
  WINPR_ASSERT(thread);
315
316
16.7k
  return mux_condition_bundle_signal(&thread->isRunning);
317
16.7k
}
318
319
static DWORD ThreadCleanupHandle(HANDLE handle)
320
33.5k
{
321
33.5k
  DWORD status = WAIT_FAILED;
322
33.5k
  WINPR_THREAD* thread = (WINPR_THREAD*)handle;
323
324
33.5k
  if (!ThreadIsHandled(handle))
325
0
    return WAIT_FAILED;
326
327
33.5k
  if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
328
0
    return WAIT_FAILED;
329
330
33.5k
  if (!thread->joined)
331
16.7k
  {
332
16.7k
    int rc = pthread_join(thread->thread, NULL);
333
334
16.7k
    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
16.7k
    else
342
16.7k
      thread->joined = TRUE;
343
16.7k
  }
344
345
33.5k
  status = WAIT_OBJECT_0;
346
347
33.5k
fail:
348
33.5k
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
349
0
    return WAIT_FAILED;
350
351
33.5k
  return status;
352
33.5k
}
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
33.5k
{
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
33.5k
  WINPR_UNUSED(thread);
418
33.5k
#endif
419
33.5k
}
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
16.7k
{
427
16.7k
  return winpr_event_set(&thread->event);
428
16.7k
}
429
430
static BOOL reset_event(WINPR_THREAD* thread)
431
16.7k
{
432
16.7k
  return winpr_event_reset(&thread->event);
433
16.7k
}
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
16.7k
{
485
16.7k
  BOOL res = FALSE;
486
487
16.7k
  WINPR_ASSERT(thread);
488
489
16.7k
  if (!mux_condition_bundle_lock(&thread->isRunning))
490
0
    return FALSE;
491
492
16.7k
  if (!signal_thread_ready(thread))
493
0
    goto fail;
494
495
16.7k
  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
16.7k
  res = TRUE;
507
508
16.7k
fail:
509
16.7k
  if (!mux_condition_bundle_unlock(&thread->isRunning))
510
0
    return FALSE;
511
512
16.7k
  return res;
513
16.7k
}
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
16.7k
{
520
16.7k
  DWORD rc = 0;
521
16.7k
  WINPR_THREAD* thread = (WINPR_THREAD*)arg;
522
16.7k
  LPTHREAD_START_ROUTINE fkt = NULL;
523
524
16.7k
  if (!thread)
525
0
  {
526
0
    WLog_ERR(TAG, "Called with invalid argument %p", arg);
527
0
    goto exit;
528
0
  }
529
530
16.7k
  if (!TlsSetValue(currentThreadTlsIndex, thread))
531
0
  {
532
0
    WLog_ERR(TAG, "thread %" PRIu64 ", unable to set current thread value",
533
0
             WINPR_CXX_COMPAT_CAST(uint64_t, pthread_self()));
534
0
    goto exit;
535
0
  }
536
537
16.7k
  if (!(fkt = thread->lpStartAddress))
538
0
  {
539
0
    union
540
0
    {
541
0
      LPTHREAD_START_ROUTINE fkt;
542
0
      void* pv;
543
0
    } cnv;
544
0
    cnv.fkt = fkt;
545
0
    WLog_ERR(TAG, "Thread function argument is %p", cnv.pv);
546
0
    goto exit;
547
0
  }
548
549
16.7k
  if (!signal_and_wait_for_ready(thread))
550
0
    goto exit;
551
552
16.7k
  rc = fkt(thread->lpParameter);
553
16.7k
exit:
554
555
16.7k
  if (thread)
556
16.7k
  {
557
16.7k
    apc_cleanupThread(thread);
558
559
16.7k
    if (!thread->exited)
560
16.7k
      thread->dwExitCode = rc;
561
562
16.7k
    set_event(thread);
563
564
16.7k
    (void)signal_thread_ready(thread);
565
566
16.7k
    if (thread->detached || !thread->started)
567
0
      cleanup_handle(thread);
568
16.7k
  }
569
570
16.7k
  return NULL;
571
16.7k
}
572
573
static BOOL winpr_StartThread(WINPR_THREAD* thread)
574
16.7k
{
575
16.7k
  BOOL rc = FALSE;
576
16.7k
  BOOL locked = FALSE;
577
16.7k
  pthread_attr_t attr = { 0 };
578
579
16.7k
  if (!mux_condition_bundle_lock(&thread->isCreated))
580
0
    return FALSE;
581
16.7k
  locked = TRUE;
582
583
16.7k
  pthread_attr_init(&attr);
584
16.7k
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
585
586
16.7k
  if (thread->dwStackSize > 0)
587
0
    pthread_attr_setstacksize(&attr, thread->dwStackSize);
588
589
16.7k
  thread->started = TRUE;
590
16.7k
  reset_event(thread);
591
592
#if defined(WITH_THREAD_LIST)
593
  if (!ListDictionary_Add(thread_list, &thread->thread, thread))
594
  {
595
    WLog_ERR(TAG, "failed to add the thread to the thread list");
596
    goto error;
597
  }
598
#endif
599
600
16.7k
  if (pthread_create(&thread->thread, &attr, thread_launcher, thread))
601
0
    goto error;
602
603
16.7k
  if (!mux_condition_bundle_wait(&thread->isCreated, "threadIsCreated"))
604
0
    goto error;
605
606
16.7k
  locked = FALSE;
607
16.7k
  if (!mux_condition_bundle_unlock(&thread->isCreated))
608
0
    goto error;
609
610
16.7k
  if (!signal_thread_is_running(thread))
611
0
  {
612
0
    WLog_ERR(TAG, "failed to signal the thread was ready");
613
0
    goto error;
614
0
  }
615
616
16.7k
  rc = TRUE;
617
16.7k
error:
618
16.7k
  if (locked)
619
0
  {
620
0
    if (!mux_condition_bundle_unlock(&thread->isCreated))
621
0
      rc = FALSE;
622
0
  }
623
624
16.7k
  pthread_attr_destroy(&attr);
625
626
16.7k
  if (rc)
627
16.7k
    dump_thread(thread);
628
629
16.7k
  return rc;
630
16.7k
}
631
632
BOOL SetThreadPriority(HANDLE hThread, int nPriority)
633
0
{
634
0
  ULONG Type = 0;
635
0
  WINPR_HANDLE* Object = NULL;
636
637
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
638
0
    return FALSE;
639
640
0
  const int min = 19;
641
0
  const int max = 0;
642
0
  const int diff = (max - min);
643
0
  const int normal = min + diff / 2;
644
0
  const int off = MIN(1, diff / 4);
645
0
  int sched_priority = -1;
646
647
0
  switch (nPriority & ~(THREAD_MODE_BACKGROUND_BEGIN | THREAD_MODE_BACKGROUND_END))
648
0
  {
649
0
    case THREAD_PRIORITY_ABOVE_NORMAL:
650
0
      sched_priority = MIN(normal + off, max);
651
0
      break;
652
0
    case THREAD_PRIORITY_BELOW_NORMAL:
653
0
      sched_priority = MAX(normal - off, min);
654
0
      break;
655
0
    case THREAD_PRIORITY_HIGHEST:
656
0
      sched_priority = max;
657
0
      break;
658
0
    case THREAD_PRIORITY_IDLE:
659
0
      sched_priority = min;
660
0
      break;
661
0
    case THREAD_PRIORITY_LOWEST:
662
0
      sched_priority = min;
663
0
      break;
664
0
    case THREAD_PRIORITY_TIME_CRITICAL:
665
0
      sched_priority = max;
666
0
      break;
667
0
    default:
668
0
    case THREAD_PRIORITY_NORMAL:
669
0
      sched_priority = normal;
670
0
      break;
671
0
  }
672
0
#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L) && defined(PTHREAD_SETSCHEDPRIO)
673
0
  WINPR_THREAD* thread = (WINPR_THREAD*)Object;
674
0
  const int rc = pthread_setschedprio(thread->thread, sched_priority);
675
0
  if (rc != 0)
676
0
  {
677
0
    char buffer[256] = { 0 };
678
0
    WLog_ERR(TAG, "pthread_setschedprio(%d) %s [%d]", sched_priority,
679
0
             winpr_strerror(rc, buffer, sizeof(buffer)), rc);
680
0
  }
681
0
  return rc == 0;
682
#else
683
  WLog_WARN(TAG, "pthread_setschedprio(%d) not implemented, requires POSIX 2008 or later",
684
            sched_priority);
685
  return TRUE;
686
#endif
687
0
}
688
689
HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, size_t dwStackSize,
690
                    LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter,
691
                    DWORD dwCreationFlags, WINPR_ATTR_UNUSED LPDWORD lpThreadId)
692
16.7k
{
693
16.7k
  HANDLE handle = NULL;
694
16.7k
  WINPR_THREAD* thread = (WINPR_THREAD*)calloc(1, sizeof(WINPR_THREAD));
695
696
16.7k
  if (!thread)
697
0
    return NULL;
698
699
16.7k
  thread->dwStackSize = dwStackSize;
700
16.7k
  thread->lpParameter = lpParameter;
701
16.7k
  thread->lpStartAddress = lpStartAddress;
702
16.7k
  thread->lpThreadAttributes = lpThreadAttributes;
703
16.7k
  thread->common.ops = &ops;
704
#if defined(WITH_DEBUG_THREADS)
705
  thread->create_stack = winpr_backtrace(20);
706
  dump_thread(thread);
707
#endif
708
709
16.7k
  if (!winpr_event_init(&thread->event))
710
0
  {
711
0
    WLog_ERR(TAG, "failed to create event");
712
0
    goto fail;
713
0
  }
714
715
16.7k
  if (!run_mutex_init(pthread_mutex_init, &thread->mutex, NULL))
716
0
  {
717
0
    WLog_ERR(TAG, "failed to initialize thread mutex");
718
0
    goto fail;
719
0
  }
720
721
16.7k
  if (!apc_init(&thread->apc))
722
0
  {
723
0
    WLog_ERR(TAG, "failed to initialize APC");
724
0
    goto fail;
725
0
  }
726
727
16.7k
  if (!mux_condition_bundle_init(&thread->isCreated))
728
0
    goto fail;
729
16.7k
  if (!mux_condition_bundle_init(&thread->isRunning))
730
0
    goto fail;
731
732
16.7k
  WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ);
733
16.7k
  handle = (HANDLE)thread;
734
735
16.7k
  InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
736
737
16.7k
  if (!(dwCreationFlags & CREATE_SUSPENDED))
738
16.7k
  {
739
16.7k
    if (!winpr_StartThread(thread))
740
0
      goto fail;
741
16.7k
  }
742
0
  else
743
0
  {
744
0
    if (!set_event(thread))
745
0
      goto fail;
746
0
  }
747
748
16.7k
  return handle;
749
0
fail:
750
0
  cleanup_handle(thread);
751
0
  return NULL;
752
16.7k
}
753
754
void cleanup_handle(void* obj)
755
16.7k
{
756
16.7k
  WINPR_THREAD* thread = (WINPR_THREAD*)obj;
757
16.7k
  if (!thread)
758
0
    return;
759
760
16.7k
  if (!apc_uninit(&thread->apc))
761
0
    WLog_ERR(TAG, "failed to destroy APC");
762
763
16.7k
  mux_condition_bundle_uninit(&thread->isCreated);
764
16.7k
  mux_condition_bundle_uninit(&thread->isRunning);
765
16.7k
  run_mutex_fkt(pthread_mutex_destroy, &thread->mutex);
766
767
16.7k
  winpr_event_uninit(&thread->event);
768
769
#if defined(WITH_THREAD_LIST)
770
  ListDictionary_Remove(thread_list, &thread->thread);
771
#endif
772
#if defined(WITH_DEBUG_THREADS)
773
774
  if (thread->create_stack)
775
    winpr_backtrace_free(thread->create_stack);
776
777
  if (thread->exit_stack)
778
    winpr_backtrace_free(thread->exit_stack);
779
780
#endif
781
16.7k
  free(thread);
782
16.7k
}
783
784
BOOL ThreadCloseHandle(HANDLE handle)
785
16.7k
{
786
16.7k
  WINPR_THREAD* thread = (WINPR_THREAD*)handle;
787
788
#if defined(WITH_THREAD_LIST)
789
  if (!thread_list)
790
  {
791
    WLog_ERR(TAG, "Thread list does not exist, check call!");
792
    dump_thread(thread);
793
  }
794
  else if (!ListDictionary_Contains(thread_list, &thread->thread))
795
  {
796
    WLog_ERR(TAG, "Thread list does not contain this thread! check call!");
797
    dump_thread(thread);
798
  }
799
  else
800
  {
801
    ListDictionary_Lock(thread_list);
802
#endif
803
16.7k
    dump_thread(thread);
804
805
16.7k
    if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0))
806
0
    {
807
0
      WLog_DBG(TAG, "Thread running, setting to detached state!");
808
0
      thread->detached = TRUE;
809
0
      pthread_detach(thread->thread);
810
0
    }
811
16.7k
    else
812
16.7k
    {
813
16.7k
      cleanup_handle(thread);
814
16.7k
    }
815
816
#if defined(WITH_THREAD_LIST)
817
    ListDictionary_Unlock(thread_list);
818
  }
819
#endif
820
821
16.7k
  return TRUE;
822
16.7k
}
823
824
HANDLE CreateRemoteThread(WINPR_ATTR_UNUSED HANDLE hProcess,
825
                          WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpThreadAttributes,
826
                          WINPR_ATTR_UNUSED size_t dwStackSize,
827
                          WINPR_ATTR_UNUSED LPTHREAD_START_ROUTINE lpStartAddress,
828
                          WINPR_ATTR_UNUSED LPVOID lpParameter,
829
                          WINPR_ATTR_UNUSED DWORD dwCreationFlags,
830
                          WINPR_ATTR_UNUSED LPDWORD lpThreadId)
831
0
{
832
0
  WLog_ERR(TAG, "not implemented");
833
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
834
0
  return NULL;
835
0
}
836
837
VOID ExitThread(DWORD dwExitCode)
838
0
{
839
#if defined(WITH_THREAD_LIST)
840
  DWORD rc;
841
  pthread_t tid = pthread_self();
842
843
  if (!thread_list)
844
  {
845
    WLog_ERR(TAG, "function called without existing thread list!");
846
#if defined(WITH_DEBUG_THREADS)
847
    DumpThreadHandles();
848
#endif
849
    pthread_exit(0);
850
  }
851
  else if (!ListDictionary_Contains(thread_list, &tid))
852
  {
853
    WLog_ERR(TAG, "function called, but no matching entry in thread list!");
854
#if defined(WITH_DEBUG_THREADS)
855
    DumpThreadHandles();
856
#endif
857
    pthread_exit(0);
858
  }
859
  else
860
  {
861
    WINPR_THREAD* thread;
862
    ListDictionary_Lock(thread_list);
863
    thread = ListDictionary_GetItemValue(thread_list, &tid);
864
    WINPR_ASSERT(thread);
865
    thread->exited = TRUE;
866
    thread->dwExitCode = dwExitCode;
867
#if defined(WITH_DEBUG_THREADS)
868
    thread->exit_stack = winpr_backtrace(20);
869
#endif
870
    ListDictionary_Unlock(thread_list);
871
    set_event(thread);
872
    rc = thread->dwExitCode;
873
874
    if (thread->detached || !thread->started)
875
      cleanup_handle(thread);
876
877
    pthread_exit((void*)(size_t)rc);
878
  }
879
#else
880
0
  WINPR_UNUSED(dwExitCode);
881
0
#endif
882
0
}
883
884
BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)
885
0
{
886
0
  ULONG Type = 0;
887
0
  WINPR_HANDLE* Object = NULL;
888
0
  WINPR_THREAD* thread = NULL;
889
890
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
891
0
  {
892
0
    WLog_ERR(TAG, "hThread is not a thread");
893
0
    SetLastError(ERROR_INVALID_PARAMETER);
894
0
    return FALSE;
895
0
  }
896
897
0
  thread = (WINPR_THREAD*)Object;
898
0
  *lpExitCode = thread->dwExitCode;
899
0
  return TRUE;
900
0
}
901
902
WINPR_THREAD* winpr_GetCurrentThread(VOID)
903
0
{
904
0
  WINPR_THREAD* ret = NULL;
905
906
0
  InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
907
0
  if (mainThreadId == pthread_self())
908
0
    return (HANDLE)&mainThread;
909
910
0
  ret = TlsGetValue(currentThreadTlsIndex);
911
0
  return ret;
912
0
}
913
914
HANDLE _GetCurrentThread(VOID)
915
0
{
916
0
  return (HANDLE)winpr_GetCurrentThread();
917
0
}
918
919
DWORD GetCurrentThreadId(VOID)
920
10.9M
{
921
#if defined(__FreeBSD__)
922
  return WINPR_CXX_COMPAT_CAST(DWORD, pthread_getthreadid_np());
923
#elif defined(__linux__)
924
10.9M
  return WINPR_CXX_COMPAT_CAST(DWORD, syscall(SYS_gettid));
925
#else
926
  pthread_t tid = pthread_self();
927
  /* Since pthread_t can be 64-bits on some systems, take just the    */
928
  /* lower 32-bits of it for the thread ID returned by this function. */
929
  uintptr_t ptid = WINPR_REINTERPRET_CAST(tid, pthread_t, uintptr_t);
930
  return (ptid & UINT32_MAX) ^ (ptid >> 32);
931
#endif
932
10.9M
}
933
934
typedef struct
935
{
936
  WINPR_APC_ITEM apc;
937
  PAPCFUNC completion;
938
  ULONG_PTR completionArg;
939
} UserApcItem;
940
941
static void userAPC(LPVOID arg)
942
0
{
943
0
  UserApcItem* userApc = (UserApcItem*)arg;
944
945
0
  userApc->completion(userApc->completionArg);
946
947
0
  userApc->apc.markedForRemove = TRUE;
948
0
}
949
950
DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData)
951
0
{
952
0
  ULONG Type = 0;
953
0
  WINPR_HANDLE* Object = NULL;
954
0
  WINPR_APC_ITEM* apc = NULL;
955
0
  UserApcItem* apcItem = NULL;
956
957
0
  if (!pfnAPC)
958
0
    return 1;
959
960
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
961
0
  {
962
0
    WLog_ERR(TAG, "hThread is not a thread");
963
0
    SetLastError(ERROR_INVALID_PARAMETER);
964
0
    return (DWORD)0;
965
0
  }
966
967
0
  apcItem = calloc(1, sizeof(*apcItem));
968
0
  if (!apcItem)
969
0
  {
970
0
    SetLastError(ERROR_INVALID_PARAMETER);
971
0
    return (DWORD)0;
972
0
  }
973
974
0
  apc = &apcItem->apc;
975
0
  apc->type = APC_TYPE_USER;
976
0
  apc->markedForFree = TRUE;
977
0
  apc->alwaysSignaled = TRUE;
978
0
  apc->completion = userAPC;
979
0
  apc->completionArgs = apc;
980
0
  apcItem->completion = pfnAPC;
981
0
  apcItem->completionArg = dwData;
982
0
  apc_register(hThread, apc);
983
0
  return 1;
984
0
}
985
986
DWORD ResumeThread(HANDLE hThread)
987
0
{
988
0
  ULONG Type = 0;
989
0
  WINPR_HANDLE* Object = NULL;
990
0
  WINPR_THREAD* thread = NULL;
991
992
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
993
0
  {
994
0
    WLog_ERR(TAG, "hThread is not a thread");
995
0
    SetLastError(ERROR_INVALID_PARAMETER);
996
0
    return (DWORD)-1;
997
0
  }
998
999
0
  thread = (WINPR_THREAD*)Object;
1000
1001
0
  if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
1002
0
    return (DWORD)-1;
1003
1004
0
  if (!thread->started)
1005
0
  {
1006
0
    if (!winpr_StartThread(thread))
1007
0
    {
1008
0
      run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex);
1009
0
      return (DWORD)-1;
1010
0
    }
1011
0
  }
1012
0
  else
1013
0
    WLog_WARN(TAG, "Thread already started!");
1014
1015
0
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1016
0
    return (DWORD)-1;
1017
1018
0
  return 0;
1019
0
}
1020
1021
DWORD SuspendThread(WINPR_ATTR_UNUSED HANDLE hThread)
1022
0
{
1023
0
  WLog_ERR(TAG, "not implemented");
1024
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1025
0
  return (DWORD)-1;
1026
0
}
1027
1028
BOOL SwitchToThread(VOID)
1029
0
{
1030
  /**
1031
   * Note: on some operating systems sched_yield is a stub returning -1.
1032
   * usleep should at least trigger a context switch if any thread is waiting.
1033
   */
1034
0
  if (sched_yield() != 0)
1035
0
    usleep(1);
1036
1037
0
  return TRUE;
1038
0
}
1039
1040
BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode)
1041
0
{
1042
0
  ULONG Type = 0;
1043
0
  WINPR_HANDLE* Object = NULL;
1044
0
  WINPR_THREAD* thread = NULL;
1045
1046
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
1047
0
    return FALSE;
1048
1049
0
  thread = (WINPR_THREAD*)Object;
1050
0
  thread->exited = TRUE;
1051
0
  thread->dwExitCode = dwExitCode;
1052
1053
0
  if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
1054
0
    return FALSE;
1055
1056
0
#ifndef ANDROID
1057
0
  pthread_cancel(thread->thread);
1058
#else
1059
  WLog_ERR(TAG, "Function not supported on this platform!");
1060
#endif
1061
1062
0
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1063
0
    return FALSE;
1064
1065
0
  set_event(thread);
1066
0
  return TRUE;
1067
0
}
1068
1069
VOID DumpThreadHandles(void)
1070
0
{
1071
#if defined(WITH_DEBUG_THREADS)
1072
  char** msg = NULL;
1073
  size_t used = 0;
1074
  void* stack = winpr_backtrace(20);
1075
  WLog_DBG(TAG, "---------------- Called from ----------------------------");
1076
  msg = winpr_backtrace_symbols(stack, &used);
1077
1078
  for (size_t i = 0; i < used; i++)
1079
  {
1080
    WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1081
  }
1082
1083
  free(msg);
1084
  winpr_backtrace_free(stack);
1085
  WLog_DBG(TAG, "---------------- Start Dumping thread handles -----------");
1086
1087
#if defined(WITH_THREAD_LIST)
1088
  if (!thread_list)
1089
  {
1090
    WLog_DBG(TAG, "All threads properly shut down and disposed of.");
1091
  }
1092
  else
1093
  {
1094
    ULONG_PTR* keys = NULL;
1095
    ListDictionary_Lock(thread_list);
1096
    int x, count = ListDictionary_GetKeys(thread_list, &keys);
1097
    WLog_DBG(TAG, "Dumping %d elements", count);
1098
1099
    for (size_t x = 0; x < count; x++)
1100
    {
1101
      WINPR_THREAD* thread = ListDictionary_GetItemValue(thread_list, (void*)keys[x]);
1102
      WLog_DBG(TAG, "Thread [%d] handle created still not closed!", x);
1103
      msg = winpr_backtrace_symbols(thread->create_stack, &used);
1104
1105
      for (size_t i = 0; i < used; i++)
1106
      {
1107
        WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1108
      }
1109
1110
      free(msg);
1111
1112
      if (thread->started)
1113
      {
1114
        WLog_DBG(TAG, "Thread [%d] still running!", x);
1115
      }
1116
      else
1117
      {
1118
        WLog_DBG(TAG, "Thread [%d] exited at:", x);
1119
        msg = winpr_backtrace_symbols(thread->exit_stack, &used);
1120
1121
        for (size_t i = 0; i < used; i++)
1122
          WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1123
1124
        free(msg);
1125
      }
1126
    }
1127
1128
    free(keys);
1129
    ListDictionary_Unlock(thread_list);
1130
  }
1131
#endif
1132
1133
  WLog_DBG(TAG, "---------------- End Dumping thread handles -------------");
1134
#endif
1135
0
}
1136
#endif