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

bdwgc / bdwgc / 2101

11 Apr 2026 10:06AM UTC coverage: 80.219% (-0.1%) from 80.342%
2101

push

travis-ci

ivmai
Fix 'GC_DllMain undeclared' compiler error if CPPCHECK defined
(fix of commit bc1866b14)

* win32_threads.c [!GC_NO_THREADS_DISCOVERY && !GC_INSIDE_DLL]
(GC_DllMain): Move macro definition upper (to be before
`GC_use_threads_discovery()`); declare function (as `WINAPI`).

7186 of 8958 relevant lines covered (80.22%)

19087294.81 hits per line

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

76.48
/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,002✔
298
{
299
  int i;
300
  GC_thread p;
301

302
  for (i = 0; i < THREAD_TABLE_SZ; ++i) {
7,967,514✔
303
    for (p = GC_threads[i]; p != NULL; p = p->tm.next) {
8,213,776✔
304
      if (!KNOWN_FINISHED(p))
277,264✔
305
        GC_mark_thread_local_fls_for(&p->tlfs);
255,705✔
306
    }
307
  }
308
}
31,002✔
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,002✔
317
{
318
  int i;
319
  GC_thread p;
320

321
  for (i = 0; i < THREAD_TABLE_SZ; ++i) {
7,967,514✔
322
    for (p = GC_threads[i]; p != NULL; p = p->tm.next) {
8,213,776✔
323
      if (!KNOWN_FINISHED(p))
277,264✔
324
        GC_check_tls_for(&p->tlfs);
255,705✔
325
    }
326
  }
327
#      if defined(USE_CUSTOM_SPECIFIC)
328
  if (GC_thread_key != 0)
31,002✔
329
    GC_check_tsd_marks(GC_thread_key);
30,963✔
330
#      endif
331
}
31,002✔
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();
54✔
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) {
1,981✔
525
    if (my_mark_no - GC_mark_no > (word)2) {
2,059✔
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,059✔
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. */
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_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
    pthread_t new_thread;
655

656
#    ifdef GC_WIN32_THREADS
657
    GC_marker_last_stack_min[i] = ADDR_LIMIT;
658
#    endif
659
    if (UNLIKELY(REAL_FUNC(pthread_create)(&new_thread, &attr, GC_mark_thread,
78✔
660
                                           NUMERIC_TO_VPTR(i))
661
                 != 0)) {
662
      WARN("Marker thread %" WARN_PRIdPTR " creation failed\n",
×
663
           (GC_signed_word)i);
664
      /* Do not try to create other marker threads. */
665
      GC_markers_m1 = i;
×
666
      break;
×
667
    }
668
  }
669

670
#    ifndef NO_MARKER_SPECIAL_SIGMASK
671
  /* Restore previous signal mask. */
672
  if (UNLIKELY(REAL_FUNC(pthread_sigmask)(SIG_SETMASK, &oldset, NULL) != 0)) {
78✔
673
    WARN("pthread_sigmask restore failed\n", 0);
×
674
  }
675
#    endif
676

677
  (void)pthread_attr_destroy(&attr);
78✔
678
  GC_wait_for_markers_init();
78✔
679
  GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1);
78✔
680
}
681

682
#  endif /* GC_PTHREADS_PARAMARK */
683

684
GC_INNER GC_thread GC_threads[THREAD_TABLE_SZ] = { NULL };
685

686
/*
687
 * It may not be safe to allocate when we register the first thread.
688
 * Note that `next` and `status` fields are unused, but there might be
689
 * some other fields (`crtn`) to be pushed.
690
 */
691
static struct GC_StackContext_Rep first_crtn;
692
static struct GC_Thread_Rep first_thread;
693

694
#  ifndef GC_NO_DEINIT
695
GC_INNER void
696
GC_reset_threads(void)
×
697
{
698
  BZERO(GC_threads, sizeof(GC_threads));
×
699
  BZERO(&first_crtn, sizeof(first_crtn));
×
700
  BZERO(&first_thread, sizeof(first_thread));
×
701
}
×
702
#  endif
703

704
/*
705
 * A place to retain a pointer to an allocated object while a thread
706
 * registration is ongoing.  Protected by the allocator lock.
707
 */
708
static GC_stack_context_t saved_crtn = NULL;
709

710
GC_INNER void
711
GC_push_thread_structures(void)
×
712
{
713
  GC_ASSERT(I_HOLD_LOCK());
×
714
#  if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS)
715
  if (GC_win32_dll_threads) {
716
    /*
717
     * Unlike the other threads implementations, the thread table here
718
     * contains no pointers to the collectible heap (note also that
719
     * `GC_PTHREADS` is incompatible with `DllMain`-based thread
720
     * registration).  Thus we have no private structures we need to
721
     * preserve.
722
     */
723
  } else
724
#  endif
725
  /* else */ {
726
    GC_push_all(&GC_threads, (ptr_t)(&GC_threads) + sizeof(GC_threads));
×
727
    GC_ASSERT(NULL == first_thread.tm.next);
×
728
#  ifdef GC_PTHREADS
729
    GC_ASSERT(NULL == first_thread.status);
×
730
#  endif
731
    GC_PUSH_ALL_SYM(first_thread.crtn);
×
732
    GC_PUSH_ALL_SYM(saved_crtn);
×
733
  }
734
#  if defined(THREAD_LOCAL_ALLOC) && defined(USE_CUSTOM_SPECIFIC)
735
  GC_PUSH_ALL_SYM(GC_thread_key);
×
736
#  endif
737
}
×
738

739
#  if defined(MPROTECT_VDB) && defined(GC_WIN32_THREADS)
740
GC_INNER void
741
GC_win32_unprotect_thread(GC_thread t)
742
{
743
  GC_ASSERT(I_HOLD_LOCK());
744
  if (!GC_win32_dll_threads && GC_auto_incremental) {
745
    GC_stack_context_t crtn = t->crtn;
746

747
    if (crtn != &first_crtn) {
748
      GC_ASSERT(SMALL_OBJ(GC_size(crtn)));
749
      GC_remove_protection(HBLKPTR(crtn), 1, FALSE);
750
    }
751
    if (t != &first_thread) {
752
      GC_ASSERT(SMALL_OBJ(GC_size(t)));
753
      GC_remove_protection(HBLKPTR(t), 1, FALSE);
754
    }
755
  }
756
}
757
#  endif /* MPROTECT_VDB && GC_WIN32_THREADS */
758

759
#  ifdef DEBUG_THREADS
760
STATIC int
761
GC_count_threads(void)
762
{
763
  int i;
764
  int count = 0;
765

766
#    if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS)
767
  if (GC_win32_dll_threads)
768
    return -1; /*< not implemented */
769
#    endif
770
  GC_ASSERT(I_HOLD_READER_LOCK());
771
  for (i = 0; i < THREAD_TABLE_SZ; ++i) {
772
    GC_thread p;
773

774
    for (p = GC_threads[i]; p != NULL; p = p->tm.next) {
775
      if (!KNOWN_FINISHED(p))
776
        ++count;
777
    }
778
  }
779
  return count;
780
}
781
#  endif /* DEBUG_THREADS */
782

783
GC_INNER_WIN32THREAD GC_thread
784
GC_new_thread(thread_id_t self_id)
102,634✔
785
{
786
  int hv = THREAD_TABLE_INDEX(self_id);
102,634✔
787
  GC_thread result;
788

789
  GC_ASSERT(I_HOLD_LOCK());
102,634✔
790
#  ifdef DEBUG_THREADS
791
  GC_log_printf("Creating thread %p\n", THREAD_ID_TO_VPTR(self_id));
792
  for (result = GC_threads[hv]; result != NULL; result = result->tm.next)
793
    if (!THREAD_ID_EQUAL(result->id, self_id)) {
794
      GC_log_printf("Hash collision at GC_threads[%d]\n", hv);
795
      break;
796
    }
797
#  endif
798
  if (UNLIKELY(NULL == first_thread.crtn)) {
102,634✔
799
    result = &first_thread;
39✔
800
    first_thread.crtn = &first_crtn;
39✔
801
    GC_ASSERT(NULL == GC_threads[hv]);
39✔
802
#  if defined(CPPCHECK) && defined(THREAD_SANITIZER) \
803
      && defined(SIGNAL_BASED_STOP_WORLD)
804
    GC_noop1((unsigned char)first_crtn.dummy[0]);
805
#  endif
806
  } else {
807
    GC_stack_context_t crtn;
808

809
    GC_ASSERT(!GC_win32_dll_threads);
810
    GC_ASSERT(!GC_in_thread_creation);
102,595✔
811
    GC_in_thread_creation = TRUE; /*< OK to collect from unknown thread */
102,595✔
812
    crtn = (GC_stack_context_t)GC_INTERNAL_MALLOC(
102,595✔
813
        sizeof(struct GC_StackContext_Rep), NORMAL);
814

815
    /*
816
     * The current stack is not scanned until the thread is registered,
817
     * thus `crtn` pointer is to be retained in the global data roots for
818
     * a while (and pushed explicitly if a collection occurs here).
819
     */
820
    GC_ASSERT(NULL == saved_crtn);
102,595✔
821
    saved_crtn = crtn;
102,595✔
822
    result
823
        = (GC_thread)GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep), NORMAL);
