• 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

83.4
/mallocx.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) 1996 by Silicon Graphics.  All rights reserved.
5
 * Copyright (c) 2000 by Hewlett-Packard Company.  All rights reserved.
6
 * Copyright (c) 2009-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/gc_priv.h"
19

20
/*
21
 * These are extra allocation routines that are likely to be less
22
 * frequently used than those in `malloc.c` file.  They are separate in
23
 * the hope that the `.o` file will be excluded from statically linked
24
 * executables.  We should probably break this up further.
25
 */
26

27
#include <string.h>
28

29
#ifndef MSWINCE
30
#  include <errno.h>
31
#endif
32

33
/*
34
 * Some externally visible but unadvertised variables to allow access
35
 * to free lists from inlined allocators without include `gc_priv.h` file
36
 * or introducing dependencies on internal data structure layouts.
37
 */
38
#include "private/gc_alloc_ptrs.h"
39
void **const GC_objfreelist_ptr = GC_objfreelist;
40
void **const GC_aobjfreelist_ptr = GC_aobjfreelist;
41
void **const GC_uobjfreelist_ptr = GC_uobjfreelist;
42
#ifdef GC_ATOMIC_UNCOLLECTABLE
43
void **const GC_auobjfreelist_ptr = GC_auobjfreelist;
44
#endif
45

46
GC_API int GC_CALL
47
GC_get_kind_and_size(const void *p, size_t *psize)
1,260,504✔
48
{
49
  const hdr *hhdr = HDR(p);
1,260,504✔
50

51
  if (psize != NULL) {
1,260,504✔
52
    *psize = hhdr->hb_sz;
630,252✔
53
  }
54
  return hhdr->hb_obj_kind;
1,260,504✔
55
}
56

57
GC_API GC_ATTR_MALLOC void *GC_CALL
58
GC_generic_or_special_malloc(size_t lb, int kind)
3,130,504✔
59
{
60
  switch (kind) {
3,130,504✔
61
  case PTRFREE:
3,130,378✔
62
  case NORMAL:
63
    return GC_malloc_kind(lb, kind);
3,130,378✔
64
  case UNCOLLECTABLE:
126✔
65
#ifdef GC_ATOMIC_UNCOLLECTABLE
66
  case AUNCOLLECTABLE:
67
#endif
68
    return GC_generic_malloc_uncollectable(lb, kind);
126✔
69
  default:
×
70
    return GC_generic_malloc_aligned(lb, kind, 0 /* `flags` */, 0);
×
71
  }
72
}
73

