fc4f8a4c664a1a1ef07db48d4c5dc9db9867c5ce
[WebKit-https.git] / Source / JavaScriptCore / heap / BlockAllocator.cpp
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 #include "config.h"
27 #include "BlockAllocator.h"
28
29 #include <wtf/CurrentTime.h>
30
31 namespace JSC {
32
33 BlockAllocator::BlockAllocator()
34     : m_numberOfFreeBlocks(0)
35     , m_isCurrentlyAllocating(false)
36     , m_blockFreeingThreadShouldQuit(false)
37     , m_blockFreeingThread(createThread(blockFreeingThreadStartFunc, this, "JavaScriptCore::BlockFree"))
38 {
39     ASSERT(m_blockFreeingThread);
40     m_freeBlockLock.Init();
41 }
42
43 BlockAllocator::~BlockAllocator()
44 {
45     releaseFreeBlocks();
46     {
47         MutexLocker locker(m_freeBlockConditionLock);
48
49         m_blockFreeingThreadShouldQuit = true;
50         m_freeBlockCondition.broadcast();
51     }
52     waitForThreadCompletion(m_blockFreeingThread);
53 }
54
55 void BlockAllocator::releaseFreeBlocks()
56 {
57     while (true) {
58         HeapBlock* block;
59         {
60             SpinLockHolder locker(&m_freeBlockLock);
61             if (!m_numberOfFreeBlocks)
62                 block = 0;
63             else {
64                 block = m_freeBlocks.removeHead();
65                 ASSERT(block);
66                 m_numberOfFreeBlocks--;
67             }
68         }
69         
70         if (!block)
71             break;
72
73         block->m_allocation.deallocate();
74     }
75 }
76
77 void BlockAllocator::waitForRelativeTimeWhileHoldingLock(double relative)
78 {
79     if (m_blockFreeingThreadShouldQuit)
80         return;
81
82     m_freeBlockCondition.timedWait(m_freeBlockConditionLock, currentTime() + relative);
83 }
84
85 void BlockAllocator::waitForRelativeTime(double relative)
86 {
87     // If this returns early, that's fine, so long as it doesn't do it too
88     // frequently. It would only be a bug if this function failed to return
89     // when it was asked to do so.
90     
91     MutexLocker locker(m_freeBlockConditionLock);
92     waitForRelativeTimeWhileHoldingLock(relative);
93 }
94
95 void BlockAllocator::blockFreeingThreadStartFunc(void* blockAllocator)
96 {
97     static_cast<BlockAllocator*>(blockAllocator)->blockFreeingThreadMain();
98 }
99
100 void BlockAllocator::blockFreeingThreadMain()
101 {
102     while (!m_blockFreeingThreadShouldQuit) {
103         // Generally wait for one second before scavenging free blocks. This
104         // may return early, particularly when we're being asked to quit.
105         waitForRelativeTime(1.0);
106         if (m_blockFreeingThreadShouldQuit)
107             break;
108         
109         if (m_isCurrentlyAllocating) {
110             m_isCurrentlyAllocating = false;
111             continue;
112         }
113
114         // Now process the list of free blocks. Keep freeing until half of the
115         // blocks that are currently on the list are gone. Assume that a size_t
116         // field can be accessed atomically.
117         size_t currentNumberOfFreeBlocks = m_numberOfFreeBlocks;
118         if (!currentNumberOfFreeBlocks)
119             continue;
120         
121         size_t desiredNumberOfFreeBlocks = currentNumberOfFreeBlocks / 2;
122         
123         while (!m_blockFreeingThreadShouldQuit) {
124             HeapBlock* block;
125             {
126                 SpinLockHolder locker(&m_freeBlockLock);
127                 if (m_numberOfFreeBlocks <= desiredNumberOfFreeBlocks)
128                     block = 0;
129                 else {
130                     block = m_freeBlocks.removeHead();
131                     ASSERT(block);
132                     m_numberOfFreeBlocks--;
133                 }
134             }
135             
136             if (!block)
137                 break;
138             
139             block->m_allocation.deallocate();
140         }
141     }
142 }
143
144 } // namespace JSC