102,595✔
824
    /* No more collections till thread is registered. */
825
    saved_crtn = NULL;
102,595✔
826
    GC_in_thread_creation = FALSE;
102,595✔
827
    if (NULL == crtn || NULL == result)
102,595✔
828
      ABORT("Failed to allocate memory for thread registering");
×
829
    result->crtn = crtn;
102,595✔
830
  }
831
  /* The `id` field is not set here. */
832
#  ifdef USE_TKILL_ON_ANDROID
833
  result->kernel_id = gettid();
834
#  endif
835
  result->tm.next = GC_threads[hv];
102,634✔
836
  GC_threads[hv] = result;
102,634✔
837
#  ifdef NACL
838
  GC_nacl_initialize_gc_thread(result);
839
#  endif
840
  GC_ASSERT(0 == result->flags);
102,634✔
841
  if (LIKELY(result != &first_thread))
102,634✔
842
    GC_dirty(result);
102,595✔
843
  return result;
102,634✔
844
}
845

846
GC_INNER_WIN32THREAD void
847
GC_delete_thread(GC_thread t)
102,349✔
848
{
849
#  if defined(GC_WIN32_THREADS) && !defined(MSWINCE)
850
  CloseHandle(t->handle);
851
#  endif
852
#  if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS)
853
  if (GC_win32_dll_threads) {
854
    /*
855
     * This is intended to be lock-free.  In this branch asynchronous changes
856
     * to `*t` are possible.  Note that it is not allowed to call `GC_printf`
857
     * (and the friends) here, see `GC_stop_world()` in `win32_threads.c` file
858
     * for the information.
859
     */
860
    t->crtn->stack_end = NULL;
861
    t->id = 0;
862
    /* The thread is not suspended. */
863
    t->flags = 0;
864
#    ifdef RETRY_GET_THREAD_CONTEXT
865
    t->context_sp = NULL;
866
#    endif
867
    AO_store_release(&t->tm.in_use, FALSE);
868
  } else
869
#  endif
870
  /* else */ {
871
    thread_id_t id = t->id;
102,349✔
872
    int hv = THREAD_TABLE_INDEX(id);
102,349✔
873
    GC_thread p;
874
    GC_thread prev = NULL;
102,349✔
875

876
    GC_ASSERT(I_HOLD_LOCK());
102,349✔
877
#  if defined(DEBUG_THREADS) && !defined(MSWINCE) \
878
      && (!defined(MSWIN32) || defined(CONSOLE_LOG))
879
    GC_log_printf("Deleting thread %p, n_threads= %d\n", THREAD_ID_TO_VPTR(id),
880
                  GC_count_threads());
881
#  endif
882
    for (p = GC_threads[hv]; p != t; p = p->tm.next) {
105,270✔
883
      prev = p;
2,921✔
884
    }
885
    if (NULL == prev) {
102,349✔
886
      GC_threads[hv] = p->tm.next;
99,668✔
887
    } else {
888
      GC_ASSERT(prev != &first_thread);
2,681✔
889
      prev->tm.next = p->tm.next;
2,681✔
890
      GC_dirty(prev);
2,681✔
891
    }
892
    if (LIKELY(p != &first_thread)) {
102,349✔
893
#  ifdef DARWIN
894
      mach_port_deallocate(mach_task_self(), p->mach_thread);
895
#  endif
896
      GC_ASSERT(p->crtn != &first_crtn);
102,346✔
897
      GC_INTERNAL_FREE(p->crtn);
102,346✔
898
      GC_INTERNAL_FREE(p);
102,346✔
899
    }
900
  }
901
}
102,349✔
902

903
GC_INNER GC_thread
904
GC_lookup_thread(thread_id_t id)
204,038,062✔
905
{
906
  GC_thread p;
907

908
#  if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS)
909
  if (GC_win32_dll_threads)
910
    return GC_win32_dll_lookup_thread(id);
911
#  endif
912
  for (p = GC_threads[THREAD_TABLE_INDEX(id)]; p != NULL; p = p->tm.next) {
204,603,531✔
913
    if (LIKELY(THREAD_ID_EQUAL(p->id, id)))
204,553,994✔
914
      break;
203,988,525✔
915
  }
916
  return p;
204,038,062✔
917
}
918

919
/*
920
 * Same as `GC_self_thread_inner()` but acquires the allocator lock (in
921
 * the reader mode).
922
 */
923
STATIC GC_thread
924
GC_self_thread(void)
200,021,358✔
925
{
926
  GC_thread p;
927

928
  READER_LOCK();
200,021,358✔
929
  p = GC_self_thread_inner();
200,021,358✔
930
  READER_UNLOCK();
200,021,358✔
931
  return p;
200,021,358✔
932
}
933

934
#  ifndef GC_NO_FINALIZATION
935
GC_INNER void
936
GC_reset_finalizer_nested(void)
×
937
{
938
  GC_ASSERT(I_HOLD_LOCK());
×
939
  GC_self_thread_inner()->crtn->finalizer_nested = 0;
×
940
}
×
941

942
GC_INNER unsigned char *
943
GC_check_finalizer_nested(void)
3,377✔
944
{
945
  GC_thread me;
946
  GC_stack_context_t crtn;
947
  unsigned nesting_level;
948

949
  GC_ASSERT(I_HOLD_LOCK());
3,377✔
950
  me = GC_self_thread_inner();
3,377✔
951
#    if defined(INCLUDE_LINUX_THREAD_DESCR) && defined(REDIRECT_MALLOC)
952
  /*
953
   * As noted in `GC_pthread_start`, an allocation may happen in
954
   * `GC_get_stack_base`, causing `GC_notify_or_invoke_finalizers`
955
   * to be called before the thread gets registered.
956
   */
957
  if (UNLIKELY(NULL == me))
958
    return NULL;
959
#    endif
960
  crtn = me->crtn;
3,377✔
961
  nesting_level = crtn->finalizer_nested;
3,377✔
962
  if (nesting_level) {
3,377✔
963
    /*
964
     * We are inside another `GC_invoke_finalizers()`.  Skip some
965
     * implicitly-called `GC_invoke_finalizers()` depending on the
966
     * nesting (recursion) level.
967
     */
968
    if ((unsigned)(++crtn->finalizer_skipped) < (1U << nesting_level))
×
969
      return NULL;
×
970
    crtn->finalizer_skipped = 0;
×
971
  }
972
  crtn->finalizer_nested = (unsigned char)(nesting_level + 1);
3,377✔
973
  return &crtn->finalizer_nested;
3,377✔
974
}
975
#  endif /* !GC_NO_FINALIZATION */
976

977
#  define ADDR_INSIDE_OBJ(p, obj) \
978
    ADDR_INSIDE(p, (ptr_t)(&(obj)), (ptr_t)(&(obj)) + sizeof(obj))
979

980
#  if defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC)
981
/* This is called from thread-local `GC_malloc()`. */
982
GC_bool
983
GC_is_thread_tsd_valid(void *tsd)
200,021,289✔
984
{
985
  GC_thread me = GC_self_thread();
200,021,289✔
986

987
  return ADDR_INSIDE_OBJ((ptr_t)tsd, me->tlfs);
200,021,289✔
988
}
989
#  endif /* GC_ASSERTIONS && THREAD_LOCAL_ALLOC */
990

