Coverage Report

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