]>
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 | ||
4e4e5a6f A |
38 | #if OS(SYMBIAN) |
39 | #include <wtf/symbian/BlockAllocatorSymbian.h> | |
40 | #endif | |
41 | ||
9dae56ea A |
42 | #define ASSERT_CLASS_FITS_IN_CELL(class) COMPILE_ASSERT(sizeof(class) <= CELL_SIZE, class_fits_in_cell) |
43 | ||
44 | namespace JSC { | |
45 | ||
9dae56ea A |
46 | class CollectorBlock; |
47 | class JSCell; | |
48 | class JSGlobalData; | |
ba379fdc | 49 | class JSValue; |
f9bf01c6 A |
50 | class MarkedArgumentBuffer; |
51 | class MarkStack; | |
9dae56ea A |
52 | |
53 | enum OperationInProgress { NoOperation, Allocation, Collection }; | |
9dae56ea | 54 | |
f9bf01c6 | 55 | class LiveObjectIterator; |
9dae56ea A |
56 | |
57 | struct CollectorHeap { | |
f9bf01c6 A |
58 | size_t nextBlock; |
59 | size_t nextCell; | |
9dae56ea | 60 | CollectorBlock** blocks; |
f9bf01c6 A |
61 | |
62 | void* nextNumber; | |
63 | ||
9dae56ea A |
64 | size_t numBlocks; |
65 | size_t usedBlocks; | |
9dae56ea | 66 | |
9dae56ea | 67 | size_t extraCost; |
f9bf01c6 | 68 | bool didShrink; |
9dae56ea A |
69 | |
70 | OperationInProgress operationInProgress; | |
71 | }; | |
72 | ||
f9bf01c6 | 73 | class Heap : public Noncopyable { |
9dae56ea A |
74 | public: |
75 | class Thread; | |
9dae56ea A |
76 | |
77 | void destroy(); | |
78 | ||
9dae56ea A |
79 | void* allocateNumber(size_t); |
80 | void* allocate(size_t); | |
81 | ||
9dae56ea | 82 | bool isBusy(); // true if an allocation or collection is in progress |
f9bf01c6 | 83 | void collectAllGarbage(); |
9dae56ea | 84 | |
f9bf01c6 A |
85 | static const size_t minExtraCost = 256; |
86 | static const size_t maxExtraCost = 1024 * 1024; | |
9dae56ea A |
87 | |
88 | void reportExtraMemoryCost(size_t cost); | |
89 | ||
f9bf01c6 | 90 | size_t objectCount() const; |
9dae56ea A |
91 | struct Statistics { |
92 | size_t size; | |
93 | size_t free; | |
94 | }; | |
95 | Statistics statistics() const; | |
96 | ||
ba379fdc | 97 | void protect(JSValue); |
4e4e5a6f A |
98 | // Returns true if the value is no longer protected by any protect pointers |
99 | // (though it may still be alive due to heap/stack references). | |
100 | bool unprotect(JSValue); | |
9dae56ea | 101 | |
ba379fdc | 102 | static Heap* heap(JSValue); // 0 for immediate values |
f9bf01c6 | 103 | static Heap* heap(JSCell*); |
9dae56ea A |
104 | |
105 | size_t globalObjectCount(); | |
106 | size_t protectedObjectCount(); | |
107 | size_t protectedGlobalObjectCount(); | |
108 | HashCountedSet<const char*>* protectedObjectTypeCounts(); | |
4e4e5a6f | 109 | HashCountedSet<const char*>* objectTypeCounts(); |
9dae56ea A |
110 | |
111 | void registerThread(); // Only needs to be called by clients that can use the same heap from multiple threads. | |
112 | ||
113 | static bool isCellMarked(const JSCell*); | |
114 | static void markCell(JSCell*); | |
115 | ||
f9bf01c6 | 116 | void markConservatively(MarkStack&, void* start, void* end); |
9dae56ea | 117 | |
ba379fdc | 118 | HashSet<MarkedArgumentBuffer*>& markListSet() { if (!m_markListSet) m_markListSet = new HashSet<MarkedArgumentBuffer*>; return *m_markListSet; } |
9dae56ea A |
119 | |
120 | JSGlobalData* globalData() const { return m_globalData; } | |
121 | static bool isNumber(JSCell*); | |
122 | ||
f9bf01c6 A |
123 | LiveObjectIterator primaryHeapBegin(); |
124 | LiveObjectIterator primaryHeapEnd(); | |
9dae56ea A |
125 | |
126 | private: | |
f9bf01c6 A |
127 | void reset(); |
128 | void sweep(); | |
9dae56ea A |
129 | static CollectorBlock* cellBlock(const JSCell*); |
130 | static size_t cellOffset(const JSCell*); | |
131 | ||
132 | friend class JSGlobalData; | |
133 | Heap(JSGlobalData*); | |
134 | ~Heap(); | |
135 | ||
f9bf01c6 A |
136 | NEVER_INLINE CollectorBlock* allocateBlock(); |
137 | NEVER_INLINE void freeBlock(size_t); | |
138 | NEVER_INLINE void freeBlockPtr(CollectorBlock*); | |
139 | void freeBlocks(); | |
140 | void resizeBlocks(); | |
141 | void growBlocks(size_t neededBlocks); | |
142 | void shrinkBlocks(size_t neededBlocks); | |
143 | void clearMarkBits(); | |
144 | void clearMarkBits(CollectorBlock*); | |
145 | size_t markedCells(size_t startBlock = 0, size_t startCell = 0) const; | |
146 | ||
9dae56ea | 147 | void recordExtraCost(size_t); |
f9bf01c6 A |
148 | |
149 | void addToStatistics(Statistics&) const; | |
150 | ||
151 | void markRoots(); | |
152 | void markProtectedObjects(MarkStack&); | |
153 | void markCurrentThreadConservatively(MarkStack&); | |
154 | void markCurrentThreadConservativelyInternal(MarkStack&); | |
155 | void markOtherThreadConservatively(MarkStack&, Thread*); | |
156 | void markStackObjectsConservatively(MarkStack&); | |
9dae56ea A |
157 | |
158 | typedef HashCountedSet<JSCell*> ProtectCountSet; | |
159 | ||
f9bf01c6 | 160 | CollectorHeap m_heap; |
9dae56ea | 161 | |
9dae56ea A |
162 | ProtectCountSet m_protectedValues; |
163 | ||
ba379fdc | 164 | HashSet<MarkedArgumentBuffer*>* m_markListSet; |
9dae56ea A |
165 | |
166 | #if ENABLE(JSC_MULTIPLE_THREADS) | |
167 | void makeUsableFromMultipleThreads(); | |
168 | ||
169 | static void unregisterThread(void*); | |
170 | void unregisterThread(); | |
171 | ||
172 | Mutex m_registeredThreadsMutex; | |
173 | Thread* m_registeredThreads; | |
174 | pthread_key_t m_currentThreadRegistrar; | |
175 | #endif | |
176 | ||
4e4e5a6f A |
177 | #if OS(SYMBIAN) |
178 | // Allocates collector blocks with correct alignment | |
179 | WTF::AlignedBlockAllocator m_blockallocator; | |
180 | #endif | |
181 | ||
9dae56ea A |
182 | JSGlobalData* m_globalData; |
183 | }; | |
184 | ||
185 | // tunable parameters | |
186 | template<size_t bytesPerWord> struct CellSize; | |
187 | ||
188 | // cell size needs to be a power of two for certain optimizations in collector.cpp | |
ba379fdc A |
189 | #if USE(JSVALUE32) |
190 | template<> struct CellSize<sizeof(uint32_t)> { static const size_t m_value = 32; }; | |
191 | #else | |
192 | template<> struct CellSize<sizeof(uint32_t)> { static const size_t m_value = 64; }; | |
193 | #endif | |
194 | template<> struct CellSize<sizeof(uint64_t)> { static const size_t m_value = 64; }; | |
195 | ||
f9bf01c6 | 196 | const size_t BLOCK_SIZE = 64 * 1024; // 64k |
9dae56ea A |
197 | |
198 | // derived constants | |
199 | const size_t BLOCK_OFFSET_MASK = BLOCK_SIZE - 1; | |
200 | const size_t BLOCK_MASK = ~BLOCK_OFFSET_MASK; | |
201 | const size_t MINIMUM_CELL_SIZE = CellSize<sizeof(void*)>::m_value; | |
202 | const size_t CELL_ARRAY_LENGTH = (MINIMUM_CELL_SIZE / sizeof(double)) + (MINIMUM_CELL_SIZE % sizeof(double) != 0 ? sizeof(double) : 0); | |
203 | const size_t CELL_SIZE = CELL_ARRAY_LENGTH * sizeof(double); | |
204 | const size_t SMALL_CELL_SIZE = CELL_SIZE / 2; | |
205 | const size_t CELL_MASK = CELL_SIZE - 1; | |
206 | const size_t CELL_ALIGN_MASK = ~CELL_MASK; | |
f9bf01c6 A |
207 | 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. |
208 | ||
9dae56ea A |
209 | const size_t BITMAP_SIZE = (CELLS_PER_BLOCK + 7) / 8; |
210 | const size_t BITMAP_WORDS = (BITMAP_SIZE + 3) / sizeof(uint32_t); | |
f9bf01c6 | 211 | |
9dae56ea A |
212 | struct CollectorBitmap { |
213 | uint32_t bits[BITMAP_WORDS]; | |
214 | bool get(size_t n) const { return !!(bits[n >> 5] & (1 << (n & 0x1F))); } | |
215 | void set(size_t n) { bits[n >> 5] |= (1 << (n & 0x1F)); } | |
216 | void clear(size_t n) { bits[n >> 5] &= ~(1 << (n & 0x1F)); } | |
217 | void clearAll() { memset(bits, 0, sizeof(bits)); } | |
f9bf01c6 A |
218 | size_t count(size_t startCell = 0) |
219 | { | |
220 | size_t result = 0; | |
221 | for ( ; (startCell & 0x1F) != 0; ++startCell) { | |
222 | if (get(startCell)) | |
223 | ++result; | |
224 | } | |
225 | for (size_t i = startCell >> 5; i < BITMAP_WORDS; ++i) | |
226 | result += WTF::bitCount(bits[i]); | |
227 | return result; | |
228 | } | |
229 | size_t isEmpty() // Much more efficient than testing count() == 0. | |
230 | { | |
231 | for (size_t i = 0; i < BITMAP_WORDS; ++i) | |
232 | if (bits[i] != 0) | |
233 | return false; | |
234 | return true; | |
235 | } | |
9dae56ea A |
236 | }; |
237 | ||
238 | struct CollectorCell { | |
f9bf01c6 | 239 | double memory[CELL_ARRAY_LENGTH]; |
9dae56ea A |
240 | }; |
241 | ||
242 | class CollectorBlock { | |
243 | public: | |
244 | CollectorCell cells[CELLS_PER_BLOCK]; | |
9dae56ea A |
245 | CollectorBitmap marked; |
246 | Heap* heap; | |
9dae56ea A |
247 | }; |
248 | ||
f9bf01c6 | 249 | struct HeapConstants { |
9dae56ea A |
250 | static const size_t cellSize = CELL_SIZE; |
251 | static const size_t cellsPerBlock = CELLS_PER_BLOCK; | |
9dae56ea A |
252 | typedef CollectorCell Cell; |
253 | typedef CollectorBlock Block; | |
254 | }; | |
255 | ||
9dae56ea A |
256 | inline CollectorBlock* Heap::cellBlock(const JSCell* cell) |
257 | { | |
258 | return reinterpret_cast<CollectorBlock*>(reinterpret_cast<uintptr_t>(cell) & BLOCK_MASK); | |
259 | } | |
260 | ||
9dae56ea A |
261 | inline size_t Heap::cellOffset(const JSCell* cell) |
262 | { | |
263 | return (reinterpret_cast<uintptr_t>(cell) & BLOCK_OFFSET_MASK) / CELL_SIZE; | |
264 | } | |
265 | ||
266 | inline bool Heap::isCellMarked(const JSCell* cell) | |
267 | { | |
268 | return cellBlock(cell)->marked.get(cellOffset(cell)); | |
269 | } | |
270 | ||
271 | inline void Heap::markCell(JSCell* cell) | |
272 | { | |
273 | cellBlock(cell)->marked.set(cellOffset(cell)); | |
274 | } | |
275 | ||
276 | inline void Heap::reportExtraMemoryCost(size_t cost) | |
277 | { | |
f9bf01c6 A |
278 | if (cost > minExtraCost) |
279 | recordExtraCost(cost); | |
280 | } | |
281 | ||
282 | inline void* Heap::allocateNumber(size_t s) | |
283 | { | |
284 | if (void* result = m_heap.nextNumber) { | |
285 | m_heap.nextNumber = 0; | |
286 | return result; | |
287 | } | |
288 | ||
289 | void* result = allocate(s); | |
290 | m_heap.nextNumber = static_cast<char*>(result) + (CELL_SIZE / 2); | |
291 | return result; | |
9dae56ea A |
292 | } |
293 | ||
294 | } // namespace JSC | |
295 | ||
296 | #endif /* Collector_h */ |