1 /* 2 * Copyright (C) 2009 Google 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 are 6 * met: 7 * 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 13 * distribution. 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. 17 * 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. 29 */ 30 31 #include "config.h" 32 #include "core/workers/WorkerRunLoop.h" 33 34 #include "core/inspector/InspectorInstrumentation.h" 35 #include "core/workers/WorkerGlobalScope.h" 36 #include "core/workers/WorkerThread.h" 37 #include "platform/PlatformThreadData.h" 38 #include "platform/SharedTimer.h" 39 #include "platform/ThreadTimers.h" 40 #include "platform/heap/ThreadState.h" 41 #include "wtf/CurrentTime.h" 42 43 namespace WebCore { 44 45 class WorkerRunLoopTask : public blink::WebThread::Task { 46 WTF_MAKE_NONCOPYABLE(WorkerRunLoopTask); WTF_MAKE_FAST_ALLOCATED; 47 public: 48 static PassOwnPtr<WorkerRunLoopTask> create(const WorkerRunLoop& runLoop, PassOwnPtr<ExecutionContextTask> task) 49 { 50 return adoptPtr(new WorkerRunLoopTask(runLoop, task)); 51 } 52 53 virtual ~WorkerRunLoopTask() { } 54 55 virtual void run() OVERRIDE 56 { 57 WorkerGlobalScope* workerGlobalScope = m_runLoop.context(); 58 if ((!workerGlobalScope->isClosing() && !m_runLoop.terminated()) || m_task->isCleanupTask()) 59 m_task->performTask(workerGlobalScope); 60 } 61 62 private: 63 WorkerRunLoopTask(const WorkerRunLoop& runLoop, PassOwnPtr<ExecutionContextTask> task) 64 : m_runLoop(runLoop) 65 , m_task(task) 66 { 67 } 68 69 const WorkerRunLoop& m_runLoop; 70 OwnPtr<ExecutionContextTask> m_task; 71 }; 72 73 class TickleDebuggerQueueTask FINAL : public ExecutionContextTask { 74 public: 75 static PassOwnPtr<TickleDebuggerQueueTask> create(WorkerRunLoop* loop) 76 { 77 return adoptPtr(new TickleDebuggerQueueTask(loop)); 78 } 79 virtual void performTask(ExecutionContext* context) OVERRIDE 80 { 81 ASSERT(context->isWorkerGlobalScope()); 82 m_loop->runDebuggerTask(WorkerRunLoop::DontWaitForMessage); 83 } 84 85 private: 86 explicit TickleDebuggerQueueTask(WorkerRunLoop* loop) : m_loop(loop) { } 87 88 WorkerRunLoop* m_loop; 89 }; 90 91 class WorkerSharedTimer : public SharedTimer { 92 public: 93 WorkerSharedTimer() 94 : m_sharedTimerFunction(0) 95 , m_nextFireTime(0) 96 { 97 } 98 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; } 103 104 bool isActive() { return m_sharedTimerFunction && m_nextFireTime; } 105 double fireTime() { return m_nextFireTime; } 106 void fire() { m_sharedTimerFunction(); } 107 108 private: 109 void (*m_sharedTimerFunction)(); 110 double m_nextFireTime; 111 }; 112 113 WorkerRunLoop::WorkerRunLoop() 114 : m_sharedTimer(adoptPtr(new WorkerSharedTimer)) 115 , m_context(0) 116 , m_nestedCount(0) 117 { 118 } 119 120 WorkerRunLoop::~WorkerRunLoop() 121 { 122 ASSERT(!m_nestedCount); 123 } 124 125 class RunLoopSetup { 126 WTF_MAKE_NONCOPYABLE(RunLoopSetup); 127 public: 128 RunLoopSetup(WorkerRunLoop& runLoop, WorkerGlobalScope* context) 129 : m_runLoop(runLoop) 130 , m_context(context) 131 { 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); 136 } 137 138 ~RunLoopSetup() 139 { 140 m_runLoop.m_nestedCount--; 141 if (!m_runLoop.m_nestedCount) 142 PlatformThreadData::current().threadTimers().setSharedTimer(0); 143 InspectorInstrumentation::didLeaveNestedRunLoop(m_context); 144 } 145 private: 146 WorkerRunLoop& m_runLoop; 147 WorkerGlobalScope* m_context; 148 }; 149 150 void WorkerRunLoop::setWorkerGlobalScope(WorkerGlobalScope* context) 151 { 152 ASSERT(!m_context); 153 ASSERT(context); 154 m_context = context; 155 } 156 157 void WorkerRunLoop::run() 158 { 159 ASSERT(m_context); 160 RunLoopSetup setup(*this, m_context); 161 MessageQueueWaitResult result; 162 do { 163 ThreadState::current()->safePoint(ThreadState::NoHeapPointersOnStack); 164 result = run(m_messageQueue, WaitForMessage); 165 } while (result != MessageQueueTerminated); 166 runCleanupTasks(); 167 } 168 169 MessageQueueWaitResult WorkerRunLoop::runDebuggerTask(WaitMode waitMode) 170 { 171 ASSERT(m_context); 172 RunLoopSetup setup(*this, m_context); 173 return run(m_debuggerMessageQueue, waitMode); 174 } 175 176 MessageQueueWaitResult WorkerRunLoop::run(MessageQueue<blink::WebThread::Task>& queue, WaitMode waitMode) 177 { 178 ASSERT(m_context); 179 ASSERT(m_context->thread()); 180 ASSERT(m_context->thread()->isCurrentThread()); 181 182 bool isDebuggerQueue = (&queue == &m_debuggerMessageQueue); 183 bool nextTimeoutEventIsIdleWatchdog; 184 MessageQueueWaitResult result; 185 OwnPtr<blink::WebThread::Task> task; 186 do { 187 double absoluteTime = 0.0; 188 nextTimeoutEventIsIdleWatchdog = false; 189 if (waitMode == WaitForMessage) { 190 absoluteTime = !isDebuggerQueue && m_sharedTimer->isActive() ? m_sharedTimer->fireTime() : MessageQueue<blink::WebThread::Task>::infiniteTime(); 191 192 // Do a script engine idle notification if the next event is distant enough. 193 const double kMinIdleTimespan = 0.3; // seconds 194 if (queue.isEmpty() && absoluteTime > currentTime() + kMinIdleTimespan) { 195 bool hasMoreWork = !m_context->idleNotification(); 196 if (hasMoreWork) { 197 // Schedule a watchdog, so if there are no events within a particular time interval 198 // idle notifications won't stop firing. 199 const double kWatchdogInterval = 3; // seconds 200 double nextWatchdogTime = currentTime() + kWatchdogInterval; 201 if (absoluteTime > nextWatchdogTime) { 202 absoluteTime = nextWatchdogTime; 203 nextTimeoutEventIsIdleWatchdog = true; 204 } 205 } 206 } 207 } 208 209 { 210 ThreadState::SafePointScope safePointScope(ThreadState::NoHeapPointersOnStack); 211 task = queue.waitForMessageWithTimeout(result, absoluteTime); 212 } 213 } while (result == MessageQueueTimeout && nextTimeoutEventIsIdleWatchdog); 214 215 // If the context is closing, don't execute any further JavaScript tasks (per section 4.1.1 of the Web Workers spec). 216 // However, there may be implementation cleanup tasks in the queue, so keep running through it. 217 218 switch (result) { 219 case MessageQueueTerminated: 220 break; 221 222 case MessageQueueMessageReceived: 223 InspectorInstrumentation::willProcessTask(m_context); 224 task->run(); 225 InspectorInstrumentation::didProcessTask(m_context); 226 break; 227 228 case MessageQueueTimeout: 229 ASSERT(!isDebuggerQueue || waitMode != WaitForMessage); 230 if (!m_context->isClosing()) 231 m_sharedTimer->fire(); 232 break; 233 } 234 235 return result; 236 } 237 238 void WorkerRunLoop::runCleanupTasks() 239 { 240 ASSERT(m_context); 241 ASSERT(m_context->thread()); 242 ASSERT(m_context->thread()->isCurrentThread()); 243 ASSERT(m_messageQueue.killed()); 244 ASSERT(m_debuggerMessageQueue.killed()); 245 246 while (true) { 247 OwnPtr<blink::WebThread::Task> task = m_debuggerMessageQueue.tryGetMessageIgnoringKilled(); 248 if (!task) 249 task = m_messageQueue.tryGetMessageIgnoringKilled(); 250 if (!task) 251 return; 252 task->run(); 253 } 254 } 255 256 void WorkerRunLoop::terminate() 257 { 258 m_messageQueue.kill(); 259 m_debuggerMessageQueue.kill(); 260 } 261 262 bool WorkerRunLoop::postTask(PassOwnPtr<ExecutionContextTask> task) 263 { 264 return m_messageQueue.append(WorkerRunLoopTask::create(*this, task)); 265 } 266 267 void WorkerRunLoop::postTaskAndTerminate(PassOwnPtr<ExecutionContextTask> task) 268 { 269 m_debuggerMessageQueue.kill(); 270 m_messageQueue.appendAndKill(WorkerRunLoopTask::create(*this, task)); 271 } 272 273 bool WorkerRunLoop::postDebuggerTask(PassOwnPtr<ExecutionContextTask> task) 274 { 275 bool posted = m_debuggerMessageQueue.append(WorkerRunLoopTask::create(*this, task)); 276 if (posted) 277 postTask(TickleDebuggerQueueTask::create(this)); 278 return posted; 279 } 280 281 } // namespace WebCore 282