74
GC_API void *GC_CALL
75
GC_realloc(void *p, size_t lb)
20,000,378✔
76
{
77
  hdr *hhdr;
78
  void *result;
79
#if defined(_FORTIFY_SOURCE) && defined(__GNUC__) && !defined(__clang__)
80
  /*
81
   * Use `cleared_p` instead of `p` as a workaround to avoid passing
82
   * `alloc_size(lb)` attribute associated with `p` to `memset`
83
   * (including a `memset` call inside `GC_free`).
84
   */
85
  volatile GC_uintptr_t cleared_p = (GC_uintptr_t)p;
86
#else
87
#  define cleared_p p
88
#endif
89
  size_t sz;      /*< current size in bytes */
90
  size_t orig_sz; /*< original `sz` (in bytes) */
91
  int obj_kind;
92

93
  if (NULL == p) {
20,000,378✔
94
    /* Required by ANSI. */
95
    return GC_malloc(lb);
×
96
  }
97
  if (0 == lb) /* `&& p != NULL` */ {
20,000,378✔
98
#ifndef IGNORE_FREE
99
    GC_free(p);
×
100
#endif
101
    return NULL;
×
102
  }
103
  hhdr = HDR(HBLKPTR(p));
20,000,378✔
104
  sz = hhdr->hb_sz;
20,000,378✔
105
  obj_kind = hhdr->hb_obj_kind;
20,000,378✔
106
  orig_sz = sz;
20,000,378✔
107

108
  if (sz > MAXOBJBYTES) {
20,000,378✔
109
    const struct obj_kind *ok = &GC_obj_kinds[obj_kind];
252✔
110
    word descr = ok->ok_descriptor;
252✔
111

112
    /* Round it up to the next whole heap block. */
113
    sz = (sz + HBLKSIZE - 1) & ~(HBLKSIZE - 1);
252✔
114
#if ALIGNMENT > GC_DS_TAGS
115
    /*
116
     * An extra byte is not added in case of ignore-off-page allocated
117
     * objects not smaller than `HBLKSIZE`.
118
     */
119
    GC_ASSERT(sz >= HBLKSIZE);
252✔
120
    if (EXTRA_BYTES != 0 && (hhdr->hb_flags & IGNORE_OFF_PAGE) != 0
252✔
121
        && obj_kind == NORMAL)
×
122
      descr += ALIGNMENT; /*< or set to 0 */
×
123
#endif
124
    if (ok->ok_relocate_descr) {
252✔
125
      descr += sz;
252✔
126
    }
127

128
    /*
129
     * `GC_realloc` might be changing the block size while
130
     * `GC_reclaim_block` or `GC_clear_hdr_marks` is examining it.
131
     * The change to the size field is benign, in that `GC_reclaim`
132
     * (and `GC_clear_hdr_marks`) would work correctly with either
133
     * value, since we are not changing the number of objects in
134
     * the block.  But seeing a half-updated value (though unlikely
135
     * to occur in practice) could be probably bad.
136
     * Using unordered atomic accesses on `hb_sz` and `hb_descr`
137
     * fields would solve the issue.  (The alternate solution might
138
     * be to initially overallocate large objects, so we do not
139
     * have to adjust the size in `GC_realloc`, if they still fit.
140
     * But that is probably more expensive, since we may end up
141
     * scanning a bunch of zeros during the collection.)
142
     */
143
#ifdef AO_HAVE_store
144
    AO_store(&hhdr->hb_sz, sz);
252✔
145
    AO_store((AO_t *)&hhdr->hb_descr, descr);
252✔
146
#else
147
    {
148
      LOCK();
149
      hhdr->hb_sz = sz;
150
      hhdr->hb_descr = descr;
151
      UNLOCK();
152
    }
153
#endif
154

155
#ifdef MARK_BIT_PER_OBJ
156
    GC_ASSERT(hhdr->hb_inv_sz == LARGE_INV_SZ);
157
#else
158
    GC_ASSERT((hhdr->hb_flags & LARGE_BLOCK) != 0
252✔
159
              && hhdr->hb_map[ANY_INDEX] == 1);
160
#endif
161
    if (IS_UNCOLLECTABLE(obj_kind))
252✔
162
      GC_non_gc_bytes += (sz - orig_sz);
×
163
    /* Extra area is already cleared by `GC_alloc_large_and_clear`. */
164
  }
165
  if (ADD_EXTRA_BYTES(lb) <= sz) {
20,000,378✔
166
    if (lb >= (sz >> 1)) {
17,500,126✔
167
      if (orig_sz > lb) {
17,500,126✔
168
        /* Clear unneeded part of object to avoid bogus pointer tracing. */
169
        BZERO((ptr_t)cleared_p + lb, orig_sz - lb);
17,500,000✔
170
      }
171
      return p;
17,500,126✔
172
    }
173
    /*
174
     * Shrink it.  Note: shrinking of large blocks is not implemented
175
     * efficiently.
176
     */
177
    sz = lb;
×
178
  }
179
  result = GC_generic_or_special_malloc((word)lb, obj_kind);
2,500,252✔
180
  if (LIKELY(result != NULL)) {
2,500,252✔
181
    /*
182
     * In case of shrink, it could also return original object.
183
     * But this gives the client warning of imminent disaster.
184
     */
185
    BCOPY(p, result, sz);
2,500,252✔
186
#ifndef IGNORE_FREE
187
    GC_free((ptr_t)cleared_p);
2,500,252✔
188
#endif
189
  }
190
  return result;
2,500,252✔
191
#undef cleared_p
192
}
193

194
GC_API void *GC_CALL
195
GC_reallocf(void *p, size_t lb)
126✔
196
{
197
  void *result = GC_realloc(p, lb);
126✔
198

199
#ifndef IGNORE_FREE
200
  if (UNLIKELY(NULL == result))
126✔
201
    GC_free(p);
×
202
#endif
203
  return result;
126✔
204
}
205

206
#if defined(REDIRECT_MALLOC) && !defined(REDIRECT_MALLOC_IN_HEADER)
207
#  ifdef REDIRECT_MALLOC_DEBUG
208
#    include "private/dbg_mlc.h"
209
#    define REDIRECT_REALLOC_F(p, lb)                                \
210
      GC_debug_realloc_inner(p, lb, FALSE, TRUE /* `is_redirect` */, \
211
                             GC_DBG_EXTRAS)
212
#    define REDIRECT_REALLOCF_F(p, lb)                               \
213
      GC_debug_realloc_inner(p, lb, TRUE /* `free_on_fail` */, TRUE, \
214
                             GC_DBG_EXTRAS)
215
#  else
216
#    define REDIRECT_REALLOC_F GC_realloc
217
#    define REDIRECT_REALLOCF_F GC_reallocf
218
#  endif
219

220
void *
221
realloc(void *p, size_t lb)
222
{
223
  return REDIRECT_REALLOC_F(p, lb);
224
}
225

226
void *
227
reallocf(void *p, size_t lb)
228
{
229
  return REDIRECT_REALLOCF_F(p, lb);
230
}
231
#endif
232

233
GC_API GC_ATTR_MALLOC void *GC_CALL
234
GC_generic_malloc_ignore_off_page(size_t lb, int kind)
630,000✔
235
{
236
  return GC_generic_malloc_aligned(lb, kind, IGNORE_OFF_PAGE,
630,000✔
237
                                   0 /* `align_m1` */);
238
}
239

240
GC_API GC_ATTR_MALLOC void *GC_CALL
241
GC_malloc_ignore_off_page(size_t lb)
630,000✔
242
{
243
  return GC_generic_malloc_aligned(lb, NORMAL, IGNORE_OFF_PAGE, 0);
630,000✔
244
}
245

246
GC_API GC_ATTR_MALLOC void *GC_CALL
247
GC_malloc_atomic_ignore_off_page(size_t lb)
630,002✔
248
{
249
  return GC_generic_malloc_aligned(lb, PTRFREE, IGNORE_OFF_PAGE, 0);
630,002✔
250
}
251

252
/*
253
 * Increment `GC_bytes_allocd` from code that does not have direct access
254
 * to `GC_arrays`.
255
 */
256
void GC_CALL
257
GC_incr_bytes_allocd(size_t n)
63✔
258
{
259
  GC_bytes_allocd += n;
63✔
260
}
63✔
261

262
/* The same as `GC_incr_bytes_allocd` but for `GC_bytes_freed`. */
263
void GC_CALL
264
GC_incr_bytes_freed(size_t n)
63✔
265
{
266
  GC_bytes_freed += n;
63✔
267
}
63✔
268

269
GC_API size_t GC_CALL
270
GC_get_expl_freed_bytes_since_gc(void)
2,858,800✔
271
{
272
  return (size_t)GC_bytes_freed;
2,858,800✔
273
}
274

275
#ifdef PARALLEL_MARK
276
static void
277
acquire_mark_lock_notify_builders(void)
721,053✔
278
{
279
  GC_acquire_mark_lock();
721,053✔
280
  --GC_fl_builder_count;
721,053✔
281
  if (0 == GC_fl_builder_count)
721,053✔
282
    GC_notify_all_builder();
597,572✔
283
  GC_release_mark_lock();
721,053✔
284
}
721,053✔
285
#endif
286

287
GC_API void GC_CALL
288
GC_generic_malloc_many(size_t lb_adjusted, int kind, void **result)
3,369,419✔
289
{
290
  void *op;
291
  void *p;
292
  void **opp;
293
  /* The value of `lb_adjusted` converted to granules. */
294
  size_t lg;
295
  word my_bytes_allocd = 0;
3,369,419✔
296
  struct obj_kind *ok;
297
  struct hblk **rlh;
298

299
  if (UNLIKELY(!GC_is_initialized))
3,369,419✔
300
    GC_init();
×
301
  GC_ASSERT(NONNULL_ARG_NOT_NULL(result));
3,369,419✔
302
  GC_ASSERT(lb_adjusted != 0 && (lb_adjusted & (GC_GRANULE_BYTES - 1)) == 0);
3,369,419✔
303
  /* Currently a single object is always allocated if manual VDB. */
304
  /*
305
   * TODO: `GC_dirty` should be called for each linked object (but the
306
   * last one) to support multiple objects allocation.
307
   */
308
  if (UNLIKELY(lb_adjusted > MAXOBJBYTES) || GC_manual_vdb) {
3,369,419✔
309
    op = GC_generic_malloc_aligned(lb_adjusted - EXTRA_BYTES, kind,
×
310
                                   0 /* `flags` */, 0 /* `align_m1` */);
311
    if (LIKELY(op != NULL))
×
312
      obj_link(op) = NULL;
×
313
    *result = op;
×
314
#ifndef NO_MANUAL_VDB
315
    if (GC_manual_vdb && GC_is_heap_ptr(result)) {
×
316
      GC_dirty_inner(result);
×
317
      REACHABLE_AFTER_DIRTY(op);
×
318
    }
319
#endif
320
    return;
2,256,667✔
321
  }
322

323
  GC_ASSERT(kind < MAXOBJKINDS);
3,369,419✔
324
  lg = BYTES_TO_GRANULES(lb_adjusted);
3,369,419✔
325
  if (UNLIKELY(get_have_errors()))
3,369,419✔
326
    GC_print_all_errors();
40✔
327
  GC_notify_or_invoke_finalizers();
3,369,419✔
328
  GC_DBG_COLLECT_AT_MALLOC(lb_adjusted - EXTRA_BYTES);
329

330
  LOCK();
3,369,419✔
331
  /* Do our share of marking work. */
332
  if (GC_incremental && !GC_dont_gc) {
3,369,419✔
333
    GC_collect_a_little_inner(1);
2,792,604✔
334
  }
335

336
  /* First see if we can reclaim a page of objects waiting to be reclaimed. */
337
  ok = &GC_obj_kinds[kind];
3,369,419✔
338
  rlh = ok->ok_reclaim_list;
3,369,419✔
339
  if (rlh != NULL) {
3,369,419✔
340
    struct hblk *hbp;
341
    hdr *hhdr;
342

343
    while ((hbp = rlh[lg]) != NULL) {
3,369,419✔
344
      hhdr = HDR(hbp);
1,535,614✔
345
      rlh[lg] = hhdr->hb_next;
1,535,614✔
346
      GC_ASSERT(hhdr->hb_sz == lb_adjusted);
1,535,614✔
347
      hhdr->hb_last_reclaimed = (unsigned short)GC_gc_no;
1,535,614✔
348
#ifdef PARALLEL_MARK
349
      if (GC_parallel) {
1,535,614✔
350
        GC_signed_word my_bytes_allocd_tmp
1,358,711✔
351
            = (GC_signed_word)AO_load(&GC_bytes_allocd_tmp);
1,358,711✔
352
        GC_ASSERT(my_bytes_allocd_tmp >= 0);
1,358,711✔
353
        /*
354
         * We only decrement it while holding the allocator lock.
355
         * Thus, we cannot accidentally adjust it down in more than
356
         * one thread simultaneously.
357
         */
358
        if (my_bytes_allocd_tmp != 0) {
1,358,711✔
359
          (void)AO_fetch_and_add(&GC_bytes_allocd_tmp,
1,218,334✔
360
                                 (AO_t)(-my_bytes_allocd_tmp));
361
          GC_bytes_allocd += (word)my_bytes_allocd_tmp;
1,218,334✔
362
        }
363
        GC_acquire_mark_lock();
1,358,711✔
364
        ++GC_fl_builder_count;
1,358,711✔
365
        UNLOCK();
1,358,711✔
366
        GC_release_mark_lock();
1,358,711✔
367

368
        op = GC_reclaim_generic(hbp, hhdr, lb_adjusted, ok->ok_init, NULL,
1,358,711✔
369
                                &my_bytes_allocd);
370
        if (op != NULL) {
1,358,711✔
371
          *result = op;
1,358,711✔
372
          (void)AO_fetch_and_add(&GC_bytes_allocd_tmp, (AO_t)my_bytes_allocd);
1,358,711✔
373
          GC_acquire_mark_lock();
1,358,711✔
374
          --GC_fl_builder_count;
1,358,711✔
375
          if (0 == GC_fl_builder_count)
1,358,711✔
376
            GC_notify_all_builder();
945,899✔
377
#  ifdef THREAD_SANITIZER
378
          GC_release_mark_lock();
379
          LOCK();
380
          GC_bytes_found += (GC_signed_word)my_bytes_allocd;
381
          UNLOCK();
382
#  else
383
          /* The resulting `GC_bytes_found` may be inaccurate. */
384
          GC_bytes_found += (GC_signed_word)my_bytes_allocd;
1,358,711✔
385
          GC_release_mark_lock();
1,358,711✔
386
#  endif
387
          (void)GC_clear_stack(NULL);
1,358,711✔
388
          return;
1,358,711✔
389
        }
390

391
        acquire_mark_lock_notify_builders();
×
392

393
        /*
394
         * The allocator lock is needed for access to the reclaim list.
395
         * We must decrement `GC_fl_builder_count` before reacquiring
396
         * the allocator lock.  Hopefully this path is rare.
397
         */
398
        LOCK();
×
399
        rlh = ok->ok_reclaim_list; /*< reload `rlh` after locking */
×
400
        if (UNLIKELY(NULL == rlh))
×
401
          break;
×
402
        continue;
×
403
      }
404
#endif
405

406
      op = GC_reclaim_generic(hbp, hhdr, lb_adjusted, ok->ok_init, NULL,
176,903✔
407
                              &my_bytes_allocd);
408
      if (op != NULL) {
176,903✔
409
        /* We also reclaimed memory, so we need to adjust that count. */
410
        GC_bytes_found += (GC_signed_word)my_bytes_allocd;
176,903✔
411
        GC_bytes_allocd += my_bytes_allocd;
176,903✔
412
        *result = op;
176,903✔
413
        UNLOCK();
176,903✔
414
        (void)GC_clear_stack(NULL);
176,903✔
415
        return;
176,903✔
416
      }
417
    }
418
  }
419

420
  /*
421
   * Next try to use prefix of global free list if there is one.
422
   * We do not refill it, but we need to use it up before allocating
423
   * a new block ourselves.
424
   */
425
  opp = &ok->ok_freelist[lg];
1,833,805✔
426
  op = *opp;
1,833,805✔
427
  if (op != NULL) {
1,833,805✔
428
    *opp = NULL;
541,608✔
429
    my_bytes_allocd = 0;
541,608✔
430
    for (p = op; p != NULL; p = obj_link(p)) {
17,814,448✔
431
      my_bytes_allocd += lb_adjusted;
17,289,462✔
432
      if ((word)my_bytes_allocd >= HBLKSIZE) {
17,289,462✔
433
        *opp = obj_link(p);
16,622✔
434
        obj_link(p) = NULL;
16,622✔
435
        break;
16,622✔
436
      }
437
    }
438
    GC_bytes_allocd += my_bytes_allocd;
541,608✔
439

440
  } else {
441
    /* Next try to allocate a new block worth of objects of this size. */
442
    struct hblk *h
443
        = GC_allochblk(lb_adjusted, kind, 0 /* `flags` */, 0 /* `align_m1` */);
1,292,197✔
444

445
    if (h /* `!= NULL` */) { /*< CPPCHECK */
1,292,197✔
446
      if (IS_UNCOLLECTABLE(kind))
1,287,462✔
447
        GC_set_hdr_marks(HDR(h));
×
448
      GC_bytes_allocd += HBLKSIZE - (HBLKSIZE % lb_adjusted);
1,287,462✔
449
#ifdef PARALLEL_MARK
450
      if (GC_parallel) {
1,287,462✔
451
        GC_acquire_mark_lock();
721,053✔
452
        ++GC_fl_builder_count;
721,053✔
453
        UNLOCK();
721,053✔
454
        GC_release_mark_lock();
721,053✔
455

456
        op = GC_build_fl(h, NULL, lg, ok->ok_init || GC_debugging_started);
721,053✔
457
        *result = op;
721,053✔
458

459
        acquire_mark_lock_notify_builders();
721,053✔
460
        (void)GC_clear_stack(NULL);
721,053✔
461
        return;
721,053✔
462
      }
463
#endif
464

465
      op = GC_build_fl(h, NULL, lg, ok->ok_init || GC_debugging_started);
566,409✔
466
    } else {
467
      /*
468
       * As a last attempt, try allocating a single object.
469
       * Note that this may trigger a collection or expand the heap.
470
       */
471
      op = GC_generic_malloc_inner(lb_adjusted - EXTRA_BYTES, kind,
4,735✔
472
                                   0 /* `flags` */);
473
      if (op != NULL)
4,735✔
474
        obj_link(op) = NULL;
4,735✔
475
    }
476
  }
477

478
  *result = op;
1,112,752✔
479
  UNLOCK();
1,112,752✔
480
  (void)GC_clear_stack(NULL);
1,112,752✔
481
}
482

483
GC_API GC_ATTR_MALLOC void *GC_CALL
484
GC_malloc_many(size_t lb)
635,234✔
485
{
486
  void *result;
487
  size_t lg, lb_adjusted;
488

489
  if (UNLIKELY(0 == lb))
635,234✔
490
    lb = 1;
×
491
  lg = ALLOC_REQUEST_GRANS(lb);
635,234✔
492
  lb_adjusted = GRANULES_TO_BYTES(lg);
635,234✔
493
  GC_generic_malloc_many(lb_adjusted, NORMAL, &result);
635,234✔
494
  return result;
635,234✔
495
}
496

497
/*
498
 * TODO: The debugging variant of `GC_memalign` and friends is tricky
499
 * and currently missing.  The major difficulty is: `store_debug_info`
500
 * should return the pointer of the object with the requested alignment
501
 * (unlike the object header).
502
 */
503

504
GC_API GC_ATTR_MALLOC void *GC_CALL
505
GC_memalign(size_t align, size_t lb)
949✔
506
{
507
  size_t align_m1 = align - 1;
949✔
508

509
  /* Check the alignment argument. */
510
  if (UNLIKELY(0 == align || (align & align_m1) != 0))
949✔
511
    return NULL;
×
512

513
  /* TODO: Use thread-local allocation. */
514
  if (align <= GC_GRANULE_BYTES)
949✔
515
    return GC_malloc(lb);
130✔
516
  return GC_malloc_kind_aligned_global(lb, NORMAL, align_m1);
819✔
517
}
518

519
GC_API int GC_CALL
520
GC_posix_memalign(void **memptr, size_t align, size_t lb)
63✔
521
{
522
  void *p;
523
  size_t align_minus_one = align - 1; /*< to workaround a cppcheck warning */
63✔
524

525
  /* Check alignment properly. */
526
  if (UNLIKELY(align < sizeof(void *) || (align_minus_one & align) != 0)) {
63✔
527
#ifdef MSWINCE
528
    return ERROR_INVALID_PARAMETER;
529
#else
530
    return EINVAL;
×
531
#endif
532
  }
533

534
  p = GC_memalign(align, lb);
63✔
535
  if (UNLIKELY(NULL == p)) {
63✔
536
#ifdef MSWINCE
537
    return ERROR_NOT_ENOUGH_MEMORY;
538
#else
539
    return ENOMEM;
×
540
#endif
541
  }
542
  *memptr = p;
63✔
543
  return 0; /*< success */
63✔
544
}
545

546
#ifndef GC_NO_VALLOC
547
GC_API GC_ATTR_MALLOC void *GC_CALL
548
GC_valloc(size_t lb)
63✔
549
{
550
  if (UNLIKELY(!GC_is_initialized))
63✔
551
    GC_init();
×
552
  GC_ASSERT(GC_real_page_size != 0);
63✔
553
  return GC_memalign(GC_real_page_size, lb);
63✔
554
}
555

556
GC_API GC_ATTR_MALLOC void *GC_CALL
557
GC_pvalloc(size_t lb)
63✔
558
{
559
  if (UNLIKELY(!GC_is_initialized))
63✔
560
    GC_init();
×
561
  GC_ASSERT(GC_real_page_size != 0);
63✔
562
  lb = SIZET_SAT_ADD(lb, GC_real_page_size - 1) & ~(GC_real_page_size - 1);
63✔
563
  return GC_memalign(GC_real_page_size, lb);
63✔
564
}
565
#endif /* !GC_NO_VALLOC */
566

567
GC_API GC_ATTR_MALLOC char *GC_CALL
568
GC_strdup(const char *s)
44,412✔
569
{
570
  /*
571
   * Implementation of a variant of `strdup()` that uses the collector
572
   * to allocate a copy of the string.
573
   */
574
  char *copy;
575
  size_t lb;
576
  if (s == NULL)
44,412✔
577
    return NULL;
×
578
  lb = strlen(s) + 1;
44,412✔
579
  copy = (char *)GC_malloc_atomic(lb);
44,412✔
580
  if (UNLIKELY(NULL == copy)) {
44,412✔
581
#ifndef MSWINCE
582
    errno = ENOMEM;
×
583
#endif
584
    return NULL;
×
585
  }
586
  BCOPY(s, copy, lb);
44,412✔
587
  return copy;
44,412✔
588
}
589

590
GC_API GC_ATTR_MALLOC char *GC_CALL
591
GC_strndup(const char *str, size_t size)
63✔
592
{
593
  char *copy;
594
  /* Note: `str` is expected to be non-`NULL`. */
595
  size_t len = strlen(str);
63✔
596
  if (UNLIKELY(len > size))
63✔
597
    len = size;
63✔
598
  copy = (char *)GC_malloc_atomic(len + 1);
63✔
599
  if (UNLIKELY(NULL == copy)) {
63✔
600
#ifndef MSWINCE
601
    errno = ENOMEM;
×
602
#endif
603
    return NULL;
×
604
  }
605
  if (LIKELY(len > 0))
63✔
606
    BCOPY(str, copy, len);
63✔
607
  copy[len] = '\0';
63✔
608
  return copy;
63✔
609
}
610

611
#ifdef GC_REQUIRE_WCSDUP
612
#  include <wchar.h> /*< for `wcslen()` */
613

614
GC_API GC_ATTR_MALLOC wchar_t *GC_CALL
615
GC_wcsdup(const wchar_t *str)
63✔
616
{
617
  size_t lb = (wcslen(str) + 1) * sizeof(wchar_t);
63✔
618
  wchar_t *copy = (wchar_t *)GC_malloc_atomic(lb);
63✔
619

620
  if (UNLIKELY(NULL == copy)) {
63✔
621
#  ifndef MSWINCE
622
    errno = ENOMEM;
×
623
#  endif
624
    return NULL;
×
625
  }
626
  BCOPY(str, copy, lb);
63✔
627
  return copy;
63✔
628
}
629

630
#  if !defined(wcsdup) && defined(REDIRECT_MALLOC) \
631
      && !defined(REDIRECT_MALLOC_IN_HEADER)
632
wchar_t *
633
wcsdup(const wchar_t *str)
634
{
635
  return GC_wcsdup(str);
636
}
637
#  endif
638
#endif /* GC_REQUIRE_WCSDUP */
639

640
#ifndef CPPCHECK
641
GC_API void *GC_CALL
642
GC_malloc_stubborn(size_t lb)
×
643
{
644
  return GC_malloc(lb);
×
645
}
646

647
GC_API void GC_CALL
648
GC_change_stubborn(const void *p)
×
649
{
650
  UNUSED_ARG(p);
651
}
×
652
#endif /* !CPPCHECK */
653

654
GC_API void GC_CALL
655
GC_end_stubborn_change(const void *p)
205,028,803✔
656
{
657
  GC_dirty(p); /*< entire object */
205,028,803✔
658
}
205,028,803✔
659

660
GC_API void GC_CALL
661
GC_ptr_store_and_dirty(void *p, const void *q)
194,944,054✔
662
{
663
  *(const void **)p = q;
194,944,054✔
664
  GC_dirty(p);
194,944,054✔
665
  REACHABLE_AFTER_DIRTY(q);
194,944,054✔
666
}
194,944,054✔
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