FinalizationObservers.h (7342B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef gc_FinalizationObservers_h 8 #define gc_FinalizationObservers_h 9 10 #include "gc/Barrier.h" 11 #include "gc/WeakMap.h" // For GetSymbolHash. 12 #include "gc/ZoneAllocator.h" 13 #include "js/GCHashTable.h" 14 #include "js/GCVector.h" 15 #include "js/Value.h" 16 #include "vm/NativeObject.h" 17 18 namespace js { 19 20 class FinalizationRegistryObject; 21 class FinalizationRecordObject; 22 class FinalizationQueueObject; 23 class WeakRefObject; 24 25 namespace gc { 26 27 JS::Zone* GetWeakTargetZone(const Value& value); 28 29 // ObserverList 30 // 31 // The following classes provide ObserverList, a circular doubly linked list of 32 // ObserverListObjects with weak, possibly-cross-zone pointers between the 33 // elements. Despite how bad this sounds this is OK because: 34 // 35 // 1) The list is only accessed from the main thread (no races) 36 // 2) The link pointers are weak (no pre barriers are required on update) 37 // 3) The elements don't escape 38 // 39 // These lists are used to hold the WeakRefObjects and FinalizationRecordObjects 40 // associated with a WeakRef or FinalizationRegistry target. They live in the 41 // target's zone although the elements themselves may be in any zone. 42 43 class ObserverListObject; 44 class ObserverList; 45 46 // Link pointers in a ObserverList. These are encoded as PrivateValues to allow 47 // storing them in object slots. They contain a tagged pointer to either an 48 // ObserverListObject or an ObserverList (the list head). 49 class ObserverListPtr { 50 Value value; 51 52 enum Kind : uintptr_t { ElementKind = 0, ListHeadKind = 1, KindMask = 1 }; 53 54 explicit ObserverListPtr(Value value); 55 ObserverListPtr(void* ptr, Kind kind); 56 57 Kind kind() const; 58 void* ptr() const; 59 60 template <typename F> 61 auto map(F&& func) const; 62 63 public: 64 MOZ_IMPLICIT ObserverListPtr(ObserverListObject* element); 65 MOZ_IMPLICIT ObserverListPtr(ObserverList* list); 66 67 static ObserverListPtr fromValue(Value value); 68 69 bool operator==(const ObserverListPtr& other) const { 70 return value == other.value; 71 } 72 bool operator!=(const ObserverListPtr& other) const { 73 return !(*this == other); 74 } 75 76 Value asValue() const { return value; } 77 78 bool isElement() const; 79 ObserverListObject* asElement() const; 80 ObserverList* asList() const; 81 82 ObserverListPtr getNext() const; 83 ObserverListPtr getPrev() const; 84 void setNext(ObserverListPtr next); 85 void setPrev(ObserverListPtr prev); 86 }; 87 88 // Base class for the elements of an ObserverList. 89 class ObserverListObject : public NativeObject { 90 using Ptr = ObserverListPtr; 91 92 Ptr getNext() const; 93 Ptr getPrev() const; 94 void setNext(Ptr next); 95 void setPrev(Ptr prev); 96 friend class ObserverListPtr; 97 friend class ObserverList; 98 99 static size_t objectMoved(JSObject* obj, JSObject* old); 100 void objectMovedFrom(ObserverListObject* old); 101 102 protected: 103 // These fields are weak and possibly cross-zone pointers. They must not be 104 // allowed to escape. 105 enum { NextSlot, PrevSlot, SlotCount }; 106 107 static const ClassExtension classExtension_; 108 109 public: 110 void unlink(); 111 bool isInList() const; 112 }; 113 114 // A circular doubly linked list of ObserverListObjects with weak references 115 // between them. 116 class ObserverList { 117 using Ptr = ObserverListPtr; 118 119 // These fields are weak and possibly cross-zone pointers. They must not be 120 // allowed to escape. 121 Ptr next; 122 Ptr prev; 123 124 Ptr getNext() const { return next; } 125 Ptr getPrev() const { return prev; } 126 void setNext(Ptr link); 127 void setPrev(Ptr link); 128 friend class ObserverListPtr; 129 130 public: 131 class Iter; 132 133 ObserverList(); 134 ~ObserverList(); 135 136 // The list must be relinked on move. 137 ObserverList(const ObserverList& other) = delete; 138 ObserverList& operator=(const ObserverList& other) = delete; 139 ObserverList(ObserverList&& other); 140 ObserverList& operator=(ObserverList&& other); 141 142 bool isEmpty() const; 143 ObserverListObject* getFirst() const; 144 145 Iter iter(); 146 147 void insertFront(ObserverListObject* obj); 148 }; 149 150 // A hasher for GC things used as WeakRef and FinalizationRegistry targets. Uses 151 // stable cell hashing, except for symbols where it uses the symbol's stored 152 // hash. 153 struct WeakTargetHasher { 154 using Key = HeapPtr<Value>; 155 using Lookup = Value; 156 157 static bool maybeGetHash(const Lookup& l, HashNumber* hashOut) { 158 if (l.isSymbol()) { 159 *hashOut = GetSymbolHash(l.toSymbol()); 160 return true; 161 } 162 return StableCellHasher<Cell*>::maybeGetHash(l.toGCThing(), hashOut); 163 } 164 static bool ensureHash(const Lookup& l, HashNumber* hashOut) { 165 if (l.isSymbol()) { 166 *hashOut = GetSymbolHash(l.toSymbol()); 167 return true; 168 } 169 return StableCellHasher<Cell*>::ensureHash(l.toGCThing(), hashOut); 170 } 171 static HashNumber hash(const Lookup& l) { 172 if (l.isSymbol()) { 173 return GetSymbolHash(l.toSymbol()); 174 } 175 return StableCellHasher<Cell*>::hash(l.toGCThing()); 176 } 177 static bool match(const Key& k, const Lookup& l) { 178 if (l.isSymbol()) { 179 return k.toSymbol() == l.toSymbol(); 180 } 181 return StableCellHasher<Cell*>::match(k.toGCThing(), l.toGCThing()); 182 } 183 }; 184 185 // Per-zone data structures to support FinalizationRegistry and WeakRef. 186 class FinalizationObservers { 187 // The set of all finalization registries in the associated zone. 188 using RegistrySet = 189 GCHashSet<HeapPtr<FinalizationRegistryObject*>, 190 StableCellHasher<HeapPtr<FinalizationRegistryObject*>>, 191 ZoneAllocPolicy>; 192 RegistrySet registries; 193 194 // A map from finalization registry targets in the associated zone to a list 195 // of finalization records representing registries that the target is 196 // registered with and their associated held values. The records may be in 197 // other zones. They are direct pointers and are not wrapped. 198 using RecordMap = GCHashMap<HeapPtr<Value>, ObserverList, WeakTargetHasher, 199 ZoneAllocPolicy>; 200 RecordMap recordMap; 201 202 using WeakRefMap = GCHashMap<HeapPtr<Value>, ObserverList, WeakTargetHasher, 203 ZoneAllocPolicy>; 204 WeakRefMap weakRefMap; 205 206 public: 207 explicit FinalizationObservers(Zone* zone); 208 ~FinalizationObservers(); 209 210 // FinalizationRegistry support: 211 bool addRegistry(Handle<FinalizationRegistryObject*> registry); 212 bool addRecord(HandleValue target, Handle<FinalizationRecordObject*> record); 213 void clearRecords(); 214 215 // WeakRef support: 216 bool addWeakRefTarget(Handle<Value> target, Handle<WeakRefObject*> weakRef); 217 void removeWeakRefTarget(Handle<Value> target, 218 Handle<WeakRefObject*> weakRef); 219 220 void traceWeakEdges(JSTracer* trc); 221 222 private: 223 void traceWeakFinalizationRegistryEdges(JSTracer* trc); 224 void traceWeakWeakRefEdges(JSTracer* trc); 225 void traceWeakWeakRefList(JSTracer* trc, ObserverList& weakRefs, 226 Value target); 227 bool shouldQueueFinalizationRegistryForCleanup(FinalizationQueueObject*); 228 }; 229 230 } // namespace gc 231 } // namespace js 232 233 namespace mozilla { 234 template <> 235 struct FallibleHashMethods<js::gc::WeakTargetHasher> 236 : public js::gc::WeakTargetHasher {}; 237 } // namespace mozilla 238 239 #endif // gc_FinalizationObservers_h