• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bdwgc / bdwgc / 2146

25 May 2026 07:08PM UTC coverage: 80.526% (-0.02%) from 80.544%
2146

push

travis-ci

ivmai
Update AUTHORS file (add Kyle Cesare)

7224 of 8971 relevant lines covered (80.53%)

18671099.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.84
/pthread_support.c
1
/*
2
 * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
3
 * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
4
 * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
5
 * Copyright (c) 2000-2008 by Hewlett-Packard Company.  All rights reserved.
6
 * Copyright (c) 2008-2025 Ivan Maidanski
7
 *
8
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
9
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
10
 *
11
 * Permission is hereby granted to use or copy this program
12
 * for any purpose, provided the above notices are retained on all copies.
13
 * Permission to modify the code and to distribute modified code is granted,
14
 * provided the above notices are retained, and a notice that the code was
15
 * modified is included with the above copyright notice.
16
 */
17

18
#include "private/pthread_support.h"
19

20
/*
21
 * Support code originally for LinuxThreads, the `clone`-based kernel
22
 * thread package for Linux which is included in `libc6`.
23
 *
24
 * This code no doubt makes some assumptions beyond what is guaranteed by
25
 * the `pthreads` standard, though it now does very little of that.
26
 * It now also supports NPTL, and many other POSIX thread implementations.
27
 * We are trying to merge all flavors of `pthreads` support code into this
28
 * file.
29
 */
30

31
#ifdef THREADS
32

33
#  ifdef GC_PTHREADS
34
#    if defined(DARWIN) \
35
        || (defined(GC_WIN32_THREADS) && defined(EMULATE_PTHREAD_SEMAPHORE))
36
#      include "private/darwin_semaphore.h"
37
#    elif !defined(PLATFORM_THREADS) && !defined(SN_TARGET_PSP2)
38
#      include <semaphore.h>
39
#    endif
40
#    include <errno.h>
41
#  endif /* GC_PTHREADS */
42

43
#  if !defined(GC_WIN32_THREADS)
44
#    include <sched.h>
45
#    include <time.h>
46
#    if !defined(PLATFORM_THREADS) && !defined(SN_TARGET_PSP2)
47
#      ifndef RTEMS
48
#        include <sys/mman.h>
49
#      endif
50
#      include <fcntl.h>
51
#      include <sys/stat.h>
52
#      include <sys/time.h>
53
#    endif
54
#    if defined(GC_EXPLICIT_SIGNALS_UNBLOCK) \
55
        || !defined(GC_NO_PTHREAD_SIGMASK)   \
56
        || (defined(GC_PTHREADS_PARAMARK)    \
57
            && !defined(NO_MARKER_SPECIAL_SIGMASK))
58
#      include <signal.h>
59
#    endif
60
#  endif /* !GC_WIN32_THREADS */
61

62
#  ifdef E2K
63
#    include <alloca.h>
64
#  endif
65

66
#  if defined(DARWIN) || defined(ANY_BSD)
67
#    if defined(NETBSD) || defined(OPENBSD)
68
#      include <sys/param.h>
69
#    endif
70
#    include <sys/sysctl.h>
71
#  elif defined(DGUX)
72
#    include <sys/_int_psem.h>
73
#    include <sys/dg_sys_info.h>
74
/* Note: `sem_t` is `uint` in DG/UX. */
75
typedef unsigned int sem_t;
76
#  endif
77

78
#  if defined(GC_PTHREADS) && !defined(PLATFORM_THREADS) \
79
      && !defined(SN_TARGET_PSP2)
80
/* Undefine macros used to redirect `pthreads` primitives. */
81
#    undef pthread_create
82
#    ifndef GC_NO_PTHREAD_SIGMASK
83
#      undef pthread_sigmask
84
#    endif
85
#    ifndef GC_NO_PTHREAD_CANCEL
86
#      undef pthread_cancel
87
#    endif
88
#    ifdef GC_HAVE_PTHREAD_EXIT
89
#      undef pthread_exit
90
#    endif
91
#    undef pthread_join
92
#    undef pthread_detach
93
#    if defined(OSF1) && defined(_PTHREAD_USE_MANGLED_NAMES_) \
94
        && !defined(_PTHREAD_USE_PTDNAM_)
95
/* Restore the original mangled names on Tru64 UNIX. */
96
#      define pthread_create __pthread_create
97
#      define pthread_join __pthread_join
98
#      define pthread_detach __pthread_detach
99
#      ifndef GC_NO_PTHREAD_CANCEL
100
#        define pthread_cancel __pthread_cancel
101
#      endif
102
#      ifdef GC_HAVE_PTHREAD_EXIT
103
#        define pthread_exit __pthread_exit
104
#      endif
105
#    endif
106
#  endif /* GC_PTHREADS */
107

108
#  if !defined(GC_WIN32_THREADS) && !defined(PLATFORM_THREADS) \
109
      && !defined(SN_TARGET_PSP2)
110
/* TODO: Enable `GC_USE_DLOPEN_WRAP` for Cygwin? */
111

112
#    ifdef GC_USE_LD_WRAP
113
#      define WRAP_FUNC(f) __wrap_##f
114
#      define REAL_FUNC(f) __real_##f
115
int REAL_FUNC(pthread_create)(pthread_t *,
116
                              GC_PTHREAD_CREATE_CONST pthread_attr_t *,
117
                              void *(*start_routine)(void *), void *);
118
int REAL_FUNC(pthread_join)(pthread_t, void **);
119
int REAL_FUNC(pthread_detach)(pthread_t);
120
#      ifndef GC_NO_PTHREAD_SIGMASK
121
int REAL_FUNC(pthread_sigmask)(int, const sigset_t *, sigset_t *);
122
#      endif
123
#      ifndef GC_NO_PTHREAD_CANCEL
124
int REAL_FUNC(pthread_cancel)(pthread_t);
125
#      endif
126
#      ifdef GC_HAVE_PTHREAD_EXIT
127
void REAL_FUNC(pthread_exit)(void *) GC_PTHREAD_EXIT_ATTRIBUTE;
128
#      endif
129
#    elif defined(GC_USE_DLOPEN_WRAP)
130
#      include <dlfcn.h>
131
#      define WRAP_FUNC(f) f
132
#      define REAL_FUNC(f) GC_real_##f
133
/*
134
 * We define both `GC_<fn>` and plain `fn` to be the wrapped function.
135
 * In that way plain calls work, as do calls from files that include
136
 * `gc.h` file which redefines `fn` to `GC_<fn>`.
137
 */
138
/* FIXME: Needs work for `DARWIN` and True64 (`OSF1`). */
139
typedef int (*GC_pthread_create_t)(pthread_t *,
140
                                   GC_PTHREAD_CREATE_CONST pthread_attr_t *,
141
                                   void *(*)(void *), void *);
142
static GC_pthread_create_t REAL_FUNC(pthread_create);
143
#      ifndef GC_NO_PTHREAD_SIGMASK
144
typedef int (*GC_pthread_sigmask_t)(int, const sigset_t *, sigset_t *);
145
static GC_pthread_sigmask_t REAL_FUNC(pthread_sigmask);
146
#      endif
147
typedef int (*GC_pthread_join_t)(pthread_t, void **);
148
static GC_pthread_join_t REAL_FUNC(pthread_join);
149
typedef int (*GC_pthread_detach_t)(pthread_t);
150
static GC_pthread_detach_t REAL_FUNC(pthread_detach);
151
#      ifndef GC_NO_PTHREAD_CANCEL
152
typedef int (*GC_pthread_cancel_t)(pthread_t);
153
static GC_pthread_cancel_t REAL_FUNC(pthread_cancel);
154
#      endif
155
#      ifdef GC_HAVE_PTHREAD_EXIT
156
typedef void (*GC_pthread_exit_t)(void *) GC_PTHREAD_EXIT_ATTRIBUTE;
157
static GC_pthread_exit_t REAL_FUNC(pthread_exit);
158
#      endif
159
#    else
160
#      define WRAP_FUNC(f) GC_##f
161
#      ifdef DGUX
162
#        define REAL_FUNC(f) __d10_##f
163
#      else
164
#        define REAL_FUNC(f) f
165
#      endif
166
#    endif /* !GC_USE_LD_WRAP && !GC_USE_DLOPEN_WRAP */
167

168
/*
169
 * Define `GC_` functions as aliases for the plain ones, which will
170
 * be intercepted.  This allows files that include `gc.h` file, and
171
 * hence generate references to the `GC_` symbols, to see the right ones.
172
 */
173
#    if defined(GC_USE_LD_WRAP) || defined(GC_USE_DLOPEN_WRAP)
174

175
GC_API int
176
GC_pthread_create(pthread_t *t, GC_PTHREAD_CREATE_CONST pthread_attr_t *a,
177
                  void *(*fn)(void *), void *arg)
178
{
179
  return pthread_create(t, a, fn, arg);
180
}
181

182
#      ifndef GC_NO_PTHREAD_SIGMASK
183
GC_API int
184
GC_pthread_sigmask(int how, const sigset_t *mask, sigset_t *old)
185
{
186
  return pthread_sigmask(how, mask, old);
187
}
188
#      endif /* !GC_NO_PTHREAD_SIGMASK */
189

190
GC_API int
191
GC_pthread_join(pthread_t t, void **res)
192
{
193
  return pthread_join(t, res);
194
}
195

196
GC_API int
197
GC_pthread_detach(pthread_t t)
198
{
199
  return pthread_detach(t);
200
}
201

202
#      ifndef GC_NO_PTHREAD_CANCEL
203
GC_API int
204
GC_pthread_cancel(pthread_t t)
205
{
206
  return pthread_cancel(t);
207
}
208
#      endif /* !GC_NO_PTHREAD_CANCEL */
209

210
#      ifdef GC_HAVE_PTHREAD_EXIT
211
GC_API GC_PTHREAD_EXIT_ATTRIBUTE void
212
GC_pthread_exit(void *retval)
213
{
214
  pthread_exit(retval);
215
}
216
#      endif
217
#    endif /* GC_USE_LD_WRAP || GC_USE_DLOPEN_WRAP */
218

219
#    ifdef GC_USE_DLOPEN_WRAP
220
/*
221
 * Resolve a symbol with name `n` from the dynamic library (given by
222
 * handle `h`) and cast it to the given functional type `fn`.
223
 */
224
#      define TYPED_DLSYM(fn, h, n) CAST_THRU_UINTPTR(fn, dlsym(h, n))
225

226
STATIC void
227
GC_init_real_syms(void)
228
{
229
  void *dl_handle;
230

231
  GC_ASSERT(!GC_syms_wrap_initialized);
232
#      ifdef RTLD_NEXT
233
  dl_handle = RTLD_NEXT;
234
#      else
235
  dl_handle = dlopen("libpthread.so.0", RTLD_LAZY);
236
  if (NULL == dl_handle) {
237
    /* Retry without ".0" suffix. */
238
    dl_handle = dlopen("libpthread.so", RTLD_LAZY);
239
    if (NULL == dl_handle)
240
      ABORT("Couldn't open libpthread");
241
  }
242
#      endif
243
  REAL_FUNC(pthread_create)
244
      = TYPED_DLSYM(GC_pthread_create_t, dl_handle, "pthread_create");
245
#      ifdef RTLD_NEXT
246
  if (REAL_FUNC(pthread_create) == 0)
247
    ABORT("pthread_create not found"
248
          " (probably -lgc is specified after -lpthread)");
249
#      endif
250
#      ifndef GC_NO_PTHREAD_SIGMASK
251
  REAL_FUNC(pthread_sigmask)
252
      = TYPED_DLSYM(GC_pthread_sigmask_t, dl_handle, "pthread_sigmask");
253
#      endif
254
  REAL_FUNC(pthread_join)
255
      = TYPED_DLSYM(GC_pthread_join_t, dl_handle, "pthread_join");
256
  REAL_FUNC(pthread_detach)
257
      = TYPED_DLSYM(GC_pthread_detach_t, dl_handle, "pthread_detach");
258
#      ifndef GC_NO_PTHREAD_CANCEL
259
  REAL_FUNC(pthread_cancel)
260
      = TYPED_DLSYM(GC_pthread_cancel_t, dl_handle, "pthread_cancel");
261
#      endif
262
#      ifdef GC_HAVE_PTHREAD_EXIT
263
  REAL_FUNC(pthread_exit)
264
      = TYPED_DLSYM(GC_pthread_exit_t, dl_handle, "pthread_exit");
265
#      endif
266
  GC_syms_wrap_initialized = TRUE;
267
}
268

269
#      define INIT_REAL_SYMS()                  \
270
        if (LIKELY(GC_syms_wrap_initialized)) { \
271
        } else                                  \
272
          GC_init_real_syms()
273
#    else
274
#      define INIT_REAL_SYMS() (void)0
275
#    endif /* !GC_USE_DLOPEN_WRAP */
276

277
#  else
278
#    define WRAP_FUNC(f) GC_##f
279
#    define REAL_FUNC(f) f
280
#    define INIT_REAL_SYMS() (void)0
281
#  endif /* GC_WIN32_THREADS */
282

283
#  if defined(MPROTECT_VDB) && defined(DARWIN)
284
GC_INNER int
285
GC_inner_pthread_create(pthread_t *t,
286
                        GC_PTHREAD_CREATE_CONST pthread_attr_t *a,
287
                        void *(*fn)(void *), void *arg)
288
{
289
  INIT_REAL_SYMS();
290
  return REAL_FUNC(pthread_create)(t, a, fn, arg);
291
}
292
#  endif
293

294
#  ifdef THREAD_LOCAL_ALLOC
295

296
GC_INNER void
297
GC_mark_thread_local_free_lists(void)
31,043✔
298
{
299
  int i;
300
  GC_thread p;
301

302
  for (i = 0; i < THREAD_TABLE_SZ; ++i) {
7,978,051✔
303
    for (p = GC_threads[i]; p != NULL; p = p->tm.next) {
8,224,423✔
304
      if (!KNOWN_FINISHED(p))
277,415✔
305
        GC_mark_thread_local_fls_for(&p->tlfs);
258,090✔
306
    }
307
  }
308
}
31,043✔
309

310
#    if defined(GC_ASSERTIONS)
311
/*
312
 * Check that all thread-local free-lists are completely marked.
313
 * Also check that thread-specific-data structures are marked.
314
 */
315
void
316
GC_check_tls(void)
31,043✔
317
{
318
  int i;
319
  GC_thread p;
320

321
  for (i = 0; i < THREAD_TABLE_SZ; ++i) {
7,978,051✔
322
    for (p = GC_threads[i]; p != NULL; p = p->tm.next) {
8,224,423✔
323
      if (!KNOWN_FINISHED(p))
277,415✔
324
        GC_check_tls_for(&p->tlfs);
258,090✔
325
    }
326
  }
327
#      if defined(USE_CUSTOM_SPECIFIC)
328
  if (GC_thread_key != 0)
31,043✔
329
    GC_check_tsd_marks(GC_thread_key);
31,004✔
330
#      endif
331
}
31,043✔
332
#    endif
333

334
#  endif /* THREAD_LOCAL_ALLOC */
335

336
#  ifdef GC_WIN32_THREADS
337
/*
338
 * A macro for functions and variables that should be accessible
339
 * from `win32_threads.c` file but otherwise could be `static`.
340
 */
341
#    define GC_INNER_WIN32THREAD GC_INNER
342
#  else
343
#    define GC_INNER_WIN32THREAD STATIC
344
#  endif
345

346
#  ifdef PARALLEL_MARK
347

348
#    if defined(GC_WIN32_THREADS)                              \
349
        || (defined(USE_PROC_FOR_LIBRARIES) && defined(LINUX)) \
350
        || (defined(IA64)                                      \
351
            && (defined(HAVE_PTHREAD_ATTR_GET_NP)              \
352
                || defined(HAVE_PTHREAD_GETATTR_NP)))
353
GC_INNER_WIN32THREAD ptr_t GC_marker_sp[MAX_MARKERS - 1] = { NULL };
354
#    endif
355

356
#    if defined(IA64) && defined(USE_PROC_FOR_LIBRARIES)
357
static ptr_t marker_bsp[MAX_MARKERS - 1] = { NULL };
358
#    endif
359

360
#    if defined(DARWIN) && !defined(GC_NO_THREADS_DISCOVERY)
361
static mach_port_t marker_mach_threads[MAX_MARKERS - 1] = { 0 };
362

363
GC_INNER GC_bool
364
GC_is_mach_marker(thread_act_t thread)
365
{
366
  int i;
367
  for (i = 0; i < GC_markers_m1; i++) {
368
    if (marker_mach_threads[i] == thread)
369
      return TRUE;
370
  }
371
  return FALSE;
372
}
373
#    endif /* DARWIN && !GC_NO_THREADS_DISCOVERY */
374

375
#    ifdef HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG
376
/* For NetBSD. */
377
static void
378
set_marker_thread_name(unsigned id)
379
{
380
  int err = pthread_setname_np(pthread_self(), "GC-marker-%zu",
381
                               NUMERIC_TO_VPTR(id));
382
  if (UNLIKELY(err != 0))
383
    WARN("pthread_setname_np failed, errno= %" WARN_PRIdPTR "\n",
384
         (GC_signed_word)err);
385
}
386

387
#    elif defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID)     \
388
        || defined(HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID) \
389
        || defined(HAVE_PTHREAD_SET_NAME_NP)
390
#      ifdef HAVE_PTHREAD_SET_NAME_NP
391
#        include <pthread_np.h>
392
#      endif
393
static void
394
set_marker_thread_name(unsigned id)
78✔
395
{
396
  /*
397
   * Note: a smaller size of the buffer may result in
398
   * "output may be truncated" compiler warning.
399
   */
400
  char name_buf[10 + 20 + 1];
401

402
  GC_snprintf_s_ld_s(name_buf, sizeof(name_buf), "GC-marker-", (long)id, "");
78✔
403
#      ifdef HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID
404
  /* The iOS or OS X case. */
405
  (void)pthread_setname_np(name_buf);
406
#      elif defined(HAVE_PTHREAD_SET_NAME_NP)
407
  /* The OpenBSD case. */
408
  pthread_set_name_np(pthread_self(), name_buf);
409
#      else
410
  /* The case of Linux, Solaris, etc. */
411
  GC_ASSERT(strlen(name_buf) < 16);
78✔
412
  /* `pthread_setname_np()` may fail for longer names. */
413
  if (UNLIKELY(pthread_setname_np(pthread_self(), name_buf) != 0))
78✔
414
    WARN("pthread_setname_np failed\n", 0);
×
415
#      endif
416
}
78✔
417

