GCContext.h (7990B)
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_GCContext_h 8 #define gc_GCContext_h 9 10 #include "mozilla/Assertions.h" // MOZ_ASSERT 11 #include "mozilla/ThreadLocal.h" 12 13 #include "jspubtd.h" 14 #include "jstypes.h" // JS_PUBLIC_API 15 #include "gc/GCEnum.h" // js::MemoryUse 16 #include "jit/ExecutableAllocator.h" // jit::JitPoisonRangeVector 17 #include "js/Utility.h" // js_free 18 19 struct JS_PUBLIC_API JSRuntime; 20 21 namespace js { 22 23 class AutoTouchingGrayThings; 24 25 namespace gc { 26 27 class AutoSetThreadGCUse; 28 class AutoSetThreadIsSweeping; 29 class AutoDisallowPreWriteBarrier; 30 31 enum class GCUse { 32 // This thread is not running in the garbage collector. 33 None, 34 35 // This thread is currently collecting. Used when no finer detail is known. 36 Unspecified, 37 38 // This thread is currently marking GC things. This thread could be the main 39 // thread or a helper thread doing sweep-marking. 40 Marking, 41 42 // This thread is currently sweeping GC things. This thread could be the 43 // main thread or a helper thread while the main thread is running the 44 // mutator. 45 Sweeping, 46 47 // Whether this thread is currently finalizing GC things. This thread could 48 // be the main thread or a helper thread doing finalization while the main 49 // thread is running the mutator. 50 Finalizing 51 }; 52 53 } // namespace gc 54 } // namespace js 55 56 namespace JS { 57 58 /* 59 * GCContext is by GC operations that can run on or off the main thread. 60 * 61 * Its main function is to provide methods to free memory and update memory 62 * accounting. For convenience, it also has delete_ convenience methods that 63 * also call destructors. 64 * 65 * It is passed to finalizers and other sweep-phase hooks as JSContext is not 66 * available off the main thread. 67 */ 68 class GCContext { 69 using Cell = js::gc::Cell; 70 using MemoryUse = js::MemoryUse; 71 72 JSRuntime* const runtime_; 73 74 js::jit::JitPoisonRangeVector jitPoisonRanges; 75 76 // Which part of the garbage collector this context is running at the moment. 77 js::gc::GCUse gcUse_ = js::gc::GCUse::None; 78 friend class js::gc::AutoSetThreadGCUse; 79 friend class js::gc::AutoSetThreadIsSweeping; 80 friend class js::gc::AutoDisallowPreWriteBarrier; 81 82 #ifdef DEBUG 83 // The specific zone currently being swept, if any. 84 Zone* gcSweepZone_ = nullptr; 85 86 // Whether this thread is currently manipulating possibly-gray GC things. 87 size_t isTouchingGrayThings_ = false; 88 friend class js::AutoTouchingGrayThings; 89 90 // Whether it's safe to perform pre-write barriers. 91 bool preWriteBarrierAllowed_ = true; 92 #endif 93 94 public: 95 explicit GCContext(JSRuntime* maybeRuntime); 96 ~GCContext(); 97 98 JSRuntime* runtime() const { 99 MOZ_ASSERT(onMainThread()); 100 return runtimeFromAnyThread(); 101 } 102 JSRuntime* runtimeFromAnyThread() const { 103 MOZ_ASSERT(runtime_); 104 return runtime_; 105 } 106 107 js::gc::GCUse gcUse() const { return gcUse_; } 108 bool isCollecting() const { return gcUse() != js::gc::GCUse::None; } 109 bool isFinalizing() const { return gcUse_ == js::gc::GCUse::Finalizing; } 110 111 #ifdef DEBUG 112 bool onMainThread() const { 113 return js::CurrentThreadCanAccessRuntime(runtime_); 114 } 115 116 Zone* gcSweepZone() const { return gcSweepZone_; } 117 bool isTouchingGrayThings() const { return isTouchingGrayThings_; } 118 bool isPreWriteBarrierAllowed() const { return preWriteBarrierAllowed_; } 119 #endif 120 121 // Deprecated. Where possible, memory should be tracked against the owning GC 122 // thing by calling js::AddCellMemory and the memory freed with free_() below. 123 void freeUntracked(void* p) { js_free(p); } 124 125 // Free memory associated with a GC thing and update the memory accounting. 126 // 127 // The memory should have been associated with the GC thing using 128 // js::InitReservedSlot or js::InitObjectPrivate, or possibly 129 // js::AddCellMemory. 130 void free_(Cell* cell, void* p, size_t nbytes, MemoryUse use); 131 132 bool appendJitPoisonRange(const js::jit::JitPoisonRange& range) { 133 return jitPoisonRanges.append(range); 134 } 135 bool hasJitCodeToPoison() const { return !jitPoisonRanges.empty(); } 136 void poisonJitCode(); 137 138 // Deprecated. Where possible, memory should be tracked against the owning GC 139 // thing by calling js::AddCellMemory and the memory freed with delete_() 140 // below. 141 template <class T> 142 void deleteUntracked(T* p) { 143 if (p) { 144 p->~T(); 145 js_free(p); 146 } 147 } 148 149 // Delete a C++ object that was associated with a GC thing and update the 150 // memory accounting. The size is determined by the type T. 151 // 152 // The memory should have been associated with the GC thing using 153 // js::InitReservedSlot or js::InitObjectPrivate, or possibly 154 // js::AddCellMemory. 155 template <class T> 156 void delete_(Cell* cell, T* p, MemoryUse use) { 157 delete_(cell, p, sizeof(T), use); 158 } 159 160 // Delete a C++ object that was associated with a GC thing and update the 161 // memory accounting. 162 // 163 // The memory should have been associated with the GC thing using 164 // js::InitReservedSlot or js::InitObjectPrivate, or possibly 165 // js::AddCellMemory. 166 template <class T> 167 void delete_(Cell* cell, T* p, size_t nbytes, MemoryUse use) { 168 if (p) { 169 p->~T(); 170 free_(cell, p, nbytes, use); 171 } 172 } 173 174 // Release a RefCounted object that was associated with a GC thing and update 175 // the memory accounting. 176 // 177 // The memory should have been associated with the GC thing using 178 // js::InitReservedSlot or js::InitObjectPrivate, or possibly 179 // js::AddCellMemory. 180 // 181 // This counts the memory once per association with a GC thing. It's not 182 // expected that the same object is associated with more than one GC thing in 183 // each zone. If this is the case then some other form of accounting would be 184 // more appropriate. 185 template <class T> 186 void release(Cell* cell, T* p, MemoryUse use) { 187 release(cell, p, sizeof(T), use); 188 } 189 190 // Release a RefCounted object and that was associated with a GC thing and 191 // update the memory accounting. 192 // 193 // The memory should have been associated with the GC thing using 194 // js::InitReservedSlot or js::InitObjectPrivate, or possibly 195 // js::AddCellMemory. 196 template <class T> 197 void release(Cell* cell, T* p, size_t nbytes, MemoryUse use); 198 199 // Update the memory accounting for a GC for memory freed by some other 200 // method. 201 void removeCellMemory(Cell* cell, size_t nbytes, MemoryUse use); 202 }; 203 204 } // namespace JS 205 206 namespace js { 207 208 /* Thread Local Storage for storing the GCContext for a thread. */ 209 extern MOZ_THREAD_LOCAL(JS::GCContext*) TlsGCContext; 210 211 inline JS::GCContext* MaybeGetGCContext() { 212 if (!TlsGCContext.init()) { 213 return nullptr; 214 } 215 return TlsGCContext.get(); 216 } 217 218 class MOZ_RAII AutoTouchingGrayThings { 219 public: 220 #ifdef DEBUG 221 AutoTouchingGrayThings() { TlsGCContext.get()->isTouchingGrayThings_++; } 222 ~AutoTouchingGrayThings() { 223 JS::GCContext* gcx = TlsGCContext.get(); 224 MOZ_ASSERT(gcx->isTouchingGrayThings_); 225 gcx->isTouchingGrayThings_--; 226 } 227 #else 228 AutoTouchingGrayThings() {} 229 #endif 230 }; 231 232 #ifdef DEBUG 233 234 inline bool CurrentThreadIsGCMarking() { 235 JS::GCContext* gcx = MaybeGetGCContext(); 236 return gcx && gcx->gcUse() == gc::GCUse::Marking; 237 } 238 239 inline bool CurrentThreadIsGCSweeping() { 240 JS::GCContext* gcx = MaybeGetGCContext(); 241 return gcx && gcx->gcUse() == gc::GCUse::Sweeping; 242 } 243 244 inline bool CurrentThreadIsGCFinalizing() { 245 JS::GCContext* gcx = MaybeGetGCContext(); 246 return gcx && gcx->gcUse() == gc::GCUse::Finalizing; 247 } 248 249 inline bool CurrentThreadIsTouchingGrayThings() { 250 JS::GCContext* gcx = MaybeGetGCContext(); 251 return gcx && gcx->isTouchingGrayThings(); 252 } 253 254 inline bool CurrentThreadIsPerformingGC() { 255 JS::GCContext* gcx = MaybeGetGCContext(); 256 return gcx && gcx->isCollecting(); 257 } 258 259 #endif 260 261 } // namespace js 262 263 #endif // gc_GCContext_h