1 /*
2 * Inkscape::GC::Finalized - mixin for GC-managed objects with non-trivial
3 * destructors
4 *
5 * Copyright 2004 MenTaLguY <mental@rydia.net>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * See the file COPYING for details.
13 *
14 */
16 #ifndef SEEN_INKSCAPE_GC_FINALIZED_H
17 #define SEEN_INKSCAPE_GC_FINALIZED_H
19 #include <new>
20 #include "gc-core.h"
22 namespace Inkscape {
24 namespace GC {
26 /* @brief a mix-in ensuring that a object's destructor will get called before
27 * the garbage collector destroys it
28 *
29 * Normally, the garbage collector does not call destructors before destroying
30 * an object. On construction, this "mix-in" will register a finalizer
31 * function to call destructors before derived objects are destroyed.
32 *
33 * This works pretty well, with the following caveats:
34 *
35 * 1. The garbage collector uses strictly topologically-ordered
36 * finalization; if objects with finalizers reference each other
37 * directly or indirectly, the collector will refuse to finalize (and
38 * therefor free) them. You'll see a warning on the console if this
39 * happens.
40 *
41 * The best way to limit this effect is to only make "leaf" objects
42 * (i.e. those that don't point to other finalizaable objects)
43 * finalizable, and otherwise use GC::soft_ptr<> instead of a regular
44 * pointer for "backreferences" (e.g. parent pointers in a tree
45 * structure), so that those references can be cleared to break any
46 * finalization cycles.
47 *
48 * @see Inkscape::GC::soft_ptr<>
49 *
50 * 2. Because there is no guarantee when the collector will destroy
51 * objects, there is no guarantee when the destructor will get called.
52 *
53 * It may not get called until the very end of the program, or ever.
54 *
55 * 3. If allocated in arrays, only the first object in the array will
56 * have its destructor called, unless you make other arrangements by
57 * registering your own finalizer instead.
58 *
59 * 4. Similarly, making multiple GC::Finalized-derived objects members
60 * of a non-finalized but garbage-collected object generally won't
61 * work unless you take care of registering finalizers yourself.
62 *
63 * [n.b., by "member", I mean an actual by-value-member of a type that
64 * derives from GC::Finalized, not simply a member that's a pointer or a
65 * reference to such a type]
66 *
67 */
68 class Finalized {
69 public:
70 Finalized() {
71 void *base=Core::base(this);
72 if (base) { // only if we are managed by the collector
73 CleanupFunc old_cleanup;
74 void *old_data;
76 // the finalization callback needs to know the value of 'this'
77 // to call the destructor, but registering a real pointer to
78 // ourselves would pin us forever and prevent us from being
79 // finalized; instead we use an offset-from-base-address
81 Core::register_finalizer_ignore_self(base, _invoke_dtor,
82 _offset(base, this),
83 &old_cleanup, &old_data);
85 if (old_cleanup) {
86 // If there was already a finalizer registered for our
87 // base address, there are two main possibilities:
88 //
89 // 1. one of our members is also a GC::Finalized and had
90 // already registered a finalizer -- keep ours, since
91 // it will call that member's destructor, too
92 //
93 // 2. someone else registered a finalizer and we will have
94 // to trust that they will call the destructor -- put
95 // the existing finalizer back
96 //
97 // It's also possible that a member's constructor was called
98 // after ours (e.g. via placement new). Don't do that.
100 if ( old_cleanup != _invoke_dtor ) {
101 Core::register_finalizer_ignore_self(base,
102 old_cleanup, old_data,
103 NULL, NULL);
104 }
105 }
106 }
107 }
109 virtual ~Finalized() {
110 // make sure the destructor won't get invoked twice
111 Core::register_finalizer_ignore_self(Core::base(this),
112 NULL, NULL, NULL, NULL);
113 }
115 private:
116 /// invoke the destructor for an object given a base and offset pair
117 static void _invoke_dtor(void *base, void *offset);
119 /// turn 'this' pointer into an offset-from-base-address (stored as void *)
120 static void *_offset(void *base, Finalized *self) {
121 return reinterpret_cast<void *>(
122 reinterpret_cast<char *>(self) - reinterpret_cast<char *>(base)
123 );
124 }
125 /// reconstitute 'this' given an offset-from-base-address in a void *
126 static Finalized *_unoffset(void *base, void *offset) {
127 return reinterpret_cast<Finalized *>(
128 reinterpret_cast<char *>(base) +
129 reinterpret_cast<std::ptrdiff_t>(offset)
130 );
131 }
132 };
134 }
136 }
138 #endif
139 /*
140 Local Variables:
141 mode:c++
142 c-file-style:"stroustrup"
143 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
144 indent-tabs-mode:nil
145 fill-column:99
146 End:
147 */
148 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :