• 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

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

15
#include "private/gc_priv.h"
16

17
#if defined(THREAD_LOCAL_ALLOC)
18

19
#  if !defined(THREADS) && !defined(CPPCHECK)
20
#    error Invalid config - THREAD_LOCAL_ALLOC requires GC_THREADS
21
#  endif
22

23
#  include "private/thread_local_alloc.h"
24

25
#  if defined(USE_COMPILER_TLS)
26
__thread GC_ATTR_TLS_FAST
27
#  elif defined(USE_WIN32_COMPILER_TLS)
28
__declspec(thread) GC_ATTR_TLS_FAST
29
#  endif
30
    GC_key_t GC_thread_key;
31

32
#  if !defined(USE_COMPILER_TLS) && !defined(USE_WIN32_COMPILER_TLS)
33
static GC_bool keys_initialized;
34
#  endif
35

36
#  ifndef GC_NO_DEINIT
37
GC_INNER void
38
GC_reset_thread_local_initialization(void)
×
39
{
40
#    if !defined(USE_COMPILER_TLS) && !defined(USE_WIN32_COMPILER_TLS)
41
  keys_initialized = FALSE;
×
42
#    endif
43
  /* TODO: Dispose resources associated with `GC_thread_key`. */
44
}
×
45
#  endif
46

47
/* Initialize all the free lists of a thread-local storage structure. */
48
static void
49
init_freelists(GC_tlfs p)
95,338✔
50
{
51
  int kind, j;
52

53
  for (j = 0; j < GC_TINY_FREELISTS; ++j) {
2,478,765✔
54
    for (kind = 0; kind < THREAD_FREELISTS_KINDS; ++kind) {
9,533,707✔
55
      p->_freelists[kind][j] = NUMERIC_TO_VPTR(1);
7,150,280✔
56
    }
57
#  ifdef GC_GCJ_SUPPORT
58
    p->gcj_freelists[j] = NUMERIC_TO_VPTR(1);
2,383,427✔
59
#  endif
60
  }
61
  /*
62
   * The zero-sized free list is handled like the regular free list, to
63
   * ensure that the explicit deallocation works.  However, an allocation
64
   * of a `gcj` object with the zero size is always an error.
65
   */
66
#  ifdef GC_GCJ_SUPPORT
67
  p->gcj_freelists[0] = MAKE_CPTR(ERROR_FL);
95,338✔
68
#  endif
69
}
95,338✔
70

71
/*
72
 * Return a single nonempty free list `fl` to the global one pointed to
73
 * by `gfl`.
74
 */
75
static void
76
return_single_freelist(void *fl, void **gfl)
1,722✔
77
{
78
  if (NULL == *gfl) {
1,722✔
79
    *gfl = fl;
1,561✔
80
  } else {
81
    void *q = fl;
161✔
82
    void **q_ptr;
83

84
    GC_ASSERT(GC_size(fl) == GC_size(*gfl));
161✔
85
    /* Concatenate. */
86
    do {
87
      q_ptr = &obj_link(q);
8,621✔
88
      q = *q_ptr;
8,621✔
89
    } while (ADDR(q) >= HBLKSIZE);
8,621✔
90
    GC_ASSERT(NULL == q);
161✔
91
    *q_ptr = *gfl;
161✔
92
    *gfl = fl;
161✔
93
  }
94
}
1,722✔
95

96
/*
97
 * Recover the contents of the free-list array `fl` into the global one
98
 * `gfl`.
99
 */
100
static void
101
return_freelists(void **fl, void **gfl)
380,160✔
102
{
103
  int i;
104

105
  for (i = 1; i < GC_TINY_FREELISTS; ++i) {
9,504,000✔
106
    if (ADDR(fl[i]) >= HBLKSIZE) {
9,123,840✔
107
      return_single_freelist(fl[i], &gfl[i]);
1,722✔
108
    }
109
    /*
110
     * Clear `fl[i]`, since the thread structure may hang around.
111
     * Do it in a way that is likely to trap if we access it.
112
     */
113
    fl[i] = (ptr_t)NUMERIC_TO_VPTR(HBLKSIZE);
9,123,840✔
114
  }
115
  /* The 0 granule free list really contains 1 granule objects. */
116
  if (ADDR(fl[0]) >= HBLKSIZE
380,160✔
117
#  ifdef GC_GCJ_SUPPORT
118
      && ADDR(fl[0]) != ERROR_FL
95,040✔
119
#  endif
120
  ) {
121
    return_single_freelist(fl[0], &gfl[1]);
×
122
  }
123
}
380,160✔
124

125
#  ifdef HAS_WIN32_THREADS_DISCOVERY
126
static void
127
return_freelists_async(void **fl, void **gfl, GC_bool is_async)
128
{
129
  if (is_async) {
130
    int i;
131

132
    /* TODO: For now these objects will be collected during the next GC. */
133
    for (i = 1; i < GC_TINY_FREELISTS; ++i) {
134
      /* Clear `fl[i]`, in a way that is likely to trap if we access it. */
135
      fl[i] = (ptr_t)NUMERIC_TO_VPTR(HBLKSIZE);
136
    }
137
  } else {
138
    return_freelists(fl, gfl);
139
  }
140
}
141
#  else
142
#    define return_freelists_async(fl, gfl, a) return_freelists(fl, gfl)
143
#  endif
144

145
#  ifdef USE_PTHREAD_SPECIFIC
146
/*
147
 * Re-set the TLS value on thread cleanup to allow thread-local allocations
148
 * to happen in the TLS destructors.  `GC_unregister_my_thread()` (and
149
 * similar routines) will finally set the `GC_thread_key` to `NULL`
150
 * preventing this destructor from being called repeatedly.
151
 */
152
static void
153
reset_thread_key(void *v)
154
{
155
  pthread_setspecific(GC_thread_key, v);
156
}
157
#  else
158
#    define reset_thread_key 0
159
#  endif
160

161
GC_INNER void
162
GC_init_thread_local(GC_tlfs p)
95,338✔
163
{
164
#  if !defined(USE_COMPILER_TLS) && !defined(USE_WIN32_COMPILER_TLS)
165
  GC_ASSERT(I_HOLD_LOCK());
95,338✔
166
  if (UNLIKELY(!keys_initialized)) {
95,338✔
167
#    ifdef USE_CUSTOM_SPECIFIC
168
    /* Ensure proper alignment of a "pushed" GC symbol. */
169
    ASSERT_ALIGNMENT(&GC_thread_key);
170
#    endif
171
    if (GC_key_create(&GC_thread_key, reset_thread_key) != 0)
39✔
172
      ABORT("Failed to create key for local allocator");
×
173
    keys_initialized = TRUE;
39✔
174
  }
175
#  endif
176
  init_freelists(p);
95,338✔
177
#  if !defined(USE_COMPILER_TLS) && !defined(USE_WIN32_COMPILER_TLS)
178
  if (GC_setspecific(GC_thread_key, p) != 0)
95,337✔
179
    ABORT("Failed to set thread specific allocation pointers");
×
180
#  else
181
  GC_thread_key = p;
182
#  endif
183
}
95,337✔
184

185
GC_INNER void
186
#  ifdef HAS_WIN32_THREADS_DISCOVERY
187
GC_destroy_thread_local_async(GC_tlfs p, GC_bool is_async)
188
#  else
189
GC_destroy_thread_local(GC_tlfs p)
95,040✔
190
#  endif
191
{
192
  int kind;
193

194
#  ifdef HAS_WIN32_THREADS_DISCOVERY
195
  GC_ASSERT(is_async || I_HOLD_LOCK());
196
#  else
197
  GC_ASSERT(I_HOLD_LOCK());
95,040✔
198
  GC_ASSERT(GC_getspecific(GC_thread_key) == p);
95,040✔
199
  /* We do this from the thread itself. */
200
#  endif
201
  GC_STATIC_ASSERT(THREAD_FREELISTS_KINDS <= MAXOBJKINDS);
202
  for (kind = 0; kind < THREAD_FREELISTS_KINDS; ++kind) {
380,160✔
203
    if (kind == (int)GC_n_kinds) {
285,120✔
204
      /* The kind is not created. */
205
      break;
×
206
    }
207
    return_freelists_async(p->_freelists[kind], GC_obj_kinds[kind].ok_freelist,
285,120✔
208
                           is_async);
209
  }
210
#  ifdef GC_GCJ_SUPPORT
211
  return_freelists_async(p->gcj_freelists, (void **)GC_gcjobjfreelist,
95,040✔
212
                         is_async);
213
#  endif
214
}
95,040✔
215

216
STATIC void *
217
GC_get_tlfs(void)
200,019,737✔
218
{
219
#  if defined(USE_PTHREAD_SPECIFIC) || defined(USE_WIN32_SPECIFIC)
220
  if (UNLIKELY(!keys_initialized))
221
    return NULL;
222

223
  return GC_getspecific(GC_thread_key);
224
#  else
225
  GC_key_t k = GC_thread_key;
200,019,737✔
226

227
  if (UNLIKELY(0 == k)) {
200,019,737✔
228
    /*
229
     * We have not yet run `GC_init_parallel()`.  That means we also
230
     * are not locking, so `GC_malloc_kind_global()` is fairly cheap.
231
     */
232
    return NULL;
×
233
  }
234
  return GC_getspecific(k);
200,019,737✔
235
#  endif
236
}
237

238
GC_API GC_ATTR_MALLOC void *GC_CALL
239
GC_malloc_kind(size_t lb, int kind)
209,292,722✔
240
{
241
  size_t lg;
242
  void *tsd;
243
  void *result;
244

245
#  if MAXOBJKINDS > THREAD_FREELISTS_KINDS
246
  if (UNLIKELY(kind >= THREAD_FREELISTS_KINDS))
209,292,722✔
247
    return GC_malloc_kind_global(lb, kind);
9,272,985✔
248
#  endif
249
  tsd = GC_get_tlfs();
200,019,737✔
250
  if (UNLIKELY(NULL == tsd))
200,019,737✔
251
    return GC_malloc_kind_global(lb, kind);
×
252

253
  GC_ASSERT(GC_is_initialized);
200,019,737✔
254
  GC_ASSERT(GC_is_thread_tsd_valid(tsd));
200,019,737✔
255
  lg = ALLOC_REQUEST_GRANS(lb);
200,019,737✔
256
  GC_FAST_MALLOC_GRANS(
202,741,810✔
257
      result, lg, ((GC_tlfs)tsd)->_freelists[kind], DIRECT_GRANULES, kind,
258
      GC_malloc_kind_global(lb, kind),
259
      (void)(kind == PTRFREE ? NULL : (obj_link(result) = NULL)));
260
#  ifdef LOG_ALLOCS
261
  GC_log_printf("GC_malloc_kind(%lu, %d) returned %p, recent GC #%lu\n",
262
                (unsigned long)lb, kind, result, (unsigned long)GC_gc_no);
263
#  endif
264
  return result;
200,019,737✔
265
}
266

267
#  ifdef GC_GCJ_SUPPORT
268

269
#    include "gc/gc_gcj.h"
270

