Decouple component class from plugin subsystem, remove component factory
[babeltrace.git] / doc / ref-counting.md
CommitLineData
e7b68776
JG
1# Babeltrace Object Reference Counting and Lifetime
2
3This document covers the rationale behind the design of Babeltrace's object
4lifetime management.
5
6Starting from Babeltrace 2.x, all publicly exposed objects inherit a common
7base: bt_object. This base provides a number of facilities to all objects, chief
8amongst which are lifetime management functions.
9
10The reference count of all public objects is controlled by invoking
11the `bt_get()` and `bt_put()` functions which respectively
12increment and decrement an object's reference count.
13
14As far as lifetime management in concerned, Babeltrace makes a clear
15distinction between regular objects, which have a single parent, and root
16objects, which don't.
17
18## The Problem
19
20Let us consider a problematic case to illustrate the need for this
21distinction.
22
23A user of the CTF Writer library declares a Trace, which *has* a
24Stream Class (the declaration of a stream) and that Stream Class, in
25turn, *has* an Event Class (the declaration of an event).
26
27Nothing prevents this user from releasing his reference on any one of
28these objects in any order. However, all objects in the "Trace <->
29Stream <-> Event" hierarchy can be retrieved from any other.
30
31For instance, the user could discard his reference on both the Event
32Class and the Stream Class, only keeping a reference on the Trace.
33From this Trace reference, Stream Classes can be enumerated, providing
34the user with a new reference to the Stream Class he discarded
35earlier. Event Classes can also be enumerated from Stream Classes,
36providing the user with references to the individual Event Classes.
37
38Conversely, the user could also hold a reference to an Event Class and
39retrieve its parent Stream Class. The Trace, in turn, can then be
40retrieved from the Stream Class.
41
42This example illustrates what could be interpreted as a circular
43reference dependency existing between these objects. Of course, if the
44objects in such a scenario were to hold references to each other (in
45both directions), we would be in presence of a circular ownership
46resulting in a leak of both objects as their reference counts would
47never reach zero.
48
49Nonetheless, the API must offer the guarantee that holding a node to any node of
50the graph keeps all other reachable nodes alive.
51
52## The Solution
53
54The scheme employed in Babeltrace to break this cycle consists in the
55"children" holding *Reverse Component References* to their parents. That
56is, in the context of CTF-IR, that Event Classes hold a reference to
57their Stream Class and Stream Classes hold a reference to their Trace.
58
59On the other hand, parents hold *Claiming Aggregation References* to
60their children. A claiming aggregation reference means that the
61object being referenced should not be deleted as long as the reference
62still exists. In this respect, it can be said that parents truly hold the
63ownership of their children, since they control their lifetime. Conversely,
64the reference counting mechanism is leveraged by children to notify parents
65that no other child indirectly exposes the parent.
66
67When a parented object's reference count reaches zero, it invokes
68`bt_put()` on its parent and does **not** free itself. However, from that
69point, the object depends on its parent to signal the moment when it
70can be safely reclaimed.
71
72The invocation of `bt_put()` by the last children holding a reference to its
73parent might trigger a cascade of `bt_put()` from child to parent. Eventually,
74a **root** object is reached. At that point, if this orphaned object's
75reference count reaches zero, the object will invoke the `destroy()` method
76defined by every one of its children as part of their base `struct bt_object`.
77The key point here is that the cascade of `destroy()` will necessarily originate
78from the root and propagate in pre-order to the children. These children will
79propagate the destruction to their own children before reclaiming their own
80memory. This ensures that a node's pointer to its parent is *always* valid
81since the parent has the responsibility of tearing-down their children before
82cleaning themselves-up.
83
84Assuming a reference to an object is *acquired* by calling `bt_get()` while its
85reference count is zero, the object will, in turn, acquire a reference on its
86parent using `bt_get()`. At that point, the child can be thought of as having
87converted its weak reference to its parent into a regular reference. That is
88why this reference is referred to as a *claiming* aggregation reference.
89
90## Caveats
91
92This scheme imposes a number of strict rules defining the relation
93between objects:
94
95 * Objects may only have one parent,
96 * Objects, beside the root, are only retrievable from their direct parent or
97 children.
98
99## Walking through an example
100
101The initial situation is rather simple. **User A** is holding a reference to a
102trace, **TC1**. As per the rules previously enounced, Stream Classes **SC1** and
103**SC2** don't hold a reference to **TC1** since their own reference counts are
104zero. The same holds true for **EC1**, **EC2** and **EC3** with respect to
105**SC1** and **SC2**.
106
107![](images/bt-ref01.svg)
108
109In this second step, we can see that User A has acquired a reference on **SC2**
110through the Trace, **TC1**.
111
112The Stream Class' reference count transitions from zero to one, triggering the
113acquisition of a strong reference on **TC1** from **SC2**.
114
115Hence, at this point, the Trace's ownership is shared by **User A** and
116**SC2**.
117
118![](images/bt-ref02.svg)
119
120Next, **User A** acquires a reference on the **EC3** Event Class through its
121parent Stream Class, **SC2**. Again, the transition of an object's reference
122count from 0 to 1 triggers the acquisition of a reference on its parent.
123
124Note that SC2's reference count was incremented to 2. The Trace's reference
125count remains unchanged.
126
127![](images/bt-ref03.svg)
128
129**User A** decides to drop its reference on **SC2**. **SC2**'s reference count
130returns back to 1, everything else remaining unchanged.
131
132![](images/bt-ref04.svg)
133
134**User A** can then decide to drop its reference on the Trace. This results in
135a reversal of the initial situation: **User A** now owns an event, **EC3**,
136which is keeping everything else alive and reachable.
137
138![](images/bt-ref05.svg)
139
140If another object, **User B**, enters the picture and acquires a reference on
141the **SC1** Stream Class, we see that **SC1**'s reference count
142transitioned from 0 to 1, triggering the acquisition of a reference on **TC1**.
143
144![](images/bt-ref06.svg)
145
146**User B** hands off a reference to **EC1**, acquired through **SC1**, to
147another object, **User C**. The acquisition of a reference on **EC1**, which
148transitions from 0 to 1, triggers the acquisition of a reference on its parent,
149**SC1**.
150
151![](images/bt-ref07.svg)
152
153At some point, **User A** releases its reference on **EC3**. Since **EC3**'s
154reference count transitions to zero, it releases its reference on **SC2**.
155**SC2**'s reference count, in turn, reaches zero and it releases its reference
156to **TC1**.
157
158**TC1**'s reference count is now 1 and no further action is taken.
159
160![](images/bt-ref08.svg)
161
162**User B** releases its reference on **SC1**. **User C** becomes the sole owner
163of the whole hierarchy through his ownership of **EC1**.
164
165![](images/bt-ref09.svg)
166
167Finally, **User C** releases his ownership of **EC1**, triggering the release of
168the whole hierarchy. We will walk through the reclamation of the whole graph.
169
170Mirroring what happened when **User A** released its last reference on **EC3**,
171the release of **EC1** by **User C** causes its reference count to fall to zero.
172
173This transition to zero causes **EC1** to release its reference on **SC1**.
174**SC1**'s reference count reaching zero causes it to release its reference on
175**TC1**.
176
177![](images/bt-ref10.svg)
178
179Since the reference count of **TC1**, a root object, has reached zero, it
180invokes the `destroy()` method on its children. This method is recursive and
181causes the Stream Classes to call the `destroy()` method on their Event Classes.
182
183The Event Classes are reached and, having no children of their own, are
184reclaimed.
185
186![](images/bt-ref11.svg)
187
188The Stream Classes having destroyed their children, are then reclaimed by the
189Trace.
190
191![](images/bt-ref12.svg)
192
193Finally, the Stream Classes having been reclaimed, **TC1** is reclaimed.
194
195![](images/bt-ref13.svg)
This page took 0.033207 seconds and 4 git commands to generate.