991
GC_API int GC_CALL
992
GC_thread_is_registered(void)
67✔
993
{
994
  /* TODO: Use `GC_get_tlfs()` instead. */
995
  GC_thread me = GC_self_thread();
67✔
996

997
  return me != NULL && !KNOWN_FINISHED(me);
67✔
998
}
999

1000
GC_API void GC_CALL
1001
GC_register_altstack(void *normstack, size_t normstack_size, void *altstack,
×
1002
                     size_t altstack_size)
1003
{
1004
#  ifdef GC_WIN32_THREADS
1005
  /* TODO: Implement. */
1006
  UNUSED_ARG(normstack);
1007
  UNUSED_ARG(normstack_size);
1008
  UNUSED_ARG(altstack);
1009
  UNUSED_ARG(altstack_size);
1010
#  else
1011
  GC_thread me;
1012
  GC_stack_context_t crtn;
1013

1014
  READER_LOCK();
×
1015
  me = GC_self_thread_inner();
×
1016
  if (UNLIKELY(NULL == me)) {
×
1017
    /* We are called before `GC_thr_init()`. */
1018
    me = &first_thread;
×
1019
  }
1020
  crtn = me->crtn;
×
1021
  crtn->normstack = (ptr_t)normstack;
×
1022
  crtn->normstack_size = normstack_size;
×
1023
  crtn->altstack = (ptr_t)altstack;
×
1024
  crtn->altstack_size = altstack_size;
×
1025
  READER_UNLOCK_RELEASE();
×
1026
#  endif
1027
}
×
1028

1029
#  if defined(USE_PROC_FOR_LIBRARIES) && defined(LINUX)
1030
GC_INNER GC_bool
1031
GC_segment_is_thread_stack(ptr_t lo, ptr_t hi)
1032
{
1033
  int i;
1034
  GC_thread p;
1035

1036
  GC_ASSERT(I_HOLD_READER_LOCK());
1037
#    ifdef PARALLEL_MARK
1038
  for (i = 0; i < GC_markers_m1; ++i) {
1039
    if (ADDR_LT(lo, GC_marker_sp[i]) && ADDR_LT(GC_marker_sp[i], hi))
1040
      return TRUE;
1041
#      ifdef IA64
1042
    if (ADDR_LT(lo, marker_bsp[i]) && ADDR_LT(marker_bsp[i], hi))
1043
      return TRUE;
1044
#      endif
1045
  }
1046
#    endif
1047
  for (i = 0; i < THREAD_TABLE_SZ; i++) {
1048
    for (p = GC_threads[i]; p != NULL; p = p->tm.next) {
1049
      ptr_t stack_end = p->crtn->stack_end;
1050

1051
      if (stack_end != NULL) {
1052
#    ifdef STACK_GROWS_UP
1053
        if (ADDR_INSIDE(stack_end, lo, hi))
1054
          return TRUE;
1055
#    else
1056
        if (ADDR_LT(lo, stack_end) && ADDR_GE(hi, stack_end))
1057
          return TRUE;
1058
#    endif
1059
      }
1060
    }
1061
  }
1062
  return FALSE;
1063
}
1064
#  endif
1065

1066
#  if (defined(HAVE_PTHREAD_ATTR_GET_NP) || defined(HAVE_PTHREAD_GETATTR_NP)) \
1067
      && defined(IA64)
1068
GC_INNER ptr_t
1069
GC_greatest_stack_base_below(ptr_t bound)
1070
{
1071
  int i;
1072
  GC_thread p;
1073
  ptr_t result = NULL;
1074

1075
  GC_ASSERT(I_HOLD_READER_LOCK());
1076
#    ifdef PARALLEL_MARK
1077
  for (i = 0; i < GC_markers_m1; ++i) {
1078
    if (ADDR_LT(result, GC_marker_sp[i]) && ADDR_LT(GC_marker_sp[i], bound))
1079
      result = GC_marker_sp[i];
1080
  }
1081
#    endif
1082
  for (i = 0; i < THREAD_TABLE_SZ; i++) {
1083
    for (p = GC_threads[i]; p != NULL; p = p->tm.next) {
1084
      ptr_t stack_end = p->crtn->stack_end;
1085

1086
      if (ADDR_LT(result, stack_end) && ADDR_LT(stack_end, bound))
1087
        result = stack_end;
1088
    }
1089
  }
1090
  return result;
1091
}
1092
#  endif /* IA64 */
1093

1094
#  ifndef STAT_READ
1095
/*
1096
 * Note: if `read()` is wrapped, this may need to be redefined to call
1097
 * the real one.
1098
 */
1099
#    define STAT_READ read
1100
#  endif
1101

1102
#  ifdef HPUX
1103
#    define GC_get_nprocs() pthread_num_processors_np()
1104

1105
#  elif defined(AIX) || defined(COSMO) || defined(HAIKU)         \
1106
      || defined(HOST_ANDROID) || defined(HURD) || defined(NACL) \
1107
      || defined(OSF1) || defined(SOLARIS)
1108
GC_INLINE int
1109
GC_get_nprocs(void)
1110
{
1111
  int nprocs = (int)sysconf(_SC_NPROCESSORS_ONLN);
1112
  /* Note: ignore any error silently. */
1113
  return nprocs > 0 ? nprocs : 1;
1114
}
1115

1116
#  elif defined(IRIX5)
1117
GC_INLINE int
1118
GC_get_nprocs(void)
1119
{
1120
  int nprocs = (int)sysconf(_SC_NPROC_ONLN);
1121
  /* Note: ignore any error silently. */
1122
  return nprocs > 0 ? nprocs : 1;
1123
}
1124

1125
#  elif defined(LINUX)
1126
/* Return the number of processors. */
1127
STATIC int
1128
GC_get_nprocs(void)
39✔
1129
{
1130
  /*
1131
   * Should be just `return sysconf(_SC_NPROCESSORS_ONLN)` but that
1132
   * appears to be buggy in many cases.  We look for lines "cpu<N>" in
1133
   * `/proc/stat` pseudo-file.  No need to read the entire `/proc/stat`
1134
   * pseudo-file to get maximum "cpu<N>" such as:
1135
   *   - the requested lines are located at the beginning of the file;
1136
   *   - the lines with "cpu<N>" where `N` is greater than `MAX_MARKERS`
1137
   *     are not needed.
1138
   */
1139
#    define PROC_STAT_BUF_SZ ((1 + MAX_MARKERS) * 100)
1140
  char stat_buf[PROC_STAT_BUF_SZ + 1]; /*< the size should be enough */
1141
  int f;
1142
  int result, i, len;
1143

1144
  f = open("/proc/stat", O_RDONLY);
39✔
1145
  if (f < 0) {
39✔
1146
    WARN("Could not open /proc/stat\n", 0);
×
1147
    /* Assume an uniprocessor. */
1148
    return 1;
39✔
1149
  }
1150
  len = STAT_READ(f, stat_buf, sizeof(stat_buf) - 1);
39✔
1151
  /* Unlikely that we need to retry because of an incomplete read here. */
1152
  if (len < 0) {
39✔
1153
    WARN("Failed to read /proc/stat, errno= %" WARN_PRIdPTR "\n",
×
1154
         (GC_signed_word)errno);
1155
    close(f);
×
1156
    return 1;
×
1157
  }
1158
  /* Avoid potential buffer overrun by `atoi()`. */
1159
  stat_buf[len] = '\0';
39✔
1160

1161
  close(f);
39✔
1162

1163
  /*
1164
   * Some old kernels only have a single "cpu nnnn ..." entry in
1165
   * `/proc/stat` pseudo-file.  We identify those as uniprocessors.
1166
   */
1167
  result = 1;
39✔
1168

1169
  for (i = 0; i < len - 4; ++i) {
45,860✔
1170
    if (stat_buf[i] == '\n' && stat_buf[i + 1] == 'c' && stat_buf[i + 2] == 'p'
45,821✔
1171
        && stat_buf[i + 3] == 'u') {
78✔
1172
      int cpu_no = atoi(&stat_buf[i + 4]);
78✔
1173
      if (cpu_no >= result)
78✔
1174
        result = cpu_no + 1;
39✔
1175
    }
1176
  }
1177
  return result;
39✔
1178
}
1179

