Coverage Report

Created: 2026-02-26 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/thread/thread.c
Line
Count
Source
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Process Thread Functions
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2015 Hewlett-Packard Development Company, L.P.
7
 * Copyright 2021 David Fort <contact@hardening-consulting.com>
8
 *
9
 *
10
 * Licensed under the Apache License, Version 2.0 (the "License");
11
 * you may not use this file except in compliance with the License.
12
 * You may obtain a copy of the License at
13
 *
14
 *     http://www.apache.org/licenses/LICENSE-2.0
15
 *
16
 * Unless required by applicable law or agreed to in writing, software
17
 * distributed under the License is distributed on an "AS IS" BASIS,
18
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
 * See the License for the specific language governing permissions and
20
 * limitations under the License.
21
 */
22
23
#include <winpr/config.h>
24
25
#include <winpr/winpr.h>
26
#include <winpr/assert.h>
27
28
#include <winpr/handle.h>
29
30
#include <winpr/thread.h>
31
32
#if defined(__FreeBSD__)
33
#include <pthread_np.h>
34
#elif defined(__linux__)
35
#include <sys/syscall.h>
36
#endif
37
38
#ifndef MIN
39
0
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
40
#endif
41
42
#ifndef MAX
43
0
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
44
#endif
45
46
/**
47
 * api-ms-win-core-processthreads-l1-1-1.dll
48
 *
49
 * CreateRemoteThread
50
 * CreateRemoteThreadEx
51
 * CreateThread
52
 * DeleteProcThreadAttributeList
53
 * ExitThread
54
 * FlushInstructionCache
55
 * FlushProcessWriteBuffers
56
 * GetCurrentThread
57
 * GetCurrentThreadId
58
 * GetCurrentThreadStackLimits
59
 * GetExitCodeThread
60
 * GetPriorityClass
61
 * GetStartupInfoW
62
 * GetThreadContext
63
 * GetThreadId
64
 * GetThreadIdealProcessorEx
65
 * GetThreadPriority
66
 * GetThreadPriorityBoost
67
 * GetThreadTimes
68
 * InitializeProcThreadAttributeList
69
 * OpenThread
70
 * OpenThreadToken
71
 * QueryProcessAffinityUpdateMode
72
 * QueueUserAPC
73
 * ResumeThread
74
 * SetPriorityClass
75
 * SetThreadContext
76
 * SetThreadPriority
77
 * SetThreadPriorityBoost
78
 * SetThreadStackGuarantee
79
 * SetThreadToken
80
 * SuspendThread
81
 * SwitchToThread
82
 * TerminateThread
83
 * UpdateProcThreadAttribute
84
 */
85
86
#ifndef _WIN32
87
88
#include <winpr/crt.h>
89
#include <winpr/platform.h>
90
91
#include <string.h>
92
#ifdef WINPR_HAVE_UNISTD_H
93
#include <unistd.h>
94
#endif
95
96
#ifdef WINPR_HAVE_SYS_EVENTFD_H
97
#include <sys/eventfd.h>
98
#endif
99
100
#include <winpr/debug.h>
101
102
#include <errno.h>
103
#include <fcntl.h>
104
105
#include <winpr/collections.h>
106
107
#include "thread.h"
108
#include "apc.h"
109
110
#include "../handle/handle.h"
111
#include "../log.h"
112
#define TAG WINPR_TAG("thread")
113
114
static WINPR_THREAD mainThread;
115
116
#if defined(WITH_THREAD_LIST)
117
static wListDictionary* thread_list = NULL;
118
#endif
119
120
static BOOL ThreadCloseHandle(HANDLE handle);
121
static void cleanup_handle(void* obj);
122
123
static BOOL ThreadIsHandled(HANDLE handle)
124
0
{
125
0
  return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_THREAD, FALSE);
126
0
}
127
128
static int ThreadGetFd(HANDLE handle)
129
0
{
130
0
  WINPR_THREAD* pThread = (WINPR_THREAD*)handle;
131
132
0
  if (!ThreadIsHandled(handle))
133
0
    return -1;
134
135
0
  return pThread->event.fds[0];
136
0
}
137
138
0
#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
0
{
143
0
  int rc = 0;
144
145
0
  WINPR_ASSERT(fkt);
146
0
  WINPR_ASSERT(mutex);
147
148
0
  rc = fkt(mutex, mutexattr);
149
0
  if (rc != 0)
150
0
  {
151
0
    char ebuffer[256] = WINPR_C_ARRAY_INIT;
152
0
    WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
153
0
  }
154
0
  return rc == 0;
155
0
}
156
157
0
#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
0
{
161
0
  int rc = 0;
162
163
0
  WINPR_ASSERT(fkt);
164
0
  WINPR_ASSERT(mutex);
165
166
0
  rc = fkt(mutex);
167
0
  if (rc != 0)
168
0
  {
169
0
    char ebuffer[256] = WINPR_C_ARRAY_INIT;
170
0
    WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
171
0
  }
172
0
  return rc == 0;
173
0
}
174
175
0
#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
0
{
179
0
  int rc = 0;
180
181
0
  WINPR_ASSERT(fkt);
182
0
  WINPR_ASSERT(condition);
183
184
0
  rc = fkt(condition, conditionattr);
185
0
  if (rc != 0)
186
0
  {
187
0
    char ebuffer[256] = WINPR_C_ARRAY_INIT;
188
0
    WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
189
0
  }
190
0
  return rc == 0;
191
0
}
192
193
0
#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
0
{
197
0
  int rc = 0;
198
199
0
  WINPR_ASSERT(fkt);
200
0
  WINPR_ASSERT(condition);
201
202
0
  rc = fkt(condition);
203
0
  if (rc != 0)
204
0
  {
205
0
    char ebuffer[256] = WINPR_C_ARRAY_INIT;
206
0
    WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
207
0
  }
208
0
  return rc == 0;
209
0
}
210
211
static int pthread_mutex_checked_unlock(pthread_mutex_t* mutex)
212
0
{
213
0
  WINPR_ASSERT(mutex);
214
0
  WINPR_ASSERT(pthread_mutex_trylock(mutex) == EBUSY);
215
0
  return pthread_mutex_unlock(mutex);
216
0
}
217
218
static BOOL mux_condition_bundle_init(mux_condition_bundle* bundle)
219
0
{
220
0
  WINPR_ASSERT(bundle);
221
222
0
  bundle->val = FALSE;
223
0
  if (!run_mutex_init(pthread_mutex_init, &bundle->mux, NULL))
224
0
    return FALSE;
225
226
0
  if (!run_cond_init(pthread_cond_init, &bundle->cond, NULL))
227
0
    return FALSE;
228
0
  return TRUE;
229
0
}
230
231
static void mux_condition_bundle_uninit(mux_condition_bundle* bundle)
232
0
{
233
0
  mux_condition_bundle empty = WINPR_C_ARRAY_INIT;
234
235
0
  WINPR_ASSERT(bundle);
236
237
0
  run_cond_fkt(pthread_cond_destroy, &bundle->cond);
238
0
  run_mutex_fkt(pthread_mutex_destroy, &bundle->mux);
239
0
  *bundle = empty;
240
0
}
241
242
static BOOL mux_condition_bundle_signal(mux_condition_bundle* bundle)
243
0
{
244
0
  BOOL rc = TRUE;
245
0
  WINPR_ASSERT(bundle);
246
247
0
  if (!run_mutex_fkt(pthread_mutex_lock, &bundle->mux))
248
0
    return FALSE;
249
0
  bundle->val = TRUE;
250
0
  if (!run_cond_fkt(pthread_cond_signal, &bundle->cond))
251
0
    rc = FALSE;
252
0
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux))
253
0
    rc = FALSE;
254
0
  return rc;
255
0
}
256
257
static BOOL mux_condition_bundle_lock(mux_condition_bundle* bundle)
258
0
{
259
0
  WINPR_ASSERT(bundle);
260
0
  return run_mutex_fkt(pthread_mutex_lock, &bundle->mux);
261
0
}
262
263
static BOOL mux_condition_bundle_unlock(mux_condition_bundle* bundle)
264
0
{
265
0
  WINPR_ASSERT(bundle);
266
0
  return run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux);
267
0
}
268
269
static BOOL mux_condition_bundle_wait(mux_condition_bundle* bundle, const char* name)
270
0
{
271
0
  BOOL rc = FALSE;
272
273
0
  WINPR_ASSERT(bundle);
274
0
  WINPR_ASSERT(name);
275
0
  WINPR_ASSERT(pthread_mutex_trylock(&bundle->mux) == EBUSY);
276
277
0
  while (!bundle->val)
278
0
  {
279
0
    int r = pthread_cond_wait(&bundle->cond, &bundle->mux);
280
0
    if (r != 0)
281
0
    {
282
0
      char ebuffer[256] = WINPR_C_ARRAY_INIT;
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
0
  }
298
299
0
  rc = bundle->val;
300
301
0
fail:
302
0
  return rc;
303
0
}
304
305
static BOOL signal_thread_ready(WINPR_THREAD* thread)
306
0
{
307
0
  WINPR_ASSERT(thread);
308
309
0
  return mux_condition_bundle_signal(&thread->isCreated);
310
0
}
311
312
static BOOL signal_thread_is_running(WINPR_THREAD* thread)
313
0
{
314
0
  WINPR_ASSERT(thread);
315
316
0
  return mux_condition_bundle_signal(&thread->isRunning);
317
0
}
318
319
static DWORD ThreadCleanupHandle(HANDLE handle)
320
0
{
321
0
  DWORD status = WAIT_FAILED;
322
0
  WINPR_THREAD* thread = (WINPR_THREAD*)handle;
323
324
0
  if (!ThreadIsHandled(handle))
325
0
    return WAIT_FAILED;
326
327
0
  if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
328
0
    return WAIT_FAILED;
329
330
0
  if (!thread->joined)
331
0
  {
332
0
    int rc = pthread_join(thread->thread, NULL);
333
334
0
    if (rc != 0)
335
0
    {
336
0
      char ebuffer[256] = WINPR_C_ARRAY_INIT;
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
0
    else
342
0
      thread->joined = TRUE;
343
0
  }
344
345
0
  status = WAIT_OBJECT_0;
346
347
0
fail:
348
0
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
349
0
    return WAIT_FAILED;
350
351
0
  return status;
352
0
}
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
0
{
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
0
  WINPR_UNUSED(thread);
418
0
#endif
419
0
}
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
0
{
427
0
  return winpr_event_set(&thread->event);
428
0
}
429
430
static BOOL reset_event(WINPR_THREAD* thread)
431
0
{
432
0
  return winpr_event_reset(&thread->event);
433
0
}
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
0
{
452
0
  if (!apc_init(&mainThread.apc))
453
0
  {
454
0
    WLog_ERR(TAG, "failed to initialize APC");
455
0
    goto out;
456
0
  }
457
458
0
  mainThread.common.Type = HANDLE_TYPE_THREAD;
459
0
  mainThreadId = pthread_self();
460
461
0
  currentThreadTlsIndex = TlsAlloc();
462
0
  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
0
out:
480
0
  return TRUE;
481
0
}
482
483
static BOOL signal_and_wait_for_ready(WINPR_THREAD* thread)
484
0
{
485
0
  BOOL res = FALSE;
486
487
0
  WINPR_ASSERT(thread);
488
489
0
  if (!mux_condition_bundle_lock(&thread->isRunning))
490
0
    return FALSE;
491
492
0
  if (!signal_thread_ready(thread))
493
0
    goto fail;
494
495
0
  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
0
  res = TRUE;
507
508
0
fail:
509
0
  if (!mux_condition_bundle_unlock(&thread->isRunning))
510
0
    return FALSE;
511
512
0
  return res;
513
0
}
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
0
{
520
0
  DWORD rc = 0;
521
0
  WINPR_THREAD* thread = (WINPR_THREAD*)arg;
522
0
  LPTHREAD_START_ROUTINE fkt = NULL;
523
524
0
  if (!thread)
525
0
  {
526
0
    WLog_ERR(TAG, "Called with invalid argument %p", arg);
527
0
    goto exit;
528
0
  }
529
530
0
  if (!TlsSetValue(currentThreadTlsIndex, thread))
531
0
  {
532
0
    WLog_ERR(TAG, "thread %" PRIu64 ", unable to set current thread value",
533
0
             WINPR_CXX_COMPAT_CAST(uint64_t, pthread_self()));
534
0
    goto exit;
535
0
  }
536
537
0
  if (!(fkt = thread->lpStartAddress))
538
0
  {
539
0
    union
540
0
    {
541
0
      LPTHREAD_START_ROUTINE fkt;
542
0
      void* pv;
543
0
    } cnv;
544
0
    cnv.fkt = fkt;
545
0
    WLog_ERR(TAG, "Thread function argument is %p", cnv.pv);
546
0
    goto exit;
547
0
  }
548
549
0
  if (!signal_and_wait_for_ready(thread))
550
0
    goto exit;
551
552
0
  rc = fkt(thread->lpParameter);
553
0
exit:
554
555
0
  if (thread)
556
0
  {
557
0
    apc_cleanupThread(thread);
558
559
0
    if (!thread->exited)
560
0
      thread->dwExitCode = rc;
561
562
0
    set_event(thread);
563
564
0
    (void)signal_thread_ready(thread);
565
566
0
    if (thread->detached || !thread->started)
567
0
      cleanup_handle(thread);
568
0
  }
569
570
0
  return NULL;
571
0
}
572
573
static BOOL winpr_StartThread(WINPR_THREAD* thread)
574
0
{
575
0
  BOOL rc = FALSE;
576
0
  BOOL locked = FALSE;
577
0
  pthread_attr_t attr = WINPR_C_ARRAY_INIT;
578
579
0
  if (!mux_condition_bundle_lock(&thread->isCreated))
580
0
    return FALSE;
581
0
  locked = TRUE;
582
583
0
  pthread_attr_init(&attr);
584
0
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
585
586
0
  if (thread->dwStackSize > 0)
587
0
    pthread_attr_setstacksize(&attr, thread->dwStackSize);
588
589
0
  thread->started = TRUE;
590
0
  reset_event(thread);
591
592
#if defined(WITH_THREAD_LIST)
593
  if (!ListDictionary_Add(thread_list, &thread->thread, thread))
594
  {
595
    WLog_ERR(TAG, "failed to add the thread to the thread list");
596
    goto error;
597
  }
598
#endif
599
600
0
  if (pthread_create(&thread->thread, &attr, thread_launcher, thread))
601
0
    goto error;
602
603
0
  if (!mux_condition_bundle_wait(&thread->isCreated, "threadIsCreated"))
604
0
    goto error;
605
606
0
  locked = FALSE;
607
0
  if (!mux_condition_bundle_unlock(&thread->isCreated))
608
0
    goto error;
609
610
0
  if (!signal_thread_is_running(thread))
611
0
  {
612
0
    WLog_ERR(TAG, "failed to signal the thread was ready");
613
0
    goto error;
614
0
  }
615
616
0
  rc = TRUE;
617
0
error:
618
0
  if (locked)
619
0
  {
620
0
    if (!mux_condition_bundle_unlock(&thread->isCreated))
621
0
      rc = FALSE;
622
0
  }
623
624
0
  pthread_attr_destroy(&attr);
625
626
0
  if (rc)
627
0
    dump_thread(thread);
628
629
0
  return rc;
630
0
}
631
632
BOOL SetThreadPriority(HANDLE hThread, int nPriority)
633
0
{
634
0
  ULONG Type = 0;
635
0
  WINPR_HANDLE* Object = NULL;
636
637
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
638
0
    return FALSE;
639
640
0
  const int min = 19;
641
0
  const int max = 0;
642
0
  const int diff = (max - min);
643
0
  const int normal = min + diff / 2;
644
0
  const int off = MIN(1, diff / 4);
645
0
  int sched_priority = -1;
646
647
0
  switch (nPriority & ~(THREAD_MODE_BACKGROUND_BEGIN | THREAD_MODE_BACKGROUND_END))
648
0
  {
649
0
    case THREAD_PRIORITY_ABOVE_NORMAL:
650
0
      sched_priority = MIN(normal + off, max);
651
0
      break;
652
0
    case THREAD_PRIORITY_BELOW_NORMAL:
653
0
      sched_priority = MAX(normal - off, min);
654
0
      break;
655
0
    case THREAD_PRIORITY_HIGHEST:
656
0
      sched_priority = max;
657
0
      break;
658
0
    case THREAD_PRIORITY_IDLE:
659
0
      sched_priority = min;
660
0
      break;
661
0
    case THREAD_PRIORITY_LOWEST:
662
0
      sched_priority = min;
663
0
      break;
664
0
    case THREAD_PRIORITY_TIME_CRITICAL:
665
0
      sched_priority = max;
666
0
      break;
667
0
    default:
668
0
    case THREAD_PRIORITY_NORMAL:
669
0
      sched_priority = normal;
670
0
      break;
671
0
  }
672
0
#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L) && defined(PTHREAD_SETSCHEDPRIO)
673
0
  WINPR_THREAD* thread = (WINPR_THREAD*)Object;
674
0
  const int rc = pthread_setschedprio(thread->thread, sched_priority);
675
0
  if (rc != 0)
676
0
  {
677
0
    char buffer[256] = WINPR_C_ARRAY_INIT;
678
0
    WLog_ERR(TAG, "pthread_setschedprio(%d) %s [%d]", sched_priority,
679
0
             winpr_strerror(rc, buffer, sizeof(buffer)), rc);
680
0
  }
681
0
  return rc == 0;
682
#else
683
  WLog_WARN(TAG, "pthread_setschedprio(%d) not implemented, requires POSIX 2008 or later",
684
            sched_priority);
685
  return TRUE;
686
#endif
687
0
}
688
689
HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, size_t dwStackSize,
690
                    LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter,
691
                    DWORD dwCreationFlags, WINPR_ATTR_UNUSED LPDWORD lpThreadId)
692
0
{
693
0
  HANDLE handle = NULL;
694
0
  WINPR_THREAD* thread = (WINPR_THREAD*)calloc(1, sizeof(WINPR_THREAD));
695
696
0
  if (!thread)
697
0
    return NULL;
698
699
0
  thread->dwStackSize = dwStackSize;
700
0
  thread->lpParameter = lpParameter;
701
0
  thread->lpStartAddress = lpStartAddress;
702
0
  thread->lpThreadAttributes = lpThreadAttributes;
703
0
  thread->common.ops = &ops;
704
#if defined(WITH_DEBUG_THREADS)
705
  thread->create_stack = winpr_backtrace(20);
706
  dump_thread(thread);
707
#endif
708
709
0
  if (!winpr_event_init(&thread->event))
710
0
  {
711
0
    WLog_ERR(TAG, "failed to create event");
712
0
    goto fail;
713
0
  }
714
715
0
  if (!run_mutex_init(pthread_mutex_init, &thread->mutex, NULL))
716
0
  {
717
0
    WLog_ERR(TAG, "failed to initialize thread mutex");
718
0
    goto fail;
719
0
  }
720
721
0
  if (!apc_init(&thread->apc))
722
0
  {
723
0
    WLog_ERR(TAG, "failed to initialize APC");
724
0
    goto fail;
725
0
  }
726
727
0
  if (!mux_condition_bundle_init(&thread->isCreated))
728
0
    goto fail;
729
0
  if (!mux_condition_bundle_init(&thread->isRunning))
730
0
    goto fail;
731
732
0
  WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ);
733
0
  handle = (HANDLE)thread;
734
735
0
  InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
736
737
0
  if (!(dwCreationFlags & CREATE_SUSPENDED))
738
0
  {
739
0
    if (!winpr_StartThread(thread))
740
0
      goto fail;
741
0
  }
742
0
  else
743
0
  {
744
0
    if (!set_event(thread))
745
0
      goto fail;
746
0
  }
747
748
0
  return handle;
749
0
fail:
750
0
  cleanup_handle(thread);
751
0
  return NULL;
752
0
}
753
754
void cleanup_handle(void* obj)
755
0
{
756
0
  WINPR_THREAD* thread = (WINPR_THREAD*)obj;
757
0
  if (!thread)
758
0
    return;
759
760
0
  if (!apc_uninit(&thread->apc))
761
0
    WLog_ERR(TAG, "failed to destroy APC");
762
763
0
  mux_condition_bundle_uninit(&thread->isCreated);
764
0
  mux_condition_bundle_uninit(&thread->isRunning);
765
0
  run_mutex_fkt(pthread_mutex_destroy, &thread->mutex);
766
767
0
  winpr_event_uninit(&thread->event);
768
769
#if defined(WITH_THREAD_LIST)
770
  ListDictionary_Remove(thread_list, &thread->thread);
771
#endif
772
#if defined(WITH_DEBUG_THREADS)
773
774
  if (thread->create_stack)
775
    winpr_backtrace_free(thread->create_stack);
776
777
  if (thread->exit_stack)
778
    winpr_backtrace_free(thread->exit_stack);
779
780
#endif
781
0
  free(thread);
782
0
}
783
784
BOOL ThreadCloseHandle(HANDLE handle)
785
0
{
786
0
  WINPR_THREAD* thread = (WINPR_THREAD*)handle;
787
788
#if defined(WITH_THREAD_LIST)
789
  if (!thread_list)
790
  {
791
    WLog_ERR(TAG, "Thread list does not exist, check call!");
792
    dump_thread(thread);
793
  }
794
  else if (!ListDictionary_Contains(thread_list, &thread->thread))
795
  {
796
    WLog_ERR(TAG, "Thread list does not contain this thread! check call!");
797
    dump_thread(thread);
798
  }
799
  else
800
  {
801
    ListDictionary_Lock(thread_list);
802
#endif
803
0
    dump_thread(thread);
804
805
0
    if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0))
806
0
    {
807
0
      WLog_DBG(TAG, "Thread running, setting to detached state!");
808
0
      thread->detached = TRUE;
809
0
      pthread_detach(thread->thread);
810
0
    }
811
0
    else
812
0
    {
813
0
      cleanup_handle(thread);
814
0
    }
815
816
#if defined(WITH_THREAD_LIST)
817
    ListDictionary_Unlock(thread_list);
818
  }
819
#endif
820
821
0
  return TRUE;
822
0
}
823
824
HANDLE CreateRemoteThread(WINPR_ATTR_UNUSED HANDLE hProcess,
825
                          WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpThreadAttributes,
826
                          WINPR_ATTR_UNUSED size_t dwStackSize,
827
                          WINPR_ATTR_UNUSED LPTHREAD_START_ROUTINE lpStartAddress,
828
                          WINPR_ATTR_UNUSED LPVOID lpParameter,
829
                          WINPR_ATTR_UNUSED DWORD dwCreationFlags,
830
                          WINPR_ATTR_UNUSED LPDWORD lpThreadId)
831
0
{
832
0
  WLog_ERR(TAG, "not implemented");
833
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
834
0
  return NULL;
835
0
}
836
837
VOID ExitThread(DWORD dwExitCode)
838
0
{
839
#if defined(WITH_THREAD_LIST)
840
  DWORD rc;
841
  pthread_t tid = pthread_self();
842
843
  if (!thread_list)
844
  {
845
    WLog_ERR(TAG, "function called without existing thread list!");
846
#if defined(WITH_DEBUG_THREADS)
847
    DumpThreadHandles();
848
#endif
849
    pthread_exit(0);
850
  }
851
  else if (!ListDictionary_Contains(thread_list, &tid))
852
  {
853
    WLog_ERR(TAG, "function called, but no matching entry in thread list!");
854
#if defined(WITH_DEBUG_THREADS)
855
    DumpThreadHandles();
856
#endif
857
    pthread_exit(0);
858
  }
859
  else
860
  {
861
    WINPR_THREAD* thread;
862
    ListDictionary_Lock(thread_list);
863
    thread = ListDictionary_GetItemValue(thread_list, &tid);
864
    WINPR_ASSERT(thread);
865
    thread->exited = TRUE;
866
    thread->dwExitCode = dwExitCode;
867
#if defined(WITH_DEBUG_THREADS)
868
    thread->exit_stack = winpr_backtrace(20);
869
#endif
870
    ListDictionary_Unlock(thread_list);
871
    set_event(thread);
872
    rc = thread->dwExitCode;
873
874
    if (thread->detached || !thread->started)
875
      cleanup_handle(thread);
876
877
    pthread_exit((void*)(size_t)rc);
878
  }
879
#else
880
0
  WINPR_UNUSED(dwExitCode);
881
0
#endif
882
0
}
883
884
BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)
885
0
{
886
0
  ULONG Type = 0;
887
0
  WINPR_HANDLE* Object = NULL;
888
0
  WINPR_THREAD* thread = NULL;
889
890
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
891
0
  {
892
0
    WLog_ERR(TAG, "hThread is not a thread");
893
0
    SetLastError(ERROR_INVALID_PARAMETER);
894
0
    return FALSE;
895
0
  }
896
897
0
  thread = (WINPR_THREAD*)Object;
898
0
  *lpExitCode = thread->dwExitCode;
899
0
  return TRUE;
900
0
}
901
902
WINPR_THREAD* winpr_GetCurrentThread(VOID)
903
0
{
904
0
  WINPR_THREAD* ret = NULL;
905
906
0
  InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
907
0
  if (mainThreadId == pthread_self())
908
0
    return (HANDLE)&mainThread;
909
910
0
  ret = TlsGetValue(currentThreadTlsIndex);
911
0
  return ret;
912
0
}
913
914
HANDLE _GetCurrentThread(VOID)
915
0
{
916
0
  return (HANDLE)winpr_GetCurrentThread();
917
0
}
918
919
DWORD GetCurrentThreadId(VOID)
920
1.00k
{
921
#if defined(__FreeBSD__)
922
  return WINPR_CXX_COMPAT_CAST(DWORD, pthread_getthreadid_np());
923
#elif defined(__linux__)
924
1.00k
  return WINPR_CXX_COMPAT_CAST(DWORD, syscall(SYS_gettid));
925
#else
926
  pthread_t tid = pthread_self();
927
  /* Since pthread_t can be 64-bits on some systems, take just the    */
928
  /* lower 32-bits of it for the thread ID returned by this function. */
929
  uintptr_t ptid = WINPR_REINTERPRET_CAST(tid, pthread_t, uintptr_t);
930
  return (ptid & UINT32_MAX) ^ (ptid >> 32);
931
#endif
932
1.00k
}
933
934
typedef struct
935
{
936
  WINPR_APC_ITEM apc;
937
  PAPCFUNC completion;
938
  ULONG_PTR completionArg;
939
} UserApcItem;
940
941
static void userAPC(LPVOID arg)
942
0
{
943
0
  UserApcItem* userApc = (UserApcItem*)arg;
944
945
0
  userApc->completion(userApc->completionArg);
946
947
0
  userApc->apc.markedForRemove = TRUE;
948
0
}
949
950
DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData)
951
0
{
952
0
  ULONG Type = 0;
953
0
  WINPR_HANDLE* Object = NULL;
954
0
  WINPR_APC_ITEM* apc = NULL;
955
0
  UserApcItem* apcItem = NULL;
956
957
0
  if (!pfnAPC)
958
0
    return 1;
959
960
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
961
0
  {
962
0
    WLog_ERR(TAG, "hThread is not a thread");
963
0
    SetLastError(ERROR_INVALID_PARAMETER);
964
0
    return (DWORD)0;
965
0
  }
966
967
0
  apcItem = calloc(1, sizeof(*apcItem));
968
0
  if (!apcItem)
969
0
  {
970
0
    SetLastError(ERROR_INVALID_PARAMETER);
971
0
    return (DWORD)0;
972
0
  }
973
974
0
  apc = &apcItem->apc;
975
0
  apc->type = APC_TYPE_USER;
976
0
  apc->markedForFree = TRUE;
977
0
  apc->alwaysSignaled = TRUE;
978
0
  apc->completion = userAPC;
979
0
  apc->completionArgs = apc;
980
0
  apcItem->completion = pfnAPC;
981
0
  apcItem->completionArg = dwData;
982
0
  apc_register(hThread, apc);
983
0
  return 1;
984
0
}
985
986
DWORD ResumeThread(HANDLE hThread)
987
0
{
988
0
  ULONG Type = 0;
989
0
  WINPR_HANDLE* Object = NULL;
990
0
  WINPR_THREAD* thread = NULL;
991
992
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
993
0
  {
994
0
    WLog_ERR(TAG, "hThread is not a thread");
995
0
    SetLastError(ERROR_INVALID_PARAMETER);
996
0
    return (DWORD)-1;
997
0
  }
998
999
0
  thread = (WINPR_THREAD*)Object;
1000
1001
0
  if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
1002
0
    return (DWORD)-1;
1003
1004
0
  if (!thread->started)
1005
0
  {
1006
0
    if (!winpr_StartThread(thread))
1007
0
    {
1008
0
      run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex);
1009
0
      return (DWORD)-1;
1010
0
    }
1011
0
  }
