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

bdwgc / bdwgc / 2152

30 May 2026 09:17AM UTC coverage: 80.515% (-0.01%) from 80.526%
2152

push

travis-ci

ivmai
Fix async thread termination in GC_stack_range_for if DARWIN_PARSE_STACK

* darwin_stop_world.c [DARWIN_PARSE_STACK] (GC_stack_range_for): If
`kern_result` is `MACH_SEND_INVALID_DEST`, then immediately return 0.

7223 of 8971 relevant lines covered (80.51%)

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

302
#    ifdef HAS_WIN32_THREADS_DISCOVERY
303
  if (GC_win32_dll_threads) {
304
    GC_mark_dll_thread_tlfs();
305
    return;
306
  }
307
#    endif
308
  for (i = 0; i < THREAD_TABLE_SZ; ++i) {
7,928,964✔
309
    for (p = GC_threads[i]; p != NULL; p = p->tm.next) {
8,168,913✔
310
      if (!KNOWN_FINISHED(p))
270,801✔
311
        GC_mark_thread_local_fls_for(&p->tlfs);
253,286✔
312
    }
313
  }
314
}
30,852✔
315

316
#    if defined(GC_ASSERTIONS)
317
/*
318
 * Check that all thread-local free-lists are completely marked.
319
 * Also check that thread-specific-data structures are marked.
320
 */
321
void
322
GC_check_tls(void)
30,852✔
323
{
324
  int i;
325
  GC_thread p;
326

327
#      ifdef HAS_WIN32_THREADS_DISCOVERY
328
  /* TODO: Not implemented. */
329
  if (GC_win32_dll_threads)
330
    return;
331
#      endif
332
  for (i = 0; i < THREAD_TABLE_SZ; ++i) {
7,928,964✔
333
    for (p = GC_threads[i]; p != NULL; p = p->tm.next) {
8,168,913✔
334
      if (!KNOWN_FINISHED(p))
270,801✔
335
        GC_check_tls_for(&p->tlfs);
253,286✔
336
    }
337
  }
338
#      if defined(USE_CUSTOM_SPECIFIC)
339
  if (GC_thread_key != 0)
30,852✔
340
    GC_check_tsd_marks(GC_thread_key);
30,813✔
341
#      endif
342
}
30,852✔
343
#    endif
344

345
#  endif /* THREAD_LOCAL_ALLOC */
346

347
#  ifdef GC_WIN32_THREADS
348
/*
349
 * A macro for functions and variables that should be accessible
350
 * from `win32_threads.c` file but otherwise could be `static`.
351
 */
352
#    define GC_INNER_WIN32THREAD GC_INNER
353
#  else
354
#    define GC_INNER_WIN32THREAD STATIC
355
#  endif
356

357
#  ifdef PARALLEL_MARK
358

359
#    if defined(GC_WIN32_THREADS)                              \
360
        || (defined(USE_PROC_FOR_LIBRARIES) && defined(LINUX)) \
361
        || (defined(IA64)                                      \
362
            && (defined(HAVE_PTHREAD_ATTR_GET_NP)              \
363
                || defined(HAVE_PTHREAD_GETATTR_NP)))
364
GC_INNER_WIN32THREAD ptr_t GC_marker_sp[MAX_MARKERS - 1] = { NULL };
365
#    endif
366

367
#    if defined(IA64) && defined(USE_PROC_FOR_LIBRARIES)
368
static ptr_t marker_bsp[MAX_MARKERS - 1] = { NULL };
369
#    endif
370

371
#    if defined(DARWIN) && defined(DARWIN_PARSE_STACK)
372
static mach_port_t marker_mach_threads[MAX_MARKERS - 1] = { 0 };
373

374
GC_INNER GC_bool
375
GC_is_mach_marker(thread_act_t thread)
376
{
377
  int i;
378
  for (i = 0; i < GC_markers_m1; i++) {
379
    if (marker_mach_threads[i] == thread)
380
      return TRUE;
381
  }
382
  return FALSE;
383
}
384
#    endif
385

386
#    ifdef HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG
387
/* For NetBSD. */
388
static void
389
set_marker_thread_name(unsigned id)
390
{
391
  int err = pthread_setname_np(pthread_self(), "GC-marker-%zu",
392
                               NUMERIC_TO_VPTR(id));
393
  if (UNLIKELY(err != 0))
394
    WARN("pthread_setname_np failed, errno= %" WARN_PRIdPTR "\n",
395
         (GC_signed_word)err);
396
}
397

398
#    elif defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID)     \
399
        || defined(HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID) \
400
        || defined(HAVE_PTHREAD_SET_NAME_NP)
401
#      ifdef HAVE_PTHREAD_SET_NAME_NP
402
#        include <pthread_np.h>
403
#      endif
404
static void
405
set_marker_thread_name(unsigned id)
78✔
406
{
407
  /*
408
   * Note: a smaller size of the buffer may result in
409
   * "output may be truncated" compiler warning.
410
   */
411
  char name_buf[10 + 20 + 1];
412

413
  GC_snprintf_s_ld_s(name_buf, sizeof(name_buf), "GC-marker-", (long)id, "");
78✔
414
#      ifdef HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID
415
  /* The iOS or OS X case. */
416
  (void)pthread_setname_np(name_buf);
417
#      elif defined(HAVE_PTHREAD_SET_NAME_NP)
418
  /* The OpenBSD case. */
419
  pthread_set_name_np(pthread_self(), name_buf);
420
#      else
421
  /* The case of Linux, Solaris, etc. */
422
  GC_ASSERT(strlen(name_buf) < 16);
78✔
423
  /* `pthread_setname_np()` may fail for longer names. */
424
  if (UNLIKELY(pthread_setname_np(pthread_self(), name_buf) != 0))
78✔
425
    WARN("pthread_setname_np failed\n", 0);
×
426
#      endif
427
}
78✔
428

429
#    elif defined(GC_WIN32_THREADS) && !defined(MSWINCE)
430
/*
431
 * A pointer to `SetThreadDescription()` which is available since Windows 10.
432
 * The function prototype is in the platform `processthreadsapi.h` file.
433
 */
434
static FARPROC setThreadDescription_fn;
435

436
GC_INNER void
437
GC_init_win32_thread_naming(HMODULE hK32)
438
{
439
  if (hK32)
440
    setThreadDescription_fn = GetProcAddress(hK32, "SetThreadDescription");
441
}
442

443
static void
444
set_marker_thread_name(unsigned id)
445
{
446
  WCHAR name_buf[16];
447
  int len = sizeof(L"GC-marker-") / sizeof(WCHAR) - 1;
448
  HRESULT hr;
449

450
  if (!setThreadDescription_fn) {
451
    /* `SetThreadDescription()` is missing. */
452
    return;
453
  }
454

455
  /* Compose the name manually as `swprintf` may be unavailable. */
456
  BCOPY(L"GC-marker-", name_buf, len * sizeof(WCHAR));
457
  if (id >= 10)
458
    name_buf[len++] = (WCHAR)('0' + (id / 10) % 10);
459
  name_buf[len] = (WCHAR)('0' + id % 10);
460
  name_buf[len + 1] = 0;
461

462
  /*
463
   * Invoke `SetThreadDescription()`.  Cast the function pointer to
464
   * `GC_funcptr_uint` first to avoid "incompatible function types"
465
   * compiler warning.
466
   */
467
  hr = (*(HRESULT(WINAPI *)(HANDLE, const WCHAR *))(
468
      GC_funcptr_uint)setThreadDescription_fn)(GetCurrentThread(), name_buf);
469
  if (hr < 0)
470
    WARN("SetThreadDescription failed\n", 0);
471
}
472
#    else
473
#      define set_marker_thread_name(id) (void)(id)
474
#    endif
475

476
GC_INNER_WIN32THREAD
477
#    ifdef GC_PTHREADS_PARAMARK
478
void *
479
GC_mark_thread(void *id)
78✔
480
#    elif defined(MSWINCE)
481
DWORD WINAPI
482
GC_mark_thread(LPVOID id)
483
#    else
484
unsigned __stdcall GC_mark_thread(void *id)
485
#    endif
486
{
487
  word my_mark_no = 0;
78✔
488
  word id_n = (word)(GC_uintptr_t)id;
78✔
489
  IF_CANCEL(int cancel_state;)
490

491
#    ifdef CPPCHECK
492
  GC_noop1_ptr(id);
493
#    endif
494
  if (id_n == GC_WORD_MAX)
78✔
495
    return 0; /*< to prevent a compiler warning */
×
496

497
  /*
498
   * Mark threads are not cancellable; they should be invisible to
499
   * client.
500
   */
501
  DISABLE_CANCEL(cancel_state);
78✔
502

503
  set_marker_thread_name((unsigned)id_n);
78✔
504
#    if defined(GC_WIN32_THREADS)                              \
505
        || (defined(USE_PROC_FOR_LIBRARIES) && defined(LINUX)) \
506
        || (defined(IA64)                                      \
507
            && (defined(HAVE_PTHREAD_ATTR_GET_NP)              \
508
                || defined(HAVE_PTHREAD_GETATTR_NP)))
509
  GC_marker_sp[id_n] = GC_approx_sp();
510
#    endif
511
#    if defined(IA64) && defined(USE_PROC_FOR_LIBRARIES)
512
  marker_bsp[id_n] = GC_save_regs_in_stack();
513
#    endif
514
#    if defined(DARWIN) && defined(DARWIN_PARSE_STACK)
515
  marker_mach_threads[id_n] = mach_thread_self();
516
#    endif
517
#    if !defined(GC_PTHREADS_PARAMARK)
518
  GC_marker_Id[id_n] = thread_id_self();
519
#    endif
520

521
  /* Inform `GC_start_mark_threads` about completion of marker data init. */
522
  GC_acquire_mark_lock();
78✔
523
  /* Note: the count variable may have a negative value. */
524
  if (0 == --GC_fl_builder_count)
78✔
525
    GC_notify_all_builder();
64✔
526

527
  /*
528
   * `GC_mark_no` is passed only to allow `GC_help_marker` to
529
   * terminate promptly.  This is important if it were called from the
530
   * signal handler or from the allocator lock acquisition code.
531
   * On Linux, it is not safe to call it from a signal handler, since
532
   * it uses mutex and condition variables.  Since it is called only
533
   * here, the argument is unnecessary.
534
   */
535
  for (;; ++my_mark_no) {
1,947✔
536
    if (my_mark_no - GC_mark_no > (word)2) {
2,025✔
537
      /* Resynchronize if we get far off, e.g. because `GC_mark_no` wrapped. */
538
      my_mark_no = GC_mark_no;
84✔
539
    }
540
#    ifdef DEBUG_THREADS
541
    GC_log_printf("Starting helper for mark number %lu (thread %u)\n",
542
                  (unsigned long)my_mark_no, (unsigned)id_n);
543
#    endif
544
    GC_help_marker(my_mark_no);
2,025✔
545
  }
546
}
547

548
GC_INNER_WIN32THREAD int GC_available_markers_m1 = 0;
549

550
#  endif /* PARALLEL_MARK */
551

552
#  ifdef GC_PTHREADS_PARAMARK
553

554
#    ifdef GLIBC_2_1_MUTEX_HACK
555
/*
556
 * Ugly workaround for a Linux threads bug in the final versions
557
 * of `glibc` 2.1.  `pthread_mutex_trylock` sets the mutex owner
558
 * field even when it fails to acquire the mutex.  This causes
559
 * `pthread_cond_wait` to die.  Should not be needed for `glibc` 2.2.
560
 * According to the man page, we should use
561
 * `PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP`, but that is not actually
562
 * defined.
563
 */
564
static pthread_mutex_t mark_mutex
565
    = { 0, 0, 0, PTHREAD_MUTEX_ERRORCHECK_NP, { 0, 0 } };
566
#    else
567
static pthread_mutex_t mark_mutex = PTHREAD_MUTEX_INITIALIZER;
568
#    endif
569

570
#    ifdef CAN_HANDLE_FORK
571
/* Note: this is initialized by `GC_start_mark_threads_inner()`. */
572
static pthread_cond_t mark_cv;
573
#    else
574
static pthread_cond_t mark_cv = PTHREAD_COND_INITIALIZER;
575
#    endif
576

577
GC_INNER void
578
GC_start_mark_threads_inner(void)
78✔
579
{
580
  int i;
581
  pthread_attr_t attr;
582
#    ifndef NO_MARKER_SPECIAL_SIGMASK
583
  sigset_t set, oldset;
584
#    endif
585

586
  GC_ASSERT(I_HOLD_LOCK());
78✔
587
  ASSERT_CANCEL_DISABLED();
78✔
588
  if (GC_available_markers_m1 <= 0 || GC_parallel) {
78✔
589
    /* Skip if parallel markers disabled or already started (or starting). */
590
    return;
×
591
  }
592
  GC_wait_for_gc_completion(TRUE);
78✔
593

594
#    ifdef CAN_HANDLE_FORK
595
  /*
596
   * Initialize `mark_cv` (for the first time), or cleanup its value
597
   * after forking in the child process.  All the marker threads in the
598
   * parent process were blocked on this variable at process fork, so
599
   * `pthread_cond_wait()` malfunction (hang) is possible in the child
600
   * process without such a cleanup.
601
   */
602

603
  /*
604
   * TODO: This is not portable, it is better to shortly unblock all
605
   * marker threads in the parent process at `fork`.
606
   */
607
  {
608
    pthread_cond_t mark_cv_local = PTHREAD_COND_INITIALIZER;
78✔
609
    BCOPY(&mark_cv_local, &mark_cv, sizeof(mark_cv));
78✔
610
  }
611
#    endif
612

613
  GC_ASSERT(0 == GC_fl_builder_count);
78✔
614
  INIT_REAL_SYMS(); /*< for `pthread_sigmask` and `pthread_create` */
615

616
  if (pthread_attr_init(&attr) != 0)
78✔
617
    ABORT("pthread_attr_init failed");
×
618
  if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0)
78✔
619
    ABORT("pthread_attr_setdetachstate failed");
×
620

621
#    ifdef DEFAULT_STACK_MAYBE_SMALL
622
  /*
623
   * The default stack size is usually too small: increase it.
624
   * Otherwise marker threads may run out of space.
625
   */
626
  {
627
    size_t old_size;
628

629
    if (pthread_attr_getstacksize(&attr, &old_size) != 0)
630
      ABORT("pthread_attr_getstacksize failed");
631
    if (old_size < MIN_STACK_SIZE && old_size != 0 /* stack size is known */) {
632
      if (pthread_attr_setstacksize(&attr, MIN_STACK_SIZE) != 0)
633
        ABORT("pthread_attr_setstacksize failed");
634
    }
635
  }
636
#    endif /* DEFAULT_STACK_MAYBE_SMALL */
637

638
#    ifndef NO_MARKER_SPECIAL_SIGMASK
639
  /*
640
   * Apply special signal mask to GC marker threads, and do not drop
641
   * user-defined signals by the marker threads.
642
   */
643
  if (sigfillset(&set) != 0)
78✔
644
    ABORT("sigfillset failed");
×
645

646
#      ifdef SIGNAL_BASED_STOP_WORLD
647
  /* These are used by GC to stop and restart the world. */
648
  if (sigdelset(&set, GC_get_suspend_signal()) != 0
78✔
649
      || sigdelset(&set, GC_get_thr_restart_signal()) != 0)
78✔
650
    ABORT("sigdelset failed");
×
651
#      endif
652

653
  if (UNLIKELY(REAL_FUNC(pthread_sigmask)(SIG_BLOCK, &set, &oldset) != 0)) {
78✔
654
    WARN("pthread_sigmask set failed, no markers started\n", 0);
×
655
    GC_markers_m1 = 0;
×
656
    (void)pthread_attr_destroy(&attr);
×
657
    return;
×
658
  }
659
#    endif /* !NO_MARKER_SPECIAL_SIGMASK */
660

661
  /* To have proper `GC_parallel` value in `GC_help_marker()`. */
662
  GC_markers_m1 = GC_available_markers_m1;
78✔
663

664
  for (i = 0; i < GC_available_markers_m1; ++i) {
156✔
665
    int res;
666
    pthread_t new_thread;
667

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

697
#    ifndef NO_MARKER_SPECIAL_SIGMASK
698
  /* Restore previous signal mask. */
699
  if (UNLIKELY(REAL_FUNC(pthread_sigmask)(SIG_SETMASK, &oldset, NULL) != 0)) {
78✔
700
    WARN("pthread_sigmask restore failed\n", 0);
×
701
  }
702
#    endif
703

704
  (void)pthread_attr_destroy(&attr);
78✔
705
  GC_wait_for_markers_init();
78✔
706
  GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1);
78✔
707
}
708

709
#  endif /* GC_PTHREADS_PARAMARK */
710

711
GC_INNER GC_thread GC_threads[THREAD_TABLE_SZ] = { NULL };
712

713
/*
714
 * It may not be safe to allocate when we register the first thread.
715
 * Note that `next` and `status` fields are unused, but there might be
716
 * some other fields (`crtn`) to be pushed.
717
 */
718
static struct GC_StackContext_Rep first_crtn;
719
static struct GC_Thread_Rep first_thread;
720

721
#  ifndef GC_NO_DEINIT
722
GC_INNER void
723
GC_reset_threads(void)
×
724
{
725
  BZERO(GC_threads, sizeof(GC_threads));
×
726
  BZERO(&first_crtn, sizeof(first_crtn));
×
727
  BZERO(&first_thread, sizeof(first_thread));
×
728
}
×
729
#  endif
730

731
/*
732
 * A place to retain a pointer to an allocated object while a thread
733
 * registration is ongoing.  Protected by the allocator lock.
734
 */
735
static GC_stack_context_t saved_crtn = NULL;
736

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

766
#  if defined(MPROTECT_VDB) && defined(GC_WIN32_THREADS)
767
GC_INNER void
768
GC_win32_unprotect_thread(GC_thread t)
769
{
770
  GC_ASSERT(I_HOLD_LOCK());
771
  if (GC_auto_incremental
772
#    ifdef HAS_WIN32_THREADS_DISCOVERY /*< for `LINT2` */
773
      && !GC_win32_dll_threads
774
#    endif
775
  ) {
776
    GC_stack_context_t crtn = t->crtn;
777

778
    if (crtn != &first_crtn) {
779
      GC_ASSERT(SMALL_OBJ(GC_size(crtn)));
780
      GC_remove_protection(HBLKPTR(crtn), 1, FALSE);
781
    }
782
    if (t != &first_thread) {
783
      GC_ASSERT(SMALL_OBJ(GC_size(t)));
784
      GC_remove_protection(HBLKPTR(t), 1, FALSE);
785
    }
786
  }
787
}
788
#  endif /* MPROTECT_VDB && GC_WIN32_THREADS */
789

790
#  ifdef DEBUG_THREADS
791
STATIC int
792
GC_count_threads(void)
793
{
794
  int i, count = 0;
795

796
#    ifdef HAS_WIN32_THREADS_DISCOVERY
797
  /* TODO: Not implemented. */
798
  if (GC_win32_dll_threads)
799
    return -1;
800
#    endif
801
  GC_ASSERT(I_HOLD_READER_LOCK());
802
  for (i = 0; i < THREAD_TABLE_SZ; ++i) {
803
    GC_thread p;
804

805
    for (p = GC_threads[i]; p != NULL; p = p->tm.next) {
806
      if (!KNOWN_FINISHED(p))
807
        ++count;
808
    }
809
  }
810
  return count;
811
}
812
#  endif /* DEBUG_THREADS */
813

