summaryrefslogtreecommitdiffstats
path: root/js/src/gc/GCContext.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/gc/GCContext.h')
-rw-r--r--js/src/gc/GCContext.h257
1 files changed, 257 insertions, 0 deletions
diff --git a/js/src/gc/GCContext.h b/js/src/gc/GCContext.h
new file mode 100644
index 0000000000..8c74a81e0c
--- /dev/null
+++ b/js/src/gc/GCContext.h
@@ -0,0 +1,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