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

bdwgc / bdwgc / 2139

12 May 2026 09:29PM UTC coverage: 80.899% (+0.5%) from 80.39%
2139

push

travis-ci

ivmai
Eliminate 'condition always true' MSVC warning in win32_unprotect_thread

* pthread_support.c [GC_WIN32_THREADS && MPROTECT_VDB]
(GC_win32_unprotect_thread): Do not check `GC_win32_dll_threads`
variable if `GC_NO_THREADS_DISCOVERY`.

7255 of 8968 relevant lines covered (80.9%)

18926688.63 hits per line

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

76.44
/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)
30,898✔
298
{
299
  int i;
300
  GC_thread p;
301

302
  for (i = 0; i < THREAD_TABLE_SZ; ++i) {
7,940,786✔
303
    for (p = GC_threads[i]; p != NULL; p = p->tm.next) {
8,184,433✔
304
      if (!KNOWN_FINISHED(p))
274,545✔
305
        GC_mark_thread_local_fls_for(&p->tlfs);
252,976✔
306
    }
307
  }
308
}
30,898✔
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)
30,896✔
317
{
318
  int i;
319
  GC_thread p;
320

321
  for (i = 0; i < THREAD_TABLE_SZ; ++i) {
7,940,272✔
322
    for (p = GC_threads[i]; p != NULL; p = p->tm.next) {
8,183,890✔
323
      if (!KNOWN_FINISHED(p))
274,514✔
324
        GC_check_tls_for(&p->tlfs);
252,945✔
325
    }
326
  }
327
#      if defined(USE_CUSTOM_SPECIFIC)
328
  if (GC_thread_key != 0)
30,896✔
329
    GC_check_tsd_marks(GC_thread_key);
30,857✔
330
#      endif
331
}
30,896✔
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();
55✔
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,963✔
525
    if (my_mark_no - GC_mark_no > (word)2) {
2,041✔
526
      /* Resynchronize if we get far off, e.g. because `GC_mark_no` wrapped. */
527
      my_mark_no = GC_mark_no;
127✔
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,041✔
534
  }
535
}
536

537
GC_INNER_WIN32THREAD int GC_available_markers_m1 = 0;
538

539
#  endif /* PARALLEL_MARK */
540

541
#  ifdef GC_PTHREADS_PARAMARK
542

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

698
#  endif /* GC_PTHREADS_PARAMARK */
699

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

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

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

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

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

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

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

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

786
#    if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS)
787
  if (GC_win32_dll_threads)
788
    return -1; /*< not implemented */
789
#    endif
790
  GC_ASSERT(I_HOLD_READER_LOCK());
791
  for (i = 0; i < THREAD_TABLE_SZ; ++i) {
792
    GC_thread p;
793

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

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

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

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

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

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

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

923
GC_INNER GC_thread
924
GC_lookup_thread(thread_id_t id)
204,031,842✔
925
{
926
  GC_thread p;
927

928
#  if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS)
929
  if (GC_win32_dll_threads)
930
    return GC_win32_dll_lookup_thread(id);
931
#  endif
932
  for (p = GC_threads[THREAD_TABLE_INDEX(id)]; p != NULL; p = p->tm.next) {
204,205,575✔
933
    if (LIKELY(THREAD_ID_EQUAL(p->id, id)))
204,157,127✔
934
      break;
203,983,394✔
935
  }
936
  return p;
204,031,842✔
937
}
938

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1184
  close(f);
39✔
1185

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1361
#  ifdef CAN_HANDLE_FORK
1362

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

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

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

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

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

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

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

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

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

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

1494
#    ifdef THREAD_LOCAL_ALLOC
1495
#      ifdef USE_CUSTOM_SPECIFIC
1496
  GC_update_specific_after_fork(GC_thread_key);
×
1497
#      else
1498
  /*
1499
   * Some TLS implementations (e.g., in Cygwin) might be not `fork`-friendly,
1500
   * so we re-assign thread-local pointer to `tlfs` for safety instead of the
1501
   * assertion check (again, it is OK to call `GC_destroy_thread_local()` and
1502
   * `GC_free_internal()` before).
1503
   */
1504
  {
1505
    int res = GC_setspecific(GC_thread_key, &me->tlfs);
1506

1507
    if (COVERT_DATAFLOW(res) != 0)
1508
      ABORT("GC_setspecific failed (in child)");
1509
  }
1510
#      endif
1511
#    endif
1512
#    undef pthread_id
1513
}
1514

1515
/*
1516
 * Same as `GC_thread_is_registered()` but assumes the allocator lock is held.
1517
 */
1518
static GC_bool
1519
is_thread_registered_inner(void)
63✔
1520
{
1521
  GC_thread me = GC_self_thread_inner();
63✔
1522

1523
  return me != NULL && !KNOWN_FINISHED(me);
63✔
1524
}
1525

1526
/* Called before a `fork()`. */
1527
#    if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK)
1528
/* `GC_lock_holder` is updated safely (no data race actually). */
1529
GC_ATTR_NO_SANITIZE_THREAD
1530
#    endif
1531
static void
1532
fork_prepare_proc(void)
63✔
1533
{
1534
#    if defined(GC_EXPLICIT_SIGNALS_UNBLOCK) && defined(CAN_CALL_ATFORK)
1535
  /*
1536
   * The signals might be blocked by `fork()` implementation when the
1537
   * at-fork prepare handler is invoked.
1538
   */
1539
  if (GC_handle_fork == 1)
1540
    GC_unblock_gc_signals();
1541
#    endif
1542

1543
  /*
1544
   * Acquire all relevant locks, so that after releasing the locks the child
1545
   * process will see a consistent state in which monitor invariants hold.
1546
   * Unfortunately, we cannot acquire `libc` locks we might need, and there
1547
   * seems to be no guarantee that `libc` must install a suitable `fork`
1548
   * handler.  Wait for an ongoing collection to finish, since we cannot
1549
   * finish it in the (one remaining thread in) the child process.
1550
   */
1551

1552
  LOCK();
63✔
1553
  DISABLE_CANCEL(fork_cancel_state);
63✔
1554
  GC_parent_pthread_self = pthread_self();
63✔
1555
  /* The following waits may include cancellation points. */
1556
#    ifdef PARALLEL_MARK
1557
  if (GC_parallel)
63✔
1558
    wait_for_reclaim_atfork();
63✔
1559
#    endif
1560
  if (is_thread_registered_inner()) {
63✔
1561
    /* `fork()` is called from a thread registered in the collector. */
1562
    GC_wait_for_gc_completion(TRUE);
63✔
1563
  }
1564
#    ifdef PARALLEL_MARK
1565
  if (GC_parallel) {
63✔
1566
#      if defined(THREAD_SANITIZER) && defined(GC_ASSERTIONS) \
1567
          && defined(CAN_CALL_ATFORK)
1568
    /*
1569
     * Prevent TSan false positive (FP) about the data race when updating
1570
     * `GC_mark_lock_holder`.
1571
     */
1572
    GC_generic_lock(&mark_mutex);
1573
#      else
1574
    GC_acquire_mark_lock();
63✔
1575
#      endif
1576
  }
1577
#    endif
1578
  GC_acquire_dirty_lock();
1579
}
63✔
1580

1581
/*
1582
 * Called in the parent process after a `fork()` (even if the latter
1583
 * has failed).
1584
 */
1585
#    if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK)
1586
GC_ATTR_NO_SANITIZE_THREAD
1587
#    endif
1588
static void
1589
fork_parent_proc(void)
63✔
1590
{
1591
  GC_release_dirty_lock();
1592
#    ifdef PARALLEL_MARK
1593
  if (GC_parallel) {
63✔
1594
#      if defined(THREAD_SANITIZER) && defined(GC_ASSERTIONS) \
1595
          && defined(CAN_CALL_ATFORK)
1596
    /* To match that in `fork_prepare_proc`. */
1597
    (void)pthread_mutex_unlock(&mark_mutex);
1598
#      else
1599
    GC_release_mark_lock();
63✔
1600
#      endif
1601
  }
1602
#    endif
1603
  RESTORE_CANCEL(fork_cancel_state);
63✔
1604
#    ifdef GC_ASSERTIONS
1605
  BZERO(&GC_parent_pthread_self, sizeof(pthread_t));
63✔
1606
#    endif
1607
  UNLOCK();
63✔
1608
}
63✔
1609

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

1716
/*
1717
 * Routines for `fork()` handling by client (no-op if `pthread_atfork`
1718
 * works).
1719
 */
1720

1721
GC_API void GC_CALL
1722
GC_atfork_prepare(void)
63✔
1723
{
1724
  if (UNLIKELY(!GC_is_initialized))
63✔
1725
    GC_init();
×
1726
  if (GC_handle_fork <= 0)
63✔
1727
    fork_prepare_proc();
×
1728
}
63✔
1729

1730
GC_API void GC_CALL
1731
GC_atfork_parent(void)
63✔
1732
{
1733
  if (GC_handle_fork <= 0)
63✔
1734
    fork_parent_proc();
×
1735
}
63✔
1736

1737
GC_API void GC_CALL
1738
GC_atfork_child(void)
63✔
1739
{
1740
  if (GC_handle_fork <= 0)
63✔
1741
    fork_child_proc();
×
1742
}
63✔
1743

1744
GC_INNER_WIN32THREAD void
1745
GC_setup_atfork(void)
39✔
1746
{
1747
  if (GC_handle_fork) {
39✔
1748
#    ifdef CAN_CALL_ATFORK
1749
    if (pthread_atfork(fork_prepare_proc, fork_parent_proc, fork_child_proc)
39✔
1750
        == 0) {
1751
      /* Handlers successfully registered. */
1752
      GC_handle_fork = 1;
39✔
1753
    } else
1754
#    endif
1755
    /* else */ {
1756
      if (GC_handle_fork != -1)
×
1757
        ABORT("pthread_atfork failed");
×
1758
    }
1759
  }
1760
}
39✔
1761

1762
#  endif /* CAN_HANDLE_FORK */
1763

1764
#  ifdef INCLUDE_LINUX_THREAD_DESCR
1765
__thread int GC_dummy_thread_local;
1766
#  endif
1767

1768
#  ifdef PARALLEL_MARK
1769
#    ifndef GC_WIN32_THREADS
1770
static void setup_mark_lock(void);
1771
#    endif
1772

1773
GC_INNER_WIN32THREAD unsigned GC_required_markers_cnt = 0;
1774

