ee8ad94c4805d0a7346101587f8f6fac760ff1cc
[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 "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     AtomicStringTable::create(*this);
102     m_currentAtomicStringTable = m_defaultAtomicStringTable;
103 }
104
105 void Thread::entryPoint(NewThreadContext* newThreadContext)
106 {
107     Function<void()> function;
108     {
109         // Ref is already incremented by Thread::create.
110         Ref<NewThreadContext> context = adoptRef(*newThreadContext);
111         // Block until our creating thread has completed any extra setup work, including establishing ThreadIdentifier.
112         MutexLocker locker(context->mutex);
113         ASSERT(context->stage == NewThreadContext::Stage::EstablishedHandle);
114
115         Thread::initializeCurrentThreadInternal(context->name);
116         function = WTFMove(context->entryPoint);
117         context->thread->initializeInThread();
118
119         Thread::initializeTLS(WTFMove(context->thread));
120
121 #if !HAVE(STACK_BOUNDS_FOR_NEW_THREAD)
122         // Ack completion of initialization to the creating thread.
123         context->stage = NewThreadContext::Stage::Initialized;
124         context->condition.signal();
125 #endif
126     }
127
128     ASSERT(!Thread::current().stack().isEmpty());
129     function();
130 }
131
132 Ref<Thread> Thread::create(const char* name, Function<void()>&& entryPoint)
133 {
134     WTF::initializeThreading();
135     Ref<Thread> thread = adoptRef(*new Thread());
136     Ref<NewThreadContext> context = adoptRef(*new NewThreadContext { name, WTFMove(entryPoint), thread.copyRef() });
137     // 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:
138     // 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.
139     // 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.
140     // 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,
141     // and make the context ThreadSafeRefCounted.
142     context->ref();
143     {
144         MutexLocker locker(context->mutex);
145         bool success = thread->establishHandle(context.ptr());
146         RELEASE_ASSERT(success);
147         context->stage = NewThreadContext::Stage::EstablishedHandle;
148
149 #if HAVE(STACK_BOUNDS_FOR_NEW_THREAD)
150         thread->m_stack = StackBounds::newThreadStackBounds(thread->m_handle);
151 #else
152         // In platforms which do not support StackBounds::newThreadStackBounds(), we do not have a way to get stack
153         // bounds outside the target thread itself. Thus, we need to initialize thread information in the target thread
154         // and wait for completion of initialization in the caller side.
155         while (context->stage != NewThreadContext::Stage::Initialized)
156             context->condition.wait(context->mutex);
157 #endif
158     }
159
160     ASSERT(!thread->stack().isEmpty());
161     return thread;
162 }
163
164 static bool shouldRemoveThreadFromThreadGroup()
165 {
166 #if OS(WINDOWS)
167     // On Windows the thread specific destructor is also called when the
168     // main thread is exiting. This may lead to the main thread waiting
169     // forever for the thread group lock when exiting, if the sampling
170     // profiler thread was terminated by the system while holding the
171     // thread group lock.
172     if (WTF::isMainThread())
173         return false;
174 #endif
175     return true;
176 }
177
178 void Thread::didExit()
179 {
180     if (shouldRemoveThreadFromThreadGroup()) {
181         Vector<std::shared_ptr<ThreadGroup>> threadGroups;
182         {
183             std::lock_guard<std::mutex> locker(m_mutex);
184             for (auto& threadGroup : m_threadGroups) {
185                 // If ThreadGroup is just being destroyed,
186                 // we do not need to perform unregistering.
187                 if (auto retained = threadGroup.lock())
188                     threadGroups.append(WTFMove(retained));
189             }
190             m_isShuttingDown = true;
191         }
192         for (auto& threadGroup : threadGroups) {
193             std::lock_guard<std::mutex> threadGroupLocker(threadGroup->getLock());
194             std::lock_guard<std::mutex> locker(m_mutex);
195             threadGroup->m_threads.remove(*this);
196         }
197     }
198
199     AtomicStringTable::destroy(m_defaultAtomicStringTable);
200
201     // We would like to say "thread is exited" after unregistering threads from thread groups.
202     // So we need to separate m_isShuttingDown from m_didExit.
203     std::lock_guard<std::mutex> locker(m_mutex);
204     m_didExit = true;
205 }
206
207 ThreadGroupAddResult Thread::addToThreadGroup(const AbstractLocker& threadGroupLocker, ThreadGroup& threadGroup)
208 {
209     UNUSED_PARAM(threadGroupLocker);
210     std::lock_guard<std::mutex> locker(m_mutex);
211     if (m_isShuttingDown)
212         return ThreadGroupAddResult::NotAdded;
213     if (threadGroup.m_threads.add(*this).isNewEntry) {
214         m_threadGroups.append(threadGroup.weakFromThis());
215         return ThreadGroupAddResult::NewlyAdded;
216     }
217     return ThreadGroupAddResult::AlreadyAdded;
218 }
219
220 void Thread::removeFromThreadGroup(const AbstractLocker& threadGroupLocker, ThreadGroup& threadGroup)
221 {
222     UNUSED_PARAM(threadGroupLocker);
223     std::lock_guard<std::mutex> locker(m_mutex);
224     if (m_isShuttingDown)
225         return;
226     m_threadGroups.removeFirstMatching([&] (auto weakPtr) {
227         if (auto sharedPtr = weakPtr.lock())
228             return sharedPtr.get() == &threadGroup;
229         return false;
230     });
231 }
232
233 void Thread::setCurrentThreadIsUserInteractive(int relativePriority)
234 {
235 #if HAVE(QOS_CLASSES)
236     ASSERT(relativePriority <= 0);
237     ASSERT(relativePriority >= QOS_MIN_RELATIVE_PRIORITY);
238     pthread_set_qos_class_self_np(adjustedQOSClass(QOS_CLASS_USER_INTERACTIVE), relativePriority);
239 #else
240     UNUSED_PARAM(relativePriority);
241 #endif
242 }
243
244 void Thread::setCurrentThreadIsUserInitiated(int relativePriority)
245 {
246 #if HAVE(QOS_CLASSES)
247     ASSERT(relativePriority <= 0);
248     ASSERT(relativePriority >= QOS_MIN_RELATIVE_PRIORITY);
249     pthread_set_qos_class_self_np(adjustedQOSClass(QOS_CLASS_USER_INITIATED), relativePriority);
250 #else
251     UNUSED_PARAM(relativePriority);
252 #endif
253 }
254
255 #if HAVE(QOS_CLASSES)
256 static qos_class_t globalMaxQOSclass { QOS_CLASS_UNSPECIFIED };
257
258 void Thread::setGlobalMaxQOSClass(qos_class_t maxClass)
259 {
260     bmalloc::api::setScavengerThreadQOSClass(maxClass);
261     globalMaxQOSclass = maxClass;
262 }
263
264 qos_class_t Thread::adjustedQOSClass(qos_class_t originalClass)
265 {
266     if (globalMaxQOSclass != QOS_CLASS_UNSPECIFIED)
267         return std::min(originalClass, globalMaxQOSclass);
268     return originalClass;
269 }
270 #endif
271
272 void Thread::dump(PrintStream& out) const
273 {
274     out.print("Thread:", RawPointer(this));
275 }
276
277 #if !HAVE(FAST_TLS)
278 ThreadSpecificKey Thread::s_key = InvalidThreadSpecificKey;
279 #endif
280
281 void initializeThreading()
282 {
283     static std::once_flag onceKey;
284     std::call_once(onceKey, [] {
285         initializeRandomNumberGenerator();
286 #if !HAVE(FAST_TLS)
287         Thread::initializeTLSKey();
288 #endif
289         initializeDates();
290         Thread::initializePlatformThreading();
291     });
292 }
293
294 } // namespace WTF