summaryrefslogtreecommitdiffstats
path: root/js/src/gc/GCContext.h
blob: 8c74a81e0c65d19d14bb49480a2a61a0e7321474 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef gc_GCContext_h
#define gc_GCContext_h

#include "mozilla/Assertions.h"  // MOZ_ASSERT
#include "mozilla/ThreadLocal.h"

#include "jspubtd.h"
#include "jstypes.h"                  // JS_PUBLIC_API
#include "gc/GCEnum.h"                // js::MemoryUse
#include "jit/ExecutableAllocator.h"  // jit::JitPoisonRangeVector
#include "js/Utility.h"               // js_free

struct JS_PUBLIC_API JSRuntime;

namespace js {

class AutoTouchingGrayThings;

namespace gc {

class AutoSetThreadGCUse;
class AutoSetThreadIsSweeping;

enum class GCUse {
  // This thread is not running in the garbage collector.
  None,

  // This thread is currently collecting. Used when no finer detail is known.
  Unspecified,

  // This thread is currently marking GC things. This thread could be the main
  // thread or a helper thread doing sweep-marking.
  Marking,

  // This thread is currently sweeping GC things. This thread could be the
  // main thread or a helper thread while the main thread is running the
  // mutator.
  Sweeping,

  // Whether this thread is currently finalizing GC things. This thread could
  // be the main thread or a helper thread doing finalization while the main
  // thread is running the mutator.
  Finalizing
};

}  // namespace gc
}  // namespace js

namespace JS {

/*
 * GCContext is by GC operations that can run on or off the main thread.
 *
 * Its main function is to provide methods to free memory and update memory
 * accounting. For convenience, it also has delete_ convenience methods that
 * also call destructors.
 *
 * It is passed to finalizers and other sweep-phase hooks as JSContext is not
 * available off the main thread.
 */
class GCContext {
  using Cell = js::gc::Cell;
  using MemoryUse = js::MemoryUse;

  JSRuntime* const runtime_;

  js::jit::JitPoisonRangeVector jitPoisonRanges;

  // Which part of the garbage collector this context is running at the moment.
  js::gc::GCUse gcUse_ = js::gc::GCUse::None;
  friend class js::gc::AutoSetThreadGCUse;
  friend class js::gc::AutoSetThreadIsSweeping;

#ifdef DEBUG
  // The specific zone currently being swept, if any.
  Zone* gcSweepZone_ = nullptr;

  // Whether this thread is currently manipulating possibly-gray GC things.
  size_t isTouchingGrayThings_ = false;
  friend class js::AutoTouchingGrayThings;
#endif

 public:
  explicit GCContext(JSRuntime* maybeRuntime);
  ~GCContext();

  JSRuntime* runtime() const {
    MOZ_ASSERT(onMainThread());
    return runtimeFromAnyThread();
  }
  JSRuntime* runtimeFromAnyThread() const {
    MOZ_ASSERT(runtime_);
    return runtime_;
  }

  js::gc::GCUse gcUse() const { return gcUse_; }
  bool isCollecting() const { return gcUse() != js::gc::GCUse::None; }
  bool isFinalizing() const { return gcUse_ == js::gc::GCUse::Finalizing; }

#ifdef DEBUG
  bool onMainThread() const {
    return js::CurrentThreadCanAccessRuntime(runtime_);
  }

  Zone* gcSweepZone() const { return gcSweepZone_; }
  bool isTouchingGrayThings() const { return isTouchingGrayThings_; }
#endif

  // Deprecated. Where possible, memory should be tracked against the owning GC
  // thing by calling js::AddCellMemory and the memory freed with free_() below.
  void freeUntracked(void* p) { js_free(p); }

  // Free memory associated with a GC thing and update the memory accounting.
  //
  // The memory should have been associated with the GC thing using
  // js::InitReservedSlot or js::InitObjectPrivate, or possibly
  // js::AddCellMemory.
  void free_(Cell* cell, void* p, size_t nbytes, MemoryUse use);

  bool appendJitPoisonRange(const js::jit::JitPoisonRange& range) {
    return jitPoisonRanges.append(range);
  }
  bool hasJitCodeToPoison() const { return !jitPoisonRanges.empty(); }
  void poisonJitCode();

