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 "JSCInlines.h" 33#include "WeakBlock.h" 34#include <wtf/CurrentTime.h> 35 36namespace JSC { 37 38inline ThreadIdentifier createBlockFreeingThread(BlockAllocator* allocator) 39{ 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); 44 return identifier; 45} 46 47BlockAllocator::BlockAllocator() 48 : m_superRegion() 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)) 57{ 58 m_regionLock.Init(); 59} 60 61BlockAllocator::~BlockAllocator() 62{ 63 releaseFreeRegions(); 64 { 65 std::lock_guard<std::mutex> lock(m_emptyRegionConditionMutex); 66 m_blockFreeingThreadShouldQuit = true; 67 m_emptyRegionCondition.notify_all(); 68 } 69 if (m_blockFreeingThread) 70 waitForThreadCompletion(m_blockFreeingThread); 71 ASSERT(allRegionSetsAreEmpty()); 72 ASSERT(m_emptyRegions.isEmpty()); 73} 74 75bool BlockAllocator::allRegionSetsAreEmpty() const 76{ 77 return m_copiedRegionSet.isEmpty() 78 && m_markedRegionSet.isEmpty() 79 && m_fourKBBlockRegionSet.isEmpty() 80 && m_workListRegionSet.isEmpty(); 81} 82 83void BlockAllocator::releaseFreeRegions() 84{ 85 while (true) { 86 Region* region; 87 { 88 SpinLockHolder locker(&m_regionLock); 89 if (!m_numberOfEmptyRegions) 90 region = 0; 91 else { 92 region = m_emptyRegions.removeHead(); 93 RELEASE_ASSERT(region); 94 m_numberOfEmptyRegions--; 95 } 96 } 97 98 if (!region) 99 break; 100 101 region->destroy(); 102 } 103} 104 105void BlockAllocator::waitForDuration(std::chrono::milliseconds duration) 106{ 107 std::unique_lock<std::mutex> lock(m_emptyRegionConditionMutex); 108 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) 113 return; 114 115 m_emptyRegionCondition.wait_for(lock, duration); 116} 117 118void BlockAllocator::blockFreeingThreadStartFunc(void* blockAllocator) 119{ 120 static_cast<BlockAllocator*>(blockAllocator)->blockFreeingThreadMain(); 121} 122 123void BlockAllocator::blockFreeingThreadMain() 124{ 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) 131 break; 132 133 if (m_isCurrentlyAllocating) { 134 m_isCurrentlyAllocating = false; 135 continue; 136 } 137 138 // Sleep until there is actually work to do rather than waking up every second to check. 139 { 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); 145 m_regionLock.Lock(); 146 } 147 currentNumberOfEmptyRegions = m_numberOfEmptyRegions; 148 } 149 150 size_t desiredNumberOfEmptyRegions = currentNumberOfEmptyRegions / 2; 151 152 while (!m_blockFreeingThreadShouldQuit) { 153 Region* region; 154 { 155 SpinLockHolder locker(&m_regionLock); 156 if (m_numberOfEmptyRegions <= desiredNumberOfEmptyRegions) 157 region = 0; 158 else { 159 region = m_emptyRegions.removeHead(); 160 RELEASE_ASSERT(region); 161 m_numberOfEmptyRegions--; 162 } 163 } 164 165 if (!region) 166 break; 167 168 region->destroy(); 169 } 170 } 171} 172 173} // namespace JSC 174