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

bdwgc / bdwgc / 2146

25 May 2026 07:08PM UTC coverage: 80.526% (-0.02%) from 80.544%
2146

push

travis-ci

ivmai
Update AUTHORS file (add Kyle Cesare)

7224 of 8971 relevant lines covered (80.53%)

18671099.85 hits per line

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

86.44
/alloc.c
1
/*
2
 * Copyright (c) 1988-1989 Hans-J. Boehm, Alan J. Demers
3
 * Copyright (c) 1991-1996 by Xerox Corporation.  All rights reserved.
4
 * Copyright (c) 1996-1999 by Silicon Graphics.  All rights reserved.
5
 * Copyright (c) 1999-2011 Hewlett-Packard Development Company, L.P.
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/gc_priv.h"
19

20
/*
21
 * Separate free lists are maintained for different sized objects up
22
 * to `MAXOBJBYTES`.
23
 * The call `GC_allocobj(lg, kind)` ensures that the free list for the given
24
 * kind of objects of the given size in granules is a nonempty one.
25
 * It returns a pointer to the first entry on the free list.
26
 * In a single-threaded world, `GC_allocobj` may be called to allocate
27
 * an object of small size `lb` (and `NORMAL` kind) as follows
28
 * (`GC_generic_malloc_inner` is a wrapper over `GC_allocobj` which also
29
 * fills in `GC_size_map` if needed):
30
 *
31
 * ```
32
 *   lg = GC_size_map[lb];
33
 *   op = GC_objfreelist[lg];
34
 *   if (NULL == op) {
35
 *     op = GC_generic_malloc_inner(lb, NORMAL, 0);
36
 *   } else {
37
 *     GC_objfreelist[lg] = obj_link(op);
38
 *     GC_bytes_allocd += GRANULES_TO_BYTES((word)lg);
39
 *   }
40
 * ```
41
 *
42
 * Note that this is very fast if the free list is not empty; it should
43
 * only involve the execution of 4 or 5 simple instructions.
44
 * All composite objects on freelists are cleared, except for
45
 * their first "pointer-sized" word.
46
 */
47

48
/*
49
 * The allocator uses `GC_allochblk` to allocate large chunks of objects.
50
 * These chunks all start on addresses that are multiples of `HBLKSIZE`.
51
 * Each allocated chunk has an associated header, which can be located
52
 * quickly based on the address of the chunk.  This makes it possible
53
 * to check quickly whether an arbitrary address corresponds to an object
54
 * administered by the allocator.  (See `headers.c` file for details.)
55
 */
56

57
/* Number of bytes not intended to be collected. */
58
word GC_non_gc_bytes = 0;
59

60
word GC_gc_no = 0;
61

62
#ifndef NO_CLOCK
63

64
GC_API void GC_CALL
65
GC_start_performance_measurement(void)
3✔
66
{
67
  GC_measure_performance = TRUE;
3✔
68
}
3✔
69

70
GC_API unsigned long GC_CALL
71
GC_get_full_gc_total_time(void)
3✔
72
{
73
  return GC_full_gc_total_time;
3✔
74
}
75

76
GC_API unsigned long GC_CALL
77
GC_get_full_gc_total_ns_frac(void)
3✔
78
{
79
  /* Note: the result type matches the type of `GC_timeval_s.tv_nsec`. */
80
  return GC_full_gc_total_ns_frac;
3✔
81
}
82

83
GC_API unsigned long GC_CALL
84
GC_get_stopped_mark_total_time(void)
3✔
85
{
86
  return GC_stopped_mark_total_time;
3✔
87
}
88

89
#  ifndef MAX_TOTAL_TIME_DIVISOR
90
/*
91
 * We shall not use big values here (so "outdated" delay time values would
92
 * have less impact on "average" delay time value than newer ones).
93
 */
94
#    define MAX_TOTAL_TIME_DIVISOR 1000
95
#  endif
96

97
GC_API unsigned long GC_CALL
98
GC_get_avg_stopped_mark_time_ns(void)
3✔
99
{
100
  unsigned long total_time;
101
  unsigned divisor;
102

103
  READER_LOCK();
3✔
104
  total_time = (unsigned long)GC_world_stopped_total_time;
3✔
105
  divisor = GC_world_stopped_total_divisor;
3✔
106
  READER_UNLOCK();
3✔
107
  if (0 == divisor) {
3✔
108
    GC_ASSERT(0 == total_time);
×
109
    /*
110
     * No world-stopped collection has occurred since the start of
111
     * performance measurements.
112
     */
113
    return 0;
×
114
  }
115

116
  /* Halve values to prevent overflow during the multiplication. */
117
  for (; total_time > ~0UL / (1000UL * 1000); total_time >>= 1) {
3✔
118
    divisor >>= 1;
×
119
    if (UNLIKELY(0 == divisor)) {
×
120
      /* The actual result is larger than representable value. */
121
      return ~0UL;
×
122
    }
123
  }
124

125
  return total_time * (1000UL * 1000) / divisor;
3✔
126
}
127

128
#endif /* !NO_CLOCK */
129

130
GC_API int GC_CALL
131
GC_is_incremental_mode(void)
17✔
132
{
133
  return (int)GC_incremental;
17✔
134
}
135

136
#ifdef THREADS
137
int GC_parallel = FALSE; /*< parallel collection is off by default */
138
#endif
139

140
#ifdef GC_FULL_FREQ
141
int GC_full_freq = GC_FULL_FREQ;
142
#else
143
/*
144
 * Every `GC_full_freq + 1` collection is a full collection, whether we
145
 * need it or not.
146
 */
147
int GC_full_freq = 19;
148
#endif
149

150
#ifdef THREAD_LOCAL_ALLOC
151
GC_INNER GC_bool GC_world_stopped = FALSE;
152
#endif
153

154
STATIC GC_bool GC_disable_automatic_collection = FALSE;
155

156
GC_API void GC_CALL
157
GC_set_disable_automatic_collection(int value)
3✔
158
{
159
  LOCK();
3✔
160
  GC_disable_automatic_collection = value != 0;
3✔
161
  UNLOCK();
3✔
162
}
3✔
163

164
GC_API int GC_CALL
165
GC_get_disable_automatic_collection(void)
3✔
166
{
167
  int value;
168

169
  READER_LOCK();
3✔
170
  value = (int)GC_disable_automatic_collection;
3✔
171
  READER_UNLOCK();
3✔
172
  return value;
3✔
173
}
174

175
/*
176
 * The version macros are now defined in `gc_version.h` file, which is
177
 * included by `gc.h` file, which, in turn, is included by `gc_priv.h` file.
178
 */
179
#ifndef GC_NO_VERSION_VAR
180
EXTERN_C_BEGIN
181
extern const GC_VERSION_VAL_T GC_version;
182
EXTERN_C_END
183

184
const GC_VERSION_VAL_T GC_version = ((GC_VERSION_VAL_T)GC_VERSION_MAJOR << 16)
185
                                    | (GC_VERSION_MINOR << 8)
186
                                    | GC_VERSION_MICRO;
187
#endif
188

189
GC_API GC_VERSION_VAL_T GC_CALL
190
GC_get_version(void)
3✔
191
{
192
  return ((GC_VERSION_VAL_T)GC_VERSION_MAJOR << 16) | (GC_VERSION_MINOR << 8)
3✔
193
         | GC_VERSION_MICRO;
194
}
195

196
GC_API int GC_CALL
197
GC_get_dont_add_byte_at_end(void)
63✔
198
{
199
#if MAX_EXTRA_BYTES > 0
200
  /* This is meaningful only if `GC_all_interior_pointers`. */
201
  return 0;
63✔
202
#else
203
  return 1;
204
#endif
205
}
206

207
/* Some more variables. */
208

209
#ifdef GC_DONT_EXPAND
210
int GC_dont_expand = TRUE;
211
#else
212
int GC_dont_expand = FALSE;
213
#endif
214

215
#ifdef GC_FREE_SPACE_DIVISOR
216
word GC_free_space_divisor = GC_FREE_SPACE_DIVISOR; /*< must be positive */
217
#else
218
word GC_free_space_divisor = 3;
219
#endif
220

221
GC_INNER int GC_CALLBACK
222
GC_never_stop_func(void)
28,907,890✔
223
{
224
  return FALSE;
28,907,890✔
225
}
226

227
#ifdef GC_TIME_LIMIT
228
/*
229
 * We try to keep pause times from exceeding this by much.
230
 * In milliseconds.
231
 */
232
unsigned long GC_time_limit = GC_TIME_LIMIT;
233
#elif defined(PARALLEL_MARK)
234
/*
235
 * The parallel marker cannot be interrupted for now, so the time limit
236
 * is absent by default.
237
 */
238
unsigned long GC_time_limit = GC_TIME_UNLIMITED;
239
#else
240
unsigned long GC_time_limit = 15;
241
#endif
242

243
#ifndef NO_CLOCK
244
/*
245
 * The nanoseconds add-on to `GC_time_limit` value.
246
 * Not updated by `GC_set_time_limit()`.
247
 * Ignored if the value of `GC_time_limit` is `GC_TIME_UNLIMITED`.
248
 */
249
STATIC unsigned long GC_time_lim_nsec = 0;
250

251
#  define TV_NSEC_LIMIT (1000UL * 1000) /*< amount of nanoseconds in 1 ms */
252

253
GC_API void GC_CALL
254
GC_set_time_limit_tv(struct GC_timeval_s tv)
3✔
255
{
256
  GC_ASSERT(tv.tv_ms <= GC_TIME_UNLIMITED);
3✔
257
  GC_ASSERT(tv.tv_nsec < TV_NSEC_LIMIT);
3✔
258
  GC_time_limit = tv.tv_ms;
3✔
259
  GC_time_lim_nsec = tv.tv_nsec;
3✔
260
}
3✔
261

262
GC_API struct GC_timeval_s GC_CALL
263
GC_get_time_limit_tv(void)
3✔
264
{
265
  struct GC_timeval_s tv;
266

267
  tv.tv_ms = GC_time_limit;
3✔
268
  tv.tv_nsec = GC_time_lim_nsec;
3✔
269
  return tv;
3✔
270
}
271
#endif /* !NO_CLOCK */
272

273
/* Note: accessed holding the allocator lock. */
274
STATIC GC_stop_func GC_default_stop_func = GC_never_stop_func;
275

276
GC_API void GC_CALL
277
GC_set_stop_func(GC_stop_func stop_func)
3✔
278
{
279
  GC_ASSERT(NONNULL_ARG_NOT_NULL(stop_func));
3✔
280
  LOCK();
3✔
281
  GC_default_stop_func = stop_func;
3✔
282
  UNLOCK();
3✔
283
}
3✔
284

285
GC_API GC_stop_func GC_CALL
286
GC_get_stop_func(void)
3✔
287
{
288
  GC_stop_func stop_func;
289

290
  READER_LOCK();
3✔
291
  stop_func = GC_default_stop_func;
3✔
292
  READER_UNLOCK();
3✔
293
  return stop_func;
3✔
294
}
295