  // Deprecated. Where possible, memory should be tracked against the owning GC
  // thing by calling js::AddCellMemory and the memory freed with delete_()
  // below.
  template <class T>
  void deleteUntracked(T* p) {
    if (p) {
      p->~T();
      js_free(p);
    }
  }

  // Delete a C++ object that was associated with a GC thing and update the
  // memory accounting. The size is determined by the type T.
  //
  // The memory should have been associated with the GC thing using
  // js::InitReservedSlot or js::InitObjectPrivate, or possibly
  // js::AddCellMemory.
  template <class T>
  void delete_(Cell* cell, T* p, MemoryUse use) {
    delete_(cell, p, sizeof(T), use);
  }

  // Delete a C++ object that was associated with a GC thing and update the
  // memory accounting.
  //
  // The memory should have been associated with the GC thing using
  // js::InitReservedSlot or js::InitObjectPrivate, or possibly
  // js::AddCellMemory.
  template <class T>
  void delete_(Cell* cell, T* p, size_t nbytes, MemoryUse use) {
    if (p) {
      p->~T();
      free_(cell, p, nbytes, use);
    }
  }

  // Release a RefCounted object that was associated with a GC thing and update
  // the memory accounting.
  //
  // The memory should have been associated with the GC thing using
  // js::InitReservedSlot or js::InitObjectPrivate, or possibly
  // js::AddCellMemory.
  //
  // This counts the memory once per association with a GC thing. It's not
  // expected that the same object is associated with more than one GC thing in
  // each zone. If this is the case then some other form of accounting would be
  // more appropriate.
  template <class T>
  void release(Cell* cell, T* p, MemoryUse use) {
    release(cell, p, sizeof(T), use);
  }

  // Release a RefCounted object and that was associated with a GC thing and
  // update the memory accounting.
  //
  // The memory should have been associated with the GC thing using
  // js::InitReservedSlot or js::InitObjectPrivate, or possibly
  // js::AddCellMemory.
  template <class T>
  void release(Cell* cell, T* p, size_t nbytes, MemoryUse use);

  // Update the memory accounting for a GC for memory freed by some other
  // method.
  void removeCellMemory(Cell* cell, size_t nbytes, MemoryUse use);
};

}  // namespace JS

namespace js {

/* Thread Local Storage for storing the GCContext for a thread. */
extern MOZ_THREAD_LOCAL(JS::GCContext*) TlsGCContext;

inline JS::GCContext* MaybeGetGCContext() {
  if (!TlsGCContext.init()) {
    return nullptr;
  }
  return TlsGCContext.get();
}

class MOZ_RAII AutoTouchingGrayThings {
 public:
#ifdef DEBUG
  AutoTouchingGrayThings() { TlsGCContext.get()->isTouchingGrayThings_++; }
  ~AutoTouchingGrayThings() {
    JS::GCContext* gcx = TlsGCContext.get();
    MOZ_ASSERT(gcx->isTouchingGrayThings_);
    gcx->isTouchingGrayThings_--;
  }
#else
  AutoTouchingGrayThings() {}
#endif
};

#ifdef DEBUG

inline bool CurrentThreadIsGCMarking() {
  JS::GCContext* gcx = MaybeGetGCContext();
  return gcx && gcx->gcUse() == gc::GCUse::Marking;
}

inline bool CurrentThreadIsGCSweeping() {
  JS::GCContext* gcx = MaybeGetGCContext();
  return gcx && gcx->gcUse() == gc::GCUse::Sweeping;
}

inline bool CurrentThreadIsGCFinalizing() {
  JS::GCContext* gcx = MaybeGetGCContext();
  return gcx && gcx->gcUse() == gc::GCUse::Finalizing;
}

inline bool CurrentThreadIsTouchingGrayThings() {
  JS::GCContext* gcx = MaybeGetGCContext();
  return gcx && gcx->isTouchingGrayThings();
}

inline bool CurrentThreadIsPerformingGC() {
  JS::GCContext* gcx = MaybeGetGCContext();
  return gcx && gcx->isCollecting();
}

#endif

}  // namespace js

#endif  // gc_GCContext_h