Coverage Report

Created: 2025-07-01 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
#ifndef MIN
33
0
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
34
#endif
35
36
#ifndef MAX
37
0
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
38
#endif
39
40
/**
41
 * api-ms-win-core-processthreads-l1-1-1.dll
42
 *
43
 * CreateRemoteThread
44
 * CreateRemoteThreadEx
45
 * CreateThread
46
 * DeleteProcThreadAttributeList
47
 * ExitThread
48
 * FlushInstructionCache
49
 * FlushProcessWriteBuffers
50
 * GetCurrentThread
51
 * GetCurrentThreadId
52
 * GetCurrentThreadStackLimits
53
 * GetExitCodeThread
54
 * GetPriorityClass
55
 * GetStartupInfoW
56
 * GetThreadContext
57
 * GetThreadId
58
 * GetThreadIdealProcessorEx
59
 * GetThreadPriority
60
 * GetThreadPriorityBoost
61
 * GetThreadTimes
62
 * InitializeProcThreadAttributeList
63
 * OpenThread
64
 * OpenThreadToken
65
 * QueryProcessAffinityUpdateMode
66
 * QueueUserAPC
67
 * ResumeThread
68
 * SetPriorityClass
69
 * SetThreadContext
70
 * SetThreadPriority
71
 * SetThreadPriorityBoost
72
 * SetThreadStackGuarantee
73
 * SetThreadToken
74
 * SuspendThread
75
 * SwitchToThread
76
 * TerminateThread
77
 * UpdateProcThreadAttribute
78
 */
79
80
#ifndef _WIN32
81
82
#include <winpr/crt.h>
83
#include <winpr/platform.h>
84
85
#include <string.h>
86
#ifdef WINPR_HAVE_UNISTD_H
87
#include <unistd.h>
88
#endif
89
90
#ifdef WINPR_HAVE_SYS_EVENTFD_H
91
#include <sys/eventfd.h>
92
#endif
93
94
#include <winpr/debug.h>
95
96
#include <errno.h>
97
#include <fcntl.h>
98
99
#include <winpr/collections.h>
100
101
#include "thread.h"
102
#include "apc.h"
103
104
#include "../handle/handle.h"
105
#include "../log.h"
106
#define TAG WINPR_TAG("thread")
107
108
static WINPR_THREAD mainThread;
109
110
#if defined(WITH_THREAD_LIST)
111
static wListDictionary* thread_list = NULL;
112
#endif
113
114
static BOOL ThreadCloseHandle(HANDLE handle);
115
static void cleanup_handle(void* obj);
116
117
static BOOL ThreadIsHandled(HANDLE handle)
118
0
{
119
0
  return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_THREAD, FALSE);
120
0
}
121
122
static int ThreadGetFd(HANDLE handle)
123
0
{
124
0
  WINPR_THREAD* pThread = (WINPR_THREAD*)handle;
125
126
0
  if (!ThreadIsHandled(handle))
127
0
    return -1;
128
129
0
  return pThread->event.fds[0];
130
0
}
131
132
0
#define run_mutex_init(fkt, mux, arg) run_mutex_init_(fkt, #fkt, mux, arg)
133
static BOOL run_mutex_init_(int (*fkt)(pthread_mutex_t*, const pthread_mutexattr_t*),
134
                            const char* name, pthread_mutex_t* mutex,
135
                            const pthread_mutexattr_t* mutexattr)
136
0
{
137
0
  int rc = 0;
138
139
0
  WINPR_ASSERT(fkt);
140
0
  WINPR_ASSERT(mutex);
141
142
0
  rc = fkt(mutex, mutexattr);
143
0
  if (rc != 0)
144
0
  {
145
0
    char ebuffer[256] = { 0 };
146
0
    WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
147
0
  }
148
0
  return rc == 0;
149
0
}
150
151
0
#define run_mutex_fkt(fkt, mux) run_mutex_fkt_(fkt, #fkt, mux)
152
static BOOL run_mutex_fkt_(int (*fkt)(pthread_mutex_t* mux), const char* name,
153
                           pthread_mutex_t* mutex)
154
0
{
155
0
  int rc = 0;
156
157
0
  WINPR_ASSERT(fkt);
158
0
  WINPR_ASSERT(mutex);
159
160
0
  rc = fkt(mutex);
161
0
  if (rc != 0)
162
0
  {
163
0
    char ebuffer[256] = { 0 };
164
0
    WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
165
0
  }
166
0
  return rc == 0;
167
0
}
168
169
0
#define run_cond_init(fkt, cond, arg) run_cond_init_(fkt, #fkt, cond, arg)
170
static BOOL run_cond_init_(int (*fkt)(pthread_cond_t*, const pthread_condattr_t*), const char* name,
171
                           pthread_cond_t* condition, const pthread_condattr_t* conditionattr)