296
#if defined(GC_DISABLE_INCREMENTAL) || defined(NO_CLOCK)
297
#  define GC_timeout_stop_func GC_default_stop_func
298
#else
299
STATIC int GC_CALLBACK
300
GC_timeout_stop_func(void)
9,819,761✔
301
{
302
  CLOCK_TYPE current_time;
303
  static unsigned count = 0;
304
  unsigned long time_diff, nsec_diff;
305

306
  GC_ASSERT(I_HOLD_LOCK());
9,819,761✔
307
  if (GC_default_stop_func())
9,819,761✔
308
    return TRUE;
9,819,761✔
309

310
  if (GC_time_limit == GC_TIME_UNLIMITED || (count++ & 3) != 0)
9,819,670✔
311
    return FALSE;
9,819,670✔
312

313
  GET_TIME(current_time);
×
314
  time_diff = MS_TIME_DIFF(current_time, GC_start_time);
×
315
  nsec_diff = NS_FRAC_TIME_DIFF(current_time, GC_start_time);
×
316
#  if defined(CPPCHECK)
317
  /* Note: GCC reports a warning if `GC_noop1_ptr()` is used here. */
318
  GC_noop1(ADDR(&nsec_diff));
319
#  endif
320
  if (time_diff >= GC_time_limit
×
321
      && (time_diff > GC_time_limit || nsec_diff >= GC_time_lim_nsec)) {
×
322
    GC_COND_LOG_PRINTF(
×
323
        "Abandoning stopped marking after %lu ms %lu ns (attempt %d)\n",
324
        time_diff, nsec_diff, GC_n_attempts);
325
    return TRUE;
×
326
  }
327

328
  return FALSE;
×
329
}
330
#endif /* !GC_DISABLE_INCREMENTAL */
331

332
#ifdef THREADS
333
GC_INNER word GC_total_stacksize = 0;
334
#endif
335

336
/* The lowest value returned by `min_bytes_allocd()`. */
337
static size_t min_bytes_allocd_minimum = 1;
338

339
GC_API void GC_CALL
340
GC_set_min_bytes_allocd(size_t value)
3✔
341
{
342
  GC_ASSERT(value > 0);
3✔
343
  min_bytes_allocd_minimum = value;
3✔
344
}
3✔
345

346
GC_API size_t GC_CALL
347
GC_get_min_bytes_allocd(void)
3✔
348
{
349
  return min_bytes_allocd_minimum;
3✔
350
}
351

352
/*
353
 * Return the minimum number of bytes that must be allocated between
354
 * collections to amortize the cost of the latter.  Should be nonzero.
355
 */
356
static word
357
min_bytes_allocd(void)
48,707✔
358
{
359
  word result;
360
  word stack_size;
361
  /*
362
   * Total size of roots, it includes double stack size, since the stack
363
   * is expensive to scan.
364
   */
365
  word total_root_size;
366
  /* Estimate of memory to be scanned during normal collection. */
367
  word scan_size;
368

369
  GC_ASSERT(I_HOLD_LOCK());
48,707✔
370
#ifdef THREADS
371
  if (GC_has_running_threads()) {
48,707✔
372
    /* We are multi-threaded... */
373
    stack_size = GC_total_stacksize;
24,253✔
374
    /*
375
     * For now, we just use the value computed during the latest garbage
376
     * collection.
377
     */
378
#  ifdef DEBUG_THREADS
379
    GC_log_printf("Total stacks size: %lu\n", (unsigned long)stack_size);
380
#  endif
381
  } else
382
#endif
383
  /* else */ {
384
#ifdef STACK_NOT_SCANNED
385
    stack_size = 0;
386
#elif defined(STACK_GROWS_UP)
387
    stack_size = (word)(GC_approx_sp() - GC_stackbottom);
388
#else
389
    stack_size = (word)(GC_stackbottom - GC_approx_sp());
24,454✔
390
#endif
391
  }
392

393
  total_root_size = 2 * stack_size + GC_root_size;
48,707✔
394
  scan_size = 2 * GC_composite_in_use + GC_atomic_in_use / 4 + total_root_size;
48,707✔
395
  result = scan_size / GC_free_space_divisor;
48,707✔
396
  if (GC_incremental) {
48,707✔
397
    result /= 2;
39,596✔
398
  }
399
  return result > min_bytes_allocd_minimum ? result : min_bytes_allocd_minimum;
48,707✔
400
}
401

402
/*
403
 * Return the number of bytes allocated, adjusted for explicit storage
404
 * management, etc.  This number is used in deciding when to trigger
405
 * collections.
406
 */
407
STATIC word
408
GC_adj_bytes_allocd(void)
8,718,666✔
409
{
410
  GC_signed_word result;
411
  GC_signed_word expl_managed = (GC_signed_word)GC_non_gc_bytes
8,718,666✔
412
                                - (GC_signed_word)GC_non_gc_bytes_at_gc;
8,718,666✔
413

414
  /*
415
   * Do not count what was explicitly freed, or newly allocated for
416
   * explicit management.  Note that deallocating an explicitly managed
417
   * object should not alter result, assuming the client is playing by
418
   * the rules.
419
   */
420
  result = (GC_signed_word)GC_bytes_allocd + (GC_signed_word)GC_bytes_dropped
8,718,666✔
421
           - (GC_signed_word)GC_bytes_freed
8,718,666✔
422
           + (GC_signed_word)GC_finalizer_bytes_freed - expl_managed;
8,718,666✔
423
  if (result > (GC_signed_word)GC_bytes_allocd) {
8,718,666✔
424
    /* Probably a client bug or unfortunate scheduling. */
425
    result = (GC_signed_word)GC_bytes_allocd;
788,753✔
426
  }
427
  /*
428
   * We count objects enqueued for finalization as though they had been
429
   * reallocated this round.  Finalization is visible to user.
430
   * And if we do not count this, we have stability problems for programs
431
   * that finalize all objects.
432
   */
433
  result += (GC_signed_word)GC_bytes_finalized;
8,718,666✔
434
  if (result < (GC_signed_word)(GC_bytes_allocd >> 3)) {
8,718,666✔
435
    /*
436
     * Always count at least 1/8 of the allocations.  We do not want to
437
     * collect too infrequently, since that would inhibit coalescing of
438
     * free storage blocks.  This also makes us partially robust against
439
     * client bugs.
440
     */
441
    result = (GC_signed_word)(GC_bytes_allocd >> 3);
16,169✔
442
  }
443
  return (word)result;
8,718,666✔
444
}
445

446
/*
447
 * Clear up a few frames worth of garbage left at the top of the stack.
448
 * This is used to prevent us from accidentally treating garbage left
449
 * on the stack by other parts of the collector as roots.
450
 * This differs from the code in `misc.c` file, which actually tries
451
 * to keep the stack clear of long-lived, client-generated garbage.
452
 */
453
STATIC void
454
GC_clear_a_few_frames(void)
31,134✔
455
{
456
#ifndef CLEAR_STACK_NPTRS
457
#  define CLEAR_STACK_NPTRS 64 /*< pointers */
458
#endif
459
  volatile ptr_t frames[CLEAR_STACK_NPTRS];
460

461
  BZERO(CAST_AWAY_VOLATILE_PVOID(frames), sizeof(frames));
31,134✔
462
}
31,134✔
463

464
GC_API void GC_CALL
465
GC_start_incremental_collection(void)
3✔
466
{
467
#ifndef GC_DISABLE_INCREMENTAL
468
  LOCK();
3✔
469
  if (GC_incremental) {
3✔
470
    GC_should_start_incremental_collection = TRUE;
3✔
471
    if (!GC_dont_gc) {
3✔
472
      GC_collect_a_little_inner(1);
3✔
473
    }
474
  }
475
  UNLOCK();
3✔
476
#endif
477
}
3✔
478

479
GC_INNER GC_bool
480
GC_should_collect(void)
8,723,126✔
481
{
482
  static word last_min_bytes_allocd, last_gc_no;
483

484
  GC_ASSERT(I_HOLD_LOCK());
8,723,126✔
485
  if (last_gc_no != GC_gc_no) {
8,723,126✔
486
    last_min_bytes_allocd = min_bytes_allocd();
30,429✔
487
    last_gc_no = GC_gc_no;
30,429✔
488
  }
489
#ifndef GC_DISABLE_INCREMENTAL
490
  if (GC_should_start_incremental_collection) {
8,723,126✔
491
    GC_should_start_incremental_collection = FALSE;
3✔
492
    return TRUE;
3✔
493
  }
494
#endif
495
  if (GC_disable_automatic_collection)
8,723,123✔
496
    return FALSE;
×
497

498
  if (GC_last_heap_growth_gc_no == GC_gc_no) {
8,723,123✔
499
    /* Avoid expanding past limits used by black-listing. */
500
    return TRUE;
4,457✔
501
  }
502

503
  return GC_adj_bytes_allocd() >= last_min_bytes_allocd;
8,718,666✔
504
}
505

506
/*
507
 * Called at start of full collections.  Not called if zero.
508
 * Called with the allocator lock held.  Not used by the collector itself.
509
 */
510
/* `STATIC` */ GC_start_callback_proc GC_start_call_back = 0;
511

512
GC_API void GC_CALL
513
GC_set_start_callback(GC_start_callback_proc fn)
3✔
514
{
515
  LOCK();
3✔
516
  GC_start_call_back = fn;
3✔
517
  UNLOCK();
3✔
518
}
3✔
519

520
GC_API GC_start_callback_proc GC_CALL
521
GC_get_start_callback(void)
3✔
522
{
523
  GC_start_callback_proc fn;
524

525
  READER_LOCK();
3✔
526
  fn = GC_start_call_back;
3✔
527
  READER_UNLOCK();
3✔
528
  return fn;
3✔
529
}
530

531
GC_INLINE void
532
GC_notify_full_gc(void)
13,522✔
533
{
534
  if (GC_start_call_back != 0) {
13,522✔
535
    (*GC_start_call_back)();
×
536
  }
537
}
13,522✔
538

539
STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func);
540
STATIC void GC_finish_collection(void);
541

542
/*
543
 * Initiate a garbage collection if appropriate.  Choose judiciously
544
 * between partial, full, and stop-world collections.
545
 */
546
STATIC void
547
GC_maybe_gc(void)
8,119,208✔
548
{
549
  GC_ASSERT(I_HOLD_LOCK());
8,119,208✔
550
  ASSERT_CANCEL_DISABLED();
8,119,208✔
551
  if (!GC_should_collect())
8,119,208✔
552
    return;
8,100,343✔
553

554
  if (!GC_incremental) {
18,865✔
555
    GC_gcollect_inner();
50✔
556
    return;
50✔
557
  }
558

559
  GC_ASSERT(!GC_collection_in_progress());
18,815✔
560
#ifdef PARALLEL_MARK
561
  if (GC_parallel)
18,815✔
562
    GC_wait_for_reclaim();
10,801✔
563
#endif
564
  if (GC_need_full_gc || GC_n_partial_gcs >= GC_full_freq) {
18,815✔
565
    GC_COND_LOG_PRINTF(
1,294✔
566
        "***>Full mark for collection #%lu after %lu bytes allocated\n",
567
        (unsigned long)GC_gc_no + 1, (unsigned long)GC_bytes_allocd);
×
568
    GC_notify_full_gc();
1,294✔
569
    ENTER_GC();
1,294✔
570
    GC_promote_black_lists();
1,294✔
571
    (void)GC_reclaim_all((GC_stop_func)0, TRUE);
1,294✔
572
    GC_clear_marks();
1,294✔
573
    EXIT_GC();
1,294✔
574
    GC_n_partial_gcs = 0;
1,294✔
575
    GC_is_full_gc = TRUE;
1,294✔
576
  } else {
577
    GC_n_partial_gcs++;
17,521✔
578
  }
579

580
  /*
581
   * Try to mark with the world stopped.  If we run out of time, then this
582
   * turns into an incremental marking.
583
   */
584
#ifndef NO_CLOCK
585
  if (GC_time_limit != GC_TIME_UNLIMITED)
18,815✔
586
    GET_TIME(GC_start_time);
×
587
#endif
588
  if (GC_stopped_mark(GC_timeout_stop_func)) {
18,815✔
589
    SAVE_CALLERS_TO_LAST_STACK();
590
    GC_finish_collection();
18,724✔
591
  } else if (!GC_is_full_gc) {
91✔
592
    /* Count this as the first attempt. */
593
    GC_n_attempts++;
82✔
594
  }
595
}
596

597
STATIC GC_on_collection_event_proc GC_on_collection_event = 0;
598

599
GC_API void GC_CALL
600
GC_set_on_collection_event(GC_on_collection_event_proc fn)
3✔
601
{
602
  /* `fn` may be 0 (means no event notifier). */
603
  LOCK();
3✔
604
  GC_on_collection_event = fn;
3✔
605
  UNLOCK();
3✔
606
}
3✔
607

608
GC_API GC_on_collection_event_proc GC_CALL
609
GC_get_on_collection_event(void)
3✔
610
{
611
  GC_on_collection_event_proc fn;
612

613
  READER_LOCK();
3✔
614
  fn = GC_on_collection_event;
3✔
615
  READER_UNLOCK();
3✔
616
  return fn;
3✔
617
}
618

619
GC_INNER GC_bool
620
GC_try_to_collect_inner(GC_stop_func stop_func)
12,303✔
621
{
622
#ifndef NO_CLOCK
623
  CLOCK_TYPE start_time = CLOCK_TYPE_INITIALIZER;
12,303✔
624
  GC_bool start_time_valid;
625
#endif
626

627
  ASSERT_CANCEL_DISABLED();
12,303✔
628
  GC_ASSERT(I_HOLD_LOCK());
12,303✔
629
  GC_ASSERT(GC_is_initialized);
12,303✔
630
  if (GC_dont_gc || (*stop_func)())
12,303✔
631
    return FALSE;
12,303✔
632
  if (GC_on_collection_event)
12,228✔
633
    GC_on_collection_event(GC_EVENT_START);
×
634
  if (GC_incremental && GC_collection_in_progress()) {
12,228✔
635
    GC_COND_LOG_PRINTF(
×
636
        "GC_try_to_collect_inner: finishing collection in progress\n");
637
    /* Just finish collection already in progress. */
638
    do {
639
      if ((*stop_func)()) {
×
640
        /* TODO: Notify `GC_EVENT_ABANDON`. */
641
        return FALSE;
×
642
      }
643
      GC_collect_a_little_inner(1);
×
644
    } while (GC_collection_in_progress());
×
645
  }
646
  GC_notify_full_gc();
12,228✔
647
#ifndef NO_CLOCK
648
  start_time_valid = FALSE;
12,228✔
649
  if ((GC_print_stats | (int)GC_measure_performance) != 0) {
12,228✔
650
    if (GC_print_stats)
3,434✔
651
      GC_log_printf("Initiating full world-stop collection!\n");
×
652
    start_time_valid = TRUE;
3,434✔
653
    GET_TIME(start_time);
3,434✔
654
  }
655
#endif
656
  GC_promote_black_lists();
12,228✔
657
  /*
658
   * Make sure all blocks have been reclaimed, so sweep routines do not
659
   * see cleared mark bits.  If we are guaranteed to finish, then this
660
   * is unnecessary.  In the find-leak case, we have to finish to
661
   * guarantee that previously unmarked objects are not reported as leaks.
662
   */
663
#ifdef PARALLEL_MARK
664
  if (GC_parallel)
12,228✔
665
    GC_wait_for_reclaim();
5,163✔
666
#endif
667
  ENTER_GC();
12,228✔
668
  if ((GC_find_leak_inner || stop_func != GC_never_stop_func)
12,228✔
669
      && !GC_reclaim_all(stop_func, FALSE)) {
3,340✔
670
    /* Aborted.  So far everything is still consistent. */
671
    EXIT_GC();
×
672
    /* TODO: Notify `GC_EVENT_ABANDON`. */
673
    return FALSE;
×
674
  }
675
  GC_invalidate_mark_state(); /*< flush mark stack */
12,228✔
676
  GC_clear_marks();
12,228✔
677
  SAVE_CALLERS_TO_LAST_STACK();
678
  GC_is_full_gc = TRUE;
12,228✔
679
  EXIT_GC();
12,228✔
680
  if (!GC_stopped_mark(stop_func)) {
12,228✔
681
    if (!GC_incremental) {
×
682
      /*
683
       * We are partially done and have no way to complete or use
684
       * current work.  Reestablish invariants as cheaply as possible.
685
       */
686
      GC_invalidate_mark_state();
×
687
      GC_unpromote_black_lists();
×
688
    } else {
689
      /*
690
       * We claim the world is already (or still) consistent.
691
       * We will finish incrementally.
692
       */
693
    }
694
    /* TODO: Notify `GC_EVENT_ABANDON`. */
695
    return FALSE;
×
696
  }
697
  GC_finish_collection();
12,228✔
698
#ifndef NO_CLOCK
699
  if (start_time_valid) {
12,228✔
700
    CLOCK_TYPE current_time;
701
    unsigned long time_diff, ns_frac_diff;
702

703
    GET_TIME(current_time);
3,434✔
704
    time_diff = MS_TIME_DIFF(current_time, start_time);
3,434✔
705
    ns_frac_diff = NS_FRAC_TIME_DIFF(current_time, start_time);
3,434✔
706
    if (GC_measure_performance) {
3,434✔
707
      GC_full_gc_total_time += time_diff; /*< may wrap */
3,434✔
708
      GC_full_gc_total_ns_frac += (unsigned32)ns_frac_diff;
3,434✔
709
      if (GC_full_gc_total_ns_frac >= (unsigned32)1000000UL) {
3,434✔
710
        /* Overflow of the nanoseconds part. */
711
        GC_full_gc_total_ns_frac -= (unsigned32)1000000UL;
1,724✔
712
        GC_full_gc_total_time++;
1,724✔
713
      }
714
    }
715
    if (GC_print_stats)
3,434✔
716
      GC_log_printf("Complete collection took %lu ms %lu ns\n", time_diff,
×
717
                    ns_frac_diff);
718
  }
719
#endif
720
  if (GC_on_collection_event)
12,228✔
721
    GC_on_collection_event(GC_EVENT_END);
×
722
  return TRUE;
12,228✔
723
}
724

725
/* The default value of `GC_rate`. */
726
#ifndef GC_RATE
727
#  define GC_RATE 10
728
#endif
729

730
/*
731
 * When `GC_collect_a_little_inner()` performs `n_blocks` units of garbage
732
 * collection work, a unit is intended to touch roughly `GC_rate` pages.
733
 * (But, every once in a while, we do more than that.)  This needs to be
734
 * a fairly large number with our current incremental collection strategy,
735
 * since otherwise we allocate too much during garbage collection, and
736
 * the cleanup gets expensive.
737
 */
738
STATIC unsigned GC_rate = GC_RATE;
739

740
GC_API void GC_CALL
741
GC_set_rate(int value)
3✔
742
{
743
  GC_ASSERT(value > 0);
3✔
744
  GC_rate = (unsigned)value;
3✔
745
}
3✔
746

747
GC_API int GC_CALL
748
GC_get_rate(void)
3✔
749
{
750
  return (int)GC_rate;
3✔
751
}
752

753
/* The default maximum number of prior attempts at world stop marking. */
754
#ifndef MAX_PRIOR_ATTEMPTS
755
#  define MAX_PRIOR_ATTEMPTS 3
756
#endif
757

758
/*
759
 * The maximum number of prior attempts at world stop marking.
760
 * A value of 1 means that we finish the second time, no matter how long
761
 * it takes.  Does not count the initial root scan for a full collection.
762
 */
763
static int max_prior_attempts = MAX_PRIOR_ATTEMPTS;
764

765
GC_API void GC_CALL
766
GC_set_max_prior_attempts(int value)
3✔
767
{
768
  GC_ASSERT(value >= 0);
3✔
769
  max_prior_attempts = value;
3✔
770
}
3✔
771

772
GC_API int GC_CALL
773
GC_get_max_prior_attempts(void)
3✔
774
{
775
  return max_prior_attempts;
3✔
776
}
777

778
GC_INNER void
779
GC_collect_a_little_inner(size_t n_blocks)
8,127,188✔
780
{
781
  IF_CANCEL(int cancel_state;)
782

783
  GC_ASSERT(I_HOLD_LOCK());
8,127,188✔
784
  GC_ASSERT(GC_is_initialized);
8,127,188✔
785
  DISABLE_CANCEL(cancel_state);
8,127,188✔
786
  if (GC_incremental && GC_collection_in_progress()) {
8,129,515✔
787
    size_t i;
788
    size_t max_deficit = GC_rate * n_blocks;
2,327✔
789

790
    ENTER_GC();
2,327✔
791
#ifdef PARALLEL_MARK
792
    if (GC_time_limit != GC_TIME_UNLIMITED)
2,327✔
793
      GC_parallel_mark_disabled = TRUE;
×
794
#endif
795
    for (i = GC_mark_deficit; i < max_deficit; i++) {
28,798✔
796
      if (GC_mark_some(NULL))
26,562✔
797
        break;
91✔
798
    }
799
#ifdef PARALLEL_MARK
800
    GC_parallel_mark_disabled = FALSE;
2,327✔
801
#endif
802
    EXIT_GC();
2,327✔
803

804
    if (i < max_deficit && !GC_dont_gc) {
2,327✔
805
      GC_ASSERT(!GC_collection_in_progress());
91✔
806
      /* Need to follow up with a full collection. */
807
      SAVE_CALLERS_TO_LAST_STACK();
808
#ifdef PARALLEL_MARK
809
      if (GC_parallel)
91✔
810
        GC_wait_for_reclaim();
91✔
811
#endif
812
#ifndef NO_CLOCK
813
      if (GC_time_limit != GC_TIME_UNLIMITED
91✔
814
          && GC_n_attempts < max_prior_attempts)
×
815
        GET_TIME(GC_start_time);
×
816
#endif
817
      if (GC_stopped_mark(GC_n_attempts < max_prior_attempts
91✔
818
                              ? GC_timeout_stop_func
819
                              : GC_never_stop_func)) {
820
        GC_finish_collection();
91✔
821
      } else {
822
        GC_n_attempts++;
×
823
      }
824
    }
825
    if (GC_mark_deficit > 0) {
2,327✔
826
      GC_mark_deficit
827
          = GC_mark_deficit > max_deficit ? GC_mark_deficit - max_deficit : 0;
×
828
    }
829
  } else if (!GC_dont_gc) {
8,124,861✔
830
    GC_maybe_gc();
8,119,208✔
831
  }
832
  RESTORE_CANCEL(cancel_state);
8,127,188✔
833
}
8,127,188✔
834

835
#if !defined(NO_FIND_LEAK) || !defined(SHORT_DBG_HDRS)
836
GC_INNER void (*GC_check_heap)(void) = 0;
837
GC_INNER void (*GC_print_all_smashed)(void) = 0;
838
#endif
839

840
GC_API int GC_CALL
841
GC_collect_a_little(void)
3,317,138✔
842
{
843
  int result;
844

845
  if (UNLIKELY(!GC_is_initialized))
3,317,138✔
846
    GC_init();
×
847
  LOCK();
3,317,138✔
848
  /*
849
   * Note: if the collection is in progress, this may do marking (not
850
   * stopping the world) even in case of disabled garbage collection.
851
   */
852
  GC_collect_a_little_inner(1);
3,317,138✔
853
  result = (int)GC_collection_in_progress();
3,317,138✔
854
  UNLOCK();
3,317,138✔
855
  if (GC_debugging_started && !result)
3,317,138✔
856
    GC_print_all_smashed();
×
857
  return result;
3,317,138✔
858
}
859

860
#ifdef THREADS
861
GC_API void GC_CALL
862
GC_stop_world_external(void)
3✔
863
{
864
  GC_ASSERT(GC_is_initialized);
3✔
865
  LOCK();
3✔
866
#  ifdef THREAD_LOCAL_ALLOC
867
  GC_ASSERT(!GC_world_stopped);
3✔
868
#  endif
869
  ENTER_GC();
3✔
870
  STOP_WORLD();
3✔
871
#  ifdef THREAD_LOCAL_ALLOC
872
  GC_world_stopped = TRUE;
3✔
873
#  endif
874
}
3✔
875