271
GC_API GC_ATTR_MALLOC void *GC_CALL
272
GC_gcj_malloc(size_t lb, const void *vtable_ptr)
679,744✔
273
{
274
  void *result;
275
  void **tiny_fl;
276
  size_t lg;
277

278
  /*
279
   * Unlike the other thread-local allocation calls, we assume that the
280
   * collector has been explicitly initialized.
281
   */
282
  GC_ASSERT(GC_gcjobjfreelist != NULL);
679,744✔
283
#    if defined(USE_PTHREAD_SPECIFIC) || defined(USE_WIN32_SPECIFIC)
284
  GC_ASSERT(keys_initialized);
285
#    else
286
  GC_ASSERT(GC_thread_key != 0);
679,744✔
287
#    endif
288

289
  /*
290
   * `gcj`-style allocation without locks is extremely tricky.
291
   * The fundamental issue is that we may end up marking a free list,
292
   * which has free-list links instead of "vtable" pointers.
293
   * That is usually OK, since the next object on the free list will be
294
   * cleared, and will thus be interpreted as containing a zero descriptor.
295
   * That is fine if the object has not yet been initialized.  But there
296
   * are interesting potential races.  In the case of incremental
297
   * collection, this seems hopeless, since the marker may run
298
   * asynchronously, and may pick up the pointer to the next free-list
299
   * entry (which it thinks is a "vtable" pointer), get suspended for
300
   * a while, and then see an allocated object instead of the "vtable".
301
   * This may be avoidable with either a handshake with the collector or,
302
   * probably more easily, by moving the free list links to the second
303
   * "pointer-sized" word of each object.  The latter is not a universal
304
   * win, since on architecture like Itanium, nonzero offsets are not
305
   * necessarily free.  And there may be cache fill order issues.
306
   * For now, we punt with the incremental collection.  This probably means
307
   * that the incremental collection should be enabled before we create
308
   * a second thread.
309
   */
310
  if (UNLIKELY(GC_incremental))
679,744✔
311
    return GC_core_gcj_malloc(lb, vtable_ptr, 0 /* `flags` */);
566,964✔
312

313
  tiny_fl = ((GC_tlfs)GC_getspecific(GC_thread_key))->gcj_freelists;
112,780✔
314
  lg = ALLOC_REQUEST_GRANS(lb);
112,780✔
315

316
  /*
317
   * The provided `default_expr` below forces the initialization of the
318
   * "vtable" pointer.  This is necessary to ensure some very subtle
319
   * properties required if a garbage collection is run in the middle of
320
   * such an allocation.  Here we implicitly also assume atomicity for the
321
   * free list and method pointer assignments.  We must update the free list
322
   * before we store the pointer.  Otherwise a collection at this point
323
   * would see a corrupted free list.  A real memory barrier is not needed,
324
   * since the action of stopping this thread will cause prior writes
325
   * to complete.  We assert that any concurrent marker will stop us.
326
   * Thus it is impossible for a mark procedure to see the allocation of the
327
   * next object, but to see this object still containing a free-list pointer.
328
   * Otherwise the marker, by misinterpreting the free-list link as a "vtable"
329
   * pointer, might find a random "mark descriptor" in the next object.
330
   */
331
  GC_FAST_MALLOC_GRANS(
114,457✔
332
      result, lg, tiny_fl, DIRECT_GRANULES, GC_gcj_kind,
333
      GC_core_gcj_malloc(lb, vtable_ptr, 0 /* `flags` */), do {
334
        AO_compiler_barrier();
335
        *(const void **)result = vtable_ptr;
336
      } while (0));
337
  return result;
112,780✔
338
}
339

340
#  endif /* GC_GCJ_SUPPORT */
341

342
GC_INNER void
343
GC_mark_thread_local_fls_for(GC_tlfs p)
253,286✔
344
{
345
  int j;
346

347
  for (j = 0; j < GC_TINY_FREELISTS; ++j) {
6,585,436✔
348
    int kind;
349

350
    for (kind = 0; kind < THREAD_FREELISTS_KINDS; ++kind) {
25,328,600✔
351
      /*
352
       * Load the pointer atomically as it might be updated concurrently
353
       * by `GC_FAST_MALLOC_GRANS()`.
354
       */
355
      ptr_t q = GC_cptr_load((volatile ptr_t *)&p->_freelists[kind][j]);
18,996,450✔
356

357
      if (ADDR(q) > HBLKSIZE)
18,996,450✔
358
        GC_set_fl_marks(q);
1,596,317✔
359
    }
360
#  ifdef GC_GCJ_SUPPORT
361
    if (LIKELY(j > 0)) {
6,332,150✔
362
      ptr_t q = GC_cptr_load((volatile ptr_t *)&p->gcj_freelists[j]);
6,078,864✔
363

364
      if (ADDR(q) > HBLKSIZE)
6,078,864✔
365
        GC_set_fl_marks(q);
5,729✔
366
    }
367
#  endif
368
  }
369
}
253,286✔
370

371
#  if defined(GC_ASSERTIONS)
372
/* Check that all thread-local free-lists in `p` are completely marked. */
373
void
374
GC_check_tls_for(GC_tlfs p)
253,286✔
375
{
376
  int kind, j;
377

378
  for (j = 1; j < GC_TINY_FREELISTS; ++j) {
6,332,150✔
379
    for (kind = 0; kind < THREAD_FREELISTS_KINDS; ++kind) {
24,315,456✔
380
      GC_check_fl_marks(&p->_freelists[kind][j]);
18,236,592✔
381
    }
382
#    ifdef GC_GCJ_SUPPORT
383
    GC_check_fl_marks(&p->gcj_freelists[j]);
6,078,864✔
384
#    endif
385
  }
386
}
253,286✔
387
#  endif
388

389
#endif /* THREAD_LOCAL_ALLOC */
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