Coverage Report

Created: 2024-09-08 06:18

/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
1.38M
{
118
1.38M
  return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_THREAD, FALSE);
119
1.38M
}
120
121
static int ThreadGetFd(HANDLE handle)
122
691k
{
123
691k
  WINPR_THREAD* pThread = (WINPR_THREAD*)handle;
124
125
691k
  if (!ThreadIsHandled(handle))
126
0
    return -1;
127
128
691k
  return pThread->event.fds[0];
129
691k
}
130
131
1.03M
#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
1.03M
{
136
1.03M
  int rc = 0;
137
138
1.03M
  WINPR_ASSERT(fkt);
139
1.03M
  WINPR_ASSERT(mutex);
140
141
1.03M
  rc = fkt(mutex, mutexattr);
142
1.03M
  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
1.03M
  return rc == 0;
148
1.03M
}
149
150
5.88M
#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
5.87M
{
154
5.87M
  int rc = 0;
155
156
5.87M
  WINPR_ASSERT(fkt);
157
5.87M
  WINPR_ASSERT(mutex);
158
159
5.87M
  rc = fkt(mutex);
160
5.87M
  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
5.87M
  return rc == 0;
166
5.87M
}
167
168
691k
#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
691k
{
172
691k
  int rc = 0;
173
174
691k
  WINPR_ASSERT(fkt);
175
691k
  WINPR_ASSERT(condition);
176
177
691k
  rc = fkt(condition, conditionattr);
178
691k
  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
691k
  return rc == 0;
184
691k
}
185
186
1.72M
#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
1.72M
{
190
1.72M
  int rc = 0;
191
192
1.72M
  WINPR_ASSERT(fkt);
193
1.72M
  WINPR_ASSERT(condition);
194
195
1.72M
  rc = fkt(condition);
196
1.72M
  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
1.72M
  return rc == 0;
202
1.72M
}
203
204
static int pthread_mutex_checked_unlock(pthread_mutex_t* mutex)
205
2.41M
{
206
2.41M
  WINPR_ASSERT(mutex);
207
2.41M
  WINPR_ASSERT(pthread_mutex_trylock(mutex) == EBUSY);
208
2.42M
  return pthread_mutex_unlock(mutex);
209
2.41M
}
210
211
static BOOL mux_condition_bundle_init(mux_condition_bundle* bundle)
212
691k
{
213
691k
  WINPR_ASSERT(bundle);
214
215
691k
  bundle->val = FALSE;
216
691k
  if (!run_mutex_init(pthread_mutex_init, &bundle->mux, NULL))
217
0
    return FALSE;
218
219
691k
  if (!run_cond_init(pthread_cond_init, &bundle->cond, NULL))
220
0
    return FALSE;
221
691k
  return TRUE;
222
691k
}
223
224
static void mux_condition_bundle_uninit(mux_condition_bundle* bundle)
225
691k
{
226
691k
  mux_condition_bundle empty = { 0 };
227
228
691k
  WINPR_ASSERT(bundle);
229
230
691k
  run_cond_fkt(pthread_cond_destroy, &bundle->cond);
231
691k
  run_mutex_fkt(pthread_mutex_destroy, &bundle->mux);
232
691k
  *bundle = empty;
233
691k
}
234
235
static BOOL mux_condition_bundle_signal(mux_condition_bundle* bundle)
236
1.03M
{
237
1.03M
  BOOL rc = TRUE;
238
1.03M
  WINPR_ASSERT(bundle);
239
240
1.03M
  if (!run_mutex_fkt(pthread_mutex_lock, &bundle->mux))
241
0
    return FALSE;
242
1.03M
  bundle->val = TRUE;
243
1.03M
  if (!run_cond_fkt(pthread_cond_signal, &bundle->cond))
244
0
    rc = FALSE;
245
1.03M
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux))
246
0
    rc = FALSE;
247
1.03M
  return rc;
248
1.03M
}
249
250
static BOOL mux_condition_bundle_lock(mux_condition_bundle* bundle)
251
691k
{
252
691k
  WINPR_ASSERT(bundle);
253
691k
  return run_mutex_fkt(pthread_mutex_lock, &bundle->mux);
254
691k
}
255
256
static BOOL mux_condition_bundle_unlock(mux_condition_bundle* bundle)
257
691k
{
258
691k
  WINPR_ASSERT(bundle);
259
691k
  return run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux);
