2 * Copyright (C) 2012 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "BlockAllocator.h"
29 #include "CopiedBlock.h"
30 #include "CopyWorkList.h"
31 #include "MarkedBlock.h"
32 #include "JSCInlines.h"
33 #include "WeakBlock.h"
34 #include <wtf/CurrentTime.h>
38 inline ThreadIdentifier
createBlockFreeingThread(BlockAllocator
* allocator
)
40 if (!GCActivityCallback::s_shouldCreateGCTimer
)
41 return 0; // No block freeing thread.
42 ThreadIdentifier identifier
= createThread(allocator
->blockFreeingThreadStartFunc
, allocator
, "JavaScriptCore::BlockFree");
43 RELEASE_ASSERT(identifier
);
47 BlockAllocator::BlockAllocator()
49 , m_copiedRegionSet(CopiedBlock::blockSize
)
50 , m_markedRegionSet(MarkedBlock::blockSize
)
51 , m_fourKBBlockRegionSet(WeakBlock::blockSize
)
52 , m_workListRegionSet(CopyWorkListSegment::blockSize
)
53 , m_numberOfEmptyRegions(0)
54 , m_isCurrentlyAllocating(false)
55 , m_blockFreeingThreadShouldQuit(false)
56 , m_blockFreeingThread(createBlockFreeingThread(this))
61 BlockAllocator::~BlockAllocator()
65 std::lock_guard
<std::mutex
> lock(m_emptyRegionConditionMutex
);
66 m_blockFreeingThreadShouldQuit
= true;
67 m_emptyRegionCondition
.notify_all();
69 if (m_blockFreeingThread
)
70 waitForThreadCompletion(m_blockFreeingThread
);
71 ASSERT(allRegionSetsAreEmpty());
72 ASSERT(m_emptyRegions
.isEmpty());
75 bool BlockAllocator::allRegionSetsAreEmpty() const
77 return m_copiedRegionSet
.isEmpty()
78 && m_markedRegionSet
.isEmpty()
79 && m_fourKBBlockRegionSet
.isEmpty()
80 && m_workListRegionSet
.isEmpty();
83 void BlockAllocator::releaseFreeRegions()
88 SpinLockHolder
locker(&m_regionLock
);
89 if (!m_numberOfEmptyRegions
)
92 region
= m_emptyRegions
.removeHead();
93 RELEASE_ASSERT(region
);
94 m_numberOfEmptyRegions
--;
105 void BlockAllocator::waitForDuration(std::chrono::milliseconds duration
)
107 std::unique_lock
<std::mutex
> lock(m_emptyRegionConditionMutex
);
109 // If this returns early, that's fine, so long as it doesn't do it too
110 // frequently. It would only be a bug if this function failed to return
111 // when it was asked to do so.
112 if (m_blockFreeingThreadShouldQuit
)
115 m_emptyRegionCondition
.wait_for(lock
, duration
);
118 void BlockAllocator::blockFreeingThreadStartFunc(void* blockAllocator
)
120 static_cast<BlockAllocator
*>(blockAllocator
)->blockFreeingThreadMain();
123 void BlockAllocator::blockFreeingThreadMain()
125 size_t currentNumberOfEmptyRegions
;
126 while (!m_blockFreeingThreadShouldQuit
) {
127 // Generally wait for one second before scavenging free blocks. This
128 // may return early, particularly when we're being asked to quit.
129 waitForDuration(std::chrono::seconds(1));
130 if (m_blockFreeingThreadShouldQuit
)
133 if (m_isCurrentlyAllocating
) {
134 m_isCurrentlyAllocating
= false;
138 // Sleep until there is actually work to do rather than waking up every second to check.
140 std::unique_lock
<std::mutex
> lock(m_emptyRegionConditionMutex
);
141 SpinLockHolder
regionLocker(&m_regionLock
);
142 while (!m_numberOfEmptyRegions
&& !m_blockFreeingThreadShouldQuit
) {
143 m_regionLock
.Unlock();
144 m_emptyRegionCondition
.wait(lock
);
147 currentNumberOfEmptyRegions
= m_numberOfEmptyRegions
;
150 size_t desiredNumberOfEmptyRegions
= currentNumberOfEmptyRegions
/ 2;
152 while (!m_blockFreeingThreadShouldQuit
) {
155 SpinLockHolder
locker(&m_regionLock
);
156 if (m_numberOfEmptyRegions
<= desiredNumberOfEmptyRegions
)
159 region
= m_emptyRegions
.removeHead();
160 RELEASE_ASSERT(region
);
161 m_numberOfEmptyRegions
--;