418
#    elif defined(GC_WIN32_THREADS) && !defined(MSWINCE)
419
/*
420
 * A pointer to `SetThreadDescription()` which is available since Windows 10.
421
 * The function prototype is in the platform `processthreadsapi.h` file.
422
 */
423
static FARPROC setThreadDescription_fn;
424

425
GC_INNER void
426
GC_init_win32_thread_naming(HMODULE hK32)
427
{
428
  if (hK32)
429
    setThreadDescription_fn = GetProcAddress(hK32, "SetThreadDescription");
430
}
431

432
static void
433
set_marker_thread_name(unsigned id)
434
{
435
  WCHAR name_buf[16];
436
  int len = sizeof(L"GC-marker-") / sizeof(WCHAR) - 1;
437
  HRESULT hr;
438

439
  if (!setThreadDescription_fn) {
440
    /* `SetThreadDescription()` is missing. */
441
    return;
442
  }
443

444
  /* Compose the name manually as `swprintf` may be unavailable. */
445
  BCOPY(L"GC-marker-", name_buf, len * sizeof(WCHAR));
446
  if (id >= 10)
447
    name_buf[len++] = (WCHAR)('0' + (id / 10) % 10);
448
  name_buf[len] = (WCHAR)('0' + id % 10);
449
  name_buf[len + 1] = 0;
450

451
  /*
452
   * Invoke `SetThreadDescription()`.  Cast the function pointer to
453
   * `GC_funcptr_uint` first to avoid "incompatible function types"
454
   * compiler warning.
455
   */
456
  hr = (*(HRESULT(WINAPI *)(HANDLE, const WCHAR *))(
457
      GC_funcptr_uint)setThreadDescription_fn)(GetCurrentThread(), name_buf);
458
  if (hr < 0)
459
    WARN("SetThreadDescription failed\n", 0);
460
}
461
#    else
462
#      define set_marker_thread_name(id) (void)(id)
463
#    endif
464

465
GC_INNER_WIN32THREAD
466
#    ifdef GC_PTHREADS_PARAMARK
467
void *
468
GC_mark_thread(void *id)
78✔
469
#    elif defined(MSWINCE)
470
DWORD WINAPI
471
GC_mark_thread(LPVOID id)
472
#    else
473
unsigned __stdcall GC_mark_thread(void *id)
474
#    endif
475
{
476
  word my_mark_no = 0;
78✔
477
  word id_n = (word)(GC_uintptr_t)id;
78✔
478
  IF_CANCEL(int cancel_state;)
479

480
#    ifdef CPPCHECK
481
  GC_noop1_ptr(id);
482
#    endif
483
  if (id_n == GC_WORD_MAX)
78✔
484
    return 0; /*< to prevent a compiler warning */
×
485

486
  /*
487
   * Mark threads are not cancellable; they should be invisible to
488
   * client.
489
   */
490
  DISABLE_CANCEL(cancel_state);
78✔
491

492
  set_marker_thread_name((unsigned)id_n);
78✔
493
#    if defined(GC_WIN32_THREADS)                              \
494
        || (defined(USE_PROC_FOR_LIBRARIES) && defined(LINUX)) \
495
        || (defined(IA64)                                      \
496
            && (defined(HAVE_PTHREAD_ATTR_GET_NP)              \
497
                || defined(HAVE_PTHREAD_GETATTR_NP)))
498
  GC_marker_sp[id_n] = GC_approx_sp();
499
#    endif
500
#    if defined(IA64) && defined(USE_PROC_FOR_LIBRARIES)
501
  marker_bsp[id_n] = GC_save_regs_in_stack();
502
#    endif
503
#    if defined(DARWIN) && !defined(GC_NO_THREADS_DISCOVERY)
504
  marker_mach_threads[id_n] = mach_thread_self();
505
#    endif
506
#    if !defined(GC_PTHREADS_PARAMARK)
507
  GC_marker_Id[id_n] = thread_id_self();
508
#    endif
509

510
  /* Inform `GC_start_mark_threads` about completion of marker data init. */
511
  GC_acquire_mark_lock();
78✔
512
  /* Note: the count variable may have a negative value. */
513
  if (0 == --GC_fl_builder_count)
78✔
514
    GC_notify_all_builder();
48✔
515

516
  /*
517
   * `GC_mark_no` is passed only to allow `GC_help_marker` to
518
   * terminate promptly.  This is important if it were called from the
519
   * signal handler or from the allocator lock acquisition code.
520
   * On Linux, it is not safe to call it from a signal handler, since
521
   * it uses mutex and condition variables.  Since it is called only
522
   * here, the argument is unnecessary.
523
   */
524
  for (;; ++my_mark_no) {
2,036✔
525
    if (my_mark_no - GC_mark_no > (word)2) {
2,114✔
526
      /* Resynchronize if we get far off, e.g. because `GC_mark_no` wrapped. */
527
      my_mark_no = GC_mark_no;
64✔
528
    }
529
#    ifdef DEBUG_THREADS
530
    GC_log_printf("Starting helper for mark number %lu (thread %u)\n",
531
                  (unsigned long)my_mark_no, (unsigned)id_n);
532
#    endif
533
    GC_help_marker(my_mark_no);
2,114✔
534
  }
535
}
536

537
GC_INNER_WIN32THREAD int GC_available_markers_m1 = 0;
538

539
#  endif /* PARALLEL_MARK */
540

541
#  ifdef GC_PTHREADS_PARAMARK
542

543
#    ifdef GLIBC_2_1_MUTEX_HACK
544
/*
545
 * Ugly workaround for a Linux threads bug in the final versions
546
 * of `glibc` 2.1.  `pthread_mutex_trylock` sets the mutex owner
547
 * field even when it fails to acquire the mutex.  This causes
548
 * `pthread_cond_wait` to die.  Should not be needed for `glibc` 2.2.
549
 * According to the man page, we should use
550
 * `PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP`, but that is not actually
551
 * defined.
552
 */
553
static pthread_mutex_t mark_mutex
554
    = { 0, 0, 0, PTHREAD_MUTEX_ERRORCHECK_NP, { 0, 0 } };
555
#    else
556
static pthread_mutex_t mark_mutex = PTHREAD_MUTEX_INITIALIZER;
557
#    endif
558

559
#    ifdef CAN_HANDLE_FORK
560
/* Note: this is initialized by `GC_start_mark_threads_inner()`. */
561
static pthread_cond_t mark_cv;
562
#    else
563
static pthread_cond_t mark_cv = PTHREAD_COND_INITIALIZER;
564
#    endif
565

566
GC_INNER void
567
GC_start_mark_threads_inner(void)
78✔
568
{
569
  int i;
570
  pthread_attr_t attr;
571
#    ifndef NO_MARKER_SPECIAL_SIGMASK
572
  sigset_t set, oldset;
573
#    endif
574

575
  GC_ASSERT(I_HOLD_LOCK());
78✔
576
  ASSERT_CANCEL_DISABLED();
78✔
577
  if (GC_available_markers_m1 <= 0 || GC_parallel) {
78✔
578
    /* Skip if parallel markers disabled or already started (or starting). */
579
    return;
×
580
  }
581
  GC_wait_for_gc_completion(TRUE);
78✔
582

583
#    ifdef CAN_HANDLE_FORK
584
  /*
585
   * Initialize `mark_cv` (for the first time), or cleanup its value
586
   * after forking in the child process.  All the marker threads in the
587
   * parent process were blocked on this variable at process fork, so
588
   * `pthread_cond_wait()` malfunction (hang) is possible in the child
589
   * process without such a cleanup.
590
   */
591

592
  /*
593
   * TODO: This is not portable, it is better to shortly unblock all
594
   * marker threads in the parent process at `fork`.
595
   */
596
  {
597
    pthread_cond_t mark_cv_local = PTHREAD_COND_INITIALIZER;
78✔
598
    BCOPY(&mark_cv_local, &mark_cv, sizeof(mark_cv));
78✔
599
  }
600
#    endif
601

602
  GC_ASSERT(0 == GC_fl_builder_count);
78✔
603
  INIT_REAL_SYMS(); /*< for `pthread_sigmask` and `pthread_create` */
604

605
  if (pthread_attr_init(&attr) != 0)
78✔
606
    ABORT("pthread_attr_init failed");
×
607
  if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0)
78✔
608
    ABORT("pthread_attr_setdetachstate failed");
×
609

610
#    ifdef DEFAULT_STACK_MAYBE_SMALL
611
  /*
612
   * The default stack size is usually too small: increase it.
613
   * Otherwise marker threads may run out of space.
614
   */
615
  {
616
    size_t old_size;
617

618
    if (pthread_attr_getstacksize(&attr, &old_size) != 0)
619
      ABORT("pthread_attr_getstacksize failed");
620
    if (old_size < MIN_STACK_SIZE && old_size != 0 /* stack size is known */) {
621
      if (pthread_attr_setstacksize(&attr, MIN_STACK_SIZE) != 0)
622
        ABORT("pthread_attr_setstacksize failed");
623
    }
624
  }
625
#    endif /* DEFAULT_STACK_MAYBE_SMALL */
626

627
#    ifndef NO_MARKER_SPECIAL_SIGMASK
628
  /*
629
   * Apply special signal mask to GC marker threads, and do not drop
630
   * user-defined signals by the marker threads.
631
   */
632
  if (sigfillset(&set) != 0)
78✔
633
    ABORT("sigfillset failed");
×
634

635
#      ifdef SIGNAL_BASED_STOP_WORLD
636
  /* These are used by GC to stop and restart the world. */
637
  if (sigdelset(&set, GC_get_suspend_signal()) != 0
78✔
638
      || sigdelset(&set, GC_get_thr_restart_signal()) != 0)
78✔
639
    ABORT("sigdelset failed");
×
640
#      endif
641

642
  if (UNLIKELY(REAL_FUNC(pthread_sigmask)(SIG_BLOCK, &set, &oldset) != 0)) {
78✔
643
    WARN("pthread_sigmask set failed, no markers started\n", 0);
×
644
    GC_markers_m1 = 0;
×
645
    (void)pthread_attr_destroy(&attr);
×
646
    return;
×
647
  }
648
#    endif /* !NO_MARKER_SPECIAL_SIGMASK */
649

650
  /* To have proper `GC_parallel` value in `GC_help_marker()`. */
651
  GC_markers_m1 = GC_available_markers_m1;
78✔
652

653
  for (i = 0; i < GC_available_markers_m1; ++i) {
156✔
654
    int res;
655
    pthread_t new_thread;
656

657
#    ifdef GC_WIN32_THREADS
658
    GC_marker_last_stack_min[i] = ADDR_LIMIT;
659
#    endif
660
#    ifdef REDIRECT_MALLOC
661
    /*
662
     * Real `pthread_create()` could call our `calloc()` redirecting
663
     * to `GC_generic_malloc_uncollectable()`, thus we should release
664
     * the allocator lock temporarily.  As a consequence, we need to
665
     * prevent usage of the markers till `GC_wait_for_markers_init()`
666
     * completion - the easiest way is to disable collection temporarily.
667
     */
668
    GC_disable_inner();
669
    UNLOCK();
670
#    endif
671
    res = REAL_FUNC(pthread_create)(&new_thread, &attr, GC_mark_thread,
78✔
672
                                    NUMERIC_TO_VPTR(i));
78✔
673
#    ifdef REDIRECT_MALLOC
674
    LOCK();
675
    GC_enable_inner();
676
#    endif
677
    if (UNLIKELY(res != 0)) {
78✔
678
      WARN("Marker thread %" WARN_PRIdPTR " creation failed\n",
×
679
           (GC_signed_word)i);
680
      /* Do not try to create other marker threads. */
681
      GC_markers_m1 = i;
×
682
      break;
×
683
    }
684
  }
685

686
#    ifndef NO_MARKER_SPECIAL_SIGMASK
687
  /* Restore previous signal mask. */
688
  if (UNLIKELY(REAL_FUNC(pthread_sigmask)(SIG_SETMASK, &oldset, NULL) != 0)) {
78✔
689
    WARN("pthread_sigmask restore failed\n", 0);
×
690
  }
691
#    endif
692

693
  (void)pthread_attr_destroy(&attr);
78✔
694
  GC_wait_for_markers_init();
78✔
695
  GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1);
78✔
696
}
697

698
#  endif /* GC_PTHREADS_PARAMARK */
699

700
GC_INNER GC_thread GC_threads[THREAD_TABLE_SZ] = { NULL };
701

702
/*
703
 * It may not be safe to allocate when we register the first thread.
704
 * Note that `next` and `status` fields are unused, but there might be
705
 * some other fields (`crtn`) to be pushed.
706
 */
707
static struct GC_StackContext_Rep first_crtn;
708
static struct GC_Thread_Rep first_thread;
709

710
#  ifndef GC_NO_DEINIT
711
GC_INNER void
712
GC_reset_threads(void)
×
713
{
714
  BZERO(GC_threads, sizeof(GC_threads));
×
715
  BZERO(&first_crtn, sizeof(first_crtn));
×
716
  BZERO(&first_thread, sizeof(first_thread));
×
717
}
×
718
#  endif
719

720
/*
721
 * A place to retain a pointer to an allocated object while a thread
722
 * registration is ongoing.  Protected by the allocator lock.
723
 */
724
static GC_stack_context_t saved_crtn = NULL;
725

726
GC_INNER void
727
GC_push_thread_structures(void)
×
728
{
729
  GC_ASSERT(I_HOLD_LOCK());
×
730
#  ifdef HAS_WIN32_THREADS_DISCOVERY
731
  if (GC_win32_dll_threads) {
732
    /*
733
     * Unlike the other threads implementations, the thread table here
734
     * contains no pointers to the collectible heap (note also that
735
     * `GC_PTHREADS` is incompatible with `DllMain`-based thread
736
     * registration).  Thus we have no private structures we need to
737
     * preserve.
738
     */
739
  } else
740
#  endif
741
  /* else */ {
742
    GC_push_all(&GC_threads, (ptr_t)(&GC_threads) + sizeof(GC_threads));
×
743
    GC_ASSERT(NULL == first_thread.tm.next);
×
744
#  ifdef GC_PTHREADS
745
    GC_ASSERT(NULL == first_thread.status);
×
746
#  endif
747
    GC_PUSH_ALL_SYM(first_thread.crtn);
×
748
    GC_PUSH_ALL_SYM(saved_crtn);
×
749
  }
750
#  if defined(THREAD_LOCAL_ALLOC) && defined(USE_CUSTOM_SPECIFIC)
751
  GC_PUSH_ALL_SYM(GC_thread_key);
×
752
#  endif
753
}
×
754

755
#  if defined(MPROTECT_VDB) && defined(GC_WIN32_THREADS)
756
GC_INNER void
757
GC_win32_unprotect_thread(GC_thread t)
758
{
759
  GC_ASSERT(I_HOLD_LOCK());
760
  if (GC_auto_incremental
761
#    ifdef HAS_WIN32_THREADS_DISCOVERY /*< for `LINT2` */
762
      && !GC_win32_dll_threads
763
#    endif
764
  ) {
765
    GC_stack_context_t crtn = t->crtn;
766

767
    if (crtn != &first_crtn) {
768
      GC_ASSERT(SMALL_OBJ(GC_size(crtn)));
769
      GC_remove_protection(HBLKPTR(crtn), 1, FALSE);
770
    }
771
    if (t != &first_thread) {
772
      GC_ASSERT(SMALL_OBJ(GC_size(t)));
773
      GC_remove_protection(HBLKPTR(t), 1, FALSE);
774
    }
775
  }
776
}
777
#  endif /* MPROTECT_VDB && GC_WIN32_THREADS */
778

779
#  ifdef DEBUG_THREADS
780
STATIC int
781
GC_count_threads(void)
782
{
783
  int i, count = 0;
784

785
#    ifdef HAS_WIN32_THREADS_DISCOVERY
786
  /* TODO: Not implemented. */
787
  if (GC_win32_dll_threads)
788
    return -1;
789
#    endif
790
  GC_ASSERT(I_HOLD_READER_LOCK());
791
  for (i = 0; i < THREAD_TABLE_SZ; ++i) {
792
    GC_thread p;
793

794
    for (p = GC_threads[i]; p != NULL; p = p->tm.next) {
795
      if (!KNOWN_FINISHED(p))
796
        ++count;
797
    }
798
  }
799
  return count;
800
}
801
#  endif /* DEBUG_THREADS */
802

803
GC_INNER_WIN32THREAD GC_thread
804
GC_new_thread(thread_id_t self_id)
100,591✔
805
{
806
  int hv = THREAD_TABLE_INDEX(self_id);
100,591✔
807
  GC_thread result;
808

809
  GC_ASSERT(I_HOLD_LOCK());
100,591✔
810
#  ifdef DEBUG_THREADS
811
  GC_log_printf("Creating thread %p\n", THREAD_ID_TO_VPTR(self_id));
812
  for (result = GC_threads[hv]; result != NULL; result = result->tm.next)
813
    if (!THREAD_ID_EQUAL(result->id, self_id)) {
814
      GC_log_printf("Hash collision at GC_threads[%d]\n", hv);
815
      break;
816
    }
817
#  endif
818
  if (UNLIKELY(NULL == first_thread.crtn)) {
100,591✔
819
    result = &first_thread;
39✔
820
    first_thread.crtn = &first_crtn;
39✔
821
    GC_ASSERT(NULL == GC_threads[hv]);
39✔
822
#  if defined(CPPCHECK) && defined(THREAD_SANITIZER) \
823
      && defined(SIGNAL_BASED_STOP_WORLD)
824
    GC_noop1((unsigned char)first_crtn.dummy[0]);
825
#  endif
826
  } else {
827
    GC_stack_context_t crtn;
828

829
    GC_ASSERT(!GC_win32_dll_threads);
830
    GC_ASSERT(!GC_in_thread_creation);
100,552✔
831
    GC_in_thread_creation = TRUE; /*< OK to collect from unknown thread */
100,552✔
832
    crtn = (GC_stack_context_t)GC_INTERNAL_MALLOC(
100,552✔
833
        sizeof(struct GC_StackContext_Rep), NORMAL);
834

835
    /*
836
     * The current stack is not scanned until the thread is registered,
837
     * thus `crtn` pointer is to be retained in the global data roots for
838
     * a while (and pushed explicitly if a collection occurs here).
839
     */
840
    GC_ASSERT(NULL == saved_crtn);
100,552✔
841
    saved_crtn = crtn;
100,552✔
842
    result
843
        = (GC_thread)GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep), NORMAL);
100,552✔
844
    /* No more collections till thread is registered. */
845
    saved_crtn = NULL;
100,552✔
846
    GC_in_thread_creation = FALSE;
100,552✔
847
    if (NULL == crtn || NULL == result)
100,552✔
848
      ABORT("Failed to allocate memory for thread registering");
×
849
    result->crtn = crtn;
100,552✔
850
  }
851
  /* The `id` field is not set here. */
852
#  ifdef USE_TKILL_ON_ANDROID
853
  result->kernel_id = gettid();
854
#  endif
855
  result->tm.next = GC_threads[hv];
100,591✔
856
  GC_threads[hv] = result;
100,591✔
857
#  ifdef NACL
858
  GC_nacl_initialize_gc_thread(result);
859
#  endif
860
  GC_ASSERT(0 == result->flags);
100,591✔
861
  if (LIKELY(result != &first_thread))
100,591✔
862
    GC_dirty(result);
100,552✔
863
  return result;
100,591✔
864
}
865

866
GC_INNER_WIN32THREAD void
867
GC_delete_thread(GC_thread t)
100,287✔
868
{
869
#  if defined(GC_WIN32_THREADS) && !defined(MSWINCE)
870
  CloseHandle(t->handle);
871
#  endif
872
#  ifdef HAS_WIN32_THREADS_DISCOVERY
873
  if (GC_win32_dll_threads) {
874
    /*
875
     * This is intended to be lock-free.  In this branch asynchronous changes
876
     * to `*t` are possible.  Note that it is not allowed to call `GC_printf`
877
     * (and the friends) here, see `GC_stop_world()` in `win32_threads.c` file
878
     * for the information.
879
     */
880
    t->crtn->stack_end = NULL;
881
    t->id = 0;
882
    /* The thread is not suspended. */
883
    t->flags = 0;
884
#    ifdef RETRY_GET_THREAD_CONTEXT
885
    t->context_sp = NULL;
886
#    endif
887
    AO_store_release(&t->tm.in_use, FALSE);
888
  } else
889
#  endif
890
  /* else */ {
891
    thread_id_t id = t->id;
100,287✔
892
    int hv = THREAD_TABLE_INDEX(id);
100,287✔
893
    GC_thread p;
894
    GC_thread prev = NULL;
100,287✔
895

896
    GC_ASSERT(I_HOLD_LOCK());
100,287✔
897
#  if defined(DEBUG_THREADS) && !defined(MSWINCE) \
898
      && (!defined(MSWIN32) || defined(CONSOLE_LOG))
899
    GC_log_printf("Deleting thread %p, n_threads= %d\n", THREAD_ID_TO_VPTR(id),
900
                  GC_count_threads());
901
#  endif
902
    for (p = GC_threads[hv]; p != t; p = p->tm.next) {
103,562✔
903
      prev = p;
3,275✔
904
    }
905
    if (NULL == prev) {
100,287✔
906
      GC_threads[hv] = p->tm.next;
97,336✔
907
    } else {
908
      GC_ASSERT(prev != &first_thread);
2,951✔
909
      prev->tm.next = p->tm.next;
2,951✔
910
      GC_dirty(prev);
2,951✔
911
    }
912
    if (LIKELY(p != &first_thread)) {
100,287✔
913
#  ifdef DARWIN
914
      mach_port_deallocate(mach_task_self(), p->mach_thread);
915
#  endif
916
      GC_ASSERT(p->crtn != &first_crtn);
100,285✔
917
      GC_INTERNAL_FREE(p->crtn);
100,285✔
918
      GC_INTERNAL_FREE(p);
100,285✔
919
    }
920
  }
921
}
100,287✔
922

923
GC_INNER GC_thread
924
GC_lookup_thread(thread_id_t id)
203,699,680✔
925
{
926
  GC_thread p;
927

928
#  ifdef HAS_WIN32_THREADS_DISCOVERY
929
  if (GC_win32_dll_threads)
930
    return GC_win32_dll_lookup_thread(id);
931
#  endif
932
  for (p = GC_threads[THREAD_TABLE_INDEX(id)]; p != NULL; p = p->tm.next) {
205,478,004✔
933
    if (LIKELY(THREAD_ID_EQUAL(p->id, id)))
205,429,486✔
934
      break;
203,651,162✔
935
  }
936
  return p;
203,699,680✔
937
}
938

939
/*
940
 * Same as `GC_self_thread_inner()` but acquires the allocator lock (in
941
 * the reader mode).
942
 */
943
STATIC GC_thread
944
GC_self_thread(void)
200,022,341✔
945
{
946
  GC_thread p;
947

948
  READER_LOCK();
200,022,341✔
949
  p = GC_self_thread_inner();
200,022,341✔
950
  READER_UNLOCK();
200,022,341✔
951
  return p;
200,022,341✔
952
}
953

954
#  ifndef GC_NO_FINALIZATION
955
GC_INNER void
956
GC_reset_finalizer_nested(void)
×
957
{
958
  GC_ASSERT(I_HOLD_LOCK());
×
959
  GC_self_thread_inner()->crtn->finalizer_nested = 0;
×
960
}
×
961

962
GC_INNER unsigned char *
963
GC_check_finalizer_nested(void)
3,266✔
964
{
965
  GC_thread me;
966
  GC_stack_context_t crtn;
967
  unsigned nesting_level;
968

969
  GC_ASSERT(I_HOLD_LOCK());
3,266✔
970
  me = GC_self_thread_inner();
3,266✔
971
#    if defined(INCLUDE_LINUX_THREAD_DESCR) && defined(REDIRECT_MALLOC)
972
  /*
973
   * As noted in `GC_pthread_start`, an allocation may happen in
974
   * `GC_get_stack_base`, causing `GC_notify_or_invoke_finalizers`
975
   * to be called before the thread gets registered.
976
   */
977
  if (UNLIKELY(NULL == me))
978
    return NULL;
979
#    endif
980
  crtn = me->crtn;
3,266✔
981
  nesting_level = crtn->finalizer_nested;
3,266✔
982
  if (nesting_level) {
3,266✔
983
    /*
984
     * We are inside another `GC_invoke_finalizers()`.  Skip some
985
     * implicitly-called `GC_invoke_finalizers()` depending on the
986
     * nesting (recursion) level.
987
     */
988
    if ((unsigned)(++crtn->finalizer_skipped) < (1U << nesting_level))
×
989
      return NULL;
×
990
    crtn->finalizer_skipped = 0;
×
991
  }
992
  crtn->finalizer_nested = (unsigned char)(nesting_level + 1);
3,266✔
993
  return &crtn->finalizer_nested;
3,266✔
994
}
995
#  endif /* !GC_NO_FINALIZATION */
996

997
#  define ADDR_INSIDE_OBJ(p, obj) \
998
    ADDR_INSIDE(p, (ptr_t)(&(obj)), (ptr_t)(&(obj)) + sizeof(obj))
999

1000
#  if defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC)
1001
/* This is called from thread-local `GC_malloc()`. */
1002
GC_bool
1003
GC_is_thread_tsd_valid(void *tsd)
200,022,272✔
1004
{
1005
  GC_thread me = GC_self_thread();
200,022,272✔
1006

1007
  return ADDR_INSIDE_OBJ((ptr_t)tsd, me->tlfs);
200,022,272✔
1008
}
1009
#  endif /* GC_ASSERTIONS && THREAD_LOCAL_ALLOC */
1010

1011
GC_API int GC_CALL
1012
GC_thread_is_registered(void)
67✔
1013
{
1014
  /* TODO: Use `GC_get_tlfs()` instead. */
1015
  GC_thread me = GC_self_thread();
67✔
1016

1017
  if (NULL == me)
67✔
1018
    return FALSE; /*< for `LINT2` */
×
1019

1020
  return !KNOWN_FINISHED(me);
67✔
1021
}
1022

1023
GC_API void GC_CALL
1024
GC_register_altstack(void *normstack, size_t normstack_size, void *altstack,
×
1025
                     size_t altstack_size)
1026
{
1027
#  ifdef GC_WIN32_THREADS
1028
  /* TODO: Implement. */
1029
  UNUSED_ARG(normstack);
1030
  UNUSED_ARG(normstack_size);
1031
  UNUSED_ARG(altstack);
1032
  UNUSED_ARG(altstack_size);
1033
#  else
1034
  GC_thread me;
1035
  GC_stack_context_t crtn;
1036

1037
  READER_LOCK();
×
1038
  me = GC_self_thread_inner();
×
1039
  if (UNLIKELY(NULL == me)) {
×
1040
    /* We are called before `GC_thr_init()`. */
1041
    me = &first_thread;
×
1042
  }
1043
  crtn = me->crtn;
×
1044
  crtn->normstack = (ptr_t)normstack;
×
1045
  crtn->normstack_size = normstack_size;
×
1046
  crtn->altstack = (ptr_t)altstack;
×
1047
  crtn->altstack_size = altstack_size;
×
1048
  READER_UNLOCK_RELEASE();
×
1049
#  endif
1050
}
×
1051

1052
#  if defined(USE_PROC_FOR_LIBRARIES) && defined(LINUX)
1053
GC_INNER GC_bool
1054
GC_segment_is_thread_stack(ptr_t lo, ptr_t hi)
1055
{
1056
  int i;
1057
  GC_thread p;
1058

1059
  GC_ASSERT(I_HOLD_READER_LOCK());
1060
#    ifdef PARALLEL_MARK
1061
  for (i = 0; i < GC_markers_m1; ++i) {
1062
    if (ADDR_LT(lo, GC_marker_sp[i]) && ADDR_LT(GC_marker_sp[i], hi))
1063
      return TRUE;
1064
#      ifdef IA64
1065
    if (ADDR_LT(lo, marker_bsp[i]) && ADDR_LT(marker_bsp[i], hi))
1066
      return TRUE;
1067
#      endif
1068
  }
1069
#    endif
1070
  for (i = 0; i < THREAD_TABLE_SZ; i++) {
1071
    for (p = GC_threads[i]; p != NULL; p = p->tm.next) {
1072
      ptr_t stack_end = p->crtn->stack_end;
1073

1074
      if (stack_end != NULL) {
1075
#    ifdef STACK_GROWS_UP
1076
        if (ADDR_INSIDE(stack_end, lo, hi))
1077
          return TRUE;
1078
#    else
1079
        if (ADDR_LT(lo, stack_end) && ADDR_GE(hi, stack_end))
1080
          return TRUE;
1081
#    endif
1082
      }
1083
    }
1084
  }
1085
  return FALSE;
1086
}
1087
#  endif
1088

1089
#  if (defined(HAVE_PTHREAD_ATTR_GET_NP) || defined(HAVE_PTHREAD_GETATTR_NP)) \
1090
      && defined(IA64)
1091
GC_INNER ptr_t
1092
GC_greatest_stack_base_below(ptr_t bound)
1093
{
1094
  int i;
1095
  GC_thread p;
1096
  ptr_t result = NULL;
1097

1098
  GC_ASSERT(I_HOLD_READER_LOCK());
1099
#    ifdef PARALLEL_MARK
1100
  for (i = 0; i < GC_markers_m1; ++i) {
1101
    if (ADDR_LT(result, GC_marker_sp[i]) && ADDR_LT(GC_marker_sp[i], bound))
1102
      result = GC_marker_sp[i];
1103
  }
1104
#    endif
1105
  for (i = 0; i < THREAD_TABLE_SZ; i++) {
1106
    for (p = GC_threads[i]; p != NULL; p = p->tm.next) {
1107
      ptr_t stack_end = p->crtn->stack_end;
1108

1109
      if (ADDR_LT(result, stack_end) && ADDR_LT(stack_end, bound))
1110
        result = stack_end;
1111
    }
1112
  }
1113
  return result;
1114
}
1115
#  endif /* IA64 */
1116

1117
#  ifndef STAT_READ
1118
/*
1119
 * Note: if `read()` is wrapped, this may need to be redefined to call
1120
 * the real one.
1121
 */
1122
#    define STAT_READ read
1123
#  endif
1124

1125
#  ifdef HPUX
1126
#    define GC_get_nprocs() pthread_num_processors_np()
1127

1128
#  elif defined(AIX) || defined(COSMO) || defined(HAIKU)         \
1129
      || defined(HOST_ANDROID) || defined(HURD) || defined(NACL) \
1130
      || defined(OSF1) || defined(SOLARIS)
1131
GC_INLINE int
1132
GC_get_nprocs(void)
1133
{
1134
  int nprocs = (int)sysconf(_SC_NPROCESSORS_ONLN);
1135
  /* Note: ignore any error silently. */
1136
  return nprocs > 0 ? nprocs : 1;
1137
}
1138

1139
#  elif defined(IRIX5)
1140
GC_INLINE int
1141
GC_get_nprocs(void)
1142
{
1143
  int nprocs = (int)sysconf(_SC_NPROC_ONLN);
1144
  /* Note: ignore any error silently. */
1145
  return nprocs > 0 ? nprocs : 1;
1146
}
1147

1148
#  elif defined(LINUX)
1149
/* Return the number of processors. */
1150
STATIC int
1151
GC_get_nprocs(void)
39✔
1152
{
1153
  /*
1154
   * Should be just `return sysconf(_SC_NPROCESSORS_ONLN)` but that
1155
   * appears to be buggy in many cases.  We look for lines "cpu<N>" in
1156
   * `/proc/stat` pseudo-file.  No need to read the entire `/proc/stat`
1157
   * pseudo-file to get maximum "cpu<N>" such as:
1158
   *   - the requested lines are located at the beginning of the file;
1159
   *   - the lines with "cpu<N>" where `N` is greater than `MAX_MARKERS`
1160
   *     are not needed.
1161
   */
1162
#    define PROC_STAT_BUF_SZ ((1 + MAX_MARKERS) * 100)
1163
  char stat_buf[PROC_STAT_BUF_SZ + 1]; /*< the size should be enough */
1164
  int f;
1165
  int result, i, len;
1166

1167
  f = open("/proc/stat", O_RDONLY);
39✔
1168
  if (f < 0) {
39✔
1169
    WARN("Could not open /proc/stat\n", 0);
×
1170
    /* Assume an uniprocessor. */
1171
    return 1;
39✔
1172
  }
1173
  len = STAT_READ(f, stat_buf, sizeof(stat_buf) - 1);
39✔
1174
  /* Unlikely that we need to retry because of an incomplete read here. */
1175
  if (len < 0) {
39✔
1176
    WARN("Failed to read /proc/stat, errno= %" WARN_PRIdPTR "\n",
×
1177
         (GC_signed_word)errno);
1178
    close(f);
×
1179
    return 1;
×
1180
  }
1181
  /* Avoid potential buffer overrun by `atoi()`. */
1182
  stat_buf[len] = '\0';
39✔
1183

1184
  close(f);
39✔
1185

1186
  /*
1187
   * Some old kernels only have a single "cpu nnnn ..." entry in
1188
   * `/proc/stat` pseudo-file.  We identify those as uniprocessors.
1189
   */
1190
  result = 1;
39✔
1191

1192
  for (i = 0; i < len - 4; ++i) {
45,819✔
1193
    if (stat_buf[i] == '\n' && stat_buf[i + 1] == 'c' && stat_buf[i + 2] == 'p'
45,780✔
1194
        && stat_buf[i + 3] == 'u') {
78✔
1195
      int cpu_no = atoi(&stat_buf[i + 4]);
78✔
1196
      if (cpu_no >= result)
78✔
1197
        result = cpu_no + 1;
39✔
1198
    }
1199
  }
1200
  return result;
39✔
1201
}
1202

1203
#  elif defined(DGUX)
1204
/*
1205
 * Return the number of processors, or a non-positive value if the number
1206
 * cannot be determined.
1207
 */
1208
STATIC int
1209
GC_get_nprocs(void)
1210
{
1211
  int numCpus;
1212
  struct dg_sys_info_pm_info pm_sysinfo;
1213
  int status = 0;
1214

1215
  status = dg_sys_info((long int *)&pm_sysinfo, DG_SYS_INFO_PM_INFO_TYPE,
1216
                       DG_SYS_INFO_PM_CURRENT_VERSION);
1217
  if (status < 0) {
1218
    /* Set -1 for an error. */
1219
    numCpus = -1;
1220
  } else {
1221
    /* Active CPUs. */
1222
    numCpus = pm_sysinfo.idle_vp_count;
1223
  }
1224
  return numCpus;
1225
}
1226

1227
#  elif defined(ANY_BSD) || defined(DARWIN)
1228
STATIC int
1229
GC_get_nprocs(void)
1230
{
1231
  int mib[] = { CTL_HW, HW_NCPU };
1232
  int res;
1233
  size_t len = sizeof(res);
1234

1235
  sysctl(mib, sizeof(mib) / sizeof(int), &res, &len, NULL, 0);
1236
  return res;
1237
}
1238

1239
#  else
1240
/* E.g., RTEMS. */
1241
/* TODO: Implement. */
1242
#    define GC_get_nprocs() 1
1243
#  endif
1244

1245
#  if defined(LINUX) && defined(ARM32)
1246
/*
1247
 * Some buggy Linux/arm kernels show only non-sleeping CPUs in
1248
 * `/proc/stat` pseudo-file (and in `/proc/cpuinfo` pseudo-file), so
1249
 * another data system source is tried first.  Returns a non-positive
1250
 * value on error.
1251
 */
1252
STATIC int
1253
GC_get_nprocs_present(void)
1254
{
1255
  char stat_buf[16];
1256
  int f;
1257
  int len;
1258

1259
  f = open("/sys/devices/system/cpu/present", O_RDONLY);
1260
  if (f < 0) {
1261
    /* Cannot open the file. */
1262
    return -1;
1263
  }
1264

1265
  len = STAT_READ(f, stat_buf, sizeof(stat_buf));
1266
  close(f);
1267

1268
  /*
1269
   * Recognized file format: "0\n" or "0-<max_cpu_num>\n".
1270
   * The file might probably contain a comma-separated list
1271
   * but we do not need to handle it (just silently ignore).
1272
   */
1273
  if (len < 2 || stat_buf[0] != '0' || stat_buf[len - 1] != '\n') {
1274
    /* A read error or an unrecognized contents. */
1275
    return 0;
1276
  } else if (len == 2) {
1277
    /* An uniprocessor. */
1278
    return 1;
1279
  } else if (stat_buf[1] != '-') {
1280
    /* An unrecognized contents. */
1281
    return 0;
1282
  }
1283

1284
  /* Terminate the string. */
1285
  stat_buf[len - 1] = '\0';
1286

1287
  /* Skip "0-" and parse `max_cpu_num`. */
1288
  return atoi(&stat_buf[2]) + 1;
1289
}
1290
#  endif /* LINUX && ARM32 */
1291

