diff options
Diffstat (limited to 'js/src/gc/NurseryAwareHashMap.h')
-rw-r--r-- | js/src/gc/NurseryAwareHashMap.h | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/js/src/gc/NurseryAwareHashMap.h b/js/src/gc/NurseryAwareHashMap.h new file mode 100644 index 000000000..5ade2e341 --- /dev/null +++ b/js/src/gc/NurseryAwareHashMap.h @@ -0,0 +1,178 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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_NurseryAwareHashMap_h +#define gc_NurseryAwareHashMap_h + +namespace js { + +namespace detail { +// This class only handles the incremental case and does not deal with nursery +// pointers. The only users should be for NurseryAwareHashMap; it is defined +// externally because we need a GCPolicy for its use in the contained map. +template <typename T> +class UnsafeBareReadBarriered : public ReadBarrieredBase<T> +{ + public: + UnsafeBareReadBarriered() : ReadBarrieredBase<T>(JS::GCPolicy<T>::initial()) {} + MOZ_IMPLICIT UnsafeBareReadBarriered(const T& v) : ReadBarrieredBase<T>(v) {} + explicit UnsafeBareReadBarriered(const UnsafeBareReadBarriered& v) : ReadBarrieredBase<T>(v) {} + UnsafeBareReadBarriered(UnsafeBareReadBarriered&& v) + : ReadBarrieredBase<T>(mozilla::Move(v)) + {} + + UnsafeBareReadBarriered& operator=(const UnsafeBareReadBarriered& v) { + this->value = v.value; + return *this; + } + + UnsafeBareReadBarriered& operator=(const T& v) { + this->value = v; + return *this; + } + + const T get() const { + if (!InternalBarrierMethods<T>::isMarkable(this->value)) + return JS::GCPolicy<T>::initial(); + this->read(); + return this->value; + } + + explicit operator bool() const { + return bool(this->value); + } + + const T unbarrieredGet() const { return this->value; } + T* unsafeGet() { return &this->value; } + T const* unsafeGet() const { return &this->value; } +}; +} // namespace detail + +// The "nursery aware" hash map is a special case of GCHashMap that is able to +// treat nursery allocated members weakly during a minor GC: e.g. it allows for +// nursery allocated objects to be collected during nursery GC where a normal +// hash table treats such edges strongly. +// +// Doing this requires some strong constraints on what can be stored in this +// table and how it can be accessed. At the moment, this table assumes that +// all values contain a strong reference to the key. It also requires the +// policy to contain an |isTenured| and |needsSweep| members, which is fairly +// non-standard. This limits its usefulness to the CrossCompartmentMap at the +// moment, but might serve as a useful base for other tables in future. +template <typename Key, + typename Value, + typename HashPolicy = DefaultHasher<Key>, + typename AllocPolicy = TempAllocPolicy> +class NurseryAwareHashMap +{ + using BarrieredValue = detail::UnsafeBareReadBarriered<Value>; + using MapType = GCRekeyableHashMap<Key, BarrieredValue, HashPolicy, AllocPolicy>; + MapType map; + + // Keep a list of all keys for which JS::GCPolicy<Key>::isTenured is false. + // This lets us avoid a full traveral of the map on each minor GC, keeping + // the minor GC times proportional to the nursery heap size. + Vector<Key, 0, AllocPolicy> nurseryEntries; + + public: + using Lookup = typename MapType::Lookup; + using Ptr = typename MapType::Ptr; + using Range = typename MapType::Range; + + explicit NurseryAwareHashMap(AllocPolicy a = AllocPolicy()) : map(a) {} + + MOZ_MUST_USE bool init(uint32_t len = 16) { return map.init(len); } + + bool empty() const { return map.empty(); } + Ptr lookup(const Lookup& l) const { return map.lookup(l); } + void remove(Ptr p) { map.remove(p); } + Range all() const { return map.all(); } + struct Enum : public MapType::Enum { + explicit Enum(NurseryAwareHashMap& namap) : MapType::Enum(namap.map) {} + }; + size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { + return map.sizeOfExcludingThis(mallocSizeOf); + } + size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { + return map.sizeOfIncludingThis(mallocSizeOf); + } + + MOZ_MUST_USE bool put(const Key& k, const Value& v) { + auto p = map.lookupForAdd(k); + if (p) { + if (!JS::GCPolicy<Key>::isTenured(k) || !JS::GCPolicy<Value>::isTenured(v)) { + if (!nurseryEntries.append(k)) + return false; + } + p->value() = v; + return true; + } + + bool ok = map.add(p, k, v); + if (!ok) + return false; + + if (!JS::GCPolicy<Key>::isTenured(k) || !JS::GCPolicy<Value>::isTenured(v)) { + if (!nurseryEntries.append(k)) { + map.remove(k); + return false; + } + } + + return true; + } + + void sweepAfterMinorGC(JSTracer* trc) { + for (auto& key : nurseryEntries) { + auto p = map.lookup(key); + if (!p) + continue; + + // Drop the entry if the value is not marked. + if (JS::GCPolicy<BarrieredValue>::needsSweep(&p->value())) { + map.remove(key); + continue; + } + + // Update and relocate the key, if the value is still needed. + // + // Note that this currently assumes that all Value will contain a + // strong reference to Key, as per its use as the + // CrossCompartmentWrapperMap. We may need to make the following + // behavior more dynamic if we use this map in other nursery-aware + // contexts. + Key copy(key); + mozilla::DebugOnly<bool> sweepKey = JS::GCPolicy<Key>::needsSweep(©); + MOZ_ASSERT(!sweepKey); + map.rekeyIfMoved(key, copy); + } + nurseryEntries.clear(); + } + + void sweep() { + MOZ_ASSERT(nurseryEntries.empty()); + map.sweep(); + } +}; + +} // namespace js + +namespace JS { +template <typename T> +struct GCPolicy<js::detail::UnsafeBareReadBarriered<T>> +{ + static void trace(JSTracer* trc, js::detail::UnsafeBareReadBarriered<T>* thingp, + const char* name) + { + js::TraceEdge(trc, thingp, name); + } + static bool needsSweep(js::detail::UnsafeBareReadBarriered<T>* thingp) { + return js::gc::IsAboutToBeFinalized(thingp); + } +}; +} // namespace JS + +#endif // gc_NurseryAwareHashMap_h |