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 "wtf/CurrentTime.h" 41 42 namespace WebCore { 43 44 class WorkerRunLoop::Task { 45 WTF_MAKE_NONCOPYABLE(Task); WTF_MAKE_FAST_ALLOCATED; 46 public: 47 static PassOwnPtr<Task> create(PassOwnPtr<ExecutionContextTask> task, const String& mode) 48 { 49 return adoptPtr(new Task(task, mode)); 50 } 51 const String& mode() const { return m_mode; } 52 void performTask(const WorkerRunLoop& runLoop, ExecutionContext* context) 53 { 54 WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); 55 if ((!workerGlobalScope->isClosing() && !runLoop.terminated()) || m_task->isCleanupTask()) 56 m_task->performTask(context); 57 } 58 59 private: 60 Task(PassOwnPtr<ExecutionContextTask> task, const String& mode) 61 : m_task(task) 62 , m_mode(mode.isolatedCopy()) 63 { 64 } 65 66 OwnPtr<ExecutionContextTask> m_task; 67 String m_mode; 68 }; 69 70 class WorkerSharedTimer : public SharedTimer { 71 public: 72 WorkerSharedTimer() 73 : m_sharedTimerFunction(0) 74 , m_nextFireTime(0) 75 { 76 } 77 78 // SharedTimer interface. 79 virtual void setFiredFunction(void (*function)()) { m_sharedTimerFunction = function; } 80 virtual void setFireInterval(double interval) { m_nextFireTime = interval + currentTime(); } 81 virtual void stop() { m_nextFireTime = 0; } 82 83 bool isActive() { return m_sharedTimerFunction && m_nextFireTime; } 84 double fireTime() { return m_nextFireTime; } 85 void fire() { m_sharedTimerFunction(); } 86 87 private: 88 void (*m_sharedTimerFunction)(); 89 double m_nextFireTime; 90 }; 91 92 class ModePredicate { 93 public: 94 ModePredicate(const String& mode) 95 : m_mode(mode) 96 , m_defaultMode(mode == WorkerRunLoop::defaultMode()) 97 { 98 } 99 100 bool isDefaultMode() const 101 { 102 return m_defaultMode; 103 } 104 105 bool operator()(WorkerRunLoop::Task* task) const 106 { 107 return m_defaultMode || m_mode == task->mode(); 108 } 109 110 private: 111 String m_mode; 112 bool m_defaultMode; 113 }; 114 115 WorkerRunLoop::WorkerRunLoop() 116 : m_sharedTimer(adoptPtr(new WorkerSharedTimer)) 117 , m_nestedCount(0) 118 , m_uniqueId(0) 119 { 120 } 121 122 WorkerRunLoop::~WorkerRunLoop() 123 { 124 ASSERT(!m_nestedCount); 125 } 126 127 String WorkerRunLoop::defaultMode() 128 { 129 return String(); 130 } 131 132 class RunLoopSetup { 133 WTF_MAKE_NONCOPYABLE(RunLoopSetup); 134 public: 135 RunLoopSetup(WorkerRunLoop& runLoop, WorkerGlobalScope* context) 136 : m_runLoop(runLoop) 137 , m_context(context) 138 { 139 if (!m_runLoop.m_nestedCount) 140 PlatformThreadData::current().threadTimers().setSharedTimer(m_runLoop.m_sharedTimer.get()); 141 m_runLoop.m_nestedCount++; 142 InspectorInstrumentation::willEnterNestedRunLoop(m_context); 143 } 144 145 ~RunLoopSetup() 146 { 147 m_runLoop.m_nestedCount--; 148 if (!m_runLoop.m_nestedCount) 149 PlatformThreadData::current().threadTimers().setSharedTimer(0); 150 InspectorInstrumentation::didLeaveNestedRunLoop(m_context); 151 } 152 private: 153 WorkerRunLoop& m_runLoop; 154 WorkerGlobalScope* m_context; 155 }; 156 157 void WorkerRunLoop::run(WorkerGlobalScope* context) 158 { 159 RunLoopSetup setup(*this, context); 160 ModePredicate modePredicate(defaultMode()); 161 MessageQueueWaitResult result; 162 do { 163 result = runInMode(context, modePredicate, WaitForMessage); 164 } while (result != MessageQueueTerminated); 165 runCleanupTasks(context); 166 } 167 168 MessageQueueWaitResult WorkerRunLoop::runInMode(WorkerGlobalScope* context, const String& mode, WaitMode waitMode) 169 { 170 RunLoopSetup setup(*this, context); 171 ModePredicate modePredicate(mode); 172 MessageQueueWaitResult result = runInMode(context, modePredicate, waitMode); 173 return result; 174 } 175 176 MessageQueueWaitResult WorkerRunLoop::runInMode(WorkerGlobalScope* context, const ModePredicate& predicate, WaitMode waitMode) 177 { 178 ASSERT(context); 179 ASSERT(context->thread()); 180 ASSERT(context->thread()->isCurrentThread()); 181 182 bool nextTimeoutEventIsIdleWatchdog; 183 MessageQueueWaitResult result; 184 OwnPtr<WorkerRunLoop::Task> task; 185 do { 186 double absoluteTime = 0.0; 187 nextTimeoutEventIsIdleWatchdog = false; 188 if (waitMode == WaitForMessage) { 189 absoluteTime = (predicate.isDefaultMode() && m_sharedTimer->isActive()) ? m_sharedTimer->fireTime() : MessageQueue<Task>::infiniteTime(); 190 191 // Do a script engine idle notification if the next event is distant enough. 192 const double kMinIdleTimespan = 0.3; // seconds 193 if (m_messageQueue.isEmpty() && absoluteTime > currentTime() + kMinIdleTimespan) { 194 bool hasMoreWork = !context->idleNotification(); 195 if (hasMoreWork) { 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; 203 } 204 } 205 } 206 } 207 task = m_messageQueue.waitForMessageFilteredWithTimeout(result, predicate, absoluteTime); 208 } while (result == MessageQueueTimeout && nextTimeoutEventIsIdleWatchdog); 209 210 // If the context is closing, don't execute any further JavaScript tasks (per section 4.1.1 of the Web Workers spec). 211 // However, there may be implementation cleanup tasks in the queue, so keep running through it. 212 213 switch (result) { 214 case MessageQueueTerminated: 215 break; 216 217 case MessageQueueMessageReceived: 218 InspectorInstrumentation::willProcessTask(context); 219 task->performTask(*this, context); 220 InspectorInstrumentation::didProcessTask(context); 221 break; 222 223 case MessageQueueTimeout: 224 if (!context->isClosing()) 225 m_sharedTimer->fire(); 226 break; 227 } 228 229 return result; 230 } 231 232 void WorkerRunLoop::runCleanupTasks(WorkerGlobalScope* context) 233 { 234 ASSERT(context); 235 ASSERT(context->thread()); 236 ASSERT(context->thread()->isCurrentThread()); 237 ASSERT(m_messageQueue.killed()); 238 239 while (true) { 240 OwnPtr<WorkerRunLoop::Task> task = m_messageQueue.tryGetMessageIgnoringKilled(); 241 if (!task) 242 return; 243 task->performTask(*this, context); 244 } 245 } 246 247 void WorkerRunLoop::terminate() 248 { 249 m_messageQueue.kill(); 250 } 251 252 bool WorkerRunLoop::postTask(PassOwnPtr<ExecutionContextTask> task) 253 { 254 return postTaskForMode(task, defaultMode()); 255 } 256 257 bool WorkerRunLoop::postTask(const Closure& closure) 258 { 259 return postTask(CallClosureTask::create(closure)); 260 } 261 262 void WorkerRunLoop::postTaskAndTerminate(PassOwnPtr<ExecutionContextTask> task) 263 { 264 m_messageQueue.appendAndKill(Task::create(task, defaultMode().isolatedCopy())); 265 } 266 267 bool WorkerRunLoop::postTaskForMode(PassOwnPtr<ExecutionContextTask> task, const String& mode) 268 { 269 return m_messageQueue.append(Task::create(task, mode.isolatedCopy())); 270 } 271 272 bool WorkerRunLoop::postTaskForMode(const Closure& closure, const String& mode) 273 { 274 return postTaskForMode(CallClosureTask::create(closure), mode); 275 } 276 277 } // namespace WebCore 278