1180
#  elif defined(DGUX)
1181
/*
1182
 * Return the number of processors, or a non-positive value if the number
1183
 * cannot be determined.
1184
 */
1185
STATIC int
1186
GC_get_nprocs(void)
1187
{
1188
  int numCpus;
1189
  struct dg_sys_info_pm_info pm_sysinfo;
1190
  int status = 0;
1191

1192
  status = dg_sys_info((long int *)&pm_sysinfo, DG_SYS_INFO_PM_INFO_TYPE,
1193
                       DG_SYS_INFO_PM_CURRENT_VERSION);
1194
  if (status < 0) {
1195
    /* Set -1 for an error. */
1196
    numCpus = -1;
1197
  } else {
1198
    /* Active CPUs. */
1199
    numCpus = pm_sysinfo.idle_vp_count;
1200
  }
1201
  return numCpus;
1202
}
1203

1204
#  elif defined(ANY_BSD) || defined(DARWIN)
1205
STATIC int
1206
GC_get_nprocs(void)
1207
{
1208
  int mib[] = { CTL_HW, HW_NCPU };
1209
  int res;
1210
  size_t len = sizeof(res);
1211

1212
  sysctl(mib, sizeof(mib) / sizeof(int), &res, &len, NULL, 0);
1213
  return res;
1214
}
1215

1216
#  else
1217
/* E.g., RTEMS. */
1218
/* TODO: Implement. */
1219
#    define GC_get_nprocs() 1
1220
#  endif
1221

1222
#  if defined(LINUX) && defined(ARM32)
1223
/*
1224
 * Some buggy Linux/arm kernels show only non-sleeping CPUs in
1225
 * `/proc/stat` pseudo-file (and in `/proc/cpuinfo` pseudo-file), so
1226
 * another data system source is tried first.  Returns a non-positive
1227
 * value on error.
1228
 */
1229
STATIC int
1230
GC_get_nprocs_present(void)
1231
{
1232
  char stat_buf[16];
1233
  int f;
1234
  int len;
1235

1236
  f = open("/sys/devices/system/cpu/present", O_RDONLY);
1237
  if (f < 0) {
1238
    /* Cannot open the file. */
1239
    return -1;
1240
  }
1241

1242
  len = STAT_READ(f, stat_buf, sizeof(stat_buf));
1243
  close(f);
1244

1245
  /*
1246
   * Recognized file format: "0\n" or "0-<max_cpu_num>\n".
1247
   * The file might probably contain a comma-separated list
1248
   * but we do not need to handle it (just silently ignore).
1249
   */
1250
  if (len < 2 || stat_buf[0] != '0' || stat_buf[len - 1] != '\n') {
1251
    /* A read error or an unrecognized contents. */
1252
    return 0;
1253
  } else if (len == 2) {
1254
    /* An uniprocessor. */
1255
    return 1;
1256
  } else if (stat_buf[1] != '-') {
1257
    /* An unrecognized contents. */
1258
    return 0;
1259
  }
1260

1261
  /* Terminate the string. */
1262
  stat_buf[len - 1] = '\0';
1263

1264
  /* Skip "0-" and parse `max_cpu_num`. */
1265
  return atoi(&stat_buf[2]) + 1;
1266
}
1267
#  endif /* LINUX && ARM32 */
1268

1269
#  if defined(CAN_HANDLE_FORK) && defined(THREAD_SANITIZER)
1270
#    include "private/gc_pmark.h" /*< for `MS_NONE` */
1271

1272
/*
1273
 * Workaround for TSan which does not notice that the allocator lock
1274
 * is acquired in `fork_prepare_proc()`.
1275
 */
1276
GC_ATTR_NO_SANITIZE_THREAD
1277
static GC_bool
1278
collection_in_progress(void)
1279
{
1280
  return GC_mark_state != MS_NONE;
1281
}
1282
#  else
1283
#    define collection_in_progress() GC_collection_in_progress()
1284
#  endif
1285

1286
GC_INNER void
1287
GC_wait_for_gc_completion(GC_bool wait_for_all)
102,711✔
1288
{
1289
#  if !defined(THREAD_SANITIZER) || !defined(CAN_CALL_ATFORK)
1290
  /*
1291
   * `GC_lock_holder` is accessed with the allocator lock held, so
1292
   * there is no data race actually (unlike what is reported by TSan).
1293
   */
1294
  GC_ASSERT(I_HOLD_LOCK());
102,711✔
1295
#  endif
1296
  ASSERT_CANCEL_DISABLED();
102,711✔
1297
#  ifdef GC_DISABLE_INCREMENTAL
1298
  (void)wait_for_all;
1299
#  else
1300
  if (GC_incremental && collection_in_progress()) {
102,711✔
1301
    word old_gc_no = GC_gc_no;
×
1302

1303
    /*
1304
     * Make sure that no part of our stack is still on the mark stack,
1305
     * since it is about to be unmapped.
1306
     */
1307
#    ifdef LINT2
1308
    /*
1309
     * Note: do not transform this `if`-`do`-`while` construction into
1310
     * a single while statement because it might cause some static code
1311
     * analyzers to report a false positive (FP) code defect about
1312
     * missing unlock after lock.
1313
     */
1314
#    endif
1315
    do {
1316
      GC_ASSERT(!GC_in_thread_creation);
×
1317
      GC_in_thread_creation = TRUE;
×
1318
      GC_collect_a_little_inner(1);
×
1319
      GC_in_thread_creation = FALSE;
×
1320

1321
      UNLOCK();
×
1322
#    ifdef GC_WIN32_THREADS
1323
      Sleep(0);
1324
#    else
1325
      sched_yield();
×
1326
#    endif
1327
      LOCK();
×
1328
    } while (GC_incremental && collection_in_progress()
×
1329
             && (wait_for_all || old_gc_no == GC_gc_no));
×
1330
  }
1331
#  endif
1332
}
102,711✔
1333

1334
#  if defined(GC_ASSERTIONS) && defined(GC_PTHREADS_PARAMARK)
1335
STATIC unsigned long GC_mark_lock_holder = NO_THREAD;
1336
#  endif
1337

1338
#  ifdef CAN_HANDLE_FORK
1339

1340
/*
1341
 * Procedures called before and after a process fork.  The goal here is
1342
 * to make it safe to call `GC_malloc()` in the forked child process.
1343
 * It is unclear that is attainable, since the Single UNIX Specification
1344
 * seems to imply that one should only call async-signal-safe functions,
1345
 * and we probably cannot quite guarantee that.  But we give it our best
1346
 * shot.  (That same specification also implies that it is not safe to
1347
 * call the system `malloc` between `fork` and `exec`.  Thus we are doing
1348
 * no worse than it.)
1349
 */
1350

1351
IF_CANCEL(static int fork_cancel_state;) /*< protected by the allocator lock */
1352

1353
#    ifdef PARALLEL_MARK
1354
#      ifdef THREAD_SANITIZER
1355
#        if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK)
1356
STATIC void GC_generic_lock(pthread_mutex_t *);
1357
#        endif
1358
GC_ATTR_NO_SANITIZE_THREAD
1359
static void wait_for_reclaim_atfork(void);
1360
#      else
1361
#        define wait_for_reclaim_atfork() GC_wait_for_reclaim()
1362
#      endif
1363
#    endif /* PARALLEL_MARK */
1364

1365
/*
1366
 * Prevent TSan false positive (FP) about the race during items removal
1367
 * from `GC_threads`.  (The race cannot happen since only one thread
1368
 * survives in the child process.)
1369
 */
1370
#    ifdef CAN_CALL_ATFORK
1371
GC_ATTR_NO_SANITIZE_THREAD
1372
#    endif
1373
static void
1374
store_to_threads_table(int hv, GC_thread me)
×
1375
{
1376
  GC_threads[hv] = me;
×
1377
}
×
1378

1379
/*
1380
 * Remove all entries from the `GC_threads` table, except the one (if any)
1381
 * for the current thread.  Also update thread identifiers stored in the
1382
 * table for the current thread.  We need to do this in the child process
1383
 * after a `fork()`, since only the current thread survives in the child
1384
 * process.
1385
 */
