DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (a461fe03fdb0)

VCS Links

Enum

GCPolicy

NurseryAwareHashMap

UnsafeBareReadBarriered

Macros

Line Code
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
/* -*- 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

#include "gc/Barrier.h"
#include "gc/Marking.h"
#include "js/GCHashTable.h"
#include "js/GCPolicyAPI.h"
#include "js/HashTable.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;
    using Entry = typename MapType::Entry;

    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) +
               nurseryEntries.sizeOfExcludingThis(mallocSizeOf);
    }
    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
        return map.sizeOfIncludingThis(mallocSizeOf) +
               nurseryEntries.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(&copy);
            MOZ_ASSERT(!sweepKey);
            map.rekeyIfMoved(key, copy);
        }
        nurseryEntries.clear();
    }

    void sweep() {
        MOZ_ASSERT(nurseryEntries.empty());
        map.sweep();
    }

    bool hasNurseryEntries() const {
        return !nurseryEntries.empty();
    }
};

} // 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