1 =======================================
2 How to make a C++ class cycle collected
3 =======================================
5 Should my class be cycle collected?
6 ===================================
8 First, you need to decide if your class should be cycle
9 collected. There are three main criteria:
11 * It can be part of a cycle of strong references, including
12 refcounted objects and JS. Usually, this happens when it can hold
13 alive and be held alive by cycle collected objects or JS.
15 * It must be refcounted.
17 * It must be single threaded. The cycle collector can only work with
18 objects that are used on a single thread. The main thread and DOM
19 worker and worklet threads each have their own cycle collectors.
21 If your class meets the first criteria but not the second, then
22 whatever class uniquely owns it should be cycle collected, assuming
23 that is refcounted, and this class should be traversed and unlinked as
26 The cycle collector supports both nsISupports and non-nsISupports
27 (known as "native" in CC nomenclature) refcounting. However, we do not
28 support native cycle collection in the presence of inheritance, if two
29 classes related by inheritance need different CC
30 implementations. (This is because we use QueryInterface to find the
31 right CC implementation for an object.)
33 Once you've decided to make a class cycle collected, there are a few
34 things you need to add to your implementation:
36 * Cycle collected refcounting. Special refcounting is needed so that
37 the CC can tell when an object is created, used, or destroyed, so
38 that it can determine if an object is potentially part of a garbage
41 * Traversal. Once the CC has decided an object **might** be garbage,
42 it needs to know what other cycle collected objects it holds strong
43 references to. This is done with a "traverse" method.
45 * Unlinking. Once the CC has decided that an object **is** garbage, it
46 needs to break the cycles by clearing out all strong references to
47 other cycle collected objects. This is done with an "unlink"
48 method. This usually looks very similar to the traverse method.
50 The traverse and unlink methods, along with other methods needed for
51 cycle collection, are defined on a special inner class object, called a
52 "participant", for performance reasons. The existence of the
53 participant is mostly hidden behind macros so you shouldn't need
56 Next, we'll go over what the declaration and definition of these parts
57 look like. (Spoiler: there are lots of `ALL_CAPS` macros.) This will
58 mostly cover the most common variants. If you need something slightly
59 different, you should look at the location of the declaration of the
60 macros we mention here and see if the variants already exist.
69 If your class inherits from nsISupports, you'll need to add
70 `NS_DECL_CYCLE_COLLECTING_ISUPPORTS` to the class declaration. This
71 will declare the QueryInterface (QI), AddRef and Release methods you
72 need to implement nsISupports, as well as the actual refcount field.
74 In the `.cpp` file for your class you'll have to define the
75 QueryInterface, AddRef and Release methods. The ins and outs of
76 defining the QI method is out-of-scope for this document, but you'll
77 need to use the special cycle collection variants of the macros, like
78 `NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION`. (This is because we use
79 the nsISupports system to define a special interface used to
80 dynamically find the correct CC participant for the object.)
82 Finally, you'll have to actually define the AddRef and Release methods
83 for your class. If your class is called `MyClass`, then you'd do this
84 with the declarations `NS_IMPL_CYCLE_COLLECTING_ADDREF(MyClass)` and
85 `NS_IMPL_CYCLE_COLLECTING_RELEASE(MyClass)`.
90 If your class does **not** inherit from nsISupports, you'll need to
91 add `NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING` to the class
92 declaration. This will give inline definitions for the AddRef and
93 Release methods, as well as the actual refcount field.
95 Cycle collector participant
96 ===========================
98 Next we need to declare and define the cycle collector
99 participant. This is mostly boilerplate hidden behind macros, but you
100 will need to specify which fields are to be traversed and unlinked
101 because they are strong references to cycle collected objects.
106 First, we need to add a declaration for the participant. As before,
107 let's say your class is `MyClass`.
109 The basic way to declare this for an nsISupports class is
110 `NS_DECL_CYCLE_COLLECTION_CLASS(MyClass)`.
112 If your class inherits from multiple classes that inherit from
113 nsISupports classes, say `Parent1` and `Parent2`, then you'll need to
114 use `NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(MyClass, Parent1)` to
115 tell the CC to cast to nsISupports via `Parent1`. You probably want to
116 pick the first class it inherits from. (The cycle collector needs to be
117 able to cast `MyClass*` to `nsISupports*`.)
119 Another situation you might encounter is that your nsISupports class
120 inherits from another cycle collected class `CycleCollectedParent`. In
121 that case, your participant declaration will look like
122 `NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MyClass,
123 CycleCollectedParent)`. (This is needed so that the CC can cast from
124 nsISupports down to `MyClass`.) Note that we do not support inheritance
125 for non-nsISupports classes.
127 If your class is non-nsISupports, then you'll need to use the `NATIVE`
128 family of macros, like
129 `NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(MyClass)`.
131 In addition to these modifiers, these different variations have
132 further `SCRIPT_HOLDER` variations which are needed if your class
133 holds alive JavaScript objects. This is because the tracing of JS
134 objects held alive by this class must be declared separately from the
135 tracing of C++ objects held alive by this class so that the garbage
136 collector can also use the tracing. For example,
137 `NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(MyClass,
140 There are also `WRAPPERCACHE` variants of the macros which you need to
141 use if your class is wrapper cached. These are effectively a
142 specialized form of `SCRIPT_HOLDER`, as a cached wrapper is a
143 JS object held alive by the C++ object. For example,
144 `NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS_AMBIGUOUS(MyClass,
147 There is yet another variant of these macros, `SKIPPABLE`. This
148 document won't go into detail here about how this works, but the basic
149 idea is that a class can tell the CC when it is definitely alive,
150 which lets the CC skip it. This is a very important optimization for
151 things like DOM elements in active documents, but a new class you are
152 making cycle collected is likely not common enough to worry about.
157 Finally, you must write the actual implementation of the CC
158 participant, in the .cpp file for your class. This will define the
159 traverse and unlink methods, and some other random helper
160 functions. In the simplest case, this can be done with a single macro
161 like this: `NS_IMPL_CYCLE_COLLECTION(MyClass, mField1, mField2,
162 mField3)`, where `mField1` and the rest are the names of the fields of
163 your class that are strong references to cycle collected
164 objects. There is some template magic that says how many common types
165 like RefPtr, nsCOMPtr, and even some arrays, should be traversed and
166 unlinked. There’s also a variant `NS_IMPL_CYCLE_COLLECTION_INHERITED`,
167 which you should use when there’s a parent class that is also cycle
168 collected, to ensure that fields of the parent class are traversed and
169 unlinked. The name of that parent class is passed in as the second
170 argument. If either of these work, then you are done. Your class is
171 now cycle collected. Note that this does not work for fields that are
174 However, if that doesn’t work, you’ll have to get into the details a
175 bit more. A good place to start is by copying the definition of
176 `NS_IMPL_CYCLE_COLLECTION`.
178 For a script holder method, you also need to define a trace method in
179 addition to the traverse and unlink, using
180 `NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN` and other similar
181 macros. You'll need to include all of the JS fields that your class
182 holds alive. The trace method will be used by the GC as well as the
183 CC, so if you miss something you can end up with use-after-free
184 crashes. You'll also need to call `mozilla::HoldJSObjects(this);` in
185 the ctor for your class, and `mozilla::DropJSObjects(this);` in the
186 dtor. This will register (and unregister) each instance of your object
187 with the JS runtime, to ensure that it gets traced properly. This
188 does not apply if you have a wrapper cached class that does not have
189 any additional JS fields, as nsWrapperCache deals with all of that