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

bdwgc / bdwgc / 2066

04 Mar 2026 04:03AM UTC coverage: 77.808% (+0.05%) from 77.762%
2066

push

travis-ci

ivmai
Define and use TEST_ASSERT macro in gctest
(refactoring)

* tests/gctest.c (TEST_ASSERT): New macro.
* tests/gctest.c (cons, collect_from_other_thread, check_ints,
check_uncollectable_ints, tiny_reverse_test, fork_a_thread,
test_generic_malloc_or_special, mktree, chktree, alloc8bytes,
tree_test, typed_test, set_print_procs, run_one_test,
reachable_objs_counter, test_long_mult, check_heap_stats, thr_window,
main): Use `TEST_ASSERT()` instead of `FAIL`.
* tests/gctest.c [THREADS] (collect_from_other_thread, fork_a_thread,
run_one_test, thr_window, main): Call `exit(69)` instead of `FAIL` in
case of out of a resource.
* tests/gctest.c [IRIX5] (main): Define `local_var` local variable;
move `err` local variable definition to the blocks where it is used.

6935 of 8913 relevant lines covered (77.81%)

17692925.14 hits per line

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

89.49
/malloc.c
1
/*
2
 * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
3
 * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
4
 * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P.
5
 * Copyright (c) 2008-2025 Ivan Maidanski
6
 *
7
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
8
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
9
 *
10
 * Permission is hereby granted to use or copy this program
11
 * for any purpose, provided the above notices are retained on all copies.
12
 * Permission to modify the code and to distribute modified code is granted,
13
 * provided the above notices are retained, and a notice that the code was
14
 * modified is included with the above copyright notice.
15
 */
16

17
#include "private/gc_priv.h"
18

19
#include <string.h>
20

21
/* Allocate reclaim list for the kind.  Returns `TRUE` on success. */
22
STATIC GC_bool
23
GC_alloc_reclaim_list(struct obj_kind *ok)
168✔
24
{
25
  struct hblk **result;
26

27
  GC_ASSERT(I_HOLD_LOCK());
168✔
28
  result = (struct hblk **)GC_scratch_alloc((MAXOBJGRANULES + 1)
168✔
29
                                            * sizeof(struct hblk *));
30
  if (UNLIKELY(NULL == result))
168✔
31
    return FALSE;
×
32

33
  BZERO(result, (MAXOBJGRANULES + 1) * sizeof(struct hblk *));
168✔
34
  ok->ok_reclaim_list = result;
168✔
35
  return TRUE;
168✔
36
}
37

38
/*
39
 * Allocate a large block of size `lb_adjusted` bytes with the requested
40
 * alignment (`align_m1 + 1`).  The block is not cleared.  We assume that
41
 * the size is nonzero and a multiple of `GC_GRANULE_BYTES`, and that
42
 * it already includes `EXTRA_BYTES` value.  The `flags` argument should
43
 * be `IGNORE_OFF_PAGE` or 0.  Calls `GC_allochblk()` to do the actual
44
 * allocation, but also triggers collection and/or heap expansion
45
 * as appropriate.  Updates value of `GC_bytes_allocd`; does also other
46
 * accounting.
47
 */
48
STATIC ptr_t
49
GC_alloc_large(size_t lb_adjusted, int kind, unsigned flags, size_t align_m1)
2,530,398✔
50
{
51
  /*
52
   * TODO: It is unclear which retries limit is sufficient (value of 3 leads
53
   * to fail in some 32-bit applications, 10 is a kind of arbitrary value).
54
   */
55
#define MAX_ALLOCLARGE_RETRIES 10
56

57
  int retry_cnt;
58
  size_t n_blocks; /*< includes alignment */
59
  struct hblk *h;
60
  ptr_t result;
61

62
  GC_ASSERT(I_HOLD_LOCK());
2,530,398✔
63
  if (UNLIKELY(!GC_is_initialized)) {
2,530,398✔
64
    UNLOCK(); /*< just to unset `GC_lock_holder` */
×
65
    GC_init();
×
66
    LOCK();
×
67
  }
68
  GC_ASSERT(lb_adjusted != 0 && (lb_adjusted & (GC_GRANULE_BYTES - 1)) == 0);
2,530,398✔
69
  n_blocks = OBJ_SZ_TO_BLOCKS_CHECKED(SIZET_SAT_ADD(lb_adjusted, align_m1));
2,530,398✔
70

71
  /* Do our share of marking work. */
72
  if (GC_incremental && !GC_dont_gc) {
2,530,398✔
73
    GC_collect_a_little_inner(n_blocks);
2,040,774✔
74
  }
75

76
  h = GC_allochblk(lb_adjusted, kind, flags, align_m1);
2,530,398✔
77
#ifdef USE_MUNMAP
78
  if (NULL == h && GC_merge_unmapped()) {
2,530,398✔
79
    h = GC_allochblk(lb_adjusted, kind, flags, align_m1);
4✔
80
  }
81
#endif
82
  for (retry_cnt = 0; NULL == h; retry_cnt++) {
2,533,396✔
83
    /*
84
     * Only a few iterations are expected at most, otherwise something
85
     * is wrong in one of the functions called below.
86
     */
87
    if (retry_cnt > MAX_ALLOCLARGE_RETRIES)
3,018✔
88
      ABORT("Too many retries in GC_alloc_large");
×
89
    if (UNLIKELY(!GC_collect_or_expand(n_blocks, flags, retry_cnt > 0)))
3,018✔
90
      return NULL;
20✔
91
    h = GC_allochblk(lb_adjusted, kind, flags, align_m1);
2,998✔
92
  }
93

94
  GC_bytes_allocd += lb_adjusted;
2,530,378✔
95
  if (lb_adjusted > HBLKSIZE) {
2,530,378✔
96
    GC_large_allocd_bytes += HBLKSIZE * OBJ_SZ_TO_BLOCKS(lb_adjusted);
1,102,775✔
97
    if (GC_large_allocd_bytes > GC_max_large_allocd_bytes)
1,102,775✔
98
      GC_max_large_allocd_bytes = GC_large_allocd_bytes;
6,109✔
99
  }
100
  /* FIXME: Do we need some way to reset `GC_max_large_allocd_bytes`? */
101
  result = h->hb_body;
2,530,378✔
102
  GC_ASSERT((ADDR(result) & align_m1) == 0);
2,530,378✔
103
  return result;
2,530,378✔
104
}
105