1292
#  if defined(CAN_HANDLE_FORK) && defined(THREAD_SANITIZER)
1293
#    include "private/gc_pmark.h" /*< for `MS_NONE` */
1294

1295
/*
1296
 * Workaround for TSan which does not notice that the allocator lock
1297
 * is acquired in `fork_prepare_proc()`.
1298
 */
1299
GC_ATTR_NO_SANITIZE_THREAD
1300
static GC_bool
1301
collection_in_progress(void)
1302
{
1303
  return GC_mark_state != MS_NONE;
1304
}
1305
#  else
1306
#    define collection_in_progress() GC_collection_in_progress()
1307
#  endif
1308

1309
GC_INNER void
1310
GC_wait_for_gc_completion(GC_bool wait_for_all)
100,645✔
1311
{
1312
#  if !defined(THREAD_SANITIZER) || !defined(CAN_CALL_ATFORK)
1313
  /*
1314
   * `GC_lock_holder` is accessed with the allocator lock held, so
1315
   * there is no data race actually (unlike what is reported by TSan).
1316
   */
1317
  GC_ASSERT(I_HOLD_LOCK());
100,645✔
1318
#  endif
1319
  ASSERT_CANCEL_DISABLED();
100,645✔
1320
#  ifdef GC_DISABLE_INCREMENTAL
1321
  (void)wait_for_all;
1322
#  else
1323
  if (GC_incremental && collection_in_progress()) {
100,645✔
1324
    word old_gc_no = GC_gc_no;
1✔
1325

1326
    /*
1327
     * Make sure that no part of our stack is still on the mark stack,
1328
     * since it is about to be unmapped.
1329
     */
1330
#    ifdef LINT2
1331
    /*
1332
     * Note: do not transform this `if`-`do`-`while` construction into
1333
     * a single while statement because it might cause some static code
1334
     * analyzers to report a false positive (FP) code defect about
1335
     * missing unlock after lock.
1336
     */
1337
#    endif
1338
    do {
1339
      GC_ASSERT(!GC_in_thread_creation);
1✔
1340
      GC_in_thread_creation = TRUE;
1✔
1341
      GC_collect_a_little_inner(1);
1✔
1342
      GC_in_thread_creation = FALSE;
1✔
1343

1344
      UNLOCK();
1✔
1345
#    ifdef GC_WIN32_THREADS
1346
      Sleep(0);
1347
#    else
1348
      sched_yield();
1✔
1349
#    endif
1350
      LOCK();
1✔
1351
    } while (GC_incremental && collection_in_progress()
1✔
1352
             && (wait_for_all || old_gc_no == GC_gc_no));
1✔
1353
  }
1354
#  endif
1355
}
100,645✔
1356

1357
#  if defined(GC_ASSERTIONS) && defined(GC_PTHREADS_PARAMARK)
1358
STATIC unsigned long GC_mark_lock_holder = NO_THREAD;
1359
#  endif
1360

1361
#  ifdef CAN_HANDLE_FORK
1362

1363
/*
1364
 * Procedures called before and after a process fork.  The goal here is
1365
 * to make it safe to call `GC_malloc()` in the forked child process.
1366
 * It is unclear that is attainable, since the Single UNIX Specification
1367
 * seems to imply that one should only call async-signal-safe functions,
1368
 * and we probably cannot quite guarantee that.  But we give it our best
1369
 * shot.  (That same specification also implies that it is not safe to
1370
 * call the system `malloc` between `fork` and `exec`.  Thus we are doing
1371
 * no worse than it.)
1372
 */
1373

1374
IF_CANCEL(static int fork_cancel_state;) /*< protected by the allocator lock */
1375

1376
#    ifdef PARALLEL_MARK
1377
#      ifdef THREAD_SANITIZER
1378
#        if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK)
1379
STATIC void GC_generic_lock(pthread_mutex_t *);
1380
#        endif
1381
GC_ATTR_NO_SANITIZE_THREAD
1382
static void wait_for_reclaim_atfork(void);
1383
#      else
1384
#        define wait_for_reclaim_atfork() GC_wait_for_reclaim()
1385
#      endif
1386
#    endif /* PARALLEL_MARK */
1387

1388
/*
1389
 * Prevent TSan false positive (FP) about the race during items removal
1390
 * from `GC_threads`.  (The race cannot happen since only one thread
1391
 * survives in the child process.)
1392
 */
1393
#    ifdef CAN_CALL_ATFORK
1394
GC_ATTR_NO_SANITIZE_THREAD
1395
#    endif
1396
static void
1397
store_to_threads_table(int hv, GC_thread me)
×
1398
{
1399
  GC_threads[hv] = me;
×
1400
}
×
1401

1402
/*
1403
 * Remove all entries from the `GC_threads` table, except the one (if any)
1404
 * for the current thread.  Also update thread identifiers stored in the
1405
 * table for the current thread.  We need to do this in the child process
1406
 * after a `fork()`, since only the current thread survives in the child
1407
 * process.
1408
 */
1409
STATIC void
1410
GC_remove_all_threads_but_me(void)
×
1411
{
1412
  int hv;
1413
  GC_thread me = NULL;
×
1414
#    ifndef GC_WIN32_THREADS
1415
#      define pthread_id id
1416
#    endif
1417

1418
  for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) {
×
1419
    GC_thread p, next;
1420

1421
    for (p = GC_threads[hv]; p != NULL; p = next) {
×
1422
      next = p->tm.next;
×
1423
      if (THREAD_EQUAL(p->pthread_id, GC_parent_pthread_self) && me == NULL) {
×
1424
        /* Ignore dead threads with the same id. */
1425
        me = p;
×
1426
        p->tm.next = NULL;
×
1427
      } else {
1428
#    ifdef THREAD_LOCAL_ALLOC
1429
        if (!KNOWN_FINISHED(p)) {
×
1430
          /*
1431
           * Cannot call `GC_destroy_thread_local` here.  The free
1432
           * lists may be in an inconsistent state (as thread `p` may
1433
           * be updating one of the lists by `GC_generic_malloc_many()`
1434
           * or `GC_FAST_MALLOC_GRANS()` when `fork()` is invoked).
1435
           * This should not be a problem because the lost elements
1436
           * of the free lists will be collected during GC.
1437
           */
1438
          GC_remove_specific_after_fork(GC_thread_key, p->pthread_id);
×
1439
        }
1440
#    endif
1441
        /*
1442
         * TODO: To avoid TSan hang (when updating `GC_bytes_freed`),
1443
         * we just skip explicit freeing of `GC_threads` entries.
1444
         */
1445
#    if !defined(THREAD_SANITIZER) || !defined(CAN_CALL_ATFORK)
1446
        if (p != &first_thread) {
×
1447
          /* TODO: Should call `mach_port_deallocate`? */
1448
          GC_ASSERT(p->crtn != &first_crtn);
×
1449
          GC_INTERNAL_FREE(p->crtn);
×
1450
          GC_INTERNAL_FREE(p);
×
1451
        }
1452
#    endif
1453
      }
1454
    }
1455
    store_to_threads_table(hv, NULL);
×
1456
  }
1457

1458
  if (NULL == me)
×
1459
    return; /*< `fork()` is called from an unregistered thread */
×
1460

1461
  /*
1462
   * Update `pthreads` id as it is not guaranteed to be the same between
1463
   * this (child) process and the parent one.
1464
   */
1465
  me->pthread_id = pthread_self();
×
1466
#    ifdef GC_WIN32_THREADS
1467
  /*
1468
   * Update Win32 thread id and handle.  They differ from that in the
1469
   * parent process.
1470
   */
1471
  me->id = thread_id_self();
1472
#      ifndef MSWINCE
1473
  if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
1474
                       GetCurrentProcess(), (HANDLE *)&me->handle,
1475
                       0 /* `dwDesiredAccess` */, FALSE /* `bInheritHandle` */,
1476
                       DUPLICATE_SAME_ACCESS))
1477
    ABORT("DuplicateHandle failed");
1478
#      endif
1479
#    endif
1480
#    ifdef DARWIN
1481
  /*
1482
   * Update thread id after process fork (it is OK to call
1483
   * `GC_destroy_thread_local()` and `GC_free_internal()` before update).
1484
   */
1485
  me->mach_thread = mach_thread_self();
1486
#    endif
1487
#    ifdef USE_TKILL_ON_ANDROID
1488
  me->kernel_id = gettid();
1489
#    endif
1490

1491
  /* Put `me` back to `GC_threads`. */
1492
  store_to_threads_table(THREAD_TABLE_INDEX(me->id), me);
×
1493

1494
#    ifdef THREAD_LOCAL_ALLOC
1495
  GC_update_specific_after_fork(GC_thread_key, &me->tlfs);
×
1496
#    endif
1497
#    undef pthread_id
1498
}
1499

1500
/*
1501
 * Same as `GC_thread_is_registered()` but assumes the allocator lock is held.
1502
 */
1503
static GC_bool
1504
is_thread_registered_inner(void)
63✔
1505
{
1506
  GC_thread me = GC_self_thread_inner();
63✔
1507

1508
  return me != NULL && !KNOWN_FINISHED(me);
63✔
1509
}
1510

1511
/* Called before a `fork()`. */
1512
#    if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK)
1513
/* `GC_lock_holder` is updated safely (no data race actually). */
1514
GC_ATTR_NO_SANITIZE_THREAD
1515
#    endif
1516
static void
1517
fork_prepare_proc(void)
63✔
1518
{
1519
#    if defined(GC_EXPLICIT_SIGNALS_UNBLOCK) && defined(CAN_CALL_ATFORK)
1520
  /*
1521
   * The signals might be blocked by `fork()` implementation when the
1522
   * at-fork prepare handler is invoked.
1523
   */
1524
  if (GC_handle_fork == 1)
1525
    GC_unblock_gc_signals();
1526
#    endif
1527

1528
  /*
1529
   * Acquire all relevant locks, so that after releasing the locks the child
1530
   * process will see a consistent state in which monitor invariants hold.
1531
   * Unfortunately, we cannot acquire `libc` locks we might need, and there
1532
   * seems to be no guarantee that `libc` must install a suitable `fork`
1533
   * handler.  Wait for an ongoing collection to finish, since we cannot
1534
   * finish it in the (one remaining thread in) the child process.
1535
   */
1536

1537
  LOCK();
63✔
1538
  DISABLE_CANCEL(fork_cancel_state);
63✔
1539
  GC_parent_pthread_self = pthread_self();
63✔
1540
  /* The following waits may include cancellation points. */
1541
#    ifdef PARALLEL_MARK
1542
  if (GC_parallel)
63✔
1543
    wait_for_reclaim_atfork();
63✔
1544
#    endif
1545
  if (is_thread_registered_inner()) {
63✔
1546
    /* `fork()` is called from a thread registered in the collector. */
1547
    GC_wait_for_gc_completion(TRUE);
63✔
1548
  }
1549
#    ifdef PARALLEL_MARK
1550
  if (GC_parallel) {
63✔
1551
#      if defined(THREAD_SANITIZER) && defined(GC_ASSERTIONS) \
1552
          && defined(CAN_CALL_ATFORK)
1553
    /*
1554
     * Prevent TSan false positive (FP) about the data race when updating
1555
     * `GC_mark_lock_holder`.
1556
     */
1557
    GC_generic_lock(&mark_mutex);
1558
#      else
1559
    GC_acquire_mark_lock();
63✔
1560
#      endif
1561
  }
1562
#    endif
1563
  GC_acquire_dirty_lock();
1564
}
63✔
1565

1566
/*
1567
 * Called in the parent process after a `fork()` (even if the latter
1568
 * has failed).
1569
 */
1570
#    if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK)
1571
GC_ATTR_NO_SANITIZE_THREAD
1572
#    endif
1573
static void
1574
fork_parent_proc(void)
63✔
1575
{
1576
  GC_release_dirty_lock();
1577
#    ifdef PARALLEL_MARK
1578
  if (GC_parallel) {
63✔
1579
#      if defined(THREAD_SANITIZER) && defined(GC_ASSERTIONS) \
1580
          && defined(CAN_CALL_ATFORK)
1581
    /* To match that in `fork_prepare_proc`. */
1582
    (void)pthread_mutex_unlock(&mark_mutex);
1583
#      else
1584
    GC_release_mark_lock();
63✔
1585
#      endif
1586
  }
1587
#    endif
1588
  RESTORE_CANCEL(fork_cancel_state);
63✔
1589
#    ifdef GC_ASSERTIONS
1590
  BZERO(&GC_parent_pthread_self, sizeof(pthread_t));
63✔
1591
#    endif
1592
  UNLOCK();
63✔
1593
}
63✔
1594

1595
/* Called in the child process after a `fork()`. */
1596
#    if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK)
1597
GC_ATTR_NO_SANITIZE_THREAD
1598
#    endif
1599
static void
1600
fork_child_proc(void)
×
1601
{
1602
#    ifdef GC_ASSERTIONS
1603
  /*
1604
   * Update `GC_lock_holder` as value of `thread_id_self()` might differ
1605
   * from that of the parent process.
1606
   */
1607
  SET_LOCK_HOLDER();
×
1608
#    endif
1609
  GC_release_dirty_lock();
1610
#    ifndef GC_DISABLE_INCREMENTAL
1611
  GC_dirty_update_child();
×
1612
#    endif
1613
#    ifdef PARALLEL_MARK
1614
  if (GC_parallel) {
×
1615
#      ifdef GC_WIN32_THREADS
1616
    GC_release_mark_lock();
1617
#      else
1618
#        if !defined(GC_ASSERTIONS) \
1619
            || (defined(THREAD_SANITIZER) && defined(CAN_CALL_ATFORK))
1620
    /* Do not change `GC_mark_lock_holder`. */
1621
#        else
1622
    GC_mark_lock_holder = NO_THREAD;
×
1623
#        endif
1624
    /*
1625
     * The unlock operation may fail on some targets, just ignore
1626
     * the error silently.
1627
     */
1628
    (void)pthread_mutex_unlock(&mark_mutex);
×
1629
    /*
1630
     * Reinitialize the mark lock.  The reason is the same as for
1631
     * `GC_allocate_ml` below.
1632
     */
1633
    (void)pthread_mutex_destroy(&mark_mutex);
×
1634
    /* TODO: `GLIBC_2_19_TSX_BUG` has no effect. */
1635
    if (pthread_mutex_init(&mark_mutex, NULL) != 0)
×
1636
      ABORT("mark_mutex re-init failed in child");
×
1637
#      endif
1638
    /*
1639
     * Turn off parallel marking in the child process, since we are probably
1640
     * just going to exec, and we would have to restart mark threads.
1641
     */
1642
    GC_parallel = FALSE;
×
1643
  }
1644
#      ifdef THREAD_SANITIZER
1645
  /* TSan does not support threads creation in the child process. */
1646
  GC_available_markers_m1 = 0;
1647
#      endif
1648
#    endif
1649
  /* Clean up the thread table, so that just our thread is left. */
1650
  GC_remove_all_threads_but_me();
×
1651
  GC_stackbase_info_update_after_fork();
1652
  RESTORE_CANCEL(fork_cancel_state);
×
1653
#    ifdef GC_ASSERTIONS
1654
  BZERO(&GC_parent_pthread_self, sizeof(pthread_t));
×
1655
#    endif
1656
  UNLOCK();
×
1657
  /*
1658
   * Even though after a `fork()` the child process only inherits the
1659
   * single thread that called the `fork()`, if another thread in the
1660
   * parent process was attempting to lock the mutex while being held
1661
   * in `fork_child_prepare()`, the mutex will be left in
1662
   * an inconsistent state in the child process after the `UNLOCK()`.
1663
   * This is the case of macOS, at least, and it leads to an unusable
1664
   * collector in the child process which will block when attempting
1665
   * to perform any GC operation that acquires the allocator lock.
1666
   */
1667
#    if defined(USE_PTHREAD_LOCKS) && !defined(GC_WIN32_THREADS)
1668
  GC_ASSERT(I_DONT_HOLD_LOCK());
×
1669
  /*
1670
   * Reinitialize the mutex.  It should be safe since we are running
1671
   * this in the child process which only inherits a single thread.
1672
   * `pthread_mutex_destroy()` and `pthread_rwlock_destroy()` may
1673
   * return `EBUSY`, which makes no sense, but that is the reason for
1674
   * the need of the reinitialization.
1675
   * Note: excluded for Cygwin as does not seem to be needed.
1676
   */
1677
#      ifdef USE_RWLOCK
1678
  (void)pthread_rwlock_destroy(&GC_allocate_ml);
1679
#        ifdef DARWIN
1680
  /* A workaround for `pthread_rwlock_init()` fail with `EBUSY`. */
1681
  {
1682
    pthread_rwlock_t rwlock_local = PTHREAD_RWLOCK_INITIALIZER;
1683
    BCOPY(&rwlock_local, &GC_allocate_ml, sizeof(GC_allocate_ml));
1684
  }
1685
#        else
1686
  if (pthread_rwlock_init(&GC_allocate_ml, NULL) != 0)
1687
    ABORT("pthread_rwlock_init failed (in child)");
1688
#        endif
1689
#      else
1690
  (void)pthread_mutex_destroy(&GC_allocate_ml);
×
1691
  /*
1692
   * TODO: Probably some targets (e.g. with `GLIBC_2_19_TSX_BUG`) might
1693
   * need the default mutex attribute to be passed instead of `NULL`.
1694
   */
1695
  if (pthread_mutex_init(&GC_allocate_ml, NULL) != 0)
×
1696
    ABORT("pthread_mutex_init failed (in child)");
×
1697
#      endif
1698
#    endif
1699
}
×
1700

1701
/*
1702
 * Routines for `fork()` handling by client (no-op if `pthread_atfork`
1703
 * works).
1704
 */
1705

1706
GC_API void GC_CALL
1707
GC_atfork_prepare(void)
63✔
1708
{
1709
  if (UNLIKELY(!GC_is_initialized))
63✔
1710
    GC_init();
×
1711
  if (GC_handle_fork <= 0)
63✔
1712
    fork_prepare_proc();
×
1713
}
63✔
1714

1715
GC_API void GC_CALL
1716
GC_atfork_parent(void)
63✔
1717
{
1718
  if (GC_handle_fork <= 0)
63✔
1719
    fork_parent_proc();
×
1720
}
63✔
1721

1722
GC_API void GC_CALL
1723
GC_atfork_child(void)
63✔
1724
{
1725
  if (GC_handle_fork <= 0)
63✔
1726
    fork_child_proc();
×
1727
}
63✔
1728

