diff options
Diffstat (limited to '')
-rw-r--r-- | doc/refcounting.txt | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/doc/refcounting.txt b/doc/refcounting.txt new file mode 100644 index 0000000..e409262 --- /dev/null +++ b/doc/refcounting.txt @@ -0,0 +1,136 @@ +Refcounting +----------- + +INTRODUCTION + +Many objects in Inkscape have lifecycles which are managed by +"reference counting". Each such object has a counter associated with it, +which is supposed to reflect the number of outstanding references to it. +When that counter falls to zero, the object can be freed. + +This releases the programmer from worrying about having freed an object +while somebody else is still using it (or someone else freeing while +you're using it!). Simply "ref" the object (increment the counter) to +stake your claim, and then "unref" it (decrement the counter) when you don't +need it anymore. The ultimate decision to free the object is made safely +behind the scenes. + +You should "ref" an object whenever you plan to hold onto it while +transferring control to another part of the code (which might otherwise +end up freeing the object out from under you). + +REFCOUNTING FUNCTIONS + +Ref and unref functions are provided to manipulate an object's refcount +(and perhaps make the final decision to free the object), but their names +will vary depending on the type of object. + +Examples include g_object_ref()/g_object_unref() (for most GObject-based +types), sp_object_ref()/sp_object_unref() (for SPObject-derived classes), +and GC::anchor()/GC::release (for garbage-collector managed objects deriving +from GC::Anchored -- more on that later). + +[ note: for code underneath the Inkscape namespace, you need only write + GC::anchor(), but in other code you will need to write + Inkscape::GC::anchor(), or import the GC namespace to your .cpp file + with: + + namespace GC = Inkscape::GC; + + Consider this encouragement to start writing new code in the Inkscape + namespace. ] + +REFCOUNTING POLICY + +Refcounted objects start with a reference count of one when they are +created. This means that you do not need to manually ref one that you've +just created. However, you will still be responsible for unreffing it when +you're done with it. + +This means that during the lifetime of an object, there should be N refs +and N+1 unrefs on it. If these become unbalanced, then you are likely to +experience either transient crashing bugs (the object gets freed while +someone is still using it) or memory leaks (the object never gets freed). + +As a rule, an object should be unreffed by the same class or compilation +unit that reffed it. Reffing or unreffing an object on someone else's behalf +is usually a recipe for confusion (and defeats the point of refcounting, +really). If you pass someone a pointer, they should be the ones responsible +for reffing it if they need to hold onto it. Similarly, you shouldn't try to +make the decision to unref it for them. + +When you've unreffed the last ref you know about, you should generally +assume that the object is now gone forever. + +CIRCULAR REFERENCES + +One disadvantage of reference counting is that a naive application of it +breaks down in the presence of objects that reference each other. Common +examples are elements in a doubly-linked list with "prev" and "next" +pointers, and nodes in a tree, where a parent keeps a list of children, and +children keep a pointer to their parent. If both cases, if there is a "ref" +in both directions, neither object can ever get freed. + +Because of this, circular data structures should be avoided when possible. +When they are necessary, try only "reffing" in one direction +(e.g. parent -> child) but not the other (e.g. child -> parent). + +This can sometimes be trickier than it sounds -- circular references don't +have to be direct to cause problems. A simple example of an indirect circular +reference would be a circular singly-linked list, where the "last" element +in the list points back to the "first". In that case, unidirectional +reffing isn't sufficient; you'd have no choice but to delegate ref handling to +some object which encapsuled the circular list, reffing and unreffing entries +as it added and removed them. + +ANCHORED OBJECTS + +As a rule of thumb, the garbage collector can see pointers in: + + * global/static variables in the program + + * local variables/parameters + + * objects derived from GC::Managed<> + + * STL containers using GC::Alloc<> + + * objects manually allocated with GC::SCANNED + +It cannot see pointers in: + + * global/static variables in shared libraries + + * objects not derived from GC::Managed<> + + * STL containers not using GC::Alloc<> + +Since a lot of important objects (e.g. gtkmm widgets or Glib collections) +fall into the latter category, I've provided the GC::Anchored class from +which garbage-collector managed classes can be derived if they may be +remembered in such places. As noted earlier, the associated ref and unref +functions are GC::anchor() and GC::release(), respectively. + +For most refcounted objects, a nonzero refcount means "this object is in +use", and a zero refcount means "this object is no longer in use, you can +free it now". For GC::Anchored objects, refcounting is merely an override +to the normal operation of the garbage collector, so the rules are relaxed +somewhat: a nonzero refcount merely means "the object is in use in places that +the garbage collector can't see", and a zero refcount asserts that "the garbage +collector can now see every use that matters". + +While GC::Anchored objects start with an initial refcount of one like +any other refcounted type, in most cases it's safe (and convenient) to +GC::release the object immediately upon creating it. This is because the +garbage collector can see references to the object from parameters or local +variables. Trust the collector. + +One final note: when code is converted from pure refcounting to garbage +collection with GC::Anchored, refs and unrefs between GC::Anchored objects +should be removed. Refs are no longer necessary, and when circular references +are present, reffing will lead to memory leaks. + +Normally (unlike pure refcounting) the collector has no problem with freeing +circular references, but GC::anchor()ing a reference the collector can +already see overrides the collector's judgement. + |