1012
0
  else
1013
0
    WLog_WARN(TAG, "Thread already started!");
1014
1015
0
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1016
0
    return (DWORD)-1;
1017
1018
0
  return 0;
1019
0
}
1020
1021
DWORD SuspendThread(WINPR_ATTR_UNUSED HANDLE hThread)
1022
0
{
1023
0
  WLog_ERR(TAG, "not implemented");
1024
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1025
0
  return (DWORD)-1;
1026
0
}
1027
1028
BOOL SwitchToThread(VOID)
1029
0
{
1030
  /**
1031
   * Note: on some operating systems sched_yield is a stub returning -1.
1032
   * usleep should at least trigger a context switch if any thread is waiting.
1033
   */
1034
0
  if (sched_yield() != 0)
1035
0
    usleep(1);
1036
1037
0
  return TRUE;
1038
0
}
1039
1040
BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode)
1041
0
{
1042
0
  ULONG Type = 0;
1043
0
  WINPR_HANDLE* Object = NULL;
1044
0
  WINPR_THREAD* thread = NULL;
1045
1046
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
1047
0
    return FALSE;
1048
1049
0
  thread = (WINPR_THREAD*)Object;
1050
0
  thread->exited = TRUE;
1051
0
  thread->dwExitCode = dwExitCode;
1052
1053
0
  if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
1054
0
    return FALSE;
1055
1056
0
#ifndef ANDROID
1057
0
  pthread_cancel(thread->thread);
1058
#else
1059
  WLog_ERR(TAG, "Function not supported on this platform!");
1060
#endif
1061
1062
0
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1063
0
    return FALSE;
1064
1065
0
  set_event(thread);
1066
0
  return TRUE;
1067
0
}
1068
1069
VOID DumpThreadHandles(void)
1070
0
{
1071
#if defined(WITH_DEBUG_THREADS)
1072
  char** msg = NULL;
1073
  size_t used = 0;
1074
  void* stack = winpr_backtrace(20);
1075
  WLog_DBG(TAG, "---------------- Called from ----------------------------");
1076
  msg = winpr_backtrace_symbols(stack, &used);
1077
1078
  for (size_t i = 0; i < used; i++)
1079
  {
1080
    WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1081
  }
1082
1083
  free(msg);
1084
  winpr_backtrace_free(stack);
1085
  WLog_DBG(TAG, "---------------- Start Dumping thread handles -----------");
1086
1087
#if defined(WITH_THREAD_LIST)
1088
  if (!thread_list)
1089
  {
1090
    WLog_DBG(TAG, "All threads properly shut down and disposed of.");
1091
  }
1092
  else
1093
  {
1094
    ULONG_PTR* keys = NULL;
1095
    ListDictionary_Lock(thread_list);
1096
    int x, count = ListDictionary_GetKeys(thread_list, &keys);
1097
    WLog_DBG(TAG, "Dumping %d elements", count);
1098
1099
    for (size_t x = 0; x < count; x++)
1100
    {
1101
      WINPR_THREAD* thread = ListDictionary_GetItemValue(thread_list, (void*)keys[x]);
1102
      WLog_DBG(TAG, "Thread [%d] handle created still not closed!", x);
1103
      msg = winpr_backtrace_symbols(thread->create_stack, &used);
1104
1105
      for (size_t i = 0; i < used; i++)
1106
      {
1107
        WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1108
      }
1109
1110
      free(msg);
1111
1112
      if (thread->started)
1113
      {
1114
        WLog_DBG(TAG, "Thread [%d] still running!", x);
1115
      }
1116
      else
1117
      {
1118
        WLog_DBG(TAG, "Thread [%d] exited at:", x);
1119
        msg = winpr_backtrace_symbols(thread->exit_stack, &used);
1120
1121
        for (size_t i = 0; i < used; i++)
1122
          WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1123
1124
        free(msg);
1125
      }
1126
    }
1127
1128
    free(keys);
1129
    ListDictionary_Unlock(thread_list);
1130
  }
1131
#endif
1132
1133
  WLog_DBG(TAG, "---------------- End Dumping thread handles -------------");
1134
#endif
1135
0
}
1136
#endif