1775
GC_API void GC_CALL
1776
GC_set_markers_count(unsigned markers)
3✔
1777
{
1778
  GC_required_markers_cnt = markers < MAX_MARKERS ? markers : MAX_MARKERS;
3✔
1779
}
3✔
1780
#  endif /* PARALLEL_MARK */
1781

1782
GC_INNER GC_bool GC_in_thread_creation = FALSE;
1783

1784
GC_INNER_WIN32THREAD void
1785
GC_record_stack_base(GC_stack_context_t crtn, const struct GC_stack_base *sb)
100,833✔
1786
{
1787
#  if !defined(DARWIN) && !defined(GC_WIN32_THREADS)
1788
  crtn->stack_ptr = (ptr_t)sb->mem_base;
100,833✔
1789
#  endif
1790
  if ((crtn->stack_end = (ptr_t)sb->mem_base) == NULL)
100,833✔
1791
    ABORT("Bad stack base in GC_register_my_thread");
×
1792
#  ifdef E2K
1793
  crtn->ps_ofs = (size_t)(GC_uintptr_t)sb->reg_base;
1794
#  elif defined(IA64)
1795
  crtn->backing_store_end = (ptr_t)sb->reg_base;
1796
#  elif defined(I386) && defined(GC_WIN32_THREADS)
1797
  crtn->initial_stack_base = (ptr_t)sb->mem_base;
1798
#  endif
1799
}
100,833✔
1800

1801
#  if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS) \
1802
      || !defined(DONT_USE_ATEXIT)
1803
GC_INNER_WIN32THREAD thread_id_t GC_main_thread_id;
1804
#  endif
1805

1806
#  ifndef DONT_USE_ATEXIT
1807
GC_INNER GC_bool
1808
GC_is_main_thread(void)
4✔
1809
{
1810
  GC_ASSERT(GC_thr_initialized);
4✔
1811
  return THREAD_ID_EQUAL(GC_main_thread_id, thread_id_self());
4✔
1812
}
1813
#  endif /* !DONT_USE_ATEXIT */
1814

1815
#  ifndef GC_WIN32_THREADS
1816

1817
STATIC GC_thread
1818
GC_register_my_thread_inner(const struct GC_stack_base *sb,
100,534✔
1819
                            thread_id_t self_id)
1820
{
1821
  GC_thread me;
1822

1823
  GC_ASSERT(I_HOLD_LOCK());
100,534✔
1824
  me = GC_new_thread(self_id);
100,534✔
1825
  me->id = self_id;
100,534✔
1826
#    ifdef DARWIN
1827
  me->mach_thread = mach_thread_self();
1828
#    endif
1829
  GC_record_stack_base(me->crtn, sb);
100,534✔
1830
  return me;
100,534✔
1831
}
1832

1833
/*
1834
 * Number of processors.  We may not have access to all of them, but
1835
 * this is as good a guess as any...
1836
 */
1837
STATIC int GC_nprocs = 1;
1838

1839
GC_INNER void
1840
GC_thr_init(void)
39✔
1841
{
1842
  GC_ASSERT(I_HOLD_LOCK());
39✔
1843
  GC_ASSERT(!GC_thr_initialized);
39✔
1844
  ASSERT_ALIGNMENT(&GC_threads);
1845
#    ifdef GC_ASSERTIONS
1846
  GC_thr_initialized = TRUE;
39✔
1847
#    endif
1848
#    ifdef CAN_HANDLE_FORK
1849
  GC_setup_atfork();
39✔
1850
#    endif
1851

1852
#    ifdef INCLUDE_LINUX_THREAD_DESCR
1853
  /*
1854
   * Explicitly register the region including the address of a thread-local
1855
   * variable.  This should include thread locals for the main thread,
1856
   * except for those allocated in response to `dlopen()` calls.
1857
   */
1858
  {
1859
    ptr_t thread_local_addr = (ptr_t)(&GC_dummy_thread_local);
1860
    ptr_t main_thread_start, main_thread_end;
1861
    if (!GC_enclosing_writable_mapping(thread_local_addr, &main_thread_start,
1862
                                       &main_thread_end)) {
1863
      ABORT("Failed to find TLS mapping for the primordial thread");
1864
    } else {
1865
      /* `main_thread_start` and `main_thread_end` are initialized. */
1866
      GC_add_roots_inner(main_thread_start, main_thread_end, FALSE);
1867
    }
1868
  }
1869
#    endif
1870

1871
  /* Set `GC_nprocs` and `GC_available_markers_m1` variables. */
1872
  {
1873
    const char *nprocs_string = GETENV("GC_NPROCS");
39✔
1874
    GC_nprocs = -1;
39✔
1875
    if (nprocs_string != NULL)
39✔
1876
      GC_nprocs = atoi(nprocs_string);
×
1877
  }
1878
  if (GC_nprocs <= 0
39✔
1879
#    if defined(LINUX) && defined(ARM32)
1880
      /* Workaround for some Linux/arm kernels. */
1881
      && (GC_nprocs = GC_get_nprocs_present()) <= 1
1882
#    endif
1883
  ) {
1884
    GC_nprocs = GC_get_nprocs();
39✔
1885
  }
1886
  if (GC_nprocs <= 0) {
39✔
1887
    WARN("GC_get_nprocs() returned %" WARN_PRIdPTR "\n",
×
1888
         (GC_signed_word)GC_nprocs);
1889
    /* Assume a dual-core CPU. */
1890
    GC_nprocs = 2;
×
1891
#    ifdef PARALLEL_MARK
1892
    /* But use only one marker. */
1893
    GC_available_markers_m1 = 0;
×
1894
#    endif
1895
  } else {
1896
#    ifdef PARALLEL_MARK
1897
    {
1898
      const char *markers_string = GETENV("GC_MARKERS");
39✔
1899
      int markers = GC_required_markers_cnt;
39✔
1900

1901
      if (markers_string != NULL) {
39✔
1902
        markers = atoi(markers_string);
×
1903
        if (markers <= 0 || markers > MAX_MARKERS) {
×
1904
          WARN("Too big or invalid number of mark threads: %" WARN_PRIdPTR
×
1905
               "; using maximum threads\n",
1906
               (GC_signed_word)markers);
1907
          markers = MAX_MARKERS;
×
1908
        }
1909
      } else if (0 == markers) {
39✔
1910
        /*
1911
         * Unless the client sets the desired number of parallel markers,
1912
         * it is determined based on the number of CPU cores.
1913
         */
1914
        markers = GC_nprocs;
39✔
1915
#      ifdef GC_MIN_MARKERS
1916
        /* This is primarily for targets without `getenv()`. */
1917
        if (markers < GC_MIN_MARKERS)
1918
          markers = GC_MIN_MARKERS;
1919
#      endif
1920
        if (markers > MAX_MARKERS) {
39✔
1921
          /* Silently limit the amount of markers. */
1922
          markers = MAX_MARKERS;
×
1923
        }
1924
      }
1925
      GC_available_markers_m1 = markers - 1;
39✔
1926
    }
1927
#    endif
1928
  }
1929
  GC_COND_LOG_PRINTF("Number of processors: %d\n", GC_nprocs);
39✔
1930

1931
#    if defined(BASE_ATOMIC_OPS_EMULATED) && defined(SIGNAL_BASED_STOP_WORLD)
1932
  /*
1933
   * Ensure the process is running on just one CPU core.  This is needed
1934
   * because the AO primitives emulated with locks cannot be used inside
1935
   * signal handlers.
1936
   */
1937
  {
1938
    cpu_set_t mask;
1939
    int cpu_set_cnt = 0;
1940
    int cpu_lowest_set = 0;
1941
#      ifdef RANDOM_ONE_CPU_CORE
1942
    int cpu_highest_set = 0;
1943
#      endif
1944
    /* Ensure at least 2 cores. */
1945
    int i = GC_nprocs > 1 ? GC_nprocs : 2;
1946

1947
    if (sched_getaffinity(0 /* current process */, sizeof(mask), &mask) == -1)
1948
      ABORT_ARG1("sched_getaffinity failed", ": errno= %d", errno);
1949
    while (i-- > 0)
1950
      if (CPU_ISSET(i, &mask)) {
1951
#      ifdef RANDOM_ONE_CPU_CORE
1952
        if (i + 1 != cpu_lowest_set)
1953
          cpu_highest_set = i;
1954
#      endif
1955
        cpu_lowest_set = i;
1956
        cpu_set_cnt++;
1957
      }
1958
    if (0 == cpu_set_cnt)
1959
      ABORT("sched_getaffinity returned empty mask");
1960
    if (cpu_set_cnt > 1) {
1961
#      ifdef RANDOM_ONE_CPU_CORE
1962
      if (cpu_lowest_set < cpu_highest_set) {
1963
        /* Pseudo-randomly adjust the bit to set among valid ones. */
1964
        cpu_lowest_set
1965
            += (unsigned)getpid() % (cpu_highest_set - cpu_lowest_set + 1);
1966
      }
1967
#      endif
1968
      CPU_ZERO(&mask);
1969
      /* Select just one CPU. */
1970
      CPU_SET(cpu_lowest_set, &mask);
1971
      if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
1972
        ABORT_ARG1("sched_setaffinity failed", ": errno= %d", errno);
1973
      WARN("CPU affinity mask is set to %p\n", (word)1 << cpu_lowest_set);
1974
    }
1975
  }
1976
#    endif /* BASE_ATOMIC_OPS_EMULATED */
1977

1978
#    ifndef DARWIN
1979
  GC_stop_init();
39✔
1980
#    endif
1981

1982
#    ifdef PARALLEL_MARK
1983
  if (GC_available_markers_m1 <= 0) {
39✔
1984
    /* Disable parallel marking. */
1985
    GC_parallel = FALSE;
×
1986
    GC_COND_LOG_PRINTF("Single marker thread, turning off parallel marking\n");
×
1987
  } else {
1988
    setup_mark_lock();
39✔
1989
  }
1990
#    endif
1991

1992
  /* Add the initial thread, so we can stop it. */
1993
  {
1994
    struct GC_stack_base sb;
1995
    GC_thread me;
1996
    thread_id_t self_id = thread_id_self();
39✔
1997

1998
    sb.mem_base = GC_stackbottom;
39✔
1999
    GC_ASSERT(sb.mem_base != NULL);
39✔
2000
#    if defined(E2K) || defined(IA64)
2001
    sb.reg_base = GC_register_stackbottom;
2002
#    endif
2003
    GC_ASSERT(NULL == GC_self_thread_inner());
39✔
2004
    me = GC_register_my_thread_inner(&sb, self_id);
39✔
2005
#    ifndef DONT_USE_ATEXIT
2006
    GC_main_thread_id = self_id;
39✔
2007
#    endif
2008
    me->flags = DETACHED;
39✔
2009
  }
2010
}
39✔
2011

