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