172
0
{
173
0
  int rc = 0;
174
175
0
  WINPR_ASSERT(fkt);
176
0
  WINPR_ASSERT(condition);
177
178
0
  rc = fkt(condition, conditionattr);
179
0
  if (rc != 0)
180
0
  {
181
0
    char ebuffer[256] = { 0 };
182
0
    WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
183
0
  }
184
0
  return rc == 0;
185
0
}
186
187
0
#define run_cond_fkt(fkt, cond) run_cond_fkt_(fkt, #fkt, cond)
188
static BOOL run_cond_fkt_(int (*fkt)(pthread_cond_t* mux), const char* name,
189
                          pthread_cond_t* condition)
190
0
{
191
0
  int rc = 0;
192
193
0
  WINPR_ASSERT(fkt);
194
0
  WINPR_ASSERT(condition);
195
196
0
  rc = fkt(condition);
197
0
  if (rc != 0)
198
0
  {
199
0
    char ebuffer[256] = { 0 };
200
0
    WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
201
0
  }
202
0
  return rc == 0;
203
0
}
204
205
static int pthread_mutex_checked_unlock(pthread_mutex_t* mutex)
206
0
{
207
0
  WINPR_ASSERT(mutex);
208
0
  WINPR_ASSERT(pthread_mutex_trylock(mutex) == EBUSY);
209
0
  return pthread_mutex_unlock(mutex);
210
0
}
211
212
static BOOL mux_condition_bundle_init(mux_condition_bundle* bundle)
213
0
{
214
0
  WINPR_ASSERT(bundle);
215
216
0
  bundle->val = FALSE;
217
0
  if (!run_mutex_init(pthread_mutex_init, &bundle->mux, NULL))
218
0
    return FALSE;
219
220
0
  if (!run_cond_init(pthread_cond_init, &bundle->cond, NULL))
221
0
    return FALSE;
222
0
  return TRUE;
223
0
}
224
225
static void mux_condition_bundle_uninit(mux_condition_bundle* bundle)
226
0
{
227
0
  mux_condition_bundle empty = { 0 };
228
229
0
  WINPR_ASSERT(bundle);
230
231
0
  run_cond_fkt(pthread_cond_destroy, &bundle->cond);
232
0
  run_mutex_fkt(pthread_mutex_destroy, &bundle->mux);
233
0
  *bundle = empty;
234
0
}
235
236
static BOOL mux_condition_bundle_signal(mux_condition_bundle* bundle)
237
0
{
238
0
  BOOL rc = TRUE;
239
0
  WINPR_ASSERT(bundle);
240
241
0
  if (!run_mutex_fkt(pthread_mutex_lock, &bundle->mux))
242
0
    return FALSE;
243
0
  bundle->val = TRUE;
244
0
  if (!run_cond_fkt(pthread_cond_signal, &bundle->cond))
245
0
    rc = FALSE;
246
0
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux))
247
0
    rc = FALSE;
248
0
  return rc;
249
0
}
250
251
static BOOL mux_condition_bundle_lock(mux_condition_bundle* bundle)
252
0
{
253
0
  WINPR_ASSERT(bundle);
254
0
  return run_mutex_fkt(pthread_mutex_lock, &bundle->mux);
255
0
}
256
257
static BOOL mux_condition_bundle_unlock(mux_condition_bundle* bundle)
258
0
{
259
0
  WINPR_ASSERT(bundle);
260
0
  return run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux);
