Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.

Commit cad0052

Browse files
committed
Bug 1735296 - Part 2: Add an UnsafeBarePtr wrapper for use in NurseryAwareHashMap r=sfink
This is also required to remove some public GCPolicy methods for bare pointer types. Differential Revision: https://phabricator.services.mozilla.com/D128258
1 parent 0b48399 commit cad0052

File tree

5 files changed

+88
-55
lines changed

5 files changed

+88
-55
lines changed

js/src/gc/NurseryAwareHashMap.h

Lines changed: 81 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,29 @@
1616
namespace js {
1717

1818
namespace detail {
19+
// A wrapper for a bare pointer, with no barriers. The only user should be for
20+
// NurseryAwareHashMap; it is defined externally because we need a GCPolicy for
21+
// its use in the contained map.
22+
template <typename T>
23+
class UnsafeBarePtr : public BarrieredBase<T> {
24+
public:
25+
UnsafeBarePtr() : BarrieredBase<T>(JS::SafelyInitialized<T>()) {}
26+
MOZ_IMPLICIT UnsafeBarePtr(T v) : BarrieredBase<T>(v) {}
27+
const T& get() const { return this->value; }
28+
void set(T newValue) { this->value = newValue; }
29+
DECLARE_POINTER_CONSTREF_OPS(T);
30+
};
31+
32+
template <class T>
33+
struct UnsafeBarePtrHasher {
34+
using Key = UnsafeBarePtr<T>;
35+
using Lookup = T;
36+
37+
static HashNumber hash(const Lookup& l) { return DefaultHasher<T>::hash(l); }
38+
static bool match(const Key& k, Lookup l) { return k.get() == l; }
39+
static void rekey(Key& k, const Key& newKey) { k.set(newKey.get()); }
40+
};
41+
1942
// This class only handles the incremental case and does not deal with nursery
2043
// pointers. The only users should be for NurseryAwareHashMap; it is defined
2144
// externally because we need a GCPolicy for its use in the contained map.
@@ -63,24 +86,23 @@ enum : bool { DuplicatesNotPossible, DuplicatesPossible };
6386
// hash table treats such edges strongly.
6487
//
6588
// 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.
89+
// table and how it can be accessed. At the moment, this table assumes that all
90+
// values contain a strong reference to the key. This limits its usefulness to
91+
// the CrossCompartmentMap at the moment, but might serve as a useful base for
92+
// other tables in future.
7193
template <typename Key, typename Value,
72-
typename HashPolicy = DefaultHasher<Key>,
7394
typename AllocPolicy = TempAllocPolicy,
7495
bool AllowDuplicates = DuplicatesNotPossible>
7596
class NurseryAwareHashMap {
76-
using BarrieredValue = detail::UnsafeBareWeakHeapPtr<Value>;
77-
using MapType =
78-
GCRekeyableHashMap<Key, BarrieredValue, HashPolicy, AllocPolicy>;
97+
using MapKey = detail::UnsafeBarePtr<Key>;
98+
using MapValue = detail::UnsafeBareWeakHeapPtr<Value>;
99+
using HashPolicy = DefaultHasher<MapKey>;
100+
using MapType = GCRekeyableHashMap<MapKey, MapValue, HashPolicy, AllocPolicy>;
79101
MapType map;
80102

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.
103+
// Keep a list of all keys for which key->isTenured() is false. This lets us
104+
// avoid a full traversal of the map on each minor GC, keeping the minor GC
105+
// times proportional to the nursery heap size.
84106
Vector<Key, 0, AllocPolicy> nurseryEntries;
85107

86108
public:
@@ -111,33 +133,19 @@ class NurseryAwareHashMap {
111133
nurseryEntries.sizeOfIncludingThis(mallocSizeOf);
112134
}
113135

114-
[[nodiscard]] bool put(const Key& k, const Value& v) {
115-
auto p = map.lookupForAdd(k);
116-
if (p) {
117-
if (!JS::GCPolicy<Key>::isTenured(k) ||
118-
!JS::GCPolicy<Value>::isTenured(v)) {
119-
if (!nurseryEntries.append(k)) {
120-
return false;
121-
}
122-
}
123-
p->value() = v;
124-
return true;
125-
}
126-
127-
bool ok = map.add(p, k, v);
128-
if (!ok) {
136+
[[nodiscard]] bool put(const Key& key, const Value& value) {
137+
if ((!key->isTenured() || !value->isTenured()) &&
138+
!nurseryEntries.append(key)) {
129139
return false;
130140
}
131141

132-
if (!JS::GCPolicy<Key>::isTenured(k) ||
133-
!JS::GCPolicy<Value>::isTenured(v)) {
134-
if (!nurseryEntries.append(k)) {
135-
map.remove(k);
136-
return false;
137-
}
142+
auto p = map.lookupForAdd(key);
143+
if (p) {
144+
p->value() = value;
145+
return true;
138146
}
139147

140-
return true;
148+
return map.add(p, key, value);
141149
}
142150

143151
void sweepAfterMinorGC(JSTracer* trc) {
@@ -148,25 +156,25 @@ class NurseryAwareHashMap {
148156
}
149157

150158
// Drop the entry if the value is not marked.
151-
if (JS::GCPolicy<BarrieredValue>::needsSweep(&p->value())) {
159+
if (JS::GCPolicy<MapValue>::needsSweep(&p->value())) {
152160
map.remove(key);
153161
continue;
154162
}
155163

156164
// Update and relocate the key, if the value is still needed.
157165
//
158-
// Non-string Values will contain a strong reference to Key, as per
159-
// its use in the CrossCompartmentWrapperMap, so the key will never
160-
// be dying here. Strings do *not* have any sort of pointer from
161-
// wrapper to wrappee, as they are just copies. The wrapper map
162-
// entry is merely used as a cache to avoid re-copying the string,
163-
// and currently that entire cache is flushed on major GC.
164-
Key copy(key);
165-
bool sweepKey = JS::GCPolicy<Key>::needsSweep(&copy);
166-
if (sweepKey) {
167-
map.remove(key);
166+
// Non-string Values will contain a strong reference to Key, as per its
167+
// use in the CrossCompartmentWrapperMap, so the key will never be dying
168+
// here. Strings do *not* have any sort of pointer from wrapper to
169+
// wrappee, as they are just copies. The wrapper map entry is merely used
170+
// as a cache to avoid re-copying the string, and currently that entire
171+
// cache is flushed on major GC.
172+
MapKey copy(key);
173+
if (JS::GCPolicy<MapKey>::needsSweep(&copy)) {
174+
map.remove(p);
168175
continue;
169176
}
177+
170178
if (AllowDuplicates) {
171179
// Drop duplicated keys.
172180
//
@@ -178,7 +186,7 @@ class NurseryAwareHashMap {
178186
} else if (map.has(copy)) {
179187
// Key was forwarded to the same place that another key was already
180188
// forwarded to.
181-
map.remove(key);
189+
map.remove(p);
182190
} else {
183191
map.rekeyAs(key, copy, copy);
184192
}
@@ -203,6 +211,24 @@ class NurseryAwareHashMap {
203211
} // namespace js
204212

205213
namespace JS {
214+
215+
template <typename T>
216+
struct GCPolicy<js::detail::UnsafeBarePtr<T>> {
217+
static bool needsSweep(js::detail::UnsafeBarePtr<T>* vp) {
218+
if (*vp) {
219+
return js::gc::IsAboutToBeFinalizedUnbarriered(vp->unbarrieredAddress());
220+
}
221+
return false;
222+
}
223+
static bool traceWeak(JSTracer* trc, js::detail::UnsafeBarePtr<T>* vp) {
224+
if (*vp) {
225+
return js::TraceManuallyBarrieredWeakEdge(trc, vp->unbarrieredAddress(),
226+
"UnsafeBarePtr");
227+
}
228+
return true;
229+
}
230+
};
231+
206232
template <typename T>
207233
struct GCPolicy<js::detail::UnsafeBareWeakHeapPtr<T>> {
208234
static void trace(JSTracer* trc, js::detail::UnsafeBareWeakHeapPtr<T>* thingp,
@@ -213,6 +239,15 @@ struct GCPolicy<js::detail::UnsafeBareWeakHeapPtr<T>> {
213239
return js::gc::IsAboutToBeFinalized(thingp);
214240
}
215241
};
242+
216243
} // namespace JS
217244

245+
namespace mozilla {
246+
247+
template <class T>
248+
struct DefaultHasher<js::detail::UnsafeBarePtr<T>>
249+
: js::detail::UnsafeBarePtrHasher<T> {};
250+
251+
} // namespace mozilla
252+
218253
#endif // gc_NurseryAwareHashMap_h

js/src/gc/Zone.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ void Zone::checkStringWrappersAfterMovingGC() {
380380
// wrapper map that points into the nursery, and that the hash table entries
381381
// are discoverable.
382382
auto key = e.front().key();
383-
CheckGCThingAfterMovingGC(key);
383+
CheckGCThingAfterMovingGC(key.get());
384384

385385
auto ptr = crossZoneStringWrappers().lookup(key);
386386
MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &e.front());

js/src/gc/Zone.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ using FinalizationRecordVector = GCVector<HeapPtrObject, 1, ZoneAllocPolicy>;
6767
// `DuplicatesPossible` will allow this and map both wrappers to the same (now
6868
// tenured) source string.
6969
using StringWrapperMap =
70-
NurseryAwareHashMap<JSString*, JSString*, DefaultHasher<JSString*>,
71-
ZoneAllocPolicy, DuplicatesPossible>;
70+
NurseryAwareHashMap<JSString*, JSString*, ZoneAllocPolicy,
71+
DuplicatesPossible>;
7272

7373
class MOZ_NON_TEMPORARY_CLASS ExternalStringCache {
7474
static const size_t NumEntries = 4;

js/src/vm/Compartment.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ void Compartment::checkObjectWrappersAfterMovingGC() {
5858
// wrapper map that points into the nursery, and that the hash table entries
5959
// are discoverable.
6060
auto key = e.front().key();
61-
CheckGCThingAfterMovingGC(key);
61+
CheckGCThingAfterMovingGC(key.get());
6262

6363
auto ptr = crossCompartmentObjectWrappers.lookup(key);
6464
MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &e.front());

js/src/vm/Compartment.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@ namespace js {
3131
class ObjectWrapperMap {
3232
static const size_t InitialInnerMapSize = 4;
3333

34-
using InnerMap =
35-
NurseryAwareHashMap<JSObject*, JSObject*, DefaultHasher<JSObject*>,
36-
ZoneAllocPolicy>;
34+
using InnerMap = NurseryAwareHashMap<JSObject*, JSObject*, ZoneAllocPolicy>;
3735
using OuterMap = GCHashMap<JS::Compartment*, InnerMap,
3836
DefaultHasher<JS::Compartment*>, ZoneAllocPolicy>;
3937

@@ -247,8 +245,8 @@ class ObjectWrapperMap {
247245
};
248246

249247
using StringWrapperMap =
250-
NurseryAwareHashMap<JSString*, JSString*, DefaultHasher<JSString*>,
251-
ZoneAllocPolicy, DuplicatesPossible>;
248+
NurseryAwareHashMap<JSString*, JSString*, ZoneAllocPolicy,
249+
DuplicatesPossible>;
252250

253251
} // namespace js
254252

0 commit comments

Comments
 (0)