1 /* 2 * Copyright (C) 2008 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 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 * 25 */ 26 27 #include "config.h" 28 29 #include "core/workers/WorkerThread.h" 30 31 #include "bindings/core/v8/ScriptSourceCode.h" 32 #include "core/dom/Microtask.h" 33 #include "core/inspector/InspectorInstrumentation.h" 34 #include "core/inspector/WorkerInspectorController.h" 35 #include "core/workers/DedicatedWorkerGlobalScope.h" 36 #include "core/workers/WorkerClients.h" 37 #include "core/workers/WorkerReportingProxy.h" 38 #include "core/workers/WorkerThreadStartupData.h" 39 #include "platform/PlatformThreadData.h" 40 #include "platform/Task.h" 41 #include "platform/ThreadTimers.h" 42 #include "platform/heap/ThreadState.h" 43 #include "platform/weborigin/KURL.h" 44 #include "public/platform/Platform.h" 45 #include "public/platform/WebThread.h" 46 #include "public/platform/WebWaitableEvent.h" 47 #include "public/platform/WebWorkerRunLoop.h" 48 #include "wtf/Noncopyable.h" 49 #include "wtf/text/WTFString.h" 50 51 #include <utility> 52 53 namespace blink { 54 55 namespace { 56 const int64 kShortIdleHandlerDelayMs = 1000; 57 const int64 kLongIdleHandlerDelayMs = 10*1000; 58 59 class MicrotaskRunner : public WebThread::TaskObserver { 60 public: 61 virtual void willProcessTask() OVERRIDE { } 62 virtual void didProcessTask() OVERRIDE 63 { 64 Microtask::performCheckpoint(); 65 } 66 }; 67 68 } // namespace 69 70 static Mutex& threadSetMutex() 71 { 72 AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); 73 return mutex; 74 } 75 76 static HashSet<WorkerThread*>& workerThreads() 77 { 78 DEFINE_STATIC_LOCAL(HashSet<WorkerThread*>, threads, ()); 79 return threads; 80 } 81 82 unsigned WorkerThread::workerThreadCount() 83 { 84 MutexLocker lock(threadSetMutex()); 85 return workerThreads().size(); 86 } 87 88 class WorkerSharedTimer : public SharedTimer { 89 public: 90 explicit WorkerSharedTimer(WorkerThread* workerThread) 91 : m_workerThread(workerThread) 92 , m_nextFireTime(0.0) 93 , m_running(false) 94 { } 95 96 typedef void (*SharedTimerFunction)(); 97 virtual void setFiredFunction(SharedTimerFunction func) 98 { 99 m_sharedTimerFunction = func; 100 if (!m_sharedTimerFunction) 101 m_nextFireTime = 0.0; 102 } 103 104 virtual void setFireInterval(double interval) 105 { 106 ASSERT(m_sharedTimerFunction); 107 108 // See BlinkPlatformImpl::setSharedTimerFireInterval for explanation of 109 // why ceil is used in the interval calculation. 110 int64 delay = static_cast<int64>(ceil(interval * 1000)); 111 112 if (delay < 0) { 113 delay = 0; 114 m_nextFireTime = 0.0; 115 } 116 117 m_running = true; 118 m_nextFireTime = currentTime() + interval; 119 m_workerThread->postDelayedTask(createSameThreadTask(&WorkerSharedTimer::OnTimeout, this), delay); 120 } 121 122 virtual void stop() 123 { 124 m_running = false; 125 } 126 127 double nextFireTime() { return m_nextFireTime; } 128 129 private: 130 void OnTimeout() 131 { 132 ASSERT(m_workerThread->workerGlobalScope()); 133 if (m_sharedTimerFunction && m_running && !m_workerThread->workerGlobalScope()->isClosing()) 134 m_sharedTimerFunction(); 135 } 136 137 WorkerThread* m_workerThread; 138 SharedTimerFunction m_sharedTimerFunction; 139 double m_nextFireTime; 140 bool m_running; 141 }; 142 143 class WorkerThreadTask : public blink::WebThread::Task { 144 WTF_MAKE_NONCOPYABLE(WorkerThreadTask); WTF_MAKE_FAST_ALLOCATED; 145 public: 146 static PassOwnPtr<WorkerThreadTask> create(const WorkerThread& workerThread, PassOwnPtr<ExecutionContextTask> task, bool isInstrumented) 147 { 148 return adoptPtr(new WorkerThreadTask(workerThread, task, isInstrumented)); 149 } 150 151 virtual ~WorkerThreadTask() { } 152 153 virtual void run() OVERRIDE 154 { 155 WorkerGlobalScope* workerGlobalScope = m_workerThread.workerGlobalScope(); 156 // Tasks could be put on the message loop after the cleanup task, 157 // ensure none of those are ran. 158 if (!workerGlobalScope) 159 return; 160 161 if (m_isInstrumented) 162 InspectorInstrumentation::willPerformExecutionContextTask(workerGlobalScope, m_task.get()); 163 if ((!workerGlobalScope->isClosing() && !m_workerThread.terminated()) || m_task->isCleanupTask()) 164 m_task->performTask(workerGlobalScope); 165 if (m_isInstrumented) 166 InspectorInstrumentation::didPerformExecutionContextTask(workerGlobalScope); 167 } 168 169 private: 170 WorkerThreadTask(const WorkerThread& workerThread, PassOwnPtr<ExecutionContextTask> task, bool isInstrumented) 171 : m_workerThread(workerThread) 172 , m_task(task) 173 , m_isInstrumented(isInstrumented) 174 { 175 if (m_isInstrumented) 176 m_isInstrumented = !m_task->taskNameForInstrumentation().isEmpty(); 177 if (m_isInstrumented) 178 InspectorInstrumentation::didPostExecutionContextTask(m_workerThread.workerGlobalScope(), m_task.get()); 179 } 180 181 const WorkerThread& m_workerThread; 182 OwnPtr<ExecutionContextTask> m_task; 183 bool m_isInstrumented; 184 }; 185 186 class RunDebuggerQueueTask FINAL : public ExecutionContextTask { 187 public: 188 static PassOwnPtr<RunDebuggerQueueTask> create(WorkerThread* thread) 189 { 190 return adoptPtr(new RunDebuggerQueueTask(thread)); 191 } 192 virtual void performTask(ExecutionContext* context) OVERRIDE 193 { 194 ASSERT(context->isWorkerGlobalScope()); 195 m_thread->runDebuggerTask(WorkerThread::DontWaitForMessage); 196 } 197 198 private: 199 explicit RunDebuggerQueueTask(WorkerThread* thread) : m_thread(thread) { } 200 201 WorkerThread* m_thread; 202 }; 203 204 WorkerThread::WorkerThread(WorkerLoaderProxy& workerLoaderProxy, WorkerReportingProxy& workerReportingProxy, PassOwnPtrWillBeRawPtr<WorkerThreadStartupData> startupData) 205 : m_terminated(false) 206 , m_workerLoaderProxy(workerLoaderProxy) 207 , m_workerReportingProxy(workerReportingProxy) 208 , m_startupData(startupData) 209 , m_shutdownEvent(adoptPtr(blink::Platform::current()->createWaitableEvent())) 210 , m_terminationEvent(adoptPtr(blink::Platform::current()->createWaitableEvent())) 211 { 212 MutexLocker lock(threadSetMutex()); 213 workerThreads().add(this); 214 } 215 216 WorkerThread::~WorkerThread() 217 { 218 MutexLocker lock(threadSetMutex()); 219 ASSERT(workerThreads().contains(this)); 220 workerThreads().remove(this); 221 } 222 223 void WorkerThread::start() 224 { 225 if (m_thread) 226 return; 227 228 m_thread = WebThreadSupportingGC::create("WebCore: Worker"); 229 m_thread->postTask(new Task(WTF::bind(&WorkerThread::initialize, this))); 230 } 231 232 void WorkerThread::interruptAndDispatchInspectorCommands() 233 { 234 MutexLocker locker(m_workerInspectorControllerMutex); 235 if (m_workerInspectorController) 236 m_workerInspectorController->interruptAndDispatchInspectorCommands(); 237 } 238 239 PlatformThreadId WorkerThread::platformThreadId() const 240 { 241 if (!m_thread) 242 return 0; 243 return m_thread->platformThread().threadId(); 244 } 245 246 void WorkerThread::initialize() 247 { 248 KURL scriptURL = m_startupData->m_scriptURL; 249 String sourceCode = m_startupData->m_sourceCode; 250 WorkerThreadStartMode startMode = m_startupData->m_startMode; 251 252 { 253 MutexLocker lock(m_threadCreationMutex); 254 255 // The worker was terminated before the thread had a chance to run. 256 if (m_terminated) { 257 // Notify the proxy that the WorkerGlobalScope has been disposed of. 258 // This can free this thread object, hence it must not be touched afterwards. 259 m_workerReportingProxy.workerThreadTerminated(); 260 return; 261 } 262 263 m_microtaskRunner = adoptPtr(new MicrotaskRunner); 264 m_thread->addTaskObserver(m_microtaskRunner.get()); 265 m_thread->attachGC(); 266 m_workerGlobalScope = createWorkerGlobalScope(m_startupData.release()); 267 268 m_sharedTimer = adoptPtr(new WorkerSharedTimer(this)); 269 PlatformThreadData::current().threadTimers().setSharedTimer(m_sharedTimer.get()); 270 } 271 272 // The corresponding call to didStopWorkerRunLoop is in 273 // ~WorkerScriptController. 274 blink::Platform::current()->didStartWorkerRunLoop(blink::WebWorkerRunLoop(this)); 275 276 // Notify proxy that a new WorkerGlobalScope has been created and started. 277 m_workerReportingProxy.workerGlobalScopeStarted(m_workerGlobalScope.get()); 278 279 WorkerScriptController* script = m_workerGlobalScope->script(); 280 if (!script->isExecutionForbidden()) 281 script->initializeContextIfNeeded(); 282 InspectorInstrumentation::willEvaluateWorkerScript(workerGlobalScope(), startMode); 283 script->evaluate(ScriptSourceCode(sourceCode, scriptURL)); 284 285 postInitialize(); 286 287 postDelayedTask(createSameThreadTask(&WorkerThread::idleHandler, this), kShortIdleHandlerDelayMs); 288 } 289 290 void WorkerThread::cleanup() 291 { 292 293 // This should be called before we start the shutdown procedure. 294 workerReportingProxy().willDestroyWorkerGlobalScope(); 295 296 // The below assignment will destroy the context, which will in turn notify messaging proxy. 297 // We cannot let any objects survive past thread exit, because no other thread will run GC or otherwise destroy them. 298 // If Oilpan is enabled, we detach of the context/global scope, with the final heap cleanup below sweeping it out. 299 #if !ENABLE(OILPAN) 300 ASSERT(m_workerGlobalScope->hasOneRef()); 301 #endif 302 m_workerGlobalScope->dispose(); 303 m_workerGlobalScope = nullptr; 304 305 m_thread->detachGC(); 306 307 m_thread->removeTaskObserver(m_microtaskRunner.get()); 308 m_microtaskRunner = nullptr; 309 310 // Notify the proxy that the WorkerGlobalScope has been disposed of. 311 // This can free this thread object, hence it must not be touched afterwards. 312 workerReportingProxy().workerThreadTerminated(); 313 314 m_terminationEvent->signal(); 315 316 // Clean up PlatformThreadData before WTF::WTFThreadData goes away! 317 PlatformThreadData::current().destroy(); 318 } 319 320 class WorkerThreadShutdownFinishTask : public ExecutionContextTask { 321 public: 322 static PassOwnPtr<WorkerThreadShutdownFinishTask> create() 323 { 324 return adoptPtr(new WorkerThreadShutdownFinishTask()); 325 } 326 327 virtual void performTask(ExecutionContext *context) 328 { 329 WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); 330 workerGlobalScope->clearInspector(); 331 // It's not safe to call clearScript until all the cleanup tasks posted by functions initiated by WorkerThreadShutdownStartTask have completed. 332 workerGlobalScope->clearScript(); 333 workerGlobalScope->thread()->m_thread->postTask(new Task(WTF::bind(&WorkerThread::cleanup, workerGlobalScope->thread()))); 334 } 335 336 virtual bool isCleanupTask() const { return true; } 337 }; 338 339 class WorkerThreadShutdownStartTask : public ExecutionContextTask { 340 public: 341 static PassOwnPtr<WorkerThreadShutdownStartTask> create() 342 { 343 return adoptPtr(new WorkerThreadShutdownStartTask()); 344 } 345 346 virtual void performTask(ExecutionContext *context) 347 { 348 WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); 349 workerGlobalScope->stopFetch(); 350 workerGlobalScope->stopActiveDOMObjects(); 351 PlatformThreadData::current().threadTimers().setSharedTimer(nullptr); 352 353 // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects, 354 // which become dangling once Heap is destroyed. 355 workerGlobalScope->removeAllEventListeners(); 356 357 // Stick a shutdown command at the end of the queue, so that we deal 358 // with all the cleanup tasks the databases post first. 359 workerGlobalScope->postTask(WorkerThreadShutdownFinishTask::create()); 360 } 361 362 virtual bool isCleanupTask() const { return true; } 363 }; 364 365 void WorkerThread::stop() 366 { 367 // Prevent the deadlock between GC and an attempt to stop a thread. 368 ThreadState::SafePointScope safePointScope(ThreadState::HeapPointersOnStack); 369 stopInternal(); 370 } 371 372 void WorkerThread::stopInShutdownSequence() 373 { 374 stopInternal(); 375 } 376 377 void WorkerThread::stopInternal() 378 { 379 // Protect against this method and initialize() racing each other. 380 MutexLocker lock(m_threadCreationMutex); 381 382 // If stop has already been called, just return. 383 if (m_terminated) 384 return; 385 m_terminated = true; 386 387 // Signal the thread to notify that the thread's stopping. 388 if (m_shutdownEvent) 389 m_shutdownEvent->signal(); 390 391 if (!m_workerGlobalScope) 392 return; 393 394 // Ensure that tasks are being handled by thread event loop. If script execution weren't forbidden, a while(1) loop in JS could keep the thread alive forever. 395 m_workerGlobalScope->script()->scheduleExecutionTermination(); 396 InspectorInstrumentation::didKillAllExecutionContextTasks(m_workerGlobalScope.get()); 397 m_debuggerMessageQueue.kill(); 398 postTask(WorkerThreadShutdownStartTask::create()); 399 } 400 401 void WorkerThread::terminateAndWaitForAllWorkers() 402 { 403 // Keep this lock to prevent WorkerThread instances from being destroyed. 404 MutexLocker lock(threadSetMutex()); 405 HashSet<WorkerThread*> threads = workerThreads(); 406 for (HashSet<WorkerThread*>::iterator itr = threads.begin(); itr != threads.end(); ++itr) 407 (*itr)->stopInShutdownSequence(); 408 409 for (HashSet<WorkerThread*>::iterator itr = threads.begin(); itr != threads.end(); ++itr) 410 (*itr)->terminationEvent()->wait(); 411 } 412 413 bool WorkerThread::isCurrentThread() const 414 { 415 return m_thread && m_thread->isCurrentThread(); 416 } 417 418 void WorkerThread::idleHandler() 419 { 420 ASSERT(m_workerGlobalScope.get()); 421 int64 delay = kLongIdleHandlerDelayMs; 422 423 // Do a script engine idle notification if the next event is distant enough. 424 const double kMinIdleTimespan = 0.3; 425 if (m_sharedTimer->nextFireTime() == 0.0 || m_sharedTimer->nextFireTime() > currentTime() + kMinIdleTimespan) { 426 bool hasMoreWork = !m_workerGlobalScope->idleNotification(); 427 if (hasMoreWork) 428 delay = kShortIdleHandlerDelayMs; 429 } 430 431 postDelayedTask(createSameThreadTask(&WorkerThread::idleHandler, this), delay); 432 } 433 434 void WorkerThread::postTask(PassOwnPtr<ExecutionContextTask> task) 435 { 436 m_thread->postTask(WorkerThreadTask::create(*this, task, true).leakPtr()); 437 } 438 439 void WorkerThread::postDelayedTask(PassOwnPtr<ExecutionContextTask> task, long long delayMs) 440 { 441 m_thread->postDelayedTask(WorkerThreadTask::create(*this, task, true).leakPtr(), delayMs); 442 } 443 444 void WorkerThread::postDebuggerTask(PassOwnPtr<ExecutionContextTask> task) 445 { 446 m_debuggerMessageQueue.append(WorkerThreadTask::create(*this, task, false)); 447 postTask(RunDebuggerQueueTask::create(this)); 448 } 449 450 MessageQueueWaitResult WorkerThread::runDebuggerTask(WaitMode waitMode) 451 { 452 ASSERT(isCurrentThread()); 453 MessageQueueWaitResult result; 454 double absoluteTime = MessageQueue<blink::WebThread::Task>::infiniteTime(); 455 OwnPtr<blink::WebThread::Task> task; 456 { 457 if (waitMode == DontWaitForMessage) 458 absoluteTime = 0.0; 459 ThreadState::SafePointScope safePointScope(ThreadState::NoHeapPointersOnStack); 460 task = m_debuggerMessageQueue.waitForMessageWithTimeout(result, absoluteTime); 461 } 462 463 if (result == MessageQueueMessageReceived) { 464 InspectorInstrumentation::willProcessTask(workerGlobalScope()); 465 task->run(); 466 InspectorInstrumentation::didProcessTask(workerGlobalScope()); 467 } 468 469 return result; 470 } 471 472 void WorkerThread::willEnterNestedLoop() 473 { 474 InspectorInstrumentation::willEnterNestedRunLoop(m_workerGlobalScope.get()); 475 } 476 477 void WorkerThread::didLeaveNestedLoop() 478 { 479 InspectorInstrumentation::didLeaveNestedRunLoop(m_workerGlobalScope.get()); 480 } 481 482 void WorkerThread::setWorkerInspectorController(WorkerInspectorController* workerInspectorController) 483 { 484 MutexLocker locker(m_workerInspectorControllerMutex); 485 m_workerInspectorController = workerInspectorController; 486 } 487 488 } // namespace blink 489