814
GC_INNER_WIN32THREAD GC_thread
815
GC_new_thread(thread_id_t self_id)
95,041✔
816
{
817
  int hv = THREAD_TABLE_INDEX(self_id);
95,041✔
818
  GC_thread result;
819

820
  GC_ASSERT(I_HOLD_LOCK());
95,041✔
821
#  ifdef DEBUG_THREADS
822
  GC_log_printf("Creating thread %p\n", THREAD_ID_TO_VPTR(self_id));
823
  for (result = GC_threads[hv]; result != NULL; result = result->tm.next)
824
    if (!THREAD_ID_EQUAL(result->id, self_id)) {
825
      GC_log_printf("Hash collision at GC_threads[%d]\n", hv);
826
      break;
827
    }
828
#  endif
829
  if (UNLIKELY(NULL == first_thread.crtn)) {
95,041✔
830
    result = &first_thread;
39✔
831
    first_thread.crtn = &first_crtn;
39✔
832
    GC_ASSERT(NULL == GC_threads[hv]);
39✔
833
#  if defined(CPPCHECK) && defined(THREAD_SANITIZER) \
834
      && defined(SIGNAL_BASED_STOP_WORLD)
835
    GC_noop1((unsigned char)first_crtn.dummy[0]);
836
#  endif
837
  } else {
838
    GC_stack_context_t crtn;
839

840
    GC_ASSERT(!GC_win32_dll_threads);
841
    GC_ASSERT(!GC_in_thread_creation);
95,002✔
842
    GC_in_thread_creation = TRUE; /*< OK to collect from unknown thread */
95,002✔
843
    crtn = (GC_stack_context_t)GC_INTERNAL_MALLOC(
95,002✔
844
        sizeof(struct GC_StackContext_Rep), NORMAL);
845

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

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

907
    GC_ASSERT(I_HOLD_LOCK());
94,743✔
908
#  if defined(DEBUG_THREADS) && !defined(MSWINCE) \
909
      && (!defined(MSWIN32) || defined(CONSOLE_LOG))
910
    GC_log_printf("Deleting thread %p, n_threads= %d\n", THREAD_ID_TO_VPTR(id),
911
                  GC_count_threads());
912
#  endif
913
    for (p = GC_threads[hv]; p != t; p = p->tm.next) {
97,575✔
914
      prev = p;
2,832✔
915
    }
916
    if (NULL == prev) {
94,743✔
917
      GC_threads[hv] = p->tm.next;
92,142✔
918
    } else {
919
      GC_ASSERT(prev != &first_thread);
2,601✔
920
      prev->tm.next = p->tm.next;
2,601✔
921
      GC_dirty(prev);
2,601✔
922
    }
923
    if (LIKELY(p != &first_thread)) {
94,743✔
924
#  ifdef DARWIN
925
      mach_port_deallocate(mach_task_self(), p->mach_thread);
926
#  endif
927
      GC_ASSERT(p->crtn != &first_crtn);
94,740✔
928
      GC_INTERNAL_FREE(p->crtn);
94,740✔
929
      GC_INTERNAL_FREE(p);
94,740✔
930
    }
931
  }
932
}
94,743✔
933

934
GC_INNER GC_thread
935
GC_lookup_thread(thread_id_t id)
203,827,246✔
936
{
937
  GC_thread p;
938

939
#  ifdef HAS_WIN32_THREADS_DISCOVERY
940
  if (GC_win32_dll_threads)
941
    return GC_win32_dll_lookup_thread(id);
942
#  endif
943
  for (p = GC_threads[THREAD_TABLE_INDEX(id)]; p != NULL; p = p->tm.next) {
204,067,784✔
944
    if (LIKELY(THREAD_ID_EQUAL(p->id, id)))
204,022,080✔
945
      break;
203,781,542✔
946
  }
947
  return p;
203,827,246✔
948
}
949

950
/*
951
 * Same as `GC_self_thread_inner()` but acquires the allocator lock (in
952
 * the reader mode).
953
 */
954
STATIC GC_thread
955
GC_self_thread(void)
200,019,805✔
956
{
957
  GC_thread p;
958

959
  READER_LOCK();
200,019,805✔
960
  p = GC_self_thread_inner();
200,019,805✔
961
  READER_UNLOCK();
200,019,805✔
962
  return p;
200,019,805✔
963
}
964

965
#  ifndef GC_NO_FINALIZATION
966
GC_INNER void
967
GC_reset_finalizer_nested(void)
×
968
{
969
  GC_ASSERT(I_HOLD_LOCK());
×
970
  GC_self_thread_inner()->crtn->finalizer_nested = 0;
×
971
}
×
972

973
GC_INNER unsigned char *
974
GC_check_finalizer_nested(void)
3,268✔
975
{
976
  GC_thread me;
977
  GC_stack_context_t crtn;
978
  unsigned nesting_level;
979

980
  GC_ASSERT(I_HOLD_LOCK());
3,268✔
981
  me = GC_self_thread_inner();
3,268✔
982
#    if defined(INCLUDE_LINUX_THREAD_DESCR) && defined(REDIRECT_MALLOC)
983
  /*
984
   * As noted in `GC_pthread_start`, an allocation may happen in
985
   * `GC_get_stack_base`, causing `GC_notify_or_invoke_finalizers`
986
   * to be called before the thread gets registered.
987
   */
988
  if (UNLIKELY(NULL == me))
989
    return NULL;
990
#    endif
991
  crtn = me->crtn;
3,268✔
992
  nesting_level = crtn->finalizer_nested;
3,268✔
993
  if (nesting_level) {
3,268✔
994
    /*
995
     * We are inside another `GC_invoke_finalizers()`.  Skip some
996
     * implicitly-called `GC_invoke_finalizers()` depending on the
997
     * nesting (recursion) level.
998
     */
999
    if ((unsigned)(++crtn->finalizer_skipped) < (1U << nesting_level))
×
1000
      return NULL;
×
1001
    crtn->finalizer_skipped = 0;
×
1002
  }
1003
  crtn->finalizer_nested = (unsigned char)(nesting_level + 1);
3,268✔
1004
  return &crtn->finalizer_nested;
3,268✔
1005
}
1006
#  endif /* !GC_NO_FINALIZATION */
1007

1008
#  define ADDR_INSIDE_OBJ(p, obj) \
1009
    ADDR_INSIDE(p, (ptr_t)(&(obj)), (ptr_t)(&(obj)) + sizeof(obj))
1010

1011
#  if defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC)
1012
/* This is called from thread-local `GC_malloc()`. */
1013
GC_bool
1014
GC_is_thread_tsd_valid(void *tsd)
200,019,736✔
1015
{
1016
  GC_thread me = GC_self_thread();
200,019,736✔
1017

1018
  return ADDR_INSIDE_OBJ((ptr_t)tsd, me->tlfs);
200,019,736✔
1019
}
1020
#  endif /* GC_ASSERTIONS && THREAD_LOCAL_ALLOC */
1021

1022
GC_API int GC_CALL
1023
GC_thread_is_registered(void)
67✔
1024
{
1025
  /* TODO: Use `GC_get_tlfs()` instead. */
1026
  GC_thread me = GC_self_thread();
67✔
1027

1028
  if (NULL == me)
67✔
1029
    return FALSE; /*< for `LINT2` */
×
1030

1031
  return !KNOWN_FINISHED(me);
67✔
1032
}
1033

1034
GC_API void GC_CALL
1035
GC_register_altstack(void *normstack, size_t normstack_size, void *altstack,
×
1036
                     size_t altstack_size)
1037
{
1038
#  ifdef GC_WIN32_THREADS
1039
  /* TODO: Implement. */
1040
  UNUSED_ARG(normstack);
1041
  UNUSED_ARG(normstack_size);
1042
  UNUSED_ARG(altstack);
1043
  UNUSED_ARG(altstack_size);
1044
#  else
1045
  GC_thread me;
1046
  GC_stack_context_t crtn;
1047

1048
  READER_LOCK();
×
1049
  me = GC_self_thread_inner();
×
1050
  if (UNLIKELY(NULL == me)) {
×
1051
    /* We are called before `GC_thr_init()`. */
1052
    me = &first_thread;
×
1053
  }
1054
  crtn = me->crtn;
×
1055
  crtn->normstack = (ptr_t)normstack;
×
1056
  crtn->normstack_size = normstack_size;
×
1057
  crtn->altstack = (ptr_t)altstack;
×
1058
  crtn->altstack_size = altstack_size;
×
1059
  READER_UNLOCK_RELEASE();
×
1060
#  endif
1061
}
×
1062

1063
#  if defined(USE_PROC_FOR_LIBRARIES) && defined(LINUX)
1064
GC_INNER GC_bool
1065
GC_segment_is_thread_stack(ptr_t lo, ptr_t hi)
1066
{
1067
  int i;
1068
  GC_thread p;
1069

1070
  GC_ASSERT(I_HOLD_READER_LOCK());
1071
#    ifdef PARALLEL_MARK
1072
  for (i = 0; i < GC_markers_m1; ++i) {
1073
    if (ADDR_LT(lo, GC_marker_sp[i]) && ADDR_LT(GC_marker_sp[i], hi))
1074
      return TRUE;
1075
#      ifdef IA64
1076
    if (ADDR_LT(lo, marker_bsp[i]) && ADDR_LT(marker_bsp[i], hi))
1077
      return TRUE;
1078
#      endif
1079
  }
1080
#    endif
1081
  for (i = 0; i < THREAD_TABLE_SZ; i++) {
1082
    for (p = GC_threads[i]; p != NULL; p = p->tm.next) {
1083
      ptr_t stack_end = p->crtn->stack_end;
1084

1085
      if (stack_end != NULL) {
1086
#    ifdef STACK_GROWS_UP
1087
        if (ADDR_INSIDE(stack_end, lo, hi))
1088
          return TRUE;
1089
#    else
1090
        if (ADDR_LT(lo, stack_end) && ADDR_GE(hi, stack_end))
1091
          return TRUE;
1092
#    endif
1093
      }
1094
    }
1095
  }
1096
  return FALSE;
1097
}
1098
#  endif
1099

