]>
Commit | Line | Data |
---|---|---|
9dae56ea A |
1 | /* |
2 | * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) | |
3 | * Copyright (C) 2001 Peter Kelly (pmk@post.com) | |
4 | * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | * | |
20 | */ | |
21 | ||
22 | #ifndef Collector_h | |
23 | #define Collector_h | |
24 | ||
25 | #include <stddef.h> | |
26 | #include <string.h> | |
27 | #include <wtf/HashCountedSet.h> | |
28 | #include <wtf/HashSet.h> | |
29 | #include <wtf/Noncopyable.h> | |
30 | #include <wtf/OwnPtr.h> | |
f9bf01c6 | 31 | #include <wtf/StdLibExtras.h> |
9dae56ea A |
32 | #include <wtf/Threading.h> |
33 | ||
9dae56ea A |
34 | #if ENABLE(JSC_MULTIPLE_THREADS) |
35 | #include <pthread.h> | |
36 | #endif | |
37 | ||
38 | #define ASSERT_CLASS_FITS_IN_CELL(class) COMPILE_ASSERT(sizeof(class) <= CELL_SIZE, class_fits_in_cell) | |
39 | ||
40 | namespace JSC { | |
41 | ||
9dae56ea A |
42 | class CollectorBlock; |
43 | class JSCell; | |
44 | class JSGlobalData; | |
ba379fdc | 45 | class JSValue; |
f9bf01c6 A |
46 | class MarkedArgumentBuffer; |
47 | class MarkStack; | |
9dae56ea A |
48 | |
49 | enum OperationInProgress { NoOperation, Allocation, Collection }; | |
9dae56ea | 50 | |
f9bf01c6 | 51 | class LiveObjectIterator; |
9dae56ea A |
52 | |
53 | struct CollectorHeap { | |
f9bf01c6 A |
54 | size_t nextBlock; |
55 | size_t nextCell; | |
9dae56ea | 56 | CollectorBlock** blocks; |
f9bf01c6 A |
57 | |
58 | void* nextNumber; | |
59 | ||
9dae56ea A |
60 | size_t numBlocks; |
61 | size_t usedBlocks; | |
9dae56ea | 62 | |
9dae56ea | 63 | size_t extraCost; |
f9bf01c6 | 64 | bool didShrink; |
9dae56ea A |
65 | |
66 | OperationInProgress operationInProgress; | |
67 | }; | |
68 | ||
f9bf01c6 | 69 | class Heap : public Noncopyable { |
9dae56ea A |
70 | public: |
71 | class Thread; | |
9dae56ea A |
72 | |
73 | void destroy(); | |
74 | ||
9dae56ea A |
75 | void* allocateNumber(size_t); |
76 | void* allocate(size_t); | |
77 | ||
9dae56ea | 78 | bool isBusy(); // true if an allocation or collection is in progress |
f9bf01c6 | 79 | void collectAllGarbage(); |
9dae56ea | 80 | |
f9bf01c6 A |
81 | static const size_t minExtraCost = 256; |
82 | static const size_t maxExtraCost = 1024 * 1024; | |
9dae56ea A |
83 | |
84 | void reportExtraMemoryCost(size_t cost); | |
85 | ||
f9bf01c6 | 86 | size_t objectCount() const; |
9dae56ea A |
87 | struct Statistics { |
88 | size_t size; | |
89 | size_t free; | |
90 | }; | |
91 | Statistics statistics() const; | |
92 | ||
ba379fdc A |
93 | void protect(JSValue); |
94 | void unprotect(JSValue); | |
9dae56ea | 95 | |
ba379fdc | 96 | static Heap* heap(JSValue); // 0 for immediate values |
f9bf01c6 | 97 | static Heap* heap(JSCell*); |
9dae56ea A |
98 | |
99 | size_t globalObjectCount(); | |
100 | size_t protectedObjectCount(); | |
101 | size_t protectedGlobalObjectCount(); | |
102 | HashCountedSet<const char*>* protectedObjectTypeCounts(); | |
103 | ||
104 | void registerThread(); // Only needs to be called by clients that can use the same heap from multiple threads. | |
105 | ||
106 | static bool isCellMarked(const JSCell*); | |
107 | static void markCell(JSCell*); | |
108 | ||
f9bf01c6 | 109 | void markConservatively(MarkStack&, void* start, void* end); |
9dae56ea | 110 | |
ba379fdc | 111 | HashSet<MarkedArgumentBuffer*>& markListSet() { if (!m_markListSet) m_markListSet = new HashSet<MarkedArgumentBuffer*>; return *m_markListSet; } |
9dae56ea A |
112 | |
113 | JSGlobalData* globalData() const { return m_globalData; } | |
114 | static bool isNumber(JSCell*); | |
115 | ||
f9bf01c6 A |
116 | LiveObjectIterator primaryHeapBegin(); |
117 | LiveObjectIterator primaryHeapEnd(); | |
9dae56ea A |
118 | |
119 | private: | |
f9bf01c6 A |
120 | void reset(); |
121 | void sweep(); | |
9dae56ea A |
122 | static CollectorBlock* cellBlock(const JSCell*); |
123 | static size_t cellOffset(const JSCell*); | |
124 | ||
125 | friend class JSGlobalData; | |
126 | Heap(JSGlobalData*); | |
127 | ~Heap(); | |
128 | ||
f9bf01c6 A |
129 | NEVER_INLINE CollectorBlock* allocateBlock(); |
130 | NEVER_INLINE void freeBlock(size_t); | |
131 | NEVER_INLINE void freeBlockPtr(CollectorBlock*); | |
132 | void freeBlocks(); | |
133 | void resizeBlocks(); | |
134 | void growBlocks(size_t neededBlocks); | |
135 | void shrinkBlocks(size_t neededBlocks); | |
136 | void clearMarkBits(); | |
137 | void clearMarkBits(CollectorBlock*); | |
138 | size_t markedCells(size_t startBlock = 0, size_t startCell = 0) const; | |
139 | ||
9dae56ea | 140 | void recordExtraCost(size_t); |
f9bf01c6 A |
141 | |
142 | void addToStatistics(Statistics&) const; | |
143 | ||
144 | void markRoots(); | |
145 | void markProtectedObjects(MarkStack&); | |
146 | void markCurrentThreadConservatively(MarkStack&); | |
147 | void markCurrentThreadConservativelyInternal(MarkStack&); | |
148 | void markOtherThreadConservatively(MarkStack&, Thread*); | |
149 | void markStackObjectsConservatively(MarkStack&); | |
9dae56ea A |
150 | |
151 | typedef HashCountedSet<JSCell*> ProtectCountSet; | |
152 | ||
f9bf01c6 | 153 | CollectorHeap m_heap; |
9dae56ea | 154 | |
9dae56ea A |
155 | ProtectCountSet m_protectedValues; |
156 | ||
ba379fdc | 157 | HashSet<MarkedArgumentBuffer*>* m_markListSet; |
9dae56ea A |
158 | |
159 | #if ENABLE(JSC_MULTIPLE_THREADS) | |
160 | void makeUsableFromMultipleThreads(); | |
161 | ||
162 | static void unregisterThread(void*); | |
163 | void unregisterThread(); | |
164 | ||
165 | Mutex m_registeredThreadsMutex; | |
166 | Thread* m_registeredThreads; | |
167 | pthread_key_t m_currentThreadRegistrar; | |
168 | #endif | |
169 | ||
170 | JSGlobalData* m_globalData; | |
171 | }; | |
172 | ||
173 | // tunable parameters | |
174 | template<size_t bytesPerWord> struct CellSize; | |
175 | ||
176 | // cell size needs to be a power of two for certain optimizations in collector.cpp | |
ba379fdc A |
177 | #if USE(JSVALUE32) |
178 | template<> struct CellSize<sizeof(uint32_t)> { static const size_t m_value = 32; }; | |
179 | #else | |
180 | template<> struct CellSize<sizeof(uint32_t)> { static const size_t m_value = 64; }; | |
181 | #endif | |
182 | template<> struct CellSize<sizeof(uint64_t)> { static const size_t m_value = 64; }; | |
183 | ||
f9bf01c6 | 184 | const size_t BLOCK_SIZE = 64 * 1024; // 64k |
9dae56ea A |
185 | |
186 | // derived constants | |
187 | const size_t BLOCK_OFFSET_MASK = BLOCK_SIZE - 1; | |
188 | const size_t BLOCK_MASK = ~BLOCK_OFFSET_MASK; | |
189 | const size_t MINIMUM_CELL_SIZE = CellSize<sizeof(void*)>::m_value; | |
190 | const size_t CELL_ARRAY_LENGTH = (MINIMUM_CELL_SIZE / sizeof(double)) + (MINIMUM_CELL_SIZE % sizeof(double) != 0 ? sizeof(double) : 0); | |
191 | const size_t CELL_SIZE = CELL_ARRAY_LENGTH * sizeof(double); | |
192 | const size_t SMALL_CELL_SIZE = CELL_SIZE / 2; | |
193 | const size_t CELL_MASK = CELL_SIZE - 1; | |
194 | const size_t CELL_ALIGN_MASK = ~CELL_MASK; | |
f9bf01c6 A |
195 | const size_t CELLS_PER_BLOCK = (BLOCK_SIZE - sizeof(Heap*)) * 8 * CELL_SIZE / (8 * CELL_SIZE + 1) / CELL_SIZE; // one bitmap byte can represent 8 cells. |
196 | ||
9dae56ea A |
197 | const size_t BITMAP_SIZE = (CELLS_PER_BLOCK + 7) / 8; |
198 | const size_t BITMAP_WORDS = (BITMAP_SIZE + 3) / sizeof(uint32_t); | |
f9bf01c6 | 199 | |
9dae56ea A |
200 | struct CollectorBitmap { |
201 | uint32_t bits[BITMAP_WORDS]; | |
202 | bool get(size_t n) const { return !!(bits[n >> 5] & (1 << (n & 0x1F))); } | |
203 | void set(size_t n) { bits[n >> 5] |= (1 << (n & 0x1F)); } | |
204 | void clear(size_t n) { bits[n >> 5] &= ~(1 << (n & 0x1F)); } | |
205 | void clearAll() { memset(bits, 0, sizeof(bits)); } | |
f9bf01c6 A |
206 | size_t count(size_t startCell = 0) |
207 | { | |
208 | size_t result = 0; | |
209 | for ( ; (startCell & 0x1F) != 0; ++startCell) { | |
210 | if (get(startCell)) | |
211 | ++result; | |
212 | } | |
213 | for (size_t i = startCell >> 5; i < BITMAP_WORDS; ++i) | |
214 | result += WTF::bitCount(bits[i]); | |
215 | return result; | |
216 | } | |
217 | size_t isEmpty() // Much more efficient than testing count() == 0. | |
218 | { | |
219 | for (size_t i = 0; i < BITMAP_WORDS; ++i) | |
220 | if (bits[i] != 0) | |
221 | return false; | |
222 | return true; | |
223 | } | |
9dae56ea A |
224 | }; |
225 | ||
226 | struct CollectorCell { | |
f9bf01c6 | 227 | double memory[CELL_ARRAY_LENGTH]; |
9dae56ea A |
228 | }; |
229 | ||
230 | class CollectorBlock { | |
231 | public: | |
232 | CollectorCell cells[CELLS_PER_BLOCK]; | |
9dae56ea A |
233 | CollectorBitmap marked; |
234 | Heap* heap; | |
9dae56ea A |
235 | }; |
236 | ||
f9bf01c6 | 237 | struct HeapConstants { |
9dae56ea A |
238 | static const size_t cellSize = CELL_SIZE; |
239 | static const size_t cellsPerBlock = CELLS_PER_BLOCK; | |
9dae56ea A |
240 | typedef CollectorCell Cell; |
241 | typedef CollectorBlock Block; | |
242 | }; | |
243 | ||
9dae56ea A |
244 | inline CollectorBlock* Heap::cellBlock(const JSCell* cell) |
245 | { | |
246 | return reinterpret_cast<CollectorBlock*>(reinterpret_cast<uintptr_t>(cell) & BLOCK_MASK); | |
247 | } | |
248 | ||
9dae56ea A |
249 | inline size_t Heap::cellOffset(const JSCell* cell) |
250 | { | |
251 | return (reinterpret_cast<uintptr_t>(cell) & BLOCK_OFFSET_MASK) / CELL_SIZE; | |
252 | } | |
253 | ||
254 | inline bool Heap::isCellMarked(const JSCell* cell) | |
255 | { | |
256 | return cellBlock(cell)->marked.get(cellOffset(cell)); | |
257 | } | |
258 | ||
259 | inline void Heap::markCell(JSCell* cell) | |
260 | { | |
261 | cellBlock(cell)->marked.set(cellOffset(cell)); | |
262 | } | |
263 | ||
264 | inline void Heap::reportExtraMemoryCost(size_t cost) | |
265 | { | |
f9bf01c6 A |
266 | if (cost > minExtraCost) |
267 | recordExtraCost(cost); | |
268 | } | |
269 | ||
270 | inline void* Heap::allocateNumber(size_t s) | |
271 | { | |
272 | if (void* result = m_heap.nextNumber) { | |
273 | m_heap.nextNumber = 0; | |
274 | return result; | |
275 | } | |
276 | ||
277 | void* result = allocate(s); | |
278 | m_heap.nextNumber = static_cast<char*>(result) + (CELL_SIZE / 2); | |
279 | return result; | |
9dae56ea A |
280 | } |
281 | ||
282 | } // namespace JSC | |
283 | ||
284 | #endif /* Collector_h */ |