/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 |