/src/mozilla-central/js/src/vm/AtomsTable.h
Line | Count | Source (jump to first uncovered line) |
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 | | /* |
8 | | * Implementation details of the atoms table. |
9 | | */ |
10 | | |
11 | | #ifndef vm_AtomsTable_h |
12 | | #define vm_AtomsTable_h |
13 | | |
14 | | #include "js/GCHashTable.h" |
15 | | #include "js/TypeDecls.h" |
16 | | #include "vm/JSAtom.h" |
17 | | |
18 | | /* |
19 | | * The atoms table is a mapping from strings to JSAtoms that supports concurrent |
20 | | * access and incremental sweeping. |
21 | | * |
22 | | * The table is partitioned based on the key into multiple sub-tables. Each |
23 | | * sub-table is protected by a lock to ensure safety when accessed by helper |
24 | | * threads. Concurrent access improves performance of off-thread parsing which |
25 | | * frequently creates large numbers of atoms. Locking is only required when |
26 | | * off-thread parsing is running. |
27 | | */ |
28 | | |
29 | | namespace js { |
30 | | |
31 | | // Take all atoms table locks to allow iterating over cells in the atoms zone. |
32 | | class MOZ_RAII AutoLockAllAtoms |
33 | | { |
34 | | JSRuntime* runtime; |
35 | | |
36 | | public: |
37 | | explicit AutoLockAllAtoms(JSRuntime* rt); |
38 | | ~AutoLockAllAtoms(); |
39 | | }; |
40 | | |
41 | | class AtomStateEntry |
42 | | { |
43 | | uintptr_t bits; |
44 | | |
45 | | static const uintptr_t NO_TAG_MASK = uintptr_t(-1) - 1; |
46 | | |
47 | | public: |
48 | 0 | AtomStateEntry() : bits(0) {} |
49 | 34.0k | AtomStateEntry(const AtomStateEntry& other) : bits(other.bits) {} |
50 | | AtomStateEntry(JSAtom* ptr, bool tagged) |
51 | | : bits(uintptr_t(ptr) | uintptr_t(tagged)) |
52 | 14.6k | { |
53 | 14.6k | MOZ_ASSERT((uintptr_t(ptr) & 0x1) == 0); |
54 | 14.6k | } |
55 | | |
56 | 0 | bool isPinned() const { |
57 | 0 | return bits & 0x1; |
58 | 0 | } |
59 | | |
60 | | /* |
61 | | * Non-branching code sequence. Note that the const_cast is safe because |
62 | | * the hash function doesn't consider the tag to be a portion of the key. |
63 | | */ |
64 | 15 | void setPinned(bool pinned) const { |
65 | 15 | const_cast<AtomStateEntry*>(this)->bits |= uintptr_t(pinned); |
66 | 15 | } |
67 | | |
68 | 3.32M | JSAtom* asPtrUnbarriered() const { |
69 | 3.32M | MOZ_ASSERT(bits); |
70 | 3.32M | return reinterpret_cast<JSAtom*>(bits & NO_TAG_MASK); |
71 | 3.32M | } |
72 | | |
73 | | JSAtom* asPtr(JSContext* cx) const; |
74 | | |
75 | 0 | bool needsSweep() { |
76 | 0 | JSAtom* atom = asPtrUnbarriered(); |
77 | 0 | return gc::IsAboutToBeFinalizedUnbarriered(&atom); |
78 | 0 | } |
79 | | }; |
80 | | |
81 | | struct AtomHasher |
82 | | { |
83 | | struct Lookup; |
84 | | static inline HashNumber hash(const Lookup& l); |
85 | | static MOZ_ALWAYS_INLINE bool match(const AtomStateEntry& entry, const Lookup& lookup); |
86 | 0 | static void rekey(AtomStateEntry& k, const AtomStateEntry& newKey) { k = newKey; } |
87 | | }; |
88 | | |
89 | | using AtomSet = JS::GCHashSet<AtomStateEntry, AtomHasher, SystemAllocPolicy>; |
90 | | |
91 | | // This class is a wrapper for AtomSet that is used to ensure the AtomSet is |
92 | | // not modified. It should only expose read-only methods from AtomSet. |
93 | | // Note however that the atoms within the table can be marked during GC. |
94 | | class FrozenAtomSet |
95 | | { |
96 | | AtomSet* mSet; |
97 | | |
98 | | public: |
99 | | // This constructor takes ownership of the passed-in AtomSet. |
100 | 3 | explicit FrozenAtomSet(AtomSet* set) { mSet = set; } |
101 | | |
102 | 0 | ~FrozenAtomSet() { js_delete(mSet); } |
103 | | |
104 | | MOZ_ALWAYS_INLINE AtomSet::Ptr readonlyThreadsafeLookup(const AtomSet::Lookup& l) const; |
105 | | |
106 | 0 | size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { |
107 | 0 | return mSet->shallowSizeOfIncludingThis(mallocSizeOf); |
108 | 0 | } |
109 | | |
110 | | typedef AtomSet::Range Range; |
111 | | |
112 | 0 | AtomSet::Range all() const { return mSet->all(); } |
113 | | }; |
114 | | |
115 | | class AtomsTable |
116 | | { |
117 | | static const size_t PartitionShift = 5; |
118 | | static const size_t PartitionCount = 1 << PartitionShift; |
119 | | |
120 | | // Use a low initial capacity for atom hash tables to avoid penalizing runtimes |
121 | | // which create a small number of atoms. |
122 | | static const size_t InitialTableSize = 16; |
123 | | |
124 | | // A single partition, representing a subset of the atoms in the table. |
125 | | struct Partition |
126 | | { |
127 | | explicit Partition(uint32_t index); |
128 | | ~Partition(); |
129 | | |
130 | | // Lock that must be held to access this set. |
131 | | Mutex lock; |
132 | | |
133 | | // The atoms in this set. |
134 | | AtomSet atoms; |
135 | | |
136 | | // Set of atoms added while the |atoms| set is being swept. |
137 | | AtomSet* atomsAddedWhileSweeping; |
138 | | }; |
139 | | |
140 | | Partition* partitions[PartitionCount]; |
141 | | |
142 | | #ifdef DEBUG |
143 | | bool allPartitionsLocked = false; |
144 | | #endif |
145 | | |
146 | | public: |
147 | | class AutoLock; |
148 | | |
149 | | // An iterator used for sweeping atoms incrementally. |
150 | | class SweepIterator |
151 | | { |
152 | | AtomsTable& atoms; |
153 | | size_t partitionIndex; |
154 | | mozilla::Maybe<AtomSet::Enum> atomsIter; |
155 | | |
156 | | void settle(); |
157 | | void startSweepingPartition(); |
158 | | void finishSweepingPartition(); |
159 | | |
160 | | public: |
161 | | explicit SweepIterator(AtomsTable& atoms); |
162 | | bool empty() const; |
163 | | JSAtom* front() const; |
164 | | void removeFront(); |
165 | | void popFront(); |
166 | | }; |
167 | | |
168 | | ~AtomsTable(); |
169 | | bool init(); |
170 | | |
171 | | template <typename CharT> |
172 | | MOZ_ALWAYS_INLINE JSAtom* atomizeAndCopyChars(JSContext* cx, |
173 | | const CharT* tbchars, size_t length, |
174 | | PinningBehavior pin, |
175 | | const mozilla::Maybe<uint32_t>& indexValue, |
176 | | const AtomHasher::Lookup& lookup); |
177 | | |
178 | | void pinExistingAtom(JSContext* cx, JSAtom* atom); |
179 | | |
180 | | void tracePinnedAtoms(JSTracer* trc, const AutoAccessAtomsZone& access); |
181 | | |
182 | | // Sweep all atoms non-incrementally. |
183 | | void sweepAll(JSRuntime* rt); |
184 | | |
185 | | bool startIncrementalSweep(); |
186 | | |
187 | | // Sweep some atoms incrementally and return whether we finished. |
188 | | bool sweepIncrementally(SweepIterator& atomsToSweep, SliceBudget& budget); |
189 | | |
190 | | #ifdef DEBUG |
191 | | bool mainThreadHasAllLocks() const { |
192 | | return allPartitionsLocked; |
193 | | } |
194 | | #endif |
195 | | |
196 | | size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; |
197 | | |
198 | | private: |
199 | | // Map a key to a partition based on its hash. |
200 | | MOZ_ALWAYS_INLINE size_t getPartitionIndex(const AtomHasher::Lookup& lookup); |
201 | | |
202 | | void tracePinnedAtomsInSet(JSTracer* trc, AtomSet& atoms); |
203 | | void mergeAtomsAddedWhileSweeping(Partition& partition); |
204 | | |
205 | | friend class AutoLockAllAtoms; |
206 | | void lockAll(); |
207 | | void unlockAll(); |
208 | | }; |
209 | | |
210 | | } // namespace js |
211 | | |
212 | | #endif /* vm_AtomsTable_h */ |