2012
#  endif /* !GC_WIN32_THREADS */
2013

2014
GC_INNER void
2015
GC_init_parallel(void)
39✔
2016
{
2017
#  ifdef THREAD_LOCAL_ALLOC
2018
  GC_thread me;
2019

2020
  GC_ASSERT(GC_is_initialized);
39✔
2021
  LOCK();
39✔
2022
  me = GC_self_thread_inner();
39✔
2023
  GC_init_thread_local(&me->tlfs);
39✔
2024
  UNLOCK();
39✔
2025
#  endif
2026
#  if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS)
2027
  if (GC_win32_dll_threads) {
2028
    /*
2029
     * Cannot intercept thread creation.  Hence we do not know if other
2030
     * threads exist.  However, client is not allowed to create other threads
2031
     * before collector initialization.  Thus it is OK not to lock before
2032
     * this.
2033
     */
2034
    set_need_to_lock();
2035
  }
2036
#  endif
2037
}
39✔
2038

2039
#  if !defined(GC_NO_PTHREAD_SIGMASK) && defined(GC_PTHREADS)
2040
#    define GC_wrap_pthread_sigmask WRAP_FUNC(pthread_sigmask)
2041
GC_API int
2042
GC_wrap_pthread_sigmask(int how, const sigset_t *set, sigset_t *old_set)
6✔
2043
{
2044
#    ifdef GC_WIN32_THREADS
2045
  /*
2046
   * `pthreads-win32` library does not support `sigmask`.
2047
   * So, nothing is required here...
2048
   */
2049
#    else
2050
  sigset_t fudged_set;
2051

2052
  INIT_REAL_SYMS();
2053
  if (LIKELY(set != NULL) && (how == SIG_BLOCK || how == SIG_SETMASK)) {
6✔
2054
    int sig_suspend = GC_get_suspend_signal();
3✔
2055

2056
    fudged_set = *set;
3✔
2057
    GC_ASSERT(sig_suspend >= 0);
3✔
2058
    if (sigdelset(&fudged_set, sig_suspend) != 0)
3✔
2059
      ABORT("sigdelset failed");
×
2060
    set = &fudged_set;
3✔
2061
  }
2062
#    endif
2063
  return REAL_FUNC(pthread_sigmask)(how, set, old_set);
6✔
2064
}
2065
#    undef GC_wrap_pthread_sigmask
2066
#  endif /* !GC_NO_PTHREAD_SIGMASK */
2067

2068
/*
2069
 * Wrapper for functions that are likely to block for an appreciable
2070
 * length of time.
2071
 */
2072

2073
#  ifdef E2K
2074
/*
2075
 * Cannot be defined as a function because the stack-allocated buffer
2076
 * (pointed to by `bs_lo`) should be preserved till completion of
2077
 * `GC_do_blocking_inner` (or `GC_suspend_self_blocked`).
2078
 */
2079
#    define do_blocking_enter(pTopOfStackUnset, me)                   \
2080
      do {                                                            \
2081
        ptr_t bs_lo;                                                  \
2082
        size_t stack_size;                                            \
2083
        GC_stack_context_t crtn = (me)->crtn;                         \
2084
                                                                      \
2085
        *(pTopOfStackUnset) = FALSE;                                  \
2086
        crtn->stack_ptr = GC_approx_sp();                             \
2087
        GC_ASSERT(NULL == crtn->backing_store_end);                   \
2088
        GET_PROCEDURE_STACK_LOCAL(crtn->ps_ofs, &bs_lo, &stack_size); \
2089
        crtn->backing_store_end = bs_lo;                              \
2090
        crtn->backing_store_ptr = bs_lo + stack_size;                 \
2091
        (me)->flags |= DO_BLOCKING;                                   \
2092
      } while (0)
2093

2094
#  else /* !E2K */
2095
static void
2096
do_blocking_enter(GC_bool *pTopOfStackUnset, GC_thread me)
1,449✔
2097
{
2098
#    if defined(SPARC) || defined(IA64)
2099
  ptr_t bs_hi = GC_save_regs_in_stack();
2100
  /* TODO: Registers saving already done by `GC_with_callee_saves_pushed`. */
2101
#    endif
2102
  GC_stack_context_t crtn = me->crtn;
1,449✔
2103

2104
  GC_ASSERT(I_HOLD_READER_LOCK());
1,449✔
2105
  GC_ASSERT((me->flags & DO_BLOCKING) == 0);
1,449✔
2106
  *pTopOfStackUnset = FALSE;
1,449✔
2107
#    ifdef SPARC
2108
  crtn->stack_ptr = bs_hi;
2109
#    else
2110
  crtn->stack_ptr = GC_approx_sp();
1,449✔
2111
#    endif
2112
#    if defined(DARWIN) && !defined(DARWIN_DONT_PARSE_STACK)
2113
  if (NULL == crtn->topOfStack) {
2114
    /*
2115
     * `GC_do_blocking_inner` is not called recursively, so `topOfStack`
2116
     * should be computed now.
2117
     */
2118
    *pTopOfStackUnset = TRUE;
2119
    crtn->topOfStack = GC_FindTopOfStack(0);
2120
  }
2121
#    endif
2122
#    ifdef IA64
2123
  crtn->backing_store_ptr = bs_hi;
2124
#    endif
2125
  me->flags |= DO_BLOCKING;
1,449✔
2126
  /* Save context here if we want to support precise stack marking. */
2127
}
1,449✔
2128
#  endif /* !E2K */
2129

2130
static void
2131
do_blocking_leave(GC_thread me, GC_bool topOfStackUnset)
1,449✔
2132
{
2133
  GC_ASSERT(I_HOLD_READER_LOCK());
1,449✔
2134
  me->flags &= (unsigned char)~DO_BLOCKING;
1,449✔
2135
#  ifdef E2K
2136
  {
2137
    GC_stack_context_t crtn = me->crtn;
2138

2139
    GC_ASSERT(crtn->backing_store_end != NULL);
2140
    crtn->backing_store_ptr = NULL;
2141
    crtn->backing_store_end = NULL;
2142
  }
2143
#  endif
2144
#  if defined(DARWIN) && !defined(DARWIN_DONT_PARSE_STACK)
2145
  if (topOfStackUnset) {
2146
    /* Make it unset again. */
2147
    me->crtn->topOfStack = NULL;
2148
  }
2149
#  else
2150
  (void)topOfStackUnset;
2151
#  endif
2152
}
1,449✔
2153

2154
GC_INNER void
2155
GC_do_blocking_inner(ptr_t data, void *context)
126✔
2156
{
2157
  GC_thread me;
2158
  GC_bool topOfStackUnset;
2159

2160
  UNUSED_ARG(context);
2161
  READER_LOCK();
126✔
2162
  me = GC_self_thread_inner();
126✔
2163
  do_blocking_enter(&topOfStackUnset, me);
126✔
2164
  READER_UNLOCK_RELEASE();
126✔
2165

2166
  ((struct blocking_data *)data)->client_data /*< result */
2167
      = ((struct blocking_data *)data)
252✔
2168
            ->fn(((struct blocking_data *)data)->client_data);
126✔
2169

2170
  /* This will block if the world is stopped. */
2171
  READER_LOCK();
126✔
2172

2173
#  ifdef LINT2
2174
  {
2175
#    ifdef GC_ASSERTIONS
2176
    GC_thread saved_me = me;
2177
#    endif
2178

2179
    /*
2180
     * The pointer to the GC thread descriptor should not be changed while
2181
     * the thread is registered but a static analysis tool might complain
2182
     * that this pointer value (obtained in the first locked section) is
2183
     * unreliable in the second locked section.
2184
     */
2185
    me = GC_self_thread_inner();
2186
    GC_ASSERT(me == saved_me);
2187
  }
2188
#  endif
2189
#  if defined(GC_ENABLE_SUSPEND_THREAD) && defined(SIGNAL_BASED_STOP_WORLD)
2190
  /*
2191
   * Note: this code cannot be moved into `do_blocking_leave()` otherwise
2192
   * there could be a static analysis tool warning (false positive) about
2193
   * unlock without a matching lock.
2194
   */
2195
  while (UNLIKELY((me->ext_suspend_cnt & 1) != 0)) {
252✔
2196
    /* Read suspend counter (number) before unlocking. */
2197
    size_t suspend_cnt = me->ext_suspend_cnt;
×
2198

2199
    READER_UNLOCK_RELEASE();
×
2200
    GC_suspend_self_inner(me, suspend_cnt);
×
2201
    READER_LOCK();
×
2202
  }
2203
#  endif
2204
  do_blocking_leave(me, topOfStackUnset);
126✔
2205
  READER_UNLOCK_RELEASE();
126✔
2206
}
126✔
2207

2208
#  if defined(GC_ENABLE_SUSPEND_THREAD) && defined(SIGNAL_BASED_STOP_WORLD)
2209
GC_INNER void
2210
GC_suspend_self_blocked(ptr_t thread_me, void *context)
1,323✔
2211
{
2212
  GC_thread me = (GC_thread)thread_me;
1,323✔
2213
  GC_bool topOfStackUnset;
2214

2215
  UNUSED_ARG(context);
2216

2217
  /*
2218
   * The caller holds the allocator lock in the exclusive mode, thus
2219
   * we require and restore it to the same mode upon return from the
2220
   * function.
2221
   */
2222
  GC_ASSERT(I_HOLD_LOCK());
1,323✔
2223

2224
  do_blocking_enter(&topOfStackUnset, me);
1,323✔
2225
  while ((me->ext_suspend_cnt & 1) != 0) {
3,969✔
2226
    size_t suspend_cnt = me->ext_suspend_cnt;
1,323✔
2227

2228
    UNLOCK();
1,323✔
2229
    GC_suspend_self_inner(me, suspend_cnt);
1,323✔
2230
    LOCK();
1,323✔
2231
  }
2232
  do_blocking_leave(me, topOfStackUnset);
1,323✔
2233
}
1,323✔
2234
#  endif /* GC_ENABLE_SUSPEND_THREAD */
2235

