Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef gc_NurseryAwareHashMap_h
8 : #define gc_NurseryAwareHashMap_h
9 :
10 : #include "gc/Barrier.h"
11 : #include "gc/Marking.h"
12 : #include "js/GCHashTable.h"
13 : #include "js/GCPolicyAPI.h"
14 : #include "js/HashTable.h"
15 :
16 : namespace js {
17 :
18 : namespace detail {
19 : // This class only handles the incremental case and does not deal with nursery
20 : // pointers. The only users should be for NurseryAwareHashMap; it is defined
21 : // externally because we need a GCPolicy for its use in the contained map.
22 : template <typename T>
23 : class UnsafeBareReadBarriered : public ReadBarrieredBase<T>
24 : {
25 : public:
26 : UnsafeBareReadBarriered() : ReadBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
27 9597 : MOZ_IMPLICIT UnsafeBareReadBarriered(const T& v) : ReadBarrieredBase<T>(v) {}
28 : explicit UnsafeBareReadBarriered(const UnsafeBareReadBarriered& v) : ReadBarrieredBase<T>(v) {}
29 12461 : UnsafeBareReadBarriered(UnsafeBareReadBarriered&& v)
30 12461 : : ReadBarrieredBase<T>(mozilla::Move(v))
31 12461 : {}
32 :
33 0 : UnsafeBareReadBarriered& operator=(const UnsafeBareReadBarriered& v) {
34 0 : this->value = v.value;
35 0 : return *this;
36 : }
37 :
38 12 : UnsafeBareReadBarriered& operator=(const T& v) {
39 12 : this->value = v;
40 12 : return *this;
41 : }
42 :
43 20105 : const T get() const {
44 20105 : if (!InternalBarrierMethods<T>::isMarkable(this->value))
45 0 : return JS::GCPolicy<T>::initial();
46 20105 : this->read();
47 20105 : return this->value;
48 : }
49 :
50 : explicit operator bool() const {
51 : return bool(this->value);
52 : }
53 :
54 0 : const T unbarrieredGet() const { return this->value; }
55 65 : T* unsafeGet() { return &this->value; }
56 : T const* unsafeGet() const { return &this->value; }
57 : };
58 : } // namespace detail
59 :
60 : // The "nursery aware" hash map is a special case of GCHashMap that is able to
61 : // treat nursery allocated members weakly during a minor GC: e.g. it allows for
62 : // nursery allocated objects to be collected during nursery GC where a normal
63 : // hash table treats such edges strongly.
64 : //
65 : // Doing this requires some strong constraints on what can be stored in this
66 : // table and how it can be accessed. At the moment, this table assumes that
67 : // all values contain a strong reference to the key. It also requires the
68 : // policy to contain an |isTenured| and |needsSweep| members, which is fairly
69 : // non-standard. This limits its usefulness to the CrossCompartmentMap at the
70 : // moment, but might serve as a useful base for other tables in future.
71 : template <typename Key,
72 : typename Value,
73 : typename HashPolicy = DefaultHasher<Key>,
74 : typename AllocPolicy = TempAllocPolicy>
75 6117 : class NurseryAwareHashMap
76 : {
77 : using BarrieredValue = detail::UnsafeBareReadBarriered<Value>;
78 : using MapType = GCRekeyableHashMap<Key, BarrieredValue, HashPolicy, AllocPolicy>;
79 : MapType map;
80 :
81 : // Keep a list of all keys for which JS::GCPolicy<Key>::isTenured is false.
82 : // This lets us avoid a full traveral of the map on each minor GC, keeping
83 : // the minor GC times proportional to the nursery heap size.
84 : Vector<Key, 0, AllocPolicy> nurseryEntries;
85 :
86 : public:
87 : using Lookup = typename MapType::Lookup;
88 : using Ptr = typename MapType::Ptr;
89 : using Range = typename MapType::Range;
90 : using Entry = typename MapType::Entry;
91 :
92 1681 : explicit NurseryAwareHashMap(AllocPolicy a = AllocPolicy()) : map(a) {}
93 :
94 1681 : MOZ_MUST_USE bool init(uint32_t len = 16) { return map.init(len); }
95 :
96 13681 : bool empty() const { return map.empty(); }
97 40220 : Ptr lookup(const Lookup& l) const { return map.lookup(l); }
98 10 : void remove(Ptr p) { map.remove(p); }
99 : Range all() const { return map.all(); }
100 1265 : struct Enum : public MapType::Enum {
101 1265 : explicit Enum(NurseryAwareHashMap& namap) : MapType::Enum(namap.map) {}
102 : };
103 0 : size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
104 0 : return map.sizeOfExcludingThis(mallocSizeOf) +
105 0 : nurseryEntries.sizeOfExcludingThis(mallocSizeOf);
106 : }
107 : size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
108 : return map.sizeOfIncludingThis(mallocSizeOf) +
109 : nurseryEntries.sizeOfIncludingThis(mallocSizeOf);
110 : }
111 :
112 9609 : MOZ_MUST_USE bool put(const Key& k, const Value& v) {
113 9609 : auto p = map.lookupForAdd(k);
114 9609 : if (p) {
115 12 : if (!JS::GCPolicy<Key>::isTenured(k) || !JS::GCPolicy<Value>::isTenured(v)) {
116 0 : if (!nurseryEntries.append(k))
117 0 : return false;
118 : }
119 12 : p->value() = v;
120 12 : return true;
121 : }
122 :
123 9597 : bool ok = map.add(p, k, v);
124 9597 : if (!ok)
125 0 : return false;
126 :
127 9597 : if (!JS::GCPolicy<Key>::isTenured(k) || !JS::GCPolicy<Value>::isTenured(v)) {
128 3387 : if (!nurseryEntries.append(k)) {
129 0 : map.remove(k);
130 0 : return false;
131 : }
132 : }
133 :
134 9597 : return true;
135 : }
136 :
137 12428 : void sweepAfterMinorGC(JSTracer* trc) {
138 15516 : for (auto& key : nurseryEntries) {
139 3088 : auto p = map.lookup(key);
140 3088 : if (!p)
141 987 : continue;
142 :
143 : // Drop the entry if the value is not marked.
144 3088 : if (JS::GCPolicy<BarrieredValue>::needsSweep(&p->value())) {
145 987 : map.remove(key);
146 987 : continue;
147 : }
148 :
149 : // Update and relocate the key, if the value is still needed.
150 : //
151 : // Note that this currently assumes that all Value will contain a
152 : // strong reference to Key, as per its use as the
153 : // CrossCompartmentWrapperMap. We may need to make the following
154 : // behavior more dynamic if we use this map in other nursery-aware
155 : // contexts.
156 4202 : Key copy(key);
157 4202 : mozilla::DebugOnly<bool> sweepKey = JS::GCPolicy<Key>::needsSweep(©);
158 2101 : MOZ_ASSERT(!sweepKey);
159 2101 : map.rekeyIfMoved(key, copy);
160 : }
161 12428 : nurseryEntries.clear();
162 12428 : }
163 :
164 0 : void sweep() {
165 0 : MOZ_ASSERT(nurseryEntries.empty());
166 0 : map.sweep();
167 0 : }
168 :
169 0 : bool hasNurseryEntries() const {
170 0 : return !nurseryEntries.empty();
171 : }
172 : };
173 :
174 : } // namespace js
175 :
176 : namespace JS {
177 : template <typename T>
178 : struct GCPolicy<js::detail::UnsafeBareReadBarriered<T>>
179 : {
180 : static void trace(JSTracer* trc, js::detail::UnsafeBareReadBarriered<T>* thingp,
181 : const char* name)
182 : {
183 : js::TraceEdge(trc, thingp, name);
184 : }
185 3088 : static bool needsSweep(js::detail::UnsafeBareReadBarriered<T>* thingp) {
186 3088 : return js::gc::IsAboutToBeFinalized(thingp);
187 : }
188 : };
189 : } // namespace JS
190 :
191 : #endif // gc_NurseryAwareHashMap_h
|