2 * Copyright (C) 2009 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "core/workers/WorkerRunLoop.h"
34 #include "core/inspector/InspectorInstrumentation.h"
35 #include "core/workers/WorkerGlobalScope.h"
36 #include "core/workers/WorkerThread.h"
37 #include "heap/ThreadState.h"
38 #include "platform/PlatformThreadData.h"
39 #include "platform/SharedTimer.h"
40 #include "platform/ThreadTimers.h"
41 #include "wtf/CurrentTime.h"
45 class WorkerRunLoopTask : public blink::WebThread::Task {
46 WTF_MAKE_NONCOPYABLE(WorkerRunLoopTask); WTF_MAKE_FAST_ALLOCATED;
48 static PassOwnPtr<WorkerRunLoopTask> create(const WorkerRunLoop& runLoop, PassOwnPtr<ExecutionContextTask> task)
50 return adoptPtr(new WorkerRunLoopTask(runLoop, task));
53 virtual ~WorkerRunLoopTask() { }
55 virtual void run() OVERRIDE
57 WorkerGlobalScope* workerGlobalScope = m_runLoop.context();
58 if ((!workerGlobalScope->isClosing() && !m_runLoop.terminated()) || m_task->isCleanupTask())
59 m_task->performTask(workerGlobalScope);
63 WorkerRunLoopTask(const WorkerRunLoop& runLoop, PassOwnPtr<ExecutionContextTask> task)
69 const WorkerRunLoop& m_runLoop;
70 OwnPtr<ExecutionContextTask> m_task;
73 class TickleDebuggerQueueTask FINAL : public ExecutionContextTask {
75 static PassOwnPtr<TickleDebuggerQueueTask> create(WorkerRunLoop* loop)
77 return adoptPtr(new TickleDebuggerQueueTask(loop));
79 virtual void performTask(ExecutionContext* context) OVERRIDE
81 ASSERT(context->isWorkerGlobalScope());
82 m_loop->runDebuggerTask(WorkerRunLoop::DontWaitForMessage);
86 explicit TickleDebuggerQueueTask(WorkerRunLoop* loop) : m_loop(loop) { }
88 WorkerRunLoop* m_loop;
91 class WorkerSharedTimer : public SharedTimer {
94 : m_sharedTimerFunction(0)
99 // SharedTimer interface.
100 virtual void setFiredFunction(void (*function)()) { m_sharedTimerFunction = function; }
101 virtual void setFireInterval(double interval) { m_nextFireTime = interval + currentTime(); }
102 virtual void stop() { m_nextFireTime = 0; }
104 bool isActive() { return m_sharedTimerFunction && m_nextFireTime; }
105 double fireTime() { return m_nextFireTime; }
106 void fire() { m_sharedTimerFunction(); }
109 void (*m_sharedTimerFunction)();
110 double m_nextFireTime;
113 WorkerRunLoop::WorkerRunLoop()
114 : m_sharedTimer(adoptPtr(new WorkerSharedTimer))
120 WorkerRunLoop::~WorkerRunLoop()
122 ASSERT(!m_nestedCount);
126 WTF_MAKE_NONCOPYABLE(RunLoopSetup);
128 RunLoopSetup(WorkerRunLoop& runLoop, WorkerGlobalScope* context)
132 if (!m_runLoop.m_nestedCount)
133 PlatformThreadData::current().threadTimers().setSharedTimer(m_runLoop.m_sharedTimer.get());
134 m_runLoop.m_nestedCount++;
135 InspectorInstrumentation::willEnterNestedRunLoop(m_context);
140 m_runLoop.m_nestedCount--;
141 if (!m_runLoop.m_nestedCount)
142 PlatformThreadData::current().threadTimers().setSharedTimer(0);
143 InspectorInstrumentation::didLeaveNestedRunLoop(m_context);
146 WorkerRunLoop& m_runLoop;
147 WorkerGlobalScope* m_context;
150 void WorkerRunLoop::setWorkerGlobalScope(WorkerGlobalScope* context)
157 void WorkerRunLoop::run()
160 RunLoopSetup setup(*this, m_context);
161 MessageQueueWaitResult result;
163 ThreadState::current()->safePoint(ThreadState::NoHeapPointersOnStack);
164 result = run(m_messageQueue, WaitForMessage);
165 } while (result != MessageQueueTerminated);
169 MessageQueueWaitResult WorkerRunLoop::runDebuggerTask(WaitMode waitMode)
172 RunLoopSetup setup(*this, m_context);
173 return run(m_debuggerMessageQueue, waitMode);
176 MessageQueueWaitResult WorkerRunLoop::run(MessageQueue<blink::WebThread::Task>& queue, WaitMode waitMode)
179 ASSERT(m_context->thread());
180 ASSERT(m_context->thread()->isCurrentThread());
182 bool nextTimeoutEventIsIdleWatchdog;
183 MessageQueueWaitResult result;
184 OwnPtr<blink::WebThread::Task> task;
186 double absoluteTime = 0.0;
187 nextTimeoutEventIsIdleWatchdog = false;
188 if (waitMode == WaitForMessage) {
189 absoluteTime = m_sharedTimer->isActive() ? m_sharedTimer->fireTime() : MessageQueue<blink::WebThread::Task>::infiniteTime();
191 // Do a script engine idle notification if the next event is distant enough.
192 const double kMinIdleTimespan = 0.3; // seconds
193 if (queue.isEmpty() && absoluteTime > currentTime() + kMinIdleTimespan) {
194 bool hasMoreWork = !m_context->idleNotification();
196 // Schedule a watchdog, so if there are no events within a particular time interval
197 // idle notifications won't stop firing.
198 const double kWatchdogInterval = 3; // seconds
199 double nextWatchdogTime = currentTime() + kWatchdogInterval;
200 if (absoluteTime > nextWatchdogTime) {
201 absoluteTime = nextWatchdogTime;
202 nextTimeoutEventIsIdleWatchdog = true;
209 ThreadState::SafePointScope safePointScope(ThreadState::NoHeapPointersOnStack);
210 task = queue.waitForMessageWithTimeout(result, absoluteTime);
212 } while (result == MessageQueueTimeout && nextTimeoutEventIsIdleWatchdog);
214 // If the context is closing, don't execute any further JavaScript tasks (per section 4.1.1 of the Web Workers spec).
215 // However, there may be implementation cleanup tasks in the queue, so keep running through it.
218 case MessageQueueTerminated:
221 case MessageQueueMessageReceived:
222 InspectorInstrumentation::willProcessTask(m_context);
224 InspectorInstrumentation::didProcessTask(m_context);
227 case MessageQueueTimeout:
228 if (!m_context->isClosing())
229 m_sharedTimer->fire();
236 void WorkerRunLoop::runCleanupTasks()
239 ASSERT(m_context->thread());
240 ASSERT(m_context->thread()->isCurrentThread());
241 ASSERT(m_messageQueue.killed());
242 ASSERT(m_debuggerMessageQueue.killed());
245 OwnPtr<blink::WebThread::Task> task = m_debuggerMessageQueue.tryGetMessageIgnoringKilled();
247 task = m_messageQueue.tryGetMessageIgnoringKilled();
254 void WorkerRunLoop::terminate()
256 m_messageQueue.kill();
257 m_debuggerMessageQueue.kill();
260 bool WorkerRunLoop::postTask(PassOwnPtr<ExecutionContextTask> task)
262 return m_messageQueue.append(WorkerRunLoopTask::create(*this, task));
265 void WorkerRunLoop::postTaskAndTerminate(PassOwnPtr<ExecutionContextTask> task)
267 m_debuggerMessageQueue.kill();
268 m_messageQueue.appendAndKill(WorkerRunLoopTask::create(*this, task));
271 bool WorkerRunLoop::postDebuggerTask(PassOwnPtr<ExecutionContextTask> task)
273 bool posted = m_debuggerMessageQueue.append(WorkerRunLoopTask::create(*this, task));
275 postTask(TickleDebuggerQueueTask::create(this));
279 } // namespace WebCore