Enable gigacage on iOS
[WebKit-https.git] / Source / bmalloc / bmalloc / Scavenger.cpp
1 /*
2  * Copyright (C) 2017 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 "Scavenger.h"
27
28 #include "AvailableMemory.h"
29 #include "Heap.h"
30 #include <thread>
31
32 namespace bmalloc {
33
34 Scavenger::Scavenger(std::lock_guard<StaticMutex>&)
35 {
36 #if BOS(DARWIN)
37     auto queue = dispatch_queue_create("WebKit Malloc Memory Pressure Handler", DISPATCH_QUEUE_SERIAL);
38     m_pressureHandlerDispatchSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, 0, DISPATCH_MEMORYPRESSURE_CRITICAL, queue);
39     dispatch_source_set_event_handler(m_pressureHandlerDispatchSource, ^{
40         scavenge();
41     });
42     dispatch_resume(m_pressureHandlerDispatchSource);
43     dispatch_release(queue);
44 #endif
45     
46     m_thread = std::thread(&threadEntryPoint, this);
47 }
48
49 void Scavenger::run()
50 {
51     std::lock_guard<Mutex> lock(m_mutex);
52     runHoldingLock();
53 }
54
55 void Scavenger::runHoldingLock()
56 {
57     m_state = State::Run;
58     m_condition.notify_all();
59 }
60
61 void Scavenger::runSoon()
62 {
63     std::lock_guard<Mutex> lock(m_mutex);
64     runSoonHoldingLock();
65 }
66
67 void Scavenger::runSoonHoldingLock()
68 {
69     if (willRunSoon())
70         return;
71     m_state = State::RunSoon;
72     m_condition.notify_all();
73 }
74
75 void Scavenger::didStartGrowing()
76 {
77     // We don't really need to lock here, since this is just a heuristic.
78     m_isProbablyGrowing = true;
79 }
80
81 void Scavenger::scheduleIfUnderMemoryPressure(size_t bytes)
82 {
83     std::lock_guard<Mutex> lock(m_mutex);
84     scheduleIfUnderMemoryPressureHoldingLock(bytes);
85 }
86
87 void Scavenger::scheduleIfUnderMemoryPressureHoldingLock(size_t bytes)
88 {
89     m_scavengerBytes += bytes;
90     if (m_scavengerBytes < scavengerBytesPerMemoryPressureCheck)
91         return;
92
93     m_scavengerBytes = 0;
94
95     if (willRun())
96         return;
97
98     if (!isUnderMemoryPressure())
99         return;
100
101     m_isProbablyGrowing = false;
102     runHoldingLock();
103 }
104
105 void Scavenger::schedule(size_t bytes)
106 {
107     std::lock_guard<Mutex> lock(m_mutex);
108     scheduleIfUnderMemoryPressureHoldingLock(bytes);
109     
110     if (willRunSoon())
111         return;
112     
113     m_isProbablyGrowing = false;
114     runSoonHoldingLock();
115 }
116
117 void Scavenger::scavenge()
118 {
119     std::lock_guard<StaticMutex> lock(Heap::mutex());
120     for (unsigned i = numHeaps; i--;) {
121         if (!isActiveHeapKind(static_cast<HeapKind>(i)))
122             continue;
123         PerProcess<PerHeapKind<Heap>>::get()->at(i).scavenge(lock);
124     }
125 }
126
127 void Scavenger::threadEntryPoint(Scavenger* scavenger)
128 {
129     scavenger->threadRunLoop();
130 }
131
132 void Scavenger::threadRunLoop()
133 {
134     setSelfQOSClass();
135     
136     // This loop ratchets downward from most active to least active state. While
137     // we ratchet downward, any other thread may reset our state.
138     
139     // We require any state change while we are sleeping to signal to our
140     // condition variable and wake us up.
141     
142     auto truth = [] { return true; };
143     
144     while (truth()) {
145         if (m_state == State::Sleep) {
146             std::unique_lock<Mutex> lock(m_mutex);
147             m_condition.wait(lock, [&]() { return m_state != State::Sleep; });
148         }
149         
150         if (m_state == State::RunSoon) {
151             std::unique_lock<Mutex> lock(m_mutex);
152             m_condition.wait_for(lock, asyncTaskSleepDuration, [&]() { return m_state != State::RunSoon; });
153         }
154         
155         m_state = State::Sleep;
156         
157         setSelfQOSClass();
158         
159         {
160             std::unique_lock<Mutex> lock(m_mutex);
161             if (m_isProbablyGrowing && !isUnderMemoryPressure()) {
162                 m_isProbablyGrowing = false;
163                 runSoonHoldingLock();
164                 continue;
165             }
166         }
167         
168         scavenge();
169     }
170 }
171
172 void Scavenger::setSelfQOSClass()
173 {
174 #if BOS(DARWIN)
175     pthread_set_qos_class_self_np(requestedScavengerThreadQOSClass(), 0);
176 #endif
177 }
178
179 } // namespace bmalloc
180