CopiedBlock and MarkedBlock should have proper value semantics (i.e., destructors)
[WebKit-https.git] / Source / JavaScriptCore / heap / CopiedSpace.cpp
1 /*
2  * Copyright (C) 2011 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "CopiedSpace.h"
28
29 #include "CopiedSpaceInlineMethods.h"
30 #include "GCActivityCallback.h"
31
32 namespace JSC {
33
34 CopiedSpace::CopiedSpace(Heap* heap)
35     : m_heap(heap)
36     , m_toSpace(0)
37     , m_fromSpace(0)
38     , m_inCopyingPhase(false)
39     , m_numberOfLoanedBlocks(0)
40 {
41 }
42
43 void CopiedSpace::init()
44 {
45     m_toSpace = &m_blocks1;
46     m_fromSpace = &m_blocks2;
47     
48     if (!addNewBlock())
49         CRASH();
50 }   
51
52 CheckedBoolean CopiedSpace::tryAllocateSlowCase(size_t bytes, void** outPtr)
53 {
54     if (isOversize(bytes))
55         return tryAllocateOversize(bytes, outPtr);
56     
57     m_heap->didAllocate(m_allocator.currentCapacity());
58
59     if (!addNewBlock()) {
60         *outPtr = 0;
61         return false;
62     }
63     *outPtr = m_allocator.allocate(bytes);
64     ASSERT(*outPtr);
65     return true;
66 }
67
68 CheckedBoolean CopiedSpace::tryAllocateOversize(size_t bytes, void** outPtr)
69 {
70     ASSERT(isOversize(bytes));
71     
72     size_t blockSize = WTF::roundUpToMultipleOf(WTF::pageSize(), sizeof(CopiedBlock) + bytes);
73
74     PageAllocationAligned allocation = PageAllocationAligned::allocate(blockSize, WTF::pageSize(), OSAllocator::JSGCHeapPages);
75     if (!static_cast<bool>(allocation)) {
76         *outPtr = 0;
77         return false;
78     }
79
80     CopiedBlock* block = CopiedBlock::create(allocation);
81     m_oversizeBlocks.push(block);
82     m_oversizeFilter.add(reinterpret_cast<Bits>(block));
83     
84     *outPtr = allocateFromBlock(block, bytes);
85
86     m_heap->didAllocate(blockSize);
87
88     return true;
89 }
90
91 CheckedBoolean CopiedSpace::tryReallocate(void** ptr, size_t oldSize, size_t newSize)
92 {
93     if (oldSize >= newSize)
94         return true;
95     
96     void* oldPtr = *ptr;
97     ASSERT(!m_heap->globalData()->isInitializingObject());
98
99     if (isOversize(oldSize) || isOversize(newSize))
100         return tryReallocateOversize(ptr, oldSize, newSize);
101
102     if (m_allocator.wasLastAllocation(oldPtr, oldSize)) {
103         size_t delta = newSize - oldSize;
104         if (m_allocator.fitsInCurrentBlock(delta)) {
105             (void)m_allocator.allocate(delta);
106             return true;
107         }
108     }
109
110     void* result = 0;
111     if (!tryAllocate(newSize, &result)) {
112         *ptr = 0;
113         return false;
114     }
115     memcpy(result, oldPtr, oldSize);
116     *ptr = result;
117     return true;
118 }
119
120 CheckedBoolean CopiedSpace::tryReallocateOversize(void** ptr, size_t oldSize, size_t newSize)
121 {
122     ASSERT(isOversize(oldSize) || isOversize(newSize));
123     ASSERT(newSize > oldSize);
124
125     void* oldPtr = *ptr;
126     
127     void* newPtr = 0;
128     if (!tryAllocateOversize(newSize, &newPtr)) {
129         *ptr = 0;
130         return false;
131     }
132
133     memcpy(newPtr, oldPtr, oldSize);
134
135     if (isOversize(oldSize)) {
136         CopiedBlock* oldBlock = oversizeBlockFor(oldPtr);
137         m_oversizeBlocks.remove(oldBlock);
138         CopiedBlock::destroy(oldBlock).deallocate();
139     }
140     
141     *ptr = newPtr;
142     return true;
143 }
144
145 void CopiedSpace::doneFillingBlock(CopiedBlock* block)
146 {
147     ASSERT(block);
148     ASSERT(block->m_offset < reinterpret_cast<char*>(block) + HeapBlock::s_blockSize);
149     ASSERT(m_inCopyingPhase);
150
151     if (block->m_offset == block->payload()) {
152         recycleBlock(block);
153         return;
154     }
155
156     {
157         MutexLocker locker(m_toSpaceLock);
158         m_toSpace->push(block);
159         m_toSpaceSet.add(block);
160         m_toSpaceFilter.add(reinterpret_cast<Bits>(block));
161     }
162
163     {
164         MutexLocker locker(m_loanedBlocksLock);
165         ASSERT(m_numberOfLoanedBlocks > 0);
166         m_numberOfLoanedBlocks--;
167         if (!m_numberOfLoanedBlocks)
168             m_loanedBlocksCondition.signal();
169     }
170 }
171
172 void CopiedSpace::doneCopying()
173 {
174     {
175         MutexLocker locker(m_loanedBlocksLock);
176         while (m_numberOfLoanedBlocks > 0)
177             m_loanedBlocksCondition.wait(m_loanedBlocksLock);
178     }
179
180     ASSERT(m_inCopyingPhase);
181     m_inCopyingPhase = false;
182     while (!m_fromSpace->isEmpty()) {
183         CopiedBlock* block = static_cast<CopiedBlock*>(m_fromSpace->removeHead());
184         if (block->m_isPinned) {
185             block->m_isPinned = false;
186             // We don't add the block to the toSpaceSet because it was never removed.
187             ASSERT(m_toSpaceSet.contains(block));
188             m_toSpaceFilter.add(reinterpret_cast<Bits>(block));
189             m_toSpace->push(block);
190             continue;
191         }
192
193         m_toSpaceSet.remove(block);
194         m_heap->blockAllocator().deallocate(CopiedBlock::destroy(block));
195     }
196
197     CopiedBlock* curr = static_cast<CopiedBlock*>(m_oversizeBlocks.head());
198     while (curr) {
199         CopiedBlock* next = static_cast<CopiedBlock*>(curr->next());
200         if (!curr->m_isPinned) {
201             m_oversizeBlocks.remove(curr);
202             CopiedBlock::destroy(curr).deallocate();
203         } else
204             curr->m_isPinned = false;
205         curr = next;
206     }
207
208     if (!m_toSpace->head()) {
209         if (!addNewBlock())
210             CRASH();
211     } else
212         m_allocator.resetCurrentBlock(static_cast<CopiedBlock*>(m_toSpace->head()));
213 }
214
215 CheckedBoolean CopiedSpace::getFreshBlock(AllocationEffort allocationEffort, CopiedBlock** outBlock)
216 {
217     CopiedBlock* block = 0;
218     if (allocationEffort == AllocationMustSucceed)
219         block = CopiedBlock::create(m_heap->blockAllocator().allocate());
220     else {
221         ASSERT(allocationEffort == AllocationCanFail);
222         if (m_heap->shouldCollect())
223             m_heap->collect(Heap::DoNotSweep);
224         
225         if (!getFreshBlock(AllocationMustSucceed, &block)) {
226             *outBlock = 0;
227             ASSERT_NOT_REACHED();
228             return false;
229         }
230     }
231     ASSERT(block);
232     ASSERT(is8ByteAligned(block->m_offset));
233     *outBlock = block;
234     return true;
235 }
236
237 void CopiedSpace::freeAllBlocks()
238 {
239     while (!m_toSpace->isEmpty())
240         m_heap->blockAllocator().deallocate(CopiedBlock::destroy(static_cast<CopiedBlock*>(m_toSpace->removeHead())));
241
242     while (!m_fromSpace->isEmpty())
243         m_heap->blockAllocator().deallocate(CopiedBlock::destroy(static_cast<CopiedBlock*>(m_fromSpace->removeHead())));
244
245     while (!m_oversizeBlocks.isEmpty())
246         CopiedBlock::destroy(static_cast<CopiedBlock*>(m_oversizeBlocks.removeHead())).deallocate();
247 }
248
249 size_t CopiedSpace::size()
250 {
251     size_t calculatedSize = 0;
252
253     for (CopiedBlock* block = static_cast<CopiedBlock*>(m_toSpace->head()); block; block = static_cast<CopiedBlock*>(block->next()))
254         calculatedSize += block->size();
255
256     for (CopiedBlock* block = static_cast<CopiedBlock*>(m_fromSpace->head()); block; block = static_cast<CopiedBlock*>(block->next()))
257         calculatedSize += block->size();
258
259     for (CopiedBlock* block = static_cast<CopiedBlock*>(m_oversizeBlocks.head()); block; block = static_cast<CopiedBlock*>(block->next()))
260         calculatedSize += block->size();
261
262     return calculatedSize;
263 }
264
265 size_t CopiedSpace::capacity()
266 {
267     size_t calculatedCapacity = 0;
268
269     for (CopiedBlock* block = static_cast<CopiedBlock*>(m_toSpace->head()); block; block = static_cast<CopiedBlock*>(block->next()))
270         calculatedCapacity += block->capacity();
271
272     for (CopiedBlock* block = static_cast<CopiedBlock*>(m_fromSpace->head()); block; block = static_cast<CopiedBlock*>(block->next()))
273         calculatedCapacity += block->capacity();
274
275     for (CopiedBlock* block = static_cast<CopiedBlock*>(m_oversizeBlocks.head()); block; block = static_cast<CopiedBlock*>(block->next()))
276         calculatedCapacity += block->capacity();
277
278     return calculatedCapacity;
279 }
280
281 static bool isBlockListPagedOut(double deadline, DoublyLinkedList<HeapBlock>* list)
282 {
283     unsigned itersSinceLastTimeCheck = 0;
284     HeapBlock* current = list->head();
285     while (current) {
286         current = current->next();
287         ++itersSinceLastTimeCheck;
288         if (itersSinceLastTimeCheck >= Heap::s_timeCheckResolution) {
289             double currentTime = WTF::monotonicallyIncreasingTime();
290             if (currentTime > deadline)
291                 return true;
292             itersSinceLastTimeCheck = 0;
293         }
294     }
295
296     return false;
297 }
298
299 bool CopiedSpace::isPagedOut(double deadline)
300 {
301     return isBlockListPagedOut(deadline, m_toSpace) 
302         || isBlockListPagedOut(deadline, m_fromSpace) 
303         || isBlockListPagedOut(deadline, &m_oversizeBlocks);
304 }
305
306 } // namespace JSC