261
0
}
262
263
static BOOL mux_condition_bundle_wait(mux_condition_bundle* bundle, const char* name)
264
0
{
265
0
  BOOL rc = FALSE;
266
267
0
  WINPR_ASSERT(bundle);
268
0
  WINPR_ASSERT(name);
269
0
  WINPR_ASSERT(pthread_mutex_trylock(&bundle->mux) == EBUSY);
270
271
0
  while (!bundle->val)
272
0
  {
273
0
    int r = pthread_cond_wait(&bundle->cond, &bundle->mux);
274
0
    if (r != 0)
275
0
    {
276
0
      char ebuffer[256] = { 0 };
277
0
      WLog_ERR(TAG, "failed to wait for %s [%s]", name,
278
0
               winpr_strerror(r, ebuffer, sizeof(ebuffer)));
279
0
      switch (r)
280
0
      {
281
0
        case ENOTRECOVERABLE:
282
0
        case EPERM:
283
0
        case ETIMEDOUT:
284
0
        case EINVAL:
285
0
          goto fail;
286
287
0
        default:
288
0
          break;
289
0
      }
290
0
    }
291
0
  }
292
293
0
  rc = bundle->val;
294
295
0
fail:
296
0
  return rc;
297
0
}
298
299
static BOOL signal_thread_ready(WINPR_THREAD* thread)
300
0
{
301
0
  WINPR_ASSERT(thread);
302
303
0
  return mux_condition_bundle_signal(&thread->isCreated);
304
0
}
305
306
static BOOL signal_thread_is_running(WINPR_THREAD* thread)
307
0
{
308
0
  WINPR_ASSERT(thread);
309
310
0
  return mux_condition_bundle_signal(&thread->isRunning);
311
0
}
312
313
static DWORD ThreadCleanupHandle(HANDLE handle)
314
0
{
315
0
  DWORD status = WAIT_FAILED;
316
0
  WINPR_THREAD* thread = (WINPR_THREAD*)handle;
317
318
0
  if (!ThreadIsHandled(handle))
319
0
    return WAIT_FAILED;
320
321
0
  if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
322
0
    return WAIT_FAILED;
323
324
0
  if (!thread->joined)
325
0
  {
326
0
    int rc = pthread_join(thread->thread, NULL);
327
328
0
    if (rc != 0)
329
0
    {
330
0
      char ebuffer[256] = { 0 };
331
0
      WLog_ERR(TAG, "pthread_join failure: [%d] %s", rc,
332
0
               winpr_strerror(rc, ebuffer, sizeof(ebuffer)));
333
0
      goto fail;
334
0
    }
335
0
    else
336
0
      thread->joined = TRUE;
337
0
  }
338
339
0
  status = WAIT_OBJECT_0;
340
341
0
fail:
342
0
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
343
0
    return WAIT_FAILED;
344
345
0
  return status;
346
0
}
347
348
static HANDLE_OPS ops = { ThreadIsHandled,
349
                        ThreadCloseHandle,
350
                        ThreadGetFd,
351
                        ThreadCleanupHandle,
352
                        NULL,
353
                        NULL,
354
                        NULL,
355
                        NULL,
356
                        NULL,
357
                        NULL,
358
                        NULL,
359
                        NULL,
360
                        NULL,
361
                        NULL,
362
                        NULL,
363
                        NULL,
364
                        NULL,
365
                        NULL,
366
                        NULL,
367
                        NULL,
368
                        NULL };
369
370
static void dump_thread(WINPR_THREAD* thread)
371
0
{
372
#if defined(WITH_DEBUG_THREADS)
373
  void* stack = winpr_backtrace(20);
374
  char** msg = NULL;
375
  size_t used = 0;
376
  WLog_DBG(TAG, "Called from:");
377
  msg = winpr_backtrace_symbols(stack, &used);
378
379
  for (size_t i = 0; i < used; i++)
380
    WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
381
382
  free(msg);
383
  winpr_backtrace_free(stack);
384
  WLog_DBG(TAG, "Thread handle created still not closed!");
385
  msg = winpr_backtrace_symbols(thread->create_stack, &used);
386
387
  for (size_t i = 0; i < used; i++)
388
    WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
389
390
  free(msg);
391
392
  if (thread->started)
393
  {
394
    WLog_DBG(TAG, "Thread still running!");
395
  }
396
  else if (!thread->exit_stack)
397
  {
398
    WLog_DBG(TAG, "Thread suspended.");
399
  }
400
  else
401
  {
402
    WLog_DBG(TAG, "Thread exited at:");
403
    msg = winpr_backtrace_symbols(thread->exit_stack, &used);
404
405
    for (size_t i = 0; i < used; i++)
406
      WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
407
408
    free(msg);
409
  }
410
#else
411
0
  WINPR_UNUSED(thread);
412
0
#endif
413
0
}
414
415
/**
416
 * TODO: implement thread suspend/resume using pthreads
417
 * http://stackoverflow.com/questions/3140867/suspend-pthreads-without-using-condition
418
 */
419
static BOOL set_event(WINPR_THREAD* thread)
420
0
{
421
0
  return winpr_event_set(&thread->event);
422
0
}
423
424
static BOOL reset_event(WINPR_THREAD* thread)
425
0
{
426
0
  return winpr_event_reset(&thread->event);
427
0
}
428
429
#if defined(WITH_THREAD_LIST)
430
static BOOL thread_compare(const void* a, const void* b)
431
{
432
  const pthread_t* p1 = a;
433
  const pthread_t* p2 = b;
434
  BOOL rc = pthread_equal(*p1, *p2);
435
  return rc;
436
}
437
#endif
438
439
static INIT_ONCE threads_InitOnce = INIT_ONCE_STATIC_INIT;
440
static pthread_t mainThreadId;
441
static DWORD currentThreadTlsIndex = TLS_OUT_OF_INDEXES;
442
443
static BOOL initializeThreads(WINPR_ATTR_UNUSED PINIT_ONCE InitOnce,
444
                              WINPR_ATTR_UNUSED PVOID Parameter, WINPR_ATTR_UNUSED PVOID* Context)