2236
GC_API void GC_CALL
2237
GC_set_stackbottom(void *gc_thread_handle, const struct GC_stack_base *sb)
63✔
2238
{
2239
  GC_thread t = (GC_thread)gc_thread_handle;
63✔
2240
  GC_stack_context_t crtn;
2241

2242
  GC_ASSERT(sb->mem_base != NULL);
63✔
2243
  if (UNLIKELY(!GC_is_initialized)) {
63✔
2244
    GC_ASSERT(NULL == t);
×
2245
    /* Alter the stack bottom of the primordial thread. */
2246
    GC_stackbottom = (char *)sb->mem_base;
×
2247
#  if defined(E2K) || defined(IA64)
2248
    GC_register_stackbottom = (ptr_t)sb->reg_base;
2249
#  endif
2250
    return;
×
2251
  }
2252

2253
  GC_ASSERT(I_HOLD_READER_LOCK());
63✔
2254
  if (NULL == t) {
63✔
2255
    /* The current thread. */
2256
    t = GC_self_thread_inner();
×
2257
  }
2258
  GC_ASSERT(!KNOWN_FINISHED(t));
63✔
2259
  crtn = t->crtn;
63✔
2260
  GC_ASSERT((t->flags & DO_BLOCKING) == 0
63✔
2261
            && NULL == crtn->traced_stack_sect); /*< for now */
2262

2263
  crtn->stack_end = (ptr_t)sb->mem_base;
63✔
2264
#  ifdef E2K
2265
  crtn->ps_ofs = (size_t)(GC_uintptr_t)sb->reg_base;
2266
#  elif defined(IA64)
2267
  crtn->backing_store_end = (ptr_t)sb->reg_base;
2268
#  endif
2269
#  ifdef GC_WIN32_THREADS
2270
  /* Reset the known minimum (hottest address in the stack). */
2271
  crtn->last_stack_min = ADDR_LIMIT;
2272
#  endif
2273
}
2274

2275
GC_API void *GC_CALL
2276
GC_get_my_stackbottom(struct GC_stack_base *sb)
63✔
2277
{
2278
  GC_thread me;
2279
  GC_stack_context_t crtn;
2280

2281
  READER_LOCK();
63✔
2282
  me = GC_self_thread_inner();
63✔
2283
  /* The thread is assumed to be registered. */
2284
  crtn = me->crtn;
63✔
2285
  sb->mem_base = crtn->stack_end;
63✔
2286
#  ifdef E2K
2287
  /* Store the offset in the procedure stack, not address. */
2288
  sb->reg_base = NUMERIC_TO_VPTR(crtn->ps_ofs);
2289
#  elif defined(IA64)
2290
  sb->reg_base = crtn->backing_store_end;
2291
#  endif
2292
  READER_UNLOCK();
63✔
2293
  return me; /*< `gc_thread_handle` */
63✔
2294
}
2295

2296
GC_ATTR_NOINLINE
2297
GC_API void *GC_CALL
2298
GC_call_with_gc_active(GC_fn_type fn, void *client_data)
126✔
2299
{
2300
  struct GC_traced_stack_sect_s stacksect;
2301
  GC_thread me;
2302
  GC_stack_context_t crtn;
2303
  ptr_t stack_end;
2304
#  ifdef E2K
2305
  ptr_t saved_bs_ptr, saved_bs_end;
2306
  size_t saved_ps_ofs;
2307
#  endif
2308

2309
  /* This will block if the world is stopped. */
2310
  READER_LOCK();
126✔
2311

2312
  me = GC_self_thread_inner();
126✔
2313
  crtn = me->crtn;
126✔
2314

2315
  /*
2316
   * Adjust our stack bottom value (this could happen unless
2317
   * `GC_get_stack_base()` was used which returned `GC_SUCCESS`).
2318
   */
2319
  stack_end = crtn->stack_end; /*< read of a `volatile` field */
126✔
2320
  GC_ASSERT(stack_end != NULL);
126✔
2321
  STORE_APPROX_SP_TO(*(volatile ptr_t *)&stacksect.saved_stack_ptr);
126✔
2322
  if (HOTTER_THAN(stack_end, stacksect.saved_stack_ptr)) {
126✔
2323
    crtn->stack_end = stacksect.saved_stack_ptr;
×
2324
#  if defined(I386) && defined(GC_WIN32_THREADS)
2325
    crtn->initial_stack_base = stacksect.saved_stack_ptr;
2326
#  endif
2327
  }
2328

2329
  if ((me->flags & DO_BLOCKING) == 0) {
126✔
2330
    /* We are not inside `GC_do_blocking()` - do nothing more. */
2331
    READER_UNLOCK_RELEASE();
×
2332
    /* Cast `fn` to a `volatile` type to prevent its call inlining. */
2333
    client_data = (*(GC_fn_type volatile *)&fn)(client_data);
×
2334
    /* Prevent treating the above as a tail call. */
2335
    GC_noop1(COVERT_DATAFLOW(ADDR(&stacksect)));
×
2336
    return client_data; /*< result */
126✔
2337
  }
2338

2339
#  if defined(GC_ENABLE_SUSPEND_THREAD) && defined(SIGNAL_BASED_STOP_WORLD)
2340
  while (UNLIKELY((me->ext_suspend_cnt & 1) != 0)) {
126✔
2341
    size_t suspend_cnt = me->ext_suspend_cnt;
×
2342

2343
    READER_UNLOCK_RELEASE();
×
2344
    GC_suspend_self_inner(me, suspend_cnt);
×
2345
    READER_LOCK();
×
2346
    GC_ASSERT(me->crtn == crtn);
×
2347
  }
2348
#  endif
2349

2350
  /* Setup new "stack section". */
2351
  stacksect.saved_stack_ptr = crtn->stack_ptr;
126✔
2352
#  ifdef E2K
2353
  GC_ASSERT(crtn->backing_store_end != NULL);
2354
  {
2355
    unsigned long long sz_ull;
2356

2357
    GET_PROCEDURE_STACK_SIZE_INNER(&sz_ull);
2358
    saved_ps_ofs = crtn->ps_ofs;
2359
    GC_ASSERT(saved_ps_ofs <= (size_t)sz_ull);
2360
    crtn->ps_ofs = (size_t)sz_ull;
2361
  }
2362
  saved_bs_end = crtn->backing_store_end;
2363
  saved_bs_ptr = crtn->backing_store_ptr;
2364
  crtn->backing_store_ptr = NULL;
2365
  crtn->backing_store_end = NULL;
2366
#  elif defined(IA64)
2367
  /* This is the same as in `GC_call_with_stack_base()`. */
2368
  stacksect.backing_store_end = GC_save_regs_in_stack();
2369
  /*
2370
   * Unnecessarily flushes the register stack, but that probably does
2371
   * not hurt.
2372
   */
2373
  stacksect.saved_backing_store_ptr = crtn->backing_store_ptr;
2374
#  endif
2375
  stacksect.prev = crtn->traced_stack_sect;
126✔
2376
  me->flags &= (unsigned char)~DO_BLOCKING;
126✔
2377
  crtn->traced_stack_sect = &stacksect;
126✔
2378

2379
  READER_UNLOCK_RELEASE();
126✔
2380
  client_data = (*(GC_fn_type volatile *)&fn)(client_data);
126✔
2381
  GC_ASSERT((me->flags & DO_BLOCKING) == 0);
126✔
2382

2383
  /* Restore original "stack section". */
2384
  READER_LOCK();
126✔
2385
  GC_ASSERT(me->crtn == crtn);
126✔
2386
  GC_ASSERT(crtn->traced_stack_sect == &stacksect);
126✔
2387
  crtn->traced_stack_sect = stacksect.prev;
126✔
2388
#  ifdef E2K
2389
  GC_ASSERT(NULL == crtn->backing_store_end);
2390
  crtn->backing_store_end = saved_bs_end;
2391
  crtn->backing_store_ptr = saved_bs_ptr;
2392
  crtn->ps_ofs = saved_ps_ofs;
2393
#  elif defined(IA64)
2394
  crtn->backing_store_ptr = stacksect.saved_backing_store_ptr;
2395
#  endif
2396
  me->flags |= DO_BLOCKING;
126✔
2397
  crtn->stack_ptr = stacksect.saved_stack_ptr;
126✔
2398
  READER_UNLOCK_RELEASE();
126✔
2399
  return client_data; /*< result */
126✔
2400
}
2401

2402
STATIC void
2403
GC_unregister_my_thread_inner(GC_thread me)
100,543✔
2404
{
2405
  GC_ASSERT(I_HOLD_LOCK());
100,543✔
2406
#  ifdef DEBUG_THREADS
2407
  GC_log_printf("Unregistering thread %p, gc_thread= %p, n_threads= %d\n",
2408
                THREAD_ID_TO_VPTR(me->id), (void *)me, GC_count_threads());
2409
#  endif
2410
  GC_ASSERT(!KNOWN_FINISHED(me));
100,543✔
2411
#  if defined(THREAD_LOCAL_ALLOC)
2412
  GC_destroy_thread_local(&me->tlfs);
100,543✔
2413
#  endif
2414
#  ifdef NACL
2415
  GC_nacl_shutdown_gc_thread();
2416
#  endif
2417
#  ifdef GC_PTHREADS
2418
#    if defined(GC_HAVE_PTHREAD_EXIT) || !defined(GC_NO_PTHREAD_CANCEL)
2419
  /*
2420
   * Handle `DISABLED_GC` flag which is set by the intercepted
2421
   * `pthread_cancel()` or `pthread_exit()`.
2422
   */
2423
  if ((me->flags & DISABLED_GC) != 0)
100,543✔
2424
    GC_enable_inner();
1,768✔
2425
#    endif
2426
  if ((me->flags & DETACHED) == 0) {
100,543✔
2427
    me->flags |= FINISHED;
3,611✔
2428
  } else
2429
#  endif
2430
  /* else */ {
2431
    GC_delete_thread(me);
96,932✔
2432
  }
2433
#  if defined(THREAD_LOCAL_ALLOC)
2434
  /*
2435
   * It is required to call `GC_remove_specific()` defined in
2436
   * `specific.c` file.
2437
   */
2438
  GC_remove_specific(GC_thread_key);
100,543✔
2439
#  endif
2440
}
100,543✔
2441