1729
GC_INNER_WIN32THREAD void
1730
GC_setup_atfork(void)
39✔
1731
{
1732
  if (GC_handle_fork) {
39✔
1733
#    ifdef CAN_CALL_ATFORK
1734
    if (pthread_atfork(fork_prepare_proc, fork_parent_proc, fork_child_proc)
39✔
1735
        == 0) {
1736
      /* Handlers successfully registered. */
1737
      GC_handle_fork = 1;
39✔
1738
    } else
1739
#    endif
1740
    /* else */ {
1741
      if (GC_handle_fork != -1)
×
1742
        ABORT("pthread_atfork failed");
×
1743
    }
1744
  }
1745
}
39✔
1746

1747
#  endif /* CAN_HANDLE_FORK */
1748

1749
#  ifdef INCLUDE_LINUX_THREAD_DESCR
1750
__thread int GC_dummy_thread_local;
1751
#  endif
1752

1753
#  ifdef PARALLEL_MARK
1754
#    ifndef GC_WIN32_THREADS
1755
static void setup_mark_lock(void);
1756
#    endif
1757

1758
GC_INNER_WIN32THREAD unsigned GC_required_markers_cnt = 0;
1759

1760
GC_API void GC_CALL
1761
GC_set_markers_count(unsigned markers)
3✔
1762
{
1763
  GC_required_markers_cnt = markers < MAX_MARKERS ? markers : MAX_MARKERS;
3✔
1764
}
3✔
1765
#  endif /* PARALLEL_MARK */
1766

1767
GC_INNER GC_bool GC_in_thread_creation = FALSE;
1768

1769
GC_INNER_WIN32THREAD void
1770
GC_record_stack_base(GC_stack_context_t crtn, const struct GC_stack_base *sb)
100,808✔
1771
{
1772
#  if !defined(DARWIN) && !defined(GC_WIN32_THREADS)
1773
  crtn->stack_ptr = (ptr_t)sb->mem_base;
100,808✔
1774
#  endif
1775
  if ((crtn->stack_end = (ptr_t)sb->mem_base) == NULL)
100,808✔
1776
    ABORT("Bad stack base in GC_register_my_thread");
×
1777
#  ifdef E2K
1778
  crtn->ps_ofs = (size_t)(GC_uintptr_t)sb->reg_base;
1779
#  elif defined(IA64)
1780
  crtn->backing_store_end = (ptr_t)sb->reg_base;
1781
#  elif defined(I386) && defined(GC_WIN32_THREADS)
1782
  crtn->initial_stack_base = (ptr_t)sb->mem_base;
1783
#  endif
1784
}
100,808✔
1785

1786
#  if !defined(DONT_USE_ATEXIT) || defined(HAS_WIN32_THREADS_DISCOVERY)
1787
GC_INNER_WIN32THREAD thread_id_t GC_main_thread_id;
1788
#  endif
1789

1790
#  ifndef DONT_USE_ATEXIT
1791
GC_INNER GC_bool
1792
GC_is_main_thread(void)
4✔
1793
{
1794
  GC_ASSERT(GC_thr_initialized);
4✔
1795
  return THREAD_ID_EQUAL(GC_main_thread_id, thread_id_self());
4✔
1796
}
1797
#  endif /* !DONT_USE_ATEXIT */
1798

1799
#  ifndef GC_WIN32_THREADS
1800

1801
STATIC GC_thread
1802
GC_register_my_thread_inner(const struct GC_stack_base *sb,
100,591✔
1803
                            thread_id_t self_id)
1804
{
1805
  GC_thread me;
1806

1807
  GC_ASSERT(I_HOLD_LOCK());
100,591✔
1808
  me = GC_new_thread(self_id);
100,591✔
1809
  me->id = self_id;
100,591✔
1810
#    ifdef DARWIN
1811
  me->mach_thread = mach_thread_self();
1812
#    endif
1813
  GC_record_stack_base(me->crtn, sb);
100,591✔
1814
  return me;
100,591✔
1815
}
1816

1817
/*
1818
 * Number of processors.  We may not have access to all of them, but
1819
 * this is as good a guess as any...
1820
 */
1821
STATIC int GC_nprocs = 1;
1822

1823
GC_INNER void
1824
GC_thr_init(void)
39✔
1825
{
1826
  GC_ASSERT(I_HOLD_LOCK());
39✔
1827
  GC_ASSERT(!GC_thr_initialized);
39✔
1828
  ASSERT_ALIGNMENT(&GC_threads);
1829
#    ifdef GC_ASSERTIONS
1830
  GC_thr_initialized = TRUE;
39✔
1831
#    endif
1832
#    ifdef CAN_HANDLE_FORK
1833
  GC_setup_atfork();
39✔
1834
#    endif
1835

1836
#    ifdef INCLUDE_LINUX_THREAD_DESCR
1837
  /*
1838
   * Explicitly register the region including the address of a thread-local
1839
   * variable.  This should include thread locals for the main thread,
1840
   * except for those allocated in response to `dlopen()` calls.
1841
   */
1842
  {
1843
    ptr_t thread_local_addr = (ptr_t)(&GC_dummy_thread_local);
1844
    ptr_t main_thread_start, main_thread_end;
1845
    if (!GC_enclosing_writable_mapping(thread_local_addr, &main_thread_start,
1846
                                       &main_thread_end)) {
1847
      ABORT("Failed to find TLS mapping for the primordial thread");
1848
    } else {
1849
      /* `main_thread_start` and `main_thread_end` are initialized. */
1850
      GC_add_roots_inner(main_thread_start, main_thread_end, FALSE);
1851
    }
1852
  }
1853
#    endif
1854

1855
  /* Set `GC_nprocs` and `GC_available_markers_m1` variables. */
1856
  {
1857
    const char *nprocs_string = GETENV("GC_NPROCS");
39✔
1858
    GC_nprocs = -1;
39✔
1859
    if (nprocs_string != NULL)
39✔
1860
      GC_nprocs = atoi(nprocs_string);
×
1861
  }
1862
  if (GC_nprocs <= 0
39✔
1863
#    if defined(LINUX) && defined(ARM32)
1864
      /* Workaround for some Linux/arm kernels. */
1865
      && (GC_nprocs = GC_get_nprocs_present()) <= 1
1866
#    endif
1867
  ) {
1868
    GC_nprocs = GC_get_nprocs();
39✔
1869
  }
1870
  if (GC_nprocs <= 0) {
39✔
1871
    WARN("GC_get_nprocs() returned %" WARN_PRIdPTR "\n",
×
1872
         (GC_signed_word)GC_nprocs);
1873
    /* Assume a dual-core CPU. */
1874
    GC_nprocs = 2;
×
1875
#    ifdef PARALLEL_MARK
1876
    /* But use only one marker. */
1877
    GC_available_markers_m1 = 0;
×
1878
#    endif
1879
  } else {
1880
#    ifdef PARALLEL_MARK
1881
    {
1882
      const char *markers_string = GETENV("GC_MARKERS");
39✔
1883
      int markers = GC_required_markers_cnt;
39✔
1884

1885
      if (markers_string != NULL) {
39✔
1886
        markers = atoi(markers_string);
×
1887
        if (markers <= 0 || markers > MAX_MARKERS) {
×
1888
          WARN("Too big or invalid number of mark threads: %" WARN_PRIdPTR
×
1889
               "; using maximum threads\n",
1890
               (GC_signed_word)markers);
1891
          markers = MAX_MARKERS;
×
1892
        }
1893
      } else if (0 == markers) {
39✔
1894
        /*
1895
         * Unless the client sets the desired number of parallel markers,
1896
         * it is determined based on the number of CPU cores.
1897
         */
1898
        markers = GC_nprocs;
39✔
1899
#      ifdef GC_MIN_MARKERS
1900
        /* This is primarily for targets without `getenv()`. */
1901
        if (markers < GC_MIN_MARKERS)
1902
          markers = GC_MIN_MARKERS;
1903
#      endif
1904
        if (markers > MAX_MARKERS) {
39✔
1905
          /* Silently limit the amount of markers. */
1906
          markers = MAX_MARKERS;
×
1907
        }
1908
      }
1909
      GC_available_markers_m1 = markers - 1;
39✔
1910
    }
1911
#    endif
1912
  }
1913
  GC_COND_LOG_PRINTF("Number of processors: %d\n", GC_nprocs);
39✔
1914

1915
#    if defined(BASE_ATOMIC_OPS_EMULATED) && defined(SIGNAL_BASED_STOP_WORLD)
1916
  /*
1917
   * Ensure the process is running on just one CPU core.  This is needed
1918
   * because the AO primitives emulated with locks cannot be used inside
1919
   * signal handlers.
1920
   */
1921
  {
1922
    cpu_set_t mask;
1923
    int cpu_set_cnt = 0;
1924
    int cpu_lowest_set = 0;
1925
#      ifdef RANDOM_ONE_CPU_CORE
1926
    int cpu_highest_set = 0;
1927
#      endif
1928
    /* Ensure at least 2 cores. */
1929
    int i = GC_nprocs > 1 ? GC_nprocs : 2;
1930

1931
    if (sched_getaffinity(0 /* current process */, sizeof(mask), &mask) == -1)
1932
      ABORT_ARG1("sched_getaffinity failed", ": errno= %d", errno);
1933
    while (i-- > 0)
1934
      if (CPU_ISSET(i, &mask)) {
1935
#      ifdef RANDOM_ONE_CPU_CORE
1936
        if (i + 1 != cpu_lowest_set)
1937
          cpu_highest_set = i;
1938
#      endif
1939
        cpu_lowest_set = i;
1940
        cpu_set_cnt++;
1941
      }
1942
    if (0 == cpu_set_cnt)
1943
      ABORT("sched_getaffinity returned empty mask");
1944
    if (cpu_set_cnt > 1) {
1945
#      ifdef RANDOM_ONE_CPU_CORE
1946
      if (cpu_lowest_set < cpu_highest_set) {
1947
        /* Pseudo-randomly adjust the bit to set among valid ones. */
1948
        cpu_lowest_set
1949
            += (unsigned)getpid() % (cpu_highest_set - cpu_lowest_set + 1);
1950
      }
1951
#      endif
1952
      CPU_ZERO(&mask);
1953
      /* Select just one CPU. */
1954
      CPU_SET(cpu_lowest_set, &mask);
1955
      if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
1956
        ABORT_ARG1("sched_setaffinity failed", ": errno= %d", errno);
1957
      WARN("CPU affinity mask is set to %p\n", (word)1 << cpu_lowest_set);
1958
    }
1959
  }
1960
#    endif /* BASE_ATOMIC_OPS_EMULATED */
1961

1962
#    ifndef DARWIN
1963
  GC_stop_init();
39✔
1964
#    endif
1965

1966
#    ifdef PARALLEL_MARK
1967
  if (GC_available_markers_m1 <= 0) {
39✔
1968
    /* Disable parallel marking. */
1969
    GC_parallel = FALSE;
×
1970
    GC_COND_LOG_PRINTF("Single marker thread, turning off parallel marking\n");
×
1971
  } else {
1972
    setup_mark_lock();
39✔
1973
  }
1974
#    endif
1975

1976
  /* Add the initial thread, so we can stop it. */
1977
  {
1978
    struct GC_stack_base sb;
1979
    GC_thread me;
1980
    thread_id_t self_id = thread_id_self();
39✔
1981

1982
    sb.mem_base = GC_stackbottom;
39✔
1983
    GC_ASSERT(sb.mem_base != NULL);
39✔
1984
#    if defined(E2K) || defined(IA64)
1985
    sb.reg_base = GC_register_stackbottom;
1986
#    endif
1987
    GC_ASSERT(NULL == GC_self_thread_inner());
39✔
1988
    me = GC_register_my_thread_inner(&sb, self_id);
39✔
1989
#    ifndef DONT_USE_ATEXIT
1990
    GC_main_thread_id = self_id;
39✔
1991
#    endif
1992
    me->flags = DETACHED;
39✔
1993
  }
1994
}
39✔
1995

1996
#  endif /* !GC_WIN32_THREADS */
1997

1998
GC_INNER void
1999
GC_init_parallel(void)
39✔
2000
{
2001
#  ifdef THREAD_LOCAL_ALLOC
2002
  GC_thread me;
2003

2004
  GC_ASSERT(GC_is_initialized);
39✔
2005
  LOCK();
39✔
2006
  me = GC_self_thread_inner();
39✔
2007
  GC_init_thread_local(&me->tlfs);
39✔
2008
  UNLOCK();
39✔
2009
#  endif
2010
#  ifdef HAS_WIN32_THREADS_DISCOVERY
2011
  if (GC_win32_dll_threads) {
2012
    /*
2013
     * Cannot intercept thread creation.  Hence we do not know if other
2014
     * threads exist.  However, client is not allowed to create other threads
2015
     * before collector initialization.  Thus it is OK not to lock before
2016
     * this.
2017
     */
2018
    set_need_to_lock();
2019
  }
2020
#  endif
2021
}
39✔
2022

2023
#  if !defined(GC_NO_PTHREAD_SIGMASK) && defined(GC_PTHREADS)
2024
#    define GC_wrap_pthread_sigmask WRAP_FUNC(pthread_sigmask)
2025
GC_API int
2026
GC_wrap_pthread_sigmask(int how, const sigset_t *set, sigset_t *old_set)
6✔
2027
{
2028
#    ifdef GC_WIN32_THREADS
2029
  /*
2030
   * `pthreads-win32` library does not support `sigmask`.
2031
   * So, nothing is required here...
2032
   */
2033
#    else
2034
  sigset_t fudged_set;
2035

2036
  INIT_REAL_SYMS();
2037
  if (LIKELY(set != NULL) && (how == SIG_BLOCK || how == SIG_SETMASK)) {
6✔
2038
    int sig_suspend = GC_get_suspend_signal();
3✔
2039

2040
    fudged_set = *set;
3✔
2041
    GC_ASSERT(sig_suspend >= 0);
3✔
2042
    if (sigdelset(&fudged_set, sig_suspend) != 0)
3✔
2043
      ABORT("sigdelset failed");
×
2044
    set = &fudged_set;
3✔
2045
  }
2046
#    endif
2047
  return REAL_FUNC(pthread_sigmask)(how, set, old_set);
6✔
2048
}
2049
#    undef GC_wrap_pthread_sigmask
2050
#  endif /* !GC_NO_PTHREAD_SIGMASK */
2051

2052
/*
2053
 * Wrapper for functions that are likely to block for an appreciable
2054
 * length of time.
2055
 */
2056

2057
#  ifdef E2K
2058
/*
2059
 * Cannot be defined as a function because the stack-allocated buffer
2060
 * (pointed to by `bs_lo`) should be preserved till completion of
2061
 * `GC_do_blocking_inner` (or `GC_suspend_self_blocked`).
2062
 */
2063
#    define do_blocking_enter(pTopOfStackUnset, me)                   \
2064
      do {                                                            \
2065
        ptr_t bs_lo;                                                  \
2066
        size_t stack_size;                                            \
2067
        GC_stack_context_t crtn = (me)->crtn;                         \
2068
                                                                      \
2069
        *(pTopOfStackUnset) = FALSE;                                  \
2070
        crtn->stack_ptr = GC_approx_sp();                             \
2071
        GC_ASSERT(NULL == crtn->backing_store_end);                   \
2072
        GET_PROCEDURE_STACK_LOCAL(crtn->ps_ofs, &bs_lo, &stack_size); \
2073
        crtn->backing_store_end = bs_lo;                              \
2074
        crtn->backing_store_ptr = bs_lo + stack_size;                 \
2075
        (me)->flags |= DO_BLOCKING;                                   \
2076
      } while (0)
2077

2078
#  else /* !E2K */
2079
static void
2080
do_blocking_enter(GC_bool *pTopOfStackUnset, GC_thread me)
1,449✔
2081
{
2082
#    if defined(SPARC) || defined(IA64)
2083
  ptr_t bs_hi = GC_save_regs_in_stack();
2084
  /* TODO: Registers saving already done by `GC_with_callee_saves_pushed`. */
2085
#    endif
2086
  GC_stack_context_t crtn = me->crtn;
1,449✔
2087

2088
  GC_ASSERT(I_HOLD_READER_LOCK());
1,449✔
2089
  GC_ASSERT((me->flags & DO_BLOCKING) == 0);
1,449✔
2090
  *pTopOfStackUnset = FALSE;
1,449✔
2091
#    ifdef SPARC
2092
  crtn->stack_ptr = bs_hi;
2093
#    else
2094
  crtn->stack_ptr = GC_approx_sp();
1,449✔
2095
#    endif
2096
#    if defined(DARWIN) && defined(DARWIN_PARSE_STACK)
2097
  if (NULL == crtn->topOfStack) {
2098
    /*
2099
     * `GC_do_blocking_inner` is not called recursively, so `topOfStack`
2100
     * should be computed now.
2101
     */
2102
    *pTopOfStackUnset = TRUE;
2103
    crtn->topOfStack = GC_FindTopOfStack(0);
2104
  }
2105
#    endif
2106
#    ifdef IA64
2107
  crtn->backing_store_ptr = bs_hi;
2108
#    endif
2109
  me->flags |= DO_BLOCKING;
1,449✔
2110
  /* Save context here if we want to support precise stack marking. */
2111
}
1,449✔
2112
#  endif /* !E2K */
2113

2114
static void
2115
do_blocking_leave(GC_thread me, GC_bool topOfStackUnset)
1,449✔
2116
{
2117
  GC_ASSERT(I_HOLD_READER_LOCK());
1,449✔
2118
  me->flags &= (unsigned char)~DO_BLOCKING;
1,449✔
2119
#  ifdef E2K
2120
  {
2121
    GC_stack_context_t crtn = me->crtn;
2122

2123
    GC_ASSERT(crtn->backing_store_end != NULL);
2124
    crtn->backing_store_ptr = NULL;
2125
    crtn->backing_store_end = NULL;
2126
  }
2127
#  endif
2128
#  if defined(DARWIN) && defined(DARWIN_PARSE_STACK)
2129
  if (topOfStackUnset) {
2130
    /* Make it unset again. */
2131
    me->crtn->topOfStack = NULL;
2132
  }
2133
#  else
2134
  (void)topOfStackUnset;
2135
#  endif
2136
}
1,449✔
2137

