Coverage Report

Created: 2025-08-29 06:46

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