1/*
2 * Copyright (C) 2007-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 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include <wtf/MainThread.h>
31
32#include <mutex>
33#include <wtf/Condition.h>
34#include <wtf/Deque.h>
35#include <wtf/Lock.h>
36#include <wtf/MonotonicTime.h>
37#include <wtf/NeverDestroyed.h>
38#include <wtf/StdLibExtras.h>
39#include <wtf/ThreadSpecific.h>
40#include <wtf/Threading.h>
41
42namespace WTF {
43
44static bool callbacksPaused; // This global variable is only accessed from main thread.
45static Lock mainThreadFunctionQueueMutex;
46
47static Deque<Function<void ()>>& functionQueue()
48{
49 static NeverDestroyed<Deque<Function<void ()>>> functionQueue;
50 return functionQueue;
51}
52
53// Share this initializeKey with initializeMainThread and initializeMainThreadToProcessMainThread.
54static std::once_flag initializeKey;
55void initializeMainThread()
56{
57 std::call_once(initializeKey, [] {
58 initializeThreading();
59 initializeMainThreadPlatform();
60 initializeGCThreads();
61 });
62}
63
64#if PLATFORM(COCOA)
65#if !USE(WEB_THREAD)
66void initializeMainThreadToProcessMainThread()
67{
68 std::call_once(initializeKey, [] {
69 initializeThreading();
70 initializeMainThreadToProcessMainThreadPlatform();
71 initializeGCThreads();
72 });
73}
74#else
75void initializeWebThread()
76{
77 static std::once_flag initializeKey;
78 std::call_once(initializeKey, [] {
79 initializeWebThreadPlatform();
80 });
81}
82#endif // !USE(WEB_THREAD)
83#endif // PLATFORM(COCOA)
84
85#if !USE(WEB_THREAD)
86bool canAccessThreadLocalDataForThread(Thread& thread)
87{
88 return &thread == &Thread::current();
89}
90#endif
91
92// 0.1 sec delays in UI is approximate threshold when they become noticeable. Have a limit that's half of that.
93static const auto maxRunLoopSuspensionTime = 50_ms;
94
95void dispatchFunctionsFromMainThread()
96{
97 ASSERT(isMainThread());
98
99 if (callbacksPaused)
100 return;
101
102 auto startTime = MonotonicTime::now();
103
104 Function<void ()> function;
105
106 while (true) {
107 {
108 std::lock_guard<Lock> lock(mainThreadFunctionQueueMutex);
109 if (!functionQueue().size())
110 break;
111
112 function = functionQueue().takeFirst();
113 }
114
115 function();
116
117 // Clearing the function can have side effects, so do so outside of the lock above.
118 function = nullptr;
119
120 // If we are running accumulated functions for too long so UI may become unresponsive, we need to
121 // yield so the user input can be processed. Otherwise user may not be able to even close the window.
122 // This code has effect only in case the scheduleDispatchFunctionsOnMainThread() is implemented in a way that
123 // allows input events to be processed before we are back here.
124 if (MonotonicTime::now() - startTime > maxRunLoopSuspensionTime) {
125 scheduleDispatchFunctionsOnMainThread();
126 break;
127 }
128 }
129}
130
131void callOnMainThread(Function<void()>&& function)
132{
133 ASSERT(function);
134
135 bool needToSchedule = false;
136
137 {
138 std::lock_guard<Lock> lock(mainThreadFunctionQueueMutex);
139 needToSchedule = functionQueue().size() == 0;
140 functionQueue().append(WTFMove(function));
141 }
142
143 if (needToSchedule)
144 scheduleDispatchFunctionsOnMainThread();
145}
146
147void setMainThreadCallbacksPaused(bool paused)
148{
149 ASSERT(isMainThread());
150
151 if (callbacksPaused == paused)
152 return;
153
154 callbacksPaused = paused;
155
156 if (!callbacksPaused)
157 scheduleDispatchFunctionsOnMainThread();
158}
159
160static ThreadSpecific<Optional<GCThreadType>, CanBeGCThread::True>* isGCThread;
161
162void initializeGCThreads()
163{
164 static std::once_flag flag;
165 std::call_once(
166 flag,
167 [] {
168 isGCThread = new ThreadSpecific<Optional<GCThreadType>, CanBeGCThread::True>();
169 });
170}
171
172void registerGCThread(GCThreadType type)
173{
174 if (!isGCThread) {
175 // This happens if we're running in a process that doesn't care about
176 // MainThread.
177 return;
178 }
179
180 **isGCThread = type;
181}
182
183bool isMainThreadOrGCThread()
184{
185 if (mayBeGCThread())
186 return true;
187
188 return isMainThread();
189}
190
191Optional<GCThreadType> mayBeGCThread()
192{
193 if (!isGCThread)
194 return WTF::nullopt;
195 if (!isGCThread->isSet())
196 return WTF::nullopt;
197 return **isGCThread;
198}
199
200void callOnMainThreadAndWait(WTF::Function<void()>&& function)
201{
202 if (isMainThread()) {
203 function();
204 return;
205 }
206
207 Lock mutex;
208 Condition conditionVariable;
209
210 bool isFinished = false;
211
212 callOnMainThread([&, function = WTFMove(function)] {
213 function();
214
215 std::lock_guard<Lock> lock(mutex);
216 isFinished = true;
217 conditionVariable.notifyOne();
218 });
219
220 std::unique_lock<Lock> lock(mutex);
221 conditionVariable.wait(lock, [&] {
222 return isFinished;
223 });
224}
225
226} // namespace WTF
227