2138
GC_INNER void
2139
GC_do_blocking_inner(ptr_t data, void *context)
126✔
2140
{
2141
  GC_thread me;
2142
  GC_bool topOfStackUnset;
2143

2144
  UNUSED_ARG(context);
2145
  READER_LOCK();
126✔
2146
  me = GC_self_thread_inner();
126✔
2147
  do_blocking_enter(&topOfStackUnset, me);
126✔
2148
  READER_UNLOCK_RELEASE();
126✔
2149

2150
  ((struct blocking_data *)data)->client_data /*< result */
2151
      = ((struct blocking_data *)data)
252✔
2152
            ->fn(((struct blocking_data *)data)->client_data);
126✔
2153

2154
  /* This will block if the world is stopped. */
2155
  READER_LOCK();
126✔
2156

2157
#  ifdef LINT2
2158
  {
2159
#    ifdef GC_ASSERTIONS
2160
    GC_thread saved_me = me;
2161
#    endif
2162

2163
    /*
2164
     * The pointer to the GC thread descriptor should not be changed while
2165
     * the thread is registered but a static analysis tool might complain
2166
     * that this pointer value (obtained in the first locked section) is
2167
     * unreliable in the second locked section.
2168
     */
2169
    me = GC_self_thread_inner();
2170
    GC_ASSERT(me == saved_me);
2171
  }
2172
#  endif
2173
#  if defined(GC_ENABLE_SUSPEND_THREAD) && defined(SIGNAL_BASED_STOP_WORLD)
2174
  /*
2175
   * Note: this code cannot be moved into `do_blocking_leave()` otherwise
2176
   * there could be a static analysis tool warning (false positive) about
2177
   * unlock without a matching lock.
2178
   */
2179
  while (UNLIKELY((me->ext_suspend_cnt & 1) != 0)) {
252✔
2180
    /* Read suspend counter (number) before unlocking. */
2181
    size_t suspend_cnt = me->ext_suspend_cnt;
×
2182

2183
    READER_UNLOCK_RELEASE();
×
2184
    GC_suspend_self_inner(me, suspend_cnt);
×
2185
    READER_LOCK();
×
2186
  }
2187
#  endif
2188
  do_blocking_leave(me, topOfStackUnset);
126✔
2189
  READER_UNLOCK_RELEASE();
126✔
2190
}
126✔
2191

2192
#  if defined(GC_ENABLE_SUSPEND_THREAD) && defined(SIGNAL_BASED_STOP_WORLD)
2193
GC_INNER void
2194
GC_suspend_self_blocked(ptr_t thread_me, void *context)
1,323✔
2195
{
2196
  GC_thread me = (GC_thread)thread_me;
1,323✔
2197
  GC_bool topOfStackUnset;
2198

2199
  UNUSED_ARG(context);
2200

2201
  /*
2202
   * The caller holds the allocator lock in the exclusive mode, thus
2203
   * we require and restore it to the same mode upon return from the
2204
   * function.
2205
   */
2206
  GC_ASSERT(I_HOLD_LOCK());
1,323✔
2207

2208
  do_blocking_enter(&topOfStackUnset, me);
1,323✔
2209
  while ((me->ext_suspend_cnt & 1) != 0) {
3,969✔
2210
    size_t suspend_cnt = me->ext_suspend_cnt;
1,323✔
2211

2212
    UNLOCK();
1,323✔
2213
    GC_suspend_self_inner(me, suspend_cnt);
1,323✔
2214
    LOCK();
1,323✔
2215
  }
2216
  do_blocking_leave(me, topOfStackUnset);
1,323✔
2217
}
1,323✔
2218
#  endif /* GC_ENABLE_SUSPEND_THREAD */
2219

2220
GC_API void GC_CALL
2221
GC_set_stackbottom(void *gc_thread_handle, const struct GC_stack_base *sb)
63✔
2222
{
2223
  GC_thread t = (GC_thread)gc_thread_handle;
63✔
2224
  GC_stack_context_t crtn;
2225

2226
  GC_ASSERT(sb->mem_base != NULL);
63✔
2227
  if (UNLIKELY(!GC_is_initialized)) {
63✔
2228
    GC_ASSERT(NULL == t);
×
2229
    /* Alter the stack bottom of the primordial thread. */
2230
    GC_stackbottom = (char *)sb->mem_base;
×
2231
#  if defined(E2K) || defined(IA64)
2232
    GC_register_stackbottom = (ptr_t)sb->reg_base;
2233
#  endif
2234
    return;
×
2235
  }
2236

2237
  GC_ASSERT(I_HOLD_READER_LOCK());
63✔
2238
  if (NULL == t) {
63✔
2239
    /* The current thread. */
2240
    t = GC_self_thread_inner();
×
2241
  }
2242
  GC_ASSERT(!KNOWN_FINISHED(t));
63✔
2243
  crtn = t->crtn;
63✔
2244
  GC_ASSERT((t->flags & DO_BLOCKING) == 0
63✔
2245
            && NULL == crtn->traced_stack_sect); /*< for now */
2246

2247
  crtn->stack_end = (ptr_t)sb->mem_base;
63✔
2248
#  ifdef E2K
2249
  crtn->ps_ofs = (size_t)(GC_uintptr_t)sb->reg_base;
2250
#  elif defined(IA64)
2251
  crtn->backing_store_end = (ptr_t)sb->reg_base;
2252
#  endif
2253
#  ifdef GC_WIN32_THREADS
2254
  /* Reset the known minimum (hottest address in the stack). */
2255
  crtn->last_stack_min = ADDR_LIMIT;
2256
#  endif
2257
}
2258

2259
GC_API void *GC_CALL
2260
GC_get_my_stackbottom(struct GC_stack_base *sb)
63✔
2261
{
2262
  GC_thread me;
2263
  GC_stack_context_t crtn;
2264

2265
  READER_LOCK();
63✔
2266
  me = GC_self_thread_inner();
63✔
2267
  /* The thread is assumed to be registered. */
2268
  crtn = me->crtn;
63✔
2269
  sb->mem_base = crtn->stack_end;
63✔
2270
#  ifdef E2K
2271
  /* Store the offset in the procedure stack, not address. */
2272
  sb->reg_base = NUMERIC_TO_VPTR(crtn->ps_ofs);
2273
#  elif defined(IA64)
2274
  sb->reg_base = crtn->backing_store_end;
2275
#  endif
2276
  READER_UNLOCK();
63✔
2277
  return me; /*< `gc_thread_handle` */
63✔
2278
}
2279

2280
GC_ATTR_NOINLINE
2281
GC_API void *GC_CALL
2282
GC_call_with_gc_active(GC_fn_type fn, void *client_data)
126✔
2283
{
2284
  struct GC_traced_stack_sect_s stacksect;
2285
  GC_thread me;
2286
  GC_stack_context_t crtn;
2287
  ptr_t stack_end;
2288
#  ifdef E2K
2289
  ptr_t saved_bs_ptr, saved_bs_end;
2290
  size_t saved_ps_ofs;
2291
#  endif
2292

2293
  /* This will block if the world is stopped. */
2294
  READER_LOCK();
126✔
2295

2296
  me = GC_self_thread_inner();
126✔
2297
  crtn = me->crtn;
126✔
2298

2299
  /*
2300
   * Adjust our stack bottom value (this could happen unless
2301
   * `GC_get_stack_base()` was used which returned `GC_SUCCESS`).
2302
   */
2303
  stack_end = crtn->stack_end; /*< read of a `volatile` field */
126✔
2304
  GC_ASSERT(stack_end != NULL);
126✔
2305
  STORE_APPROX_SP_TO(*(volatile ptr_t *)&stacksect.saved_stack_ptr);
126✔
2306
  if (HOTTER_THAN(stack_end, stacksect.saved_stack_ptr)) {
126✔
2307
    crtn->stack_end = stacksect.saved_stack_ptr;
×
2308
#  if defined(I386) && defined(GC_WIN32_THREADS)
2309
    crtn->initial_stack_base = stacksect.saved_stack_ptr;
2310
#  endif
2311
  }
2312

2313
  if ((me->flags & DO_BLOCKING) == 0) {
126✔
2314
    /* We are not inside `GC_do_blocking()` - do nothing more. */
2315
    READER_UNLOCK_RELEASE();
×
2316
    /* Cast `fn` to a `volatile` type to prevent its call inlining. */
2317
    client_data = (*(GC_fn_type volatile *)&fn)(client_data);
×
2318
    /* Prevent treating the above as a tail call. */
2319
    GC_noop1(COVERT_DATAFLOW(ADDR(&stacksect)));
×
2320
    return client_data; /*< result */
126✔
2321
  }
2322

2323
#  if defined(GC_ENABLE_SUSPEND_THREAD) && defined(SIGNAL_BASED_STOP_WORLD)
2324
  while (UNLIKELY((me->ext_suspend_cnt & 1) != 0)) {
126✔
2325
    size_t suspend_cnt = me->ext_suspend_cnt;
×
2326

2327
    READER_UNLOCK_RELEASE();
×
2328
    GC_suspend_self_inner(me, suspend_cnt);
×
2329
    READER_LOCK();
×
2330
    GC_ASSERT(me->crtn == crtn);
×
2331
  }
2332
#  endif
2333

2334
  /* Setup new "stack section". */
2335
  stacksect.saved_stack_ptr = crtn->stack_ptr;
126✔
2336
#  ifdef E2K
2337
  GC_ASSERT(crtn->backing_store_end != NULL);
2338
  {
2339
    unsigned long long sz_ull;
2340

2341
    GET_PROCEDURE_STACK_SIZE_INNER(&sz_ull);
2342
    saved_ps_ofs = crtn->ps_ofs;
2343
    GC_ASSERT(saved_ps_ofs <= (size_t)sz_ull);
2344
    crtn->ps_ofs = (size_t)sz_ull;
2345
  }
2346
  saved_bs_end = crtn->backing_store_end;
2347
  saved_bs_ptr = crtn->backing_store_ptr;
2348
  crtn->backing_store_ptr = NULL;
2349
  crtn->backing_store_end = NULL;
2350
#  elif defined(IA64)
2351
  /* This is the same as in `GC_call_with_stack_base()`. */
2352
  stacksect.backing_store_end = GC_save_regs_in_stack();
2353
  /*
2354
   * Unnecessarily flushes the register stack, but that probably does
2355
   * not hurt.
2356
   */
2357
  stacksect.saved_backing_store_ptr = crtn->backing_store_ptr;
2358
#  endif
2359
  stacksect.prev = crtn->traced_stack_sect;
126✔
2360
  me->flags &= (unsigned char)~DO_BLOCKING;
126✔
2361
  crtn->traced_stack_sect = &stacksect;
126✔
2362

2363
  READER_UNLOCK_RELEASE();
126✔
2364
  client_data = (*(GC_fn_type volatile *)&fn)(client_data);
126✔
2365
  GC_ASSERT((me->flags & DO_BLOCKING) == 0);
126✔
2366

2367
  /* Restore original "stack section". */
2368
  READER_LOCK();
126✔
2369
  GC_ASSERT(me->crtn == crtn);
126✔
2370
  GC_ASSERT(crtn->traced_stack_sect == &stacksect);
126✔
2371
  crtn->traced_stack_sect = stacksect.prev;
126✔
2372
#  ifdef E2K
2373
  GC_ASSERT(NULL == crtn->backing_store_end);
2374
  crtn->backing_store_end = saved_bs_end;
2375
  crtn->backing_store_ptr = saved_bs_ptr;
2376
  crtn->ps_ofs = saved_ps_ofs;
2377
#  elif defined(IA64)
2378
  crtn->backing_store_ptr = stacksect.saved_backing_store_ptr;
2379
#  endif
2380
  me->flags |= DO_BLOCKING;
126✔
2381
  crtn->stack_ptr = stacksect.saved_stack_ptr;
126✔
2382
  READER_UNLOCK_RELEASE();
126✔
2383
  return client_data; /*< result */
126✔
2384
}
2385

2386
STATIC void
2387
GC_unregister_my_thread_inner(GC_thread me)
100,504✔
2388
{
2389
  GC_ASSERT(I_HOLD_LOCK());
100,504✔
2390
#  ifdef DEBUG_THREADS
2391
  GC_log_printf("Unregistering thread %p, gc_thread= %p, n_threads= %d\n",
2392
                THREAD_ID_TO_VPTR(me->id), (void *)me, GC_count_threads());
2393
#  endif
2394
  GC_ASSERT(!KNOWN_FINISHED(me));
100,504✔
2395
#  if defined(THREAD_LOCAL_ALLOC)
2396
  GC_destroy_thread_local(&me->tlfs);
100,504✔
2397
#  endif
2398
#  ifdef NACL
2399
  GC_nacl_shutdown_gc_thread();
2400
#  endif
2401
#  ifdef GC_PTHREADS
2402
#    if defined(GC_HAVE_PTHREAD_EXIT) || !defined(GC_NO_PTHREAD_CANCEL)
2403
  /*
2404
   * Handle `DISABLED_GC` flag which is set by the intercepted
2405
   * `pthread_cancel()` or `pthread_exit()`.
2406
   */
2407
  if ((me->flags & DISABLED_GC) != 0)
100,504✔
2408
    GC_enable_inner();
1,770✔
2409
#    endif
2410
  if ((me->flags & DETACHED) == 0) {
100,504✔
2411
    me->flags |= FINISHED;
3,539✔
2412
  } else
2413
#  endif
2414
  /* else */ {
2415
    GC_delete_thread(me);
96,965✔
2416
  }
2417
#  if defined(THREAD_LOCAL_ALLOC)
2418
  /*
2419
   * It is required to call `GC_remove_specific()` defined in
2420
   * `specific.c` file.
2421
   */
2422
  GC_remove_specific(GC_thread_key);
100,504✔
2423
#  endif
2424
}
100,504✔
2425

2426
GC_API int GC_CALL
2427
GC_unregister_my_thread(void)
48,431✔
2428
{
2429
  GC_thread me;
2430
  IF_CANCEL(int cancel_state;)
2431

2432
  /*
2433
   * Client should not unregister the thread explicitly
2434
   * if it is registered by `DllMain`, except for the main thread.
2435
   */
2436
#  ifdef HAS_WIN32_THREADS_DISCOVERY
2437
  GC_ASSERT(!GC_win32_dll_threads
2438
            || THREAD_ID_EQUAL(GC_main_thread_id, thread_id_self()));
2439
#  endif
2440

2441
  LOCK();
48,431✔
2442
  DISABLE_CANCEL(cancel_state);
48,431✔
2443
  /*
2444
   * Wait for any collection that may be marking from our stack to complete
2445
   * before we remove this thread.
2446
   */
2447
  GC_wait_for_gc_completion(FALSE);
48,431✔
2448
  me = GC_self_thread_inner();
48,431✔
2449
  GC_ASSERT(THREAD_ID_EQUAL(me->id, thread_id_self()));
48,431✔
2450
  GC_unregister_my_thread_inner(me);
48,431✔
2451
  RESTORE_CANCEL(cancel_state);
48,431✔
2452
  UNLOCK();
48,431✔
2453
  return GC_SUCCESS;
48,431✔
2454
}
2455

2456
#  if (!defined(GC_NO_PTHREAD_CANCEL) && defined(GC_PTHREADS) \
2457
       && defined(CANCEL_SAFE))                               \
2458
      || defined(GC_HAVE_PTHREAD_EXIT)
2459
/*
2460
 * Disables collection and sets the corresponding flag for the given thread.
2461
 * Does nothing if the argument is `NULL` or the flag (`DISABLED_GC`)
2462
 * is already set.
2463
 */
2464
static void
2465
disable_gc_for_pthread(GC_thread t)
2,205✔
2466
{
2467
  if (t != NULL && (t->flags & DISABLED_GC) == 0) {
2,205✔
2468
    t->flags |= DISABLED_GC;
1,770✔
2469
    GC_disable_inner();
1,770✔
2470
  }
2471
}
2,205✔
2472
#  endif
2473

2474
#  if !defined(GC_NO_PTHREAD_CANCEL) && defined(GC_PTHREADS)
2475
/*
2476
 * We should deal with the fact that apparently on Solaris and, probably,
2477
 * on some Linux we cannot collect while a thread is exiting, since
2478
 * signals are not handled properly.  This currently gives rise to deadlocks.
2479
 * The only workaround seen is to intercept `pthread_cancel()` and
2480
 * `pthread_exit()`, and disable the collections until the thread exit
2481
 * handler is called.  That is ugly, because we risk growing the heap
2482
 * unnecessarily.  But it seems that we do not really have an option in that
2483
 * the process is not in a fully functional state while a thread is exiting.
2484
 */
2485
#    define GC_wrap_pthread_cancel WRAP_FUNC(pthread_cancel)
2486
GC_API int
2487
GC_wrap_pthread_cancel(pthread_t thread)
882✔
2488
{
2489
  INIT_REAL_SYMS();
2490
#    ifdef CANCEL_SAFE
2491
  LOCK();
882✔
2492
  disable_gc_for_pthread(GC_lookup_by_pthread(thread));
882✔
2493
  /* Note: if not found, then real `pthread_cancel` should return `ESRCH`. */
2494
  UNLOCK();
882✔
2495
#    endif
2496
  return REAL_FUNC(pthread_cancel)(thread);
882✔
2497
}
2498
#    undef GC_wrap_pthread_cancel
2499
#  endif /* !GC_NO_PTHREAD_CANCEL */
2500

2501
#  ifdef GC_HAVE_PTHREAD_EXIT
2502
#    define GC_wrap_pthread_exit WRAP_FUNC(pthread_exit)
2503
GC_API GC_PTHREAD_EXIT_ATTRIBUTE void
2504
GC_wrap_pthread_exit(void *retval)
1,323✔
2505
{
2506
  INIT_REAL_SYMS();
2507
  LOCK();
1,323✔
2508
  disable_gc_for_pthread(GC_self_thread_inner());
1,323✔
2509
  UNLOCK();
1,323✔
2510
  REAL_FUNC(pthread_exit)(retval);
1,323✔
2511
}
2512
#    undef GC_wrap_pthread_exit
2513
#  endif /* GC_HAVE_PTHREAD_EXIT */
2514

2515
GC_API void GC_CALL
2516
GC_allow_register_threads(void)
2✔
2517
{
2518
  /*
2519
   * Check the collector is initialized and the current thread is
2520
   * registered.
2521
   */
2522
  GC_ASSERT(GC_self_thread() != NULL);
2✔
2523

2524
  /* Initialize symbols while still single-threaded. */
2525
  INIT_REAL_SYMS();
2526

2527
  GC_init_lib_bounds();
2528
  GC_start_mark_threads();
2✔
2529
  set_need_to_lock();
2✔
2530
}
2✔
2531

2532
#  if defined(PTHREAD_STOP_WORLD_IMPL)            \
2533
          && !defined(NO_SIGNALS_UNBLOCK_IN_MAIN) \
