56faf7d54ee29fcb8a3125cfbeeb5a5d4801075c
[WebKit-https.git] / Source / WebCore / workers / WorkerRunLoop.cpp
1 /*
2  * Copyright (C) 2009 Google Inc. All rights reserved.
3  * Copyright (C) 2016-2017 Apple Inc.  All rights reserved.
4  * 
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  * 
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  * 
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31  
32 #include "config.h"
33
34 #include "ScriptExecutionContext.h"
35 #include "SharedTimer.h"
36 #include "ThreadGlobalData.h"
37 #include "ThreadTimers.h"
38 #include "WorkerRunLoop.h"
39 #include "WorkerGlobalScope.h"
40 #include "WorkerThread.h"
41 #include <JavaScriptCore/PromiseDeferredTimer.h>
42 #include <wtf/CurrentTime.h>
43
44 #if USE(GLIB)
45 #include <glib.h>
46 #endif
47
48 namespace WebCore {
49
50 class WorkerSharedTimer final : public SharedTimer {
51 public:
52     // SharedTimer interface.
53     void setFiredFunction(WTF::Function<void()>&& function) override { m_sharedTimerFunction = WTFMove(function); }
54     void setFireInterval(Seconds interval) override { m_nextFireTime = interval + WallTime::now(); }
55     void stop() override { m_nextFireTime = WallTime(); }
56
57     bool isActive() { return m_sharedTimerFunction && m_nextFireTime; }
58     WallTime fireTime() { return m_nextFireTime; }
59     void fire() { m_sharedTimerFunction(); }
60
61 private:
62     WTF::Function<void()> m_sharedTimerFunction;
63     WallTime m_nextFireTime;
64 };
65
66 class ModePredicate {
67 public:
68     ModePredicate(const String& mode)
69         : m_mode(mode)
70         , m_defaultMode(mode == WorkerRunLoop::defaultMode())
71     {
72     }
73
74     bool isDefaultMode() const
75     {
76         return m_defaultMode;
77     }
78
79     bool operator()(const WorkerRunLoop::Task& task) const
80     {
81         return m_defaultMode || m_mode == task.mode();
82     }
83
84 private:
85     String m_mode;
86     bool m_defaultMode;
87 };
88
89 WorkerRunLoop::WorkerRunLoop()
90     : m_sharedTimer(std::make_unique<WorkerSharedTimer>())
91     , m_nestedCount(0)
92     , m_uniqueId(0)
93 {
94 }
95
96 WorkerRunLoop::~WorkerRunLoop()
97 {
98     ASSERT(!m_nestedCount);
99 }
100
101 String WorkerRunLoop::defaultMode()
102 {
103     return String();
104 }
105
106 String WorkerRunLoop::debuggerMode()
107 {
108     return ASCIILiteral("debugger");
109 }
110
111 class RunLoopSetup {
112     WTF_MAKE_NONCOPYABLE(RunLoopSetup);
113 public:
114     RunLoopSetup(WorkerRunLoop& runLoop)
115         : m_runLoop(runLoop)
116     {
117         if (!m_runLoop.m_nestedCount)
118             threadGlobalData().threadTimers().setSharedTimer(m_runLoop.m_sharedTimer.get());
119         m_runLoop.m_nestedCount++;
120     }
121
122     ~RunLoopSetup()
123     {
124         m_runLoop.m_nestedCount--;
125         if (!m_runLoop.m_nestedCount)
126             threadGlobalData().threadTimers().setSharedTimer(nullptr);
127     }
128 private:
129     WorkerRunLoop& m_runLoop;
130 };
131
132 void WorkerRunLoop::run(WorkerGlobalScope* context)
133 {
134     RunLoopSetup setup(*this);
135     ModePredicate modePredicate(defaultMode());
136     MessageQueueWaitResult result;
137     do {
138         result = runInMode(context, modePredicate, WaitForMessage);
139     } while (result != MessageQueueTerminated);
140     runCleanupTasks(context);
141 }
142
143 MessageQueueWaitResult WorkerRunLoop::runInMode(WorkerGlobalScope* context, const String& mode, WaitMode waitMode)
144 {
145     RunLoopSetup setup(*this);
146     ModePredicate modePredicate(mode);
147     MessageQueueWaitResult result = runInMode(context, modePredicate, waitMode);
148     return result;
149 }
150
151 MessageQueueWaitResult WorkerRunLoop::runInMode(WorkerGlobalScope* context, const ModePredicate& predicate, WaitMode waitMode)
152 {
153     ASSERT(context);
154     ASSERT(context->thread().thread() == &Thread::current());
155
156     JSC::JSRunLoopTimer::TimerNotificationCallback timerAddedTask = WTF::createSharedTask<JSC::JSRunLoopTimer::TimerNotificationType>([this] {
157         // We don't actually do anything here, we just want to loop around runInMode
158         // to both recalculate our deadline and to potentially run the run loop.
159         this->postTask([](ScriptExecutionContext&) { }); 
160     });
161
162 #if USE(GLIB)
163     GMainContext* mainContext = g_main_context_get_thread_default();
164     if (g_main_context_pending(mainContext))
165         g_main_context_iteration(mainContext, FALSE);
166 #endif
167
168     WallTime deadline = WallTime::infinity();
169
170 #if USE(CF)
171     CFAbsoluteTime nextCFRunLoopTimerFireDate = CFRunLoopGetNextTimerFireDate(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
172     double timeUntilNextCFRunLoopTimerInSeconds = nextCFRunLoopTimerFireDate - CFAbsoluteTimeGetCurrent();
173     deadline = WallTime::now() + std::max(0_s, Seconds(timeUntilNextCFRunLoopTimerInSeconds));
174 #endif
175
176     WallTime absoluteTime;
177     if (waitMode == WaitForMessage) {
178         if (predicate.isDefaultMode() && m_sharedTimer->isActive())
179             absoluteTime = std::min(deadline, m_sharedTimer->fireTime());
180         else
181             absoluteTime = deadline;
182     }
183
184     if (WorkerScriptController* script = context->script()) {
185         script->releaseHeapAccess();
186         script->addTimerSetNotification(timerAddedTask);
187     }
188     MessageQueueWaitResult result;
189     auto task = m_messageQueue.waitForMessageFilteredWithTimeout(result, predicate, absoluteTime);
190     if (WorkerScriptController* script = context->script()) {
191         script->acquireHeapAccess();
192         script->removeTimerSetNotification(timerAddedTask);
193     }
194
195     // If the context is closing, don't execute any further JavaScript tasks (per section 4.1.1 of the Web Workers spec).  However, there may be implementation cleanup tasks in the queue, so keep running through it.
196
197     switch (result) {
198     case MessageQueueTerminated:
199         break;
200
201     case MessageQueueMessageReceived:
202         task->performTask(context);
203         break;
204
205     case MessageQueueTimeout:
206         if (!context->isClosing() && !isNested())
207             m_sharedTimer->fire();
208         break;
209     }
210
211 #if USE(CF)
212     if (result != MessageQueueTerminated) {
213         if (nextCFRunLoopTimerFireDate <= CFAbsoluteTimeGetCurrent())
214             CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, /*returnAfterSourceHandled*/ false);
215     }
216 #endif
217
218     return result;
219 }
220
221 void WorkerRunLoop::runCleanupTasks(WorkerGlobalScope* context)
222 {
223     ASSERT(context);
224     ASSERT(context->thread().thread() == &Thread::current());
225     ASSERT(m_messageQueue.killed());
226
227     while (true) {
228         auto task = m_messageQueue.tryGetMessageIgnoringKilled();
229         if (!task)
230             return;
231         task->performTask(context);
232     }
233 }
234
235 void WorkerRunLoop::terminate()
236 {
237     m_messageQueue.kill();
238 }
239
240 void WorkerRunLoop::postTask(ScriptExecutionContext::Task&& task)
241 {
242     postTaskForMode(WTFMove(task), defaultMode());
243 }
244
245 void WorkerRunLoop::postTaskAndTerminate(ScriptExecutionContext::Task&& task)
246 {
247     m_messageQueue.appendAndKill(std::make_unique<Task>(WTFMove(task), defaultMode()));
248 }
249
250 void WorkerRunLoop::postTaskForMode(ScriptExecutionContext::Task&& task, const String& mode)
251 {
252     m_messageQueue.append(std::make_unique<Task>(WTFMove(task), mode));
253 }
254
255 void WorkerRunLoop::Task::performTask(WorkerGlobalScope* context)
256 {
257     if ((!context->isClosing() && context->script() && !context->script()->isTerminatingExecution()) || m_task.isCleanupTask())
258         m_task.performTask(*context);
259 }
260
261 WorkerRunLoop::Task::Task(ScriptExecutionContext::Task&& task, const String& mode)
262     : m_task(WTFMove(task))
263     , m_mode(mode.isolatedCopy())
264 {
265 }
266
267 } // namespace WebCore