Coverage Report

Created: 2024-09-08 06:16

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