1386
STATIC void
1387
GC_remove_all_threads_but_me(void)
×
1388
{
1389
  int hv;
1390
  GC_thread me = NULL;
×
1391
#    ifndef GC_WIN32_THREADS
1392
#      define pthread_id id
1393
#    endif
1394

1395
  for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) {
×
1396
    GC_thread p, next;
1397

1398
    for (p = GC_threads[hv]; p != NULL; p = next) {
×
1399
      next = p->tm.next;
×
1400
      if (THREAD_EQUAL(p->pthread_id, GC_parent_pthread_self) && me == NULL) {
×
1401
        /* Ignore dead threads with the same id. */
1402
        me = p;
×
1403
        p->tm.next = NULL;
×
1404
      } else {
1405
#    ifdef THREAD_LOCAL_ALLOC
1406
        if (!KNOWN_FINISHED(p)) {
×
1407
          /*
1408
           * Cannot call `GC_destroy_thread_local` here.  The free
1409
           * lists may be in an inconsistent state (as thread `p` may
1410
           * be updating one of the lists by `GC_generic_malloc_many()`
1411
           * or `GC_FAST_MALLOC_GRANS()` when `fork()` is invoked).
1412
           * This should not be a problem because the lost elements
1413
           * of the free lists will be collected during GC.
1414
           */
1415
          GC_remove_specific_after_fork(GC_thread_key, p->pthread_id);
×
1416
        }
1417
#    endif
1418
        /*
1419
         * TODO: To avoid TSan hang (when updating `GC_bytes_freed`),
1420
         * we just skip explicit freeing of `GC_threads` entries.
1421
         */
1422
#    if !defined(THREAD_SANITIZER) || !defined(CAN_CALL_ATFORK)
1423
        if (p != &first_thread) {
×
1424
          /* TODO: Should call `mach_port_deallocate`? */
1425
          GC_ASSERT(p->crtn != &first_crtn);
×
1426
          GC_INTERNAL_FREE(p->crtn);
×
1427
          GC_INTERNAL_FREE(p);
×
1428
        }
1429
#    endif
1430
      }
1431
    }
1432
    store_to_threads_table(hv, NULL);
×
1433
  }
1434

1435
  if (NULL == me)
×
1436
    return; /*< `fork()` is called from an unregistered thread */
×
1437

1438
  /*
1439
   * Update `pthreads` id as it is not guaranteed to be the same between
1440
   * this (child) process and the parent one.
1441
   */
1442
  me->pthread_id = pthread_self();
×
1443
#    ifdef GC_WIN32_THREADS
1444
  /*
1445
   * Update Win32 thread id and handle.  They differ from that in the
1446
   * parent process.
1447
   */
1448
  me->id = thread_id_self();
1449
#      ifndef MSWINCE
1450
  if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
1451
                       GetCurrentProcess(), (HANDLE *)&me->handle,
1452
                       0 /* `dwDesiredAccess` */, FALSE /* `bInheritHandle` */,
1453
                       DUPLICATE_SAME_ACCESS))
1454
    ABORT("DuplicateHandle failed");
1455
#      endif
1456
#    endif
1457
#    ifdef DARWIN
1458
  /*
1459
   * Update thread id after process fork (it is OK to call
1460
   * `GC_destroy_thread_local()` and `GC_free_internal()` before update).
1461
   */
1462
  me->mach_thread = mach_thread_self();
1463
#    endif
1464
#    ifdef USE_TKILL_ON_ANDROID
1465
  me->kernel_id = gettid();
1466
#    endif
1467

1468
  /* Put `me` back to `GC_threads`. */
1469
  store_to_threads_table(THREAD_TABLE_INDEX(me->id), me);
×
1470

1471
#    ifdef THREAD_LOCAL_ALLOC
1472
#      ifdef USE_CUSTOM_SPECIFIC
1473
  GC_update_specific_after_fork(GC_thread_key);
×
1474
#      else
1475
  /*
1476
   * Some TLS implementations (e.g., in Cygwin) might be not `fork`-friendly,
1477
   * so we re-assign thread-local pointer to `tlfs` for safety instead of the
1478
   * assertion check (again, it is OK to call `GC_destroy_thread_local()` and
1479
   * `GC_free_internal()` before).
1480
   */
1481
  {
1482
    int res = GC_setspecific(GC_thread_key, &me->tlfs);
1483

1484
    if (COVERT_DATAFLOW(res) != 0)
1485
      ABORT("GC_setspecific failed (in child)");
1486
  }
1487
#      endif
1488
#    endif
1489
#    undef pthread_id
1490
}
1491

1492
/*
1493
 * Same as `GC_thread_is_registered()` but assumes the allocator lock is held.
1494
 */
1495
static GC_bool
1496
is_thread_registered_inner(void)
63✔
1497
{
1498
  GC_thread me = GC_self_thread_inner();
63✔
1499

1500
  return me != NULL && !KNOWN_FINISHED(me);
63✔
1501
}
1502

1503
/* Called before a `fork()`. */
1504
#    if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK)
1505
/* `GC_lock_holder` is updated safely (no data race actually). */
1506
GC_ATTR_NO_SANITIZE_THREAD
1507
#    endif
1508
static void
1509
fork_prepare_proc(void)
63✔
1510
{
1511
#    if defined(GC_EXPLICIT_SIGNALS_UNBLOCK) && defined(CAN_CALL_ATFORK)
1512
  /*
1513
   * The signals might be blocked by `fork()` implementation when the
1514
   * at-fork prepare handler is invoked.
1515
   */
1516
  if (GC_handle_fork == 1)
1517
    GC_unblock_gc_signals();
1518
#    endif
1519

1520
  /*
1521
   * Acquire all relevant locks, so that after releasing the locks the child
1522
   * process will see a consistent state in which monitor invariants hold.
1523
   * Unfortunately, we cannot acquire `libc` locks we might need, and there
1524
   * seems to be no guarantee that `libc` must install a suitable `fork`
1525
   * handler.  Wait for an ongoing collection to finish, since we cannot
1526
   * finish it in the (one remaining thread in) the child process.
1527
   */
1528

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

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

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

1693
/*
1694
 * Routines for `fork()` handling by client (no-op if `pthread_atfork`
1695
 * works).
1696
 */
1697

1698
GC_API void GC_CALL
1699
GC_atfork_prepare(void)
63✔
1700
{
1701
  if (UNLIKELY(!GC_is_initialized))
63✔
1702
    GC_init();
×
1703
  if (GC_handle_fork <= 0)
63✔
1704
    fork_prepare_proc();
×
1705
}
63✔
1706

1707
GC_API void GC_CALL
1708
GC_atfork_parent(void)
63✔
1709
{
1710
  if (GC_handle_fork <= 0)
63✔
1711
    fork_parent_proc();
×
1712
}
63✔
1713

1714
GC_API void GC_CALL
1715
GC_atfork_child(void)
63✔
1716
{
1717
  if (GC_handle_fork <= 0)
63✔
1718
    fork_child_proc();
×
1719
}
63✔
1720

1721
GC_INNER_WIN32THREAD void
1722
GC_setup_atfork(void)
39✔
1723
{
1724
  if (GC_handle_fork) {
39✔
1725
#    ifdef CAN_CALL_ATFORK
1726
    if (pthread_atfork(fork_prepare_proc, fork_parent_proc, fork_child_proc)
39✔
1727
        == 0) {
1728
      /* Handlers successfully registered. */
1729
      GC_handle_fork = 1;
39✔
1730
    } else
1731
#    endif
1732
    /* else */ {
1733
      if (GC_handle_fork != -1)
×
1734
        ABORT("pthread_atfork failed");
×
1735
    }
1736
  }
1737
}
39✔
1738

1739
#  endif /* CAN_HANDLE_FORK */
1740

1741
#  ifdef INCLUDE_LINUX_THREAD_DESCR
1742
__thread int GC_dummy_thread_local;
1743
#  endif
1744

1745
#  ifdef PARALLEL_MARK
1746
#    ifndef GC_WIN32_THREADS
1747
static void setup_mark_lock(void);
1748
#    endif
1749

1750
GC_INNER_WIN32THREAD unsigned GC_required_markers_cnt = 0;
1751

1752
GC_API void GC_CALL
1753
GC_set_markers_count(unsigned markers)
3✔
1754
{
1755
  GC_required_markers_cnt = markers < MAX_MARKERS ? markers : MAX_MARKERS;
3✔
1756
}
3✔
1757
#  endif /* PARALLEL_MARK */
1758

1759
GC_INNER GC_bool GC_in_thread_creation = FALSE;
1760

1761
GC_INNER_WIN32THREAD void
1762
GC_record_stack_base(GC_stack_context_t crtn, const struct GC_stack_base *sb)
102,854✔
1763
{
1764
#  if !defined(DARWIN) && !defined(GC_WIN32_THREADS)
1765
  crtn->stack_ptr = (ptr_t)sb->mem_base;
102,854✔
1766
#  endif
1767
  if ((crtn->stack_end = (ptr_t)sb->mem_base) == NULL)
102,854✔
1768
    ABORT("Bad stack base in GC_register_my_thread");
×
1769
#  ifdef E2K
1770
  crtn->ps_ofs = (size_t)(GC_uintptr_t)sb->reg_base;
1771
#  elif defined(IA64)
1772
  crtn->backing_store_end = (ptr_t)sb->reg_base;
1773
#  elif defined(I386) && defined(GC_WIN32_THREADS)
1774
  crtn->initial_stack_base = (ptr_t)sb->mem_base;
1775
#  endif
1776
}
102,854✔
1777

1778
#  if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS) \
1779
      || !defined(DONT_USE_ATEXIT)
1780
GC_INNER_WIN32THREAD thread_id_t GC_main_thread_id;
1781
#  endif
1782

1783
#  ifndef DONT_USE_ATEXIT
1784
GC_INNER GC_bool
1785
GC_is_main_thread(void)
4✔
1786
{
1787
  GC_ASSERT(GC_thr_initialized);
4✔
1788
  return THREAD_ID_EQUAL(GC_main_thread_id, thread_id_self());
4✔
1789
}
1790
#  endif /* !DONT_USE_ATEXIT */
1791

1792
#  ifndef GC_WIN32_THREADS
1793

1794
STATIC GC_thread
1795
GC_register_my_thread_inner(const struct GC_stack_base *sb,
102,634✔
1796
                            thread_id_t self_id)
1797
{
1798
  GC_thread me;
1799

1800
  GC_ASSERT(I_HOLD_LOCK());
102,634✔
1801
  me = GC_new_thread(self_id);
102,634✔
1802
  me->id = self_id;
102,634✔
1803
#    ifdef DARWIN
1804
  me->mach_thread = mach_thread_self();
1805
#    endif
1806
  GC_record_stack_base(me->crtn, sb);
102,634✔
1807
  return me;
102,634✔
1808
}
1809

1810
/*
1811
 * Number of processors.  We may not have access to all of them, but
1812
 * this is as good a guess as any...
1813
 */
1814
STATIC int GC_nprocs = 1;
1815

1816
GC_INNER void
1817
GC_thr_init(void)
39✔
1818
{
1819
  GC_ASSERT(I_HOLD_LOCK());
39✔
1820
  GC_ASSERT(!GC_thr_initialized);
39✔
1821
  ASSERT_ALIGNMENT(&GC_threads);
1822
#    ifdef GC_ASSERTIONS
1823
  GC_thr_initialized = TRUE;
39✔
1824
#    endif
1825
#    ifdef CAN_HANDLE_FORK
1826
  GC_setup_atfork();
39✔
1827
#    endif
1828

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

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

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

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

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

1955
#    ifndef DARWIN
1956
  GC_stop_init();
39✔
1957
#    endif
1958

1959
#    ifdef PARALLEL_MARK
1960
  if (GC_available_markers_m1 <= 0) {
39✔
1961
    /* Disable parallel marking. */
1962
    GC_parallel = FALSE;
×
1963
    GC_COND_LOG_PRINTF("Single marker thread, turning off parallel marking\n");
×
1964
  } else {
1965
    setup_mark_lock();
39✔
1966
  }
1967
#    endif
1968

1969
  /* Add the initial thread, so we can stop it. */
1970
  {
1971
    struct GC_stack_base sb;
1972
    GC_thread me;
1973
    thread_id_t self_id = thread_id_self();
39✔
1974

1975
    sb.mem_base = GC_stackbottom;
39✔
1976
    GC_ASSERT(sb.mem_base != NULL);
39✔
1977
#    if defined(E2K) || defined(IA64)
1978
    sb.reg_base = GC_register_stackbottom;
1979
#    endif
1980
    GC_ASSERT(NULL == GC_self_thread_inner());
39✔
1981
    me = GC_register_my_thread_inner(&sb, self_id);
39✔
1982
#    ifndef DONT_USE_ATEXIT
1983
    GC_main_thread_id = self_id;
39✔
1984
#    endif
1985
    me->flags = DETACHED;
39✔
1986
  }
1987
}
39✔
1988

1989
#  endif /* !GC_WIN32_THREADS */
1990

1991
GC_INNER void
1992
GC_init_parallel(void)
39✔
1993
{
1994
#  ifdef THREAD_LOCAL_ALLOC
1995
  GC_thread me;
1996

1997
  GC_ASSERT(GC_is_initialized);
39✔
1998
  LOCK();
39✔
1999
  me = GC_self_thread_inner();
39✔
2000
  GC_init_thread_local(&me->tlfs);
39✔
2001
  UNLOCK();
39✔
2002
#  endif
2003
#  if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS)
2004
  if (GC_win32_dll_threads) {
2005
    /*
2006
     * Cannot intercept thread creation.  Hence we do not know if other
2007
     * threads exist.  However, client is not allowed to create other threads
2008
     * before collector initialization.  Thus it is OK not to lock before
2009
     * this.
2010
     */
2011
    set_need_to_lock();
2012
  }
2013
#  endif
2014
}
39✔
2015

2016
#  if !defined(GC_NO_PTHREAD_SIGMASK) && defined(GC_PTHREADS)
2017
#    define GC_wrap_pthread_sigmask WRAP_FUNC(pthread_sigmask)
2018
GC_API int
2019
GC_wrap_pthread_sigmask(int how, const sigset_t *set, sigset_t *old_set)
6✔
2020
{
2021
#    ifdef GC_WIN32_THREADS
2022
  /*
2023
   * `pthreads-win32` library does not support `sigmask`.
2024
   * So, nothing is required here...
2025
   */
2026
#    else
2027
  sigset_t fudged_set;
2028

2029
  INIT_REAL_SYMS();
2030
  if (LIKELY(set != NULL) && (how == SIG_BLOCK || how == SIG_SETMASK)) {
6✔
2031
    int sig_suspend = GC_get_suspend_signal();
3✔
2032

2033
    fudged_set = *set;
3✔
2034
    GC_ASSERT(sig_suspend >= 0);
3✔
2035
    if (sigdelset(&fudged_set, sig_suspend) != 0)
3✔
2036
      ABORT("sigdelset failed");
×
2037
    set = &fudged_set;
3✔
2038
  }
2039
#    endif
2040
  return REAL_FUNC(pthread_sigmask)(how, set, old_set);
6✔
2041
}
2042
#    undef GC_wrap_pthread_sigmask
2043
#  endif /* !GC_NO_PTHREAD_SIGMASK */
2044

2045
/*
2046
 * Wrapper for functions that are likely to block for an appreciable
2047
 * length of time.
2048
 */
2049

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

2071
#  else /* !E2K */
2072
static void
2073
do_blocking_enter(GC_bool *pTopOfStackUnset, GC_thread me)
1,449✔
2074
{
2075
#    if defined(SPARC) || defined(IA64)
2076
  ptr_t bs_hi = GC_save_regs_in_stack();
2077
  /* TODO: Registers saving already done by `GC_with_callee_saves_pushed`. */
2078
#    endif
2079
  GC_stack_context_t crtn = me->crtn;
1,449✔
2080

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

2107
static void
2108
do_blocking_leave(GC_thread me, GC_bool topOfStackUnset)
1,449✔
2109
{
2110
  GC_ASSERT(I_HOLD_READER_LOCK());
1,449✔
2111
  me->flags &= (unsigned char)~DO_BLOCKING;
1,449✔
2112
#  ifdef E2K
2113
  {
2114
    GC_stack_context_t crtn = me->crtn;
2115

2116
    GC_ASSERT(crtn->backing_store_end != NULL);
2117
    crtn->backing_store_ptr = NULL;
2118
    crtn->backing_store_end = NULL;
2119
  }
2120
#  endif
2121
#  if defined(DARWIN) && !defined(DARWIN_DONT_PARSE_STACK)
2122
  if (topOfStackUnset) {
2123
    /* Make it unset again. */
2124
    me->crtn->topOfStack = NULL;
2125
  }
2126
#  else
2127
  (void)topOfStackUnset;
2128
#  endif
2129
}
1,449✔
2130

2131
GC_INNER void
2132
GC_do_blocking_inner(ptr_t data, void *context)
126✔
2133
{
2134
  GC_thread me;
2135
  GC_bool topOfStackUnset;
2136

2137
  UNUSED_ARG(context);
2138
  READER_LOCK();
126✔
2139
  me = GC_self_thread_inner();
126✔
2140
  do_blocking_enter(&topOfStackUnset, me);
126✔
2141
  READER_UNLOCK_RELEASE();
126✔
2142

2143
  ((struct blocking_data *)data)->client_data /*< result */
2144
      = ((struct blocking_data *)data)
252✔
2145
            ->fn(((struct blocking_data *)data)->client_data);
126✔
2146

2147
  /* This will block if the world is stopped. */
2148
  READER_LOCK();
126✔
2149

2150
#  ifdef LINT2
2151
  {
2152
#    ifdef GC_ASSERTIONS
2153
    GC_thread saved_me = me;
2154
#    endif
2155

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

2176
    READER_UNLOCK_RELEASE();
×
2177
    GC_suspend_self_inner(me, suspend_cnt);
×
2178
    READER_LOCK();
×
2179
  }
2180
#  endif
2181
  do_blocking_leave(me, topOfStackUnset);
126✔
2182
  READER_UNLOCK_RELEASE();
126✔
2183
}
126✔
2184

2185
#  if defined(GC_ENABLE_SUSPEND_THREAD) && defined(SIGNAL_BASED_STOP_WORLD)
2186
GC_INNER void
2187
GC_suspend_self_blocked(ptr_t thread_me, void *context)
1,323✔
2188
{
2189
  GC_thread me = (GC_thread)thread_me;
1,323✔
2190
  GC_bool topOfStackUnset;
2191

2192
  UNUSED_ARG(context);
2193

2194
  /*
2195
   * The caller holds the allocator lock in the exclusive mode, thus
2196
   * we require and restore it to the same mode upon return from the
2197
   * function.
2198
   */
2199
  GC_ASSERT(I_HOLD_LOCK());
1,323✔
2200

2201
  do_blocking_enter(&topOfStackUnset, me);
1,323✔
2202
  while ((me->ext_suspend_cnt & 1) != 0) {
3,969✔
2203
    size_t suspend_cnt = me->ext_suspend_cnt;
1,323✔
2204

2205
    UNLOCK();
1,323✔
2206
    GC_suspend_self_inner(me, suspend_cnt);
1,323✔
2207
    LOCK();
1,323✔
2208
  }
2209
  do_blocking_leave(me, topOfStackUnset);
1,323✔
2210
}
1,323✔
2211
#  endif /* GC_ENABLE_SUSPEND_THREAD */
2212

2213
GC_API void GC_CALL
2214
GC_set_stackbottom(void *gc_thread_handle, const struct GC_stack_base *sb)
63✔
2215
{
2216
  GC_thread t = (GC_thread)gc_thread_handle;
63✔
2217
  GC_stack_context_t crtn;
2218

2219
  GC_ASSERT(sb->mem_base != NULL);
63✔
2220
  if (UNLIKELY(!GC_is_initialized)) {
63✔
2221
    GC_ASSERT(NULL == t);
×
2222
    /* Alter the stack bottom of the primordial thread. */
2223
    GC_stackbottom = (char *)sb->mem_base;
×
2224
#  if defined(E2K) || defined(IA64)
2225
    GC_register_stackbottom = (ptr_t)sb->reg_base;
2226
#  endif
2227
    return;
×
2228
  }
2229

2230
  GC_ASSERT(I_HOLD_READER_LOCK());
63✔
2231
  if (NULL == t) {
63✔
2232
    /* The current thread. */
2233
    t = GC_self_thread_inner();
×
2234
  }
2235
  GC_ASSERT(!KNOWN_FINISHED(t));
63✔
2236
  crtn = t->crtn;
63✔
2237
  GC_ASSERT((t->flags & DO_BLOCKING) == 0
63✔
2238
            && NULL == crtn->traced_stack_sect); /*< for now */
2239

2240
  crtn->stack_end = (ptr_t)sb->mem_base;
63✔
2241
#  ifdef E2K
2242
  crtn->ps_ofs = (size_t)(GC_uintptr_t)sb->reg_base;
2243
#  elif defined(IA64)
2244
  crtn->backing_store_end = (ptr_t)sb->reg_base;
2245
#  endif
2246
#  ifdef GC_WIN32_THREADS
2247
  /* Reset the known minimum (hottest address in the stack). */
2248
  crtn->last_stack_min = ADDR_LIMIT;
2249
#  endif
2250
}
2251

2252
GC_API void *GC_CALL
2253
GC_get_my_stackbottom(struct GC_stack_base *sb)
63✔
2254
{
2255
  GC_thread me;
2256
  GC_stack_context_t crtn;
2257

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

2273
GC_ATTR_NOINLINE
2274
GC_API void *GC_CALL
2275
GC_call_with_gc_active(GC_fn_type fn, void *client_data)
126✔
2276
{
2277
  struct GC_traced_stack_sect_s stacksect;
2278
  GC_thread me;
2279
  GC_stack_context_t crtn;
2280
  ptr_t stack_end;
2281
#  ifdef E2K
2282
  ptr_t saved_bs_ptr, saved_bs_end;
2283
  size_t saved_ps_ofs;
2284
#  endif
2285

2286
  /* This will block if the world is stopped. */
2287
  READER_LOCK();
126✔
2288

2289
  me = GC_self_thread_inner();
126✔
2290
  crtn = me->crtn;
126✔
2291

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

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

2316
#  if defined(GC_ENABLE_SUSPEND_THREAD) && defined(SIGNAL_BASED_STOP_WORLD)
2317
  while (UNLIKELY((me->ext_suspend_cnt & 1) != 0)) {
126✔
2318
    size_t suspend_cnt = me->ext_suspend_cnt;
×
2319

2320
    READER_UNLOCK_RELEASE();
×
2321
    GC_suspend_self_inner(me, suspend_cnt);
×
2322
    READER_LOCK();
×
2323
    GC_ASSERT(me->crtn == crtn);
×
2324
  }
2325
#  endif
2326

2327
  /* Setup new "stack section". */
2328
  stacksect.saved_stack_ptr = crtn->stack_ptr;
126✔
2329
#  ifdef E2K
2330
  GC_ASSERT(crtn->backing_store_end != NULL);
2331
  {
2332
    unsigned long long sz_ull;
2333

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

2356
  READER_UNLOCK_RELEASE();
126✔
2357
  client_data = (*(GC_fn_type volatile *)&fn)(client_data);
126✔
2358
  GC_ASSERT((me->flags & DO_BLOCKING) == 0);
126✔
2359

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

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

2420
GC_API int GC_CALL
2421
GC_unregister_my_thread(void)
49,474✔
2422
{
2423
  GC_thread me;
2424
  IF_CANCEL(int cancel_state;)
2425

2426
  /*
2427
   * Client should not unregister the thread explicitly
2428
   * if it is registered by `DllMain`, except for the main thread.
2429
   */
2430
#  if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS)
2431
  GC_ASSERT(!GC_win32_dll_threads
2432
            || THREAD_ID_EQUAL(GC_main_thread_id, thread_id_self()));
2433
#  endif
2434

2435
  LOCK();
49,474✔
2436
  DISABLE_CANCEL(cancel_state);
49,473✔
2437
  /*
2438
   * Wait for any collection that may be marking from our stack to complete
2439
   * before we remove this thread.
2440
   */
2441
  GC_wait_for_gc_completion(FALSE);
49,473✔
2442
  me = GC_self_thread_inner();
49,473✔
2443
  GC_ASSERT(THREAD_ID_EQUAL(me->id, thread_id_self()));
49,473✔
2444
  GC_unregister_my_thread_inner(me);
49,473✔
2445
  RESTORE_CANCEL(cancel_state);
49,473✔
2446
  UNLOCK();
49,473✔
2447
  return GC_SUCCESS;
49,473✔
2448
}
2449

2450
#  if !defined(GC_NO_PTHREAD_CANCEL) && defined(GC_PTHREADS)
2451
/*
2452
 * We should deal with the fact that apparently on Solaris and, probably,
2453
 * on some Linux we cannot collect while a thread is exiting, since
2454
 * signals are not handled properly.  This currently gives rise to deadlocks.
2455
 * The only workaround seen is to intercept `pthread_cancel()` and
2456
 * `pthread_exit()`, and disable the collections until the thread exit
2457
 * handler is called.  That is ugly, because we risk growing the heap
2458
 * unnecessarily.  But it seems that we do not really have an option in that
2459
 * the process is not in a fully functional state while a thread is exiting.
2460
 */
2461
#    define GC_wrap_pthread_cancel WRAP_FUNC(pthread_cancel)
2462
GC_API int
2463
GC_wrap_pthread_cancel(pthread_t thread)
882✔
2464
{
2465
#    ifdef CANCEL_SAFE
2466
  GC_thread t;
2467
#    endif
2468

2469
  INIT_REAL_SYMS();
2470
#    ifdef CANCEL_SAFE
2471
  LOCK();
882✔
2472
  t = GC_lookup_by_pthread(thread);
882✔
2473
  /*
2474
   * We test `DISABLED_GC` because `pthread_exit` could be called at
2475
   * the same time.  (If `t` is `NULL`, then `pthread_cancel()` should
2476
   * return `ESRCH`.)
2477
   */
2478
  if (t != NULL && (t->flags & DISABLED_GC) == 0) {
882✔
2479
    t->flags |= DISABLED_GC;
882✔
2480
    GC_dont_gc++;
882✔
2481
  }
2482
  UNLOCK();
882✔
2483
#    endif
2484
  return REAL_FUNC(pthread_cancel)(thread);
882✔
2485
}
2486
#    undef GC_wrap_pthread_cancel
2487
#  endif /* !GC_NO_PTHREAD_CANCEL */
2488

2489
#  ifdef GC_HAVE_PTHREAD_EXIT
2490
#    define GC_wrap_pthread_exit WRAP_FUNC(pthread_exit)
2491
GC_API GC_PTHREAD_EXIT_ATTRIBUTE void
2492
GC_wrap_pthread_exit(void *retval)
1,323✔
2493
{
2494
  GC_thread me;
2495

2496
  INIT_REAL_SYMS();
2497
  LOCK();
1,323✔
2498
  me = GC_self_thread_inner();
1,323✔
2499
  /*
2500
   * We test `DISABLED_GC` because someone else could call `pthread_cancel()`
2501
   * at the same time.
2502
   */
2503
  if (me != NULL && (me->flags & DISABLED_GC) == 0) {
1,323✔
2504
    me->flags |= DISABLED_GC;
879✔
2505
    GC_dont_gc++;
879✔
2506
  }
2507
  UNLOCK();
1,323✔
2508

2509
  REAL_FUNC(pthread_exit)(retval);
1,323✔
2510
}
2511
#    undef GC_wrap_pthread_exit
2512
#  endif /* GC_HAVE_PTHREAD_EXIT */
2513

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

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

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

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

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

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

2550
GC_API int GC_CALL
2551
GC_register_my_thread(const struct GC_stack_base *sb)
49,718✔
2552
{
2553
  GC_thread me;
2554

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

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

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

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

2625
GC_INNER_PTHRSTART void
2626
GC_thread_exit_proc(void *arg)
53,097✔
2627
{
2628
  GC_thread me = (GC_thread)arg;
53,097✔
2629
  IF_CANCEL(int cancel_state;)
2630

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

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

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

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

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

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

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

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

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

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

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

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

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

2791
STATIC void *
2792
GC_pthread_start(void *arg)
53,098✔
2793
{
2794
#    ifdef INCLUDE_LINUX_THREAD_DESCR
2795
  struct GC_stack_base sb;
2796

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

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

2826
  GC_ASSERT(I_DONT_HOLD_LOCK());
53,337✔
2827
  INIT_REAL_SYMS();
2828
  if (UNLIKELY(!GC_is_initialized))
53,337✔
2829
    GC_init();
×
2830
  GC_ASSERT(GC_thr_initialized);
53,337✔
2831
  GC_ASSERT(NONNULL_PROC_NOT_ZERO(start_routine));
2832

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

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

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

2879
  if (attr != NULL) {
53,337✔
2880
    int detachstate;
2881

2882
    if (pthread_attr_getdetachstate(attr, &detachstate) != 0)
49,778✔
2883
      ABORT("pthread_attr_getdetachstate failed");
×
2884
    if (PTHREAD_CREATE_DETACHED == detachstate)
49,778✔
2885
      si.flags |= DETACHED;
49,718✔
2886
  }
2887

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

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

2907
    /* `pthread_create()` is not a cancellation point. */
2908
    DISABLE_CANCEL(cancel_state);
53,098✔
2909

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

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

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

2941
  for (i = 0; i < GC_PAUSE_SPIN_CYCLES; ++i) {
1,428,513,603✔
2942
    /* Something that is unlikely to be optimized away. */
2943
#    if defined(AO_HAVE_compiler_barrier) && !defined(BASE_ATOMIC_OPS_EMULATED)
2944
    AO_compiler_barrier();
1,298,648,730✔
2945
#    else
2946
    GC_noop1(i);
2947
#    endif
2948
  }
2949
}
129,864,873✔
2950
#  endif /* USE_SPIN_LOCK || !NO_PTHREAD_TRYLOCK */
2951

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

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

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

2984
STATIC void
2985
GC_generic_lock(pthread_mutex_t *lock)
126,221,297✔
2986
{
2987
#    ifndef NO_PTHREAD_TRYLOCK
2988
  unsigned pause_length = 1;
126,221,297✔
2989
  unsigned i;
2990

2991
  if (LIKELY(0 == pthread_mutex_trylock(lock))) {
126,221,297✔
2992
#      ifdef LOCK_STATS
2993
    (void)AO_fetch_and_add1(&GC_unlocked_count);
2994
#      endif
2995
    return;
118,715,122✔
2996
  }
2997
  for (; pause_length <= (unsigned)SPIN_MAX; pause_length <<= 1) {
16,676,219✔
2998
    for (i = 0; i < pause_length; ++i) {
146,419,693✔
2999
      GC_pause();
129,864,873✔
3000
    }
3001
    switch (pthread_mutex_trylock(lock)) {
16,554,820✔
3002
    case 0:
7,384,776✔
3003
#      ifdef LOCK_STATS
3004
      (void)AO_fetch_and_add1(&GC_spin_count);
3005
#      endif
3006
      return;
7,384,776✔
3007
    case EBUSY:
9,170,044✔
3008
      break;
9,170,044✔
3009
    default:
×
3010
      ABORT("Unexpected error from pthread_mutex_trylock");
×
3011
    }
3012
  }
3013
#    endif /* !NO_PTHREAD_TRYLOCK */
3014
#    ifdef LOCK_STATS
3015
  (void)AO_fetch_and_add1(&GC_block_count);
3016
#    endif
3017
  pthread_mutex_lock(lock);
121,399✔
3018
}
3019
#  endif /* !USE_SPIN_LOCK || ... */
3020

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

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

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

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

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

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

3052
static volatile AO_t spin_max = low_spin_max;
3053

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

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

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

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

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

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

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

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

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

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

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

3156
#  ifdef GC_PTHREADS_PARAMARK
3157

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

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

3178
static pthread_cond_t builder_cv = PTHREAD_COND_INITIALIZER;
3179

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

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

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

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

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

3240
GC_INNER void
3241
GC_wait_for_reclaim(void)
19,014✔
3242
{
3243
  GC_acquire_mark_lock();
19,014✔
3244
  while (GC_fl_builder_count > 0) {
20,083✔
3245
    GC_wait_builder();
1,069✔
3246
  }
3247
  GC_release_mark_lock();
19,014✔
3248
}
19,014✔
3249

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

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

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

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

3295
#  endif /* GC_PTHREADS_PARAMARK */
3296

3297
GC_INNER GC_on_thread_event_proc GC_on_thread_event = 0;
3298

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

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

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

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

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

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

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

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

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

3367
#  undef do_blocking_enter
3368

3369
#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