445
0
{
446
0
  if (!apc_init(&mainThread.apc))
447
0
  {
448
0
    WLog_ERR(TAG, "failed to initialize APC");
449
0
    goto out;
450
0
  }
451
452
0
  mainThread.common.Type = HANDLE_TYPE_THREAD;
453
0
  mainThreadId = pthread_self();
454
455
0
  currentThreadTlsIndex = TlsAlloc();
456
0
  if (currentThreadTlsIndex == TLS_OUT_OF_INDEXES)
457
0
  {
458
0
    WLog_ERR(TAG, "Major bug, unable to allocate a TLS value for currentThread");
459
0
  }
460
461
#if defined(WITH_THREAD_LIST)
462
  thread_list = ListDictionary_New(TRUE);
463
464
  if (!thread_list)
465
  {
466
    WLog_ERR(TAG, "Couldn't create global thread list");
467
    goto error_thread_list;
468
  }
469
470
  thread_list->objectKey.fnObjectEquals = thread_compare;
471
#endif
472
473
0
out:
474
0
  return TRUE;
475
0
}
476
477
static BOOL signal_and_wait_for_ready(WINPR_THREAD* thread)
478
0
{
479
0
  BOOL res = FALSE;
480
481
0
  WINPR_ASSERT(thread);
482
483
0
  if (!mux_condition_bundle_lock(&thread->isRunning))
484
0
    return FALSE;
485
486
0
  if (!signal_thread_ready(thread))
487
0
    goto fail;
488
489
0
  if (!mux_condition_bundle_wait(&thread->isRunning, "threadIsRunning"))
490
0
    goto fail;
491
492
#if defined(WITH_THREAD_LIST)
493
  if (!ListDictionary_Contains(thread_list, &thread->thread))
494
  {
495
    WLog_ERR(TAG, "Thread not in thread_list, startup failed!");
496
    goto fail;
497
  }
498
#endif
499
500
0
  res = TRUE;
501
502
0
fail:
503
0
  if (!mux_condition_bundle_unlock(&thread->isRunning))
504
0
    return FALSE;
505
506
0
  return res;
507
0
}
508
509
/* Thread launcher function responsible for registering
510
 * cleanup handlers and calling pthread_exit, if not done
511
 * in thread function. */