260
691k
}
261
262
static BOOL mux_condition_bundle_wait(mux_condition_bundle* bundle, const char* name)
263
691k
{
264
691k
  BOOL rc = FALSE;
265
266
691k
  WINPR_ASSERT(bundle);
267
691k
  WINPR_ASSERT(name);
268
691k
  WINPR_ASSERT(pthread_mutex_trylock(&bundle->mux) == EBUSY);
269
270
1.38M
  while (!bundle->val)
271
691k
  {
272
691k
    int r = pthread_cond_wait(&bundle->cond, &bundle->mux);
273
691k
    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
691k
  }
291
292
691k
  rc = bundle->val;
293
294
691k
fail:
295
691k
  return rc;
296
691k
}
297
298
static BOOL signal_thread_ready(WINPR_THREAD* thread)
299
691k
{
300
691k
  WINPR_ASSERT(thread);
301
302
691k
  return mux_condition_bundle_signal(&thread->isCreated);
303
691k
}
304
305
static BOOL signal_thread_is_running(WINPR_THREAD* thread)
306
345k
{
307
345k
  WINPR_ASSERT(thread);
308
309
345k
  return mux_condition_bundle_signal(&thread->isRunning);
310
345k
}
311
312
static DWORD ThreadCleanupHandle(HANDLE handle)
313
691k
{
314
691k
  DWORD status = WAIT_FAILED;
315
691k
  WINPR_THREAD* thread = (WINPR_THREAD*)handle;
316
317
691k
  if (!ThreadIsHandled(handle))
318
0
    return WAIT_FAILED;
319
320
691k
  if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex))
321
0
    return WAIT_FAILED;
322
323
691k
  if (!thread->joined)
324
345k
  {
325
345k
    int rc = pthread_join(thread->thread, NULL);
326
327
345k
    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
345k
    else
335
345k
      thread->joined = TRUE;
336
345k
  }
337
338
691k
  status = WAIT_OBJECT_0;
339
340
691k
fail:
341
691k
  if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex))
342
0
    return WAIT_FAILED;
343
344
691k
  return status;
345
691k
}
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
691k
{
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
691k
  WINPR_UNUSED(thread);
411
691k
#endif
412
691k
}
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
340k
{
420
340k
  return winpr_event_set(&thread->event);
421
340k
}
422
423
static BOOL reset_event(WINPR_THREAD* thread)
424
345k
{
425
345k
  return winpr_event_reset(&thread->event);
426
345k
}
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
1
{
444
1
  if (!apc_init(&mainThread.apc))
445
0
  {
446
0
    WLog_ERR(TAG, "failed to initialize APC");
447
0
    goto out;
448
0
  }
449
450
1
  mainThread.common.Type = HANDLE_TYPE_THREAD;
451
1
  mainThreadId = pthread_self();
452
453
1
  currentThreadTlsIndex = TlsAlloc();
454
1
  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
1
out:
472
1
  return TRUE;
473
1
}
474
475
static BOOL signal_and_wait_for_ready(WINPR_THREAD* thread)
476
345k
{
477
345k
  BOOL res = FALSE;
478
479
345k
  WINPR_ASSERT(thread);
480
481
345k
  if (!mux_condition_bundle_lock(&thread->isRunning))
482
0
    return FALSE;
483
484
345k
  if (!signal_thread_ready(thread))
485
0
    goto fail;
486
487
345k
  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
345k
  res = TRUE;
499
500
345k
fail:
501
345k
  if (!mux_condition_bundle_unlock(&thread->isRunning))
502
0
    return FALSE;
503
504
345k
  return res;
505
345k
}
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
345k
{
512
345k
  DWORD rc = 0;
513
345k
  WINPR_THREAD* thread = (WINPR_THREAD*)arg;
514
345k
  LPTHREAD_START_ROUTINE fkt = NULL;
515
516
345k
  if (!thread)
517
0
  {
518
0
    WLog_ERR(TAG, "Called with invalid argument %p", arg);
519
0
    goto exit;
520
0
  }
521
522
345k
  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
345k
  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
345k
  if (!signal_and_wait_for_ready(thread))
541
0
    goto exit;
542
543
345k
  rc = fkt(thread->lpParameter);
544
345k
exit:
545
546
344k
  if (thread)
547
344k
  {
548
344k
    apc_cleanupThread(thread);
549
550
344k
    if (!thread->exited)
551
345k
      thread->dwExitCode = rc;
552
553
344k
    set_event(thread);
554
555
344k
    signal_thread_ready(thread);
556
557
345k
    if (thread->detached || !thread->started)
558
0
      cleanup_handle(thread);
559
344k
  }
560
561
344k
  return NULL;
562
345k
}
563
564
static BOOL winpr_StartThread(WINPR_THREAD* thread)
565
345k
{
566
345k
  BOOL rc = FALSE;
567
345k
  BOOL locked = FALSE;
568
345k
  pthread_attr_t attr = { 0 };
569
570
345k
  if (!mux_condition_bundle_lock(&thread->isCreated))
571
0
    return FALSE;
572
345k
  locked = TRUE;
573
574
345k
  pthread_attr_init(&attr);
575
345k
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
576
577
345k
  if (thread->dwStackSize > 0)
578
0
    pthread_attr_setstacksize(&attr, (size_t)thread->dwStackSize);
579
580
345k
  thread->started = TRUE;
581
345k
  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
345k
  if (pthread_create(&thread->thread, &attr, thread_launcher, thread))
592
0
    goto error;
593
594
345k
  if (!mux_condition_bundle_wait(&thread->isCreated, "threadIsCreated"))
595
0
    goto error;
596
597
345k
  locked = FALSE;
598
345k
  if (!mux_condition_bundle_unlock(&thread->isCreated))
599
0
    goto error;
600
601
345k
  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
345k
  rc = TRUE;
608
345k
error:
609
345k
  if (locked)
610
0
  {
611
0
    if (!mux_condition_bundle_unlock(&thread->isCreated))
612
0
      rc = FALSE;
613
0
  }
614
615
345k
  pthread_attr_destroy(&attr);
616
617
345k
  if (rc)
618
345k
    dump_thread(thread);
619
620
345k
  return rc;
621
345k
}
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
345k
{
681
345k
  HANDLE handle = NULL;
682
345k
  WINPR_THREAD* thread = (WINPR_THREAD*)calloc(1, sizeof(WINPR_THREAD));
683
684
345k
  if (!thread)
685
0
    return NULL;
686
687
345k
  thread->dwStackSize = dwStackSize;
688
345k
  thread->lpParameter = lpParameter;
689
345k
  thread->lpStartAddress = lpStartAddress;
690
345k
  thread->lpThreadAttributes = lpThreadAttributes;
691
345k
  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
345k
  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
345k
  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
345k
  if (!apc_init(&thread->apc))
710
0
  {
711
0
    WLog_ERR(TAG, "failed to initialize APC");
712
0
    goto fail;
713
0
  }
714
715
345k
  if (!mux_condition_bundle_init(&thread->isCreated))
716
0
    goto fail;
717
345k
  if (!mux_condition_bundle_init(&thread->isRunning))
718
0
    goto fail;
719
720
345k
  WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ);
