Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 1 | # Oilpan - Blink GC |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 2 | |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 3 | Oilpan is a garbage collector (GC) for Blink objects. |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 4 | This document explores the design, API and usage of the GC. |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 5 | |
| 6 | [TOC] |
| 7 | |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 8 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 9 | ## Overview |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 10 | |
Victor Costan | 9df64f2 | 2022-01-15 03:07:43 | [diff] [blame] | 11 | The general design of Oilpan is explained in the [Oilpan README](https://chromium.googlesource.com/v8/v8/+/main/include/cppgc/README.md). |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 12 | This section focuses on the Blink specific extensions to that design. |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 13 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 14 | ## Threading model |
| 15 | |
| 16 | Oilpan assumes heaps are not shared among threads. |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 17 | |
| 18 | Threads that want to allocate Oilpan objects must be *attached* to Oilpan, by calling either `blink::ThreadState::AttachMainThread()` or `blink::ThreadState::AttachCurrentThread()`. |
| 19 | |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 20 | Blink creates heaps and root sets (that directly refer to objects) per thread. |
| 21 | Matching a thread to its relevant heap is maintained by `blink::ThreadState`. |
| 22 | |
| 23 | An object belongs to the thread it is allocated on. |
| 24 | |
| 25 | Oilpan allows referring to objects from the same thread and cross-thread in various ways. |
| 26 | See [Handles](#Handles) for details. |
| 27 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 28 | ## Heap partitioning |
| 29 | |
| 30 | Blink assigns the following types to custom spaces in Oilpan's heap: |
| 31 | - Objects that are a collection backing are allocated in one of the collection backing *compactable* custom spaces. |
| 32 | - Objects that are a Node, a CSSValue or a LayoutObject are allocated in one of the typed custom spaces. |
| 33 | |
| 34 | ## Mode of operation |
| 35 | |
| 36 | - Blink uses concurrent garbage collection (marking and sweeping), except for during thread termination and heap destruction. |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 37 | - Blink tries to schedule GCs through the message loop where it is known that no objects are referred to from the native stack. |
| 38 | - When under memory pressure, Blink triggers a conservative GC on allocations. |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 39 | |
| 40 | # Oilpan API reference |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 41 | |
| 42 | ## Base class templates |
| 43 | |
| 44 | ### GarbageCollected |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 45 | <small>**Declared in:** `third_party/blink/renderer/platform/heap/garbage_collected.h`</small> |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 46 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 47 | A class that wants the lifetime of its instances to be managed by Oilpan must inherit from `GarbageCollected<T>`. |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 48 | |
| 49 | ```c++ |
| 50 | class YourClass : public GarbageCollected<YourClass> { |
Mounir Lamouri | 17426a8 | 2019-01-11 14:08:55 | [diff] [blame] | 51 | // ... |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 52 | }; |
| 53 | ``` |
| 54 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 55 | Instances of such classes are said to be *on Oilpan heap*, or *on heap* for short, while instances of other classes are called *off heap*. |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 56 | In the rest of this document, the terms *on heap*, *on-heap objects* or *garbage-collected objects* are used to mean the objects on Oilpan heap instead of on normal (default) dynamic allocator's heap space. |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 57 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 58 | Instances of a garbage-collected class can be created/allocated through `MakeGarbageCollected<T>`. |
| 59 | Declaring a class as garbage collected disallows the use of `operator new`. |
| 60 | It is not allowed to free garbage-collected object with `delete`, as Oilpan is responsible for deallocating the object once it determines the object is unreachable. |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 61 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 62 | Garbage-collected objects may not be allocated on stack. See [STACK_ALLOCATED](#STACK_ALLOCATED) and [Heap collections](#Heap-collections) for exceptions to this rule. |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 63 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 64 | Garbage-collected class may need to have a tracing method. See [Tracing](#Tracing) for details. |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 65 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 66 | Non-trivially destructible classes will be automatically finalized. |
Omer Katz | fdc3ad9 | 2019-11-20 16:19:49 | [diff] [blame] | 67 | Non-final classes that are not trivially destructible are required to have a virtual destructor. |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 68 | Trivially destructible classes should not have a destructor. |
| 69 | Adding a destructor to such classes would make them non-trivially destructible and would hinder performance. |
Anton Bikineev | e18d876 | 2019-09-10 15:43:04 | [diff] [blame] | 70 | Note that finalization is done at an arbitrary time after the object becomes unreachable. |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 71 | Any destructor executed within the finalization period *must not* touch any other on-heap object because destructors can be executed in any order. |
Anton Bikineev | e18d876 | 2019-09-10 15:43:04 | [diff] [blame] | 72 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 73 | `GarbageCollected<T>` or any class deriving from `GarbageCollected<T>`, directly or indirectly, must be the first element in its base class list (called "leftmost derivation rule"). |
| 74 | This rule is needed to assure each on-heap object has its own canonical address. |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 75 | |
| 76 | ```c++ |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 77 | class A : public GarbageCollected<A>, public P { |
| 78 | // OK: GarbageCollected<A> is leftmost. |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 79 | }; |
| 80 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 81 | class B : public A, public Q { |
| 82 | // OK: A is leftmost. |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 83 | }; |
| 84 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 85 | // class C : public R, public A { |
| 86 | // BAD: A must be the first base class. |
Omer Katz | fdc3ad9 | 2019-11-20 16:19:49 | [diff] [blame] | 87 | // If R is also garbage collected, A should be [GarbageCollectedMixin](#GarbageCollectedMixin). |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 88 | // }; |
| 89 | ``` |
| 90 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 91 | If a non-leftmost base class needs to retain an on-heap object, that base class needs to inherit from [GarbageCollectedMixin](#GarbageCollectedMixin). |
| 92 | It's generally recommended to make *any* non-leftmost base class inherit from `GarbageCollectedMixin` because it's dangerous to save a pointer to a non-leftmost non-`GarbageCollectedMixin` subclass of an on-heap object. |
yutak | d8f2cc2 | 2015-11-25 05:14:32 | [diff] [blame] | 93 | |
Omer Katz | fdc3ad9 | 2019-11-20 16:19:49 | [diff] [blame] | 94 | ```c++ |
| 95 | P* raw_pointer; |
| 96 | void someFunction(P* p) { |
| 97 | ... |
| 98 | raw_pointer = p; |
| 99 | ... |
| 100 | } |
| 101 | |
| 102 | class A : public GarbageCollected<A>, public P { |
| 103 | public: |
| 104 | void someMemberFunction() |
| 105 | { |
| 106 | someFunction(this); // DANGEROUS, a raw pointer to an on-heap object. Object might be collected, resulting in a dangling pointer and possible memory corruption. |
| 107 | } |
| 108 | }; |
| 109 | ``` |
| 110 | |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 111 | ### GarbageCollectedMixin |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 112 | <small>**Declared in:** `third_party/blink/renderer/platform/heap/garbage_collected.h`</small> |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 113 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 114 | A non-leftmost base class of a garbage-collected class should derive from `GarbageCollectedMixin`. |
yutak | d8f2cc2 | 2015-11-25 05:14:32 | [diff] [blame] | 115 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 116 | A class deriving from `GarbageCollectedMixin` can be treated similarly as garbage-collected classes. |
| 117 | Specifically, it can have `Member<T>`s and `WeakMember<T>`s, and a tracing method. |
| 118 | A pointer to such a class must be retained in the same smart pointer wrappers as a pointer to a garbage-collected class, such as `Member<T>` or `Persistent<T>`. |
Omer Katz | fdc3ad9 | 2019-11-20 16:19:49 | [diff] [blame] | 119 | The [tracing](#Tracing) method of a garbage-collected class, if any, must contain a delegating call for each mixin base class. |
yutak | d8f2cc2 | 2015-11-25 05:14:32 | [diff] [blame] | 120 | |
| 121 | ```c++ |
| 122 | class P : public GarbageCollectedMixin { |
Daniel Cheng | 0b5fcde | 2017-10-23 10:29:05 | [diff] [blame] | 123 | public: |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 124 | // OK: Needs to trace q_. |
Omer Katz | 052831f | 2020-05-17 03:54:06 | [diff] [blame] | 125 | virtual void Trace(Visitor* visitor) const { visitor->Trace(q_); } |
Daniel Cheng | 0b5fcde | 2017-10-23 10:29:05 | [diff] [blame] | 126 | private: |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 127 | // OK: Allowed to have Member<T>. |
Daniel Cheng | 0b5fcde | 2017-10-23 10:29:05 | [diff] [blame] | 128 | Member<Q> q_; |
yutak | d8f2cc2 | 2015-11-25 05:14:32 | [diff] [blame] | 129 | }; |
| 130 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 131 | class A final : public GarbageCollected<A>, public P { |
Daniel Cheng | 0b5fcde | 2017-10-23 10:29:05 | [diff] [blame] | 132 | public: |
| 133 | // Delegating call for P is needed. |
Omer Katz | 052831f | 2020-05-17 03:54:06 | [diff] [blame] | 134 | virtual void Trace(Visitor* visitor) const { P::Trace(visitor); } |
yutak | d8f2cc2 | 2015-11-25 05:14:32 | [diff] [blame] | 135 | }; |
| 136 | ``` |
| 137 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 138 | A class that is a descendant of `GarbageCollectedMixin` but not a descendant of `GarbageCollected<T>` cannot be instantiated.. |
yutak | d8f2cc2 | 2015-11-25 05:14:32 | [diff] [blame] | 139 | |
| 140 | ```c++ |
| 141 | class P : public GarbageCollectedMixin { }; |
| 142 | class Q : public GarbageCollectedMixin { }; |
| 143 | class R : public Q { }; |
| 144 | |
| 145 | class A : public GarbageCollected<A>, public P, public R { |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 146 | // OK: Resolving pure virtual functions of P and R. |
yutak | d8f2cc2 | 2015-11-25 05:14:32 | [diff] [blame] | 147 | }; |
| 148 | |
| 149 | class B : public GarbageCollected<B>, public P { |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 150 | // OK: Different garbage-collected classes may inherit from the same mixin (P). |
yutak | d8f2cc2 | 2015-11-25 05:14:32 | [diff] [blame] | 151 | }; |
| 152 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 153 | void foo() { |
| 154 | MakeGarbageCollected<A>(); // OK: A can be instantiated. |
| 155 | // MakeGarbageCollected<R>(); // BAD: R has pure virtual functions. |
yutak | d8f2cc2 | 2015-11-25 05:14:32 | [diff] [blame] | 156 | } |
| 157 | ``` |
| 158 | |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 159 | |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 160 | ## Class properties |
| 161 | |
yutak | 8779f554 | 2015-10-29 04:15:23 | [diff] [blame] | 162 | ### USING_PRE_FINALIZER |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 163 | <small>**Declared in:** `third_party/blink/renderer/platform/heap/prefinalizer.h`</small> |
yutak | 8779f554 | 2015-10-29 04:15:23 | [diff] [blame] | 164 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 165 | `USING_PRE_FINALIZER(ClassName, FunctionName)` in a class declaration declares the class has a *pre-finalizer* of name `FunctionName`. |
Michael Lippautz | c5eedf30 | 2019-07-11 09:13:41 | [diff] [blame] | 166 | A pre-finalizer must have the function signature `void()` but can have any name. |
yutak | 8779f554 | 2015-10-29 04:15:23 | [diff] [blame] | 167 | |
Michael Lippautz | c5eedf30 | 2019-07-11 09:13:41 | [diff] [blame] | 168 | A pre-finalizer is a user-defined member function of a garbage-collected class that is called when the object is going to be reclaimed. |
| 169 | It is invoked before the sweeping phase starts to allow a pre-finalizer to touch any other on-heap objects which is forbidden from destructors. |
| 170 | It is useful for doing cleanups that cannot be done with a destructor. |
yutak | 8779f554 | 2015-10-29 04:15:23 | [diff] [blame] | 171 | |
| 172 | ```c++ |
Anton Bikineev | e18d876 | 2019-09-10 15:43:04 | [diff] [blame] | 173 | class YourClass : public GarbageCollected<YourClass> { |
Michael Lippautz | c5eedf30 | 2019-07-11 09:13:41 | [diff] [blame] | 174 | USING_PRE_FINALIZER(YourClass, Dispose); |
yutak | 8779f554 | 2015-10-29 04:15:23 | [diff] [blame] | 175 | public: |
Michael Lippautz | c5eedf30 | 2019-07-11 09:13:41 | [diff] [blame] | 176 | void Dispose() { |
| 177 | // OK: Other on-heap objects can be touched in a pre-finalizer. |
| 178 | other_->Dispose(); |
Mounir Lamouri | 17426a8 | 2019-01-11 14:08:55 | [diff] [blame] | 179 | } |
Michael Lippautz | c5eedf30 | 2019-07-11 09:13:41 | [diff] [blame] | 180 | |
| 181 | ~YourClass() { |
| 182 | // BAD: Not allowed. |
| 183 | // other_->Dispose(); |
Mounir Lamouri | 17426a8 | 2019-01-11 14:08:55 | [diff] [blame] | 184 | } |
yutak | 8779f554 | 2015-10-29 04:15:23 | [diff] [blame] | 185 | |
| 186 | private: |
Mounir Lamouri | 17426a8 | 2019-01-11 14:08:55 | [diff] [blame] | 187 | Member<OtherClass> other_; |
yutak | 8779f554 | 2015-10-29 04:15:23 | [diff] [blame] | 188 | }; |
| 189 | ``` |
Michael Lippautz | c5eedf30 | 2019-07-11 09:13:41 | [diff] [blame] | 190 | Sometimes it is necessary to further delegate pre-finalizers up the class hierarchy, similar to how destructors destruct in reverse order wrt. to construction. |
| 191 | It is possible to construct such delegations using virtual methods. |
yutak | 8779f554 | 2015-10-29 04:15:23 | [diff] [blame] | 192 | |
Michael Lippautz | c5eedf30 | 2019-07-11 09:13:41 | [diff] [blame] | 193 | ```c++ |
Anton Bikineev | e18d876 | 2019-09-10 15:43:04 | [diff] [blame] | 194 | class Parent : public GarbageCollected<Parent> { |
Michael Lippautz | c5eedf30 | 2019-07-11 09:13:41 | [diff] [blame] | 195 | USING_PRE_FINALIZER(Parent, Dispose); |
| 196 | public: |
| 197 | void Dispose() { DisposeImpl(); } |
| 198 | |
| 199 | virtual void DisposeImpl() { |
| 200 | // Pre-finalizer for {Parent}. |
| 201 | } |
| 202 | // ... |
| 203 | }; |
| 204 | |
| 205 | class Child : public Parent { |
| 206 | public: |
| 207 | void DisposeImpl() { |
| 208 | // Pre-finalizer for {Child}. |
| 209 | Parent::DisposeImpl(); |
| 210 | } |
| 211 | // ... |
| 212 | }; |
| 213 | ``` |
| 214 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 215 | Alternatively, several classes in the same hierarchy may have pre-finalizers and these will also be executed in reverse order wrt. to construction. |
| 216 | |
| 217 | ```c++ |
| 218 | class Parent : public GarbageCollected<Parent> { |
| 219 | USING_PRE_FINALIZER(Parent, Dispose); |
| 220 | public: |
| 221 | void Dispose() { |
| 222 | // Pre-finalizer for {Parent}. |
| 223 | } |
| 224 | // ... |
| 225 | }; |
| 226 | |
| 227 | class Child : public Parent { |
| 228 | USING_PRE_FINALIZER(Child, Dispose); |
| 229 | public: |
| 230 | void Dispose() { |
| 231 | // Pre-finalizer for {Child}. |
| 232 | } |
| 233 | // ... |
| 234 | }; |
| 235 | ``` |
| 236 | |
Michael Lippautz | c5eedf30 | 2019-07-11 09:13:41 | [diff] [blame] | 237 | *Notes* |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 238 | - Pre-finalizers are not allowed to resurrect objects (i.e. they are not allowed to relink dead objects into the object graph). |
Michael Lippautz | c5eedf30 | 2019-07-11 09:13:41 | [diff] [blame] | 239 | - Pre-finalizers have some implications on the garbage collector's performance: the garbage-collector needs to iterate all registered pre-finalizers at every GC. |
| 240 | Therefore, a pre-finalizer should be avoided unless it is really necessary. |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 241 | Especially, avoid defining a pre-finalizer in a class that might be allocated a lot. |
Keishi Hattori | fde0d11 | 2017-10-03 07:31:03 | [diff] [blame] | 242 | |
Michael Lippautz | c5eedf30 | 2019-07-11 09:13:41 | [diff] [blame] | 243 | ### DISALLOW_NEW |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 244 | <small>**Declared in:** `third_party/blink/renderer/platform/wtf/allocator/allocator.h`</small> |
sigbjornf | 218c408 | 2017-02-06 12:03:11 | [diff] [blame] | 245 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 246 | Class-level annotation declaring the class cannot be separately allocated using `operator new` or `MakeGarbageCollected<T>`. |
| 247 | It can be used on stack, as an embedded part of some other object, or as a value in a heap collection. |
| 248 | If the class has `Member<T>` references, it needs a `Trace()` method, which must be called by the embedding object (e.g. the collection or the object that this class is a part of). |
sigbjornf | 218c408 | 2017-02-06 12:03:11 | [diff] [blame] | 249 | |
Keishi Hattori | fde0d11 | 2017-10-03 07:31:03 | [diff] [blame] | 250 | Classes with this annotation need a `Trace()` method, but should not inherit a garbage collected class. |
| 251 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 252 | ### STACK_ALLOCATED |
| 253 | <small>**Declared in:** `third_party/blink/renderer/platform/wtf/allocator/allocator.h`</small> |
| 254 | |
| 255 | Class level annotation that should be used if the class is only stack allocated. |
| 256 | Any fields holding garbage-collected objects should use raw pointers or references. |
| 257 | |
| 258 | Classes with this annotation do not need to define a `Trace()` method as they are on the stack, and are automatically traced and kept alive should a conservative GC be required. |
| 259 | |
| 260 | Stack allocated classes must not inherit a on-heap garbage collected class. |
| 261 | |
| 262 | Marking a class as STACK_ALLOCATED implicitly implies [DISALLOW_NEW](#DISALLOW_NEW), and thus disallow the use of `operator new` and `MakeGarbageCollected<T>`. |
sigbjornf | 218c408 | 2017-02-06 12:03:11 | [diff] [blame] | 263 | |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 264 | |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 265 | ## Handles |
| 266 | |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 267 | Class templates in this section are smart pointers, each carrying a pointer to an on-heap object (think of `scoped_refptr<T>` for `RefCounted<T>`, or `std::unique_ptr`). |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 268 | Collectively, they are called *handles*. |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 269 | |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 270 | On-heap objects must be retained by any of these, depending on the situation. |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 271 | |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 272 | ### Raw pointers |
| 273 | |
| 274 | Using raw pointers to garbage-collected objects on-heap is forbidden and will yield in memory corruptions. |
| 275 | On-stack references to garbage-collected object (including function parameters and return types), on the other hand, must use raw pointers. |
| 276 | This is the only case where raw pointers to on-heap objects should be used. |
| 277 | |
| 278 | ```c++ |
| 279 | void Foo() { |
| 280 | SomeGarbageCollectedClass* object = MakeGarbageCollected<SomeGarbageCollectedClass>(); // OK, retained by a raw pointer. |
| 281 | // ... |
| 282 | } |
| 283 | // OK to leave the object behind. The GC will reclaim it when it becomes unused. |
| 284 | ``` |
| 285 | |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 286 | ### Member, WeakMember |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 287 | <small>**Declared in:** `third_party/blink/renderer/platform/heap/member.h`</small> |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 288 | |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 289 | In a garbage-collected class, on-heap objects must be retained by `Member<T>` or `WeakMember<T>`, for strong or weak traced semantics, respectively. |
| 290 | Both references may only refer to objects that are owned by the same thread. |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 291 | |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 292 | `Member<T>` represents a *strong* reference to an object of type `T`, which means that the referred object is kept alive as long as the owner class instance is alive. |
| 293 | Unlike `scoped_refptr<T>`, it is okay to form a reference cycle with members (in on-heap objects) and raw pointers (on stack). |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 294 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 295 | `WeakMember<T>` is a *weak* reference to an object of type `T`. |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 296 | Unlike `Member<T>`, `WeakMember<T>` does not keep the pointed object alive. |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 297 | The pointer in a `WeakMember<T>` can become `nullptr` when the object gets garbage-collected. |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 298 | It may take some time for the pointer in a `WeakMember<T>` to become `nullptr` after the object actually becomes unreachable, because this rewrite is only done within Oilpan's garbage collection cycle. |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 299 | |
| 300 | ```c++ |
Johann Koenig | 0ccf642 | 2020-11-12 13:22:04 | [diff] [blame] | 301 | class SomeGarbageCollectedClass : public GarbageCollected<SomeGarbageCollectedClass> { |
Mounir Lamouri | 17426a8 | 2019-01-11 14:08:55 | [diff] [blame] | 302 | ... |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 303 | private: |
Mounir Lamouri | 17426a8 | 2019-01-11 14:08:55 | [diff] [blame] | 304 | Member<AnotherGarbageCollectedClass> another_; // OK, retained by Member<T>. |
| 305 | WeakMember<AnotherGarbageCollectedClass> anotherWeak_; // OK, weak reference. |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 306 | }; |
| 307 | ``` |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 308 | It is required that every `Member<T>` and `WeakMember<T>` in a garbage-collected class be traced. See [Tracing](#Tracing). |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 309 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 310 | The use of `WeakMember<T>` incurs some overhead in garbage collector's performance. |
| 311 | Use it sparingly. |
| 312 | Usually, weak members are not necessary at all, because reference cycles with members are allowed. |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 313 | More specifically, `WeakMember<T>` should be used only if the owner of a weak member can outlive the pointed object. |
| 314 | Otherwise, `Member<T>` should be used. |
| 315 | |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 316 | Generally `WeakMember<T>` requires to be converted into a strong reference to avoid reclamation until access. |
| 317 | The following example uses a raw pointer to create a strong reference on stack. |
| 318 | ```c++ |
| 319 | void Foo(WeakMember<T>& weak_object) { |
| 320 | T* proxy = weak_object.Get(); // OK, proxy will keep weak_object alive. |
| 321 | if (proxy) { |
| 322 | GC(); |
| 323 | proxy->Bar(); |
| 324 | } |
| 325 | } |
| 326 | ``` |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 327 | |
Omer Katz | fdc3ad9 | 2019-11-20 16:19:49 | [diff] [blame] | 328 | ### UntracedMember |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 329 | <small>**Declared in:** `third_party/blink/renderer/platform/heap/member.h`</small> |
Omer Katz | fdc3ad9 | 2019-11-20 16:19:49 | [diff] [blame] | 330 | |
| 331 | `UntracedMember<T>` represents a reference to a garbage collected object which is ignored by Oilpan. |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 332 | The reference may only refer to objects that are owned by the same thread. |
Omer Katz | fdc3ad9 | 2019-11-20 16:19:49 | [diff] [blame] | 333 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 334 | Unlike `Member<T>`, `UntracedMember<T>` will not keep an object alive. |
| 335 | However, unlike `WeakMember<T>`, the reference will not be cleared (i.e. set to `nullptr`) if the referenced object dies. |
Jacobo Aragunde Pérez | b524e47 | 2021-05-27 13:21:13 | [diff] [blame] | 336 | Furthermore, class fields of type `UntracedMember<T>` should not be traced by the class' tracing method. |
Omer Katz | fdc3ad9 | 2019-11-20 16:19:49 | [diff] [blame] | 337 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 338 | Users should avoid using `UntracedMember<T>` in any case other than when implementing [custom weakness semantics](#Custom-weak-callbacks). |
Omer Katz | fdc3ad9 | 2019-11-20 16:19:49 | [diff] [blame] | 339 | |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 340 | ### Persistent, WeakPersistent |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 341 | <small>**Declared in:** `third_party/blink/renderer/platform/heap/persistent.h`</small> |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 342 | |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 343 | In a non-garbage-collected class, on-heap objects must be retained by `Persistent<T>`, or `WeakPersistent<T>`. |
| 344 | Both references may only refer to objects that are owned by the same thread. |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 345 | |
| 346 | `Persistent<T>` is the most basic handle in the persistent family, which makes the referred object alive |
| 347 | unconditionally, as long as the persistent handle is alive. |
| 348 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 349 | `WeakPersistent<T>` does not keep the referred object alive, and becomes `nullptr` when the object gets |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 350 | garbage-collected, just like `WeakMember<T>`. |
| 351 | |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 352 | ```c++ |
Kent Tamura | ecf8ccc | 2018-09-12 02:13:43 | [diff] [blame] | 353 | #include "third_party/blink/renderer/platform/heap/persistent.h" |
| 354 | ... |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 355 | class NonGarbageCollectedClass { |
Mounir Lamouri | 17426a8 | 2019-01-11 14:08:55 | [diff] [blame] | 356 | ... |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 357 | private: |
Mounir Lamouri | 17426a8 | 2019-01-11 14:08:55 | [diff] [blame] | 358 | Persistent<SomeGarbageCollectedClass> something_; // OK, the object will be alive while this persistent is alive. |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 359 | }; |
| 360 | ``` |
| 361 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 362 | Persistents have a small unavoidable overhead because they require maintaining a list of all persistents. |
| 363 | Therefore, creating or keeping a lot of persistents at once would have performance implications and should be avoided when possible. |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 364 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 365 | Weak variants have overhead just like `WeakMember<T>`. |
| 366 | Use them sparingly. |
yutak | 49a8180 | 2015-10-20 12:24:04 | [diff] [blame] | 367 | |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 368 | *** |
| 369 | **Warning:** `Persistent<T>` is vulnerable to reference cycles. |
| 370 | If a reference cycle is formed with `Persistent`s, `Member`s, `RefPtr`s and `OwnPtr`s, all the objects in the cycle **will leak**, since |
| 371 | no object in the cycle can be aware of whether they are ever referred to from outside the cycle. |
| 372 | |
| 373 | Be careful not to create a reference cycle when adding a new persistent. |
| 374 | If a cycle is inevitable, make sure the cycle is eventually cut and objects become reclaimable. |
| 375 | *** |
| 376 | |
| 377 | ### CrossThreadPersistent, CrossThreadWeakPersistent |
| 378 | <small>**Declared in:** `third_party/blink/renderer/platform/heap/cross_thread_persistent.h`</small> |
| 379 | |
| 380 | `CrossThreadPersistent<T>` and `CrossThreadWeakPersistent<T>` are cross-thread variants of `Persistent<T>` and |
| 381 | `WeakPersistent<T>`, respectively, which can point to an object owned by a different thread (i.e. in a different heap). |
| 382 | |
| 383 | In general, Blink does not support shared-memory concurrency designs. |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 384 | The need of cross-thread persistents may indicate a poor design in multi-thread object ownership. |
| 385 | Think twice if they are really necessary. |
| 386 | |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 387 | Similar to `Persistent<T>`, `CrossThreadPersistent<T>` is prone to creating cycles that results in memory leaks. |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 388 | |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 389 | ### CrossThreadHandle, CrossThreadWeakHandle |
| 390 | <small>**Declared in:** `third_party/blink/renderer/platform/heap/cross_thread_handle.h`</small> |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 391 | |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 392 | `CrossThreadHandle<T>` and `CrossThreadWeakHandle<T>` are cross-thread variants of `Persistent<T>` and |
| 393 | `WeakPersistent<T>`, respectively, which can point to an object owned by a different thread (i.e. in a different heap). |
| 394 | The difference to `CrossThreadPersistent<T>` is that these handles prohibit deref on any thread except the thread that owns the object pointed to. |
| 395 | In other words, CrossThreadHandle<T> only support thread-safe copy, move, and destruction. |
| 396 | |
| 397 | The main use case for `CrossThreadHandle<T>` is delegating work to some other thread while retaining callbacks or other objects that are eventually used from the originating thread. |
| 398 | |
| 399 | Similar to `Persistent<T>`, `CrossThreadHandle<T>` is prone to creating cycles that results in memory leaks. |
| 400 | |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 401 | |
| 402 | ## Tracing |
| 403 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 404 | A garbage-collected class is required to have *a tracing method*, which lists all the references to on-heap objects it has. |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 405 | The tracing method is called when the garbage collector needs to determine (1) all the on-heap objects referred from a |
| 406 | live object, and (2) all the weak handles that may be filled with `nullptr` later. |
yutak | 7f3d7d9 | 2015-10-26 11:20:49 | [diff] [blame] | 407 | |
| 408 | The basic form of tracing is illustrated below: |
| 409 | |
| 410 | ```c++ |
| 411 | // In a header file: |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 412 | class SomeGarbageCollectedClass final |
Daniel Cheng | 0b5fcde | 2017-10-23 10:29:05 | [diff] [blame] | 413 | : public GarbageCollected<SomeGarbageCollectedClass> { |
| 414 | public: |
Omer Katz | 052831f | 2020-05-17 03:54:06 | [diff] [blame] | 415 | void Trace(Visitor*) const; |
yutak | 7f3d7d9 | 2015-10-26 11:20:49 | [diff] [blame] | 416 | |
| 417 | private: |
Daniel Cheng | 0b5fcde | 2017-10-23 10:29:05 | [diff] [blame] | 418 | Member<AnotherGarbageCollectedClass> another_; |
yutak | 7f3d7d9 | 2015-10-26 11:20:49 | [diff] [blame] | 419 | }; |
| 420 | |
| 421 | // In an implementation file: |
Omer Katz | 052831f | 2020-05-17 03:54:06 | [diff] [blame] | 422 | void SomeGarbageCollectedClass::Trace(Visitor* visitor) const { |
Daniel Cheng | 0b5fcde | 2017-10-23 10:29:05 | [diff] [blame] | 423 | visitor->Trace(another_); |
yutak | 7f3d7d9 | 2015-10-26 11:20:49 | [diff] [blame] | 424 | } |
| 425 | ``` |
| 426 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 427 | Specifically, if a class needs a tracing method, declaring and defining a `Trace(Visitor*) const` method is required. |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 428 | This method is normally declared in the header file and defined once in the implementation file, but there are variations. |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 429 | A common variation is to declare a virtual `Trace()` for base classes that will be subclassed. |
yutak | 7f3d7d9 | 2015-10-26 11:20:49 | [diff] [blame] | 430 | |
| 431 | The function implementation must contain: |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 432 | - For each reference tp an on-heap object `object` in the class, a tracing call: `visitor->Trace(object);`. |
| 433 | - For each embedded `DISALLOW_NEW` object `object` in the class, a tracing call: `visitor->Trace(object);`. |
| 434 | - For each base class of the class `BaseClass` that is a descendant of `GarbageCollected<T>` or `GarbageCollectedMixin`, a delegation call to base class: `BaseClass::Trace(visitor)`. |
yutak | 7f3d7d9 | 2015-10-26 11:20:49 | [diff] [blame] | 435 | It is recommended that the delegation call, if any, is put at the end of a tracing method. |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 436 | - See [Advanced weak handling](#Advanced%20Weak%20Handling) for implementing non-trivial weakness. |
yutak | 7f3d7d9 | 2015-10-26 11:20:49 | [diff] [blame] | 437 | |
yutak | 7f3d7d9 | 2015-10-26 11:20:49 | [diff] [blame] | 438 | The following example shows more involved usage: |
| 439 | |
| 440 | ```c++ |
| 441 | class A : public GarbageCollected<A> { |
Daniel Cheng | 0b5fcde | 2017-10-23 10:29:05 | [diff] [blame] | 442 | public: |
Omer Katz | 052831f | 2020-05-17 03:54:06 | [diff] [blame] | 443 | virtual void Trace(Visitor*) const {} // Nothing to trace here. |
yutak | 7f3d7d9 | 2015-10-26 11:20:49 | [diff] [blame] | 444 | }; |
| 445 | |
| 446 | class B : public A { |
Daniel Cheng | 0b5fcde | 2017-10-23 10:29:05 | [diff] [blame] | 447 | // Nothing to trace here; exempted from having a tracing method. |
yutak | 7f3d7d9 | 2015-10-26 11:20:49 | [diff] [blame] | 448 | }; |
| 449 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 450 | class C final : public B { |
Daniel Cheng | 0b5fcde | 2017-10-23 10:29:05 | [diff] [blame] | 451 | public: |
Omer Katz | 052831f | 2020-05-17 03:54:06 | [diff] [blame] | 452 | void Trace(Visitor*) const final; |
yutak | 7f3d7d9 | 2015-10-26 11:20:49 | [diff] [blame] | 453 | |
Daniel Cheng | 0b5fcde | 2017-10-23 10:29:05 | [diff] [blame] | 454 | private: |
| 455 | Member<X> x_; |
| 456 | WeakMember<Y> y_; |
| 457 | HeapVector<Member<Z>> z_; |
yutak | 7f3d7d9 | 2015-10-26 11:20:49 | [diff] [blame] | 458 | }; |
| 459 | |
Omer Katz | 052831f | 2020-05-17 03:54:06 | [diff] [blame] | 460 | void C::Trace(Visitor* visitor) const { |
Mounir Lamouri | 17426a8 | 2019-01-11 14:08:55 | [diff] [blame] | 461 | visitor->Trace(x_); |
| 462 | visitor->Trace(y_); // Weak member needs to be traced. |
| 463 | visitor->Trace(z_); // Heap collection does, too. |
| 464 | B::Trace(visitor); // Delegate to the parent. In this case it's empty, but this is required. |
yutak | 7f3d7d9 | 2015-10-26 11:20:49 | [diff] [blame] | 465 | } |
| 466 | ``` |
| 467 | |
Omer Katz | a756639 | 2022-04-06 18:15:20 | [diff] [blame] | 468 | Generally, a `Trace()` method would be just a sequence of delegations of the form `visitor->Trace(object);` for fields or `BaseClass::Trace(visitor)` for base classes. |
| 469 | It should avoid more complex logic such as branches or iterations. |
| 470 | In case a branch is unavoidable, the branch condition should depend only on const fields of the object. |
| 471 | In case an iteration is unavoidable, the number of iterations and the location of the buffer that is iterated should also depend only on const fields of the object. |
| 472 | Using non-const fields in `Trace()` methods may cause data races and other issues in the GC. |
| 473 | |
yutak | d560486 | 2015-10-19 10:26:44 | [diff] [blame] | 474 | ## Heap collections |
keishi | d4658a2 | 2016-12-05 03:28:46 | [diff] [blame] | 475 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 476 | Oilpan, like any other managed runtime library, provides basic support for collections that integrate its managed types `Member<T>` and `WeakMember<T>`. |
L. David Baron | 9379d0c8 | 2023-07-08 06:59:37 | [diff] [blame] | 477 | Do not use heap collection with persistent types (e.g. `HeapVector<Persistent<T>>`). |
keishi | d4658a2 | 2016-12-05 03:28:46 | [diff] [blame] | 478 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 479 | Collections compared to other libraries used in Blink: |
keishi | d4658a2 | 2016-12-05 03:28:46 | [diff] [blame] | 480 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 481 | | stdlib | WTF | Oilpan | |
| 482 | | ------------------ | ------------------- | ------------------------- | |
| 483 | | std::vector | WTF::Vector | blink::HeapVector | |
| 484 | | std::deque | WTF::Deque | blink::HeapDeque | |
| 485 | | std::unordered_map | WTF::HashMap | blink::HeapHashMap | |
| 486 | | std::unordered_set | WTF::HashSet | blink::HeapHashSet | |
| 487 | | - | WTF::LinkedHashSet | blink::HeapLinkedHashSet | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 488 | | - | WTF::HashCountedSet | blink::HeapHashCountedSet | |
keishi | d4658a2 | 2016-12-05 03:28:46 | [diff] [blame] | 489 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 490 | These heap collections work mostly the same way as their stdlib or WTF collection counterparts but there are some things to keep in mind. |
Matthew Denton | 678f25f | 2021-07-21 14:05:31 | [diff] [blame] | 491 | Heap collections are regular heap objects and thus must be properly traced from a `Trace` method. |
| 492 | They can also be inlined into other objects for performance reasons and are allowed to be directly used on stack. |
keishi | d4658a2 | 2016-12-05 03:28:46 | [diff] [blame] | 493 | |
| 494 | ```c++ |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 495 | class A final : public GarbageCollected<A> { |
Daniel Cheng | 0b5fcde | 2017-10-23 10:29:05 | [diff] [blame] | 496 | public: |
Omer Katz | 052831f | 2020-05-17 03:54:06 | [diff] [blame] | 497 | void Trace(Visitor* visitor) const { visitor->Trace(vec_); } |
Daniel Cheng | 0b5fcde | 2017-10-23 10:29:05 | [diff] [blame] | 498 | private: |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 499 | HeapVector<Member<B>> vec_; |
keishi | d4658a2 | 2016-12-05 03:28:46 | [diff] [blame] | 500 | }; |
| 501 | ``` |
| 502 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 503 | Like any other object, they may be referred to from a non-garbage-collected class using `Persistent`. |
keishi | d4658a2 | 2016-12-05 03:28:46 | [diff] [blame] | 504 | |
| 505 | ```c++ |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 506 | class NonGCed final { |
Daniel Cheng | 0b5fcde | 2017-10-23 10:29:05 | [diff] [blame] | 507 | private: |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 508 | Persistent<HeapVector<Member<B>>> vec_; |
keishi | d4658a2 | 2016-12-05 03:28:46 | [diff] [blame] | 509 | }; |
| 510 | ``` |
| 511 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 512 | ## Advanced weak handling |
Keishi Hattori | 15659fa | 2018-05-24 03:59:39 | [diff] [blame] | 513 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 514 | In addition to basic weak handling using `WeakMember<T>` Oilpan also supports: |
| 515 | - Weak collections |
| 516 | - Custom weak callbacks |
keishi | d4658a2 | 2016-12-05 03:28:46 | [diff] [blame] | 517 | |
| 518 | ### Weak collections |
| 519 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 520 | Like regular weakness, collections support weakness by putting references in `WeakMember<T>`. |
keishi | d4658a2 | 2016-12-05 03:28:46 | [diff] [blame] | 521 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 522 | In associative containers such as `HeapHashMap` or `HeapHashSet` Oilpan distinguishes between *pure weakness* and *mixed weakness*: |
| 523 | - Pure weakness: All entries in such containers are wrapped in `WeakMember<T>`. |
| 524 | Examples are `HeapHashSet<WeakMember<T>>` and `HeapHashMap<WeakMember<T>, WeakMember<U>>`. |
| 525 | - Mixed weakness: Only some entries in such containers are wrapped in `WeakMember<T>`. |
| 526 | This can only happen in `HeapHashMap`. |
Omer Katz | fdc3ad9 | 2019-11-20 16:19:49 | [diff] [blame] | 527 | Examples are `HeapHashMap<WeakMember<T>, Member<U>>, HeapHashMap<Member<T>, WeakMember<U>>, HeapHashMap<WeakMember<T>, int>, and HeapHashMap<int, WeakMember<T>>. |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 528 | Note that in the last example the type `int` is traced even though it does not support tracing. |
| 529 | |
| 530 | The semantics then are as follows: |
| 531 | - Pure weakness: Oilpan will automatically remove the entries from the container if any of its declared `WeakMember<T>` fields points to a dead object. |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 532 | - Mixed weakness: Oilpan applies ephemeron semantics for mixed weakness with `WeakMember<T>` and `Member<T>`. Mixing `WeakMember<T>` with non-tracable types results in pure weakness treatment for the corresponding entry. |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 533 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 534 | Weak references (e.g. `WeakMember<T>`) are not supported in sequential containers such as `HeapVector` or `HeapDeque`. |
| 535 | |
Michael Lippautz | 17bf72b | 2023-10-09 20:05:18 | [diff] [blame] | 536 | Iterators to weak collections keep their collections alive strongly. |
| 537 | This allows for the GC to kick in during weak collection iteration. |
| 538 | |
| 539 | ```c++ |
| 540 | template <typename Callback> |
| 541 | void IterateAndCall(HeapHashMapM<WeakMember<T>, WeakMember<U>>* hash_map, Callback callback) { |
| 542 | for (auto pair& : *hash_map) { |
| 543 | callback(pair.first, pair.second); // OK, will invoke callback(WeakMember<T>, WeakMember<U>). |
| 544 | } |
| 545 | } |
| 546 | ``` |
| 547 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 548 | ### Custom weak callbacks |
| 549 | |
| 550 | In case very specific weakness semantics are required Oilpan allows adding custom weakness callbacks through its tracing method. |
| 551 | |
| 552 | There exist two helper methods on `blink::Visitor` to add such callbacks: |
Michael Lippautz | f025c01d7 | 2020-04-21 09:47:20 | [diff] [blame] | 553 | - `RegisterWeakCallback`: Used to add custom weak callbacks of the form `void(void*, const blink::LivenessBroker&)`. |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 554 | - `RegisterWeakCallbackMethod`: Helper for adding an instance method. |
| 555 | |
Omer Katz | fdc3ad9 | 2019-11-20 16:19:49 | [diff] [blame] | 556 | Note that custom weak callbacks should not be used to clear `WeakMember<T>` fields as such fields are automatically handled by Oilpan. |
| 557 | Instead, for managed fields that require custom weakness semantics, users should wrap such fields in `UntracedMember<T>` indicating that Oilpan is ignoring those fields. |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 558 | |
| 559 | The following example shows how this can be used: |
| 560 | |
| 561 | ```c++ |
| 562 | |
| 563 | class W final : public GarbageCollected<W> { |
| 564 | public: |
Omer Katz | 052831f | 2020-05-17 03:54:06 | [diff] [blame] | 565 | virtual void Trace(Visitor*) const; |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 566 | private: |
Michael Lippautz | f025c01d7 | 2020-04-21 09:47:20 | [diff] [blame] | 567 | void ProcessCustomWeakness(const LivenessBroker&); |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 568 | |
| 569 | UntracedMember<C> other_; |
| 570 | }; |
| 571 | |
Omer Katz | 052831f | 2020-05-17 03:54:06 | [diff] [blame] | 572 | void W::Trace(Visitor* visitor) const { |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 573 | visitor->template RegisterCustomWeakMethod<W, &W::ProcessCustomWeakness>(this); |
| 574 | } |
| 575 | |
Michael Lippautz | f025c01d7 | 2020-04-21 09:47:20 | [diff] [blame] | 576 | void W::ProcessCustomWeakness(const LivenessBroker& info) { |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 577 | if (info.IsHeapObjectAlive(other_)) { |
| 578 | // Do something with other_. |
| 579 | } |
| 580 | other_ = nullptr; |
| 581 | } |
| 582 | ``` |
| 583 | |
| 584 | Note that the custom weakness callback in this example is only executed if `W` is alive and properly traced. |
| 585 | If `W` itself dies than the callback will not be executed. |
| 586 | Operations that must always happen should instead go into destructors or pre-finalizers. |
Raphael Kubo da Costa | 3146ed3 | 2018-01-15 15:34:07 | [diff] [blame] | 587 | |
| 588 | ## Traits helpers |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 589 | <small>**Declared in:** `third_party/blink/renderer/platform/heap/heap_traits.h`</small> |
Raphael Kubo da Costa | 3146ed3 | 2018-01-15 15:34:07 | [diff] [blame] | 590 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 591 | At times, one may be working on code that needs to deal with both, off heap and on heap, objects. |
| 592 | The following helpers can aid in writing code that needs to use different wrappers and containers based on whether a type is managed by Oilpan. |
Raphael Kubo da Costa | 3146ed3 | 2018-01-15 15:34:07 | [diff] [blame] | 593 | |
| 594 | ### AddMemberIfNeeded<T> |
| 595 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 596 | Given a type `T`, defines a type alias that is either `Member<T>` or `T` depending on whether `T` is a type managed by Oilpan. |
Raphael Kubo da Costa | 3146ed3 | 2018-01-15 15:34:07 | [diff] [blame] | 597 | |
| 598 | ```c++ |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 599 | class A final : public GarbageCollected<A> {}; |
| 600 | class B final {}; |
Raphael Kubo da Costa | 3146ed3 | 2018-01-15 15:34:07 | [diff] [blame] | 601 | |
Michael Lippautz | 0173f0a5 | 2019-11-11 12:47:25 | [diff] [blame] | 602 | AddMemberIfNeeded<B> v1; // B v1; |
| 603 | AddMemberIfNeeded<int32_t> v2; // int32_t v2; |
| 604 | AddMemberIfNeeded<A> v3; // Member<A> v3; |
Raphael Kubo da Costa | 3146ed3 | 2018-01-15 15:34:07 | [diff] [blame] | 605 | ``` |
| 606 | |
| 607 | ### VectorOf<T> |
| 608 | |
| 609 | Given a type `T`, defines a type alias that is either `HeapVector<T>`, `HeapVector<Member<T>>` or `Vector<T>` based on the following rules: |
| 610 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 611 | * `T` is a garbage collected type managed by Oilpan → `HeapVector<Member<T>>` |
| 612 | * `T` has a `Trace()` method but is not managed by Oilpan → `HeapVector<T>` (this is a rare case; IDL unions and dictionaries fall in this category, for example) |
Raphael Kubo da Costa | 3146ed3 | 2018-01-15 15:34:07 | [diff] [blame] | 613 | * All other cases → `Vector<T>` |
| 614 | |
| 615 | ```c++ |
| 616 | class MyGarbageCollectedClass : public GarbageCollected<MyGarbageCollectedClass> { |
| 617 | // ... |
| 618 | }; |
| 619 | |
| 620 | class MyNonGCButTraceableClass { |
| 621 | public: |
Omer Katz | 052831f | 2020-05-17 03:54:06 | [diff] [blame] | 622 | void Trace(Visitor* visitor) const { |
Raphael Kubo da Costa | 3146ed3 | 2018-01-15 15:34:07 | [diff] [blame] | 623 | // ... |
| 624 | } |
| 625 | }; |
| 626 | |
| 627 | class MyNotGarbageCollectedClass { |
| 628 | // ... |
| 629 | }; |
| 630 | |
| 631 | VectorOf<float> v1; // Vector<float> v1; |
| 632 | VectorOf<MyNotGarbageCollectedClass> v2; // Vector<MyNotGarbageCollectedClass> v2; |
| 633 | VectorOf<MyNonGCButTraceableClass> v3; // HeapVector<MyNonGCButTraceableClass> v3; |
| 634 | VectorOf<MyGarbageCollectedClass> v4; // HeapVector<Member<MyGarbageCollectedClass>> v4; |
| 635 | ``` |
| 636 | |
| 637 | ### VectorOfPairs<T, U> |
| 638 | |
| 639 | Similar to `VectorOf<T>`, but defines a type alias that is either `HeapVector<std::pair<V, X>>` (where `V` is either `T` or `Member<T>` and `X` is either `U` or `Member<U>`) or `Vector<std::pair<T, U>>`. |
| 640 | |
Omer Katz | fc0775208 | 2022-01-07 11:42:35 | [diff] [blame] | 641 | In other words, if either `T` or `U` needs to be wrapped in a `HeapVector` instead of a `Vector`, `VectorOfPairs` will use a `HeapVector<std::pair<>>` and wrap them with `Member<>` appropriately. |
| 642 | Otherwise, a `Vector<std::pair<>>` will be used. |
Raphael Kubo da Costa | 3146ed3 | 2018-01-15 15:34:07 | [diff] [blame] | 643 | |
| 644 | ```c++ |
| 645 | class MyGarbageCollectedClass : public GarbageCollected<MyGarbageCollectedClass> { |
| 646 | // ... |
| 647 | }; |
| 648 | |
| 649 | class MyNonGCButTraceableClass { |
| 650 | public: |
Omer Katz | 052831f | 2020-05-17 03:54:06 | [diff] [blame] | 651 | void Trace(Visitor* visitor) const { |
Raphael Kubo da Costa | 3146ed3 | 2018-01-15 15:34:07 | [diff] [blame] | 652 | // ... |
| 653 | } |
| 654 | }; |
| 655 | |
| 656 | class MyNotGarbageCollectedClass { |
| 657 | // ... |
| 658 | }; |
| 659 | |
| 660 | // Vector<std::pair<double, int8_t>> v1; |
| 661 | VectorOfPairs<double, int8_t> v1; |
| 662 | |
| 663 | // Vector<std::pair<MyNotGarbageCollectedClass, String>> v2; |
| 664 | VectorOfPairs<MyNotGarbageCollectedClass, String> v2; |
| 665 | |
| 666 | // HeapVector<std::pair<float, MyNonGCButTraceableClass>> v3; |
| 667 | VectorOfPairs<float, MyNonGCButTraceableClass> v3; |
| 668 | |
| 669 | // HeapVector<std::pair<MyNonGCButTraceableClass, Member<MyGarbageCollectedClass>>> v4; |
| 670 | VectorOfPairs<MyNonGCButTraceableClass, MyGarbageCollectedClass> v4; |
| 671 | ``` |