2442
GC_API int GC_CALL
2443
GC_unregister_my_thread(void)
48,458✔
2444
{
2445
  GC_thread me;
2446
  IF_CANCEL(int cancel_state;)
2447

2448
  /*
2449
   * Client should not unregister the thread explicitly
2450
   * if it is registered by `DllMain`, except for the main thread.
2451
   */
2452
#  if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS)
2453
  GC_ASSERT(!GC_win32_dll_threads
2454
            || THREAD_ID_EQUAL(GC_main_thread_id, thread_id_self()));
2455
#  endif
2456

2457
  LOCK();
48,458✔
2458
  DISABLE_CANCEL(cancel_state);
48,458✔
2459
  /*
2460
   * Wait for any collection that may be marking from our stack to complete
2461
   * before we remove this thread.
2462
   */
2463
  GC_wait_for_gc_completion(FALSE);
48,458✔
2464
  me = GC_self_thread_inner();
48,458✔
2465
  GC_ASSERT(THREAD_ID_EQUAL(me->id, thread_id_self()));
48,458✔
2466
  GC_unregister_my_thread_inner(me);
48,458✔
2467
  RESTORE_CANCEL(cancel_state);
48,458✔
2468
  UNLOCK();
48,458✔
2469
  return GC_SUCCESS;
48,458✔
2470
}
2471

2472
#  if (!defined(GC_NO_PTHREAD_CANCEL) && defined(GC_PTHREADS) \
2473
       && defined(CANCEL_SAFE))                               \
2474
      || defined(GC_HAVE_PTHREAD_EXIT)
2475
/*
2476
 * Disables collection and sets the corresponding flag for the given thread.
2477
 * Does nothing if the argument is `NULL` or the flag (`DISABLED_GC`)
2478
 * is already set.
2479
 */
2480
static void
2481
disable_gc_for_pthread(GC_thread t)
2,205✔
2482
{
2483
  if (t != NULL && (t->flags & DISABLED_GC) == 0) {
2,205✔
2484
    t->flags |= DISABLED_GC;
1,768✔
2485
    GC_disable_inner();
1,768✔
2486
  }
2487
}
2,205✔
2488
#  endif
2489

2490
#  if !defined(GC_NO_PTHREAD_CANCEL) && defined(GC_PTHREADS)
2491
/*
2492
 * We should deal with the fact that apparently on Solaris and, probably,
2493
 * on some Linux we cannot collect while a thread is exiting, since
2494
 * signals are not handled properly.  This currently gives rise to deadlocks.
2495
 * The only workaround seen is to intercept `pthread_cancel()` and
2496
 * `pthread_exit()`, and disable the collections until the thread exit
2497
 * handler is called.  That is ugly, because we risk growing the heap
2498
 * unnecessarily.  But it seems that we do not really have an option in that
2499
 * the process is not in a fully functional state while a thread is exiting.
2500
 */
2501
#    define GC_wrap_pthread_cancel WRAP_FUNC(pthread_cancel)
2502
GC_API int
2503
GC_wrap_pthread_cancel(pthread_t thread)
882✔
2504
{
2505
  INIT_REAL_SYMS();
2506
#    ifdef CANCEL_SAFE
2507
  LOCK();
882✔
2508
  disable_gc_for_pthread(GC_lookup_by_pthread(thread));
882✔
2509
  /* Note: if not found, then real `pthread_cancel` should return `ESRCH`. */
2510
  UNLOCK();
882✔
2511
#    endif
2512
  return REAL_FUNC(pthread_cancel)(thread);
882✔
2513
}
2514
#    undef GC_wrap_pthread_cancel
2515
#  endif /* !GC_NO_PTHREAD_CANCEL */
2516

2517
#  ifdef GC_HAVE_PTHREAD_EXIT
2518
#    define GC_wrap_pthread_exit WRAP_FUNC(pthread_exit)
2519
GC_API GC_PTHREAD_EXIT_ATTRIBUTE void
2520
GC_wrap_pthread_exit(void *retval)
1,323✔
2521
{
2522
  INIT_REAL_SYMS();
2523
  LOCK();
1,323✔
2524
  disable_gc_for_pthread(GC_self_thread_inner());
1,323✔
2525
  UNLOCK();
1,323✔
2526
  REAL_FUNC(pthread_exit)(retval);
1,323✔
2527
}
2528
#    undef GC_wrap_pthread_exit
2529
#  endif /* GC_HAVE_PTHREAD_EXIT */
2530

2531
GC_API void GC_CALL
2532
GC_allow_register_threads(void)
2✔
2533
{
2534
  /*
2535
   * Check the collector is initialized and the current thread is
2536
   * registered.
2537
   */
2538
  GC_ASSERT(GC_self_thread() != NULL);
2✔
2539

2540
  /* Initialize symbols while still single-threaded. */
2541
  INIT_REAL_SYMS();
2542

2543
  GC_init_lib_bounds();
2544
  GC_start_mark_threads();
2✔
2545
  set_need_to_lock();
2✔
2546
}
2✔
2547

2548
#  if defined(PTHREAD_STOP_WORLD_IMPL)            \
2549
          && !defined(NO_SIGNALS_UNBLOCK_IN_MAIN) \
2550
      || defined(GC_EXPLICIT_SIGNALS_UNBLOCK)
2551
GC_INNER void
2552
GC_unblock_gc_signals(void)
39✔
2553
{
2554
  sigset_t set;
2555

2556
  /* This is for `pthread_sigmask`. */
2557
  INIT_REAL_SYMS();
2558

2559
  sigemptyset(&set);
39✔
2560
  sigaddset(&set, GC_get_suspend_signal());
39✔
2561
  sigaddset(&set, GC_get_thr_restart_signal());
39✔
2562
  if (REAL_FUNC(pthread_sigmask)(SIG_UNBLOCK, &set, NULL) != 0)
39✔
2563
    ABORT("pthread_sigmask failed");
×
2564
}
39✔
2565
#  endif /* PTHREAD_STOP_WORLD_IMPL || GC_EXPLICIT_SIGNALS_UNBLOCK */
2566

2567
GC_API int GC_CALL
2568
GC_register_my_thread(const struct GC_stack_base *sb)
48,706✔
2569
{
2570
  GC_thread me;
2571

2572
#  ifdef GC_ALWAYS_MULTITHREADED
2573
  set_need_to_lock();
2574
#  else
2575
  if (!GC_need_to_lock)
48,706✔
2576
    ABORT("Threads explicit registering is not previously enabled");
×
2577
#  endif
2578

2579
  /* We lock here, since we want to wait for an ongoing GC. */
2580
  LOCK();
48,706✔
2581
  me = GC_self_thread_inner();
48,706✔
2582
  if (LIKELY(NULL == me)) {
48,706✔
2583
    me = GC_register_my_thread_inner(sb, thread_id_self());
48,407✔
2584
#  ifdef GC_PTHREADS
2585
    /*
2586
     * Treat as detached, since we do not need to worry about pointer
2587
     * results.
2588
     */
2589
    me->flags |= DETACHED;
48,407✔
2590
#  else
2591
    (void)me;
2592
#  endif
2593
  } else {
2594
#  ifdef GC_PTHREADS
2595
    if (KNOWN_FINISHED(me)) {
299✔
2596
      /*
2597
       * This code is executed when a thread is registered from the destructor
2598
       * of the client thread key.
2599
       */
2600
#    ifdef NACL
2601
      GC_nacl_initialize_gc_thread(me);
2602
#    endif
2603
#    ifdef DARWIN
2604
      /*
2605
       * Reinitialize `mach_thread` to avoid `thread_suspend()` fail
2606
       * with `MACH_SEND_INVALID_DEST` error.
2607
       */
2608
      me->mach_thread = mach_thread_self();
2609
#    endif
2610
      GC_record_stack_base(me->crtn, sb);
299✔
2611
      me->flags &= (unsigned char)~FINISHED; /*< but not `DETACHED` */
299✔
2612
    } else
2613
#  endif
2614
    /* else */ {
2615
      UNLOCK();
×
2616
      return GC_DUPLICATE;
×
2617
    }
2618
  }
2619

2620
#  ifdef THREAD_LOCAL_ALLOC
2621
  GC_init_thread_local(&me->tlfs);
48,706✔
2622
#  endif
2623
#  ifdef GC_EXPLICIT_SIGNALS_UNBLOCK
2624
  /*
2625
   * Since this could be executed from a thread destructor,
2626
   * our signals might already be blocked.
2627
   */
2628
  GC_unblock_gc_signals();
2629
#  endif
2630
#  if defined(GC_ENABLE_SUSPEND_THREAD) && defined(SIGNAL_BASED_STOP_WORLD)
2631
  if (UNLIKELY((me->ext_suspend_cnt & 1) != 0)) {
48,706✔
2632
    GC_with_callee_saves_pushed(GC_suspend_self_blocked, (ptr_t)me);
×
2633
  }
2634
#  endif
2635
  UNLOCK();
48,706✔
2636
  return GC_SUCCESS;
48,706✔
2637
}
2638

2639
#  if defined(GC_PTHREADS) && !defined(PLATFORM_THREADS) \
2640
      && !defined(SN_TARGET_PSP2)
2641

2642
GC_INNER_PTHRSTART void
2643
GC_thread_exit_proc(void *arg)
52,085✔
2644
{
2645
  GC_thread me = (GC_thread)arg;
52,085✔
2646
  IF_CANCEL(int cancel_state;)
2647

2648
#    ifdef DEBUG_THREADS
2649
  GC_log_printf("Called GC_thread_exit_proc on %p, gc_thread= %p\n",
2650
                THREAD_ID_TO_VPTR(me->id), (void *)me);
2651
#    endif
2652
  LOCK();
52,085✔
2653
  DISABLE_CANCEL(cancel_state);
52,085✔
2654
  GC_wait_for_gc_completion(FALSE);
52,085✔
2655
  GC_unregister_my_thread_inner(me);
52,085✔
2656
  RESTORE_CANCEL(cancel_state);
52,085✔
2657
  UNLOCK();
52,085✔
2658
}
52,085✔
2659

