Coverage Report

Created: 2025-12-02 06:09

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