WeakMap.cpp (6506B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 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 #include "gc/WeakMap-inl.h" 8 9 #include <string.h> 10 11 #include "gc/PublicIterators.h" 12 #include "vm/JSObject.h" 13 14 #include "gc/Marking-inl.h" 15 #include "gc/StoreBuffer-inl.h" 16 17 using namespace js; 18 using namespace js::gc; 19 20 WeakMapBase::WeakMapBase(JSObject* memOf, Zone* zone) 21 : memberOf(memOf), zone_(zone) { 22 MOZ_ASSERT_IF(memberOf, memberOf->compartment()->zone() == zone); 23 MOZ_ASSERT(!IsMarked(mapColor())); 24 } 25 26 void WeakMapBase::unmarkZone(JS::Zone* zone) { 27 zone->gcEphemeronEdges().clearAndCompact(); 28 for (WeakMapBase* m : zone->gcWeakMapList()) { 29 m->setMapColor(CellColor::White); 30 } 31 } 32 33 void Zone::traceWeakMaps(JSTracer* trc) { 34 MOZ_ASSERT(trc->weakMapAction() != JS::WeakMapTraceAction::Skip); 35 for (WeakMapBase* m : gcWeakMapList()) { 36 m->trace(trc); 37 TraceNullableEdge(trc, &m->memberOf, "memberOf"); 38 } 39 } 40 41 bool WeakMapBase::markMap(MarkColor markColor) { 42 // We may be marking in parallel here so use a compare exchange loop to handle 43 // concurrent updates to the map color. 44 // 45 // The color increases monotonically; we don't downgrade from black to gray. 46 // 47 // We can attempt to mark gray after marking black when a barrier pushes the 48 // map object onto the black mark stack when it's already present on the 49 // gray mark stack, since this is marked later. 50 51 uint32_t targetColor = uint32_t(markColor); 52 53 for (;;) { 54 uint32_t currentColor = mapColor_; 55 56 if (currentColor >= targetColor) { 57 return false; 58 } 59 60 if (mapColor_.compareExchange(currentColor, targetColor)) { 61 return true; 62 } 63 } 64 } 65 66 bool WeakMapBase::addEphemeronEdgesForEntry(MarkColor mapColor, 67 TenuredCell* key, Cell* delegate, 68 TenuredCell* value) { 69 if (delegate) { 70 if (!delegate->isTenured()) { 71 MOZ_ASSERT(false); 72 // This case is probably not possible, or wasn't at the time of this 73 // writing. It requires a tenured wrapper with a nursery wrappee delegate, 74 // which is tough to create given that the wrapper has to be created after 75 // its target, and in fact appears impossible because the delegate has to 76 // be created after the GC begins to avoid being tenured at the beginning 77 // of the GC, and adding the key to the weakmap will mark the key via a 78 // pre-barrier. But still, handling this case is straightforward: 79 80 // The delegate is already being kept alive in a minor GC since it has an 81 // edge from a tenured cell (the key). Make sure the key stays alive too. 82 delegate->storeBuffer()->putWholeCell(key); 83 } else if (!addEphemeronEdge(mapColor, &delegate->asTenured(), key)) { 84 return false; 85 } 86 } 87 88 if (value && !addEphemeronEdge(mapColor, key, value)) { 89 return false; 90 } 91 92 return true; 93 } 94 95 bool WeakMapBase::addEphemeronEdge(MarkColor color, gc::TenuredCell* src, 96 gc::TenuredCell* dst) { 97 // Add an implicit edge from |src| to |dst|. 98 99 auto& edgeTable = src->zone()->gcEphemeronEdges(); 100 auto p = edgeTable.lookupForAdd(src); 101 if (!p) { 102 if (!edgeTable.add(p, src, EphemeronEdgeVector())) { 103 return false; 104 } 105 } 106 return p->value().emplaceBack(color, dst); 107 } 108 109 #if defined(JS_GC_ZEAL) || defined(DEBUG) 110 bool WeakMapBase::checkMarkingForZone(JS::Zone* zone) { 111 // This is called at the end of marking. 112 MOZ_ASSERT(zone->isGCMarking()); 113 114 bool ok = true; 115 for (WeakMapBase* m : zone->gcWeakMapList()) { 116 if (IsMarked(m->mapColor()) && !m->checkMarking()) { 117 ok = false; 118 } 119 } 120 121 return ok; 122 } 123 #endif 124 125 #ifdef JSGC_HASH_TABLE_CHECKS 126 /* static */ 127 void WeakMapBase::checkWeakMapsAfterMovingGC(JS::Zone* zone) { 128 for (WeakMapBase* map : zone->gcWeakMapList()) { 129 map->checkAfterMovingGC(); 130 } 131 } 132 #endif 133 134 bool WeakMapBase::markZoneIteratively(JS::Zone* zone, GCMarker* marker) { 135 bool markedAny = false; 136 for (WeakMapBase* m : zone->gcWeakMapList()) { 137 if (IsMarked(m->mapColor()) && m->markEntries(marker)) { 138 markedAny = true; 139 } 140 } 141 return markedAny; 142 } 143 144 bool WeakMapBase::findSweepGroupEdgesForZone(JS::Zone* atomsZone, 145 JS::Zone* mapZone) { 146 for (WeakMapBase* m : mapZone->gcWeakMapList()) { 147 if (!m->findSweepGroupEdges(atomsZone)) { 148 return false; 149 } 150 } 151 return true; 152 } 153 154 void Zone::sweepWeakMaps(JSTracer* trc) { 155 for (WeakMapBase* m = gcWeakMapList().getFirst(); m;) { 156 WeakMapBase* next = m->getNext(); 157 if (IsMarked(m->mapColor())) { 158 m->traceWeakEdges(trc); 159 } else { 160 m->clearAndCompact(); 161 m->removeFrom(gcWeakMapList()); 162 } 163 m = next; 164 } 165 166 #ifdef DEBUG 167 for (WeakMapBase* m : gcWeakMapList()) { 168 MOZ_ASSERT(m->isInList() && IsMarked(m->mapColor())); 169 } 170 #endif 171 } 172 173 void WeakMapBase::traceAllMappings(WeakMapTracer* tracer) { 174 JSRuntime* rt = tracer->runtime; 175 for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { 176 for (WeakMapBase* m : zone->gcWeakMapList()) { 177 // The WeakMapTracer callback is not allowed to GC. 178 JS::AutoSuppressGCAnalysis nogc; 179 m->traceMappings(tracer); 180 } 181 } 182 } 183 184 bool WeakMapBase::saveZoneMarkedWeakMaps(JS::Zone* zone, 185 WeakMapColors& markedWeakMaps) { 186 for (WeakMapBase* m : zone->gcWeakMapList()) { 187 if (IsMarked(m->mapColor()) && !markedWeakMaps.put(m, m->mapColor())) { 188 return false; 189 } 190 } 191 return true; 192 } 193 194 void WeakMapBase::restoreMarkedWeakMaps(WeakMapColors& markedWeakMaps) { 195 for (WeakMapColors::Range r = markedWeakMaps.all(); !r.empty(); 196 r.popFront()) { 197 WeakMapBase* map = r.front().key(); 198 MOZ_ASSERT(map->zone()->isGCMarking()); 199 MOZ_ASSERT(!IsMarked(map->mapColor())); 200 map->setMapColor(r.front().value()); 201 } 202 } 203 204 void WeakMapBase::setHasNurseryEntries() { 205 MOZ_ASSERT(!hasNurseryEntries); 206 207 AutoEnterOOMUnsafeRegion oomUnsafe; 208 209 GCRuntime* gc = &zone()->runtimeFromMainThread()->gc; 210 if (!gc->nursery().addWeakMapWithNurseryEntries(this)) { 211 oomUnsafe.crash("WeakMapBase::setHasNurseryEntries"); 212 } 213 214 hasNurseryEntries = true; 215 } 216 217 namespace js { 218 template class WeakMap<JSObject*, JSObject*, ZoneAllocPolicy>; 219 } // namespace js