[WTF] Add environment variable helpers
[WebKit-https.git] / Source / WTF / wtf / Threading.cpp
1 /*
2  * Copyright (C) 2008-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  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include <wtf/Threading.h>
28
29 #include <algorithm>
30 #include <cmath>
31 #include <cstring>
32 #include <thread>
33 #include <wtf/DateMath.h>
34 #include <wtf/PrintStream.h>
35 #include <wtf/RandomNumberSeed.h>
36 #include <wtf/ThreadGroup.h>
37 #include <wtf/ThreadMessage.h>
38 #include <wtf/ThreadingPrimitives.h>
39 #include <wtf/text/AtomicStringTable.h>
40 #include <wtf/text/StringView.h>
41
42 #if HAVE(QOS_CLASSES)
43 #include <bmalloc/bmalloc.h>
44 #endif
45
46 namespace WTF {
47
48 struct Thread::NewThreadContext : public ThreadSafeRefCounted<NewThreadContext> {
49 public:
50     NewThreadContext(const char* name, Function<void()>&& entryPoint, Ref<Thread>&& thread)
51         : name(name)
52         , entryPoint(WTFMove(entryPoint))
53         , thread(WTFMove(thread))
54     {
55     }
56
57     const char* name;
58     Function<void()> entryPoint;
59     Ref<Thread> thread;
60     Mutex mutex;
61     enum class Stage { Start, EstablishedHandle, Initialized };
62     Stage stage { Stage::Start };
63
64 #if !HAVE(STACK_BOUNDS_FOR_NEW_THREAD)
65     ThreadCondition condition;
66 #endif
67 };
68
69 const char* Thread::normalizeThreadName(const char* threadName)
70 {
71 #if HAVE(PTHREAD_SETNAME_NP)
72     return threadName;
73 #else
74     // This name can be com.apple.WebKit.ProcessLauncher or com.apple.CoreIPC.ReceiveQueue.
75     // We are using those names for the thread name, but both are longer than the limit of
76     // the platform thread name length, 32 for Windows and 16 for Linux.
77     StringView result(threadName);
78     size_t size = result.reverseFind('.');
79     if (size != notFound)
80         result = result.substring(size + 1);
81
82 #if OS(WINDOWS)
83     constexpr const size_t kVisualStudioThreadNameLimit = 32 - 1;
84     if (result.length() > kVisualStudioThreadNameLimit)
85         result = result.right(kVisualStudioThreadNameLimit);
86 #elif OS(LINUX)
87     constexpr const size_t kLinuxThreadNameLimit = 16 - 1;
88     if (result.length() > kLinuxThreadNameLimit)
89         result = result.right(kLinuxThreadNameLimit);
90 #endif
91     ASSERT(result.characters8()[result.length()] == '\0');
92     return reinterpret_cast<const char*>(result.characters8());
93 #endif
94 }
95
96 void Thread::initializeInThread()
97 {
98     if (m_stack.isEmpty())
99         m_stack = StackBounds::currentThreadStackBounds();
100     m_savedLastStackTop = stack().origin();
101
102     m_currentAtomicStringTable = &m_defaultAtomicStringTable;
103 #if USE(WEB_THREAD)
104     // On iOS, one AtomicStringTable is shared between the main UI thread and the WebThread.
105     if (isWebThread() || isUIThread()) {
106         static NeverDestroyed<AtomicStringTable> sharedStringTable;
107         m_currentAtomicStringTable = &sharedStringTable.get();
108     }
109 #endif
110 }
111
112 void Thread::entryPoint(NewThreadContext* newThreadContext)
113 {
114     Function<void()> function;
115     {
116         // Ref is already incremented by Thread::create.
117         Ref<NewThreadContext> context = adoptRef(*newThreadContext);
118         // Block until our creating thread has completed any extra setup work, including establishing ThreadIdentifier.
119         MutexLocker locker(context->mutex);
120         ASSERT(context->stage == NewThreadContext::Stage::EstablishedHandle);
121
122         Thread::initializeCurrentThreadInternal(context->name);
123         function = WTFMove(context->entryPoint);
124         context->thread->initializeInThread();
125
126         Thread::initializeTLS(WTFMove(context->thread));
127
128 #if !HAVE(STACK_BOUNDS_FOR_NEW_THREAD)
129         // Ack completion of initialization to the creating thread.
130         context->stage = NewThreadContext::Stage::Initialized;
131         context->condition.signal();
132 #endif
133     }
134
135     ASSERT(!Thread::current().stack().isEmpty());
136     function();
137 }
138
139 Ref<Thread> Thread::create(const char* name, Function<void()>&& entryPoint)
140 {
141     WTF::initializeThreading();
142     Ref<Thread> thread = adoptRef(*new Thread());
143     Ref<NewThreadContext> context = adoptRef(*new NewThreadContext { name, WTFMove(entryPoint), thread.copyRef() });
144     // Increment the context ref on behalf of the created thread. We do not just use a unique_ptr and leak it to the created thread because both the creator and created thread has a need to keep the context alive:
145     // 1. the created thread needs to keep it alive because Thread::create() can exit before the created thread has a chance to use the context.
146     // 2. the creator thread (if HAVE(STACK_BOUNDS_FOR_NEW_THREAD) is false) needs to keep it alive because the created thread may exit before the creator has a chance to wake up from waiting for the completion of the created thread's initialization. This waiting uses a condition variable in the context.
147     // Hence, a joint ownership model is needed if HAVE(STACK_BOUNDS_FOR_NEW_THREAD) is false. To simplify the code, we just go with joint ownership by both the creator and created threads,
148     // and make the context ThreadSafeRefCounted.
149     context->ref();
150     {
151         MutexLocker locker(context->mutex);
152         bool success = thread->establishHandle(context.ptr());
153         RELEASE_ASSERT(success);
154         context->stage = NewThreadContext::Stage::EstablishedHandle;
155
156 #if HAVE(STACK_BOUNDS_FOR_NEW_THREAD)
157         thread->m_stack = StackBounds::newThreadStackBounds(thread->m_handle);
158 #else
159         // In platforms which do not support StackBounds::newThreadStackBounds(), we do not have a way to get stack
160         // bounds outside the target thread itself. Thus, we need to initialize thread information in the target thread
161         // and wait for completion of initialization in the caller side.
162         while (context->stage != NewThreadContext::Stage::Initialized)
163             context->condition.wait(context->mutex);
164 #endif
165     }
166
167     ASSERT(!thread->stack().isEmpty());
168     return thread;
169 }
170
171 static bool shouldRemoveThreadFromThreadGroup()
172 {
173 #if OS(WINDOWS)
174     // On Windows the thread specific destructor is also called when the
175     // main thread is exiting. This may lead to the main thread waiting
176     // forever for the thread group lock when exiting, if the sampling
177     // profiler thread was terminated by the system while holding the
178     // thread group lock.
179     if (WTF::isMainThread())
180         return false;
181 #endif
182     return true;
183 }
184
185 void Thread::didExit()
186 {
187     if (shouldRemoveThreadFromThreadGroup()) {
188         {
189             Vector<std::shared_ptr<ThreadGroup>> threadGroups;
190             {
191                 auto locker = holdLock(m_mutex);
192                 for (auto& threadGroup : m_threadGroups) {
193                     // If ThreadGroup is just being destroyed,
194                     // we do not need to perform unregistering.
195                     if (auto retained = threadGroup.lock())
196                         threadGroups.append(WTFMove(retained));
197                 }
198                 m_isShuttingDown = true;
199             }
200             for (auto& threadGroup : threadGroups) {
201                 auto threadGroupLocker = holdLock(threadGroup->getLock());
202                 auto locker = holdLock(m_mutex);
203                 threadGroup->m_threads.remove(*this);
204             }
205         }
206
207         // We would like to say "thread is exited" after unregistering threads from thread groups.
208         // So we need to separate m_isShuttingDown from m_didExit.
209         auto locker = holdLock(m_mutex);
210         m_didExit = true;
211     }
212 }
213
214 ThreadGroupAddResult Thread::addToThreadGroup(const AbstractLocker& threadGroupLocker, ThreadGroup& threadGroup)
215 {
216     UNUSED_PARAM(threadGroupLocker);
217     auto locker = holdLock(m_mutex);
218     if (m_isShuttingDown)
219         return ThreadGroupAddResult::NotAdded;
220     if (threadGroup.m_threads.add(*this).isNewEntry) {
221         m_threadGroups.append(threadGroup.weakFromThis());
222         return ThreadGroupAddResult::NewlyAdded;
223     }
224     return ThreadGroupAddResult::AlreadyAdded;
225 }
226
227 void Thread::removeFromThreadGroup(const AbstractLocker& threadGroupLocker, ThreadGroup& threadGroup)
228 {
229     UNUSED_PARAM(threadGroupLocker);
230     auto locker = holdLock(m_mutex);
231     if (m_isShuttingDown)
232         return;
233     m_threadGroups.removeFirstMatching([&] (auto weakPtr) {
234         if (auto sharedPtr = weakPtr.lock())
235             return sharedPtr.get() == &threadGroup;
236         return false;
237     });
238 }
239
240 void Thread::setCurrentThreadIsUserInteractive(int relativePriority)
241 {
242 #if HAVE(QOS_CLASSES)
243     ASSERT(relativePriority <= 0);
244     ASSERT(relativePriority >= QOS_MIN_RELATIVE_PRIORITY);
245     pthread_set_qos_class_self_np(adjustedQOSClass(QOS_CLASS_USER_INTERACTIVE), relativePriority);
246 #else
247     UNUSED_PARAM(relativePriority);
248 #endif
249 }
250
251 void Thread::setCurrentThreadIsUserInitiated(int relativePriority)
252 {
253 #if HAVE(QOS_CLASSES)
254     ASSERT(relativePriority <= 0);
255     ASSERT(relativePriority >= QOS_MIN_RELATIVE_PRIORITY);
256     pthread_set_qos_class_self_np(adjustedQOSClass(QOS_CLASS_USER_INITIATED), relativePriority);
257 #else
258     UNUSED_PARAM(relativePriority);
259 #endif
260 }
261
262 #if HAVE(QOS_CLASSES)
263 static qos_class_t globalMaxQOSclass { QOS_CLASS_UNSPECIFIED };
264
265 void Thread::setGlobalMaxQOSClass(qos_class_t maxClass)
266 {
267     bmalloc::api::setScavengerThreadQOSClass(maxClass);
268     globalMaxQOSclass = maxClass;
269 }
270
271 qos_class_t Thread::adjustedQOSClass(qos_class_t originalClass)
272 {
273     if (globalMaxQOSclass != QOS_CLASS_UNSPECIFIED)
274         return std::min(originalClass, globalMaxQOSclass);
275     return originalClass;
276 }
277 #endif
278
279 void Thread::dump(PrintStream& out) const
280 {
281     out.print("Thread:", RawPointer(this));
282 }
283
284 #if !HAVE(FAST_TLS)
285 ThreadSpecificKey Thread::s_key = InvalidThreadSpecificKey;
286 #endif
287
288 static bool initialized = false;
289
290 bool threadingIsInitialized()
291 {
292     return initialized;
293 }
294
295 void initializeThreading()
296 {
297     static std::once_flag onceKey;
298     std::call_once(onceKey, [] {
299         initialized = true;
300         initializeRandomNumberGenerator();
301 #if !HAVE(FAST_TLS)
302         Thread::initializeTLSKey();
303 #endif
304         initializeDates();
305         Thread::initializePlatformThreading();
306     });
307 }
308
309 } // namespace WTF