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