Coverage Report

Created: 2025-08-26 06:33

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