tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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