512
static void* thread_launcher(void* arg)
513
0
{
514
0
  DWORD rc = 0;
515
0
  WINPR_THREAD* thread = (WINPR_THREAD*)arg;
516
0
  LPTHREAD_START_ROUTINE fkt = NULL;
517
518
0
  if (!thread)
519
0
  {
520
0
    WLog_ERR(TAG, "Called with invalid argument %p", arg);
521
0
    goto exit;
522
0
  }
523
524
0
  if (!TlsSetValue(currentThreadTlsIndex, thread))
525
0
  {
526
0
    WLog_ERR(TAG, "thread %d, unable to set current thread value", pthread_self());
527
0
    goto exit;
528
0
  }
529
530
0
  if (!(fkt = thread->lpStartAddress))
531
0
  {
532
0
    union
533
0
    {
534
0
      LPTHREAD_START_ROUTINE fkt;
535
0
      void* pv;
536
0
    } cnv;
537
0
    cnv.fkt = fkt;
538
0
    WLog_ERR(TAG, "Thread function argument is %p", cnv.pv);
539
0
    goto exit;
540
0
  }
541
542
0
  if (!signal_and_wait_for_ready(thread))
543
0
    goto exit;
544
545
0
  rc = fkt(thread->lpParameter);
546
0
exit:
547
548
0
  if (thread)
549
0
  {
550
0
    apc_cleanupThread(thread);
551
552
0
    if (!thread->exited)
553
0
      thread->dwExitCode = rc;
554
555
0
    set_event(thread);
556
557
0
    (void)signal_thread_ready(thread);
558
559
0
    if (thread->detached || !thread->started)
560
0
      cleanup_handle(thread);
561
0
  }
562
563
0
  return NULL;
564
0
}
565
566
static BOOL winpr_StartThread(WINPR_THREAD* thread)
567
0
{
568
0
  BOOL rc = FALSE;
569
0
  BOOL locked = FALSE;
570
0
  pthread_attr_t attr = { 0 };
571
572
0
  if (!mux_condition_bundle_lock(&thread->isCreated))
573
0
    return FALSE;
574
0
  locked = TRUE;
575
576
0
  pthread_attr_init(&attr);
577
0
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
578
579
0
  if (thread->dwStackSize > 0)
580
0
    pthread_attr_setstacksize(&attr, thread->dwStackSize);
581
582
0
  thread->started = TRUE;
583
0
  reset_event(thread);
584
585
#if defined(WITH_THREAD_LIST)
586
  if (!ListDictionary_Add(thread_list, &thread->thread, thread))
587
  {
588
    WLog_ERR(TAG, "failed to add the thread to the thread list");
589
    goto error;
590
  }
591
#endif
592
593
0
  if (pthread_create(&thread->thread, &attr, thread_launcher, thread))
594
0
    goto error;
595
596
0
  if (!mux_condition_bundle_wait(&thread->isCreated, "threadIsCreated"))
597
0
    goto error;
598
599
0
  locked = FALSE;
600
0
  if (!mux_condition_bundle_unlock(&thread->isCreated))
601
0
    goto error;
602
603
0
  if (!signal_thread_is_running(thread))
604
0
  {
605
0
    WLog_ERR(TAG, "failed to signal the thread was ready");
606
0
    goto error;
607
0
  }
608
609
0
  rc = TRUE;
610
0
error:
611
0
  if (locked)
612
0
  {
613
0
    if (!mux_condition_bundle_unlock(&thread->isCreated))
614
0
      rc = FALSE;
615
0
  }
616
617
0
  pthread_attr_destroy(&attr);
618
619
0
  if (rc)
620
0
    dump_thread(thread);
621
622
0
  return rc;
623
0
}
624
625
BOOL SetThreadPriority(HANDLE hThread, int nPriority)
626
0
{
627
0
  ULONG Type = 0;
628
0
  WINPR_HANDLE* Object = NULL;
629
630
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
631
0
    return FALSE;
632
633
0
  const int min = 19;
634
0
  const int max = 0;
635
0
  const int diff = (max - min);
636
0
  const int normal = min + diff / 2;
637
0
  const int off = MIN(1, diff / 4);
638
0
  int sched_priority = -1;
639
640
0
  switch (nPriority & ~(THREAD_MODE_BACKGROUND_BEGIN | THREAD_MODE_BACKGROUND_END))
641
0
  {
642
0
    case THREAD_PRIORITY_ABOVE_NORMAL:
643
0
      sched_priority = MIN(normal + off, max);
644
0
      break;
645
0
    case THREAD_PRIORITY_BELOW_NORMAL:
646
0
      sched_priority = MAX(normal - off, min);
647
0
      break;
648
0
    case THREAD_PRIORITY_HIGHEST:
649
0
      sched_priority = max;
650
0
      break;
651
0
    case THREAD_PRIORITY_IDLE:
652
0
      sched_priority = min;
653
0
      break;
654
0
    case THREAD_PRIORITY_LOWEST:
655
0
      sched_priority = min;
656
0
      break;
657
0
    case THREAD_PRIORITY_TIME_CRITICAL:
658
0
      sched_priority = max;
659
0
      break;
660
0
    default:
661
0
    case THREAD_PRIORITY_NORMAL:
662
0
      sched_priority = normal;
663
0
      break;
664
0
  }
665
0
#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L) && defined(PTHREAD_SETSCHEDPRIO)
666
0
  WINPR_THREAD* thread = (WINPR_THREAD*)Object;
667
0
  const int rc = pthread_setschedprio(thread->thread, sched_priority);
668
0
  if (rc != 0)
669
0
  {
670
0
    char buffer[256] = { 0 };
671
0
    WLog_ERR(TAG, "pthread_setschedprio(%d) %s [%d]", sched_priority,
672
0
             winpr_strerror(rc, buffer, sizeof(buffer)), rc);
673
0
  }
674
0
  return rc == 0;
675
#else
676
  WLog_WARN(TAG, "pthread_setschedprio(%d) not implemented, requires POSIX 2008 or later",
677
            sched_priority);
678
  return TRUE;
679
#endif
680
0
}
681
682
HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, size_t dwStackSize,
683
                    LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter,
684
                    DWORD dwCreationFlags, WINPR_ATTR_UNUSED LPDWORD lpThreadId)
685
0
{
686
0
  HANDLE handle = NULL;
687
0
  WINPR_THREAD* thread = (WINPR_THREAD*)calloc(1, sizeof(WINPR_THREAD));
688
689
0
  if (!thread)
690
0
    return NULL;
691
692
0
  thread->dwStackSize = dwStackSize;
693
0
  thread->lpParameter = lpParameter;
694
0
  thread->lpStartAddress = lpStartAddress;
695
0
  thread->lpThreadAttributes = lpThreadAttributes;
696
0
  thread->common.ops = &ops;
697
#if defined(WITH_DEBUG_THREADS)
698
  thread->create_stack = winpr_backtrace(20);
699
  dump_thread(thread);
700
#endif
701
702
0
  if (!winpr_event_init(&thread->event))
703
0
  {
704
0
    WLog_ERR(TAG, "failed to create event");
705
0
    goto fail;
706
0
  }
707
708
0
  if (!run_mutex_init(pthread_mutex_init, &thread->mutex, NULL))
709
0
  {
710
0
    WLog_ERR(TAG, "failed to initialize thread mutex");
711
0
    goto fail;
712
0
  }
713
714
0
  if (!apc_init(&thread->apc))
715
0
  {
716
0
    WLog_ERR(TAG, "failed to initialize APC");
717
0
    goto fail;
718
0
  }
719
720
0
  if (!mux_condition_bundle_init(&thread->isCreated))
721
0
    goto fail;
722
0
  if (!mux_condition_bundle_init(&thread->isRunning))
723
0
    goto fail;
724
725
0
  WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ);
