Use WTF::Lock and WTF::Condition instead of WTF::Mutex, WTF::ThreadCondition, std...
[WebKit-https.git] / Source / JavaScriptCore / jit / ExecutableAllocator.cpp
1 /*
2  * Copyright (C) 2008 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 "config.h"
27 #include "ExecutableAllocator.h"
28
29 #include "JSCInlines.h"
30
31 #if ENABLE(EXECUTABLE_ALLOCATOR_DEMAND)
32 #include "CodeProfiling.h"
33 #include <wtf/HashSet.h>
34 #include <wtf/Lock.h>
35 #include <wtf/MetaAllocator.h>
36 #include <wtf/NeverDestroyed.h>
37 #include <wtf/PageReservation.h>
38 #include <wtf/VMTags.h>
39 #endif
40
41 // Uncomment to create an artificial executable memory usage limit. This limit
42 // is imperfect and is primarily useful for testing the VM's ability to handle
43 // out-of-executable-memory situations.
44 // #define EXECUTABLE_MEMORY_LIMIT 1000000
45
46 #if ENABLE(ASSEMBLER)
47
48 using namespace WTF;
49
50 namespace JSC {
51
52 #if ENABLE(EXECUTABLE_ALLOCATOR_DEMAND)
53
54 class DemandExecutableAllocator : public MetaAllocator {
55 public:
56     DemandExecutableAllocator()
57         : MetaAllocator(jitAllocationGranule)
58     {
59         std::lock_guard<StaticLock> lock(allocatorsMutex());
60         allocators().add(this);
61         // Don't preallocate any memory here.
62     }
63     
64     virtual ~DemandExecutableAllocator()
65     {
66         {
67             std::lock_guard<StaticLock> lock(allocatorsMutex());
68             allocators().remove(this);
69         }
70         for (unsigned i = 0; i < reservations.size(); ++i)
71             reservations.at(i).deallocate();
72     }
73
74     static size_t bytesAllocatedByAllAllocators()
75     {
76         size_t total = 0;
77         std::lock_guard<StaticLock> lock(allocatorsMutex());
78         for (HashSet<DemandExecutableAllocator*>::const_iterator allocator = allocators().begin(); allocator != allocators().end(); ++allocator)
79             total += (*allocator)->bytesAllocated();
80         return total;
81     }
82
83     static size_t bytesCommittedByAllocactors()
84     {
85         size_t total = 0;
86         std::lock_guard<StaticLock> lock(allocatorsMutex());
87         for (HashSet<DemandExecutableAllocator*>::const_iterator allocator = allocators().begin(); allocator != allocators().end(); ++allocator)
88             total += (*allocator)->bytesCommitted();
89         return total;
90     }
91
92 #if ENABLE(META_ALLOCATOR_PROFILE)
93     static void dumpProfileFromAllAllocators()
94     {
95         std::lock_guard<StaticLock> lock(allocatorsMutex());
96         for (HashSet<DemandExecutableAllocator*>::const_iterator allocator = allocators().begin(); allocator != allocators().end(); ++allocator)
97             (*allocator)->dumpProfile();
98     }
99 #endif
100
101 protected:
102     virtual void* allocateNewSpace(size_t& numPages)
103     {
104         size_t newNumPages = (((numPages * pageSize() + JIT_ALLOCATOR_LARGE_ALLOC_SIZE - 1) / JIT_ALLOCATOR_LARGE_ALLOC_SIZE * JIT_ALLOCATOR_LARGE_ALLOC_SIZE) + pageSize() - 1) / pageSize();
105         
106         ASSERT(newNumPages >= numPages);
107         
108         numPages = newNumPages;
109         
110 #ifdef EXECUTABLE_MEMORY_LIMIT
111         if (bytesAllocatedByAllAllocators() >= EXECUTABLE_MEMORY_LIMIT)
112             return 0;
113 #endif
114         
115         PageReservation reservation = PageReservation::reserve(numPages * pageSize(), OSAllocator::JSJITCodePages, EXECUTABLE_POOL_WRITABLE, true);
116         RELEASE_ASSERT(reservation);
117         
118         reservations.append(reservation);
119         
120         return reservation.base();
121     }
122     
123     virtual void notifyNeedPage(void* page)
124     {
125         OSAllocator::commit(page, pageSize(), EXECUTABLE_POOL_WRITABLE, true);
126     }
127     
128     virtual void notifyPageIsFree(void* page)
129     {
130         OSAllocator::decommit(page, pageSize());
131     }
132
133 private:
134     Vector<PageReservation, 16> reservations;
135     static HashSet<DemandExecutableAllocator*>& allocators()
136     {
137         DEPRECATED_DEFINE_STATIC_LOCAL(HashSet<DemandExecutableAllocator*>, sAllocators, ());
138         return sAllocators;
139     }
140
141     static StaticLock& allocatorsMutex()
142     {
143         static StaticLock mutex;
144
145         return mutex;
146     }
147 };
148
149 #if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
150 void ExecutableAllocator::initializeAllocator()
151 {
152 }
153 #else
154 static DemandExecutableAllocator* gAllocator;
155
156 namespace {
157 static inline DemandExecutableAllocator* allocator()
158 {
159     return gAllocator;
160 }
161 }
162
163 void ExecutableAllocator::initializeAllocator()
164 {
165     ASSERT(!gAllocator);
166     gAllocator = new DemandExecutableAllocator();
167     CodeProfiling::notifyAllocator(gAllocator);
168 }
169 #endif
170
171 ExecutableAllocator::ExecutableAllocator(VM&)
172 #if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
173     : m_allocator(std::make_unique<DemandExecutableAllocator>())
174 #endif
175 {
176     ASSERT(allocator());
177 }
178
179 ExecutableAllocator::~ExecutableAllocator()
180 {
181 }
182
183 bool ExecutableAllocator::isValid() const
184 {
185     return true;
186 }
187
188 bool ExecutableAllocator::underMemoryPressure()
189 {
190 #ifdef EXECUTABLE_MEMORY_LIMIT
191     return DemandExecutableAllocator::bytesAllocatedByAllAllocators() > EXECUTABLE_MEMORY_LIMIT / 2;
192 #else
193     return false;
194 #endif
195 }
196
197 double ExecutableAllocator::memoryPressureMultiplier(size_t addedMemoryUsage)
198 {
199     double result;
200 #ifdef EXECUTABLE_MEMORY_LIMIT
201     size_t bytesAllocated = DemandExecutableAllocator::bytesAllocatedByAllAllocators() + addedMemoryUsage;
202     if (bytesAllocated >= EXECUTABLE_MEMORY_LIMIT)
203         bytesAllocated = EXECUTABLE_MEMORY_LIMIT;
204     result = static_cast<double>(EXECUTABLE_MEMORY_LIMIT) /
205         (EXECUTABLE_MEMORY_LIMIT - bytesAllocated);
206 #else
207     UNUSED_PARAM(addedMemoryUsage);
208     result = 1.0;
209 #endif
210     if (result < 1.0)
211         result = 1.0;
212     return result;
213
214 }
215
216 RefPtr<ExecutableMemoryHandle> ExecutableAllocator::allocate(VM&, size_t sizeInBytes, void* ownerUID, JITCompilationEffort effort)
217 {
218     RefPtr<ExecutableMemoryHandle> result = allocator()->allocate(sizeInBytes, ownerUID);
219     RELEASE_ASSERT(result || effort != JITCompilationMustSucceed);
220     return result;
221 }
222
223 size_t ExecutableAllocator::committedByteCount()
224 {
225     return DemandExecutableAllocator::bytesCommittedByAllocactors();
226 }
227
228 #if ENABLE(META_ALLOCATOR_PROFILE)
229 void ExecutableAllocator::dumpProfile()
230 {
231     DemandExecutableAllocator::dumpProfileFromAllAllocators();
232 }
233 #endif
234
235 #endif // ENABLE(EXECUTABLE_ALLOCATOR_DEMAND)
236
237 #if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
238
239 #if OS(WINDOWS)
240 #error "ASSEMBLER_WX_EXCLUSIVE not yet suported on this platform."
241 #endif
242
243 void ExecutableAllocator::reprotectRegion(void* start, size_t size, ProtectionSetting setting)
244 {
245     size_t pageSize = WTF::pageSize();
246
247     // Calculate the start of the page containing this region,
248     // and account for this extra memory within size.
249     intptr_t startPtr = reinterpret_cast<intptr_t>(start);
250     intptr_t pageStartPtr = startPtr & ~(pageSize - 1);
251     void* pageStart = reinterpret_cast<void*>(pageStartPtr);
252     size += (startPtr - pageStartPtr);
253
254     // Round size up
255     size += (pageSize - 1);
256     size &= ~(pageSize - 1);
257
258     mprotect(pageStart, size, (setting == Writable) ? PROTECTION_FLAGS_RW : PROTECTION_FLAGS_RX);
259 }
260
261 #endif
262
263 }
264
265 #endif // HAVE(ASSEMBLER)