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 <cstdlib> |
21 | #include <memory> |
22 | |
23 | namespace immer { |
24 | |
25 | #ifdef __APPLE__ |
26 | #define IMMER_GC_REQUIRE_INIT 1 |
27 | #else |
28 | #define IMMER_GC_REQUIRE_INIT 0 |
29 | #endif |
30 | |
31 | #if IMMER_GC_REQUIRE_INIT |
32 | |
33 | namespace detail { |
34 | |
35 | template <int Dummy=0> |
36 | struct gc_initializer |
37 | { |
38 | gc_initializer() { GC_init(); } |
39 | static gc_initializer init; |
40 | }; |
41 | |
42 | template <int D> |
43 | gc_initializer<D> gc_initializer<D>::init {}; |
44 | |
45 | inline void gc_initializer_guard() |
46 | { |
47 | static gc_initializer<> init_ = gc_initializer<>::init; |
48 | (void) init_; |
49 | } |
50 | |
51 | } // namespace detail |
52 | |
53 | #define IMMER_GC_INIT_GUARD_ ::immer::detail::gc_initializer_guard() |
54 | |
55 | #else |
56 | |
57 | #define IMMER_GC_INIT_GUARD_ |
58 | |
59 | #endif // IMMER_GC_REQUIRE_INIT |
60 | |
61 | /*! |
62 | * Heap that uses a tracing garbage collector. |
63 | * |
64 | * @rst |
65 | * |
66 | * This heap uses the `Boehm's conservative garbage collector`_ under |
67 | * the hood. This is a tracing garbage collector that automatically |
68 | * reclaims unused memory. Thus, it is not needed to call |
69 | * ``deallocate()`` in order to release memory. |
70 | * |
71 | * .. admonition:: Dependencies |
72 | * :class: tip |
73 | * |
74 | * In order to use this header file, you need to make sure that |
75 | * Boehm's ``libgc`` is your include path and link to its binary |
76 | * library. |
77 | * |
78 | * .. caution:: Memory that is allocated with the standard ``malloc`` |
79 | * and ``free`` is not visible to ``libgc`` when it is looking for |
80 | * references. This means that if, let's say, you store a |
81 | * :cpp:class:`immer::vector` using a ``gc_heap`` inside a |
82 | * ``std::vector`` that uses a standard allocator, the memory of |
83 | * the former might be released automatically at unexpected times |
84 | * causing crashes. |
85 | * |
86 | * .. caution:: When using a ``gc_heap`` in combination with immutable |
87 | * containers, the destructors of the contained objects will never |
88 | * be called. It is ok to store containers inside containers as |
89 | * long as all of them use a ``gc_heap`` consistently, but storing |
90 | * other kinds of objects with relevant destructors |
91 | * (e.g. containers with reference counting or other kinds of |
92 | * *resource handles*) might cause memory leaks and other problems. |
93 | * |
94 | * .. _boehm's conservative garbage collector: https://github.com/ivmai/bdwgc |
95 | * |
96 | * @endrst |
97 | */ |
98 | class gc_heap |
99 | { |
100 | public: |
101 | static void* allocate(std::size_t n) |
102 | { |
103 | IMMER_GC_INIT_GUARD_; |
104 | auto p = GC_malloc(n); |
105 | if (IMMER_UNLIKELY(!p)) |
106 | throw std::bad_alloc{}; |
107 | return p; |
108 | } |
109 | |
110 | static void* allocate(std::size_t n, norefs_tag) |
111 | { |
112 | IMMER_GC_INIT_GUARD_; |
113 | auto p = GC_malloc_atomic(n); |
114 | if (IMMER_UNLIKELY(!p)) |
115 | throw std::bad_alloc{}; |
116 | return p; |
117 | } |
118 | |
119 | static void deallocate(std::size_t, void* data) |
120 | { |
121 | GC_free(data); |
122 | } |
123 | |
124 | static void deallocate(std::size_t, void* data, norefs_tag) |
125 | { |
126 | GC_free(data); |
127 | } |
128 | }; |
129 | |
130 | } // namespace immer |
131 | |