2660
#    define GC_wrap_pthread_join WRAP_FUNC(pthread_join)
2661
GC_API int
2662
GC_wrap_pthread_join(pthread_t thread, void **retval)
3,207✔
2663
{
2664
  int result;
2665
  GC_thread t;
2666

2667
  INIT_REAL_SYMS();
2668
#    ifdef DEBUG_THREADS
2669
  GC_log_printf("thread %p is joining thread %p\n",
2670
                PTHREAD_TO_VPTR(pthread_self()), PTHREAD_TO_VPTR(thread));
2671
#    endif
2672

2673
  /* After the join, thread id may have been recycled. */
2674
  READER_LOCK();
3,207✔
2675
  t = (GC_thread)COVERT_DATAFLOW_P(GC_lookup_by_pthread(thread));
3,207✔
2676
  /*
2677
   * This is guaranteed to be the intended one, since the thread id
2678
   * cannot have been recycled by `pthreads`.
2679
   */
2680
  READER_UNLOCK();
3,207✔
2681

2682
  result = REAL_FUNC(pthread_join)(thread, retval);
3,207✔
2683
#    ifdef FREEBSD
2684
  /*
2685
   * On FreeBSD, the wrapped `pthread_join()` sometimes returns (what
2686
   * appears to be) a spurious `EINTR` which caused the test and real code
2687
   * to fail gratuitously.  Having looked at system `pthreads` library
2688
   * source code, I see how such return code value may be generated:
2689
   * in one path of the code, `pthread_join()` just returns the `errno`
2690
   * setting of the thread being joined - this does not match the POSIX
2691
   * specification or the local man pages.  Thus, I have taken the liberty
2692
   * to catch this one spurious return value.
2693
   */
2694
  if (UNLIKELY(result == EINTR))
2695
    result = 0;
2696
#    endif
2697

2698
  if (LIKELY(0 == result)) {
3,207✔
2699
    LOCK();
3,207✔
2700
    /*
2701
     * Here the `pthread_id` may have been recycled.  Delete the thread from
2702
     *`GC_threads` (unless it has been registered again from the destructor
2703
     * of the client thread key).
2704
     */
2705
    if (KNOWN_FINISHED(t)) {
3,207✔
2706
      GC_delete_thread(t);
3,204✔
2707
    }
2708
    UNLOCK();
3,207✔
2709
  }
2710

2711
#    ifdef DEBUG_THREADS
2712
  GC_log_printf("thread %p join with thread %p %s\n",
2713
                PTHREAD_TO_VPTR(pthread_self()), PTHREAD_TO_VPTR(thread),
2714
                result != 0 ? "failed" : "succeeded");
2715
#    endif
2716
  return result;
3,207✔
2717
}
2718
#    undef GC_wrap_pthread_join
2719

2720
#    define GC_wrap_pthread_detach WRAP_FUNC(pthread_detach)
2721
GC_API int
2722
GC_wrap_pthread_detach(pthread_t thread)
412✔
2723
{
2724
  int result;
2725
  GC_thread t;
2726

2727
  INIT_REAL_SYMS();
2728
  if (UNLIKELY(!GC_is_initialized)) {
412✔
2729
    /*
2730
     * The only case this seems to be needed is when the client calls
2731
     * `pthread_detach(pthread_self())` before the collector initialization.
2732
     */
2733
    GC_init();
×
2734
  }
2735
  READER_LOCK();
412✔
2736
  t = (GC_thread)COVERT_DATAFLOW_P(GC_lookup_by_pthread(thread));
412✔
2737
  READER_UNLOCK();
412✔
2738
  result = REAL_FUNC(pthread_detach)(thread);
412✔
2739
  if (LIKELY(0 == result)) {
412✔
2740
    LOCK();
412✔
2741
    /* Here the `pthread_id` may have been recycled. */
2742
    if (KNOWN_FINISHED(t)) {
412✔
2743
      GC_delete_thread(t);
106✔
2744
    } else {
2745
      t->flags |= DETACHED;
306✔
2746
    }
2747
    UNLOCK();
412✔
2748
  }
2749
  return result;
412✔
2750
}
2751
#    undef GC_wrap_pthread_detach
2752

2753
struct start_info {
2754
  void *(*start_routine)(void *);
2755
  void *arg;
2756
  /*
2757
   * Note: a value of 1 means the thread is in our thread table, but
2758
   * the parent process has not noticed it yet.
2759
   */
2760
  sem_t registered;
2761
  unsigned char flags;
2762
};
2763

2764
GC_INNER_PTHRSTART GC_thread
2765
GC_start_rtn_prepare_thread(void *(**pstart)(void *), void **pstart_arg,
52,085✔
2766
                            struct GC_stack_base *sb, void *arg)
2767
{
2768
  struct start_info *psi = (struct start_info *)arg;
52,085✔
2769
  thread_id_t self_id = thread_id_self();
52,085✔
2770
  GC_thread me;
2771

2772
#    ifdef DEBUG_THREADS
2773
  GC_log_printf("Starting thread %p, sp= %p\n",
2774
                PTHREAD_TO_VPTR(pthread_self()), (void *)GC_approx_sp());
2775
#    endif
2776
  /*
2777
   * If a collection occurs before the thread is registered, that collection
2778
   * will ignore this thread.  That is fine, since it will block trying to
2779
   * acquire the allocator lock, and will not yet hold interesting pointers.
2780
   */
2781
  LOCK();
52,085✔
2782
  /*
2783
   * We register the thread here instead of in the parent process, so that
2784
   * we do not need to hold the allocator lock during `pthread_create()`
2785
   * call.
2786
   */
2787
  me = GC_register_my_thread_inner(sb, self_id);
52,085✔
2788
  GC_ASSERT(me != &first_thread);
52,085✔
2789
  me->flags = psi->flags;
52,085✔
2790
#    ifdef GC_WIN32_THREADS
2791
  GC_win32_cache_self_pthread(self_id);
2792
#    endif
2793
#    ifdef THREAD_LOCAL_ALLOC
2794
  GC_init_thread_local(&me->tlfs);
52,085✔
2795
#    endif
2796
  UNLOCK();
52,085✔
2797

2798
  *pstart = psi->start_routine;
52,085✔
2799
  *pstart_arg = psi->arg;
52,085✔
2800
#    if defined(DEBUG_THREADS) && defined(FUNCPTR_IS_DATAPTR)
2801
  GC_log_printf("start_routine= %p\n", CAST_THRU_UINTPTR(void *, *pstart));
2802
#    endif
2803
  sem_post(&psi->registered);
52,085✔
2804
  /* This was the last action on `*psi`; OK to deallocate. */
2805
  return me;
52,085✔
2806
}
2807

2808
STATIC void *
2809
GC_pthread_start(void *arg)
52,085✔
2810
{
2811
#    ifdef INCLUDE_LINUX_THREAD_DESCR
2812
  struct GC_stack_base sb;
2813

2814
#      ifdef REDIRECT_MALLOC
2815
  /*
2816
   * `GC_get_stack_base()` may call `pthread_getattr_np()`, which
2817
   * can unfortunately call `realloc()`, which may allocate from
2818
   * an unregistered thread.  This is unpleasant, since it might
2819
   * force heap growth (or, even, heap overflow).
2820
   */
2821
  GC_disable();
2822
#      endif
2823
  if (GC_get_stack_base(&sb) != GC_SUCCESS)
2824
    ABORT("Failed to get thread stack base");
2825
#      ifdef REDIRECT_MALLOC
2826
  GC_enable();
2827
#      endif
2828
  return GC_pthread_start_inner(&sb, arg);
2829
#    else
2830
  return GC_call_with_stack_base(GC_pthread_start_inner, arg);
52,085✔
2831
#    endif
2832
}
2833

2834
#    define GC_wrap_pthread_create WRAP_FUNC(pthread_create)
2835
GC_API int
2836
GC_wrap_pthread_create(pthread_t *new_thread,
52,325✔
2837
                       GC_PTHREAD_CREATE_CONST pthread_attr_t *attr,
2838
                       void *(*start_routine)(void *), void *arg)
2839
{
2840
  int result;
2841
  struct start_info si;
2842

2843
  GC_ASSERT(I_DONT_HOLD_LOCK());
52,325✔
2844
  INIT_REAL_SYMS();
2845
  if (UNLIKELY(!GC_is_initialized))
52,325✔
2846
    GC_init();
×
2847
  GC_ASSERT(GC_thr_initialized);
52,325✔
2848
  GC_ASSERT(NONNULL_PROC_NOT_ZERO(start_routine));
2849

2850
  GC_init_lib_bounds();
2851
  if (sem_init(&si.registered, GC_SEM_INIT_PSHARED, 0) == -1)
52,325✔
2852
    ABORT("sem_init failed");
×
2853
  si.flags = 0;
52,325✔
2854
  si.start_routine = start_routine;
52,325✔
2855
  si.arg = arg;
52,325✔
2856

2857
  /*
2858
   * We resist the temptation to muck with the stack size here, even if the
2859
   * default is unreasonably small.  That is the client's responsibility.
2860
   */
2861
#    ifdef GC_ASSERTIONS
2862
  {
2863
    size_t stack_size = 0;
52,325✔
2864
    if (NULL != attr) {
52,325✔
2865
      if (pthread_attr_getstacksize(attr, &stack_size) != 0)
48,766✔
2866
        ABORT("pthread_attr_getstacksize failed");
×
2867
    }
2868
    if (0 == stack_size) {
52,325✔
2869
      pthread_attr_t my_attr;
2870

2871
      if (pthread_attr_init(&my_attr) != 0)
3,559✔
2872
        ABORT("pthread_attr_init failed");
×
2873
      if (pthread_attr_getstacksize(&my_attr, &stack_size) != 0)
3,559✔
2874
        ABORT("pthread_attr_getstacksize failed");
×
2875
      (void)pthread_attr_destroy(&my_attr);
3,559✔
2876
    }
2877
    /*
2878
     * On Solaris 10 and on Win32 with `winpthreads` library, with the
2879
     * default `attr` initialization, `stack_size` remains 0; fudge it.
2880
     */
2881
    if (UNLIKELY(0 == stack_size)) {
52,325✔
2882
#      if !defined(SOLARIS) && !defined(GC_WIN32_PTHREADS)
2883
      WARN("Failed to get stack size for assertion checking\n", 0);
×
2884
#      endif
2885
      stack_size = 1000000;
×
2886
    }
2887
    GC_ASSERT(stack_size >= 65536);
52,325✔
2888
    /*
2889
     * Our threads may need to do some work for the GC.
2890
     * Ridiculously small threads will not work, and they
2891
     * probably would not work anyway.
2892
     */
2893
  }
2894
#    endif
2895

2896
  if (attr != NULL) {
52,325✔
2897
    int detachstate;
2898

2899
    if (pthread_attr_getdetachstate(attr, &detachstate) != 0)
48,766✔
2900
      ABORT("pthread_attr_getdetachstate failed");
×
2901
    if (PTHREAD_CREATE_DETACHED == detachstate)
48,766✔
2902
      si.flags |= DETACHED;
48,706✔
2903
  }
2904

2905
#    ifdef PARALLEL_MARK
2906
  if (!GC_parallel && UNLIKELY(GC_available_markers_m1 > 0))
52,325✔
2907
    GC_start_mark_threads();
11✔
2908
#    endif
2909
#    ifdef DEBUG_THREADS
2910
  GC_log_printf("About to start new thread from thread %p\n",
2911
                PTHREAD_TO_VPTR(pthread_self()));
2912
#    endif
2913
  set_need_to_lock();
52,325✔
2914
  result = REAL_FUNC(pthread_create)(new_thread, attr, GC_pthread_start, &si);
52,325✔
2915

2916
  /*
2917
   * Wait until child has been added to the thread table.
2918
   * This also ensures that we hold onto the stack-allocated `si`
2919
   * until the child is done with it.
2920
   */
2921
  if (LIKELY(0 == result)) {
52,078✔
2922
    IF_CANCEL(int cancel_state;)
2923

2924
    /* `pthread_create()` is not a cancellation point. */
2925
    DISABLE_CANCEL(cancel_state);
52,078✔
2926

2927
    while (sem_wait(&si.registered) == -1) {
52,078✔
2928
#    ifdef HAIKU
2929
      /* To workaround some bug in Haiku semaphores. */
2930
      if (EACCES == errno)
2931
        continue;
2932
#    endif
2933
      if (errno != EINTR)
×
2934
        ABORT("sem_wait failed");
×
2935
    }
2936
    RESTORE_CANCEL(cancel_state);
52,073✔
2937
  }
2938
  sem_destroy(&si.registered);
52,073✔
2939
  return result;
52,073✔
2940
}
2941
#    undef GC_wrap_pthread_create
2942