1100
#  if (defined(HAVE_PTHREAD_ATTR_GET_NP) || defined(HAVE_PTHREAD_GETATTR_NP)) \
1101
      && defined(IA64)
1102
GC_INNER ptr_t
1103
GC_greatest_stack_base_below(ptr_t bound)
1104
{
1105
  int i;
1106
  GC_thread p;
1107
  ptr_t result = NULL;
1108

1109
  GC_ASSERT(I_HOLD_READER_LOCK());
1110
#    ifdef PARALLEL_MARK
1111
  for (i = 0; i < GC_markers_m1; ++i) {
1112
    if (ADDR_LT(result, GC_marker_sp[i]) && ADDR_LT(GC_marker_sp[i], bound))
1113
      result = GC_marker_sp[i];
1114
  }
1115
#    endif
1116
  for (i = 0; i < THREAD_TABLE_SZ; i++) {
1117
    for (p = GC_threads[i]; p != NULL; p = p->tm.next) {
1118
      ptr_t stack_end = p->crtn->stack_end;
1119

1120
      if (ADDR_LT(result, stack_end) && ADDR_LT(stack_end, bound))
1121
        result = stack_end;
1122
    }
1123
  }
1124
  return result;
1125
}
1126
#  endif /* IA64 */
1127

1128
#  ifndef STAT_READ
1129
/*
1130
 * Note: if `read()` is wrapped, this may need to be redefined to call
1131
 * the real one.
1132
 */
1133
#    define STAT_READ read
1134
#  endif
1135

1136
#  ifdef HPUX
1137
#    define GC_get_nprocs() pthread_num_processors_np()
1138

1139
#  elif defined(AIX) || defined(COSMO) || defined(HAIKU)         \
1140
      || defined(HOST_ANDROID) || defined(HURD) || defined(NACL) \
1141
      || defined(OSF1) || defined(SOLARIS)
1142
GC_INLINE int
1143
GC_get_nprocs(void)
1144
{
1145
  int nprocs = (int)sysconf(_SC_NPROCESSORS_ONLN);
1146
  /* Note: ignore any error silently. */
1147
  return nprocs > 0 ? nprocs : 1;
1148
}
1149

1150
#  elif defined(IRIX5)
1151
GC_INLINE int
1152
GC_get_nprocs(void)
1153
{
1154
  int nprocs = (int)sysconf(_SC_NPROC_ONLN);
1155
  /* Note: ignore any error silently. */
1156
  return nprocs > 0 ? nprocs : 1;
1157
}
1158

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

1178
  f = open("/proc/stat", O_RDONLY);
39✔
1179
  if (f < 0) {
39✔
1180
    WARN("Could not open /proc/stat\n", 0);
×
1181
    /* Assume an uniprocessor. */
1182
    return 1;
39✔
1183
  }
1184
  len = STAT_READ(f, stat_buf, sizeof(stat_buf) - 1);
39✔
1185
  /* Unlikely that we need to retry because of an incomplete read here. */
1186
  if (len < 0) {
39✔
1187
    WARN("Failed to read /proc/stat, errno= %" WARN_PRIdPTR "\n",
×
1188
         (GC_signed_word)errno);
1189
    close(f);
×
1190
    return 1;
×
1191
  }
1192
  /* Avoid potential buffer overrun by `atoi()`. */
1193
  stat_buf[len] = '\0';
39✔
1194

1195
  close(f);
39✔
1196

1197
  /*
1198
   * Some old kernels only have a single "cpu nnnn ..." entry in
1199
   * `/proc/stat` pseudo-file.  We identify those as uniprocessors.
1200
   */
1201
  result = 1;
39✔
1202

1203
  for (i = 0; i < len - 4; ++i) {
45,968✔
1204
    if (stat_buf[i] == '\n' && stat_buf[i + 1] == 'c' && stat_buf[i + 2] == 'p'
45,929✔
1205
        && stat_buf[i + 3] == 'u') {
78✔
1206
      int cpu_no = atoi(&stat_buf[i + 4]);
78✔
1207
      if (cpu_no >= result)
78✔
1208
        result = cpu_no + 1;
39✔
1209
    }
1210
  }
1211
  return result;
39✔
1212
}
1213

1214
#  elif defined(DGUX)
1215
/*
1216
 * Return the number of processors, or a non-positive value if the number
1217
 * cannot be determined.
1218
 */
1219
STATIC int
1220
GC_get_nprocs(void)
1221
{
1222
  int numCpus;
1223
  struct dg_sys_info_pm_info pm_sysinfo;
1224
  int status = 0;
1225

1226
  status = dg_sys_info((long int *)&pm_sysinfo, DG_SYS_INFO_PM_INFO_TYPE,
1227
                       DG_SYS_INFO_PM_CURRENT_VERSION);
1228
  if (status < 0) {
1229
    /* Set -1 for an error. */
1230
    numCpus = -1;
1231
  } else {
1232
    /* Active CPUs. */
1233
    numCpus = pm_sysinfo.idle_vp_count;
1234
  }
1235
  return numCpus;
1236
}
1237

1238
#  elif defined(ANY_BSD) || defined(DARWIN)
1239
STATIC int
1240
GC_get_nprocs(void)
1241
{
1242
  int mib[] = { CTL_HW, HW_NCPU };
1243
  int res;
1244
  size_t len = sizeof(res);
1245

1246
  sysctl(mib, sizeof(mib) / sizeof(int), &res, &len, NULL, 0);
1247
  return res;
1248
}
1249

1250
#  else
1251
/* E.g., RTEMS. */
1252
/* TODO: Implement. */
1253
#    define GC_get_nprocs() 1
1254
#  endif
1255

1256
#  if defined(LINUX) && defined(ARM32)
1257
/*
1258
 * Some buggy Linux/arm kernels show only non-sleeping CPUs in
1259
 * `/proc/stat` pseudo-file (and in `/proc/cpuinfo` pseudo-file), so
1260
 * another data system source is tried first.  Returns a non-positive
1261
 * value on error.
1262
 */
1263
STATIC int
1264
GC_get_nprocs_present(void)
1265
{
1266
  char stat_buf[16];
1267
  int f;
1268
  int len;
1269

1270
  f = open("/sys/devices/system/cpu/present", O_RDONLY);
1271
  if (f < 0) {
1272
    /* Cannot open the file. */
1273
    return -1;
1274
  }
1275

1276
  len = STAT_READ(f, stat_buf, sizeof(stat_buf));
1277
  close(f);
1278

1279
  /*
1280
   * Recognized file format: "0\n" or "0-<max_cpu_num>\n".
1281
   * The file might probably contain a comma-separated list
1282
   * but we do not need to handle it (just silently ignore).
1283
   */
1284
  if (len < 2 || stat_buf[0] != '0' || stat_buf[len - 1] != '\n') {
1285
    /* A read error or an unrecognized contents. */
1286
    return 0;
1287
  } else if (len == 2) {
1288
    /* An uniprocessor. */
1289
    return 1;
1290
  } else if (stat_buf[1] != '-') {
1291
    /* An unrecognized contents. */
1292
    return 0;
1293
  }
1294

1295
  /* Terminate the string. */
1296
  stat_buf[len - 1] = '\0';
1297

1298
  /* Skip "0-" and parse `max_cpu_num`. */
1299
  return atoi(&stat_buf[2]) + 1;
1300
}
1301
#  endif /* LINUX && ARM32 */
1302

1303
#  if defined(CAN_HANDLE_FORK) && defined(THREAD_SANITIZER)
1304
#    include "private/gc_pmark.h" /*< for `MS_NONE` */
1305

1306
/*
1307
 * Workaround for TSan which does not notice that the allocator lock
1308
 * is acquired in `fork_prepare_proc()`.
1309
 */
1310
GC_ATTR_NO_SANITIZE_THREAD
1311
static GC_bool
1312
collection_in_progress(void)
1313
{
1314
  return GC_mark_state != MS_NONE;
1315
}
1316
#  else
1317
#    define collection_in_progress() GC_collection_in_progress()
1318
#  endif
1319

1320
GC_INNER void
1321
GC_wait_for_gc_completion(GC_bool wait_for_all)
95,179✔
1322
{
1323
#  if !defined(THREAD_SANITIZER) || !defined(CAN_CALL_ATFORK)
1324
  /*
1325
   * `GC_lock_holder` is accessed with the allocator lock held, so
1326
   * there is no data race actually (unlike what is reported by TSan).
1327
   */
1328
  GC_ASSERT(I_HOLD_LOCK());
95,179✔
1329
#  endif
1330
  ASSERT_CANCEL_DISABLED();
95,179✔
1331
#  ifdef GC_DISABLE_INCREMENTAL
1332
  (void)wait_for_all;
1333
#  else
1334
  if (GC_incremental && collection_in_progress()) {
95,179✔
1335
    word old_gc_no = GC_gc_no;
×
1336

1337
    /*
1338
     * Make sure that no part of our stack is still on the mark stack,
1339
     * since it is about to be unmapped.
1340
     */
1341
#    ifdef LINT2
1342
    /*
1343
     * Note: do not transform this `if`-`do`-`while` construction into
1344
     * a single while statement because it might cause some static code
1345
     * analyzers to report a false positive (FP) code defect about
1346
     * missing unlock after lock.
1347
     */
1348
#    endif
1349
    do {
1350
      GC_ASSERT(!GC_in_thread_creation);
×
1351
      GC_in_thread_creation = TRUE;
×
1352
      GC_collect_a_little_inner(1);
×
1353
      GC_in_thread_creation = FALSE;
×
1354

1355
      UNLOCK();
×
1356
#    ifdef GC_WIN32_THREADS
1357
      Sleep(0);
1358
#    else
1359
      sched_yield();
×
1360
#    endif
1361
      LOCK();
×
1362
    } while (GC_incremental && collection_in_progress()
×
1363
             && (wait_for_all || old_gc_no == GC_gc_no));
×
1364
  }
1365
#  endif
1366
}
95,179✔
1367

