]>
Commit | Line | Data |
---|---|---|
6fe7ccc8 A |
1 | /* |
2 | * Copyright (C) 2012 Apple Inc. All rights reserved. | |
3 | * | |
4 | * Redistribution and use in source and binary forms, with or without | |
5 | * modification, are permitted provided that the following conditions | |
6 | * are met: | |
7 | * 1. Redistributions of source code must retain the above copyright | |
8 | * notice, this list of conditions and the following disclaimer. | |
9 | * 2. Redistributions in binary form must reproduce the above copyright | |
10 | * notice, this list of conditions and the following disclaimer in the | |
11 | * documentation and/or other materials provided with the distribution. | |
12 | * | |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' | |
14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | |
15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS | |
17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
23 | * THE POSSIBILITY OF SUCH DAMAGE. | |
24 | */ | |
25 | ||
26 | #ifndef BlockAllocator_h | |
27 | #define BlockAllocator_h | |
28 | ||
81345200 | 29 | #include "GCActivityCallback.h" |
93a37866 A |
30 | #include "HeapBlock.h" |
31 | #include "Region.h" | |
81345200 | 32 | #include <condition_variable> |
6fe7ccc8 A |
33 | #include <wtf/DoublyLinkedList.h> |
34 | #include <wtf/Forward.h> | |
93a37866 A |
35 | #include <wtf/PageAllocationAligned.h> |
36 | #include <wtf/TCSpinLock.h> | |
6fe7ccc8 A |
37 | #include <wtf/Threading.h> |
38 | ||
39 | namespace JSC { | |
40 | ||
93a37866 | 41 | class BlockAllocator; |
81345200 | 42 | class CodeBlock; |
93a37866 A |
43 | class CopiedBlock; |
44 | class CopyWorkListSegment; | |
81345200 | 45 | template <typename T> class GCArraySegment; |
93a37866 | 46 | class HandleBlock; |
81345200 | 47 | class JSCell; |
93a37866 | 48 | class VM; |
93a37866 A |
49 | class MarkedBlock; |
50 | class WeakBlock; | |
6fe7ccc8 A |
51 | |
52 | // Simple allocator to reduce VM cost by holding onto blocks of memory for | |
53 | // short periods of time and then freeing them on a secondary thread. | |
54 | ||
55 | class BlockAllocator { | |
56 | public: | |
57 | BlockAllocator(); | |
58 | ~BlockAllocator(); | |
59 | ||
93a37866 A |
60 | template <typename T> DeadBlock* allocate(); |
61 | DeadBlock* allocateCustomSize(size_t blockSize, size_t blockAlignment); | |
62 | template <typename T> void deallocate(T*); | |
63 | template <typename T> void deallocateCustomSize(T*); | |
6fe7ccc8 | 64 | |
81345200 A |
65 | JS_EXPORT_PRIVATE void releaseFreeRegions(); |
66 | ||
6fe7ccc8 | 67 | private: |
81345200 | 68 | void waitForDuration(std::chrono::milliseconds); |
6fe7ccc8 | 69 | |
81345200 | 70 | friend ThreadIdentifier createBlockFreeingThread(BlockAllocator*); |
6fe7ccc8 A |
71 | void blockFreeingThreadMain(); |
72 | static void blockFreeingThreadStartFunc(void* heap); | |
73 | ||
93a37866 A |
74 | struct RegionSet { |
75 | RegionSet(size_t blockSize) | |
76 | : m_numberOfPartialRegions(0) | |
77 | , m_blockSize(blockSize) | |
78 | { | |
79 | } | |
80 | ||
81 | bool isEmpty() const | |
82 | { | |
83 | return m_fullRegions.isEmpty() && m_partialRegions.isEmpty(); | |
84 | } | |
85 | ||
86 | DoublyLinkedList<Region> m_fullRegions; | |
87 | DoublyLinkedList<Region> m_partialRegions; | |
88 | size_t m_numberOfPartialRegions; | |
89 | size_t m_blockSize; | |
90 | }; | |
91 | ||
92 | DeadBlock* tryAllocateFromRegion(RegionSet&, DoublyLinkedList<Region>&, size_t&); | |
93 | ||
94 | bool allRegionSetsAreEmpty() const; | |
93a37866 A |
95 | |
96 | template <typename T> RegionSet& regionSetFor(); | |
97 | ||
98 | SuperRegion m_superRegion; | |
99 | RegionSet m_copiedRegionSet; | |
100 | RegionSet m_markedRegionSet; | |
81345200 | 101 | // WeakBlocks and GCArraySegments use the same RegionSet since they're the same size. |
93a37866 A |
102 | RegionSet m_fourKBBlockRegionSet; |
103 | RegionSet m_workListRegionSet; | |
104 | ||
105 | DoublyLinkedList<Region> m_emptyRegions; | |
106 | size_t m_numberOfEmptyRegions; | |
6fe7ccc8 | 107 | |
6fe7ccc8 A |
108 | bool m_isCurrentlyAllocating; |
109 | bool m_blockFreeingThreadShouldQuit; | |
93a37866 | 110 | SpinLock m_regionLock; |
81345200 A |
111 | std::mutex m_emptyRegionConditionMutex; |
112 | std::condition_variable m_emptyRegionCondition; | |
6fe7ccc8 A |
113 | ThreadIdentifier m_blockFreeingThread; |
114 | }; | |
115 | ||
93a37866 A |
116 | inline DeadBlock* BlockAllocator::tryAllocateFromRegion(RegionSet& set, DoublyLinkedList<Region>& regions, size_t& numberOfRegions) |
117 | { | |
118 | if (numberOfRegions) { | |
119 | ASSERT(!regions.isEmpty()); | |
120 | Region* region = regions.head(); | |
121 | ASSERT(!region->isFull()); | |
122 | ||
123 | if (region->isEmpty()) { | |
124 | ASSERT(region == m_emptyRegions.head()); | |
125 | m_numberOfEmptyRegions--; | |
126 | set.m_numberOfPartialRegions++; | |
127 | region = m_emptyRegions.removeHead()->reset(set.m_blockSize); | |
128 | set.m_partialRegions.push(region); | |
129 | } | |
130 | ||
131 | DeadBlock* block = region->allocate(); | |
132 | ||
133 | if (region->isFull()) { | |
134 | set.m_numberOfPartialRegions--; | |
135 | set.m_fullRegions.push(set.m_partialRegions.removeHead()); | |
136 | } | |
137 | ||
138 | return block; | |
139 | } | |
140 | return 0; | |
141 | } | |
142 | ||
143 | template<typename T> | |
144 | inline DeadBlock* BlockAllocator::allocate() | |
6fe7ccc8 | 145 | { |
93a37866 A |
146 | RegionSet& set = regionSetFor<T>(); |
147 | DeadBlock* block; | |
6fe7ccc8 | 148 | m_isCurrentlyAllocating = true; |
93a37866 A |
149 | { |
150 | SpinLockHolder locker(&m_regionLock); | |
151 | if ((block = tryAllocateFromRegion(set, set.m_partialRegions, set.m_numberOfPartialRegions))) | |
152 | return block; | |
153 | if ((block = tryAllocateFromRegion(set, m_emptyRegions, m_numberOfEmptyRegions))) | |
154 | return block; | |
6fe7ccc8 A |
155 | } |
156 | ||
93a37866 A |
157 | Region* newRegion = Region::create(&m_superRegion, T::blockSize); |
158 | ||
159 | SpinLockHolder locker(&m_regionLock); | |
160 | m_emptyRegions.push(newRegion); | |
161 | m_numberOfEmptyRegions++; | |
162 | block = tryAllocateFromRegion(set, m_emptyRegions, m_numberOfEmptyRegions); | |
163 | ASSERT(block); | |
164 | return block; | |
165 | } | |
166 | ||
167 | inline DeadBlock* BlockAllocator::allocateCustomSize(size_t blockSize, size_t blockAlignment) | |
168 | { | |
169 | size_t realSize = WTF::roundUpToMultipleOf(blockAlignment, blockSize); | |
170 | Region* newRegion = Region::createCustomSize(&m_superRegion, realSize, blockAlignment); | |
171 | DeadBlock* block = newRegion->allocate(); | |
172 | ASSERT(block); | |
173 | return block; | |
6fe7ccc8 A |
174 | } |
175 | ||
93a37866 A |
176 | template<typename T> |
177 | inline void BlockAllocator::deallocate(T* block) | |
6fe7ccc8 | 178 | { |
93a37866 A |
179 | RegionSet& set = regionSetFor<T>(); |
180 | bool shouldWakeBlockFreeingThread = false; | |
6fe7ccc8 | 181 | { |
93a37866 A |
182 | SpinLockHolder locker(&m_regionLock); |
183 | Region* region = block->region(); | |
184 | ASSERT(!region->isEmpty()); | |
185 | if (region->isFull()) | |
186 | set.m_fullRegions.remove(region); | |
187 | else { | |
188 | set.m_partialRegions.remove(region); | |
189 | set.m_numberOfPartialRegions--; | |
190 | } | |
191 | ||
192 | region->deallocate(block); | |
193 | ||
194 | if (region->isEmpty()) { | |
195 | m_emptyRegions.push(region); | |
196 | shouldWakeBlockFreeingThread = !m_numberOfEmptyRegions; | |
197 | m_numberOfEmptyRegions++; | |
198 | } else { | |
199 | set.m_partialRegions.push(region); | |
200 | set.m_numberOfPartialRegions++; | |
201 | } | |
202 | } | |
203 | ||
204 | if (shouldWakeBlockFreeingThread) { | |
81345200 A |
205 | std::lock_guard<std::mutex> lock(m_emptyRegionConditionMutex); |
206 | m_emptyRegionCondition.notify_one(); | |
6fe7ccc8 | 207 | } |
93a37866 | 208 | |
81345200 | 209 | if (!m_blockFreeingThread) |
93a37866 | 210 | releaseFreeRegions(); |
93a37866 A |
211 | } |
212 | ||
213 | template<typename T> | |
214 | inline void BlockAllocator::deallocateCustomSize(T* block) | |
215 | { | |
216 | Region* region = block->region(); | |
217 | ASSERT(region->isCustomSize()); | |
218 | region->deallocate(block); | |
219 | region->destroy(); | |
220 | } | |
221 | ||
81345200 A |
222 | #define REGION_SET_FOR(blockType, set) \ |
223 | template <> \ | |
224 | inline BlockAllocator::RegionSet& BlockAllocator::regionSetFor<blockType>() \ | |
225 | { \ | |
226 | return set; \ | |
227 | } \ | |
228 | template <> \ | |
229 | inline BlockAllocator::RegionSet& BlockAllocator::regionSetFor<HeapBlock<blockType>>() \ | |
230 | { \ | |
231 | return set; \ | |
232 | } \ | |
233 | ||
234 | REGION_SET_FOR(MarkedBlock, m_markedRegionSet); | |
235 | REGION_SET_FOR(CopiedBlock, m_copiedRegionSet); | |
236 | REGION_SET_FOR(WeakBlock, m_fourKBBlockRegionSet); | |
237 | REGION_SET_FOR(GCArraySegment<const JSCell*>, m_fourKBBlockRegionSet); | |
238 | REGION_SET_FOR(GCArraySegment<CodeBlock*>, m_fourKBBlockRegionSet); | |
239 | REGION_SET_FOR(CopyWorkListSegment, m_workListRegionSet); | |
240 | REGION_SET_FOR(HandleBlock, m_fourKBBlockRegionSet); | |
241 | ||
242 | #undef REGION_SET_FOR | |
93a37866 A |
243 | |
244 | template <typename T> | |
245 | inline BlockAllocator::RegionSet& BlockAllocator::regionSetFor() | |
246 | { | |
247 | RELEASE_ASSERT_NOT_REACHED(); | |
248 | return *(RegionSet*)0; | |
6fe7ccc8 A |
249 | } |
250 | ||
251 | } // namespace JSC | |
252 | ||
253 | #endif // BlockAllocator_h |