It should be easy to decide how WebKit yields
[WebKit-https.git] / Source / WTF / wtf / MainThread.cpp
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 "MainThread.h"
31
32 #include "CurrentTime.h"
33 #include "Deque.h"
34 #include "MonotonicTime.h"
35 #include "StdLibExtras.h"
36 #include "Threading.h"
37 #include <mutex>
38 #include <wtf/Lock.h>
39 #include <wtf/NeverDestroyed.h>
40 #include <wtf/ThreadSpecific.h>
41
42 namespace WTF {
43
44 static bool callbacksPaused; // This global variable is only accessed from main thread.
45 #if !PLATFORM(COCOA)
46 static ThreadIdentifier mainThreadIdentifier;
47 #endif
48
49 static StaticLock mainThreadFunctionQueueMutex;
50
51 static Deque<Function<void ()>>& functionQueue()
52 {
53     static NeverDestroyed<Deque<Function<void ()>>> functionQueue;
54     return functionQueue;
55 }
56
57 // Share this initializeKey with initializeMainThread and initializeMainThreadToProcessMainThread.
58 static std::once_flag initializeKey;
59 void initializeMainThread()
60 {
61     std::call_once(initializeKey, [] {
62         initializeThreading();
63 #if !PLATFORM(COCOA)
64         mainThreadIdentifier = currentThread();
65 #endif
66         initializeMainThreadPlatform();
67         initializeGCThreads();
68     });
69 }
70
71 #if !PLATFORM(COCOA)
72 bool isMainThread()
73 {
74     return currentThread() == mainThreadIdentifier;
75 }
76 #endif
77
78 #if PLATFORM(COCOA)
79 #if !USE(WEB_THREAD)
80 void initializeMainThreadToProcessMainThread()
81 {
82     std::call_once(initializeKey, [] {
83         initializeThreading();
84         initializeMainThreadToProcessMainThreadPlatform();
85         initializeGCThreads();
86     });
87 }
88 #else
89 void initializeWebThread()
90 {
91     static std::once_flag initializeKey;
92     std::call_once(initializeKey, [] {
93         initializeWebThreadPlatform();
94     });
95 }
96 #endif // !USE(WEB_THREAD)
97 #endif // PLATFORM(COCOA)
98
99 #if !USE(WEB_THREAD)
100 bool canAccessThreadLocalDataForThread(ThreadIdentifier threadId)
101 {
102     return threadId == currentThread();
103 }
104 #endif
105
106 // 0.1 sec delays in UI is approximate threshold when they become noticeable. Have a limit that's half of that.
107 static const auto maxRunLoopSuspensionTime = 50_ms;
108
109 void dispatchFunctionsFromMainThread()
110 {
111     ASSERT(isMainThread());
112
113     if (callbacksPaused)
114         return;
115
116     auto startTime = MonotonicTime::now();
117
118     Function<void ()> function;
119
120     while (true) {
121         {
122             std::lock_guard<StaticLock> lock(mainThreadFunctionQueueMutex);
123             if (!functionQueue().size())
124                 break;
125
126             function = functionQueue().takeFirst();
127         }
128
129         function();
130
131         // Clearing the function can have side effects, so do so outside of the lock above.
132         function = nullptr;
133
134         // If we are running accumulated functions for too long so UI may become unresponsive, we need to
135         // yield so the user input can be processed. Otherwise user may not be able to even close the window.
136         // This code has effect only in case the scheduleDispatchFunctionsOnMainThread() is implemented in a way that
137         // allows input events to be processed before we are back here.
138         if (MonotonicTime::now() - startTime > maxRunLoopSuspensionTime) {
139             scheduleDispatchFunctionsOnMainThread();
140             break;
141         }
142     }
143 }
144
145 void callOnMainThread(Function<void ()>&& function)
146 {
147     ASSERT(function);
148
149     bool needToSchedule = false;
150
151     {
152         std::lock_guard<StaticLock> lock(mainThreadFunctionQueueMutex);
153         needToSchedule = functionQueue().size() == 0;
154         functionQueue().append(WTFMove(function));
155     }
156
157     if (needToSchedule)
158         scheduleDispatchFunctionsOnMainThread();
159 }
160
161 void setMainThreadCallbacksPaused(bool paused)
162 {
163     ASSERT(isMainThread());
164
165     if (callbacksPaused == paused)
166         return;
167
168     callbacksPaused = paused;
169
170     if (!callbacksPaused)
171         scheduleDispatchFunctionsOnMainThread();
172 }
173
174 static ThreadSpecific<std::optional<GCThreadType>, CanBeGCThread::True>* isGCThread;
175
176 void initializeGCThreads()
177 {
178     static std::once_flag flag;
179     std::call_once(
180         flag,
181         [] {
182             isGCThread = new ThreadSpecific<std::optional<GCThreadType>, CanBeGCThread::True>();
183         });
184 }
185
186 void registerGCThread(GCThreadType type)
187 {
188     if (!isGCThread) {
189         // This happens if we're running in a process that doesn't care about
190         // MainThread.
191         return;
192     }
193
194     **isGCThread = type;
195 }
196
197 bool isMainThreadOrGCThread()
198 {
199     if (mayBeGCThread())
200         return true;
201
202     return isMainThread();
203 }
204
205 std::optional<GCThreadType> mayBeGCThread()
206 {
207     if (!isGCThread)
208         return std::nullopt;
209     if (!isGCThread->isSet())
210         return std::nullopt;
211     return **isGCThread;
212 }
213
214 } // namespace WTF