721
345k
  handle = (HANDLE)thread;
722
723
345k
  InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL);
724
725
345k
  if (!(dwCreationFlags & CREATE_SUSPENDED))
726
345k
  {
727
345k
    if (!winpr_StartThread(thread))
728
0
      goto fail;
729
345k
  }
730
0
  else
731
0
  {
732
0
    if (!set_event(thread))
733
0
      goto fail;
734
0
  }
735
736
345k
  return handle;
737
0
fail:
738
0
  cleanup_handle(thread);
739
0
  return NULL;
740
345k
}
741
742
void cleanup_handle(void* obj)
743
345k
{
744
345k
  WINPR_THREAD* thread = (WINPR_THREAD*)obj;
745
345k
  if (!thread)
746
0
    return;
747
748
345k
  if (!apc_uninit(&thread->apc))
749
345k
    WLog_ERR(TAG, "failed to destroy APC");
750
751
345k
  mux_condition_bundle_uninit(&thread->isCreated);
752
345k
  mux_condition_bundle_uninit(&thread->isRunning);
753
345k
  run_mutex_fkt(pthread_mutex_destroy, &thread->mutex);
754
755
345k
  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
345k
  free(thread);
770
345k
}
771
772
BOOL ThreadCloseHandle(HANDLE handle)
773
345k
{
774
345k
  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
345k
    dump_thread(thread);
792
793
345k
    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
345k
    else
800
345k
    {
801
345k
      cleanup_handle(thread);
802
345k
    }
803
804
#if defined(WITH_THREAD_LIST)
805
    ListDictionary_Unlock(thread_list);
806
  }
807
#endif
808
809
345k
  return TRUE;
810
345k
}
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
344k
{
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
344k
  WINPR_UNUSED(dwExitCode);
865
344k
#endif
866
344k
}
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
6.31M
{
905
6.31M
  pthread_t tid = 0;
906
6.31M
  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
6.31M
  return (DWORD)tid & 0xffffffffUL;
910
6.31M
}
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