2534
      || defined(GC_EXPLICIT_SIGNALS_UNBLOCK)
2535
GC_INNER void
2536
GC_unblock_gc_signals(void)
39✔
2537
{
2538
  sigset_t set;
2539

2540
  /* This is for `pthread_sigmask`. */
2541
  INIT_REAL_SYMS();
2542

2543
  sigemptyset(&set);
39✔
2544
  sigaddset(&set, GC_get_suspend_signal());
39✔
2545
  sigaddset(&set, GC_get_thr_restart_signal());
39✔
2546
  if (REAL_FUNC(pthread_sigmask)(SIG_UNBLOCK, &set, NULL) != 0)
39✔
2547
    ABORT("pthread_sigmask failed");
×
2548
}
39✔
2549
#  endif /* PTHREAD_STOP_WORLD_IMPL || GC_EXPLICIT_SIGNALS_UNBLOCK */
2550

2551
GC_API int GC_CALL
2552
GC_register_my_thread(const struct GC_stack_base *sb)
48,695✔
2553
{
2554
  GC_thread me;
2555

2556
#  ifdef GC_ALWAYS_MULTITHREADED
2557
  set_need_to_lock();
2558
#  else
2559
  if (!GC_need_to_lock)
48,695✔
2560
    ABORT("Threads explicit registering is not previously enabled");
×
2561
#  endif
2562

2563
  /* We lock here, since we want to wait for an ongoing GC. */
2564
  LOCK();
48,695✔
2565
  me = GC_self_thread_inner();
48,695✔
2566
  if (LIKELY(NULL == me)) {
48,695✔
2567
    me = GC_register_my_thread_inner(sb, thread_id_self());
48,478✔
2568
#  ifdef GC_PTHREADS
2569
    /*
2570
     * Treat as detached, since we do not need to worry about pointer
2571
     * results.
2572
     */
2573
    me->flags |= DETACHED;
48,478✔
2574
#  else
2575
    (void)me;
2576
#  endif
2577
  } else {
2578
#  ifdef GC_PTHREADS
2579
    if (KNOWN_FINISHED(me)) {
217✔
2580
      /*
2581
       * This code is executed when a thread is registered from the destructor
2582
       * of the client thread key.
2583
       */
2584
#    ifdef NACL
2585
      GC_nacl_initialize_gc_thread(me);
2586
#    endif
2587
#    ifdef DARWIN
2588
      /*
2589
       * Reinitialize `mach_thread` to avoid `thread_suspend()` fail
2590
       * with `MACH_SEND_INVALID_DEST` error.
2591
       */
2592
      me->mach_thread = mach_thread_self();
2593
#    endif
2594
      GC_record_stack_base(me->crtn, sb);
217✔
2595
      me->flags &= (unsigned char)~FINISHED; /*< but not `DETACHED` */
217✔
2596
    } else
2597
#  endif
2598
    /* else */ {
2599
      UNLOCK();
×
2600
      return GC_DUPLICATE;
×
2601
    }
2602
  }
2603

2604
#  ifdef THREAD_LOCAL_ALLOC
2605
  GC_init_thread_local(&me->tlfs);
48,695✔
2606
#  endif
2607
#  ifdef GC_EXPLICIT_SIGNALS_UNBLOCK
2608
  /*
2609
   * Since this could be executed from a thread destructor,
2610
   * our signals might already be blocked.
2611
   */
2612
  GC_unblock_gc_signals();
2613
#  endif
2614
#  if defined(GC_ENABLE_SUSPEND_THREAD) && defined(SIGNAL_BASED_STOP_WORLD)
2615
  if (UNLIKELY((me->ext_suspend_cnt & 1) != 0)) {
48,695✔
2616
    GC_with_callee_saves_pushed(GC_suspend_self_blocked, (ptr_t)me);
×
2617
  }
2618
#  endif
2619
  UNLOCK();
48,695✔
2620
  return GC_SUCCESS;
48,695✔
2621
}
2622

2623
#  if defined(GC_PTHREADS) && !defined(PLATFORM_THREADS) \
2624
      && !defined(SN_TARGET_PSP2)
2625

2626
GC_INNER_PTHRSTART void
2627
GC_thread_exit_proc(void *arg)
52,072✔
2628
{
2629
  GC_thread me = (GC_thread)arg;
52,072✔
2630
  IF_CANCEL(int cancel_state;)
2631

2632
#    ifdef DEBUG_THREADS
2633
  GC_log_printf("Called GC_thread_exit_proc on %p, gc_thread= %p\n",
2634
                THREAD_ID_TO_VPTR(me->id), (void *)me);
2635
#    endif
2636
  LOCK();
52,072✔
2637
  DISABLE_CANCEL(cancel_state);
52,072✔
2638
  GC_wait_for_gc_completion(FALSE);
52,072✔
2639
  GC_unregister_my_thread_inner(me);
52,072✔
2640
  RESTORE_CANCEL(cancel_state);
52,072✔
2641
  UNLOCK();
52,072✔
2642
}
52,072✔
2643

2644
#    define GC_wrap_pthread_join WRAP_FUNC(pthread_join)
2645
GC_API int
2646
GC_wrap_pthread_join(pthread_t thread, void **retval)
3,207✔
2647
{
2648
  int result;
2649
  GC_thread t;
2650

2651
  INIT_REAL_SYMS();
2652
#    ifdef DEBUG_THREADS
2653
  GC_log_printf("thread %p is joining thread %p\n",
2654
                PTHREAD_TO_VPTR(pthread_self()), PTHREAD_TO_VPTR(thread));
2655
#    endif
2656

2657
  /* After the join, thread id may have been recycled. */
2658
  READER_LOCK();
3,207✔
2659
  t = (GC_thread)COVERT_DATAFLOW_P(GC_lookup_by_pthread(thread));
3,207✔
2660
  /*
2661
   * This is guaranteed to be the intended one, since the thread id
2662
   * cannot have been recycled by `pthreads`.
2663
   */
2664
  READER_UNLOCK();
3,207✔
2665

2666
  result = REAL_FUNC(pthread_join)(thread, retval);
3,207✔
2667
#    ifdef FREEBSD
2668
  /*
2669
   * On FreeBSD, the wrapped `pthread_join()` sometimes returns (what
2670
   * appears to be) a spurious `EINTR` which caused the test and real code
2671
   * to fail gratuitously.  Having looked at system `pthreads` library
2672
   * source code, I see how such return code value may be generated:
2673
   * in one path of the code, `pthread_join()` just returns the `errno`
2674
   * setting of the thread being joined - this does not match the POSIX
2675
   * specification or the local man pages.  Thus, I have taken the liberty
2676
   * to catch this one spurious return value.
2677
   */
2678
  if (UNLIKELY(result == EINTR))
2679
    result = 0;
2680
#    endif
2681

2682
  if (LIKELY(0 == result)) {
3,207✔
2683
    LOCK();
3,207✔
2684
    /*
2685
     * Here the `pthread_id` may have been recycled.  Delete the thread from
2686
     *`GC_threads` (unless it has been registered again from the destructor
2687
     * of the client thread key).
2688
     */
2689
    if (KNOWN_FINISHED(t)) {
3,207✔
2690
      GC_delete_thread(t);
3,207✔
2691
    }
2692
    UNLOCK();
3,207✔
2693
  }
2694

2695
#    ifdef DEBUG_THREADS
2696
  GC_log_printf("thread %p join with thread %p %s\n",
2697
                PTHREAD_TO_VPTR(pthread_self()), PTHREAD_TO_VPTR(thread),
2698
                result != 0 ? "failed" : "succeeded");
2699
#    endif
2700
  return result;
3,207✔
2701
}
2702
#    undef GC_wrap_pthread_join
2703

2704
#    define GC_wrap_pthread_detach WRAP_FUNC(pthread_detach)
2705
GC_API int
2706
GC_wrap_pthread_detach(pthread_t thread)
410✔
2707
{
2708
  int result;
2709
  GC_thread t;
2710

2711
  INIT_REAL_SYMS();
2712
  if (UNLIKELY(!GC_is_initialized)) {
410✔
2713
    /*
2714
     * The only case this seems to be needed is when the client calls
2715
     * `pthread_detach(pthread_self())` before the collector initialization.
2716
     */
2717
    GC_init();
×
2718
  }
2719
  READER_LOCK();
410✔
2720
  t = (GC_thread)COVERT_DATAFLOW_P(GC_lookup_by_pthread(thread));
410✔
2721
  READER_UNLOCK();
410✔
2722
  result = REAL_FUNC(pthread_detach)(thread);
410✔
2723
  if (LIKELY(0 == result)) {
410✔
2724
    LOCK();
410✔
2725
    /* Here the `pthread_id` may have been recycled. */
2726
    if (KNOWN_FINISHED(t)) {
410✔
2727
      GC_delete_thread(t);
115✔
2728
    } else {
2729
      t->flags |= DETACHED;
295✔
2730
    }
2731
    UNLOCK();
410✔
2732
  }
2733
  return result;
410✔
2734
}
2735
#    undef GC_wrap_pthread_detach
2736

2737
struct start_info {
2738
  void *(*start_routine)(void *);
2739
  void *arg;
2740
  /*
2741
   * Note: a value of 1 means the thread is in our thread table, but
2742
   * the parent process has not noticed it yet.
2743
   */
2744
  sem_t registered;
2745
  unsigned char flags;
2746
};
2747

2748
GC_INNER_PTHRSTART GC_thread
2749
GC_start_rtn_prepare_thread(void *(**pstart)(void *), void **pstart_arg,
52,072✔
2750
                            struct GC_stack_base *sb, void *arg)
2751
{
2752
  struct start_info *psi = (struct start_info *)arg;
52,072✔
2753
  thread_id_t self_id = thread_id_self();
52,072✔
2754
  GC_thread me;
2755

2756
#    ifdef DEBUG_THREADS
2757
  GC_log_printf("Starting thread %p, sp= %p\n",
2758
                PTHREAD_TO_VPTR(pthread_self()), (void *)GC_approx_sp());
2759
#    endif
2760
  /*
2761
   * If a collection occurs before the thread is registered, that collection
2762
   * will ignore this thread.  That is fine, since it will block trying to
2763
   * acquire the allocator lock, and will not yet hold interesting pointers.
2764
   */
2765
  LOCK();
52,072✔
2766
  /*
2767
   * We register the thread here instead of in the parent process, so that
2768
   * we do not need to hold the allocator lock during `pthread_create()`
2769
   * call.
2770
   */
2771
  me = GC_register_my_thread_inner(sb, self_id);
52,072✔
2772
  GC_ASSERT(me != &first_thread);
52,072✔
2773
  me->flags = psi->flags;
52,072✔
2774
#    ifdef GC_WIN32_THREADS
2775
  GC_win32_cache_self_pthread(self_id);
2776
#    endif
2777
#    ifdef THREAD_LOCAL_ALLOC
2778
  GC_init_thread_local(&me->tlfs);
52,072✔
2779
#    endif
2780
  UNLOCK();
52,072✔
2781

2782
  *pstart = psi->start_routine;
52,072✔
2783
  *pstart_arg = psi->arg;
52,072✔
2784
#    if defined(DEBUG_THREADS) && defined(FUNCPTR_IS_DATAPTR)
2785
  GC_log_printf("start_routine= %p\n", CAST_THRU_UINTPTR(void *, *pstart));
2786
#    endif
2787
  sem_post(&psi->registered);
52,072✔
2788
  /* This was the last action on `*psi`; OK to deallocate. */
2789
  return me;
52,072✔
2790
}
2791

2792
STATIC void *
2793
GC_pthread_start(void *arg)
52,072✔
2794
{
2795
#    ifdef INCLUDE_LINUX_THREAD_DESCR
2796
  struct GC_stack_base sb;
2797

2798
#      ifdef REDIRECT_MALLOC
2799
  /*
2800
   * `GC_get_stack_base()` may call `pthread_getattr_np()`, which
2801
   * can unfortunately call `realloc()`, which may allocate from
2802
   * an unregistered thread.  This is unpleasant, since it might
2803
   * force heap growth (or, even, heap overflow).
2804
   */
2805
  GC_disable();
2806
#      endif
2807
  if (GC_get_stack_base(&sb) != GC_SUCCESS)
2808
    ABORT("Failed to get thread stack base");
2809
#      ifdef REDIRECT_MALLOC
2810
  GC_enable();
2811
#      endif
2812
  return GC_pthread_start_inner(&sb, arg);
2813
#    else
2814
  return GC_call_with_stack_base(GC_pthread_start_inner, arg);
52,072✔
2815
#    endif
2816
}
2817

2818
#    define GC_wrap_pthread_create WRAP_FUNC(pthread_create)
2819
GC_API int
2820
GC_wrap_pthread_create(pthread_t *new_thread,
52,311✔
2821
                       GC_PTHREAD_CREATE_CONST pthread_attr_t *attr,
2822
                       void *(*start_routine)(void *), void *arg)
2823
{
2824
  int result;
2825
  struct start_info si;
2826

2827
  GC_ASSERT(I_DONT_HOLD_LOCK());
52,311✔
2828
  INIT_REAL_SYMS();
2829
  if (UNLIKELY(!GC_is_initialized))
52,311✔
2830
    GC_init();
×
2831
  GC_ASSERT(GC_thr_initialized);
52,311✔
2832
  GC_ASSERT(NONNULL_PROC_NOT_ZERO(start_routine));
2833

2834
  GC_init_lib_bounds();
2835
  if (sem_init(&si.registered, GC_SEM_INIT_PSHARED, 0) == -1)
52,311✔
2836
    ABORT("sem_init failed");
×
2837
  si.flags = 0;
52,311✔
2838
  si.start_routine = start_routine;
52,311✔
2839
  si.arg = arg;
52,311✔
2840

2841
  /*
2842
   * We resist the temptation to muck with the stack size here, even if the
2843
   * default is unreasonably small.  That is the client's responsibility.
2844
   */
2845
#    ifdef GC_ASSERTIONS
2846
  {
2847
    size_t stack_size = 0;
52,311✔
2848
    if (NULL != attr) {
52,311✔
2849
      if (pthread_attr_getstacksize(attr, &stack_size) != 0)
48,754✔
2850
        ABORT("pthread_attr_getstacksize failed");
×
2851
    }
2852
    if (0 == stack_size) {
52,311✔
2853
      pthread_attr_t my_attr;
2854

2855
      if (pthread_attr_init(&my_attr) != 0)
3,557✔
2856
        ABORT("pthread_attr_init failed");
×
2857
      if (pthread_attr_getstacksize(&my_attr, &stack_size) != 0)
3,557✔
2858
        ABORT("pthread_attr_getstacksize failed");
×
2859
      (void)pthread_attr_destroy(&my_attr);
3,557✔
2860
    }
2861
    /*
2862
     * On Solaris 10 and on Win32 with `winpthreads` library, with the
2863
     * default `attr` initialization, `stack_size` remains 0; fudge it.
2864
     */
2865
    if (UNLIKELY(0 == stack_size)) {
52,311✔
2866
#      if !defined(SOLARIS) && !defined(GC_WIN32_PTHREADS)
2867
      WARN("Failed to get stack size for assertion checking\n", 0);
×
2868
#      endif
2869
      stack_size = 1000000;
×
2870
    }
2871
    GC_ASSERT(stack_size >= 65536);
52,311✔
2872
    /*
2873
     * Our threads may need to do some work for the GC.
2874
     * Ridiculously small threads will not work, and they
2875
     * probably would not work anyway.
2876
     */
2877
  }
2878
#    endif
2879

2880
  if (attr != NULL) {
52,311✔
2881
    int detachstate;
2882

2883
    if (pthread_attr_getdetachstate(attr, &detachstate) != 0)
48,754✔
2884
      ABORT("pthread_attr_getdetachstate failed");
×
2885
    if (PTHREAD_CREATE_DETACHED == detachstate)
48,754✔
2886
      si.flags |= DETACHED;
48,694✔
2887
  }
2888

2889
#    ifdef PARALLEL_MARK
2890
  if (!GC_parallel && UNLIKELY(GC_available_markers_m1 > 0))
52,311✔
2891
    GC_start_mark_threads();
11✔
2892
#    endif
2893
#    ifdef DEBUG_THREADS
2894
  GC_log_printf("About to start new thread from thread %p\n",
2895
                PTHREAD_TO_VPTR(pthread_self()));
2896
#    endif
2897
  set_need_to_lock();
52,311✔
2898
  result = REAL_FUNC(pthread_create)(new_thread, attr, GC_pthread_start, &si);
52,311✔
2899

2900
  /*
2901
   * Wait until child has been added to the thread table.
2902
   * This also ensures that we hold onto the stack-allocated `si`
2903
   * until the child is done with it.
2904
   */
2905
  if (LIKELY(0 == result)) {
52,059✔
2906
    IF_CANCEL(int cancel_state;)
2907

2908
    /* `pthread_create()` is not a cancellation point. */
2909
    DISABLE_CANCEL(cancel_state);
52,059✔
2910

2911
    while (sem_wait(&si.registered) == -1) {
52,059✔
2912
#    ifdef HAIKU
2913
      /* To workaround some bug in Haiku semaphores. */
2914
      if (EACCES == errno)
2915
        continue;
2916
#    endif
2917
      if (errno != EINTR)
×
2918
        ABORT("sem_wait failed");
×
2919
    }
2920
    RESTORE_CANCEL(cancel_state);
52,045✔
2921
  }
2922
  sem_destroy(&si.registered);
52,045✔
2923
  return result;
52,045✔
2924
}
2925
#    undef GC_wrap_pthread_create
2926

2927
#  endif /* GC_PTHREADS && !PLATFORM_THREADS && !SN_TARGET_PSP2 */
2928

2929
#  if ((defined(GC_PTHREADS_PARAMARK) || defined(USE_PTHREAD_LOCKS)) \
2930
       && !defined(NO_PTHREAD_TRYLOCK))                              \
2931
      || defined(USE_SPIN_LOCK)
2932
/*
2933
 * Spend a few cycles in a way that cannot introduce contention with
2934
 * other threads.
2935
 */
2936
#    define GC_PAUSE_SPIN_CYCLES 10
2937
STATIC void
2938
GC_pause(void)
132,783,391✔
2939
{
2940
  int i;
2941

2942
  for (i = 0; i < GC_PAUSE_SPIN_CYCLES; ++i) {
1,460,617,301✔
2943
    /* Something that is unlikely to be optimized away. */
2944
#    if defined(AO_HAVE_compiler_barrier) && !defined(BASE_ATOMIC_OPS_EMULATED)
2945
    AO_compiler_barrier();
1,327,833,910✔
2946
#    else
2947
    GC_noop1(i);
2948
#    endif
2949
  }
2950
}
132,783,391✔
2951
#  endif /* USE_SPIN_LOCK || !NO_PTHREAD_TRYLOCK */
2952

