BlockAllocator.cpp [plain text]
#include "config.h"
#include "BlockAllocator.h"
#include "CopiedBlock.h"
#include "CopyWorkList.h"
#include "MarkedBlock.h"
#include "WeakBlock.h"
#include <wtf/CurrentTime.h>
namespace JSC {
BlockAllocator::BlockAllocator()
: 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)
#if PLATFORM(IOS)
, m_blockFreeingThread(GCActivityCallback::s_shouldCreateGCTimer ?
createThread(blockFreeingThreadStartFunc, this, "JavaScriptCore::BlockFree") : 0)
#else
, m_blockFreeingThread(createThread(blockFreeingThreadStartFunc, this, "JavaScriptCore::BlockFree"))
#endif
{
#if PLATFORM(IOS)
RELEASE_ASSERT(m_blockFreeingThread || !GCActivityCallback::s_shouldCreateGCTimer);
#else
RELEASE_ASSERT(m_blockFreeingThread);
#endif
m_regionLock.Init();
}
BlockAllocator::~BlockAllocator()
{
releaseFreeRegions();
{
MutexLocker locker(m_emptyRegionConditionLock);
m_blockFreeingThreadShouldQuit = true;
m_emptyRegionCondition.broadcast();
}
#if PLATFORM(IOS)
if (GCActivityCallback::s_shouldCreateGCTimer)
waitForThreadCompletion(m_blockFreeingThread);
#else
waitForThreadCompletion(m_blockFreeingThread);
#endif
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::releaseFreeRegions()
{
while (true) {
Region* region;
{
SpinLockHolder locker(&m_regionLock);
if (!m_numberOfEmptyRegions)
region = 0;
else {
region = m_emptyRegions.removeHead();
RELEASE_ASSERT(region);
m_numberOfEmptyRegions--;
}
}
if (!region)
break;
region->destroy();
}
}
void BlockAllocator::waitForRelativeTimeWhileHoldingLock(double relative)
{
if (m_blockFreeingThreadShouldQuit)
return;
m_emptyRegionCondition.timedWait(m_emptyRegionConditionLock, currentTime() + relative);
}
void BlockAllocator::waitForRelativeTime(double relative)
{
MutexLocker locker(m_emptyRegionConditionLock);
waitForRelativeTimeWhileHoldingLock(relative);
}
void BlockAllocator::blockFreeingThreadStartFunc(void* blockAllocator)
{
static_cast<BlockAllocator*>(blockAllocator)->blockFreeingThreadMain();
}
void BlockAllocator::blockFreeingThreadMain()
{
size_t currentNumberOfEmptyRegions;
while (!m_blockFreeingThreadShouldQuit) {
waitForRelativeTime(1.0);
if (m_blockFreeingThreadShouldQuit)
break;
if (m_isCurrentlyAllocating) {
m_isCurrentlyAllocating = false;
continue;
}
{
MutexLocker locker(m_emptyRegionConditionLock);
SpinLockHolder regionLocker(&m_regionLock);
while (!m_numberOfEmptyRegions && !m_blockFreeingThreadShouldQuit) {
m_regionLock.Unlock();
m_emptyRegionCondition.wait(m_emptyRegionConditionLock);
m_regionLock.Lock();
}
currentNumberOfEmptyRegions = m_numberOfEmptyRegions;
}
size_t desiredNumberOfEmptyRegions = currentNumberOfEmptyRegions / 2;
while (!m_blockFreeingThreadShouldQuit) {
Region* region;
{
SpinLockHolder locker(&m_regionLock);
if (m_numberOfEmptyRegions <= desiredNumberOfEmptyRegions)
region = 0;
else {
region = m_emptyRegions.removeHead();
RELEASE_ASSERT(region);
m_numberOfEmptyRegions--;
}
}
if (!region)
break;
region->destroy();
}
}
}
}