2943
#  endif /* GC_PTHREADS && !PLATFORM_THREADS && !SN_TARGET_PSP2 */
2944

2945
#  if ((defined(GC_PTHREADS_PARAMARK) || defined(USE_PTHREAD_LOCKS)) \
2946
       && !defined(NO_PTHREAD_TRYLOCK))                              \
2947
      || defined(USE_SPIN_LOCK)
2948
/*
2949
 * Spend a few cycles in a way that cannot introduce contention with
2950
 * other threads.
2951
 */
2952
#    define GC_PAUSE_SPIN_CYCLES 10
2953
STATIC void
2954
GC_pause(void)
129,881,321✔
2955
{
2956
  int i;
2957

2958
  for (i = 0; i < GC_PAUSE_SPIN_CYCLES; ++i) {
1,428,694,531✔
2959
    /* Something that is unlikely to be optimized away. */
2960
#    if defined(AO_HAVE_compiler_barrier) && !defined(BASE_ATOMIC_OPS_EMULATED)
2961
    AO_compiler_barrier();
1,298,813,210✔
2962
#    else
2963
    GC_noop1(i);
2964
#    endif
2965
  }
2966
}
129,881,321✔
2967
#  endif /* USE_SPIN_LOCK || !NO_PTHREAD_TRYLOCK */
2968

2969
#  ifndef SPIN_MAX
2970
/* Maximum number of calls to `GC_pause()` before give up. */
2971
#    define SPIN_MAX 128
2972
#  endif
2973

2974
#  if (!defined(USE_SPIN_LOCK) && !defined(NO_PTHREAD_TRYLOCK) \
2975
       && defined(USE_PTHREAD_LOCKS))                          \
2976
      || defined(GC_PTHREADS_PARAMARK)
2977
/*
2978
 * If we do not want to use the below spinlock implementation, either
2979
 * because we do not have a `GC_test_and_set` implementation, or because
2980
 * we do not want to risk sleeping, we can still try spinning
2981
 * on `pthread_mutex_trylock` for a while.  This appears to be very
2982
 * beneficial in many cases.
2983
 * I suspect that under high contention this is nearly always better
2984
 * than the spin lock.  But it is a bit slower on a uniprocessor.
2985
 * Hence we still default to the spin lock.  This is also used to
2986
 * acquire the mark lock for the parallel marker.
2987
 *
2988
 * Here we use a strict exponential back-off scheme.  I do not know
2989
 * whether that is better or worse than the above.  We eventually
2990
 * yield by calling `pthread_mutex_lock()`; it never makes sense to
2991
 * explicitly sleep.
2992
 */
2993

2994
#    ifdef LOCK_STATS
2995
/* Note that `LOCK_STATS` requires `AO_HAVE_test_and_set`. */
2996
volatile AO_t GC_spin_count = 0;
2997
volatile AO_t GC_block_count = 0;
2998
volatile AO_t GC_unlocked_count = 0;
2999
#    endif
3000

3001
STATIC void
3002
GC_generic_lock(pthread_mutex_t *lock)
126,173,240✔
3003
{
3004
#    ifndef NO_PTHREAD_TRYLOCK
3005
  unsigned pause_length = 1;
126,173,240✔
3006
  unsigned i;
3007

3008
  if (LIKELY(0 == pthread_mutex_trylock(lock))) {
126,173,240✔
3009
#      ifdef LOCK_STATS
3010
    (void)AO_fetch_and_add1(&GC_unlocked_count);
3011
#      endif
3012
    return;
118,890,981✔
3013
  }
3014
  for (; pause_length <= (unsigned)SPIN_MAX; pause_length <<= 1) {
16,269,133✔
3015
    for (i = 0; i < pause_length; ++i) {
146,028,747✔
3016
      GC_pause();
129,881,321✔
3017
    }
3018
    switch (pthread_mutex_trylock(lock)) {
16,147,426✔
3019
    case 0:
7,160,552✔
3020
#      ifdef LOCK_STATS
3021
      (void)AO_fetch_and_add1(&GC_spin_count);
3022
#      endif
3023
      return;
7,160,552✔
3024
    case EBUSY:
8,986,874✔
3025
      break;
8,986,874✔
3026
    default:
×
3027
      ABORT("Unexpected error from pthread_mutex_trylock");
×
3028
    }
3029
  }
3030
#    endif /* !NO_PTHREAD_TRYLOCK */
3031
#    ifdef LOCK_STATS
3032
  (void)AO_fetch_and_add1(&GC_block_count);
3033
#    endif
3034
  pthread_mutex_lock(lock);
121,707✔
3035
}
3036
#  endif /* !USE_SPIN_LOCK || ... */
3037

3038
#  if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS)
3039
GC_INNER volatile unsigned char GC_collecting = FALSE;
3040

3041
#    if defined(AO_HAVE_char_load) && !defined(BASE_ATOMIC_OPS_EMULATED)
3042
#      define is_collecting() ((GC_bool)AO_char_load(&GC_collecting))
3043
#    else
3044
/*
3045
 * `GC_collecting` is a hint, a potential data race between `GC_lock()`
3046
 * and `ENTER_GC()`/`EXIT_GC()` is OK to ignore.
3047
 */
3048
#      define is_collecting() ((GC_bool)GC_collecting)
3049
#    endif
3050
#  endif /* GC_PTHREADS && !GC_WIN32_THREADS */
3051

3052
#  ifdef GC_ASSERTIONS
3053
GC_INNER unsigned long GC_lock_holder = NO_THREAD;
3054
#  endif
3055

3056
#  if defined(USE_SPIN_LOCK)
3057
/*
3058
 * Reasonably fast spin locks.  Basically the same implementation as
3059
 * in STL `alloc.h` file.  This is not really the right way to do this
3060
 * but until the POSIX scheduling mess gets straightened out...
3061
 */
3062

3063
/* Spin cycles if we suspect we are running on an uniprocessor. */
3064
#    define low_spin_max 30
3065

3066
/* Spin cycles for a multiprocessor. */
3067
#    define high_spin_max SPIN_MAX
3068

3069
static volatile AO_t spin_max = low_spin_max;
3070

3071
/*
3072
 * A potential data race between threads invoking `GC_lock` which reads
3073
 * and updates `spin_max` and `last_spins` could be ignored because these
3074
 * variables are hints only.
3075
 */
3076
static volatile AO_t last_spins = 0;
3077

3078
GC_INNER void
3079
GC_lock(void)
3080
{
3081
  AO_t my_spin_max, my_last_spins_half;
3082
  size_t i;
3083

3084
  if (LIKELY(AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_CLEAR))
3085
    return;
3086

3087
  my_spin_max = AO_load(&spin_max);
3088
  my_last_spins_half = AO_load(&last_spins) / 2;
3089
  for (i = 0; i < my_spin_max; i++) {
3090
    if (is_collecting() || GC_nprocs == 1)
3091
      goto yield;
3092
    if (i < my_last_spins_half) {
3093
      GC_pause();
3094
      continue;
3095
    }
3096
    if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_CLEAR) {
3097
      /*
3098
       * Got it, spinning worked!  Thus we are probably not being
3099
       * scheduled against the other process with which we were
3100
       * contending.  Thus it makes sense to spin longer the next time.
3101
       */
3102
      AO_store(&last_spins, i);
3103
      AO_store(&spin_max, high_spin_max);
3104
      return;
3105
    }
3106
  }
3107
  /* We are probably being scheduled against the other process.  Sleep. */
3108
  AO_store(&spin_max, low_spin_max);
3109
yield:
3110
  for (i = 0;; ++i) {
3111
    if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_CLEAR) {
3112
      return;
3113
    }
3114

3115
    /*
3116
     * Under Linux, very short sleeps tend to wait until the current time
3117
     * quantum expires.  On old Linux kernels, `nanosleep()` (<= 2 ms) just
3118
     * spins.  (Under Linux 2.4, this happens only for real-time processes.)
3119
     * We want to minimize both behaviors here.
3120
     */
3121
#    define SLEEP_THRESHOLD 12
3122

3123
    if (i < SLEEP_THRESHOLD) {
3124
      sched_yield();
3125
    } else {
3126
      struct timespec ts;
3127

3128
      /*
3129
       * Do not wait for more than about 15 ms, even under extreme
3130
       * contention.
3131
       */
3132
      if (i > 24)
3133
        i = 24;
3134

3135
      ts.tv_sec = 0;
3136
      ts.tv_nsec = (unsigned32)1 << i;
3137
      nanosleep(&ts, 0);
3138
    }
3139
  }
3140
}
3141

3142
#  elif defined(USE_PTHREAD_LOCKS)
3143
#    ifdef USE_RWLOCK
3144
GC_INNER pthread_rwlock_t GC_allocate_ml = PTHREAD_RWLOCK_INITIALIZER;
3145
#    else
3146
GC_INNER pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER;
3147
#    endif
3148