726
0
  handle = (HANDLE)thread;
727
728
0
  InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
729
730
0
  if (!(dwCreationFlags & CREATE_SUSPENDED))
731
0
  {
732
0
    if (!winpr_StartThread(thread))
733
0
      goto fail;
734
0
  }
735
0
  else
736
0
  {
737
0
    if (!set_event(thread))
738
0
      goto fail;
739
0
  }
740
741
0
  return handle;
742
0
fail:
743
0
  cleanup_handle(thread);
744
0
  return NULL;
745
0
}
746
747
void cleanup_handle(void* obj)
748
0
{
749
0
  WINPR_THREAD* thread = (WINPR_THREAD*)obj;
750
0
  if (!thread)
751
0
    return;
752
753
0
  if (!apc_uninit(&thread->apc))
754
0
    WLog_ERR(TAG, "failed to destroy APC");
755
756
0
  mux_condition_bundle_uninit(&thread->isCreated);
757
0
  mux_condition_bundle_uninit(&thread->isRunning);
758
0
  run_mutex_fkt(pthread_mutex_destroy, &thread->mutex);
759
760
0
  winpr_event_uninit(&thread->event);
761
762
#if defined(WITH_THREAD_LIST)
763
  ListDictionary_Remove(thread_list, &thread->thread);
764
#endif
765
#if defined(WITH_DEBUG_THREADS)
766
767
  if (thread->create_stack)
768
    winpr_backtrace_free(thread->create_stack);
769
770
  if (thread->exit_stack)
771
    winpr_backtrace_free(thread->exit_stack);
772
773
#endif
774
0
  free(thread);
775
0
}
776
777
BOOL ThreadCloseHandle(HANDLE handle)
778
0
{
779
0
  WINPR_THREAD* thread = (WINPR_THREAD*)handle;
780
781
#if defined(WITH_THREAD_LIST)
782
  if (!thread_list)
783
  {
784
    WLog_ERR(TAG, "Thread list does not exist, check call!");
785
    dump_thread(thread);
786
  }
787
  else if (!ListDictionary_Contains(thread_list, &thread->thread))
788
  {
789
    WLog_ERR(TAG, "Thread list does not contain this thread! check call!");
790
    dump_thread(thread);
791
  }
792
  else
793
  {
794
    ListDictionary_Lock(thread_list);
795
#endif
796
0
    dump_thread(thread);
797
798
0
    if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0))
799
0
    {
800
0
      WLog_DBG(TAG, "Thread running, setting to detached state!");
801
0
      thread->detached = TRUE;
802
0
      pthread_detach(thread->thread);
803
0
    }
804
0
    else
805
0
    {
806
0
      cleanup_handle(thread);
807
0
    }
808
809
#if defined(WITH_THREAD_LIST)
810
    ListDictionary_Unlock(thread_list);
811
  }
812
#endif
813
814
0
  return TRUE;
815
0
}
816
817
HANDLE CreateRemoteThread(WINPR_ATTR_UNUSED HANDLE hProcess,
818
                          WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpThreadAttributes,
819
                          WINPR_ATTR_UNUSED size_t dwStackSize,
820
                          WINPR_ATTR_UNUSED LPTHREAD_START_ROUTINE lpStartAddress,
821
                          WINPR_ATTR_UNUSED LPVOID lpParameter,
822
                          WINPR_ATTR_UNUSED DWORD dwCreationFlags,
823
                          WINPR_ATTR_UNUSED LPDWORD lpThreadId)
