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_FreeOp_h
8 #define gc_FreeOp_h
9 
10 #include "mozilla/Assertions.h"  // MOZ_ASSERT
11 
12 #include "jstypes.h"                  // JS_PUBLIC_API
13 #include "gc/GCEnum.h"                // js::MemoryUse
14 #include "jit/ExecutableAllocator.h"  // jit::JitPoisonRangeVector
15 #include "js/AllocPolicy.h"           // SystemAllocPolicy
16 #include "js/MemoryFunctions.h"       // JSFreeOp
17 #include "js/Utility.h"               // AutoEnterOOMUnsafeRegion, js_free
18 #include "js/Vector.h"                // js::Vector
19 
20 struct JS_PUBLIC_API JSRuntime;
21 
22 namespace js {
23 namespace gc {
24 class AutoSetThreadIsPerformingGC;
25 }  // namespace gc
26 }  // namespace js
27 
28 /*
29  * A JSFreeOp can do one thing: free memory. For convenience, it has delete_
30  * convenience methods that also call destructors.
31  *
32  * JSFreeOp is passed to finalizers and other sweep-phase hooks so that we do
33  * not need to pass a JSContext to those hooks.
34  */
35 class JSFreeOp {
36   using Cell = js::gc::Cell;
37   using MemoryUse = js::MemoryUse;
38 
39   JSRuntime* runtime_;
40 
41   js::jit::JitPoisonRangeVector jitPoisonRanges;
42 
43   const bool isDefault;
44   bool isCollecting_;
45 
46   friend class js::gc::AutoSetThreadIsPerformingGC;
47 
48  public:
49   explicit JSFreeOp(JSRuntime* maybeRuntime, bool isDefault = false);
50   ~JSFreeOp();
51 
runtime()52   JSRuntime* runtime() const {
53     MOZ_ASSERT(runtime_);
54     return runtime_;
55   }
56 
onMainThread()57   bool onMainThread() const { return runtime_ != nullptr; }
58 
maybeOnHelperThread()59   bool maybeOnHelperThread() const {
60     // Sometimes background finalization happens on the main thread so
61     // runtime_ being null doesn't always mean we are off thread.
62     return !runtime_;
63   }
64 
isDefaultFreeOp()65   bool isDefaultFreeOp() const { return isDefault; }
isCollecting()66   bool isCollecting() const { return isCollecting_; }
67 
68   // Deprecated. Where possible, memory should be tracked against the owning GC
69   // thing by calling js::AddCellMemory and the memory freed with free_() below.
freeUntracked(void * p)70   void freeUntracked(void* p) { js_free(p); }
71 
72   // Free memory associated with a GC thing and update the memory accounting.
73   //
74   // The memory should have been associated with the GC thing using
75   // js::InitReservedSlot or js::InitObjectPrivate, or possibly
76   // js::AddCellMemory.
77   void free_(Cell* cell, void* p, size_t nbytes, MemoryUse use);
78 
appendJitPoisonRange(const js::jit::JitPoisonRange & range)79   bool appendJitPoisonRange(const js::jit::JitPoisonRange& range) {
80     // JSFreeOps other than the defaultFreeOp() are constructed on the stack,
81     // and won't hold onto the pointers to free indefinitely.
82     MOZ_ASSERT(!isDefaultFreeOp());
83 
84     return jitPoisonRanges.append(range);
85   }
86 
87   // Deprecated. Where possible, memory should be tracked against the owning GC
88   // thing by calling js::AddCellMemory and the memory freed with delete_()
89   // below.
90   template <class T>
deleteUntracked(T * p)91   void deleteUntracked(T* p) {
92     if (p) {
93       p->~T();
94       js_free(p);
95     }
96   }
97 
98   // Delete a C++ object that was associated with a GC thing and update the
99   // memory accounting. The size is determined by the type T.
100   //
101   // The memory should have been associated with the GC thing using
102   // js::InitReservedSlot or js::InitObjectPrivate, or possibly
103   // js::AddCellMemory.
104   template <class T>
delete_(Cell * cell,T * p,MemoryUse use)105   void delete_(Cell* cell, T* p, MemoryUse use) {
106     delete_(cell, p, sizeof(T), use);
107   }
108 
109   // Delete a C++ object that was associated with a GC thing and update the
110   // memory accounting.
111   //
112   // The memory should have been associated with the GC thing using
113   // js::InitReservedSlot or js::InitObjectPrivate, or possibly
114   // js::AddCellMemory.
115   template <class T>
delete_(Cell * cell,T * p,size_t nbytes,MemoryUse use)116   void delete_(Cell* cell, T* p, size_t nbytes, MemoryUse use) {
117     if (p) {
118       p->~T();
119       free_(cell, p, nbytes, use);
120     }
121   }
122 
123   // Release a RefCounted object that was associated with a GC thing and update
124   // the memory accounting.
125   //
126   // The memory should have been associated with the GC thing using
127   // js::InitReservedSlot or js::InitObjectPrivate, or possibly
128   // js::AddCellMemory.
129   //
130   // This counts the memory once per association with a GC thing. It's not
131   // expected that the same object is associated with more than one GC thing in
132   // each zone. If this is the case then some other form of accounting would be
133   // more appropriate.
134   template <class T>
release(Cell * cell,T * p,MemoryUse use)135   void release(Cell* cell, T* p, MemoryUse use) {
136     release(cell, p, sizeof(T), use);
137   }
138 
139   // Release a RefCounted object and that was associated with a GC thing and
140   // update the memory accounting.
141   //
142   // The memory should have been associated with the GC thing using
143   // js::InitReservedSlot or js::InitObjectPrivate, or possibly
144   // js::AddCellMemory.
145   template <class T>
146   void release(Cell* cell, T* p, size_t nbytes, MemoryUse use);
147 
148   // Update the memory accounting for a GC for memory freed by some other
149   // method.
150   void removeCellMemory(Cell* cell, size_t nbytes, MemoryUse use);
151 };
152 
153 #endif  // gc_FreeOp_h
154