3149
#    ifndef NO_PTHREAD_TRYLOCK
3150
GC_INNER void
3151
GC_lock(void)
121,981,693✔
3152
{
3153
  if (1 == GC_nprocs || is_collecting()) {
121,981,693✔
3154
    pthread_mutex_lock(&GC_allocate_ml);
14,842✔
3155
  } else {
3156
    GC_generic_lock(&GC_allocate_ml);
121,966,851✔
3157
  }
3158
}
121,981,693✔
3159
#    elif defined(GC_ASSERTIONS)
3160
GC_INNER void
3161
GC_lock(void)
3162
{
3163
#      ifdef USE_RWLOCK
3164
  (void)pthread_rwlock_wrlock(&GC_allocate_ml); /*< exclusive */
3165
#      else
3166
  pthread_mutex_lock(&GC_allocate_ml);
3167
#      endif
3168
}
3169
#    endif /* NO_PTHREAD_TRYLOCK && GC_ASSERTIONS */
3170

3171
#  endif /* !USE_SPIN_LOCK && USE_PTHREAD_LOCKS */
3172

3173
#  ifdef GC_PTHREADS_PARAMARK
3174

3175
#    if defined(GC_ASSERTIONS) && defined(GC_WIN32_THREADS) \
3176
        && !defined(USE_PTHREAD_LOCKS)
3177
/* Note: result is not guaranteed to be unique. */
3178
#      define NUMERIC_THREAD_ID(id) ((unsigned long)ADDR(PTHREAD_TO_VPTR(id)))
3179
#    endif
3180

3181
#    ifdef GC_ASSERTIONS
3182
#      define SET_MARK_LOCK_HOLDER \
3183
        (void)(GC_mark_lock_holder = NUMERIC_THREAD_ID(pthread_self()))
3184
#      define UNSET_MARK_LOCK_HOLDER                       \
3185
        do {                                               \
3186
          GC_ASSERT(GC_mark_lock_holder                    \
3187
                    == NUMERIC_THREAD_ID(pthread_self())); \
3188
          GC_mark_lock_holder = NO_THREAD;                 \
3189
        } while (0)
3190
#    else
3191
#      define SET_MARK_LOCK_HOLDER (void)0
3192
#      define UNSET_MARK_LOCK_HOLDER (void)0
3193
#    endif /* !GC_ASSERTIONS */
3194

3195
static pthread_cond_t builder_cv = PTHREAD_COND_INITIALIZER;
3196

3197
#    ifndef GC_WIN32_THREADS
3198
static void
3199
setup_mark_lock(void)
39✔
3200
{
3201
#      ifdef GLIBC_2_19_TSX_BUG
3202
  pthread_mutexattr_t mattr;
3203
  int glibc_minor = -1;
39✔
3204
  int glibc_major = GC_parse_version(&glibc_minor, gnu_get_libc_version());
39✔
3205

3206
  if (glibc_major > 2 || (glibc_major == 2 && glibc_minor >= 19)) {
39✔
3207
    /* TODO: Disable this workaround for `glibc` with fixed TSX. */
3208
    /* This disables lock elision to workaround a bug in `glibc` 2.19+. */
3209
    if (pthread_mutexattr_init(&mattr) != 0)
39✔
3210
      ABORT("pthread_mutexattr_init failed");
×
3211
    if (pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL) != 0)
39✔
3212
      ABORT("pthread_mutexattr_settype failed");
×
3213
    if (pthread_mutex_init(&mark_mutex, &mattr) != 0)
39✔
3214
      ABORT("pthread_mutex_init failed");
×
3215
    (void)pthread_mutexattr_destroy(&mattr);
39✔
3216
  }
3217
#      endif
3218
}
39✔
3219
#    endif /* !GC_WIN32_THREADS */
3220

3221
GC_INNER void
3222
GC_acquire_mark_lock(void)
4,206,389✔
3223
{
3224
#    if defined(NUMERIC_THREAD_ID_UNIQUE) && !defined(THREAD_SANITIZER)
3225
  GC_ASSERT(GC_mark_lock_holder != NUMERIC_THREAD_ID(pthread_self()));
4,206,389✔
3226
#    endif
3227
  GC_generic_lock(&mark_mutex);
4,206,389✔
3228
  SET_MARK_LOCK_HOLDER;
4,206,389✔
3229
}
4,206,389✔
3230

3231
GC_INNER void
3232
GC_release_mark_lock(void)
4,206,311✔
3233
{
3234
  UNSET_MARK_LOCK_HOLDER;
4,206,311✔
3235
  if (pthread_mutex_unlock(&mark_mutex) != 0)
4,206,311✔
3236
    ABORT("pthread_mutex_unlock failed");
×
3237
}
4,206,311✔
3238

3239
/*
3240
 * Collector must wait for free-list builders for 2 reasons:
3241
 *   - Mark bits may still be getting examined without lock;
3242
 *   - Partial free lists referenced only by locals may not be scanned
3243
 *     correctly, e.g. if they contain "pointer-free" objects, since the
3244
 *     free-list link may be ignored.
3245
 */
3246
STATIC void
3247
GC_wait_builder(void)
1,024✔
3248
{
3249
  ASSERT_CANCEL_DISABLED();
1,024✔
3250
  UNSET_MARK_LOCK_HOLDER;
1,024✔
3251
  if (pthread_cond_wait(&builder_cv, &mark_mutex) != 0)
1,024✔
3252
    ABORT("pthread_cond_wait failed");
×
3253
  GC_ASSERT(GC_mark_lock_holder == NO_THREAD);
1,024✔
3254
  SET_MARK_LOCK_HOLDER;
1,024✔
3255
}
1,024✔
3256

3257
GC_INNER void
3258
GC_wait_for_reclaim(void)
18,874✔
3259
{
3260
  GC_acquire_mark_lock();
18,874✔
3261
  while (GC_fl_builder_count > 0) {
19,898✔
3262
    GC_wait_builder();
1,024✔
3263
  }
3264
  GC_release_mark_lock();
18,874✔
3265
}
18,874✔
3266

3267
#    if defined(CAN_HANDLE_FORK) && defined(THREAD_SANITIZER)
3268
/*
3269
 * Identical to `GC_wait_for_reclaim()` but with the `no_sanitize`
3270
 * attribute as a workaround for TSan which does not notice that the
3271
 * allocator lock is acquired in `fork_prepare_proc()`.
3272
 */
3273
GC_ATTR_NO_SANITIZE_THREAD
3274
static void
3275
wait_for_reclaim_atfork(void)
3276
{
3277
  GC_acquire_mark_lock();
3278
  while (GC_fl_builder_count > 0)
3279
    GC_wait_builder();
3280
  GC_release_mark_lock();
3281
}
3282
#    endif /* CAN_HANDLE_FORK && THREAD_SANITIZER */
3283

3284
GC_INNER void
3285
GC_notify_all_builder(void)
1,530,387✔
3286
{
3287
  GC_ASSERT(GC_mark_lock_holder == NUMERIC_THREAD_ID(pthread_self()));
1,530,387✔
3288
  if (pthread_cond_broadcast(&builder_cv) != 0)
1,530,387✔
3289
    ABORT("pthread_cond_broadcast failed");
×
3290
}
1,530,387✔
3291

3292
GC_INNER void
3293
GC_wait_marker(void)
8,480✔
3294
{
3295
  ASSERT_CANCEL_DISABLED();
8,480✔
3296
  GC_ASSERT(GC_parallel);
8,480✔
3297
  UNSET_MARK_LOCK_HOLDER;
8,480✔
3298
  if (pthread_cond_wait(&mark_cv, &mark_mutex) != 0)
8,480✔
3299
    ABORT("pthread_cond_wait failed");
×
3300
  GC_ASSERT(GC_mark_lock_holder == NO_THREAD);
8,402✔
3301
  SET_MARK_LOCK_HOLDER;
8,402✔
3302
}
8,402✔
3303

3304
GC_INNER void
3305
GC_notify_all_marker(void)
9,342✔
3306
{
3307
  GC_ASSERT(GC_parallel);
9,342✔
3308
  if (pthread_cond_broadcast(&mark_cv) != 0)
9,342✔
3309
    ABORT("pthread_cond_broadcast failed");
×
3310
}
9,342✔
3311

3312
#  endif /* GC_PTHREADS_PARAMARK */
3313

3314
GC_INNER GC_on_thread_event_proc GC_on_thread_event = 0;
3315

3316
GC_API void GC_CALL
3317
GC_set_on_thread_event(GC_on_thread_event_proc fn)
3✔
3318
{
3319
  /* Note: `fn` may be 0 (means no event notifier). */
3320
  LOCK();
3✔
3321
  GC_on_thread_event = fn;
3✔
3322
  UNLOCK();
3✔
3323
}
3✔
3324

3325
GC_API GC_on_thread_event_proc GC_CALL
3326
GC_get_on_thread_event(void)
3✔
3327
{
3328
  GC_on_thread_event_proc fn;
3329

3330
  READER_LOCK();
3✔
3331
  fn = GC_on_thread_event;
3✔
3332
  READER_UNLOCK();
3✔
3333
  return fn;
3✔
3334
}
3335

3336
#  ifdef STACKPTR_CORRECTOR_AVAILABLE
3337
GC_INNER GC_sp_corrector_proc GC_sp_corrector = 0;
3338
#  endif
3339

3340
GC_API void GC_CALL
3341
GC_set_sp_corrector(GC_sp_corrector_proc fn)
3✔
3342
{
3343
#  ifdef STACKPTR_CORRECTOR_AVAILABLE
3344
  LOCK();
3✔
3345
  GC_sp_corrector = fn;
3✔
3346
  UNLOCK();
3✔
3347
#  else
3348
  UNUSED_ARG(fn);
3349
#  endif
3350
}
3✔
3351

3352
GC_API GC_sp_corrector_proc GC_CALL
3353
GC_get_sp_corrector(void)
3✔
3354
{
3355
#  ifdef STACKPTR_CORRECTOR_AVAILABLE
3356
  GC_sp_corrector_proc fn;
3357

3358
  READER_LOCK();
3✔
3359
  fn = GC_sp_corrector;
3✔
3360
  READER_UNLOCK();
3✔
3361
  return fn;
3✔
3362
#  else
3363
  return 0; /*< unsupported */
3364
#  endif
3365
}
3366

3367
#  ifdef PTHREAD_REGISTER_CANCEL_WEAK_STUBS
3368
/* Workaround "undefined reference" linkage errors on some targets. */
3369
EXTERN_C_BEGIN
3370
extern void __pthread_register_cancel(void) __attribute__((__weak__));
3371
extern void __pthread_unregister_cancel(void) __attribute__((__weak__));
3372
EXTERN_C_END
3373

3374
void
3375
__pthread_register_cancel(void)
3376
{
3377
}
3378
void
3379
__pthread_unregister_cancel(void)
3380
{
3381
}
3382
#  endif
3383

3384
#  undef do_blocking_enter
3385

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