]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - heap/BlockAllocator.cpp
JavaScriptCore-7600.1.4.17.5.tar.gz
[apple/javascriptcore.git] / heap / BlockAllocator.cpp
index a20ed3ed731e6403bba28ac5f3c491b2d5779bf1..bf408f8ef0ad024e338068041a31c4f2751aaa04 100644 (file)
 #include "config.h"
 #include "BlockAllocator.h"
 
+#include "CopiedBlock.h"
+#include "CopyWorkList.h"
 #include "MarkedBlock.h"
+#include "JSCInlines.h"
+#include "WeakBlock.h"
 #include <wtf/CurrentTime.h>
 
 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<std::mutex> 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<MarkedBlock*>(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<std::mutex> 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<std::mutex> 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<MarkedBlock*>(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();
         }
     }
 }