824
0
{
825
0
  WLog_ERR(TAG, "not implemented");
826
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
827
0
  return NULL;
828
0
}
829
830
VOID ExitThread(DWORD dwExitCode)
831
0
{
832
#if defined(WITH_THREAD_LIST)
833
  DWORD rc;
834
  pthread_t tid = pthread_self();
835
836
  if (!thread_list)
837
  {
838
    WLog_ERR(TAG, "function called without existing thread list!");
839
#if defined(WITH_DEBUG_THREADS)
840
    DumpThreadHandles();
841
#endif
842
    pthread_exit(0);
843
  }
844
  else if (!ListDictionary_Contains(thread_list, &tid))
845
  {
846
    WLog_ERR(TAG, "function called, but no matching entry in thread list!");
847
#if defined(WITH_DEBUG_THREADS)
848
    DumpThreadHandles();
849
#endif
850
    pthread_exit(0);
851
  }
852
  else
853
  {
854
    WINPR_THREAD* thread;
855
    ListDictionary_Lock(thread_list);
856
    thread = ListDictionary_GetItemValue(thread_list, &tid);
857
    WINPR_ASSERT(thread);
858
    thread->exited = TRUE;
859
    thread->dwExitCode = dwExitCode;
860
#if defined(WITH_DEBUG_THREADS)
861
    thread->exit_stack = winpr_backtrace(20);
862
#endif
863
    ListDictionary_Unlock(thread_list);
864
    set_event(thread);
865
    rc = thread->dwExitCode;
866
867
    if (thread->detached || !thread->started)
868
      cleanup_handle(thread);
869
870
    pthread_exit((void*)(size_t)rc);
871
  }
872
#else
873
0
  WINPR_UNUSED(dwExitCode);
874
0
#endif
875
0
}
876
877
BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)
878
0
{
879
0
  ULONG Type = 0;
880
0
  WINPR_HANDLE* Object = NULL;
881
0
  WINPR_THREAD* thread = NULL;
882
883
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
884
0
  {
885
0
    WLog_ERR(TAG, "hThread is not a thread");
886
0
    SetLastError(ERROR_INVALID_PARAMETER);
887
0
    return FALSE;
888
0
  }
889
890
0
  thread = (WINPR_THREAD*)Object;
891
0
  *lpExitCode = thread->dwExitCode;
892
0
  return TRUE;
893
0
}
894
895
WINPR_THREAD* winpr_GetCurrentThread(VOID)
896
0
{
897
0
  WINPR_THREAD* ret = NULL;
898
899
0
  InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
900
0
  if (mainThreadId == pthread_self())
901
0
    return (HANDLE)&mainThread;
902
903
0
  ret = TlsGetValue(currentThreadTlsIndex);
904
0
  return ret;
905
0
}
906
907
HANDLE _GetCurrentThread(VOID)
908
0
{
909
0
  return (HANDLE)winpr_GetCurrentThread();
910
0
}
911
912
DWORD GetCurrentThreadId(VOID)
913
0
{
914
0
  pthread_t tid = pthread_self();
915
  /* Since pthread_t can be 64-bits on some systems, take just the    */
916
  /* lower 32-bits of it for the thread ID returned by this function. */
917
0
  uintptr_t ptid = WINPR_REINTERPRET_CAST(tid, pthread_t, uintptr_t);
918
0
  return ptid & UINT32_MAX;
919
0
}
920
921
typedef struct
922
{
923
  WINPR_APC_ITEM apc;
924
  PAPCFUNC completion;
925
  ULONG_PTR completionArg;
926
} UserApcItem;
927
928
static void userAPC(LPVOID arg)
929
0
{
930
0
  UserApcItem* userApc = (UserApcItem*)arg;
931
932
0
  userApc->completion(userApc->completionArg);
933
934
0
  userApc->apc.markedForRemove = TRUE;
935
0
}
936
937
DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData)
938
0
{
939
0
  ULONG Type = 0;
940
0
  WINPR_HANDLE* Object = NULL;
941
0
  WINPR_APC_ITEM* apc = NULL;
942
0
  UserApcItem* apcItem = NULL;
943
944
0
  if (!pfnAPC)
945
0
    return 1;
946
947
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
948
0
  {
949
0
    WLog_ERR(TAG, "hThread is not a thread");
950
0
    SetLastError(ERROR_INVALID_PARAMETER);
951
0
    return (DWORD)0;
952
0
  }
953
954
0
  apcItem = calloc(1, sizeof(*apcItem));
955
0
  if (!apcItem)
956
0
  {
957
0
    SetLastError(ERROR_INVALID_PARAMETER);
958
0
    return (DWORD)0;
959
0
  }
960
961
0
  apc = &apcItem->apc;
962
0
  apc->type = APC_TYPE_USER;
963
0
  apc->markedForFree = TRUE;
964
0
  apc->alwaysSignaled = TRUE;
965
0
  apc->completion = userAPC;
966
0
  apc->completionArgs = apc;
967
0
  apcItem->completion = pfnAPC;
968
0
  apcItem->completionArg = dwData;
969
0
  apc_register(hThread, apc);
970
0
  return 1;
971
0
}
972
973
DWORD ResumeThread(HANDLE hThread)
974
0
{
975
0
  ULONG Type = 0;
976
0
  WINPR_HANDLE* Object = NULL;
977
0
  WINPR_THREAD* thread = NULL;
978
979
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
980
0
  {
981
0
    WLog_ERR(TAG, "hThread is not a thread");
982
0
    SetLastError(ERROR_INVALID_PARAMETER);
983
0
    return (DWORD)-1;
984
0
  }
985
986
0
  thread = (WINPR_THREAD*)Object;
987
988
0
  if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
989
0
    return (DWORD)-1;
990
991
0
  if (!thread->started)
992
0
  {
993
0
    if (!winpr_StartThread(thread))
994
0
    {
995
0
      run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex);
996
0
      return (DWORD)-1;
997
0
    }
998
0
  }
