X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/6fe7ccc865dc7d7541b93c5bcaf6368d2c98a174..2d39b0e377c0896910ee49ae70082ba665faf986:/heap/BlockAllocator.cpp diff --git a/heap/BlockAllocator.cpp b/heap/BlockAllocator.cpp index a20ed3e..bf408f8 100644 --- a/heap/BlockAllocator.cpp +++ b/heap/BlockAllocator.cpp @@ -26,71 +26,93 @@ #include "config.h" #include "BlockAllocator.h" +#include "CopiedBlock.h" +#include "CopyWorkList.h" #include "MarkedBlock.h" +#include "JSCInlines.h" +#include "WeakBlock.h" #include namespace JSC { +inline ThreadIdentifier createBlockFreeingThread(BlockAllocator* allocator) +{ + if (!GCActivityCallback::s_shouldCreateGCTimer) + return 0; // No block freeing thread. + ThreadIdentifier identifier = createThread(allocator->blockFreeingThreadStartFunc, allocator, "JavaScriptCore::BlockFree"); + RELEASE_ASSERT(identifier); + return identifier; +} + BlockAllocator::BlockAllocator() - : m_numberOfFreeBlocks(0) + : m_superRegion() + , m_copiedRegionSet(CopiedBlock::blockSize) + , m_markedRegionSet(MarkedBlock::blockSize) + , m_fourKBBlockRegionSet(WeakBlock::blockSize) + , m_workListRegionSet(CopyWorkListSegment::blockSize) + , m_numberOfEmptyRegions(0) , m_isCurrentlyAllocating(false) , m_blockFreeingThreadShouldQuit(false) - , m_blockFreeingThread(GCActivityCallback::s_shouldCreateGCTimer ? - createThread(blockFreeingThreadStartFunc, this, "JavaScriptCore::BlockFree") : 0) + , m_blockFreeingThread(createBlockFreeingThread(this)) { - ASSERT(m_blockFreeingThread || !GCActivityCallback::s_shouldCreateGCTimer); + m_regionLock.Init(); } BlockAllocator::~BlockAllocator() { - releaseFreeBlocks(); + releaseFreeRegions(); { - MutexLocker locker(m_freeBlockLock); + std::lock_guard lock(m_emptyRegionConditionMutex); m_blockFreeingThreadShouldQuit = true; - m_freeBlockCondition.broadcast(); + m_emptyRegionCondition.notify_all(); } - if (GCActivityCallback::s_shouldCreateGCTimer) + if (m_blockFreeingThread) waitForThreadCompletion(m_blockFreeingThread); + ASSERT(allRegionSetsAreEmpty()); + ASSERT(m_emptyRegions.isEmpty()); +} + +bool BlockAllocator::allRegionSetsAreEmpty() const +{ + return m_copiedRegionSet.isEmpty() + && m_markedRegionSet.isEmpty() + && m_fourKBBlockRegionSet.isEmpty() + && m_workListRegionSet.isEmpty(); } -void BlockAllocator::releaseFreeBlocks() +void BlockAllocator::releaseFreeRegions() { while (true) { - MarkedBlock* block; + Region* region; { - MutexLocker locker(m_freeBlockLock); - if (!m_numberOfFreeBlocks) - block = 0; + SpinLockHolder locker(&m_regionLock); + if (!m_numberOfEmptyRegions) + region = 0; else { - // FIXME: How do we know this is a MarkedBlock? It could be a CopiedBlock. - block = static_cast(m_freeBlocks.removeHead()); - ASSERT(block); - m_numberOfFreeBlocks--; + region = m_emptyRegions.removeHead(); + RELEASE_ASSERT(region); + m_numberOfEmptyRegions--; } } - if (!block) + if (!region) break; - - MarkedBlock::destroy(block); + + region->destroy(); } } -void BlockAllocator::waitForRelativeTimeWhileHoldingLock(double relative) +void BlockAllocator::waitForDuration(std::chrono::milliseconds duration) { - if (m_blockFreeingThreadShouldQuit) - return; - m_freeBlockCondition.timedWait(m_freeBlockLock, currentTime() + relative); -} + std::unique_lock lock(m_emptyRegionConditionMutex); -void BlockAllocator::waitForRelativeTime(double relative) -{ // If this returns early, that's fine, so long as it doesn't do it too // frequently. It would only be a bug if this function failed to return // when it was asked to do so. - - MutexLocker locker(m_freeBlockLock); - waitForRelativeTimeWhileHoldingLock(relative); + if (m_blockFreeingThreadShouldQuit) + return; + + m_emptyRegionCondition.wait_for(lock, duration); } void BlockAllocator::blockFreeingThreadStartFunc(void* blockAllocator) @@ -100,10 +122,11 @@ void BlockAllocator::blockFreeingThreadStartFunc(void* blockAllocator) void BlockAllocator::blockFreeingThreadMain() { + size_t currentNumberOfEmptyRegions; while (!m_blockFreeingThreadShouldQuit) { // Generally wait for one second before scavenging free blocks. This // may return early, particularly when we're being asked to quit. - waitForRelativeTime(1.0); + waitForDuration(std::chrono::seconds(1)); if (m_blockFreeingThreadShouldQuit) break; @@ -112,33 +135,37 @@ void BlockAllocator::blockFreeingThreadMain() continue; } - // Now process the list of free blocks. Keep freeing until half of the - // blocks that are currently on the list are gone. Assume that a size_t - // field can be accessed atomically. - size_t currentNumberOfFreeBlocks = m_numberOfFreeBlocks; - if (!currentNumberOfFreeBlocks) - continue; + // Sleep until there is actually work to do rather than waking up every second to check. + { + std::unique_lock lock(m_emptyRegionConditionMutex); + SpinLockHolder regionLocker(&m_regionLock); + while (!m_numberOfEmptyRegions && !m_blockFreeingThreadShouldQuit) { + m_regionLock.Unlock(); + m_emptyRegionCondition.wait(lock); + m_regionLock.Lock(); + } + currentNumberOfEmptyRegions = m_numberOfEmptyRegions; + } - size_t desiredNumberOfFreeBlocks = currentNumberOfFreeBlocks / 2; + size_t desiredNumberOfEmptyRegions = currentNumberOfEmptyRegions / 2; while (!m_blockFreeingThreadShouldQuit) { - MarkedBlock* block; + Region* region; { - MutexLocker locker(m_freeBlockLock); - if (m_numberOfFreeBlocks <= desiredNumberOfFreeBlocks) - block = 0; + SpinLockHolder locker(&m_regionLock); + if (m_numberOfEmptyRegions <= desiredNumberOfEmptyRegions) + region = 0; else { - // FIXME: How do we know this is a MarkedBlock? It could be a CopiedBlock. - block = static_cast(m_freeBlocks.removeHead()); - ASSERT(block); - m_numberOfFreeBlocks--; + region = m_emptyRegions.removeHead(); + RELEASE_ASSERT(region); + m_numberOfEmptyRegions--; } } - if (!block) + if (!region) break; - MarkedBlock::destroy(block); + region->destroy(); } } }