1/*
2 * Copyright (C) 2014 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. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "BAssert.h"
27#include "BeginTag.h"
28#include "LargeChunk.h"
29#include "Deallocator.h"
30#include "Heap.h"
31#include "Inline.h"
32#include "PerProcess.h"
33#include "SmallChunk.h"
34#include <algorithm>
35#include <sys/mman.h>
36
37using namespace std;
38
39namespace bmalloc {
40
41Deallocator::Deallocator()
42    : m_objectLog()
43    , m_smallLineCaches()
44    , m_mediumLineCache()
45{
46}
47
48Deallocator::~Deallocator()
49{
50    scavenge();
51}
52
53void Deallocator::scavenge()
54{
55    processObjectLog();
56
57    std::lock_guard<StaticMutex> lock(PerProcess<Heap>::mutex());
58    Heap* heap = PerProcess<Heap>::getFastCase();
59
60    for (auto& smallLineCache : m_smallLineCaches) {
61        while (smallLineCache.size())
62            heap->deallocateSmallLine(lock, smallLineCache.pop());
63    }
64    while (m_mediumLineCache.size())
65        heap->deallocateMediumLine(lock, m_mediumLineCache.pop());
66}
67
68void Deallocator::deallocateLarge(void* object)
69{
70    std::lock_guard<StaticMutex> lock(PerProcess<Heap>::mutex());
71    PerProcess<Heap>::getFastCase()->deallocateLarge(lock, object);
72}
73
74void Deallocator::deallocateXLarge(void* object)
75{
76    std::lock_guard<StaticMutex> lock(PerProcess<Heap>::mutex());
77    PerProcess<Heap>::getFastCase()->deallocateXLarge(lock, object);
78}
79
80void Deallocator::processObjectLog()
81{
82    std::lock_guard<StaticMutex> lock(PerProcess<Heap>::mutex());
83
84    for (auto object : m_objectLog) {
85        if (isSmall(object)) {
86            SmallLine* line = SmallLine::get(object);
87            if (!line->deref(lock))
88                continue;
89            deallocateSmallLine(lock, line);
90        } else {
91            BASSERT(isSmallOrMedium(object));
92            MediumLine* line = MediumLine::get(object);
93            if (!line->deref(lock))
94                continue;
95            deallocateMediumLine(lock, line);
96        }
97    }
98
99    m_objectLog.clear();
100}
101
102void Deallocator::deallocateSlowCase(void* object)
103{
104    BASSERT(!deallocateFastCase(object));
105
106    if (!object)
107        return;
108
109    if (isSmallOrMedium(object)) {
110        processObjectLog();
111        m_objectLog.push(object);
112        return;
113    }
114
115    BeginTag* beginTag = LargeChunk::beginTag(object);
116    if (!beginTag->isXLarge())
117        return deallocateLarge(object);
118
119    return deallocateXLarge(object);
120}
121
122void Deallocator::deallocateSmallLine(std::lock_guard<StaticMutex>& lock, SmallLine* line)
123{
124    SmallLineCache& smallLineCache = m_smallLineCaches[SmallPage::get(line)->smallSizeClass()];
125    if (smallLineCache.size() == smallLineCache.capacity())
126        return PerProcess<Heap>::getFastCase()->deallocateSmallLine(lock, line);
127
128    smallLineCache.push(line);
129}
130
131SmallLine* Deallocator::allocateSmallLine(size_t smallSizeClass)
132{
133    SmallLineCache& smallLineCache = m_smallLineCaches[smallSizeClass];
134    if (!smallLineCache.size()) {
135        std::lock_guard<StaticMutex> lock(PerProcess<Heap>::mutex());
136        Heap* heap = PerProcess<Heap>::getFastCase();
137
138        while (smallLineCache.size() != smallLineCache.capacity())
139            smallLineCache.push(heap->allocateSmallLine(lock, smallSizeClass));
140    }
141
142    return smallLineCache.pop();
143}
144
145void Deallocator::deallocateMediumLine(std::lock_guard<StaticMutex>& lock, MediumLine* line)
146{
147    if (m_mediumLineCache.size() == m_mediumLineCache.capacity())
148        return PerProcess<Heap>::getFastCase()->deallocateMediumLine(lock, line);
149
150    m_mediumLineCache.push(line);
151}
152
153MediumLine* Deallocator::allocateMediumLine()
154{
155    if (!m_mediumLineCache.size()) {
156        std::lock_guard<StaticMutex> lock(PerProcess<Heap>::mutex());
157        Heap* heap = PerProcess<Heap>::getFastCase();
158
159        while (m_mediumLineCache.size() != m_mediumLineCache.capacity())
160            m_mediumLineCache.push(heap->allocateMediumLine(lock));
161    }
162
163    return m_mediumLineCache.pop();
164}
165
166} // namespace bmalloc
167