999
0
  else
1000
0
    WLog_WARN(TAG, "Thread already started!");
1001
1002
0
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1003
0
    return (DWORD)-1;
1004
1005
0
  return 0;
1006
0
}
1007
1008
DWORD SuspendThread(WINPR_ATTR_UNUSED HANDLE hThread)
1009
0
{
1010
0
  WLog_ERR(TAG, "not implemented");
1011
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1012
0
  return (DWORD)-1;
1013
0
}
1014
1015
BOOL SwitchToThread(VOID)
1016
0
{
1017
  /**
1018
   * Note: on some operating systems sched_yield is a stub returning -1.
1019
   * usleep should at least trigger a context switch if any thread is waiting.
1020
   */
1021
0
  if (sched_yield() != 0)
1022
0
    usleep(1);
1023
1024
0
  return TRUE;
1025
0
}
1026
1027
BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode)
1028
0
{
1029
0
  ULONG Type = 0;
1030
0
  WINPR_HANDLE* Object = NULL;
1031
0
  WINPR_THREAD* thread = NULL;
1032
1033
0
  if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD)
1034
0
    return FALSE;
1035
1036
0
  thread = (WINPR_THREAD*)Object;
1037
0
  thread->exited = TRUE;
1038
0
  thread->dwExitCode = dwExitCode;
1039
1040
0
  if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
1041
0
    return FALSE;
1042
1043
0
#ifndef ANDROID
1044
0
  pthread_cancel(thread->thread);
1045
#else
1046
  WLog_ERR(TAG, "Function not supported on this platform!");
1047
#endif
1048
1049
0
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
1050
0
    return FALSE;
1051
1052
0
  set_event(thread);
1053
0
  return TRUE;
1054
0
}
1055
1056
VOID DumpThreadHandles(void)
1057
0
{
1058
#if defined(WITH_DEBUG_THREADS)
1059
  char** msg = NULL;
1060
  size_t used = 0;
1061
  void* stack = winpr_backtrace(20);
1062
  WLog_DBG(TAG, "---------------- Called from ----------------------------");
1063
  msg = winpr_backtrace_symbols(stack, &used);
1064
1065
  for (size_t i = 0; i < used; i++)
1066
  {
1067
    WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1068
  }
1069
1070
  free(msg);
1071
  winpr_backtrace_free(stack);
1072
  WLog_DBG(TAG, "---------------- Start Dumping thread handles -----------");
1073
1074
#if defined(WITH_THREAD_LIST)
1075
  if (!thread_list)
1076
  {
1077
    WLog_DBG(TAG, "All threads properly shut down and disposed of.");
1078
  }
1079
  else
1080
  {
1081
    ULONG_PTR* keys = NULL;
1082
    ListDictionary_Lock(thread_list);
1083
    int x, count = ListDictionary_GetKeys(thread_list, &keys);
1084
    WLog_DBG(TAG, "Dumping %d elements", count);
1085
1086
    for (size_t x = 0; x < count; x++)
1087
    {
1088
      WINPR_THREAD* thread = ListDictionary_GetItemValue(thread_list, (void*)keys[x]);
1089
      WLog_DBG(TAG, "Thread [%d] handle created still not closed!", x);
1090
      msg = winpr_backtrace_symbols(thread->create_stack, &used);
1091
1092
      for (size_t i = 0; i < used; i++)
1093
      {
1094
        WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1095
      }
1096
1097
      free(msg);
1098
1099
      if (thread->started)
1100
      {
1101
        WLog_DBG(TAG, "Thread [%d] still running!", x);
1102
      }
1103
      else
1104
      {
1105
        WLog_DBG(TAG, "Thread [%d] exited at:", x);
1106
        msg = winpr_backtrace_symbols(thread->exit_stack, &used);
1107
1108
        for (size_t i = 0; i < used; i++)
1109
          WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
1110
1111
        free(msg);
1112
      }
1113
    }
1114
1115
    free(keys);
1116
    ListDictionary_Unlock(thread_list);
1117
  }
1118
#endif
1119
1120
  WLog_DBG(TAG, "---------------- End Dumping thread handles -------------");
1121
#endif
1122
0
}
1123
#endif