Stop using ThreadCondition in BlockAllocator
[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 "CopiedBlock.h"
30 #include "CopyWorkList.h"
31 #include "MarkedBlock.h"
32 #include "WeakBlock.h"
33 #include <wtf/CurrentTime.h>
34
35 namespace JSC {
36
37 inline ThreadIdentifier createBlockFreeingThread(BlockAllocator* allocator)
38 {
39     if (!GCActivityCallback::s_shouldCreateGCTimer)
40         return 0; // No block freeing thread.
41     ThreadIdentifier identifier = createThread(allocator->blockFreeingThreadStartFunc, allocator, "JavaScriptCore::BlockFree");
42     RELEASE_ASSERT(identifier);
43     return identifier;
44 }
45
46 BlockAllocator::BlockAllocator()
47     : m_superRegion()
48     , m_copiedRegionSet(CopiedBlock::blockSize)
49     , m_markedRegionSet(MarkedBlock::blockSize)
50     , m_fourKBBlockRegionSet(WeakBlock::blockSize)
51     , m_workListRegionSet(CopyWorkListSegment::blockSize)
52     , m_numberOfEmptyRegions(0)
53     , m_isCurrentlyAllocating(false)
54     , m_blockFreeingThreadShouldQuit(false)
55     , m_blockFreeingThread(createBlockFreeingThread(this))
56 {
57     m_regionLock.Init();
58 }
59
60 BlockAllocator::~BlockAllocator()
61 {
62     releaseFreeRegions();
63     {
64         std::lock_guard<std::mutex> lock(m_emptyRegionConditionMutex);
65         m_blockFreeingThreadShouldQuit = true;
66         m_emptyRegionCondition.notify_all();
67     }
68     if (m_blockFreeingThread)
69         waitForThreadCompletion(m_blockFreeingThread);
70     ASSERT(allRegionSetsAreEmpty());
71     ASSERT(m_emptyRegions.isEmpty());
72 }
73
74 bool BlockAllocator::allRegionSetsAreEmpty() const
75 {
76     return m_copiedRegionSet.isEmpty()
77         && m_markedRegionSet.isEmpty()
78         && m_fourKBBlockRegionSet.isEmpty()
79         && m_workListRegionSet.isEmpty();
80 }
81
82 void BlockAllocator::releaseFreeRegions()
83 {
84     while (true) {
85         Region* region;
86         {
87             SpinLockHolder locker(&m_regionLock);
88             if (!m_numberOfEmptyRegions)
89                 region = 0;
90             else {
91                 region = m_emptyRegions.removeHead();
92                 RELEASE_ASSERT(region);
93                 m_numberOfEmptyRegions--;
94             }
95         }
96         
97         if (!region)
98             break;
99
100         region->destroy();
101     }
102 }
103
104 void BlockAllocator::waitForDuration(std::chrono::milliseconds duration)
105 {
106     std::unique_lock<std::mutex> lock(m_emptyRegionConditionMutex);
107
108     // If this returns early, that's fine, so long as it doesn't do it too
109     // frequently. It would only be a bug if this function failed to return
110     // when it was asked to do so.
111     if (m_blockFreeingThreadShouldQuit)
112         return;
113
114     m_emptyRegionCondition.wait_for(lock, duration);
115 }
116
117 void BlockAllocator::blockFreeingThreadStartFunc(void* blockAllocator)
118 {
119     static_cast<BlockAllocator*>(blockAllocator)->blockFreeingThreadMain();
120 }
121
122 void BlockAllocator::blockFreeingThreadMain()
123 {
124     size_t currentNumberOfEmptyRegions;
125     while (!m_blockFreeingThreadShouldQuit) {
126         // Generally wait for one second before scavenging free blocks. This
127         // may return early, particularly when we're being asked to quit.
128         waitForDuration(std::chrono::seconds(1));
129         if (m_blockFreeingThreadShouldQuit)
130             break;
131         
132         if (m_isCurrentlyAllocating) {
133             m_isCurrentlyAllocating = false;
134             continue;
135         }
136
137         // Sleep until there is actually work to do rather than waking up every second to check.
138         {
139             std::unique_lock<std::mutex> lock(m_emptyRegionConditionMutex);
140             SpinLockHolder regionLocker(&m_regionLock);
141             while (!m_numberOfEmptyRegions && !m_blockFreeingThreadShouldQuit) {
142                 m_regionLock.Unlock();
143                 m_emptyRegionCondition.wait(lock);
144                 m_regionLock.Lock();
145             }
146             currentNumberOfEmptyRegions = m_numberOfEmptyRegions;
147         }
148         
149         size_t desiredNumberOfEmptyRegions = currentNumberOfEmptyRegions / 2;
150         
151         while (!m_blockFreeingThreadShouldQuit) {
152             Region* region;
153             {
154                 SpinLockHolder locker(&m_regionLock);
155                 if (m_numberOfEmptyRegions <= desiredNumberOfEmptyRegions)
156                     region = 0;
157                 else {
158                     region = m_emptyRegions.removeHead();
159                     RELEASE_ASSERT(region);
160                     m_numberOfEmptyRegions--;
161                 }
162             }
163             
164             if (!region)
165                 break;
166             
167             region->destroy();
168         }
169     }
170 }
171
172 } // namespace JSC