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