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/AtomStringTable.h>
40#include <wtf/text/StringView.h>
41
42#if HAVE(QOS_CLASSES)
43#include <bmalloc/bmalloc.h>
44#endif
45
46namespace WTF {
47
48struct Thread::NewThreadContext : public ThreadSafeRefCounted<NewThreadContext> {
49public:
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 enum class Stage { Start, EstablishedHandle, Initialized };
58 Stage stage { Stage::Start };
59 const char* name;
60 Function<void()> entryPoint;
61 Ref<Thread> thread;
62 Mutex mutex;
63
64#if !HAVE(STACK_BOUNDS_FOR_NEW_THREAD)
65 ThreadCondition condition;
66#endif
67};
68
69HashSet<Thread*>& Thread::allThreads(const LockHolder&)
70{
71 static NeverDestroyed<HashSet<Thread*>> allThreads;
72 return allThreads;
73}
74
75Lock& Thread::allThreadsMutex()
76{
77 static Lock mutex;
78 return mutex;
79}
80
81const char* Thread::normalizeThreadName(const char* threadName)
82{
83#if HAVE(PTHREAD_SETNAME_NP)
84 return threadName;
85#else
86 // This name can be com.apple.WebKit.ProcessLauncher or com.apple.CoreIPC.ReceiveQueue.
87 // We are using those names for the thread name, but both are longer than the limit of
88 // the platform thread name length, 32 for Windows and 16 for Linux.
89 StringView result(threadName);
90 size_t size = result.reverseFind('.');
91 if (size != notFound)
92 result = result.substring(size + 1);
93
94#if OS(WINDOWS)
95 constexpr const size_t kVisualStudioThreadNameLimit = 32 - 1;
96 if (result.length() > kVisualStudioThreadNameLimit)
97 result = result.right(kVisualStudioThreadNameLimit);
98#elif OS(LINUX)
99 constexpr const size_t kLinuxThreadNameLimit = 16 - 1;
100 if (result.length() > kLinuxThreadNameLimit)
101 result = result.right(kLinuxThreadNameLimit);
102#endif
103 ASSERT(result.characters8()[result.length()] == '\0');
104 return reinterpret_cast<const char*>(result.characters8());
105#endif
106}
107
108void Thread::initializeInThread()
109{
110 if (m_stack.isEmpty())
111 m_stack = StackBounds::currentThreadStackBounds();
112 m_savedLastStackTop = stack().origin();
113
114 m_currentAtomStringTable = &m_defaultAtomStringTable;
115#if USE(WEB_THREAD)
116 // On iOS, one AtomStringTable is shared between the main UI thread and the WebThread.
117 if (isWebThread() || isUIThread()) {
118 static NeverDestroyed<AtomStringTable> sharedStringTable;
119 m_currentAtomStringTable = &sharedStringTable.get();
120 }
121#endif
122}
123
124void Thread::entryPoint(NewThreadContext* newThreadContext)
125{
126 Function<void()> function;
127 {
128 // Ref is already incremented by Thread::create.
129 Ref<NewThreadContext> context = adoptRef(*newThreadContext);
130 // Block until our creating thread has completed any extra setup work, including establishing ThreadIdentifier.
131 MutexLocker locker(context->mutex);
132 ASSERT(context->stage == NewThreadContext::Stage::EstablishedHandle);
133
134 Thread::initializeCurrentThreadInternal(context->name);
135 function = WTFMove(context->entryPoint);
136 context->thread->initializeInThread();
137
138 Thread::initializeTLS(WTFMove(context->thread));
139
140#if !HAVE(STACK_BOUNDS_FOR_NEW_THREAD)
141 // Ack completion of initialization to the creating thread.
142 context->stage = NewThreadContext::Stage::Initialized;
143 context->condition.signal();
144#endif
145 }
146
147 ASSERT(!Thread::current().stack().isEmpty());
148 function();
149}
150
151Ref<Thread> Thread::create(const char* name, Function<void()>&& entryPoint)
152{
153 WTF::initializeThreading();
154 Ref<Thread> thread = adoptRef(*new Thread());
155 Ref<NewThreadContext> context = adoptRef(*new NewThreadContext { name, WTFMove(entryPoint), thread.copyRef() });
156 // 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:
157 // 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.
158 // 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.
159 // 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,
160 // and make the context ThreadSafeRefCounted.
161 context->ref();
162 {
163 MutexLocker locker(context->mutex);
164 bool success = thread->establishHandle(context.ptr());
165 RELEASE_ASSERT(success);
166 context->stage = NewThreadContext::Stage::EstablishedHandle;
167
168#if HAVE(STACK_BOUNDS_FOR_NEW_THREAD)
169 thread->m_stack = StackBounds::newThreadStackBounds(thread->m_handle);
170#else
171 // In platforms which do not support StackBounds::newThreadStackBounds(), we do not have a way to get stack
172 // bounds outside the target thread itself. Thus, we need to initialize thread information in the target thread
173 // and wait for completion of initialization in the caller side.
174 while (context->stage != NewThreadContext::Stage::Initialized)
175 context->condition.wait(context->mutex);
176#endif
177 }
178
179 {
180 LockHolder lock(allThreadsMutex());
181 allThreads(lock).add(&thread.get());
182 }
183
184 ASSERT(!thread->stack().isEmpty());
185 return thread;
186}
187
188static bool shouldRemoveThreadFromThreadGroup()
189{
190#if OS(WINDOWS)
191 // On Windows the thread specific destructor is also called when the
192 // main thread is exiting. This may lead to the main thread waiting
193 // forever for the thread group lock when exiting, if the sampling
194 // profiler thread was terminated by the system while holding the
195 // thread group lock.
196 if (WTF::isMainThread())
197 return false;
198#endif
199 return true;
200}
201
202void Thread::didExit()
203{
204 {
205 LockHolder lock(allThreadsMutex());
206 allThreads(lock).remove(this);
207 }
208
209 if (shouldRemoveThreadFromThreadGroup()) {
210 {
211 Vector<std::shared_ptr<ThreadGroup>> threadGroups;
212 {
213 auto locker = holdLock(m_mutex);
214 for (auto& threadGroup : m_threadGroups) {
215 // If ThreadGroup is just being destroyed,
216 // we do not need to perform unregistering.
217 if (auto retained = threadGroup.lock())
218 threadGroups.append(WTFMove(retained));
219 }
220 m_isShuttingDown = true;
221 }
222 for (auto& threadGroup : threadGroups) {
223 auto threadGroupLocker = holdLock(threadGroup->getLock());
224 auto locker = holdLock(m_mutex);
225 threadGroup->m_threads.remove(*this);
226 }
227 }
228
229 // We would like to say "thread is exited" after unregistering threads from thread groups.
230 // So we need to separate m_isShuttingDown from m_didExit.
231 auto locker = holdLock(m_mutex);
232 m_didExit = true;
233 }
234}
235
236ThreadGroupAddResult Thread::addToThreadGroup(const AbstractLocker& threadGroupLocker, ThreadGroup& threadGroup)
237{
238 UNUSED_PARAM(threadGroupLocker);
239 auto locker = holdLock(m_mutex);
240 if (m_isShuttingDown)
241 return ThreadGroupAddResult::NotAdded;
242 if (threadGroup.m_threads.add(*this).isNewEntry) {
243 m_threadGroups.append(threadGroup.weakFromThis());
244 return ThreadGroupAddResult::NewlyAdded;
245 }
246 return ThreadGroupAddResult::AlreadyAdded;
247}
248
249void Thread::removeFromThreadGroup(const AbstractLocker& threadGroupLocker, ThreadGroup& threadGroup)
250{
251 UNUSED_PARAM(threadGroupLocker);
252 auto locker = holdLock(m_mutex);
253 if (m_isShuttingDown)
254 return;
255 m_threadGroups.removeFirstMatching([&] (auto weakPtr) {
256 if (auto sharedPtr = weakPtr.lock())
257 return sharedPtr.get() == &threadGroup;
258 return false;
259 });
260}
261
262bool Thread::exchangeIsCompilationThread(bool newValue)
263{
264 auto& thread = Thread::current();
265 bool oldValue = thread.m_isCompilationThread;
266 thread.m_isCompilationThread = newValue;
267 return oldValue;
268}
269
270void Thread::registerGCThread(GCThreadType gcThreadType)
271{
272 Thread::current().m_gcThreadType = static_cast<unsigned>(gcThreadType);
273}
274
275bool Thread::mayBeGCThread()
276{
277 return Thread::current().gcThreadType() != GCThreadType::None;
278}
279
280void Thread::setCurrentThreadIsUserInteractive(int relativePriority)
281{
282#if HAVE(QOS_CLASSES)
283 ASSERT(relativePriority <= 0);
284 ASSERT(relativePriority >= QOS_MIN_RELATIVE_PRIORITY);
285 pthread_set_qos_class_self_np(adjustedQOSClass(QOS_CLASS_USER_INTERACTIVE), relativePriority);
286#else
287 UNUSED_PARAM(relativePriority);
288#endif
289}
290
291void Thread::setCurrentThreadIsUserInitiated(int relativePriority)
292{
293#if HAVE(QOS_CLASSES)
294 ASSERT(relativePriority <= 0);
295 ASSERT(relativePriority >= QOS_MIN_RELATIVE_PRIORITY);
296 pthread_set_qos_class_self_np(adjustedQOSClass(QOS_CLASS_USER_INITIATED), relativePriority);
297#else
298 UNUSED_PARAM(relativePriority);
299#endif
300}
301
302#if HAVE(QOS_CLASSES)
303static qos_class_t globalMaxQOSclass { QOS_CLASS_UNSPECIFIED };
304
305void Thread::setGlobalMaxQOSClass(qos_class_t maxClass)
306{
307 bmalloc::api::setScavengerThreadQOSClass(maxClass);
308 globalMaxQOSclass = maxClass;
309}
310
311qos_class_t Thread::adjustedQOSClass(qos_class_t originalClass)
312{
313 if (globalMaxQOSclass != QOS_CLASS_UNSPECIFIED)
314 return std::min(originalClass, globalMaxQOSclass);
315 return originalClass;
316}
317#endif
318
319void Thread::dump(PrintStream& out) const
320{
321 out.print("Thread:", RawPointer(this));
322}
323
324#if !HAVE(FAST_TLS)
325ThreadSpecificKey Thread::s_key = InvalidThreadSpecificKey;
326#endif
327
328void initializeThreading()
329{
330 static std::once_flag onceKey;
331 std::call_once(onceKey, [] {
332 initializeRandomNumberGenerator();
333#if !HAVE(FAST_TLS)
334 Thread::initializeTLSKey();
335#endif
336 initializeDates();
337 Thread::initializePlatformThreading();
338 });
339}
340
341} // namespace WTF
342