1368
#  if defined(GC_ASSERTIONS) && defined(GC_PTHREADS_PARAMARK)
1369
STATIC unsigned long GC_mark_lock_holder = NO_THREAD;
1370
#  endif
1371

1372
#  ifdef CAN_HANDLE_FORK
1373

1374
/*
1375
 * Procedures called before and after a process fork.  The goal here is
1376
 * to make it safe to call `GC_malloc()` in the forked child process.
1377
 * It is unclear that is attainable, since the Single UNIX Specification
1378
 * seems to imply that one should only call async-signal-safe functions,
1379
 * and we probably cannot quite guarantee that.  But we give it our best
1380
 * shot.  (That same specification also implies that it is not safe to
1381
 * call the system `malloc` between `fork` and `exec`.  Thus we are doing
1382
 * no worse than it.)
1383
 */
1384

1385
IF_CANCEL(static int fork_cancel_state;) /*< protected by the allocator lock */
1386

1387
#    ifdef PARALLEL_MARK
1388
#      ifdef THREAD_SANITIZER
1389
#        if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK)
1390
STATIC void GC_generic_lock(pthread_mutex_t *);
1391
#        endif
1392
GC_ATTR_NO_SANITIZE_THREAD
1393
static void wait_for_reclaim_atfork(void);
1394
#      else
1395
#        define wait_for_reclaim_atfork() GC_wait_for_reclaim()
1396
#      endif
1397
#    endif /* PARALLEL_MARK */
1398

1399
/*
1400
 * Prevent TSan false positive (FP) about the race during items removal
1401
 * from `GC_threads`.  (The race cannot happen since only one thread
1402
 * survives in the child process.)
1403
 */
1404
#    ifdef CAN_CALL_ATFORK
1405
GC_ATTR_NO_SANITIZE_THREAD
1406
#    endif
1407
static void
1408
store_to_threads_table(int hv, GC_thread me)
×
1409
{
1410
  GC_threads[hv] = me;
×
1411
}
×
1412

1413
/*
1414
 * Remove all entries from the `GC_threads` table, except the one (if any)
1415
 * for the current thread.  Also update thread identifiers stored in the
1416
 * table for the current thread.  We need to do this in the child process
1417
 * after a `fork()`, since only the current thread survives in the child
1418
 * process.
1419
 */
1420
STATIC void
1421
GC_remove_all_threads_but_me(void)
×
1422
{
1423
  int hv;
1424
  GC_thread me = NULL;
×
1425
#    ifndef GC_WIN32_THREADS
1426
#      define pthread_id id
1427
#    endif
1428

1429
  for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) {
×
1430
    GC_thread p, next;
1431

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

1469
  if (NULL == me)
×
1470
    return; /*< `fork()` is called from an unregistered thread */
×
1471

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

1502
  /* Put `me` back to `GC_threads`. */
1503
  store_to_threads_table(THREAD_TABLE_INDEX(me->id), me);
×
1504

1505
#    ifdef THREAD_LOCAL_ALLOC
1506
  GC_update_specific_after_fork(GC_thread_key, &me->tlfs);
×
1507
#    endif
1508
#    undef pthread_id
1509
}
1510

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

1519
  return me != NULL && !KNOWN_FINISHED(me);
63✔
1520
}
1521

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

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

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

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

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

1712
/*
1713
 * Routines for `fork()` handling by client (no-op if `pthread_atfork`
1714
 * works).
1715
 */
1716

1717
GC_API void GC_CALL
1718
GC_atfork_prepare(void)
63✔
1719
{
1720
  if (UNLIKELY(!GC_is_initialized))
63✔
1721
    GC_init();
×
1722
  if (GC_handle_fork <= 0)
63✔
1723
    fork_prepare_proc();
×
1724
}
63✔
1725

1726
GC_API void GC_CALL
1727
GC_atfork_parent(void)
63✔
1728
{
1729
  if (GC_handle_fork <= 0)
63✔
1730
    fork_parent_proc();
×
1731
}
63✔
1732

1733
GC_API void GC_CALL
1734
GC_atfork_child(void)
63✔
1735
{
1736
  if (GC_handle_fork <= 0)
63✔
1737
    fork_child_proc();
×
1738
}
63✔
1739

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

1758
#  endif /* CAN_HANDLE_FORK */
1759

1760
#  ifdef INCLUDE_LINUX_THREAD_DESCR
1761
__thread int GC_dummy_thread_local;
1762
#  endif
1763

1764
#  ifdef PARALLEL_MARK
1765
#    ifndef GC_WIN32_THREADS
1766
static void setup_mark_lock(void);
1767
#    endif
1768

1769
GC_INNER_WIN32THREAD unsigned GC_required_markers_cnt = 0;
1770

1771
GC_API void GC_CALL
1772
GC_set_markers_count(unsigned markers)
3✔
1773
{
1774
  GC_required_markers_cnt = markers < MAX_MARKERS ? markers : MAX_MARKERS;
3✔
1775
}
3✔
1776
#  endif /* PARALLEL_MARK */
1777

1778
GC_INNER GC_bool GC_in_thread_creation = FALSE;
1779

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

1797
#  if !defined(DONT_USE_ATEXIT) || defined(HAS_WIN32_THREADS_DISCOVERY)
1798
GC_INNER_WIN32THREAD thread_id_t GC_main_thread_id;
1799
#  endif
1800

1801
#  ifndef DONT_USE_ATEXIT
1802
GC_INNER GC_bool
1803
GC_is_main_thread(void)
4✔
1804
{
1805
  GC_ASSERT(GC_thr_initialized);
4✔
1806
  return THREAD_ID_EQUAL(GC_main_thread_id, thread_id_self());
4✔
1807
}
1808
#  endif /* !DONT_USE_ATEXIT */
1809

1810
#  ifndef GC_WIN32_THREADS
1811

1812
STATIC GC_thread
1813
GC_register_my_thread_inner(const struct GC_stack_base *sb,
95,039✔
1814
                            thread_id_t self_id)
1815
{
1816
  GC_thread me;
1817

1818
  GC_ASSERT(I_HOLD_LOCK());
95,039✔
1819
  me = GC_new_thread(self_id);
95,039✔
1820
  me->id = self_id;
95,039✔
1821
#    ifdef DARWIN
1822
  me->mach_thread = mach_thread_self();
1823
#    endif
1824
  GC_record_stack_base(me->crtn, sb);
95,039✔
1825
  return me;
95,039✔
1826
}
1827

1828
/*
1829
 * Number of processors.  We may not have access to all of them, but
1830
 * this is as good a guess as any...
1831
 */
1832
STATIC int GC_nprocs = 1;
1833

