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