106
/*
107
 * Allocate a large block of given size in bytes, clear it if appropriate.
108
 * We assume that the size is nonzero and a multiple of `GC_GRANULE_BYTES`,
109
 * and that it already includes `EXTRA_BYTES` value.  Update value of
110
 * `GC_bytes_allocd`.
111
 */
112
STATIC ptr_t
113
GC_alloc_large_and_clear(size_t lb_adjusted, int kind, unsigned flags)
740✔
114
{
115
  ptr_t result;
116

117
  GC_ASSERT(I_HOLD_LOCK());
740✔
118
  result = GC_alloc_large(lb_adjusted, kind, flags, 0 /* `align_m1` */);
740✔
119
  if (LIKELY(result != NULL)
740✔
120
      && (GC_debugging_started || GC_obj_kinds[kind].ok_init)) {
740✔
121
    /* Clear the whole block, in case of `GC_realloc` call. */
122
    BZERO(result, HBLKSIZE * OBJ_SZ_TO_BLOCKS(lb_adjusted));
740✔
123
  }
124
  return result;
740✔
125
}
126

127
/*
128
 * Fill in additional entries in `GC_size_map`, including the `i`-th one.
129
 * Note that a filled in section of the array ending at `n` always has
130
 * the length of at least `n / 4`.
131
 */
132
STATIC void
133
GC_extend_size_map(size_t i)
168✔
134
{
135
  size_t original_lg = ALLOC_REQUEST_GRANS(i);
168✔
136
  size_t lg;
137
  /*
138
   * The size we try to preserve.  Close to `i`, unless this would
139
   * introduce too many distinct sizes.
140
   */
141
  size_t byte_sz = GRANULES_TO_BYTES(original_lg);
168✔
142
  size_t smaller_than_i = byte_sz - (byte_sz >> 3);
168✔
143
  /* The lowest indexed entry we initialize. */
144
  size_t low_limit;
145
  size_t number_of_objs;
146

147
  GC_ASSERT(I_HOLD_LOCK());
168✔
148
  GC_ASSERT(0 == GC_size_map[i]);
168✔
149
  if (0 == GC_size_map[smaller_than_i]) {
168✔
150
    low_limit = byte_sz - (byte_sz >> 2); /*< much smaller than `i` */
89✔
151
    lg = original_lg;
89✔
152
    while (GC_size_map[low_limit] != 0)
3,103✔
153
      low_limit++;
3,014✔
154
  } else {
155
    low_limit = smaller_than_i + 1;
79✔
156
    while (GC_size_map[low_limit] != 0)
4,526✔
157
      low_limit++;
4,447✔
158

159
    lg = ALLOC_REQUEST_GRANS(low_limit);
79✔
160
    lg += lg >> 3;
79✔
161
    if (lg < original_lg)
79✔
162
      lg = original_lg;
×
163
  }
164

165
  /*
166
   * For these larger sizes, we use an even number of granules.
167
   * This makes it easier to, e.g., construct a 16-byte-aligned
168
   * allocator even if `GC_GRANULE_BYTES` is 8.
169
   */
170
  lg = (lg + 1) & ~(size_t)1;
168✔
171
  if (lg > MAXOBJGRANULES)
168✔
172
    lg = MAXOBJGRANULES;
×
173

174
  /* If we can fit the same number of larger objects in a block, do so. */
175
  GC_ASSERT(lg != 0);
168✔
176
  number_of_objs = HBLK_GRANULES / lg;
168✔
177
  GC_ASSERT(number_of_objs != 0);
168✔
178
  lg = (HBLK_GRANULES / number_of_objs) & ~(size_t)1;
168✔
179

180
  /*
181
   * We may need one extra byte; do not always fill in
182
   * `GC_size_map[byte_sz]`.
183
   */
184
  byte_sz = GRANULES_TO_BYTES(lg) - EXTRA_BYTES;
168✔
185

186
  for (; low_limit <= byte_sz; low_limit++)
48,196✔
187
    GC_size_map[low_limit] = lg;
48,028✔
188
}
168✔
189

190
STATIC void *
191
GC_generic_malloc_inner_small(size_t lb, int kind)
8,831,564✔
192
{
193
  struct obj_kind *ok = &GC_obj_kinds[kind];
8,831,564✔
194
  size_t lg = GC_size_map[lb];
8,831,564✔
195
  void **opp = &ok->ok_freelist[lg];
8,831,564✔
196
  void *op = *opp;
8,831,564✔
197

198
  GC_ASSERT(I_HOLD_LOCK());
8,831,564✔
199
  if (UNLIKELY(NULL == op)) {
8,831,564✔
200
    if (0 == lg) {
1,036,150✔
201
      if (UNLIKELY(!GC_is_initialized)) {
168✔
202
        UNLOCK(); /*< just to unset `GC_lock_holder` */
×
203
        GC_init();
×
204
        LOCK();
×
205
        lg = GC_size_map[lb];
×
206
      }
207
      if (0 == lg) {
168✔
208
        GC_extend_size_map(lb);
168✔
209
        lg = GC_size_map[lb];
168✔
210
        GC_ASSERT(lg != 0);
168✔
211
      }
212
      /* Retry. */
213
      opp = &ok->ok_freelist[lg];
168✔
214
      op = *opp;
168✔
215
    }
216
    if (NULL == op) {
1,036,150✔
217
      if (NULL == ok->ok_reclaim_list && !GC_alloc_reclaim_list(ok))
1,036,149✔
218
        return NULL;
×
219
      op = GC_allocobj(lg, kind);
1,036,149✔
220
      if (NULL == op)
1,036,149✔
221
        return NULL;
×
222
    }
223
  }
224
  *opp = obj_link(op);
8,831,564✔
225
  obj_link(op) = NULL;
8,831,564✔
226
  GC_bytes_allocd += GRANULES_TO_BYTES((word)lg);
8,831,564✔
227
  return op;
8,831,564✔
228
}
229

230
GC_INNER void *
231
GC_generic_malloc_inner(size_t lb, int kind, unsigned flags)
6,094,215✔
232
{
233
  size_t lb_adjusted;
234

235
  GC_ASSERT(I_HOLD_LOCK());
6,094,215✔
236
  GC_ASSERT(kind < MAXOBJKINDS);
6,094,215✔
237
  if (SMALL_OBJ(lb)) {
6,094,215✔
238
    return GC_generic_malloc_inner_small(lb, kind);
6,093,475✔
239
  }
240

241
#if MAX_EXTRA_BYTES > 0
242
  if ((flags & IGNORE_OFF_PAGE) != 0 && lb >= HBLKSIZE) {
740✔
243
    /* No need to add `EXTRA_BYTES`. */
244
    lb_adjusted = lb;
524✔
245
  } else
246
#endif
247
  /* else */ {
248
    lb_adjusted = ADD_EXTRA_BYTES(lb);
216✔
249
  }
250
  return GC_alloc_large_and_clear(ROUNDUP_GRANULE_SIZE(lb_adjusted), kind,
740✔
251
                                  flags);
252
}
253

254
#ifdef GC_COLLECT_AT_MALLOC
255
#  if defined(CPPCHECK)
256
size_t GC_dbg_collect_at_malloc_min_lb = 16 * 1024; /*< some value */
257
#  else
258
size_t GC_dbg_collect_at_malloc_min_lb = (GC_COLLECT_AT_MALLOC);
259
#  endif
260
#endif
261

262
GC_INNER void *
263
GC_generic_malloc_aligned(size_t lb, int kind, unsigned flags, size_t align_m1)
5,234,941✔
264
{
265
  void *result;
266

267
  GC_ASSERT(kind < MAXOBJKINDS);
5,234,941✔
268
  if (UNLIKELY(get_have_errors()))
5,234,941✔
269
    GC_print_all_errors();
119✔
270
  GC_notify_or_invoke_finalizers();
5,234,941✔
271
  GC_DBG_COLLECT_AT_MALLOC(lb);
272
  if (SMALL_OBJ(lb) && LIKELY(align_m1 < GC_GRANULE_BYTES)) {
5,234,941✔
273
    LOCK();
2,705,283✔
274
    result = GC_generic_malloc_inner_small(lb, kind);
2,705,283✔
275
    UNLOCK();
2,705,283✔
276
  } else {
277
#ifdef THREADS
278
    size_t lg;
279
#endif
280
    size_t lb_adjusted;
281
    GC_bool init;
282

283
#if MAX_EXTRA_BYTES > 0
284
    if ((flags & IGNORE_OFF_PAGE) != 0 && lb >= HBLKSIZE) {
2,529,658✔
285
      /* No need to add `EXTRA_BYTES`. */
286
      lb_adjusted = ROUNDUP_GRANULE_SIZE(lb);
630,000✔
287
#  ifdef THREADS
288
      lg = BYTES_TO_GRANULES(lb_adjusted);
630,000✔
289
#  endif
290
    } else
291
#endif
292
    /* else */ {
293
#ifndef THREADS
294
      size_t lg; /*< CPPCHECK */
295
#endif
296

297
      if (UNLIKELY(0 == lb))
1,899,658✔
298
        lb = 1;
×
299
      lg = ALLOC_REQUEST_GRANS(lb);
1,899,658✔
300
      lb_adjusted = GRANULES_TO_BYTES(lg);
1,899,658✔
301
    }
302

303
    init = GC_obj_kinds[kind].ok_init;
2,529,658✔
304
    if (LIKELY(align_m1 < GC_GRANULE_BYTES)) {
2,529,658✔
305
      align_m1 = 0;
2,529,305✔
306
    } else if (align_m1 < HBLKSIZE) {
353✔
307
      align_m1 = HBLKSIZE - 1;
227✔
308
    }
309
    LOCK();
2,529,658✔
310
    result = GC_alloc_large(lb_adjusted, kind, flags, align_m1);
2,529,658✔
311
    if (LIKELY(result != NULL)) {
2,529,658✔
312
      if (GC_debugging_started
2,529,638✔
313
#ifndef THREADS
314
          || init
315
#endif
316
      ) {
317
        BZERO(result, HBLKSIZE * OBJ_SZ_TO_BLOCKS(lb_adjusted));
4✔
318
      } else {
319
#ifdef THREADS
320
        GC_ASSERT(GRANULES_TO_PTRS(lg) >= 2);
2,529,634✔
321
        /*
322
         * Clear any memory that might be used for the GC descriptors
323
         * before we release the allocator lock.
324
         */
325
        ((ptr_t *)result)[0] = NULL;
2,529,634✔
326
        ((ptr_t *)result)[1] = NULL;
2,529,634✔
327
        ((ptr_t *)result)[GRANULES_TO_PTRS(lg) - 1] = NULL;
2,529,634✔
328
        ((ptr_t *)result)[GRANULES_TO_PTRS(lg) - 2] = NULL;
2,529,634✔
329
#endif
330
      }
331
    }
332
    UNLOCK();
2,529,658✔
333
#ifdef THREADS
334
    if (init && !GC_debugging_started && result != NULL) {
2,529,658✔
335
      /* Clear the rest (i.e. excluding the initial 2 words). */
336
      BZERO((ptr_t *)result + 2,
2,433,578✔
337
            HBLKSIZE * OBJ_SZ_TO_BLOCKS(lb_adjusted) - 2 * sizeof(ptr_t));
338
    }
339
#endif
340
  }
341
  if (UNLIKELY(NULL == result)) {
5,234,941✔
342
    result = (*GC_get_oom_fn())(lb);
20✔
343
    /* Note: result might be misaligned. */
344
  }
345
  return result;
5,234,941✔
346
}
347

348
GC_API GC_ATTR_MALLOC void *GC_CALL
349
GC_generic_malloc(size_t lb, int kind)
643,227✔
350
{
351
  return GC_generic_malloc_aligned(lb, kind, 0 /* `flags` */,
643,227✔
352
                                   0 /* `align_m1` */);
353
}
354

355
GC_API GC_ATTR_MALLOC void *GC_CALL
356
GC_malloc_kind_global(size_t lb, int kind)
13,302,860✔
357
{
358
  return GC_malloc_kind_aligned_global(lb, kind, 0 /* `align_m1` */);
13,302,860✔
359
}
360

361
GC_INNER void *
362
GC_malloc_kind_aligned_global(size_t lb, int kind, size_t align_m1)
13,303,679✔
363
{
364
  GC_ASSERT(kind < MAXOBJKINDS);
13,303,679✔
365
  if (SMALL_OBJ(lb) && LIKELY(align_m1 < HBLKSIZE / 2)) {
13,303,679✔
366
    void *op;
367
    void **opp;
368
    size_t lg;
369

370
    GC_DBG_COLLECT_AT_MALLOC(lb);
371
    LOCK();
11,404,059✔
372
    lg = GC_size_map[lb];
11,404,059✔
373
    opp = &GC_obj_kinds[kind].ok_freelist[lg];
11,404,059✔
374
    op = *opp;
11,404,059✔
375
    if (UNLIKELY(align_m1 >= GC_GRANULE_BYTES)) {
11,404,059✔
376
      /* TODO: Avoid linear search. */
377
      for (; (ADDR(op) & align_m1) != 0; op = *opp) {
9,559✔
378
        opp = &obj_link(op);
9,055✔
379
      }
380
    }
381
    if (LIKELY(op != NULL)) {
11,404,059✔
382
      GC_ASSERT(PTRFREE == kind || NULL == obj_link(op)
10,601,967✔
383
                || (ADDR(obj_link(op)) < GC_greatest_real_heap_addr
384
                    && GC_least_real_heap_addr < ADDR(obj_link(op))));
385
      *opp = obj_link(op);
10,601,967✔
386
      if (kind != PTRFREE)
10,601,967✔
387
        obj_link(op) = NULL;
10,358,543✔
388
      GC_bytes_allocd += GRANULES_TO_BYTES((word)lg);
10,601,967✔
389
      UNLOCK();
10,601,967✔
390
      GC_ASSERT((ADDR(op) & align_m1) == 0);
10,601,967✔
391
      return op;
10,601,967✔
392
    }
393
    UNLOCK();
802,092✔
394
  }
395

396
  /*
397
   * We make the `GC_clear_stack()` call a tail one, hoping to get more
398
   * of the stack.
399
   */
400
  return GC_clear_stack(
2,701,712✔
401
      GC_generic_malloc_aligned(lb, kind, 0 /* `flags` */, align_m1));
402
}
403

404
#if defined(THREADS) && !defined(THREAD_LOCAL_ALLOC)
405
GC_API GC_ATTR_MALLOC void *GC_CALL
406
GC_malloc_kind(size_t lb, int kind)
407
{
408
  return GC_malloc_kind_global(lb, kind);
409
}
410
#endif
411

412
GC_API GC_ATTR_MALLOC void *GC_CALL
413
GC_malloc_atomic(size_t lb)
41,130,112✔
414
{
415
  /* Allocate `lb` bytes of atomic (pointer-free) data. */
416
  return GC_malloc_kind(lb, PTRFREE);
41,130,112✔
417
}
418

419
GC_API GC_ATTR_MALLOC void *GC_CALL
420
GC_malloc(size_t lb)
153,772,109✔
421
{
422
  /* Allocate `lb` bytes of composite (pointer-containing) data. */
423
  return GC_malloc_kind(lb, NORMAL);
153,772,109✔
424
}
425

426
GC_API GC_ATTR_MALLOC void *GC_CALL
427
GC_generic_malloc_uncollectable(size_t lb, int kind)
8,668,415✔
428
{
429
  void *op;
430
  size_t lb_orig = lb;
8,668,415✔
431

432
  GC_ASSERT(kind < MAXOBJKINDS);
8,668,415✔
433
  if (EXTRA_BYTES != 0 && LIKELY(lb != 0)) {
8,668,415✔
434
    /*
435
     * We do not need the extra byte, since this will not be collected
436
     * anyway.
437
     */
438
    lb--;
8,668,352✔
439
  }
440

441
  if (SMALL_OBJ(lb)) {
17,336,830✔
442
    void **opp;
443
    size_t lg;
444

445
    if (UNLIKELY(get_have_errors()))
8,668,415✔
446
      GC_print_all_errors();
×
447
    GC_notify_or_invoke_finalizers();
8,668,415✔
448
    GC_DBG_COLLECT_AT_MALLOC(lb_orig);
449
    LOCK();
8,668,415✔
450
    lg = GC_size_map[lb];
8,668,415✔
451
    opp = &GC_obj_kinds[kind].ok_freelist[lg];
8,668,415✔
452
    op = *opp;
8,668,415✔
453
    if (LIKELY(op != NULL)) {
8,668,415✔
454
      *opp = obj_link(op);
8,635,609✔
455
      obj_link(op) = NULL;
8,635,609✔
456
      GC_bytes_allocd += GRANULES_TO_BYTES((word)lg);
8,635,609✔
457
      /*
458
       * Mark bit was already set on free list.  It will be cleared only
459
       * temporarily during a collection, as a result of the normal
460
       * free-list mark bit clearing.
461
       */
462
      GC_non_gc_bytes += GRANULES_TO_BYTES((word)lg);
8,635,609✔
463
    } else {
464
      op = GC_generic_malloc_inner_small(lb, kind);
32,806✔
465
      if (NULL == op) {
32,806✔
466
        GC_oom_func oom_fn = GC_oom_fn;
×
467
        UNLOCK();
×
468
        return (*oom_fn)(lb_orig);
×
469
      }
470
      /* For small objects, the free lists are completely marked. */
471
    }
472
    GC_ASSERT(GC_is_marked(op));
8,668,415✔
473
    UNLOCK();
8,668,415✔
474
  } else {
475
    op = GC_generic_malloc_aligned(lb, kind, 0 /* `flags` */,
×
476
                                   0 /* `align_m1` */);
477
    if (op /* `!= NULL` */) { /*< CPPCHECK */
×
478
      hdr *hhdr;
479

480
      GC_ASSERT(HBLKDISPL(op) == 0); /*< large block */
×
481
      LOCK();
×
482
      hhdr = HDR(op);
×
483
      set_mark_bit_from_hdr(hhdr, 0); /*< the only object */
×
484
#ifndef THREADS
485
      /*
486
       * This is not guaranteed in the multi-threaded case because the
487
       * counter could be updated before locking.
488
       */
489
      GC_ASSERT(0 == hhdr->hb_n_marks);
490
#endif
491
      hhdr->hb_n_marks = 1;
×
492
      UNLOCK();
×
493
    }
494
  }
495
  return op;
8,668,415✔
496
}
497

498
GC_API GC_ATTR_MALLOC void *GC_CALL
499
GC_malloc_uncollectable(size_t lb)
3,025,341✔
500
{
501
  /*
502
   * Allocate `lb` bytes of pointer-containing, traced, but not collectible
503
   * data.
504
   */
505
  return GC_generic_malloc_uncollectable(lb, UNCOLLECTABLE);
3,025,341✔
506
}
507

508
#ifdef GC_ATOMIC_UNCOLLECTABLE
509
GC_API GC_ATTR_MALLOC void *GC_CALL
510
GC_malloc_atomic_uncollectable(size_t lb)
5,642,948✔
511
{
512
  return GC_generic_malloc_uncollectable(lb, AUNCOLLECTABLE);
5,642,948✔
513
}
514
#endif /* GC_ATOMIC_UNCOLLECTABLE */
515

516
#if defined(REDIRECT_MALLOC) && !defined(REDIRECT_MALLOC_IN_HEADER)
517

518
#  ifndef MSWINCE
519
#    include <errno.h>
520
#  endif
521

522
#  ifdef REDIRECT_MALLOC_DEBUG
523
#    include "private/dbg_mlc.h"
524
#    ifndef REDIRECT_MALLOC_UNCOLLECTABLE
525
#      define REDIRECT_MALLOC_F(lb) \
526
        GC_debug_malloc_inner(lb, TRUE /* `is_redirect` */, GC_DBG_EXTRAS)
527
#    else
528
#      define REDIRECT_MALLOC_F(lb) \
529
        GC_debug_malloc_uncollectable_inner(lb, TRUE, GC_DBG_EXTRAS)
530
#    endif
531
#  elif defined(REDIRECT_MALLOC_UNCOLLECTABLE)
532
#    define REDIRECT_MALLOC_F GC_malloc_uncollectable
533
#  else
534
#    define REDIRECT_MALLOC_F GC_malloc
535
#  endif
536

537
void *
538
malloc(size_t lb)
539
{
540
  /*
541
   * It might help to manually inline the `GC_malloc` call here.
542
   * But any decent compiler should reduce the extra procedure call
543
   * to at most a jump instruction in this case.
544
   */
545
  return REDIRECT_MALLOC_F(lb);
546
}
547

548
#  if defined(REDIR_MALLOC_AND_LINUX_THREADS)                    \
549
      && (defined(IGNORE_FREE) || defined(REDIRECT_MALLOC_DEBUG) \
550
          || !defined(REDIRECT_MALLOC_UNCOLLECTABLE))
551
#    ifdef HAVE_LIBPTHREAD_SO
552
STATIC ptr_t GC_libpthread_start = NULL;
553
STATIC ptr_t GC_libpthread_end = NULL;
554
#    endif
555
STATIC ptr_t GC_libld_start = NULL;
556
STATIC ptr_t GC_libld_end = NULL;
557
static GC_bool lib_bounds_set = FALSE;
558

559
GC_INNER void
560
GC_init_lib_bounds(void)
561
{
562
  IF_CANCEL(int cancel_state;)
563

564
  /*
565
   * This test does not need to ensure memory visibility, since the bounds
566
   * will be set when/if we create another thread.
567
   */
568
  if (LIKELY(lib_bounds_set))
569
    return;
570

571
  DISABLE_CANCEL(cancel_state);
572
  GC_init(); /*< if not called yet */
573

574
#    if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED)
575
  LOCK(); /*< just to set `GC_lock_holder` */
576
#    endif
577
#    ifdef HAVE_LIBPTHREAD_SO
578
  if (!GC_text_mapping("libpthread-", &GC_libpthread_start,
579
                       &GC_libpthread_end)) {
580
    WARN("Failed to find libpthread.so text mapping: Expect crash\n", 0);
581
    /*
582
     * This might still work with some versions of `libpthread`,
583
     * so we do not `abort`.
584
     */
585
  }
586
#    endif
587
  if (!GC_text_mapping("ld-", &GC_libld_start, &GC_libld_end)) {
588
    WARN("Failed to find ld.so text mapping: Expect crash\n", 0);
589
  }
590
#    if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED)
591
  UNLOCK();
592
#    endif
593
  RESTORE_CANCEL(cancel_state);
594
  lib_bounds_set = TRUE;
595
}
596
#  endif
597

598
void *
599
calloc(size_t n, size_t lb)
600
{
601
  if (UNLIKELY((lb | n) > GC_SQRT_SIZE_MAX) /*< fast initial test */
602
      && lb && n > GC_SIZE_MAX / lb)
603
    return (*GC_get_oom_fn())(GC_SIZE_MAX); /*< `n * lb` overflow */
604
#  ifdef REDIR_MALLOC_AND_LINUX_THREADS
605
#    if defined(REDIRECT_MALLOC_DEBUG) \
606
        || !defined(REDIRECT_MALLOC_UNCOLLECTABLE)
607
  /*
608
   * The linker may allocate some memory that is only pointed to by
609
   * memory-mapped thread stacks.  Make sure it is not collectible.
610
   */
611
  {
612
    ptr_t caller = (ptr_t)__builtin_return_address(0);
613

614
    GC_init_lib_bounds();
615
    if (ADDR_INSIDE(caller, GC_libld_start, GC_libld_end)
616
#      ifdef HAVE_LIBPTHREAD_SO
617
        /*
618
         * Note: the two ranges are actually usually adjacent, so there
619
         * may be a way to speed this up.
620
         */
621
        || ADDR_INSIDE(caller, GC_libpthread_start, GC_libpthread_end)
622
#      endif
623
    ) {
624
      return GC_generic_malloc_uncollectable(n * lb, UNCOLLECTABLE);
625
    }
626
  }
627
#    elif defined(IGNORE_FREE)
628
  /* Just to ensure `static` variables used by `free()` are initialized. */
629
  GC_init_lib_bounds();
630
#    endif
631
#  endif
632
  return REDIRECT_MALLOC_F(n * lb);
633
}
634

635
#  ifndef strdup
636
char *
637
strdup(const char *s)
638
{
639
  size_t lb = strlen(s) + 1;
640
  char *result = (char *)REDIRECT_MALLOC_F(lb);
641

642
  if (UNLIKELY(NULL == result)) {
643
#    ifndef MSWINCE
644
    errno = ENOMEM;
645
#    endif
646
    return NULL;
647
  }
648
  BCOPY(s, result, lb);
649
  return result;
650
}
651
#  else
652
/*
653
 * If `strdup` is macro defined, we assume that it actually calls `malloc`,
654
 * and thus the right thing will happen even without overriding it.
655
 * This seems to be true on most Linux systems.
656
 */
657
#  endif /* strdup */
658

659
#  ifndef strndup
660
/* This is similar to `strdup()`. */
661
char *
662
strndup(const char *str, size_t size)
663
{
664
  char *copy;
665
  size_t len = strlen(str);
666
  if (UNLIKELY(len > size))
667
    len = size;
668
  copy = (char *)REDIRECT_MALLOC_F(len + 1);
669
  if (UNLIKELY(NULL == copy)) {
670
#    ifndef MSWINCE
671
    errno = ENOMEM;
672
#    endif
673
    return NULL;
674
  }
675
  if (LIKELY(len > 0))
676
    BCOPY(str, copy, len);
677
  copy[len] = '\0';
678
  return copy;
679
}
680
#  endif /* !strndup */
681

682
#  ifdef REDIRECT_MALLOC_DEBUG
683
#    define REDIRECT_FREE_F GC_debug_free
684
#    define REDIRECT_FREEZERO_F GC_debug_freezero
685
#  else
686
#    define REDIRECT_FREE_F GC_free
687
#    define REDIRECT_FREEZERO_F GC_freezero
688
#  endif
689

690
void
691
free(void *p)
692
{
693
#  if defined(REDIR_MALLOC_AND_LINUX_THREADS) \
694
      && !defined(USE_PROC_FOR_LIBRARIES)     \
695
      && (defined(REDIRECT_MALLOC_DEBUG) || defined(IGNORE_FREE))
696
  /*
697
   * Do not bother with initialization checks.  If nothing has been
698
   * initialized, then the check fails, and that is safe, since we have
699
   * not allocated uncollectible objects neither.
700
   */
701
  ptr_t caller = (ptr_t)__builtin_return_address(0);
702

703
  /*
704
   * This test does not need to ensure memory visibility, since the bounds
705
   * will be set when/if we create another thread.
706
   */
707
  if (ADDR_INSIDE(caller, GC_libld_start, GC_libld_end)
708
#    ifdef HAVE_LIBPTHREAD_SO
709
      || ADDR_INSIDE(caller, GC_libpthread_start, GC_libpthread_end)
710
#    endif
711
  ) {
712
    GC_free(p);
713
    return;
714
  }
715
#  endif
716
#  ifdef IGNORE_FREE
717
  UNUSED_ARG(p);
718
#  else
719
  REDIRECT_FREE_F(p);
720
#  endif
721
}
722

723
void
724
freezero(void *p, size_t clear_lb)
725
{
726
  /* We do not expect the caller is in `libdl` or `libpthread`. */
727
#  ifdef IGNORE_FREE
728
  if (UNLIKELY(NULL == p) || UNLIKELY(0 == clear_lb))
729
    return;
730

731
  LOCK();
732
  {
733
    size_t lb;
734
#    ifdef REDIRECT_MALLOC_DEBUG
735
    ptr_t base = (ptr_t)GC_base(p);
736

737
    GC_ASSERT(base != NULL);
738
    lb = HDR(p)->hb_sz - (size_t)((ptr_t)p - base); /*< `sizeof(oh)` */
739
#    else
740
    GC_ASSERT(GC_base(p) == p);
741
    lb = HDR(p)->hb_sz;
742
#    endif
743
    if (LIKELY(clear_lb > lb))
744
      clear_lb = lb;
745
  }
746
  /* Skip deallocation but clear the object. */
747
  UNLOCK();
748
  BZERO(p, clear_lb);
749
#  else
750
  REDIRECT_FREEZERO_F(p, clear_lb);
751
#  endif
752
}
753

754
void
755
freezeroall(void *p)
756
{
757
  freezero(p, GC_SIZE_MAX);
758
}
759

760
#endif /* REDIRECT_MALLOC && !REDIRECT_MALLOC_IN_HEADER */
761

762
GC_INNER void
763
GC_free_internal(void *base, const hdr *hhdr, size_t clear_ofs,
18,256,070✔
764
                 size_t clear_lb)
765
{
766
  size_t lb = hhdr->hb_sz;           /*< size in bytes */
18,256,070✔
767
  size_t lg = BYTES_TO_GRANULES(lb); /*< size in granules */
18,256,070✔
768
  int kind = hhdr->hb_obj_kind;
18,256,070✔
769

770
  GC_ASSERT(I_HOLD_LOCK());
18,256,070✔
771
#ifdef LOG_ALLOCS
772
  GC_log_printf("Free %p after GC #%lu\n", base, (unsigned long)GC_gc_no);
773
#endif
774
  GC_bytes_freed += lb;
18,256,070✔
775
  if (IS_UNCOLLECTABLE(kind))
18,256,070✔
776
    GC_non_gc_bytes -= lb;
8,655,750✔
777

778
  /*
779
   * Ensure the part of object to clear does not overrun the object.
780
   * Note: `SIZET_SAT_ADD(clear_ofs, clear_lb) > lb` cannot be used instead as
781
   * otherwise "memset specified bound exceeds maximum object size" warning
782
   * (a false positive) is reported by gcc-13.
783
   */
784
  if (UNLIKELY(clear_ofs >= GC_SIZE_MAX - clear_lb)
18,256,070✔
785
      || UNLIKELY(clear_ofs + clear_lb > lb))
17,625,731✔
786
    clear_lb = lb > clear_ofs ? lb - clear_ofs : 0;
1,260,780✔
787

788
  if (LIKELY(lg <= MAXOBJGRANULES)) {
18,256,070✔
789
    struct obj_kind *ok = &GC_obj_kinds[kind];
18,255,940✔
790
    void **flh;
791

792
    if (ok->ok_init && LIKELY(lb > sizeof(ptr_t))) {
18,255,940✔
793
      clear_ofs = sizeof(ptr_t);
6,810,724✔
794
      clear_lb = lb - sizeof(ptr_t);
6,810,724✔
795
    }
796
    if (clear_lb > 0)
18,255,940✔
797
      BZERO((ptr_t)base + clear_ofs, clear_lb);
7,440,787✔
798

799
    /*
800
     * It is unnecessary to clear the mark bit.  If the object is reallocated,
801
     * it does not matter.  Otherwise, the collector will do it, since it is
802
     * on a free list.
803
     */
804

805
    flh = &ok->ok_freelist[lg];
18,255,940✔
806
    obj_link(base) = *flh;
18,255,940✔
807
    *flh = (ptr_t)base;
18,255,940✔
808
  } else {
809
    if (clear_lb > 0)
130✔
810
      BZERO((ptr_t)base + clear_ofs, clear_lb);
×
811
    if (lb > HBLKSIZE) {
130✔
812
      GC_large_allocd_bytes -= HBLKSIZE * OBJ_SZ_TO_BLOCKS(lb);
126✔
813
    }
814
    GC_ASSERT(ADDR(HBLKPTR(base)) == ADDR(hhdr->hb_block));
130✔
815
    GC_freehblk(hhdr->hb_block);
130✔
816
  }
817
  FREE_PROFILER_HOOK(base);
18,256,070✔
818
}
18,256,070✔
819

820
GC_API void GC_CALL
821
GC_free(void *p)
16,207,144✔
822
{
823
  const hdr *hhdr;
824

825
  if (p /* `!= NULL` */) {
16,207,144✔
826
    /* CPPCHECK */
827
  } else {
828
    /* Required by ANSI.  It is not my fault... */
829
    return;
29,400✔
830
  }
831

832
  LOCK();
16,177,744✔
833
  hhdr = HDR(p);
16,177,744✔
834
#if defined(REDIRECT_MALLOC)                                           \
835
    && ((defined(NEED_CALLINFO) && defined(GC_HAVE_BUILTIN_BACKTRACE)) \
836
        || defined(REDIR_MALLOC_AND_LINUX_THREADS) || defined(MSWIN32))
837
  /*
838
   * This might be called indirectly by `GC_print_callers` to free the
839
   * result of `backtrace_symbols()`.  For the other cases, this seems
840
   * to happen implicitly.  Do not try to deallocate that memory.
841
   */
842
  if (UNLIKELY(NULL == hhdr)) {
843
    UNLOCK();
844
    return;
845
  }
846
#endif
847
  GC_ASSERT(GC_base(p) == p);
16,177,744✔
848
  GC_free_internal(p, hhdr, 0 /* `clear_ofs` */, 0 /* `clear_lb` */);
16,177,744✔
849
  UNLOCK();
16,177,744✔
850
}
851

852
GC_API void GC_CALL
853
GC_freezero(void *p, size_t clear_lb)
1,891,008✔
854
{
855
  if (UNLIKELY(NULL == p))
1,891,008✔
856
    return;
126✔
857

858
  LOCK();
1,890,882✔
859
  GC_ASSERT(GC_base(p) == p);
1,890,882✔
860
  GC_free_internal(p, HDR(p), 0 /* `clear_ofs` */, clear_lb);
1,890,882✔
861
  UNLOCK();
1,890,882✔
862
}
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