2ace1acce65b52a82e8992e58012b04dc188fc18
[WebKit-https.git] / Source / bmalloc / bmalloc / IsoTLS.cpp
1 /*
2  * Copyright (C) 2017-2018 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 "IsoTLS.h"
27
28 #include "DebugHeap.h"
29 #include "Environment.h"
30 #include "Gigacage.h"
31 #include "IsoTLSEntryInlines.h"
32 #include "IsoTLSInlines.h"
33 #include "IsoTLSLayout.h"
34
35 #include <stdio.h>
36
37 namespace bmalloc {
38
39 IsoTLS::MallocFallbackState IsoTLS::s_mallocFallbackState;
40
41 #if !HAVE_PTHREAD_MACHDEP_H
42 bool IsoTLS::s_didInitialize;
43 pthread_key_t IsoTLS::s_tlsKey;
44 #endif
45
46 void IsoTLS::scavenge()
47 {
48     if (IsoTLS* tls = get()) {
49         tls->forEachEntry(
50             [&] (IsoTLSEntry* entry, void* data) {
51                 entry->scavenge(data);
52             });
53     }
54 }
55
56 IsoTLS::IsoTLS()
57 {
58 }
59
60 IsoTLS* IsoTLS::ensureEntries(unsigned offset)
61 {
62     RELEASE_BASSERT(!get() || offset >= get()->m_extent);
63     
64     static std::once_flag onceFlag;
65     std::call_once(
66         onceFlag,
67         [] () {
68             setvbuf(stderr, NULL, _IONBF, 0);
69 #if HAVE_PTHREAD_MACHDEP_H
70             pthread_key_init_np(tlsKey, destructor);
71 #else
72             pthread_key_create(&s_tlsKey, destructor);
73             s_didInitialize = true;
74 #endif
75         });
76     
77     IsoTLS* tls = get();
78     IsoTLSLayout& layout = *PerProcess<IsoTLSLayout>::get();
79
80     IsoTLSEntry* oldLastEntry = tls ? tls->m_lastEntry : nullptr;
81     RELEASE_BASSERT(!oldLastEntry || oldLastEntry->offset() < offset);
82     
83     IsoTLSEntry* startEntry = oldLastEntry ? oldLastEntry : layout.head();
84     
85     IsoTLSEntry* targetEntry = startEntry;
86     size_t requiredCapacity = 0;
87     if (startEntry) {
88         for (;;) {
89             RELEASE_BASSERT(targetEntry);
90             RELEASE_BASSERT(targetEntry->offset() <= offset);
91             if (targetEntry->offset() == offset)
92                 break;
93             targetEntry = targetEntry->m_next;
94         }
95         RELEASE_BASSERT(targetEntry);
96         requiredCapacity = targetEntry->extent();
97     }
98     
99     if (!tls || requiredCapacity > tls->m_capacity) {
100         size_t requiredSize = sizeForCapacity(requiredCapacity);
101         size_t goodSize = roundUpToMultipleOf(vmPageSize(), requiredSize);
102         size_t goodCapacity = capacityForSize(goodSize);
103         void* memory = vmAllocate(goodSize);
104         IsoTLS* newTLS = new (memory) IsoTLS();
105         newTLS->m_capacity = goodCapacity;
106         if (tls) {
107             RELEASE_BASSERT(oldLastEntry);
108             RELEASE_BASSERT(layout.head());
109             layout.head()->walkUpToInclusive(
110                 oldLastEntry,
111                 [&] (IsoTLSEntry* entry) {
112                     void* src = tls->m_data + entry->offset();
113                     void* dst = newTLS->m_data + entry->offset();
114                     entry->move(src, dst);
115                     entry->destruct(src);
116                 });
117             vmDeallocate(tls, tls->size());
118         }
119         tls = newTLS;
120         set(tls);
121     }
122     
123     if (startEntry) {
124         startEntry->walkUpToInclusive(
125             targetEntry,
126             [&] (IsoTLSEntry* entry) {
127                 entry->construct(tls->m_data + entry->offset());
128             });
129         
130         tls->m_lastEntry = targetEntry;
131         tls->m_extent = targetEntry->extent();
132     }
133     
134     return tls;
135 }
136
137 void IsoTLS::destructor(void* arg)
138 {
139     IsoTLS* tls = static_cast<IsoTLS*>(arg);
140     RELEASE_BASSERT(tls);
141     tls->forEachEntry(
142         [&] (IsoTLSEntry* entry, void* data) {
143             entry->scavenge(data);
144             entry->destruct(data);
145         });
146 }
147
148 size_t IsoTLS::sizeForCapacity(unsigned capacity)
149 {
150     return BOFFSETOF(IsoTLS, m_data) + capacity;
151 }
152
153 unsigned IsoTLS::capacityForSize(size_t size)
154 {
155     return size - sizeForCapacity(0);
156 }
157
158 size_t IsoTLS::size()
159 {
160     return sizeForCapacity(m_capacity);
161 }
162
163 template<typename Func>
164 void IsoTLS::forEachEntry(const Func& func)
165 {
166     if (!m_lastEntry)
167         return;
168     PerProcess<IsoTLSLayout>::get()->head()->walkUpToInclusive(
169         m_lastEntry,
170         [&] (IsoTLSEntry* entry) {
171             func(entry, m_data + entry->offset());
172         });
173 }
174
175 bool IsoTLS::isUsingDebugHeap()
176 {
177     return PerProcess<Environment>::get()->isDebugHeapEnabled();
178 }
179
180 auto IsoTLS::debugMalloc(size_t size) -> DebugMallocResult
181 {
182     DebugMallocResult result;
183     if ((result.usingDebugHeap = isUsingDebugHeap()))
184         result.ptr = PerProcess<DebugHeap>::get()->malloc(size);
185     return result;
186 }
187
188 bool IsoTLS::debugFree(void* p)
189 {
190     if (isUsingDebugHeap()) {
191         PerProcess<DebugHeap>::get()->free(p);
192         return true;
193     }
194     return false;
195 }
196
197 void IsoTLS::determineMallocFallbackState()
198 {
199     static std::once_flag onceFlag;
200     std::call_once(
201         onceFlag,
202         [] {
203             if (s_mallocFallbackState != MallocFallbackState::Undecided)
204                 return;
205
206 #if GIGACAGE_ENABLED
207             if (!Gigacage::shouldBeEnabled()) {
208                 s_mallocFallbackState = MallocFallbackState::FallBackToMalloc;
209                 return;
210             }
211
212             const char* env = getenv("bmalloc_IsoHeap");
213             if (env && (!strcasecmp(env, "false") || !strcasecmp(env, "no") || !strcmp(env, "0")))
214                 s_mallocFallbackState = MallocFallbackState::FallBackToMalloc;
215             else
216                 s_mallocFallbackState = MallocFallbackState::DoNotFallBack;
217 #else
218             s_mallocFallbackState = MallocFallbackState::FallBackToMalloc;
219 #endif
220         });
221 }
222
223 } // namespace bmalloc
224