876
GC_API void GC_CALL
877
GC_start_world_external(void)
3✔
878
{
879
#  ifdef THREAD_LOCAL_ALLOC
880
  GC_ASSERT(GC_world_stopped);
3✔
881
  GC_world_stopped = FALSE;
3✔
882
#  else
883
  GC_ASSERT(GC_is_initialized);
884
#  endif
885
  START_WORLD();
3✔
886
  EXIT_GC();
3✔
887
  UNLOCK();
3✔
888
}
3✔
889
#endif /* THREADS */
890

891
#ifdef USE_MUNMAP
892
#  ifndef MUNMAP_THRESHOLD
893
#    define MUNMAP_THRESHOLD 7
894
#  endif
895
GC_INNER unsigned GC_unmap_threshold = MUNMAP_THRESHOLD;
896

897
#  define IF_USE_MUNMAP(x) x
898
#  define COMMA_IF_USE_MUNMAP(x) /* comma */ , x
899
#else
900
#  define IF_USE_MUNMAP(x)
901
#  define COMMA_IF_USE_MUNMAP(x)
902
#endif /* !USE_MUNMAP */
903

904
/*
905
 * We stop the world and mark from all roots.  If `stop_func()` ever
906
 * returns `TRUE`, we may fail and return `FALSE`.  Increment `GC_gc_no`
907
 * if we succeed.
908
 */
909
STATIC GC_bool
910
GC_stopped_mark(GC_stop_func stop_func)
31,134✔
911
{
912
  ptr_t cold_gc_frame = GC_approx_sp();
31,134✔
913
  unsigned abandoned_at;
914
#ifndef NO_CLOCK
915
  CLOCK_TYPE start_time = CLOCK_TYPE_INITIALIZER;
31,134✔
916
  GC_bool start_time_valid = FALSE;
31,134✔
917
#endif
918

919
  GC_ASSERT(I_HOLD_LOCK());
31,134✔
920
  GC_ASSERT(GC_is_initialized);
31,134✔
921
  ENTER_GC();
31,134✔
922
#if !defined(REDIRECT_MALLOC) && defined(USE_WINALLOC)
923
  GC_add_current_malloc_heap();
924
#endif
925
#if defined(REGISTER_LIBRARIES_EARLY)
926
  GC_cond_register_dynamic_libraries();
31,134✔
927
#endif
928

929
#if !defined(GC_NO_FINALIZATION) && !defined(GC_TOGGLE_REFS_NOT_NEEDED)
930
  GC_process_togglerefs();
31,134✔
931
#endif
932

933
  /* Output blank line for convenience here. */
934
  GC_COND_LOG_PRINTF(
31,134✔
935
      "\n--> Marking for collection #%lu after %lu allocated bytes\n",
936
      (unsigned long)GC_gc_no + 1, (unsigned long)GC_bytes_allocd);
×
937
#ifndef NO_CLOCK
938
  if (GC_PRINT_STATS_FLAG || GC_measure_performance) {
31,134✔
939
    GET_TIME(start_time);
14,194✔
940
    start_time_valid = TRUE;
14,194✔
941
  }
942
#endif
943
#ifdef THREADS
944
  if (GC_on_collection_event)
31,134✔
945
    GC_on_collection_event(GC_EVENT_PRE_STOP_WORLD);
×
946
#endif
947
  STOP_WORLD();
31,134✔
948
#ifdef THREADS
949
  if (GC_on_collection_event)
31,134✔
950
    GC_on_collection_event(GC_EVENT_POST_STOP_WORLD);
×
951
#  ifdef THREAD_LOCAL_ALLOC
952
  GC_world_stopped = TRUE;
31,134✔
953
#  elif defined(CPPCHECK)
954
  /* Workaround a warning about adjacent same `if` condition. */
955
  (void)0;
956
#  endif
957
#endif
958

959
#ifdef MAKE_BACK_GRAPH
960
  if (GC_print_back_height) {
961
    GC_build_back_graph();
962
  }
963
#endif
964

965
  /* Notify about marking from all roots. */
966
  if (GC_on_collection_event)
31,134✔
967
    GC_on_collection_event(GC_EVENT_MARK_START);
×
968

969
  /* Minimize junk left in my registers and on the stack. */
970
  GC_clear_a_few_frames();
31,134✔
971
  GC_noop6(0, 0, 0, 0, 0, 0);
31,134✔
972

973
  GC_initiate_gc();
31,134✔
974
#ifdef PARALLEL_MARK
975
  if (stop_func != GC_never_stop_func)
31,134✔
976
    GC_parallel_mark_disabled = TRUE;
22,180✔
977
#endif
978
  for (abandoned_at = 1; !(*stop_func)(); abandoned_at++) {
23,885,679✔
979
    if (GC_mark_some(cold_gc_frame)) {
23,885,588✔
980
#ifdef PARALLEL_MARK
981
      if (GC_parallel && GC_parallel_mark_disabled) {
31,043✔
982
        GC_COND_LOG_PRINTF("Stopped marking done after %u iterations"
14,016✔
983
                           " with disabled parallel marker\n",
984
                           abandoned_at - 1);
985
      }
986
#endif
987
      abandoned_at = 0;
31,043✔
988
      break;
31,043✔
989
    }
990
  }
991
#ifdef PARALLEL_MARK
992
  GC_parallel_mark_disabled = FALSE;
31,134✔
993
#endif
994

995
  if (abandoned_at > 0) {
31,134✔
996
    /* Give the mutator a chance. */
997
    GC_mark_deficit = abandoned_at - 1;
91✔
998
    /* TODO: Notify `GC_EVENT_MARK_ABANDON`. */
999
  } else {
1000
    GC_gc_no++;
31,043✔
1001
    /* Check all debugged objects for consistency. */
1002
    if (GC_debugging_started)
31,043✔
1003
      GC_check_heap();
291✔
1004
    if (GC_on_collection_event)
31,043✔
1005
      GC_on_collection_event(GC_EVENT_MARK_END);
×
1006
  }
1007

1008
#ifdef THREADS
1009
  if (GC_on_collection_event)
31,134✔
1010
    GC_on_collection_event(GC_EVENT_PRE_START_WORLD);
×
1011
#endif
1012
#ifdef THREAD_LOCAL_ALLOC
1013
  GC_world_stopped = FALSE;
31,134✔
1014
#endif
1015
  START_WORLD();
31,134✔
1016
#ifdef THREADS
1017
  if (GC_on_collection_event)
31,134✔
1018
    GC_on_collection_event(GC_EVENT_POST_START_WORLD);
×
1019
#endif
1020

1021
#ifndef NO_CLOCK
1022
  if (start_time_valid) {
31,134✔
1023
    CLOCK_TYPE current_time;
1024
    unsigned long time_diff, ns_frac_diff;
1025

1026
    /* TODO: Avoid code duplication from `GC_try_to_collect_inner`. */
1027
    GET_TIME(current_time);
14,194✔
1028
    time_diff = MS_TIME_DIFF(current_time, start_time);
14,194✔
1029
    ns_frac_diff = NS_FRAC_TIME_DIFF(current_time, start_time);
14,194✔
1030
    if (GC_measure_performance) {
14,194✔
1031
      GC_stopped_mark_total_time += time_diff; /*< may wrap */
14,194✔
1032
      GC_stopped_mark_total_ns_frac += (unsigned32)ns_frac_diff;
14,194✔
1033
      if (GC_stopped_mark_total_ns_frac >= (unsigned32)1000000UL) {
14,194✔
1034
        GC_stopped_mark_total_ns_frac -= (unsigned32)1000000UL;
6,395✔
1035
        GC_stopped_mark_total_time++;
6,395✔
1036
      }
1037
    }
1038

1039
    if (GC_PRINT_STATS_FLAG || GC_measure_performance) {
14,194✔
1040
      unsigned total_time = GC_world_stopped_total_time;
14,194✔
1041
      unsigned divisor = GC_world_stopped_total_divisor;
14,194✔
1042

1043
      /* Compute new world-stop delay total time. */
1044
      if (total_time > (((unsigned)-1) >> 1)
14,194✔
1045
          || divisor >= MAX_TOTAL_TIME_DIVISOR) {
14,194✔
1046
        /* Halve values if overflow occurs. */
1047
        total_time >>= 1;
26✔
1048
        divisor >>= 1;
26✔
1049
      }
1050
      total_time += time_diff < (((unsigned)-1) >> 1) ? (unsigned)time_diff
14,194✔
1051
                                                      : ((unsigned)-1) >> 1;
14,194✔
1052
      /* Update old `GC_world_stopped_total_time` and its divisor. */
1053
      GC_world_stopped_total_time = total_time;
14,194✔
1054
      GC_world_stopped_total_divisor = ++divisor;
14,194✔
1055
      if (GC_PRINT_STATS_FLAG && 0 == abandoned_at) {
14,194✔
1056
        GC_ASSERT(divisor != 0);
×
1057
        GC_log_printf(
×
1058
            "World-stopped marking took %lu ms %lu ns (%u ms in average)\n",
1059
            time_diff, ns_frac_diff, total_time / divisor);
1060
      }
1061
    }
1062
  }
1063
#endif
1064

1065
  EXIT_GC();
31,134✔
1066
  if (0 == abandoned_at)
31,134✔
1067
    return TRUE;
31,134✔
1068
  GC_COND_LOG_PRINTF("Abandoned stopped marking after %u iterations\n",
91✔
1069
                     abandoned_at - 1);
1070
  return FALSE;
91✔
1071
}
1072

1073
GC_INNER void
1074
GC_set_fl_marks(ptr_t q)
1,625,956✔
1075
{
1076
#ifdef GC_ASSERTIONS
1077
  ptr_t q2;
1078
#endif
1079
  struct hblk *h = HBLKPTR(q);
1,625,956✔
1080
  const struct hblk *last_h = h;
1,625,956✔
1081
  hdr *hhdr;
1082
#ifdef MARK_BIT_PER_OBJ
1083
  size_t sz;
1084
#endif
1085

1086
  GC_ASSERT(q != NULL);
1,625,956✔
1087
  hhdr = HDR(h);
1,625,956✔
1088
#ifdef MARK_BIT_PER_OBJ
1089
  sz = hhdr->hb_sz;
1090
#endif
1091
#ifdef GC_ASSERTIONS
1092
  q2 = (ptr_t)obj_link(q);
1,625,956✔
1093
#endif
1094
  for (;;) {
44,785,482✔
1095
    size_t bit_no = MARK_BIT_NO((size_t)((ptr_t)q - (ptr_t)h), sz);
46,411,438✔
1096

1097
    if (!mark_bit_from_hdr(hhdr, bit_no)) {
46,411,438✔
1098
      set_mark_bit_from_hdr(hhdr, bit_no);
23,939,089✔
1099
      INCR_MARKS(hhdr);
23,939,089✔
1100
    }
1101
    q = (ptr_t)obj_link(q);
46,411,438✔
1102
    if (NULL == q)
46,411,438✔
1103
      break;
1,625,956✔
1104
#ifdef GC_ASSERTIONS
1105
    /*
1106
     * Detect a cycle in the free list.  The algorithm is to have
1107
     * a "twice faster" iterator over the list which meets the first
1108
     * one in case of a cycle existing in the list.
1109
     */
1110
    if (q2 != NULL) {
44,785,482✔
1111
      q2 = (ptr_t)obj_link(q2);
22,781,235✔
1112
      GC_ASSERT(q2 != q);
22,781,235✔
1113
      if (q2 != NULL) {
22,781,235✔
1114
        q2 = (ptr_t)obj_link(q2);
22,004,247✔
1115
        GC_ASSERT(q2 != q);
22,004,247✔
1116
      }
1117
    }
1118
#endif
1119

1120
    h = HBLKPTR(q);
44,785,482✔
1121
    if (UNLIKELY(h != last_h)) {
44,785,482✔
1122
      last_h = h;
171,896✔
1123
      /* Update `hhdr` and `sz`. */
1124
      hhdr = HDR(h);
171,896✔
1125
#ifdef MARK_BIT_PER_OBJ
1126
      sz = hhdr->hb_sz;
1127
#endif
1128
    }
1129
  }
1130
}
1,625,956✔
1131

1132
#if defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC)
1133
/*
1134
 * Check that all mark bits for the free list, whose first entry is
1135
 * `*pfreelist`, are set.  The check is skipped if `*pfreelist` points to
1136
 * a special value.
1137
 */
1138
void
1139
GC_check_fl_marks(void **pfreelist)
24,776,640✔
1140
{
1141
  /*
1142
   * TODO: There is a data race with `GC_FAST_MALLOC_GRANS` (which does
1143
   * not do atomic updates to the free-list).  The race seems to be
1144
   * harmless, and for now we just skip this check in case of TSan.
1145
   */
1146
#  if defined(AO_HAVE_load_acquire_read) && !defined(THREAD_SANITIZER)
1147
  ptr_t list = GC_cptr_load_acquire_read((volatile ptr_t *)pfreelist);
24,776,640✔
1148
  /* Atomic operations are used because the world is running. */
1149
  ptr_t p, prev, next;
1150

1151
  if (ADDR(list) <= HBLKSIZE)
24,776,640✔
1152
    return;
23,151,258✔
1153

1154
  prev = (ptr_t)pfreelist;
1,625,382✔
1155
  for (p = list; p != NULL; p = next) {
48,014,566✔
1156
    if (!GC_is_marked(p)) {
46,389,184✔
1157
      ABORT_ARG2("Unmarked local free-list entry", ": object %p on list %p",
×
1158
                 (void *)p, (void *)list);
1159
    }
1160

1161
    /*
1162
     * While traversing the free-list, it re-reads the pointer to the
1163
     * current node before accepting its next pointer and bails out
1164
     * if the latter has changed.  That way, it will not try to follow
1165
     * the pointer which might be been modified after the object was
1166
     * returned to the client.  It might perform the mark-check on the
1167
     * just allocated object but that should be harmless.
1168
     */
1169
    next = GC_cptr_load_acquire_read((volatile ptr_t *)p);
46,389,184✔
1170
    if (GC_cptr_load((volatile ptr_t *)prev) != p)
46,389,184✔
1171
      break;
×
1172
    prev = p;
46,389,184✔
1173
  }
1174
#  else
1175
  /* FIXME: Not implemented (just skipped). */
1176
  (void)pfreelist;
1177
#  endif
1178
}
1179
#endif /* GC_ASSERTIONS && THREAD_LOCAL_ALLOC */
1180

1181
/*
1182
 * Clear all mark bits for the free list (specified by the first entry).
1183
 * Decrement `GC_bytes_found` by number of bytes on free list.
1184
 */
1185
STATIC void
1186
GC_clear_fl_marks(ptr_t q)
175,074✔
1187
{
1188
  struct hblk *h = HBLKPTR(q);
175,074✔
1189
  const struct hblk *last_h = h;
175,074✔
1190
  hdr *hhdr = HDR(h);
175,074✔
1191
  size_t sz = hhdr->hb_sz; /*< normally set only once */
175,074✔
1192

1193
  for (;;) {
331,829,028✔
1194
    size_t bit_no = MARK_BIT_NO((size_t)((ptr_t)q - (ptr_t)h), sz);
332,004,102✔
1195

1196
    if (mark_bit_from_hdr(hhdr, bit_no)) {
332,004,102✔
1197
      size_t n_marks = hhdr->hb_n_marks;
20,024,251✔
1198

1199
#ifdef LINT2
1200
      if (0 == n_marks)
1201
        ABORT("hhdr->hb_n_marks cannot be zero");
1202
#else
1203
      GC_ASSERT(n_marks != 0);
20,024,251✔
1204
#endif
1205
      clear_mark_bit_from_hdr(hhdr, bit_no);
20,024,251✔
1206
      n_marks--;
20,024,251✔
1207
#ifdef PARALLEL_MARK
1208
      /* Approximate count, do not decrement to zero! */
1209
      if (n_marks != 0 || !GC_parallel) {
20,024,251✔
1210
        hhdr->hb_n_marks = n_marks;
20,012,925✔
1211
      }
1212
#else
1213
      hhdr->hb_n_marks = n_marks;
1214
#endif
1215
    }
1216
    GC_bytes_found -= (GC_signed_word)sz;
332,004,102✔
1217

1218
    q = (ptr_t)obj_link(q);
332,004,102✔
1219
    if (NULL == q)
332,004,102✔
1220
      break;
175,074✔
1221

1222
    h = HBLKPTR(q);
331,829,028✔
1223
    if (UNLIKELY(h != last_h)) {
331,829,028✔
1224
      last_h = h;
5,417,168✔
1225
      /* Update `hhdr` and `sz`. */
1226
      hhdr = HDR(h);
5,417,168✔
1227
      sz = hhdr->hb_sz;
5,417,168✔
1228
    }
1229
  }
1230
}
175,074✔
1231

1232
/* Mark all objects on the free lists for every object kind. */
1233
static void
1234
set_all_fl_marks(void)
66✔
1235
{
1236
  unsigned kind;
1237

1238
  for (kind = 0; kind < GC_n_kinds; kind++) {
330✔
1239
    size_t lg;
1240

1241
    for (lg = 1; lg <= MAXOBJGRANULES; lg++) {
34,056✔
1242
      ptr_t q = (ptr_t)GC_obj_kinds[kind].ok_freelist[lg];
33,792✔
1243

1244
      if (q != NULL)
33,792✔
1245
        GC_set_fl_marks(q);
558✔
1246
    }
1247
  }
1248
}
66✔
1249

1250
/*
1251
 * Clear free-list mark bits.  Also subtract memory remaining from
1252
 * `GC_bytes_found` count.
1253
 */
1254
static void
1255
clear_all_fl_marks(void)
31,043✔
1256
{
1257
  unsigned kind;
1258

1259
  for (kind = 0; kind < GC_n_kinds; kind++) {
182,110✔
1260
    size_t lg;
1261

1262
    for (lg = 1; lg <= MAXOBJGRANULES; lg++) {
19,487,643✔
1263
      ptr_t q = (ptr_t)GC_obj_kinds[kind].ok_freelist[lg];
19,336,576✔
1264

1265
      if (q != NULL)
19,336,576✔
1266
        GC_clear_fl_marks(q);
175,074✔
1267
    }
1268
  }
1269
}
31,043✔
1270

1271
#if defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC)
1272
void GC_check_tls(void);
1273
#endif
1274

1275
GC_on_heap_resize_proc GC_on_heap_resize = 0;
1276

1277
/* Used for logging only. */
1278
GC_INLINE int
1279
GC_compute_heap_usage_percent(void)
×
1280
{
1281
  word used = GC_composite_in_use + GC_atomic_in_use + GC_bytes_allocd;
×
1282
  word heap_sz = GC_heapsize - GC_unmapped_bytes;
×
1283

1284
  return used >= heap_sz            ? 0
1285
         : used < GC_WORD_MAX / 100 ? (int)((used * 100) / heap_sz)
×
1286
                                    : (int)(used / (heap_sz / 100));
×
1287
}
1288

1289
#define GC_DBGLOG_PRINT_HEAP_IN_USE()                                        \
1290
  GC_DBGLOG_PRINTF("In-use heap: %d%% (%lu KiB pointers + %lu KiB other)\n", \
1291
                   GC_compute_heap_usage_percent(),                          \
1292
                   TO_KiB_UL(GC_composite_in_use),                           \
1293
                   TO_KiB_UL(GC_atomic_in_use + GC_bytes_allocd))
1294

1295
/*
1296
 * Finish up a collection.  Assumes mark bits are consistent, but the
1297
 * world is otherwise running.
1298
 */
1299
STATIC void
1300
GC_finish_collection(void)
31,043✔
1301
{
1302
#ifndef NO_CLOCK
1303
  CLOCK_TYPE start_time = CLOCK_TYPE_INITIALIZER;
31,043✔
1304
  CLOCK_TYPE finalize_time = CLOCK_TYPE_INITIALIZER;
31,043✔
1305
#endif
1306

1307
  GC_ASSERT(I_HOLD_LOCK());
31,043✔
1308
#if defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC) \
1309
    && !defined(DBG_HDRS_ALL)
1310
  /* Check that we marked some of our own data. */
1311
  GC_check_tls();
31,043✔
1312
  /* TODO: Add more checks. */
1313
#endif
1314

1315
#ifndef NO_CLOCK
1316
  if (GC_print_stats)
31,043✔
1317
    GET_TIME(start_time);
×
1318
#endif
1319
  if (GC_on_collection_event)
31,043✔
1320
    GC_on_collection_event(GC_EVENT_RECLAIM_START);
×
1321

1322
#ifndef GC_GET_HEAP_USAGE_NOT_NEEDED
1323
  if (GC_bytes_found > 0)
31,043✔
1324
    GC_reclaimed_bytes_before_gc += (word)GC_bytes_found;
30,107✔
1325
#endif
1326
  GC_bytes_found = 0;
31,043✔
1327
#if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG)
1328
  if (GETENV("GC_PRINT_ADDRESS_MAP") != NULL) {
31,043✔
1329
    GC_print_address_map();
×
1330
  }
1331
#endif
1332
  COND_DUMP;
31,043✔
1333
  if (GC_find_leak_inner) {
31,043✔
1334
    set_all_fl_marks();
66✔
1335
    /* This just checks; it does not really reclaim anything. */
1336
    GC_start_reclaim(TRUE);
66✔
1337
  }
1338

1339
#ifndef GC_NO_FINALIZATION
1340
  GC_finalize();
31,043✔
1341
#endif
1342
#ifndef NO_CLOCK
1343
  if (GC_print_stats)
31,043✔
1344
    GET_TIME(finalize_time);
×
1345
#endif
1346
#ifdef MAKE_BACK_GRAPH
1347
  if (GC_print_back_height) {
1348
    GC_traverse_back_graph();
1349
  }
1350
#endif
1351

1352
  /*
1353
   * Clear free-list mark bits, in case they got accidentally marked
1354
   * (or `GC_find_leak` is set and they were intentionally marked).
1355
   * Note that composite objects on free list are cleared, thus
1356
   * accidentally marking a free list is not a problem; but some objects
1357
   * on the list itself might be marked, and the given function call
1358
   * fixes it.
1359
   */
1360
  clear_all_fl_marks();
31,043✔
1361

1362
  GC_VERBOSE_LOG_PRINTF("Bytes recovered before sweep - f.l. count = %ld\n",
31,043✔
1363
                        (long)GC_bytes_found);
×
1364

1365
  /* Reconstruct free lists to contain everything not marked. */
1366
  GC_start_reclaim(FALSE);
31,043✔
1367

1368
#ifdef USE_MUNMAP
1369
  if (GC_unmap_threshold > 0    /*< memory unmapping enabled? */
31,043✔
1370
      && LIKELY(GC_gc_no != 1)) /*< do not unmap during `GC_init` */
31,043✔
1371
    GC_unmap_old(GC_unmap_threshold);
31,004✔
1372

1373
  GC_ASSERT(GC_heapsize >= GC_unmapped_bytes);
31,043✔
1374
#endif
1375
  GC_ASSERT(GC_our_mem_bytes >= GC_heapsize);
31,043✔
1376
  GC_DBGLOG_PRINTF(
31,043✔
1377
      "GC #%lu freed %ld bytes, heap %lu KiB (" IF_USE_MUNMAP(
1378
          "+ %lu KiB unmapped ") "+ %lu KiB internal)\n",
1379
      (unsigned long)GC_gc_no, (long)GC_bytes_found,
×
1380
      TO_KiB_UL(GC_heapsize - GC_unmapped_bytes) /*, */
×
1381
      COMMA_IF_USE_MUNMAP(TO_KiB_UL(GC_unmapped_bytes)),
×
1382
      TO_KiB_UL(GC_our_mem_bytes - GC_heapsize + sizeof(GC_arrays)));
×
1383
  GC_DBGLOG_PRINT_HEAP_IN_USE();
31,043✔
1384
  if (GC_is_full_gc) {
31,043✔
1385
    GC_used_heap_size_after_full = GC_heapsize - GC_large_free_bytes;
13,522✔
1386
    GC_need_full_gc = FALSE;
13,522✔
1387
  } else {
1388
    GC_need_full_gc = GC_heapsize - GC_used_heap_size_after_full
35,042✔
1389
                      > min_bytes_allocd() + GC_large_free_bytes;
17,521✔
1390
  }
1391

1392
  /* Reset or increment counters for next cycle. */
1393
  GC_n_attempts = 0;
31,043✔
1394
  GC_is_full_gc = FALSE;
31,043✔
1395
  GC_bytes_allocd_before_gc += GC_bytes_allocd;
31,043✔
1396
  GC_non_gc_bytes_at_gc = GC_non_gc_bytes;
31,043✔
1397
  GC_bytes_allocd = 0;
31,043✔
1398
  GC_bytes_dropped = 0;
31,043✔
1399
  GC_bytes_freed = 0;
31,043✔
1400
  GC_finalizer_bytes_freed = 0;
31,043✔
1401

1402
  if (GC_on_collection_event)
31,043✔
1403
    GC_on_collection_event(GC_EVENT_RECLAIM_END);
×
1404
#ifndef NO_CLOCK
1405
  if (GC_print_stats) {
31,043✔
1406
    CLOCK_TYPE done_time;
1407

1408
    GET_TIME(done_time);
×
1409
#  if !defined(SMALL_CONFIG) && !defined(GC_NO_FINALIZATION)
1410
    /* A convenient place to output finalization statistics. */
1411
    GC_print_finalization_stats();
×
1412
#  endif
1413
    GC_log_printf(
×
1414
        "Finalize and initiate sweep took %lu ms %lu ns + %lu ms %lu ns\n",
1415
        MS_TIME_DIFF(finalize_time, start_time),
×
1416
        NS_FRAC_TIME_DIFF(finalize_time, start_time),
×
1417
        MS_TIME_DIFF(done_time, finalize_time),
×
1418
        NS_FRAC_TIME_DIFF(done_time, finalize_time));
×
1419
  }
1420
#elif !defined(SMALL_CONFIG) && !defined(GC_NO_FINALIZATION)
1421
  if (GC_print_stats)
1422
    GC_print_finalization_stats();
1423
#endif
1424
}
31,043✔
1425

1426
/* Note: if `stop_func` is 0, then `GC_default_stop_func` is used instead. */
1427
STATIC GC_bool
1428
GC_try_to_collect_general(GC_stop_func stop_func, GC_bool force_unmap)
3,652✔
1429
{
1430
  GC_bool result;
1431
#ifdef USE_MUNMAP
1432
  unsigned old_unmap_threshold;
1433
#endif
1434
  IF_CANCEL(int cancel_state;)
1435

1436
  if (UNLIKELY(!GC_is_initialized))
3,652✔
1437
    GC_init();
×
1438
  if (GC_debugging_started)
3,652✔
1439
    GC_print_all_smashed();
190✔
1440
  GC_notify_or_invoke_finalizers();
3,652✔
1441
  LOCK();
3,652✔
1442
  if (force_unmap) {
3,652✔
1443
    /*
1444
     * Record current heap size to make heap growth more conservative
1445
     * afterwards (as if the heap is growing from zero size again).
1446
     */
1447
    GC_heapsize_at_forced_unmap = GC_heapsize;
105✔
1448
  }
1449
  DISABLE_CANCEL(cancel_state);
3,652✔
1450
#ifdef USE_MUNMAP
1451
  old_unmap_threshold = GC_unmap_threshold;
3,652✔
1452
  if (force_unmap || (GC_force_unmap_on_gcollect && old_unmap_threshold > 0))
3,652✔
1453
    GC_unmap_threshold = 1; /*< unmap as much as possible */
105✔
1454
#endif
1455
  /* Minimize junk left in my registers. */
1456
  GC_noop6(0, 0, 0, 0, 0, 0);
3,652✔
1457
  result = GC_try_to_collect_inner(stop_func != 0 ? stop_func
3,652✔
1458
                                                  : GC_default_stop_func);
1459
#ifdef USE_MUNMAP
1460
  /* Restore it. */
1461
  GC_unmap_threshold = old_unmap_threshold;
3,652✔
1462
#endif
1463
  RESTORE_CANCEL(cancel_state);
3,652✔
1464
  UNLOCK();
3,652✔
1465
  if (result) {
3,652✔
1466
    if (GC_debugging_started)
3,577✔
1467
      GC_print_all_smashed();
190✔
1468
    GC_notify_or_invoke_finalizers();
3,577✔
1469
  }
1470
  return result;
3,652✔
1471
}
1472

1473
/* Externally callable routines to invoke full, stop-the-world collection. */
1474

1475
GC_API int GC_CALL
1476
GC_try_to_collect(GC_stop_func stop_func)
×
1477
{
1478
  GC_ASSERT(NONNULL_ARG_NOT_NULL(stop_func));
×
1479
  return (int)GC_try_to_collect_general(stop_func, FALSE);
×
1480
}
1481

1482
GC_API void GC_CALL
1483
GC_gcollect(void)
3,547✔
1484
{
1485
  /*
1486
   * Zero is passed as stop_func to get `GC_default_stop_func` value
1487
   * while holding the allocator lock (to prevent data race).
1488
   */
1489
  (void)GC_try_to_collect_general(0, FALSE);
3,547✔
1490
  if (get_have_errors())
3,547✔
1491
    GC_print_all_errors();
54✔
1492
}
3,547✔
1493

1494
GC_API void GC_CALL
1495
GC_gcollect_and_unmap(void)
105✔
1496
{
1497
  /* Collect and force memory unmapping to OS. */
1498
  (void)GC_try_to_collect_general(GC_never_stop_func, TRUE);
105✔
1499
}
105✔
1500

1501
STATIC GC_on_os_get_mem_proc GC_on_os_get_mem = 0;
1502

1503
GC_API void GC_CALL
1504
GC_set_on_os_get_mem(GC_on_os_get_mem_proc fn)
3✔
1505
{
1506
  /* `fn` may be 0 (means no event notifier). */
1507
  LOCK();
3✔
1508
  GC_on_os_get_mem = fn;
3✔
1509
  UNLOCK();
3✔
1510
}
3✔
1511

1512
GC_API GC_on_os_get_mem_proc GC_CALL
1513
GC_get_on_os_get_mem(void)
3✔
1514
{
1515
  GC_on_os_get_mem_proc fn;
1516

1517
  READER_LOCK();
3✔
1518
  fn = GC_on_os_get_mem;
3✔
1519
  READER_UNLOCK();
3✔
1520
  return fn;
3✔
1521
}
1522

1523
GC_INNER ptr_t
1524
GC_os_get_mem(size_t bytes)
2,773✔
1525
{
1526
  ptr_t space;
1527

1528
  GC_ASSERT(I_HOLD_LOCK());
2,773✔
1529
  space = (ptr_t)GET_MEM(bytes); /*< `HBLKSIZE`-aligned */
2,773✔
1530
  if (GC_on_os_get_mem)
2,773✔
1531
    (*GC_on_os_get_mem)(space, bytes);
×
1532
  if (UNLIKELY(NULL == space))
2,773✔
1533
    return NULL;
×
1534
#ifdef USE_PROC_FOR_LIBRARIES
1535
  /* Add `HBLKSIZE`-aligned `GET_MEM`-generated block to `GC_our_memory`. */
1536
  if (GC_n_memory >= MAX_HEAP_SECTS)
1537
    ABORT("Too many GC-allocated memory sections: Increase MAX_HEAP_SECTS");
1538
  GC_our_memory[GC_n_memory].hs_start = space;
1539
  GC_our_memory[GC_n_memory].hs_bytes = bytes;
1540
  GC_n_memory++;
1541
#endif
1542
  GC_our_mem_bytes += bytes;
2,773✔
1543
  GC_VERBOSE_LOG_PRINTF("Got %lu bytes from OS\n", (unsigned long)bytes);
2,773✔
1544
  return space;
2,773✔
1545
}
1546

1547
/*
1548
 * Use the chunk of memory starting at `h` of size `sz` as part of the heap.
1549
 * Assumes `h` is `HBLKSIZE`-aligned, `sz` is a multiple of `HBLKSIZE`.
1550
 */
1551
STATIC void
1552
GC_add_to_heap(struct hblk *h, size_t sz)
987✔
1553
{
1554
  hdr *hhdr;
1555
  ptr_t endp;
1556
  size_t old_capacity = 0;
987✔
1557
  void *old_heap_sects = NULL;
987✔
1558
#ifdef GC_ASSERTIONS
1559
  size_t i;
1560
#endif
1561

1562
  GC_ASSERT(I_HOLD_LOCK());
987✔
1563
  GC_ASSERT(ADDR(h) % HBLKSIZE == 0);
987✔
1564
  GC_ASSERT(sz % HBLKSIZE == 0);
987✔
1565
  GC_ASSERT(sz > 0);
987✔
1566
  GC_ASSERT(GC_all_nils != NULL);
987✔
1567

1568
  if (UNLIKELY(GC_n_heap_sects == GC_capacity_heap_sects)) {
987✔
1569
    /* Allocate new `GC_heap_sects` with sufficient capacity. */
1570
#ifndef INITIAL_HEAP_SECTS
1571
#  define INITIAL_HEAP_SECTS 32
1572
#endif
1573
    size_t new_capacity
49✔
1574
        = GC_n_heap_sects > 0 ? GC_n_heap_sects * 2 : INITIAL_HEAP_SECTS;
49✔
1575
    void *new_heap_sects
1576
        = GC_scratch_alloc(new_capacity * sizeof(struct HeapSect));
49✔
1577

1578
    if (NULL == new_heap_sects) {
49✔
1579
      /* Retry with smaller yet sufficient capacity. */
1580
      new_capacity = GC_n_heap_sects + INITIAL_HEAP_SECTS;
×
1581
      new_heap_sects
1582
          = GC_scratch_alloc(new_capacity * sizeof(struct HeapSect));
×
1583
      if (NULL == new_heap_sects)
×
1584
        ABORT("Insufficient memory for heap sections");
×
1585
    }
1586
    old_capacity = GC_capacity_heap_sects;
49✔
1587
    old_heap_sects = GC_heap_sects;
49✔
1588
    /* Transfer `GC_heap_sects` contents to the newly allocated array. */
1589
    if (GC_n_heap_sects > 0)
49✔
1590
      BCOPY(old_heap_sects, new_heap_sects,
10✔
1591
            GC_n_heap_sects * sizeof(struct HeapSect));
1592
    GC_capacity_heap_sects = new_capacity;
49✔
1593
    GC_heap_sects = (struct HeapSect *)new_heap_sects;
49✔
1594
    GC_COND_LOG_PRINTF("Grew heap sections array to %lu elements\n",
49✔
1595
                       (unsigned long)new_capacity);
1596
  }
1597

1598
  while (UNLIKELY(ADDR(h) <= HBLKSIZE)) {
987✔
1599
    /* Cannot handle memory near address zero. */
1600
    ++h;
×
1601
    sz -= HBLKSIZE;
×
1602
    if (0 == sz)
×
1603
      return;
×
1604
  }
1605
  while (UNLIKELY(ADDR(h) >= GC_WORD_MAX - sz)) {
987✔
1606
    /* Prevent overflow when calculating `endp`. */
1607
    sz -= HBLKSIZE;
×
1608
    if (0 == sz)
×
1609
      return;
×
1610
  }
1611
  endp = (ptr_t)h + sz;
987✔
1612

1613
  hhdr = GC_install_header(h);
987✔
1614
  if (UNLIKELY(NULL == hhdr)) {
987✔
1615
    /*
1616
     * This is extremely unlikely. Cannot add it.  This will almost
1617
     * certainly result in a `NULL` returned from the allocator, which
1618
     * is entirely appropriate.
1619
     */
1620
    return;
×
1621
  }
1622
#ifdef GC_ASSERTIONS
1623
  /* Ensure no intersection between sections. */
1624
  for (i = 0; i < GC_n_heap_sects; i++) {
29,181✔
1625
    ptr_t hs_start = GC_heap_sects[i].hs_start;
28,194✔
1626
    ptr_t hs_end = hs_start + GC_heap_sects[i].hs_bytes;
28,194✔
1627

1628
    GC_ASSERT(!(ADDR_INSIDE((ptr_t)h, hs_start, hs_end)
28,194✔
1629
                || (ADDR_LT(hs_start, endp) && ADDR_GE(hs_end, endp))
1630
                || (ADDR_LT((ptr_t)h, hs_start) && ADDR_LT(hs_end, endp))));
1631
  }
1632
#endif
1633
  GC_heap_sects[GC_n_heap_sects].hs_start = (ptr_t)h;
987✔
1634
  GC_heap_sects[GC_n_heap_sects].hs_bytes = sz;
987✔
1635
  GC_n_heap_sects++;
987✔
1636
  hhdr->hb_block = h;
987✔
1637
  hhdr->hb_sz = sz;
987✔
1638
  hhdr->hb_flags = 0;
987✔
1639
  GC_freehblk(h);
987✔
1640
  GC_heapsize += sz;
987✔
1641

1642
  if (ADDR_GE((ptr_t)GC_least_plausible_heap_addr, (ptr_t)h)
987✔
1643
      || UNLIKELY(NULL == GC_least_plausible_heap_addr)) {
939✔
1644
    /*
1645
     * Making it a little smaller than necessary prevents us from
1646
     * getting a false hit from the variable itself.  There is some
1647
     * unintentional reflection here.
1648
     */
1649
    GC_least_plausible_heap_addr = (ptr_t)h - sizeof(ptr_t);
48✔
1650
  }
1651
  if (ADDR_LT((ptr_t)GC_greatest_plausible_heap_addr, endp)) {
987✔
1652
    GC_greatest_plausible_heap_addr = endp;
×
1653
  }
1654
#ifdef SET_REAL_HEAP_BOUNDS
1655
  if (ADDR(h) < GC_least_real_heap_addr
987✔
1656
      || UNLIKELY(0 == GC_least_real_heap_addr))
297✔
1657
    GC_least_real_heap_addr = ADDR(h) - sizeof(ptr_t);
729✔
1658
  if (GC_greatest_real_heap_addr < ADDR(endp)) {
987✔
1659
#  ifdef INCLUDE_LINUX_THREAD_DESCR
1660
    /* Avoid heap intersection with the static data roots. */
1661
    GC_exclude_static_roots_inner((ptr_t)h, endp);
1662
#  endif
1663
    GC_greatest_real_heap_addr = ADDR(endp);
67✔
1664
  }
1665
#endif
1666
  GC_handle_protected_regions_limit();
987✔
1667
  if (UNLIKELY(old_capacity > 0)) {
987✔
1668
#ifndef GWW_VDB
1669
    /*
1670
     * Recycling may call `GC_add_to_heap()` again but should not cause
1671
     * resizing of `GC_heap_sects`.
1672
     */
1673
    GC_scratch_recycle_no_gww(old_heap_sects,
10✔
1674
                              old_capacity * sizeof(struct HeapSect));
1675
#else
1676
    /* TODO: Implement GWW-aware recycling as in `alloc_mark_stack`. */
1677
    GC_noop1_ptr(old_heap_sects);
1678
#endif
1679
  }
1680
}
1681

1682
#ifndef NO_DEBUGGING
1683
void
1684
GC_print_heap_sects(void)
3✔
1685
{
1686
  size_t i;
1687

1688
  GC_printf("Total heap size: %lu" IF_USE_MUNMAP(" (%lu unmapped)") "\n",
3✔
1689
            (unsigned long)GC_heapsize /*, */
3✔
1690
                COMMA_IF_USE_MUNMAP((unsigned long)GC_unmapped_bytes));
3✔
1691

1692
  for (i = 0; i < GC_n_heap_sects; i++) {
304✔
1693
    ptr_t start = GC_heap_sects[i].hs_start;
301✔
1694
    size_t len = GC_heap_sects[i].hs_bytes;
301✔
1695
    unsigned nbl = 0;
301✔
1696
#  ifndef NO_BLACK_LISTING
1697
    struct hblk *h;
1698

1699
    for (h = (struct hblk *)start; ADDR_LT((ptr_t)h, start + len); h++) {
62,241✔
1700
      if (GC_is_black_listed(h, HBLKSIZE))
61,940✔
1701
        nbl++;
20✔
1702
    }
1703
#  endif
1704
    GC_printf("Section %u from %p to %p %u/%lu blacklisted\n", (unsigned)i,
301✔
1705
              (void *)start, (void *)&start[len], nbl,
301✔
1706
              (unsigned long)divHBLKSZ(len));
301✔
1707
  }
1708
}
3✔
1709
#endif /* !NO_DEBUGGING */
1710

1711
void *GC_least_plausible_heap_addr = MAKE_CPTR(GC_WORD_MAX);
1712
void *GC_greatest_plausible_heap_addr = NULL;
1713

1714
STATIC word GC_max_heapsize = 0;
1715

1716
GC_API void GC_CALL
1717
GC_set_max_heap_size(GC_word n)
2✔
1718
{
1719
  GC_max_heapsize = n;
2✔
1720
}
2✔
1721

1722
word GC_max_retries = 0;
1723

1724
GC_INNER void
1725
GC_scratch_recycle_inner(void *ptr, size_t sz)
240✔
1726
{
1727
  size_t page_offset;
1728
  size_t displ = 0;
240✔
1729
  size_t recycled_bytes;
1730

1731
  GC_ASSERT(I_HOLD_LOCK());
240✔
1732
  if (NULL == ptr)
240✔
1733
    return;
×
1734

1735
  GC_ASSERT(sz != 0);
240✔
1736
  GC_ASSERT(GC_page_size != 0);
240✔
1737
  /* TODO: Assert correct memory flags if `GWW_VDB`. */
1738
  page_offset = ADDR(ptr) & (GC_page_size - 1);
240✔
1739
  if (page_offset != 0)
240✔
1740
    displ = GC_page_size - page_offset;
4✔
1741
  recycled_bytes = sz > displ ? (sz - displ) & ~(GC_page_size - 1) : 0;
240✔
1742
  GC_COND_LOG_PRINTF("Recycle %lu/%lu scratch-allocated bytes at %p\n",
240✔
1743
                     (unsigned long)recycled_bytes, (unsigned long)sz, ptr);
1744
  if (recycled_bytes > 0)
240✔
1745
    GC_add_to_heap((struct hblk *)((ptr_t)ptr + displ), recycled_bytes);
230✔
1746
}
1747

1748
GC_INNER GC_bool
1749
GC_expand_hp_inner(word n)
777✔
1750
{
1751
  size_t sz;
1752
  struct hblk *space;
1753
  /* Number of bytes by which we expect the heap to expand soon. */
1754
  word expansion_slop;
1755

1756
  GC_ASSERT(I_HOLD_LOCK());
777✔
1757
  GC_ASSERT(GC_page_size != 0);
777✔
1758
  if (0 == n)
777✔
1759
    n = 1;
3✔
1760
  sz = ROUNDUP_PAGESIZE((size_t)n * HBLKSIZE);
777✔
1761
  GC_DBGLOG_PRINT_HEAP_IN_USE();
777✔
1762
  if (GC_max_heapsize != 0
777✔
1763
      && (GC_max_heapsize < (word)sz
24✔
1764
          || GC_heapsize > GC_max_heapsize - (word)sz)) {
4✔
1765
    /* Exceeded the self-imposed limit. */
1766
    return FALSE;
20✔
1767
  }
1768
  space = (struct hblk *)GC_os_get_mem(sz);
757✔
1769
  if (UNLIKELY(NULL == space)) {
757✔
1770
    WARN("Failed to expand heap by %" WARN_PRIuPTR " KiB\n", sz >> 10);
×
1771
    return FALSE;
×
1772
  }
1773
  GC_last_heap_growth_gc_no = GC_gc_no;
757✔
1774
  GC_INFOLOG_PRINTF("Grow heap to %lu KiB after %lu bytes allocated\n",
757✔
1775
                    TO_KiB_UL(GC_heapsize + sz),
×
1776
                    (unsigned long)GC_bytes_allocd);
×
1777

1778
  /*
1779
   * Adjust heap limits generously for black-listing to work better.
1780
   * `GC_add_to_heap()` performs minimal adjustment needed for correctness.
1781
   */
1782
  expansion_slop = min_bytes_allocd() + 4 * MAXHINCR * HBLKSIZE;
757✔
1783
  if ((0 == GC_last_heap_addr && (ADDR(space) & SIGNB) == 0)
757✔
1784
      || (GC_last_heap_addr != 0 && GC_last_heap_addr < ADDR(space))) {
718✔
1785
    /* Assume the heap is growing up. */
1786
    if (LIKELY(ADDR(space) < GC_WORD_MAX - (sz + expansion_slop))) {
108✔
1787
      ptr_t new_limit = (ptr_t)space + sz + expansion_slop;
54✔
1788

1789
      if (ADDR_LT((ptr_t)GC_greatest_plausible_heap_addr, new_limit))
54✔
1790
        GC_greatest_plausible_heap_addr = new_limit;
42✔
1791
    }
1792
  } else {
1793
    /* Heap is growing down. */
1794
    if (LIKELY(ADDR(space) > expansion_slop + sizeof(ptr_t))) {
703✔
1795
      ptr_t new_limit = (ptr_t)space - expansion_slop - sizeof(ptr_t);
703✔
1796

1797
      if (ADDR_LT(new_limit, (ptr_t)GC_least_plausible_heap_addr))
703✔
1798
        GC_least_plausible_heap_addr = new_limit;
593✔
1799
    }
1800
  }
1801
  GC_last_heap_addr = ADDR(space);
757✔
1802

1803
  GC_add_to_heap(space, sz);
757✔
1804
  if (GC_on_heap_resize)
757✔
1805
    (*GC_on_heap_resize)(GC_heapsize);
×
1806

1807
  return TRUE;
757✔
1808
}
1809

1810
GC_API int GC_CALL
1811
GC_expand_hp(size_t bytes)
5✔
1812
{
1813
  size_t n_blocks = OBJ_SZ_TO_BLOCKS_CHECKED(bytes);
5✔
1814
#ifndef NO_WARN_HEAP_GROW_WHEN_GC_DISABLED
1815
  word old_heapsize;
1816
#endif
1817
  GC_bool result;
1818

1819
  if (UNLIKELY(!GC_is_initialized))
5✔
1820
    GC_init();
×
1821
  LOCK();
5✔
1822
#ifndef NO_WARN_HEAP_GROW_WHEN_GC_DISABLED
1823
  old_heapsize = GC_heapsize;
5✔
1824
#endif
1825
  result = GC_expand_hp_inner(n_blocks);
5✔
1826
  if (result) {
5✔
1827
    GC_requested_heapsize += bytes;
5✔
1828
#ifndef NO_WARN_HEAP_GROW_WHEN_GC_DISABLED
1829
    if (GC_dont_gc) {
5✔
1830
      /* Do not call `WARN()` if the heap growth is intentional. */
1831
      GC_ASSERT(GC_heapsize >= old_heapsize);
3✔
1832
      GC_heapsize_on_gc_disable += GC_heapsize - old_heapsize;
3✔
1833
    }
1834
#endif
1835
  }
1836
  UNLOCK();
5✔
1837
  /*
1838
   * Really returns a `GC_bool` value, but the function is externally
1839
   * visible, so that is clumsy.
1840
   */
1841
  return (int)result;
5✔
1842
}
1843

1844
/*
1845
 * The minimum value of the ratio of allocated bytes since the latest
1846
 * collection to the amount of finalizers created since that collection
1847
 * which triggers the collection instead heap expansion.  Has no effect
1848
 * in the incremental mode.
1849
 */
1850
#ifdef GC_ALLOCD_BYTES_PER_FINALIZER
1851
STATIC word GC_allocd_bytes_per_finalizer = GC_ALLOCD_BYTES_PER_FINALIZER;
1852
#else
1853
STATIC word GC_allocd_bytes_per_finalizer = 10000;
1854
#endif
1855

1856
GC_API void GC_CALL
1857
GC_set_allocd_bytes_per_finalizer(GC_word value)
3✔
1858
{
1859
  GC_allocd_bytes_per_finalizer = value;
3✔
1860
}
3✔
1861

1862
GC_API GC_word GC_CALL
1863
GC_get_allocd_bytes_per_finalizer(void)
3✔
1864
{
1865
  return GC_allocd_bytes_per_finalizer;
3✔
1866
}
1867

1868
GC_INNER GC_bool
1869
GC_collect_or_expand(word needed_blocks, unsigned flags, GC_bool retry)
9,270✔
1870
{
1871
  static word last_fo_entries, last_bytes_finalized;
1872

1873
  GC_bool gc_not_stopped = TRUE;
9,270✔
1874
  word blocks_to_get;
1875
  IF_CANCEL(int cancel_state;)
1876

1877
  GC_ASSERT(I_HOLD_LOCK());
9,270✔
1878
  GC_ASSERT(GC_is_initialized);
9,270✔
1879
  DISABLE_CANCEL(cancel_state);
9,270✔
1880
  if (!GC_incremental && !GC_dont_gc
9,270✔
1881
      && ((GC_dont_expand && GC_bytes_allocd > 0)
8,617✔
1882
          || (GC_fo_entries > last_fo_entries
8,617✔
1883
              && (last_bytes_finalized | GC_bytes_finalized) != 0
478✔
1884
              && (GC_fo_entries - last_fo_entries)
447✔
1885
                         * GC_allocd_bytes_per_finalizer
447✔
1886
                     > GC_bytes_allocd)
447✔
1887
          || GC_should_collect())) {
8,173✔
1888
    /*
1889
     * Try to do a full collection using "default" `stop_func` (unless
1890
     * nothing has been allocated since the latest collection or heap
1891
     * expansion is disabled).
1892
     */
1893
    gc_not_stopped = GC_try_to_collect_inner(
8,537✔
1894
        GC_bytes_allocd > 0 && (!GC_dont_expand || !retry)
8,537✔
1895
            ? GC_default_stop_func
1896
            : GC_never_stop_func);
1897
    if (gc_not_stopped || !retry) {
8,537✔
1898
      /*
1899
       * Either the collection has not been aborted or this is the
1900
       * first attempt (in a loop).
1901
       */
1902
      last_fo_entries = GC_fo_entries;
8,537✔
1903
      last_bytes_finalized = GC_bytes_finalized;
8,537✔
1904
      RESTORE_CANCEL(cancel_state);
8,537✔
1905
      return TRUE;
9,270✔
1906
    }
1907
  }
1908

1909
  blocks_to_get = (GC_heapsize - GC_heapsize_at_forced_unmap)
733✔
1910
                      / (HBLKSIZE * GC_free_space_divisor)
733✔
1911
                  + needed_blocks;
1912
  if (blocks_to_get > MAXHINCR) {
733✔
1913
#ifdef NO_BLACK_LISTING
1914
    UNUSED_ARG(flags);
1915
    blocks_to_get = needed_blocks > MAXHINCR ? needed_blocks : MAXHINCR;
1916
#else
1917
    word slop;
1918

1919
    /*
1920
     * Get the minimum required to make it likely that we can satisfy
1921
     * the current request in the presence of black-listing.  This will
1922
     * probably be bigger than `MAXHINCR`.
1923
     */
1924
    if ((flags & IGNORE_OFF_PAGE) != 0) {
75✔
1925
      slop = 4;
×
1926
    } else {
1927
      slop = 2 * divHBLKSZ(BL_LIMIT);
75✔
1928
      if (slop > needed_blocks)
75✔
1929
        slop = needed_blocks;
55✔
1930
    }
1931
    if (needed_blocks + slop > MAXHINCR) {
75✔
1932
      blocks_to_get = needed_blocks + slop;
20✔
1933
    } else {
1934
      blocks_to_get = MAXHINCR;
55✔
1935
    }
1936
#endif
1937
    if (blocks_to_get > divHBLKSZ(GC_WORD_MAX))
75✔
1938
      blocks_to_get = divHBLKSZ(GC_WORD_MAX);
10✔
1939
  } else if (blocks_to_get < MINHINCR) {
658✔
1940
    blocks_to_get = MINHINCR;
139✔
1941
  }
1942

1943
  if (GC_max_heapsize > GC_heapsize) {
733✔
1944
    word max_get_blocks = divHBLKSZ(GC_max_heapsize - GC_heapsize);
20✔
1945
    if (blocks_to_get > max_get_blocks)
20✔
1946
      blocks_to_get
1947
          = max_get_blocks > needed_blocks ? max_get_blocks : needed_blocks;
20✔
1948
  }
1949

1950
#ifdef USE_MUNMAP
1951
  if (GC_unmap_threshold > 1) {
733✔
1952
    /*
1953
     * Return as much memory to the OS as possible before trying to
1954
     * get memory from it.
1955
     */
1956
    GC_unmap_old(0);
733✔
1957
  }
1958
#endif
1959
  if (!GC_expand_hp_inner(blocks_to_get)
733✔
1960
      && (blocks_to_get == needed_blocks
20✔
1961
          || !GC_expand_hp_inner(needed_blocks))) {
×
1962
    if (!gc_not_stopped) {
20✔
1963
      /* Do not increment `GC_alloc_fail_count` here (and no warning). */
1964
      GC_gcollect_inner();
×
1965
      GC_ASSERT(0 == GC_bytes_allocd);
×
1966
    } else if (GC_alloc_fail_count++ < GC_max_retries) {
20✔
1967
      WARN("Out of Memory!  Trying to continue...\n", 0);
×
1968
      GC_gcollect_inner();
×
1969
    } else {
1970
#ifdef USE_MUNMAP
1971
      GC_ASSERT(GC_heapsize >= GC_unmapped_bytes);
20✔
1972
#endif
1973
#if !defined(SMALL_CONFIG) && (CPP_WORDSZ >= 32)
1974
#  define MAX_HEAPSIZE_WARNED_IN_BYTES (5 << 20) /*< 5 MB */
1975

1976
      if (GC_heapsize > (word)MAX_HEAPSIZE_WARNED_IN_BYTES) {
20✔
1977
        WARN("Out of Memory! Heap size: %" WARN_PRIuPTR " MiB."
×
1978
             " Returning NULL!\n",
1979
             (GC_heapsize - GC_unmapped_bytes) >> 20);
1980
      } else
1981
#endif
1982
      /* else */ {
1983
        WARN("Out of Memory! Heap size: %" WARN_PRIuPTR " bytes."
20✔
1984
             " Returning NULL!\n",
1985
             GC_heapsize - GC_unmapped_bytes);
1986
      }
1987
      RESTORE_CANCEL(cancel_state);
20✔
1988
      return FALSE;
20✔
1989
    }
1990
  } else if (GC_alloc_fail_count > 0) {
713✔
1991
    GC_COND_LOG_PRINTF("Memory available again...\n");
×
1992
  }
1993
  RESTORE_CANCEL(cancel_state);
713✔
1994
  return TRUE;
713✔
1995
}
1996

1997
GC_INNER ptr_t
1998
GC_allocobj(size_t lg, int kind)
1,051,766✔
1999
{
2000
#define MAX_ALLOCOBJ_RETRIES 3
2001
  int retry_cnt = 0;
1,051,766✔
2002
  void **flh = &GC_obj_kinds[kind].ok_freelist[lg];
1,051,766✔
2003
#ifndef GC_DISABLE_INCREMENTAL
2004
  GC_bool tried_minor = FALSE;
1,051,766✔
2005
#endif
2006

2007
  GC_ASSERT(I_HOLD_LOCK());
1,051,766✔
2008
  GC_ASSERT(GC_is_initialized);
1,051,766✔
2009
  if (UNLIKELY(0 == lg))
1,051,766✔
2010
    return NULL;
×
2011

2012
  while (NULL == *flh) {
1,058,320✔
2013
    /*
2014
     * Only a few iterations are expected at most, otherwise something
2015
     * is wrong in one of the functions called below.
2016
     */
2017
    if (retry_cnt > MAX_ALLOCOBJ_RETRIES)
1,058,320✔
2018
      ABORT("Too many retries in GC_allocobj");
×
2019
#ifndef GC_DISABLE_INCREMENTAL
2020
    if (GC_incremental && GC_time_limit != GC_TIME_UNLIMITED && !GC_dont_gc) {
1,058,320✔
2021
      /*
2022
       * True incremental mode, not just generational.
2023
       * Do our share of marking work.
2024
       */
2025
      GC_collect_a_little_inner(1);
×
2026
    }
2027
#endif
2028
    /* Sweep blocks for objects of this size. */
2029
    GC_ASSERT(!GC_is_full_gc || NULL == GC_obj_kinds[kind].ok_reclaim_list
1,058,320✔
2030
              || NULL == GC_obj_kinds[kind].ok_reclaim_list[lg]);
2031
    GC_continue_reclaim(lg, kind);
1,058,320✔
2032
#if defined(CPPCHECK)
2033
    GC_noop1_ptr(&flh);
2034
#endif
2035
    if (*flh != NULL)
1,058,320✔
2036
      break;
204,633✔
2037

2038
    GC_new_hblk(lg, kind);
853,687✔
2039
#if defined(CPPCHECK)
2040
    GC_noop1_ptr(&flh);
2041
#endif
2042
    if (*flh != NULL)
853,687✔
2043
      break;
847,133✔
2044

2045
#ifndef GC_DISABLE_INCREMENTAL
2046
    if (GC_incremental && GC_time_limit == GC_TIME_UNLIMITED && !tried_minor
6,554✔
2047
        && !GC_dont_gc) {
377✔
2048
      GC_collect_a_little_inner(1);
313✔
2049
      tried_minor = TRUE;
313✔
2050
      continue;
313✔
2051
    }
2052
#endif
2053
    if (UNLIKELY(!GC_collect_or_expand(1, 0 /* `flags` */, retry_cnt > 0)))
6,241✔
2054
      return NULL;
×
2055
    retry_cnt++;
6,241✔
2056
  }
2057
  /* Successful allocation; reset failure count. */
2058
  GC_alloc_fail_count = 0;
1,051,766✔
2059
  return (ptr_t)(*flh);
1,051,766✔
2060
}
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