2953
#  ifndef SPIN_MAX
2954
/* Maximum number of calls to `GC_pause()` before give up. */
2955
#    define SPIN_MAX 128
2956
#  endif
2957

2958
#  if (!defined(USE_SPIN_LOCK) && !defined(NO_PTHREAD_TRYLOCK) \
2959
       && defined(USE_PTHREAD_LOCKS))                          \
2960
      || defined(GC_PTHREADS_PARAMARK)
2961
/*
2962
 * If we do not want to use the below spinlock implementation, either
2963
 * because we do not have a `GC_test_and_set` implementation, or because
2964
 * we do not want to risk sleeping, we can still try spinning
2965
 * on `pthread_mutex_trylock` for a while.  This appears to be very
2966
 * beneficial in many cases.
2967
 * I suspect that under high contention this is nearly always better
2968
 * than the spin lock.  But it is a bit slower on a uniprocessor.
2969
 * Hence we still default to the spin lock.  This is also used to
2970
 * acquire the mark lock for the parallel marker.
2971
 *
2972
 * Here we use a strict exponential back-off scheme.  I do not know
2973
 * whether that is better or worse than the above.  We eventually
2974
 * yield by calling `pthread_mutex_lock()`; it never makes sense to
2975
 * explicitly sleep.
2976
 */
2977

2978
#    ifdef LOCK_STATS
2979
/* Note that `LOCK_STATS` requires `AO_HAVE_test_and_set`. */
2980
volatile AO_t GC_spin_count = 0;
2981
volatile AO_t GC_block_count = 0;
2982
volatile AO_t GC_unlocked_count = 0;
2983
#    endif
2984

2985
STATIC void
2986
GC_generic_lock(pthread_mutex_t *lock)
125,449,857✔
2987
{
2988
#    ifndef NO_PTHREAD_TRYLOCK
2989
  unsigned pause_length = 1;
125,449,857✔
2990
  unsigned i;
2991

2992
  if (LIKELY(0 == pthread_mutex_trylock(lock))) {
125,449,857✔
2993
#      ifdef LOCK_STATS
2994
    (void)AO_fetch_and_add1(&GC_unlocked_count);
2995
#      endif
2996
    return;
118,032,092✔
2997
  }
2998
  for (; pause_length <= (unsigned)SPIN_MAX; pause_length <<= 1) {
16,603,520✔
2999
    for (i = 0; i < pause_length; ++i) {
149,258,031✔
3000
      GC_pause();
132,783,391✔
3001
    }
3002
    switch (pthread_mutex_trylock(lock)) {
16,474,640✔
3003
    case 0:
7,288,885✔
3004
#      ifdef LOCK_STATS
3005
      (void)AO_fetch_and_add1(&GC_spin_count);
3006
#      endif
3007
      return;
7,288,885✔
3008
    case EBUSY:
9,185,755✔
3009
      break;
9,185,755✔
3010
    default:
×
3011
      ABORT("Unexpected error from pthread_mutex_trylock");
×
3012
    }
3013
  }
3014
#    endif /* !NO_PTHREAD_TRYLOCK */
3015
#    ifdef LOCK_STATS
3016
  (void)AO_fetch_and_add1(&GC_block_count);
3017
#    endif
3018
  pthread_mutex_lock(lock);
128,880✔
3019
}
3020
#  endif /* !USE_SPIN_LOCK || ... */
3021

3022
#  if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS)
3023
GC_INNER volatile unsigned char GC_collecting = FALSE;
3024

3025
#    if defined(AO_HAVE_char_load) && !defined(BASE_ATOMIC_OPS_EMULATED)
3026
#      define is_collecting() ((GC_bool)AO_char_load(&GC_collecting))
3027
#    else
3028
/*
3029
 * `GC_collecting` is a hint, a potential data race between `GC_lock()`
3030
 * and `ENTER_GC()`/`EXIT_GC()` is OK to ignore.
3031
 */
3032
#      define is_collecting() ((GC_bool)GC_collecting)
3033
#    endif
3034
#  endif /* GC_PTHREADS && !GC_WIN32_THREADS */
3035

3036
#  ifdef GC_ASSERTIONS
3037
GC_INNER unsigned long GC_lock_holder = NO_THREAD;
3038
#  endif
3039

3040
#  if defined(USE_SPIN_LOCK)
3041
/*
3042
 * Reasonably fast spin locks.  Basically the same implementation as
3043
 * in STL `alloc.h` file.  This is not really the right way to do this
3044
 * but until the POSIX scheduling mess gets straightened out...
3045
 */
3046

3047
/* Spin cycles if we suspect we are running on an uniprocessor. */
3048
#    define low_spin_max 30
3049

3050
/* Spin cycles for a multiprocessor. */
3051
#    define high_spin_max SPIN_MAX
3052

3053
static volatile AO_t spin_max = low_spin_max;
3054

3055
/*
3056
 * A potential data race between threads invoking `GC_lock` which reads
3057
 * and updates `spin_max` and `last_spins` could be ignored because these
3058
 * variables are hints only.
3059
 */
3060
static volatile AO_t last_spins = 0;
3061

3062
GC_INNER void
3063
GC_lock(void)
3064
{
3065
  AO_t my_spin_max, my_last_spins_half;
3066
  size_t i;
3067

3068
  if (LIKELY(AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_CLEAR))
3069
    return;
3070

3071
  my_spin_max = AO_load(&spin_max);
3072
  my_last_spins_half = AO_load(&last_spins) / 2;
3073
  for (i = 0; i < my_spin_max; i++) {
3074
    if (is_collecting() || GC_nprocs == 1)
3075
      goto yield;
3076
    if (i < my_last_spins_half) {
3077
      GC_pause();
3078
      continue;
3079
    }
3080
    if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_CLEAR) {
3081
      /*
3082
       * Got it, spinning worked!  Thus we are probably not being
3083
       * scheduled against the other process with which we were
3084
       * contending.  Thus it makes sense to spin longer the next time.
3085
       */
3086
      AO_store(&last_spins, i);
3087
      AO_store(&spin_max, high_spin_max);
3088
      return;
3089
    }
3090
  }
3091
  /* We are probably being scheduled against the other process.  Sleep. */
3092
  AO_store(&spin_max, low_spin_max);
3093
yield:
3094
  for (i = 0;; ++i) {
3095
    if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_CLEAR) {
3096
      return;
3097
    }
3098

3099
    /*
3100
     * Under Linux, very short sleeps tend to wait until the current time
3101
     * quantum expires.  On old Linux kernels, `nanosleep()` (<= 2 ms) just
3102
     * spins.  (Under Linux 2.4, this happens only for real-time processes.)
3103
     * We want to minimize both behaviors here.
3104
     */
3105
#    define SLEEP_THRESHOLD 12
3106

3107
    if (i < SLEEP_THRESHOLD) {
3108
      sched_yield();
3109
    } else {
3110
      struct timespec ts;
3111

3112
      /*
3113
       * Do not wait for more than about 15 ms, even under extreme
3114
       * contention.
3115
       */
3116
      if (i > 24)
3117
        i = 24;
3118

3119
      ts.tv_sec = 0;
3120
      ts.tv_nsec = (unsigned32)1 << i;
3121
      nanosleep(&ts, 0);
3122
    }
3123
  }
3124
}
3125

3126
#  elif defined(USE_PTHREAD_LOCKS)
3127
#    ifdef USE_RWLOCK
3128
GC_INNER pthread_rwlock_t GC_allocate_ml = PTHREAD_RWLOCK_INITIALIZER;
3129
#    else
3130
GC_INNER pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER;
3131
#    endif
3132

3133
#    ifndef NO_PTHREAD_TRYLOCK
3134
GC_INNER void
3135
GC_lock(void)
121,305,500✔
3136
{
3137
  if (1 == GC_nprocs || is_collecting()) {
121,305,500✔
3138
    pthread_mutex_lock(&GC_allocate_ml);
14,786✔
3139
  } else {
3140
    GC_generic_lock(&GC_allocate_ml);
121,290,714✔
3141
  }
3142
}
121,305,500✔
3143
#    elif defined(GC_ASSERTIONS)
3144
GC_INNER void
3145
GC_lock(void)
3146
{
3147
#      ifdef USE_RWLOCK
3148
  (void)pthread_rwlock_wrlock(&GC_allocate_ml); /*< exclusive */
3149
#      else
3150
  pthread_mutex_lock(&GC_allocate_ml);
3151
#      endif
3152
}
3153
#    endif /* NO_PTHREAD_TRYLOCK && GC_ASSERTIONS */
3154

3155
#  endif /* !USE_SPIN_LOCK && USE_PTHREAD_LOCKS */
3156

3157
#  ifdef GC_PTHREADS_PARAMARK
3158

3159
#    if defined(GC_ASSERTIONS) && defined(GC_WIN32_THREADS) \
3160
        && !defined(USE_PTHREAD_LOCKS)
3161
/* Note: result is not guaranteed to be unique. */
3162
#      define NUMERIC_THREAD_ID(id) ((unsigned long)ADDR(PTHREAD_TO_VPTR(id)))
3163
#    endif
3164

3165
#    ifdef GC_ASSERTIONS
3166
#      define SET_MARK_LOCK_HOLDER \
3167
        (void)(GC_mark_lock_holder = NUMERIC_THREAD_ID(pthread_self()))
3168
#      define UNSET_MARK_LOCK_HOLDER                       \
3169
        do {                                               \
3170
          GC_ASSERT(GC_mark_lock_holder                    \
3171
                    == NUMERIC_THREAD_ID(pthread_self())); \
3172
          GC_mark_lock_holder = NO_THREAD;                 \
3173
        } while (0)
3174
#    else
3175
#      define SET_MARK_LOCK_HOLDER (void)0
3176
#      define UNSET_MARK_LOCK_HOLDER (void)0
3177
#    endif /* !GC_ASSERTIONS */
3178

3179
static pthread_cond_t builder_cv = PTHREAD_COND_INITIALIZER;
3180

3181
#    ifndef GC_WIN32_THREADS
3182
static void
3183
setup_mark_lock(void)
39✔
3184
{
3185
#      ifdef GLIBC_2_19_TSX_BUG
3186
  pthread_mutexattr_t mattr;
3187
  int glibc_minor = -1;
39✔
3188
  int glibc_major = GC_parse_version(&glibc_minor, gnu_get_libc_version());
39✔
3189

3190
  if (glibc_major > 2 || (glibc_major == 2 && glibc_minor >= 19)) {
39✔
3191
    /* TODO: Disable this workaround for `glibc` with fixed TSX. */
3192
    /* This disables lock elision to workaround a bug in `glibc` 2.19+. */
3193
    if (pthread_mutexattr_init(&mattr) != 0)
39✔
3194
      ABORT("pthread_mutexattr_init failed");
×
3195
    if (pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL) != 0)
39✔
3196
      ABORT("pthread_mutexattr_settype failed");
×
3197
    if (pthread_mutex_init(&mark_mutex, &mattr) != 0)
39✔
3198
      ABORT("pthread_mutex_init failed");
×
3199
    (void)pthread_mutexattr_destroy(&mattr);
39✔
3200
  }
3201
#      endif
3202
}
39✔
3203
#    endif /* !GC_WIN32_THREADS */
3204

3205
GC_INNER void
3206
GC_acquire_mark_lock(void)
4,159,143✔
3207
{
3208
#    if defined(NUMERIC_THREAD_ID_UNIQUE) && !defined(THREAD_SANITIZER)
3209
  GC_ASSERT(GC_mark_lock_holder != NUMERIC_THREAD_ID(pthread_self()));
4,159,143✔
3210
#    endif
3211
  GC_generic_lock(&mark_mutex);
4,159,143✔
3212
  SET_MARK_LOCK_HOLDER;
4,159,143✔
3213
}
4,159,143✔
3214

3215
GC_INNER void
3216
GC_release_mark_lock(void)
4,159,065✔
3217
{
3218
  UNSET_MARK_LOCK_HOLDER;
4,159,065✔
3219
  if (pthread_mutex_unlock(&mark_mutex) != 0)
4,159,065✔
3220
    ABORT("pthread_mutex_unlock failed");
×
3221
}
4,159,065✔
3222

3223
/*
3224
 * Collector must wait for free-list builders for 2 reasons:
3225
 *   - Mark bits may still be getting examined without lock;
3226
 *   - Partial free lists referenced only by locals may not be scanned
3227
 *     correctly, e.g. if they contain "pointer-free" objects, since the
3228
 *     free-list link may be ignored.
3229
 */
3230
STATIC void
3231
GC_wait_builder(void)
1,007✔
3232
{
3233
  ASSERT_CANCEL_DISABLED();
1,007✔
3234
  UNSET_MARK_LOCK_HOLDER;
1,007✔
3235
  if (pthread_cond_wait(&builder_cv, &mark_mutex) != 0)
1,007✔
3236
    ABORT("pthread_cond_wait failed");
×
3237
  GC_ASSERT(GC_mark_lock_holder == NO_THREAD);
1,007✔
3238
  SET_MARK_LOCK_HOLDER;
1,007✔
3239
}
1,007✔
3240

3241
GC_INNER void
3242
GC_wait_for_reclaim(void)
19,153✔
3243
{
3244
  GC_acquire_mark_lock();
19,153✔
3245
  while (GC_fl_builder_count > 0) {
20,160✔
3246
    GC_wait_builder();
1,007✔
3247
  }
3248
  GC_release_mark_lock();
19,153✔
3249
}
19,153✔
3250

3251
#    if defined(CAN_HANDLE_FORK) && defined(THREAD_SANITIZER)
3252
/*
3253
 * Identical to `GC_wait_for_reclaim()` but with the `no_sanitize`
3254
 * attribute as a workaround for TSan which does not notice that the
3255
 * allocator lock is acquired in `fork_prepare_proc()`.
3256
 */
3257
GC_ATTR_NO_SANITIZE_THREAD
3258
static void
3259
wait_for_reclaim_atfork(void)
3260
{
3261
  GC_acquire_mark_lock();
3262
  while (GC_fl_builder_count > 0)
3263
    GC_wait_builder();
3264
  GC_release_mark_lock();
3265
}
3266
#    endif /* CAN_HANDLE_FORK && THREAD_SANITIZER */
3267

3268
GC_INNER void
3269
GC_notify_all_builder(void)
1,523,527✔
3270
{
3271
  GC_ASSERT(GC_mark_lock_holder == NUMERIC_THREAD_ID(pthread_self()));
1,523,527✔
3272
  if (pthread_cond_broadcast(&builder_cv) != 0)
1,523,527✔
3273
    ABORT("pthread_cond_broadcast failed");
×
3274
}
1,523,527✔
3275

3276
GC_INNER void
3277
GC_wait_marker(void)
9,123✔
3278
{
3279
  ASSERT_CANCEL_DISABLED();
9,123✔
3280
  GC_ASSERT(GC_parallel);
9,123✔
3281
  UNSET_MARK_LOCK_HOLDER;
9,123✔
3282
  if (pthread_cond_wait(&mark_cv, &mark_mutex) != 0)
9,123✔
3283
    ABORT("pthread_cond_wait failed");
×
3284
  GC_ASSERT(GC_mark_lock_holder == NO_THREAD);
9,045✔
3285
  SET_MARK_LOCK_HOLDER;
9,045✔
3286
}
9,045✔
3287

3288
GC_INNER void
3289
GC_notify_all_marker(void)
9,515✔
3290
{
3291
  GC_ASSERT(GC_parallel);
9,515✔
3292
  if (pthread_cond_broadcast(&mark_cv) != 0)
9,515✔
3293
    ABORT("pthread_cond_broadcast failed");
×
3294
}
9,515✔
3295

3296
#  endif /* GC_PTHREADS_PARAMARK */
3297

3298
GC_INNER GC_on_thread_event_proc GC_on_thread_event = 0;
3299

3300
GC_API void GC_CALL
3301
GC_set_on_thread_event(GC_on_thread_event_proc fn)
3✔
3302
{
3303
  /* Note: `fn` may be 0 (means no event notifier). */
3304
  LOCK();
3✔
3305
  GC_on_thread_event = fn;
3✔
3306
  UNLOCK();
3✔
3307
}
3✔
3308

3309
GC_API GC_on_thread_event_proc GC_CALL
3310
GC_get_on_thread_event(void)
3✔
3311
{
3312
  GC_on_thread_event_proc fn;
3313

3314
  READER_LOCK();
3✔
3315
  fn = GC_on_thread_event;
3✔
3316
  READER_UNLOCK();
3✔
3317
  return fn;
3✔
3318
}
3319

3320
#  ifdef STACKPTR_CORRECTOR_AVAILABLE
3321
GC_INNER GC_sp_corrector_proc GC_sp_corrector = 0;
3322
#  endif
3323

3324
GC_API void GC_CALL
3325
GC_set_sp_corrector(GC_sp_corrector_proc fn)
3✔
3326
{
3327
#  ifdef STACKPTR_CORRECTOR_AVAILABLE
3328
  LOCK();
3✔
3329
  GC_sp_corrector = fn;
3✔
3330
  UNLOCK();
3✔
3331
#  else
3332
  UNUSED_ARG(fn);
3333
#  endif
3334
}
3✔
3335

3336
GC_API GC_sp_corrector_proc GC_CALL
3337
GC_get_sp_corrector(void)
3✔
3338
{
3339
#  ifdef STACKPTR_CORRECTOR_AVAILABLE
3340
  GC_sp_corrector_proc fn;
3341

3342
  READER_LOCK();
3✔
3343
  fn = GC_sp_corrector;
3✔
3344
  READER_UNLOCK();
3✔
3345
  return fn;
3✔
3346
#  else
3347
  return 0; /*< unsupported */
3348
#  endif
3349
}
3350

3351
#  ifdef PTHREAD_REGISTER_CANCEL_WEAK_STUBS
3352
/* Workaround "undefined reference" linkage errors on some targets. */
3353
EXTERN_C_BEGIN
3354
extern void __pthread_register_cancel(void) __attribute__((__weak__));
3355
extern void __pthread_unregister_cancel(void) __attribute__((__weak__));
3356
EXTERN_C_END
3357

3358
void
3359
__pthread_register_cancel(void)
3360
{
3361
}
3362
void
3363
__pthread_unregister_cancel(void)
3364
{
3365
}
3366
#  endif
3367

3368
#  undef do_blocking_enter
3369

3370
#endif /* THREADS */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc