1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /** @file
3 * Wrapper for Boehm GC.
4 */
5 /* Authors:
6 * MenTaLguY <mental@rydia.net>
7 *
8 * Copyright (C) 2004 MenTaLguY
9 *
10 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
11 */
12
13 #include "inkgc/gc-core.h"
14 #include <stdexcept>
15 #include <cstring>
16 #include <string>
17 #include <glib.h>
18 #include <sigc++/functors/ptr_fun.h>
19 #include <glibmm/main.h>
20 #include <cstddef>
21
22 namespace Inkscape {
23 namespace GC {
24
25 namespace {
26
display_warning(char * msg,GC_word arg)27 void display_warning(char *msg, GC_word arg) {
28 g_warning(msg, arg);
29 }
30
do_init()31 void do_init() {
32 GC_set_no_dls(1);
33 GC_set_all_interior_pointers(1);
34 GC_set_finalize_on_demand(0);
35
36 GC_INIT();
37
38 GC_set_warn_proc(&display_warning);
39 }
40
debug_malloc(std::size_t size)41 void *debug_malloc(std::size_t size) {
42 return GC_debug_malloc(size, GC_EXTRAS);
43 }
44
debug_malloc_atomic(std::size_t size)45 void *debug_malloc_atomic(std::size_t size) {
46 return GC_debug_malloc_atomic(size, GC_EXTRAS);
47 }
48
debug_malloc_uncollectable(std::size_t size)49 void *debug_malloc_uncollectable(std::size_t size) {
50 return GC_debug_malloc_uncollectable(size, GC_EXTRAS);
51 }
52
debug_malloc_atomic_uncollectable(std::size_t size)53 void *debug_malloc_atomic_uncollectable(std::size_t size) {
54 return GC_debug_malloc_uncollectable(size, GC_EXTRAS);
55 }
56
compute_debug_base_fixup()57 std::ptrdiff_t compute_debug_base_fixup() {
58 char *base=reinterpret_cast<char *>(GC_debug_malloc(1, GC_EXTRAS));
59 char *real_base=reinterpret_cast<char *>(GC_base(base));
60 GC_debug_free(base);
61 return base - real_base;
62 }
63
debug_base_fixup()64 inline std::ptrdiff_t const &debug_base_fixup() {
65 static std::ptrdiff_t fixup=compute_debug_base_fixup();
66 return fixup;
67 }
68
debug_base(void * ptr)69 void *debug_base(void *ptr) {
70 char *base=reinterpret_cast<char *>(GC_base(ptr));
71 return base + debug_base_fixup();
72 }
73
debug_general_register_disappearing_link(void ** p_ptr,void const * base)74 int debug_general_register_disappearing_link(void **p_ptr, void const *base) {
75 char const *real_base = reinterpret_cast<char const *>(base) - debug_base_fixup();
76 #if (GC_MAJOR_VERSION >= 7 && GC_MINOR_VERSION >= 4)
77 return GC_general_register_disappearing_link(p_ptr, real_base);
78 #else // compatibility with older Boehm GC versions
79 return GC_general_register_disappearing_link(p_ptr, const_cast<char *>(real_base));
80 #endif
81 }
82
dummy_do_init()83 void dummy_do_init() {}
84
dummy_base(void *)85 void *dummy_base(void *) { return nullptr; }
86
dummy_register_finalizer(void *,CleanupFunc,void *,CleanupFunc * old_func,void ** old_data)87 void dummy_register_finalizer(void *, CleanupFunc, void *,
88 CleanupFunc *old_func, void **old_data)
89 {
90 if (old_func) {
91 *old_func = nullptr;
92 }
93 if (old_data) {
94 *old_data = nullptr;
95 }
96 }
97
dummy_general_register_disappearing_link(void **,void const *)98 int dummy_general_register_disappearing_link(void **, void const *) { return false; }
99
dummy_unregister_disappearing_link(void **)100 int dummy_unregister_disappearing_link(void **/*link*/) { return false; }
101
dummy_get_heap_size()102 std::size_t dummy_get_heap_size() { return 0; }
103
dummy_get_free_bytes()104 std::size_t dummy_get_free_bytes() { return 0; }
105
dummy_gcollect()106 void dummy_gcollect() {}
107
dummy_enable()108 void dummy_enable() {}
109
dummy_disable()110 void dummy_disable() {}
111
112 Ops enabled_ops = {
113 &do_init,
114 &GC_malloc,
115 &GC_malloc_atomic,
116 &GC_malloc_uncollectable,
117 &GC_malloc_atomic_uncollectable,
118 &GC_base,
119 &GC_register_finalizer_ignore_self,
120 #if (GC_MAJOR_VERSION >= 7 && GC_MINOR_VERSION >= 4)
121 &GC_general_register_disappearing_link,
122 #else // compatibility with older Boehm GC versions
123 (int (*)(void**, const void*))(&GC_general_register_disappearing_link),
124 #endif
125 &GC_unregister_disappearing_link,
126 &GC_get_heap_size,
127 &GC_get_free_bytes,
128 &GC_gcollect,
129 &GC_enable,
130 &GC_disable,
131 &GC_free
132 };
133
134 Ops debug_ops = {
135 &do_init,
136 &debug_malloc,
137 &debug_malloc_atomic,
138 &debug_malloc_uncollectable,
139 &debug_malloc_atomic_uncollectable,
140 &debug_base,
141 &GC_debug_register_finalizer_ignore_self,
142 &debug_general_register_disappearing_link,
143 &GC_unregister_disappearing_link,
144 &GC_get_heap_size,
145 &GC_get_free_bytes,
146 &GC_gcollect,
147 &GC_enable,
148 &GC_disable,
149 &GC_debug_free
150 };
151
152 Ops disabled_ops = {
153 &dummy_do_init,
154 &std::malloc,
155 &std::malloc,
156 &std::malloc,
157 &std::malloc,
158 &dummy_base,
159 &dummy_register_finalizer,
160 &dummy_general_register_disappearing_link,
161 &dummy_unregister_disappearing_link,
162 &dummy_get_heap_size,
163 &dummy_get_free_bytes,
164 &dummy_gcollect,
165 &dummy_enable,
166 &dummy_disable,
167 &std::free
168 };
169
170 class InvalidGCModeError : public std::runtime_error {
171 public:
InvalidGCModeError(const char * mode)172 InvalidGCModeError(const char *mode)
173 : runtime_error(std::string("Unknown GC mode \"") + mode + "\"")
174 {}
175 };
176
get_ops()177 Ops const &get_ops() {
178 char *mode_string=std::getenv("_INKSCAPE_GC");
179 if (mode_string) {
180 if (!std::strcmp(mode_string, "enable")) {
181 return enabled_ops;
182 } else if (!std::strcmp(mode_string, "debug")) {
183 return debug_ops;
184 } else if (!std::strcmp(mode_string, "disable")) {
185 return disabled_ops;
186 } else {
187 throw InvalidGCModeError(mode_string);
188 }
189 } else {
190 return enabled_ops;
191 }
192 }
193
die_because_not_initialized()194 void die_because_not_initialized() {
195 g_error("Attempt to use GC allocator before call to Inkscape::GC::init()");
196 }
197
stub_malloc(std::size_t)198 void *stub_malloc(std::size_t) {
199 die_because_not_initialized();
200 return nullptr;
201 }
202
stub_base(void *)203 void *stub_base(void *) {
204 die_because_not_initialized();
205 return nullptr;
206 }
207
stub_register_finalizer_ignore_self(void *,CleanupFunc,void *,CleanupFunc *,void **)208 void stub_register_finalizer_ignore_self(void *, CleanupFunc, void *,
209 CleanupFunc *, void **)
210 {
211 die_because_not_initialized();
212 }
213
stub_general_register_disappearing_link(void **,void const *)214 int stub_general_register_disappearing_link(void **, void const *) {
215 die_because_not_initialized();
216 return 0;
217 }
218
stub_unregister_disappearing_link(void **)219 int stub_unregister_disappearing_link(void **) {
220 die_because_not_initialized();
221 return 0;
222 }
223
stub_get_heap_size()224 std::size_t stub_get_heap_size() {
225 die_because_not_initialized();
226 return 0;
227 }
228
stub_get_free_bytes()229 std::size_t stub_get_free_bytes() {
230 die_because_not_initialized();
231 return 0;
232 }
233
stub_gcollect()234 void stub_gcollect() {
235 die_because_not_initialized();
236 }
237
stub_enable()238 void stub_enable() {
239 die_because_not_initialized();
240 }
241
stub_disable()242 void stub_disable() {
243 die_because_not_initialized();
244 }
245
stub_free(void *)246 void stub_free(void *) {
247 die_because_not_initialized();
248 }
249
250 }
251
252 Ops Core::_ops = {
253 nullptr,
254 &stub_malloc,
255 &stub_malloc,
256 &stub_malloc,
257 &stub_malloc,
258 &stub_base,
259 &stub_register_finalizer_ignore_self,
260 &stub_general_register_disappearing_link,
261 &stub_unregister_disappearing_link,
262 &stub_get_heap_size,
263 &stub_get_free_bytes,
264 &stub_gcollect,
265 &stub_enable,
266 &stub_disable,
267 &stub_free
268 };
269
init()270 void Core::init() {
271 try {
272 _ops = get_ops();
273 } catch (InvalidGCModeError &e) {
274 g_warning("%s; enabling normal collection", e.what());
275 _ops = enabled_ops;
276 }
277
278 _ops.do_init();
279 }
280
281
282 namespace {
283
284 bool collection_requested=false;
collection_task()285 bool collection_task() {
286 Core::gcollect();
287 Core::gcollect();
288 collection_requested=false;
289 return false;
290 }
291
292 }
293
request_early_collection()294 void request_early_collection() {
295 if (!collection_requested) {
296 collection_requested=true;
297 Glib::signal_idle().connect(sigc::ptr_fun(&collection_task));
298 }
299 }
300
301 }
302 }
303
304 /*
305 Local Variables:
306 mode:c++
307 c-file-style:"stroustrup"
308 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
309 indent-tabs-mode:nil
310 fill-column:99
311 End:
312 */
313 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
314