Source code

Revision control

Other Tools

/* -*- 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_FreeOp_h
#define gc_FreeOp_h
#include "mozilla/Assertions.h" // MOZ_ASSERT
#include "jstypes.h" // JS_PUBLIC_API
#include "gc/GCEnum.h" // js::MemoryUse
#include "jit/ExecutableAllocator.h" // jit::JitPoisonRangeVector
#include "js/AllocPolicy.h" // SystemAllocPolicy
#include "js/MemoryFunctions.h" // JSFreeOp
#include "js/Utility.h" // AutoEnterOOMUnsafeRegion, js_free
#include "js/Vector.h" // js::Vector
struct JS_PUBLIC_API JSRuntime;
namespace js {
namespace gc {
class AutoSetThreadIsPerformingGC;
} // namespace gc
} // namespace js
/*
* A JSFreeOp can do one thing: free memory. For convenience, it has delete_
* convenience methods that also call destructors.
*
* JSFreeOp is passed to finalizers and other sweep-phase hooks so that we do
* not need to pass a JSContext to those hooks.
*/
class JSFreeOp {
using Cell = js::gc::Cell;
using MemoryUse = js::MemoryUse;
JSRuntime* runtime_;
// We may accumulate a set of deferred free operations to be performed when
// the JSFreeOp is destroyed. This only applies to non-default JSFreeOps that
// are stack allocated and used during GC sweeping.
js::Vector<void*, 0, js::SystemAllocPolicy> freeLaterList;
js::jit::JitPoisonRangeVector jitPoisonRanges;
const bool isDefault;
bool isCollecting_;
friend class js::gc::AutoSetThreadIsPerformingGC;
public:
explicit JSFreeOp(JSRuntime* maybeRuntime, bool isDefault = false);
~JSFreeOp();
JSRuntime* runtime() const {
MOZ_ASSERT(runtime_);
return runtime_;
}
bool onMainThread() const { return runtime_ != nullptr; }
bool maybeOnHelperThread() const {
// Sometimes background finalization happens on the main thread so
// runtime_ being null doesn't always mean we are off thread.
return !runtime_;
}
bool isDefaultFreeOp() const { return isDefault; }
bool isCollecting() const { return isCollecting_; }
// 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);
// Deprecated. Where possible, memory should be tracked against the owning GC
// thing by calling js::AddCellMemory and the memory freed with freeLater()
// below.
void freeUntrackedLater(void* p) { queueForFreeLater(p); }
// Queue memory that was associated with a GC thing using js::AddCellMemory to
// be freed when the JSFreeOp is destroyed.
//
// This should not be called on the default JSFreeOps returned by
// JSRuntime/JSContext::defaultFreeOp() since these are not destroyed until
// the runtime itself is destroyed.
//
// This is used to ensure that copy-on-write object elements are not freed
// until all objects that refer to them have been finalized.
void freeLater(Cell* cell, void* p, size_t nbytes, MemoryUse use);
bool appendJitPoisonRange(const js::jit::JitPoisonRange& range) {
// JSFreeOps other than the defaultFreeOp() are constructed on the stack,
// and won't hold onto the pointers to free indefinitely.
MOZ_ASSERT(!isDefaultFreeOp());
return jitPoisonRanges.append(range);
}
// 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);
private:
void queueForFreeLater(void* p);
};
#endif // gc_FreeOp_h