| OLD | NEW |
| (Empty) |
| 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 "bindings/core/v8/V8GCController.h" | |
| 33 #include "bindings/core/v8/V8Initializer.h" | |
| 34 #include "core/dom/Microtask.h" | |
| 35 #include "core/inspector/InspectorInstrumentation.h" | |
| 36 #include "core/inspector/WorkerInspectorController.h" | |
| 37 #include "core/workers/DedicatedWorkerGlobalScope.h" | |
| 38 #include "core/workers/WorkerClients.h" | |
| 39 #include "core/workers/WorkerReportingProxy.h" | |
| 40 #include "core/workers/WorkerThreadStartupData.h" | |
| 41 #include "platform/PlatformThreadData.h" | |
| 42 #include "platform/Task.h" | |
| 43 #include "platform/ThreadSafeFunctional.h" | |
| 44 #include "platform/ThreadTimers.h" | |
| 45 #include "platform/heap/SafePoint.h" | |
| 46 #include "platform/heap/ThreadState.h" | |
| 47 #include "platform/weborigin/KURL.h" | |
| 48 #include "public/platform/Platform.h" | |
| 49 #include "public/platform/WebThread.h" | |
| 50 #include "public/platform/WebWaitableEvent.h" | |
| 51 #include "wtf/Noncopyable.h" | |
| 52 #include "wtf/WeakPtr.h" | |
| 53 #include "wtf/text/WTFString.h" | |
| 54 | |
| 55 namespace blink { | |
| 56 | |
| 57 namespace { | |
| 58 const int64_t kShortIdleHandlerDelayMs = 1000; | |
| 59 const int64_t kLongIdleHandlerDelayMs = 10*1000; | |
| 60 | |
| 61 class MicrotaskRunner : public WebThread::TaskObserver { | |
| 62 public: | |
| 63 explicit MicrotaskRunner(WorkerThread* workerThread) | |
| 64 : m_workerThread(workerThread) | |
| 65 { | |
| 66 } | |
| 67 | |
| 68 virtual void willProcessTask() override { } | |
| 69 virtual void didProcessTask() override | |
| 70 { | |
| 71 Microtask::performCheckpoint(); | |
| 72 if (WorkerGlobalScope* globalScope = m_workerThread->workerGlobalScope()
) { | |
| 73 if (WorkerScriptController* scriptController = globalScope->script()
) | |
| 74 scriptController->rejectedPromises()->processQueue(); | |
| 75 } | |
| 76 } | |
| 77 | |
| 78 private: | |
| 79 // Thread owns the microtask runner; reference remains | |
| 80 // valid for the lifetime of this object. | |
| 81 WorkerThread* m_workerThread; | |
| 82 }; | |
| 83 | |
| 84 } // namespace | |
| 85 | |
| 86 static Mutex& threadSetMutex() | |
| 87 { | |
| 88 AtomicallyInitializedStaticReference(Mutex, mutex, new Mutex); | |
| 89 return mutex; | |
| 90 } | |
| 91 | |
| 92 static HashSet<WorkerThread*>& workerThreads() | |
| 93 { | |
| 94 DEFINE_STATIC_LOCAL(HashSet<WorkerThread*>, threads, ()); | |
| 95 return threads; | |
| 96 } | |
| 97 | |
| 98 unsigned WorkerThread::workerThreadCount() | |
| 99 { | |
| 100 MutexLocker lock(threadSetMutex()); | |
| 101 return workerThreads().size(); | |
| 102 } | |
| 103 | |
| 104 class WorkerThreadCancelableTask final : public ExecutionContextTask { | |
| 105 WTF_MAKE_NONCOPYABLE(WorkerThreadCancelableTask); WTF_MAKE_FAST_ALLOCATED(Wo
rkerThreadCancelableTask); | |
| 106 public: | |
| 107 static PassOwnPtr<WorkerThreadCancelableTask> create(PassOwnPtr<Closure> clo
sure) | |
| 108 { | |
| 109 return adoptPtr(new WorkerThreadCancelableTask(closure)); | |
| 110 } | |
| 111 | |
| 112 virtual ~WorkerThreadCancelableTask() { } | |
| 113 | |
| 114 virtual void performTask(ExecutionContext*) override | |
| 115 { | |
| 116 if (!m_taskCanceled) | |
| 117 (*m_closure)(); | |
| 118 } | |
| 119 | |
| 120 WeakPtr<WorkerThreadCancelableTask> createWeakPtr() { return m_weakFactory.c
reateWeakPtr(); } | |
| 121 void cancelTask() { m_taskCanceled = true; } | |
| 122 | |
| 123 private: | |
| 124 explicit WorkerThreadCancelableTask(PassOwnPtr<Closure> closure) | |
| 125 : m_closure(closure) | |
| 126 , m_weakFactory(this) | |
| 127 , m_taskCanceled(false) | |
| 128 { } | |
| 129 | |
| 130 OwnPtr<Closure> m_closure; | |
| 131 WeakPtrFactory<WorkerThreadCancelableTask> m_weakFactory; | |
| 132 bool m_taskCanceled; | |
| 133 }; | |
| 134 | |
| 135 class WorkerSharedTimer : public SharedTimer { | |
| 136 public: | |
| 137 explicit WorkerSharedTimer(WorkerThread* workerThread) | |
| 138 : m_workerThread(workerThread) | |
| 139 , m_running(false) | |
| 140 { } | |
| 141 | |
| 142 typedef void (*SharedTimerFunction)(); | |
| 143 virtual void setFiredFunction(SharedTimerFunction func) | |
| 144 { | |
| 145 m_sharedTimerFunction = func; | |
| 146 } | |
| 147 | |
| 148 virtual void setFireInterval(double interval) | |
| 149 { | |
| 150 ASSERT(m_sharedTimerFunction); | |
| 151 | |
| 152 // See BlinkPlatformImpl::setSharedTimerFireInterval for explanation of | |
| 153 // why ceil is used in the interval calculation. | |
| 154 int64_t delay = static_cast<int64_t>(ceil(interval * 1000)); | |
| 155 | |
| 156 if (delay < 0) { | |
| 157 delay = 0; | |
| 158 } | |
| 159 | |
| 160 m_running = true; | |
| 161 | |
| 162 if (m_lastQueuedTask.get()) | |
| 163 m_lastQueuedTask->cancelTask(); | |
| 164 | |
| 165 // Now queue the task as a cancellable one. | |
| 166 OwnPtr<WorkerThreadCancelableTask> task = WorkerThreadCancelableTask::cr
eate(bind(&WorkerSharedTimer::OnTimeout, this)); | |
| 167 m_lastQueuedTask = task->createWeakPtr(); | |
| 168 m_workerThread->postDelayedTask(FROM_HERE, task.release(), delay); | |
| 169 } | |
| 170 | |
| 171 virtual void stop() | |
| 172 { | |
| 173 m_running = false; | |
| 174 m_lastQueuedTask = nullptr; | |
| 175 } | |
| 176 | |
| 177 private: | |
| 178 void OnTimeout() | |
| 179 { | |
| 180 ASSERT(m_workerThread->workerGlobalScope()); | |
| 181 | |
| 182 m_lastQueuedTask = nullptr; | |
| 183 | |
| 184 if (m_sharedTimerFunction && m_running && !m_workerThread->workerGlobalS
cope()->isClosing()) | |
| 185 m_sharedTimerFunction(); | |
| 186 } | |
| 187 | |
| 188 WorkerThread* m_workerThread; | |
| 189 SharedTimerFunction m_sharedTimerFunction; | |
| 190 bool m_running; | |
| 191 | |
| 192 // The task to run OnTimeout, if any. While OnTimeout resets | |
| 193 // m_lastQueuedTask, this must be a weak pointer because the | |
| 194 // worker runloop may delete the task as it is shutting down. | |
| 195 WeakPtr<WorkerThreadCancelableTask> m_lastQueuedTask; | |
| 196 }; | |
| 197 | |
| 198 class WorkerThreadTask : public WebThread::Task { | |
| 199 WTF_MAKE_NONCOPYABLE(WorkerThreadTask); WTF_MAKE_FAST_ALLOCATED(WorkerThread
Task); | |
| 200 public: | |
| 201 static PassOwnPtr<WorkerThreadTask> create(WorkerThread& workerThread, PassO
wnPtr<ExecutionContextTask> task, bool isInstrumented) | |
| 202 { | |
| 203 return adoptPtr(new WorkerThreadTask(workerThread, task, isInstrumented)
); | |
| 204 } | |
| 205 | |
| 206 virtual ~WorkerThreadTask() { } | |
| 207 | |
| 208 virtual void run() override | |
| 209 { | |
| 210 WorkerGlobalScope* workerGlobalScope = m_workerThread.workerGlobalScope(
); | |
| 211 // Tasks could be put on the message loop after the cleanup task, | |
| 212 // ensure none of those are ran. | |
| 213 if (!workerGlobalScope) | |
| 214 return; | |
| 215 | |
| 216 if (m_isInstrumented) | |
| 217 InspectorInstrumentation::willPerformExecutionContextTask(workerGlob
alScope, m_task.get()); | |
| 218 if ((!workerGlobalScope->isClosing() && !m_workerThread.terminated()) ||
m_task->isCleanupTask()) | |
| 219 m_task->performTask(workerGlobalScope); | |
| 220 if (m_isInstrumented) | |
| 221 InspectorInstrumentation::didPerformExecutionContextTask(workerGloba
lScope); | |
| 222 } | |
| 223 | |
| 224 private: | |
| 225 WorkerThreadTask(WorkerThread& workerThread, PassOwnPtr<ExecutionContextTask
> task, bool isInstrumented) | |
| 226 : m_workerThread(workerThread) | |
| 227 , m_task(task) | |
| 228 , m_isInstrumented(isInstrumented) | |
| 229 { | |
| 230 if (m_isInstrumented) | |
| 231 m_isInstrumented = !m_task->taskNameForInstrumentation().isEmpty(); | |
| 232 if (m_isInstrumented) | |
| 233 InspectorInstrumentation::didPostExecutionContextTask(m_workerThread
.workerGlobalScope(), m_task.get()); | |
| 234 } | |
| 235 | |
| 236 WorkerThread& m_workerThread; | |
| 237 OwnPtr<ExecutionContextTask> m_task; | |
| 238 bool m_isInstrumented; | |
| 239 }; | |
| 240 | |
| 241 class RunDebuggerQueueTask final : public ExecutionContextTask { | |
| 242 public: | |
| 243 static PassOwnPtr<RunDebuggerQueueTask> create(WorkerThread* thread) | |
| 244 { | |
| 245 return adoptPtr(new RunDebuggerQueueTask(thread)); | |
| 246 } | |
| 247 virtual void performTask(ExecutionContext* context) override | |
| 248 { | |
| 249 ASSERT(context->isWorkerGlobalScope()); | |
| 250 m_thread->runDebuggerTask(WorkerThread::DontWaitForMessage); | |
| 251 } | |
| 252 | |
| 253 private: | |
| 254 explicit RunDebuggerQueueTask(WorkerThread* thread) : m_thread(thread) { } | |
| 255 | |
| 256 WorkerThread* m_thread; | |
| 257 }; | |
| 258 | |
| 259 WorkerThread::WorkerThread(PassRefPtr<WorkerLoaderProxy> workerLoaderProxy, Work
erReportingProxy& workerReportingProxy, PassOwnPtr<WorkerThreadStartupData> star
tupData) | |
| 260 : m_started(false) | |
| 261 , m_terminated(false) | |
| 262 , m_workerLoaderProxy(workerLoaderProxy) | |
| 263 , m_workerReportingProxy(workerReportingProxy) | |
| 264 , m_startupData(startupData) | |
| 265 , m_isolate(nullptr) | |
| 266 , m_shutdownEvent(adoptPtr(Platform::current()->createWaitableEvent())) | |
| 267 , m_terminationEvent(adoptPtr(Platform::current()->createWaitableEvent())) | |
| 268 { | |
| 269 MutexLocker lock(threadSetMutex()); | |
| 270 workerThreads().add(this); | |
| 271 } | |
| 272 | |
| 273 WorkerThread::~WorkerThread() | |
| 274 { | |
| 275 MutexLocker lock(threadSetMutex()); | |
| 276 ASSERT(workerThreads().contains(this)); | |
| 277 workerThreads().remove(this); | |
| 278 } | |
| 279 | |
| 280 void WorkerThread::start() | |
| 281 { | |
| 282 if (m_started) | |
| 283 return; | |
| 284 | |
| 285 m_started = true; | |
| 286 backingThread().postTask(FROM_HERE, new Task(threadSafeBind(&WorkerThread::i
nitialize, AllowCrossThreadAccess(this)))); | |
| 287 } | |
| 288 | |
| 289 void WorkerThread::interruptAndDispatchInspectorCommands() | |
| 290 { | |
| 291 MutexLocker locker(m_workerInspectorControllerMutex); | |
| 292 if (m_workerInspectorController) | |
| 293 m_workerInspectorController->interruptAndDispatchInspectorCommands(); | |
| 294 } | |
| 295 | |
| 296 PlatformThreadId WorkerThread::platformThreadId() | |
| 297 { | |
| 298 if (!m_started) | |
| 299 return 0; | |
| 300 return backingThread().platformThread().threadId(); | |
| 301 } | |
| 302 | |
| 303 void WorkerThread::initialize() | |
| 304 { | |
| 305 KURL scriptURL = m_startupData->m_scriptURL; | |
| 306 String sourceCode = m_startupData->m_sourceCode; | |
| 307 WorkerThreadStartMode startMode = m_startupData->m_startMode; | |
| 308 OwnPtr<Vector<char>> cachedMetaData = m_startupData->m_cachedMetaData.releas
e(); | |
| 309 V8CacheOptions v8CacheOptions = m_startupData->m_v8CacheOptions; | |
| 310 | |
| 311 { | |
| 312 MutexLocker lock(m_threadCreationMutex); | |
| 313 | |
| 314 // The worker was terminated before the thread had a chance to run. | |
| 315 if (m_terminated) { | |
| 316 // Notify the proxy that the WorkerGlobalScope has been disposed of. | |
| 317 // This can free this thread object, hence it must not be touched af
terwards. | |
| 318 m_workerReportingProxy.workerThreadTerminated(); | |
| 319 return; | |
| 320 } | |
| 321 | |
| 322 m_microtaskRunner = adoptPtr(new MicrotaskRunner(this)); | |
| 323 backingThread().addTaskObserver(m_microtaskRunner.get()); | |
| 324 backingThread().attachGC(); | |
| 325 | |
| 326 m_isolate = initializeIsolate(); | |
| 327 m_workerGlobalScope = createWorkerGlobalScope(m_startupData.release()); | |
| 328 m_workerGlobalScope->scriptLoaded(sourceCode.length(), cachedMetaData.ge
t() ? cachedMetaData->size() : 0); | |
| 329 | |
| 330 PlatformThreadData::current().threadTimers().setSharedTimer(adoptPtr(new
WorkerSharedTimer(this))); | |
| 331 } | |
| 332 | |
| 333 // The corresponding call to stopRunLoop() is in ~WorkerScriptController(). | |
| 334 didStartRunLoop(); | |
| 335 | |
| 336 // Notify proxy that a new WorkerGlobalScope has been created and started. | |
| 337 m_workerReportingProxy.workerGlobalScopeStarted(m_workerGlobalScope.get()); | |
| 338 | |
| 339 WorkerScriptController* script = m_workerGlobalScope->script(); | |
| 340 if (!script->isExecutionForbidden()) | |
| 341 script->initializeContextIfNeeded(); | |
| 342 if (startMode == PauseWorkerGlobalScopeOnStart) | |
| 343 m_workerGlobalScope->workerInspectorController()->pauseOnStart(); | |
| 344 | |
| 345 OwnPtr<CachedMetadataHandler> handler(workerGlobalScope()->createWorkerScrip
tCachedMetadataHandler(scriptURL, cachedMetaData.get())); | |
| 346 bool success = script->evaluate(ScriptSourceCode(sourceCode, scriptURL), nul
lptr, handler.get(), v8CacheOptions); | |
| 347 m_workerGlobalScope->didEvaluateWorkerScript(); | |
| 348 m_workerReportingProxy.didEvaluateWorkerScript(success); | |
| 349 | |
| 350 postInitialize(); | |
| 351 | |
| 352 postDelayedTask(FROM_HERE, createSameThreadTask(&WorkerThread::idleHandler,
this), kShortIdleHandlerDelayMs); | |
| 353 } | |
| 354 | |
| 355 void WorkerThread::cleanup() | |
| 356 { | |
| 357 // This should be called before we start the shutdown procedure. | |
| 358 workerReportingProxy().willDestroyWorkerGlobalScope(); | |
| 359 | |
| 360 // The below assignment will destroy the context, which will in turn notify
messaging proxy. | |
| 361 // We cannot let any objects survive past thread exit, because no other thre
ad will run GC or otherwise destroy them. | |
| 362 // If Oilpan is enabled, we detach of the context/global scope, with the fin
al heap cleanup below sweeping it out. | |
| 363 #if !ENABLE(OILPAN) | |
| 364 ASSERT(m_workerGlobalScope->hasOneRef()); | |
| 365 #endif | |
| 366 m_workerGlobalScope->notifyContextDestroyed(); | |
| 367 m_workerGlobalScope = nullptr; | |
| 368 | |
| 369 backingThread().detachGC(); | |
| 370 destroyIsolate(); | |
| 371 | |
| 372 backingThread().removeTaskObserver(m_microtaskRunner.get()); | |
| 373 m_microtaskRunner = nullptr; | |
| 374 | |
| 375 // Notify the proxy that the WorkerGlobalScope has been disposed of. | |
| 376 // This can free this thread object, hence it must not be touched afterwards
. | |
| 377 workerReportingProxy().workerThreadTerminated(); | |
| 378 | |
| 379 m_terminationEvent->signal(); | |
| 380 | |
| 381 // Clean up PlatformThreadData before WTF::WTFThreadData goes away! | |
| 382 PlatformThreadData::current().destroy(); | |
| 383 } | |
| 384 | |
| 385 class WorkerThreadShutdownFinishTask : public ExecutionContextTask { | |
| 386 public: | |
| 387 static PassOwnPtr<WorkerThreadShutdownFinishTask> create() | |
| 388 { | |
| 389 return adoptPtr(new WorkerThreadShutdownFinishTask()); | |
| 390 } | |
| 391 | |
| 392 virtual void performTask(ExecutionContext *context) | |
| 393 { | |
| 394 WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); | |
| 395 workerGlobalScope->dispose(); | |
| 396 | |
| 397 WorkerThread* workerThread = workerGlobalScope->thread(); | |
| 398 workerThread->willDestroyIsolate(); | |
| 399 workerThread->backingThread().postTask(FROM_HERE, new Task(WTF::bind(&Wo
rkerThread::cleanup, workerThread))); | |
| 400 } | |
| 401 | |
| 402 virtual bool isCleanupTask() const { return true; } | |
| 403 }; | |
| 404 | |
| 405 class WorkerThreadShutdownStartTask : public ExecutionContextTask { | |
| 406 public: | |
| 407 static PassOwnPtr<WorkerThreadShutdownStartTask> create() | |
| 408 { | |
| 409 return adoptPtr(new WorkerThreadShutdownStartTask()); | |
| 410 } | |
| 411 | |
| 412 virtual void performTask(ExecutionContext *context) | |
| 413 { | |
| 414 WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); | |
| 415 workerGlobalScope->stopActiveDOMObjects(); | |
| 416 PlatformThreadData::current().threadTimers().setSharedTimer(nullptr); | |
| 417 | |
| 418 // Event listeners would keep DOMWrapperWorld objects alive for too long
. Also, they have references to JS objects, | |
| 419 // which become dangling once Heap is destroyed. | |
| 420 workerGlobalScope->removeAllEventListeners(); | |
| 421 | |
| 422 // Stick a shutdown command at the end of the queue, so that we deal | |
| 423 // with all the cleanup tasks the databases post first. | |
| 424 workerGlobalScope->postTask(FROM_HERE, WorkerThreadShutdownFinishTask::c
reate()); | |
| 425 } | |
| 426 | |
| 427 virtual bool isCleanupTask() const { return true; } | |
| 428 }; | |
| 429 | |
| 430 void WorkerThread::stop() | |
| 431 { | |
| 432 // Prevent the deadlock between GC and an attempt to stop a thread. | |
| 433 SafePointScope safePointScope(ThreadState::HeapPointersOnStack); | |
| 434 stopInternal(); | |
| 435 } | |
| 436 | |
| 437 void WorkerThread::stopInShutdownSequence() | |
| 438 { | |
| 439 stopInternal(); | |
| 440 } | |
| 441 | |
| 442 void WorkerThread::terminateAndWait() | |
| 443 { | |
| 444 stop(); | |
| 445 m_terminationEvent->wait(); | |
| 446 } | |
| 447 | |
| 448 bool WorkerThread::terminated() | |
| 449 { | |
| 450 MutexLocker lock(m_threadCreationMutex); | |
| 451 return m_terminated; | |
| 452 } | |
| 453 | |
| 454 void WorkerThread::stopInternal() | |
| 455 { | |
| 456 // Protect against this method and initialize() racing each other. | |
| 457 MutexLocker lock(m_threadCreationMutex); | |
| 458 | |
| 459 // If stop has already been called, just return. | |
| 460 if (m_terminated) | |
| 461 return; | |
| 462 m_terminated = true; | |
| 463 | |
| 464 // Signal the thread to notify that the thread's stopping. | |
| 465 if (m_shutdownEvent) | |
| 466 m_shutdownEvent->signal(); | |
| 467 | |
| 468 if (!m_workerGlobalScope) | |
| 469 return; | |
| 470 | |
| 471 // Ensure that tasks are being handled by thread event loop. If script execu
tion weren't forbidden, a while(1) loop in JS could keep the thread alive foreve
r. | |
| 472 terminateV8Execution(); | |
| 473 | |
| 474 InspectorInstrumentation::didKillAllExecutionContextTasks(m_workerGlobalScop
e.get()); | |
| 475 m_debuggerMessageQueue.kill(); | |
| 476 postTask(FROM_HERE, WorkerThreadShutdownStartTask::create()); | |
| 477 } | |
| 478 | |
| 479 void WorkerThread::didStartRunLoop() | |
| 480 { | |
| 481 ASSERT(isCurrentThread()); | |
| 482 Platform::current()->didStartWorkerRunLoop(); | |
| 483 } | |
| 484 | |
| 485 void WorkerThread::didStopRunLoop() | |
| 486 { | |
| 487 ASSERT(isCurrentThread()); | |
| 488 Platform::current()->didStopWorkerRunLoop(); | |
| 489 } | |
| 490 | |
| 491 void WorkerThread::terminateAndWaitForAllWorkers() | |
| 492 { | |
| 493 // Keep this lock to prevent WorkerThread instances from being destroyed. | |
| 494 MutexLocker lock(threadSetMutex()); | |
| 495 HashSet<WorkerThread*> threads = workerThreads(); | |
| 496 for (WorkerThread* thread : threads) | |
| 497 thread->stopInShutdownSequence(); | |
| 498 | |
| 499 for (WorkerThread* thread : threads) | |
| 500 thread->terminationEvent()->wait(); | |
| 501 } | |
| 502 | |
| 503 bool WorkerThread::isCurrentThread() | |
| 504 { | |
| 505 return m_started && backingThread().isCurrentThread(); | |
| 506 } | |
| 507 | |
| 508 void WorkerThread::idleHandler() | |
| 509 { | |
| 510 ASSERT(m_workerGlobalScope.get()); | |
| 511 int64_t delay = kLongIdleHandlerDelayMs; | |
| 512 | |
| 513 // Do a script engine idle notification if the next event is distant enough. | |
| 514 const double kMinIdleTimespan = 0.3; | |
| 515 const double nextFireTime = PlatformThreadData::current().threadTimers().nex
tFireTime(); | |
| 516 if (nextFireTime == 0.0 || nextFireTime > currentTime() + kMinIdleTimespan)
{ | |
| 517 bool hasMoreWork = !isolate()->IdleNotificationDeadline(Platform::curren
t()->monotonicallyIncreasingTime() + 1.0); | |
| 518 if (hasMoreWork) | |
| 519 delay = kShortIdleHandlerDelayMs; | |
| 520 } | |
| 521 | |
| 522 postDelayedTask(FROM_HERE, createSameThreadTask(&WorkerThread::idleHandler,
this), delay); | |
| 523 } | |
| 524 | |
| 525 void WorkerThread::postTask(const WebTraceLocation& location, PassOwnPtr<Executi
onContextTask> task) | |
| 526 { | |
| 527 backingThread().postTask(location, WorkerThreadTask::create(*this, task, tru
e).leakPtr()); | |
| 528 } | |
| 529 | |
| 530 void WorkerThread::postDelayedTask(const WebTraceLocation& location, PassOwnPtr<
ExecutionContextTask> task, long long delayMs) | |
| 531 { | |
| 532 backingThread().postDelayedTask(location, WorkerThreadTask::create(*this, ta
sk, true).leakPtr(), delayMs); | |
| 533 } | |
| 534 | |
| 535 v8::Isolate* WorkerThread::initializeIsolate() | |
| 536 { | |
| 537 ASSERT(isCurrentThread()); | |
| 538 ASSERT(!m_isolate); | |
| 539 v8::Isolate* isolate = V8PerIsolateData::initialize(); | |
| 540 V8Initializer::initializeWorker(isolate); | |
| 541 | |
| 542 m_interruptor = adoptPtr(new V8IsolateInterruptor(isolate)); | |
| 543 ThreadState::current()->addInterruptor(m_interruptor.get()); | |
| 544 ThreadState::current()->registerTraceDOMWrappers(isolate, V8GCController::tr
aceDOMWrappers); | |
| 545 | |
| 546 return isolate; | |
| 547 } | |
| 548 | |
| 549 void WorkerThread::willDestroyIsolate() | |
| 550 { | |
| 551 ASSERT(isCurrentThread()); | |
| 552 ASSERT(m_isolate); | |
| 553 V8PerIsolateData::willBeDestroyed(m_isolate); | |
| 554 ThreadState::current()->removeInterruptor(m_interruptor.get()); | |
| 555 } | |
| 556 | |
| 557 void WorkerThread::destroyIsolate() | |
| 558 { | |
| 559 ASSERT(isCurrentThread()); | |
| 560 V8PerIsolateData::destroy(m_isolate); | |
| 561 m_isolate = nullptr; | |
| 562 } | |
| 563 | |
| 564 void WorkerThread::terminateV8Execution() | |
| 565 { | |
| 566 ASSERT(isMainThread()); | |
| 567 m_workerGlobalScope->script()->willScheduleExecutionTermination(); | |
| 568 v8::V8::TerminateExecution(m_isolate); | |
| 569 } | |
| 570 | |
| 571 void WorkerThread::postDebuggerTask(const WebTraceLocation& location, PassOwnPtr
<ExecutionContextTask> task) | |
| 572 { | |
| 573 m_debuggerMessageQueue.append(WorkerThreadTask::create(*this, task, false)); | |
| 574 postTask(location, RunDebuggerQueueTask::create(this)); | |
| 575 } | |
| 576 | |
| 577 MessageQueueWaitResult WorkerThread::runDebuggerTask(WaitMode waitMode) | |
| 578 { | |
| 579 ASSERT(isCurrentThread()); | |
| 580 MessageQueueWaitResult result; | |
| 581 double absoluteTime = MessageQueue<WebThread::Task>::infiniteTime(); | |
| 582 OwnPtr<WebThread::Task> task; | |
| 583 { | |
| 584 if (waitMode == DontWaitForMessage) | |
| 585 absoluteTime = 0.0; | |
| 586 SafePointScope safePointScope(ThreadState::NoHeapPointersOnStack); | |
| 587 task = m_debuggerMessageQueue.waitForMessageWithTimeout(result, absolute
Time); | |
| 588 } | |
| 589 | |
| 590 if (result == MessageQueueMessageReceived) { | |
| 591 InspectorInstrumentation::willProcessTask(workerGlobalScope()); | |
| 592 task->run(); | |
| 593 InspectorInstrumentation::didProcessTask(workerGlobalScope()); | |
| 594 } | |
| 595 | |
| 596 return result; | |
| 597 } | |
| 598 | |
| 599 void WorkerThread::willEnterNestedLoop() | |
| 600 { | |
| 601 InspectorInstrumentation::willEnterNestedRunLoop(m_workerGlobalScope.get()); | |
| 602 } | |
| 603 | |
| 604 void WorkerThread::didLeaveNestedLoop() | |
| 605 { | |
| 606 InspectorInstrumentation::didLeaveNestedRunLoop(m_workerGlobalScope.get()); | |
| 607 } | |
| 608 | |
| 609 void WorkerThread::setWorkerInspectorController(WorkerInspectorController* worke
rInspectorController) | |
| 610 { | |
| 611 MutexLocker locker(m_workerInspectorControllerMutex); | |
| 612 m_workerInspectorController = workerInspectorController; | |
| 613 } | |
| 614 | |
| 615 } // namespace blink | |
| OLD | NEW |