Coverage Report

Created: 2025-07-12 06:42

/src/immer/immer/heap/gc_heap.hpp
Line
Count
Source (jump to first uncovered line)
1
//
2
// immer: immutable data structures for C++
3
// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
4
//
5
// This software is distributed under the Boost Software License, Version 1.0.
6
// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
7
//
8
9
#pragma once
10
11
#include <immer/config.hpp>
12
#include <immer/heap/tags.hpp>
13
14
#if IMMER_HAS_LIBGC
15
#include <gc/gc.h>
16
#else
17
#error "Using garbage collection requires libgc"
18
#endif
19
20
#include <cassert>
21
#include <cstdlib>
22
#include <exception>
23
#include <memory>
24
25
namespace immer {
26
27
#ifdef IMMER_GC_REQUIRE_INIT
28
#define IMMER_GC_REQUIRE_INIT_ IMMER_GC_REQUIRE_INIT
29
#elif defined __APPLE__
30
#define IMMER_GC_REQUIRE_INIT_ 1
31
#else
32
#define IMMER_GC_REQUIRE_INIT_ 0
33
#endif
34
35
#if IMMER_GC_REQUIRE_INIT_
36
37
namespace detail {
38
39
template <int Dummy = 0>
40
struct gc_initializer
41
{
42
    gc_initializer() { GC_INIT(); }
43
    static gc_initializer init;
44
};
45
46
template <int D>
47
gc_initializer<D> gc_initializer<D>::init{};
48
49
inline void gc_initializer_guard()
50
{
51
    static gc_initializer<> init_ = gc_initializer<>::init;
52
    (void) init_;
53
}
54
55
} // namespace detail
56
57
#define IMMER_GC_INIT_GUARD_ ::immer::detail::gc_initializer_guard()
58
59
#else
60
61
#define IMMER_GC_INIT_GUARD_
62
63
#endif // IMMER_GC_REQUIRE_INIT_
64
65
/*!
66
 * Heap that uses a tracing garbage collector.
67
 *
68
 * @rst
69
 *
70
 * This heap uses the `Boehm's conservative garbage collector`_ under
71
 * the hood.  This is a tracing garbage collector that automatically
72
 * reclaims unused memory.  Thus, it is not needed to call
73
 * ``deallocate()`` in order to release memory.
74
 *
75
 * .. admonition:: Dependencies
76
 *    :class: tip
77
 *
78
 *    In order to use this header file, you need to make sure that
79
 *    Boehm's ``libgc`` is your include path and link to its binary
80
 *    library.
81
 *
82
 * .. caution:: Memory that is allocated with the standard ``malloc``
83
 *    and ``free`` is not visible to ``libgc`` when it is looking for
84
 *    references.  This means that if, let's say, you store a
85
 *    :cpp:class:`immer::vector` using a ``gc_heap`` inside a
86
 *    ``std::vector`` that uses a standard allocator, the memory of
87
 *    the former might be released automatically at unexpected times
88
 *    causing crashes.
89
 *
90
 * .. caution:: When using a ``gc_heap`` in combination with immutable
91
 *    containers, the destructors of the contained objects will never
92
 *    be called.  It is ok to store containers inside containers as
93
 *    long as all of them use a ``gc_heap`` consistently, but storing
94
 *    other kinds of objects with relevant destructors
95
 *    (e.g. containers with reference counting or other kinds of
96
 *    *resource handles*) might cause memory leaks and other problems.
97
 *
98
 * .. _boehm's conservative garbage collector: https://github.com/ivmai/bdwgc
99
 *
100
 * @endrst
101
 */
102
class gc_heap
103
{
104
public:
105
    static void* allocate(std::size_t n)
106
3.20M
    {
107
3.20M
        IMMER_GC_INIT_GUARD_;
108
3.20M
        auto p = GC_malloc(n);
109
3.20M
        if (IMMER_UNLIKELY(!p))
110
0
            IMMER_THROW(std::bad_alloc{});
111
3.20M
        return p;
112
3.20M
    }
113
114
    static void* allocate(std::size_t n, norefs_tag)
115
0
    {
116
0
        IMMER_GC_INIT_GUARD_;
117
0
        auto p = GC_malloc_atomic(n);
118
0
        if (IMMER_UNLIKELY(!p))
119
0
            IMMER_THROW(std::bad_alloc{});
120
0
        return p;
121
0
    }
122
123
0
    static void deallocate(std::size_t, void* data) { GC_free(data); }
124
125
    static void deallocate(std::size_t, void* data, norefs_tag)
126
0
    {
127
0
        GC_free(data);
128
0
    }
129
};
130
131
} // namespace immer