1834
GC_INNER void
1835
GC_thr_init(void)
39✔
1836
{
1837
  GC_ASSERT(I_HOLD_LOCK());
39✔
1838
  GC_ASSERT(!GC_thr_initialized);
39✔
1839
  ASSERT_ALIGNMENT(&GC_threads);
1840
#    ifdef GC_ASSERTIONS
1841
  GC_thr_initialized = TRUE;
39✔
1842
#    endif
1843
#    ifdef CAN_HANDLE_FORK
1844
  GC_setup_atfork();
39✔
1845
#    endif
1846

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

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

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

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

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

1973
#    ifndef DARWIN
1974
  GC_stop_init();
39✔
1975
#    endif
1976

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

1987
  /* Add the initial thread, so we can stop it. */
1988
  {
1989
    struct GC_stack_base sb;
1990
    GC_thread me;
1991
    thread_id_t self_id = thread_id_self();
39✔
1992

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

2007
#  endif /* !GC_WIN32_THREADS */
2008

2009
GC_INNER void
2010
GC_init_parallel(void)
39✔
2011
{
2012
#  ifdef THREAD_LOCAL_ALLOC
2013
  GC_thread me;
2014

2015
  GC_ASSERT(GC_is_initialized);
39✔
2016
  LOCK(); /*< redundant in case of `DllMain`-based thread registration */
39✔
2017
  me = GC_self_thread_inner();
39✔
2018
  GC_init_thread_local(&me->tlfs);
39✔
2019
  UNLOCK();
39✔
2020
#  endif
2021
#  ifdef HAS_WIN32_THREADS_DISCOVERY
2022
  if (GC_win32_dll_threads) {
2023
    /*
2024
     * Cannot intercept thread creation.  Hence we do not know if other
2025
     * threads exist.  However, client is not allowed to create other threads
2026
     * before collector initialization.  Thus it is OK not to lock before
2027
     * this.
2028
     */
2029
    set_need_to_lock();
2030
  }
2031
#  endif
2032
}
39✔
2033

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

2047
  INIT_REAL_SYMS();
2048
  if (LIKELY(set != NULL) && (how == SIG_BLOCK || how == SIG_SETMASK)) {
6✔
2049
    int sig_suspend = GC_get_suspend_signal();
3✔
2050

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

2063
/*
2064
 * Wrapper for functions that are likely to block for an appreciable
2065
 * length of time.
2066
 */
2067

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

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

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

2125
static void
2126
do_blocking_leave(GC_thread me, GC_bool topOfStackUnset)
1,449✔
2127
{
2128
  GC_ASSERT(I_HOLD_READER_LOCK());
1,449✔
2129
  me->flags &= (unsigned char)~DO_BLOCKING;
1,449✔
2130
#  ifdef E2K
2131
  {
2132
    GC_stack_context_t crtn = me->crtn;
2133

2134
    GC_ASSERT(crtn->backing_store_end != NULL);
2135
    crtn->backing_store_ptr = NULL;
2136
    crtn->backing_store_end = NULL;
2137
  }
2138
#  endif
2139
#  if defined(DARWIN) && defined(DARWIN_PARSE_STACK)
2140
  if (topOfStackUnset) {
2141
    /* Make it unset again. */
2142
    me->crtn->topOfStack = NULL;
2143
  }
2144
#  else
2145
  (void)topOfStackUnset;
2146
#  endif
2147
}
1,449✔
2148

2149
GC_INNER void
2150
GC_do_blocking_inner(ptr_t data, void *context)
126✔
2151
{
2152
  GC_thread me;
2153
  GC_bool topOfStackUnset;
2154

2155
  UNUSED_ARG(context);
2156
  READER_LOCK();
126✔
2157
  me = GC_self_thread_inner();
126✔
2158
  do_blocking_enter(&topOfStackUnset, me);
126✔
2159
  READER_UNLOCK_RELEASE();
126✔
2160

2161
  ((struct blocking_data *)data)->client_data /*< result */
2162
      = ((struct blocking_data *)data)
252✔
2163
            ->fn(((struct blocking_data *)data)->client_data);
126✔
2164

2165
  /* This will block if the world is stopped. */
2166
  READER_LOCK();
126✔
2167

2168
#  ifdef LINT2
2169
  {
2170
#    ifdef GC_ASSERTIONS
2171
    GC_thread saved_me = me;
2172
#    endif
2173

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

2194
    READER_UNLOCK_RELEASE();
×
2195
    GC_suspend_self_inner(me, suspend_cnt);
×
2196
    READER_LOCK();
×
2197
  }
2198
#  endif
2199
  do_blocking_leave(me, topOfStackUnset);
126✔
2200
  READER_UNLOCK_RELEASE();
126✔
2201
}
126✔
2202

2203
#  if defined(GC_ENABLE_SUSPEND_THREAD) && defined(SIGNAL_BASED_STOP_WORLD)
2204
GC_INNER void
2205
GC_suspend_self_blocked(ptr_t thread_me, void *context)
1,323✔
2206
{
2207
  GC_thread me = (GC_thread)thread_me;
1,323✔
2208
  GC_bool topOfStackUnset;
2209

2210
  UNUSED_ARG(context);
2211

2212
  /*
2213
   * The caller holds the allocator lock in the exclusive mode, thus
2214
   * we require and restore it to the same mode upon return from the
2215
   * function.
2216
   */
2217
  GC_ASSERT(I_HOLD_LOCK());
1,323✔
2218

2219
  do_blocking_enter(&topOfStackUnset, me);
1,323✔
2220
  while ((me->ext_suspend_cnt & 1) != 0) {
3,969✔
2221
    size_t suspend_cnt = me->ext_suspend_cnt;
1,323✔
2222

2223
    UNLOCK();
1,323✔
2224
    GC_suspend_self_inner(me, suspend_cnt);
1,323✔
2225
    LOCK();
1,323✔
2226
  }
2227
  do_blocking_leave(me, topOfStackUnset);
1,323✔
2228
}
1,323✔
2229
#  endif /* GC_ENABLE_SUSPEND_THREAD */
2230

2231
GC_API void GC_CALL
2232
GC_set_stackbottom(void *gc_thread_handle, const struct GC_stack_base *sb)
63✔
2233
{
2234
  GC_thread t = (GC_thread)gc_thread_handle;
63✔
2235
  GC_stack_context_t crtn;
2236

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

2248
  GC_ASSERT(I_HOLD_READER_LOCK());
63✔
2249
  if (NULL == t) {
63✔
2250
    /* The current thread. */
2251
    t = GC_self_thread_inner();
×
2252
  }
2253
  GC_ASSERT(!KNOWN_FINISHED(t));
63✔
2254
  crtn = t->crtn;
63✔
2255
  GC_ASSERT((t->flags & DO_BLOCKING) == 0
63✔
2256
            && NULL == crtn->traced_stack_sect); /*< for now */
2257

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

2270
GC_API void *GC_CALL
2271
GC_get_my_stackbottom(struct GC_stack_base *sb)
63✔
2272
{
2273
  GC_thread me;
2274
  GC_stack_context_t crtn;
2275

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

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

2304
  /* This will block if the world is stopped. */
2305
  READER_LOCK();
126✔
2306

2307
  me = GC_self_thread_inner();
126✔
2308
  crtn = me->crtn;
126✔
2309

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

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

2334
#  if defined(GC_ENABLE_SUSPEND_THREAD) && defined(SIGNAL_BASED_STOP_WORLD)
2335
  while (UNLIKELY((me->ext_suspend_cnt & 1) != 0)) {
126✔
2336
    size_t suspend_cnt = me->ext_suspend_cnt;
×
2337

2338
    READER_UNLOCK_RELEASE();
×
2339
    GC_suspend_self_inner(me, suspend_cnt);
×
2340
    READER_LOCK();
×
2341
    GC_ASSERT(me->crtn == crtn);
×
2342
  }
2343
#  endif
2344

2345
  /* Setup new "stack section". */
2346
  stacksect.saved_stack_ptr = crtn->stack_ptr;
126✔
2347
#  ifdef E2K
2348
  GC_ASSERT(crtn->backing_store_end != NULL);
2349
  {
2350
    unsigned long long sz_ull;
2351

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

2374
  READER_UNLOCK_RELEASE();
126✔
2375
  client_data = (*(GC_fn_type volatile *)&fn)(client_data);
126✔
2376
  GC_ASSERT((me->flags & DO_BLOCKING) == 0);
126✔
2377

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

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

2437
GC_API int GC_CALL
2438
GC_unregister_my_thread(void)
45,701✔
2439
{
2440
  GC_thread me;
2441
  IF_CANCEL(int cancel_state;)
2442

2443
  /*
2444
   * Client should not unregister the thread explicitly
2445
   * if it is registered by `DllMain`, except for the main thread.
2446
   */
2447
#  ifdef HAS_WIN32_THREADS_DISCOVERY
2448
  GC_ASSERT(!GC_win32_dll_threads
2449
            || THREAD_ID_EQUAL(GC_main_thread_id, thread_id_self()));
2450
#  endif
2451

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

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

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

2512
#  ifdef GC_HAVE_PTHREAD_EXIT
2513
#    define GC_wrap_pthread_exit WRAP_FUNC(pthread_exit)
2514
GC_API GC_PTHREAD_EXIT_ATTRIBUTE void
2515
GC_wrap_pthread_exit(void *retval)
1,323✔
2516
{
2517
  INIT_REAL_SYMS();
2518
  LOCK();
1,323✔
2519
  disable_gc_for_pthread(GC_self_thread_inner());
1,323✔
2520
  UNLOCK();
1,323✔
2521
  REAL_FUNC(pthread_exit)(retval);
1,323✔
2522
}
2523
#    undef GC_wrap_pthread_exit
2524
#  endif /* GC_HAVE_PTHREAD_EXIT */
2525

2526
GC_API void GC_CALL
2527
GC_allow_register_threads(void)
2✔
2528
{
2529
  /*
2530
   * Check the collector is initialized and the current thread is
2531
   * registered.
2532
   */
2533
  GC_ASSERT(GC_self_thread() != NULL);
2✔
2534

2535
  /* Initialize symbols while still single-threaded. */
2536
  INIT_REAL_SYMS();
2537

2538
  GC_init_lib_bounds();
2539
  GC_start_mark_threads();
2✔
2540
  set_need_to_lock();
2✔
2541
}
2✔
2542

2543
#  if defined(PTHREAD_STOP_WORLD_IMPL)            \
2544
          && !defined(NO_SIGNALS_UNBLOCK_IN_MAIN) \
2545
      || defined(GC_EXPLICIT_SIGNALS_UNBLOCK)
2546
GC_INNER void
2547
GC_unblock_gc_signals(void)
39✔
2548
{
2549
  sigset_t set;
2550

2551
  /* This is for `pthread_sigmask`. */
2552
  INIT_REAL_SYMS();
2553

2554
  sigemptyset(&set);
39✔
2555
  sigaddset(&set, GC_get_suspend_signal());
39✔
2556
  sigaddset(&set, GC_get_thr_restart_signal());
39✔
2557
  if (REAL_FUNC(pthread_sigmask)(SIG_UNBLOCK, &set, NULL) != 0)
39✔
2558
    ABORT("pthread_sigmask failed");
×
2559
}
39✔
2560
#  endif /* PTHREAD_STOP_WORLD_IMPL || GC_EXPLICIT_SIGNALS_UNBLOCK */
2561

2562
GC_API int GC_CALL
2563
GC_register_my_thread(const struct GC_stack_base *sb)
45,958✔
2564
{
2565
  GC_thread me;
2566

2567
#  ifdef GC_ALWAYS_MULTITHREADED
2568
  set_need_to_lock();
2569
#  else
2570
  if (!GC_need_to_lock)
45,958✔
2571
    ABORT("Threads explicit registering is not previously enabled");
×
2572
#  endif
2573

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

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

2634
#  if defined(GC_PTHREADS) && !defined(PLATFORM_THREADS) \
2635
      && !defined(SN_TARGET_PSP2)
2636

2637
GC_INNER_PTHRSTART void
2638
GC_thread_exit_proc(void *arg)
49,333✔
2639
{
2640
  GC_thread me = (GC_thread)arg;
49,333✔
2641
  IF_CANCEL(int cancel_state;)
2642

2643
#    ifdef DEBUG_THREADS
2644
  GC_log_printf("Called GC_thread_exit_proc on %p, gc_thread= %p\n",
2645
                THREAD_ID_TO_VPTR(me->id), (void *)me);
2646
#    endif
2647
  LOCK();
49,333✔
2648
  DISABLE_CANCEL(cancel_state);
49,333✔
2649
  GC_wait_for_gc_completion(FALSE);
49,333✔
2650
  GC_unregister_my_thread_inner(me);
49,333✔
2651
  RESTORE_CANCEL(cancel_state);
49,333✔
2652
  UNLOCK();
49,333✔
2653
}
49,334✔
2654

2655
#    define GC_wrap_pthread_join WRAP_FUNC(pthread_join)
2656
GC_API int
2657
GC_wrap_pthread_join(pthread_t thread, void **retval)
3,207✔
2658
{
2659
  int result;
2660
  GC_thread t;
2661

2662
  INIT_REAL_SYMS();
2663
#    ifdef DEBUG_THREADS
2664
  GC_log_printf("thread %p is joining thread %p\n",
2665
                PTHREAD_TO_VPTR(pthread_self()), PTHREAD_TO_VPTR(thread));
2666
#    endif
2667

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

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

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

2706
#    ifdef DEBUG_THREADS
2707
  GC_log_printf("thread %p join with thread %p %s\n",
2708
                PTHREAD_TO_VPTR(pthread_self()), PTHREAD_TO_VPTR(thread),
2709
                result != 0 ? "failed" : "succeeded");
2710
#    endif
2711
  return result;
3,207✔
2712
}
2713
#    undef GC_wrap_pthread_join
2714

2715
#    define GC_wrap_pthread_detach WRAP_FUNC(pthread_detach)
2716
GC_API int
2717
GC_wrap_pthread_detach(pthread_t thread)
409✔
2718
{
2719
  int result;
2720
  GC_thread t;
2721

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

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

2759
GC_INNER_PTHRSTART GC_thread
2760
GC_start_rtn_prepare_thread(void *(**pstart)(void *), void **pstart_arg,
49,333✔
2761
                            struct GC_stack_base *sb, void *arg)
2762
{
2763
  struct start_info *psi = (struct start_info *)arg;
49,333✔
2764
  thread_id_t self_id = thread_id_self();
49,333✔
2765
  GC_thread me;
2766

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

2793
  *pstart = psi->start_routine;
49,333✔
2794
  *pstart_arg = psi->arg;
49,333✔
2795
#    if defined(DEBUG_THREADS) && defined(FUNCPTR_IS_DATAPTR)
2796
  GC_log_printf("start_routine= %p\n", CAST_THRU_UINTPTR(void *, *pstart));
2797
#    endif
2798
  sem_post(&psi->registered);
49,333✔
2799
  /* This was the last action on `*psi`; OK to deallocate. */
2800
  return me;
49,333✔
2801
}
2802

2803
STATIC void *
2804
GC_pthread_start(void *arg)
49,333✔
2805
{
2806
#    ifdef INCLUDE_LINUX_THREAD_DESCR
2807
  struct GC_stack_base sb;
2808

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

2829
#    define GC_wrap_pthread_create WRAP_FUNC(pthread_create)
2830
GC_API int
2831
GC_wrap_pthread_create(pthread_t *new_thread,
49,573✔
2832
                       GC_PTHREAD_CREATE_CONST pthread_attr_t *attr,
2833
                       void *(*start_routine)(void *), void *arg)
2834
{
2835
  int result;
2836
  struct start_info si;
2837

2838
  GC_ASSERT(I_DONT_HOLD_LOCK());
49,573✔
2839
  INIT_REAL_SYMS();
2840
  if (UNLIKELY(!GC_is_initialized))
49,573✔
2841
    GC_init();
×
2842
  GC_ASSERT(GC_thr_initialized);
49,573✔
2843
  GC_ASSERT(NONNULL_PROC_NOT_ZERO(start_routine));
2844

2845
  GC_init_lib_bounds();
2846
  if (sem_init(&si.registered, GC_SEM_INIT_PSHARED, 0) == -1)
49,573✔
2847
    ABORT("sem_init failed");
×
2848
  si.flags = 0;
49,573✔
2849
  si.start_routine = start_routine;
49,573✔
2850
  si.arg = arg;
49,573✔
2851

2852
  /*
2853
   * We resist the temptation to muck with the stack size here, even if the
2854
   * default is unreasonably small.  That is the client's responsibility.
2855
   */
2856
#    ifdef GC_ASSERTIONS
2857
  {
2858
    size_t stack_size = 0;
49,573✔
2859
    if (NULL != attr) {
49,573✔
2860
      if (pthread_attr_getstacksize(attr, &stack_size) != 0)
46,017✔
2861
        ABORT("pthread_attr_getstacksize failed");
×
2862
    }
2863
    if (0 == stack_size) {
49,573✔
2864
      pthread_attr_t my_attr;
2865

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

2891
  if (attr != NULL) {
49,573✔
2892
    int detachstate;
2893

2894
    if (pthread_attr_getdetachstate(attr, &detachstate) != 0)
46,017✔
2895
      ABORT("pthread_attr_getdetachstate failed");
×
2896
    if (PTHREAD_CREATE_DETACHED == detachstate)
46,017✔
2897
      si.flags |= DETACHED;
45,957✔
2898
  }
2899

2900
#    ifdef PARALLEL_MARK
2901
  if (!GC_parallel && UNLIKELY(GC_available_markers_m1 > 0))
49,573✔
2902
    GC_start_mark_threads();
11✔
2903
#    endif
2904
#    ifdef DEBUG_THREADS
2905
  GC_log_printf("About to start new thread from thread %p\n",
2906
                PTHREAD_TO_VPTR(pthread_self()));
2907
#    endif
2908
  set_need_to_lock();
49,573✔
2909
  result = REAL_FUNC(pthread_create)(new_thread, attr, GC_pthread_start, &si);
49,573✔
2910

2911
  /*
2912
   * Wait until child has been added to the thread table.
2913
   * This also ensures that we hold onto the stack-allocated `si`
2914
   * until the child is done with it.
2915
   */
2916
  if (LIKELY(0 == result)) {
49,327✔
2917
    IF_CANCEL(int cancel_state;)
2918

2919
    /* `pthread_create()` is not a cancellation point. */
2920
    DISABLE_CANCEL(cancel_state);
49,327✔
2921

2922
    while (sem_wait(&si.registered) == -1) {
49,327✔
2923
#    ifdef HAIKU
2924
      /* To workaround some bug in Haiku semaphores. */
2925
      if (EACCES == errno)
2926
        continue;
2927
#    endif
2928
      if (errno != EINTR)
×
2929
        ABORT("sem_wait failed");
×
2930
    }
2931
    RESTORE_CANCEL(cancel_state);
49,313✔
2932
  }
2933
  sem_destroy(&si.registered);
49,313✔
2934
  return result;
49,313✔
2935
}
2936
#    undef GC_wrap_pthread_create
2937

2938
#  endif /* GC_PTHREADS && !PLATFORM_THREADS && !SN_TARGET_PSP2 */
2939

2940
#  if ((defined(GC_PTHREADS_PARAMARK) || defined(USE_PTHREAD_LOCKS)) \
2941
       && !defined(NO_PTHREAD_TRYLOCK))                              \
2942
      || defined(USE_SPIN_LOCK)
2943
/*
2944
 * Spend a few cycles in a way that cannot introduce contention with
2945
 * other threads.
2946
 */
2947
#    define GC_PAUSE_SPIN_CYCLES 10
2948
STATIC void
2949
GC_pause(void)
130,527,799✔
2950
{
2951
  int i;
2952

2953
  for (i = 0; i < GC_PAUSE_SPIN_CYCLES; ++i) {
1,435,805,789✔
2954
    /* Something that is unlikely to be optimized away. */
2955
#    if defined(AO_HAVE_compiler_barrier) && !defined(BASE_ATOMIC_OPS_EMULATED)
2956
    AO_compiler_barrier();
1,305,277,990✔
2957
#    else
2958
    GC_noop1(i);
2959
#    endif
2960
  }
2961
}
130,527,799✔
2962
#  endif /* USE_SPIN_LOCK || !NO_PTHREAD_TRYLOCK */
2963

2964
#  ifndef SPIN_MAX
2965
/* Maximum number of calls to `GC_pause()` before give up. */
2966
#    define SPIN_MAX 128
2967
#  endif
2968

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

2989
#    ifdef LOCK_STATS
2990
/* Note that `LOCK_STATS` requires `AO_HAVE_test_and_set`. */
2991
volatile AO_t GC_spin_count = 0;
2992
volatile AO_t GC_block_count = 0;
2993
volatile AO_t GC_unlocked_count = 0;
2994
#    endif
2995

2996
STATIC void
2997
GC_generic_lock(pthread_mutex_t *lock)
125,754,007✔
2998
{
2999
#    ifndef NO_PTHREAD_TRYLOCK
3000
  unsigned pause_length = 1;
125,754,007✔
3001
  unsigned i;
3002

3003
  if (LIKELY(0 == pthread_mutex_trylock(lock))) {
125,754,007✔
3004
#      ifdef LOCK_STATS
3005
    (void)AO_fetch_and_add1(&GC_unlocked_count);
3006
#      endif
3007
    return;
118,224,756✔
3008
  }
3009
  for (; pause_length <= (unsigned)SPIN_MAX; pause_length <<= 1) {
16,696,456✔
3010
    for (i = 0; i < pause_length; ++i) {
147,099,340✔
3011
      GC_pause();
130,527,799✔
3012
    }
3013
    switch (pthread_mutex_trylock(lock)) {
16,571,541✔
3014
    case 0:
7,404,336✔
3015
#      ifdef LOCK_STATS
3016
      (void)AO_fetch_and_add1(&GC_spin_count);
3017
#      endif
3018
      return;
7,404,336✔
3019
    case EBUSY:
9,167,205✔
3020
      break;
9,167,205✔
3021
    default:
×
3022
      ABORT("Unexpected error from pthread_mutex_trylock");
×
3023
    }
3024
  }
3025
#    endif /* !NO_PTHREAD_TRYLOCK */
3026
#    ifdef LOCK_STATS
3027
  (void)AO_fetch_and_add1(&GC_block_count);
3028
#    endif
3029
  pthread_mutex_lock(lock);
124,915✔
3030
}
3031
#  endif /* !USE_SPIN_LOCK || ... */
3032

3033
#  if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS)
3034
GC_INNER volatile unsigned char GC_collecting = FALSE;
3035

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

3047
#  ifdef GC_ASSERTIONS
3048
GC_INNER unsigned long GC_lock_holder = NO_THREAD;
3049
#  endif
3050

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

3058
/* Spin cycles if we suspect we are running on an uniprocessor. */
3059
#    define low_spin_max 30
3060

3061
/* Spin cycles for a multiprocessor. */
3062
#    define high_spin_max SPIN_MAX
3063

3064
static volatile AO_t spin_max = low_spin_max;
3065

3066
/*
3067
 * A potential data race between threads invoking `GC_lock` which reads
3068
 * and updates `spin_max` and `last_spins` could be ignored because these
3069
 * variables are hints only.
3070
 */
3071
static volatile AO_t last_spins = 0;
3072

3073
GC_INNER void
3074
GC_lock(void)
3075
{
3076
  AO_t my_spin_max, my_last_spins_half;
3077
  size_t i;
3078

3079
  if (LIKELY(AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_CLEAR))
3080
    return;
3081

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

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

3118
    if (i < SLEEP_THRESHOLD) {
3119
      sched_yield();
3120
    } else {
3121
      struct timespec ts;
3122

3123
      /*
3124
       * Do not wait for more than about 15 ms, even under extreme
3125
       * contention.
3126
       */
3127
      if (i > 24)
3128
        i = 24;
3129

3130
      ts.tv_sec = 0;
3131
      ts.tv_nsec = (unsigned32)1 << i;
3132
      nanosleep(&ts, 0);
3133
    }
3134
  }
3135
}
3136

3137
#  elif defined(USE_PTHREAD_LOCKS)
3138
#    ifdef USE_RWLOCK
3139
GC_INNER pthread_rwlock_t GC_allocate_ml = PTHREAD_RWLOCK_INITIALIZER;
3140
#    else
3141
GC_INNER pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER;
3142
#    endif
3143

3144
#    ifndef NO_PTHREAD_TRYLOCK
3145
GC_INNER void
3146
GC_lock(void)
121,584,148✔
3147
{
3148
  if (1 == GC_nprocs || is_collecting()) {
121,584,148✔
3149
    pthread_mutex_lock(&GC_allocate_ml);
15,315✔
3150
  } else {
3151
    GC_generic_lock(&GC_allocate_ml);
121,568,833✔
3152
  }
3153
}
121,584,148✔
3154
#    elif defined(GC_ASSERTIONS)
3155
GC_INNER void
3156
GC_lock(void)
3157
{
3158
#      ifdef USE_RWLOCK
3159
  (void)pthread_rwlock_wrlock(&GC_allocate_ml); /*< exclusive */
3160
#      else
3161
  pthread_mutex_lock(&GC_allocate_ml);
3162
#      endif
3163
}
3164
#    endif /* NO_PTHREAD_TRYLOCK && GC_ASSERTIONS */
3165

3166
#  endif /* !USE_SPIN_LOCK && USE_PTHREAD_LOCKS */
3167

3168
#  ifdef GC_PTHREADS_PARAMARK
3169

3170
#    if defined(GC_ASSERTIONS) && defined(GC_WIN32_THREADS) \
3171
        && !defined(USE_PTHREAD_LOCKS)
3172
/* Note: result is not guaranteed to be unique. */
3173
#      define NUMERIC_THREAD_ID(id) ((unsigned long)ADDR(PTHREAD_TO_VPTR(id)))
3174
#    endif
3175

3176
#    ifdef GC_ASSERTIONS
3177
#      define SET_MARK_LOCK_HOLDER \
3178
        (void)(GC_mark_lock_holder = NUMERIC_THREAD_ID(pthread_self()))
3179
#      define UNSET_MARK_LOCK_HOLDER                       \
3180
        do {                                               \
3181
          GC_ASSERT(GC_mark_lock_holder                    \
3182
                    == NUMERIC_THREAD_ID(pthread_self())); \
3183
          GC_mark_lock_holder = NO_THREAD;                 \
3184
        } while (0)
3185
#    else
3186
#      define SET_MARK_LOCK_HOLDER (void)0
3187
#      define UNSET_MARK_LOCK_HOLDER (void)0
3188
#    endif /* !GC_ASSERTIONS */
3189

3190
static pthread_cond_t builder_cv = PTHREAD_COND_INITIALIZER;
3191

3192
#    ifndef GC_WIN32_THREADS
3193
static void
3194
setup_mark_lock(void)
39✔
3195
{
3196
#      ifdef GLIBC_2_19_TSX_BUG
3197
  pthread_mutexattr_t mattr;
3198
  int glibc_minor = -1;
39✔
3199
  int glibc_major = GC_parse_version(&glibc_minor, gnu_get_libc_version());
39✔
3200

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

3216
GC_INNER void
3217
GC_acquire_mark_lock(void)
4,185,174✔
3218
{
3219
#    if defined(NUMERIC_THREAD_ID_UNIQUE) && !defined(THREAD_SANITIZER)
3220
  GC_ASSERT(GC_mark_lock_holder != NUMERIC_THREAD_ID(pthread_self()));
4,185,174✔
3221
#    endif
3222
  GC_generic_lock(&mark_mutex);
4,185,174✔
3223
  SET_MARK_LOCK_HOLDER;
4,185,174✔
3224
}
4,185,174✔
3225

3226
GC_INNER void
3227
GC_release_mark_lock(void)
4,185,096✔
3228
{
3229
  UNSET_MARK_LOCK_HOLDER;
4,185,096✔
3230
  if (pthread_mutex_unlock(&mark_mutex) != 0)
4,185,096✔
3231
    ABORT("pthread_mutex_unlock failed");
×
3232
}
4,185,096✔
3233

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

3252
GC_INNER void
3253
GC_wait_for_reclaim(void)
18,883✔
3254
{
3255
  GC_acquire_mark_lock();
18,883✔
3256
  while (GC_fl_builder_count > 0) {
19,941✔
3257
    GC_wait_builder();
1,058✔
3258
  }
3259
  GC_release_mark_lock();
18,883✔
3260
}
18,883✔
3261

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

3279
GC_INNER void
3280
GC_notify_all_builder(void)
1,541,716✔
3281
{
3282
  GC_ASSERT(GC_mark_lock_holder == NUMERIC_THREAD_ID(pthread_self()));
1,541,716✔
3283
  if (pthread_cond_broadcast(&builder_cv) != 0)
1,541,716✔
3284
    ABORT("pthread_cond_broadcast failed");
×
3285
}
1,541,716✔
3286

3287
GC_INNER void
3288
GC_wait_marker(void)
9,638✔
3289
{
3290
  ASSERT_CANCEL_DISABLED();
9,638✔
3291
  GC_ASSERT(GC_parallel);
9,638✔
3292
  UNSET_MARK_LOCK_HOLDER;
9,638✔
3293
  if (pthread_cond_wait(&mark_cv, &mark_mutex) != 0)
9,638✔
3294
    ABORT("pthread_cond_wait failed");
×
3295
  GC_ASSERT(GC_mark_lock_holder == NO_THREAD);
9,560✔
3296
  SET_MARK_LOCK_HOLDER;
9,560✔
3297
}
9,560✔
3298

3299
GC_INNER void
3300
GC_notify_all_marker(void)
10,033✔
3301
{
3302
  GC_ASSERT(GC_parallel);
10,033✔
3303
  if (pthread_cond_broadcast(&mark_cv) != 0)
10,033✔
3304
    ABORT("pthread_cond_broadcast failed");
×
3305
}
10,033✔
3306

3307
#  endif /* GC_PTHREADS_PARAMARK */
3308

3309
GC_INNER GC_on_thread_event_proc GC_on_thread_event = 0;
3310

3311
GC_API void GC_CALL
3312
GC_set_on_thread_event(GC_on_thread_event_proc fn)
3✔
3313
{
3314
  /* Note: `fn` may be 0 (means no event notifier). */
3315
  LOCK();
3✔
3316
  GC_on_thread_event = fn;
3✔
3317
  UNLOCK();
3✔
3318
}
3✔
3319

3320
GC_API GC_on_thread_event_proc GC_CALL
3321
GC_get_on_thread_event(void)
3✔
3322
{
3323
  GC_on_thread_event_proc fn;
3324

3325
  READER_LOCK();
3✔
3326
  fn = GC_on_thread_event;
3✔
3327
  READER_UNLOCK();
3✔
3328
  return fn;
3✔
3329
}
3330

3331
#  ifdef STACKPTR_CORRECTOR_AVAILABLE
3332
GC_INNER GC_sp_corrector_proc GC_sp_corrector = 0;
3333
#  endif
3334

3335
GC_API void GC_CALL
3336
GC_set_sp_corrector(GC_sp_corrector_proc fn)
3✔
3337
{
3338
#  ifdef STACKPTR_CORRECTOR_AVAILABLE
3339
  LOCK();
3✔
3340
  GC_sp_corrector = fn;
3✔
3341
  UNLOCK();
3✔
3342
#  else
3343
  UNUSED_ARG(fn);
3344
#  endif
3345
}
3✔
3346

3347
GC_API GC_sp_corrector_proc GC_CALL
3348
GC_get_sp_corrector(void)
3✔
3349
{
3350
#  ifdef STACKPTR_CORRECTOR_AVAILABLE
3351
  GC_sp_corrector_proc fn;
3352

3353
  READER_LOCK();
3✔
3354
  fn = GC_sp_corrector;
3✔
3355
  READER_UNLOCK();
3✔
3356
  return fn;
3✔
3357
#  else
3358
  return 0; /*< unsupported */
3359
#  endif
3360
}
3361

3362
#  ifdef PTHREAD_REGISTER_CANCEL_WEAK_STUBS
3363
/* Workaround "undefined reference" linkage errors on some targets. */
3364
EXTERN_C_BEGIN
3365
extern void __pthread_register_cancel(void) __attribute__((__weak__));
3366
extern void __pthread_unregister_cancel(void) __attribute__((__weak__));
3367
EXTERN_C_END
3368

3369
void
3370
__pthread_register_cancel(void)
3371
{
3372
}
3373
void
3374
__pthread_unregister_cancel(void)
3375
{
3376
}
3377
#  endif
3378

3379
#  undef do_blocking_enter
3380

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