| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. | 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
| 6 * are met: | 6 * are met: |
| 7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
| 8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
| 9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
| 10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 64 DCHECK(!m_workerThread->workerGlobalScope() || !m_workerThread->workerGl
obalScope()->isClosing()); | 64 DCHECK(!m_workerThread->workerGlobalScope() || !m_workerThread->workerGl
obalScope()->isClosing()); |
| 65 } | 65 } |
| 66 | 66 |
| 67 void didProcessTask() override | 67 void didProcessTask() override |
| 68 { | 68 { |
| 69 Microtask::performCheckpoint(m_workerThread->isolate()); | 69 Microtask::performCheckpoint(m_workerThread->isolate()); |
| 70 if (WorkerGlobalScope* globalScope = m_workerThread->workerGlobalScope()
) { | 70 if (WorkerGlobalScope* globalScope = m_workerThread->workerGlobalScope()
) { |
| 71 if (WorkerOrWorkletScriptController* scriptController = globalScope-
>scriptController()) | 71 if (WorkerOrWorkletScriptController* scriptController = globalScope-
>scriptController()) |
| 72 scriptController->getRejectedPromises()->processQueue(); | 72 scriptController->getRejectedPromises()->processQueue(); |
| 73 if (globalScope->isClosing()) { | 73 if (globalScope->isClosing()) { |
| 74 // |m_workerThread| will eventually be requested to terminate. |
| 74 m_workerThread->workerReportingProxy().workerGlobalScopeClosed()
; | 75 m_workerThread->workerReportingProxy().workerGlobalScopeClosed()
; |
| 75 m_workerThread->terminateFromWorkerThread(); | 76 |
| 77 // Dispose WorkerGlobalScope to stop associated ActiveDOMObjects |
| 78 // and close the event queue. |
| 79 m_workerThread->prepareForShutdown(); |
| 76 } | 80 } |
| 77 } | 81 } |
| 78 } | 82 } |
| 79 | 83 |
| 80 private: | 84 private: |
| 81 // Thread owns the microtask runner; reference remains | 85 // Thread owns the microtask runner; reference remains |
| 82 // valid for the lifetime of this object. | 86 // valid for the lifetime of this object. |
| 83 WorkerThread* m_workerThread; | 87 WorkerThread* m_workerThread; |
| 84 }; | 88 }; |
| 85 | 89 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 97 | 101 |
| 98 unsigned WorkerThread::workerThreadCount() | 102 unsigned WorkerThread::workerThreadCount() |
| 99 { | 103 { |
| 100 MutexLocker lock(threadSetMutex()); | 104 MutexLocker lock(threadSetMutex()); |
| 101 return workerThreads().size(); | 105 return workerThreads().size(); |
| 102 } | 106 } |
| 103 | 107 |
| 104 void WorkerThread::performTask(std::unique_ptr<ExecutionContextTask> task, bool
isInstrumented) | 108 void WorkerThread::performTask(std::unique_ptr<ExecutionContextTask> task, bool
isInstrumented) |
| 105 { | 109 { |
| 106 DCHECK(isCurrentThread()); | 110 DCHECK(isCurrentThread()); |
| 111 { |
| 112 MutexLocker lock(m_threadStateMutex); |
| 113 if (m_readyToShutdown) |
| 114 return; |
| 115 } |
| 116 |
| 107 WorkerGlobalScope* globalScope = workerGlobalScope(); | 117 WorkerGlobalScope* globalScope = workerGlobalScope(); |
| 108 // If the thread is terminated before it had a chance initialize (see | 118 // If the thread is terminated before it had a chance initialize (see |
| 109 // WorkerThread::Initialize()), we mustn't run any of the posted tasks. | 119 // WorkerThread::Initialize()), we mustn't run any of the posted tasks. |
| 110 if (!globalScope) { | 120 if (!globalScope) { |
| 111 DCHECK(terminated()); | 121 DCHECK(terminated()); |
| 112 return; | 122 return; |
| 113 } | 123 } |
| 114 | 124 |
| 115 InspectorInstrumentation::AsyncTask asyncTask(globalScope, task.get(), isIns
trumented); | 125 InspectorInstrumentation::AsyncTask asyncTask(globalScope, task.get(), isIns
trumented); |
| 116 task->performTask(globalScope); | 126 task->performTask(globalScope); |
| 117 } | 127 } |
| 118 | 128 |
| 119 std::unique_ptr<CrossThreadClosure> WorkerThread::createWorkerThreadTask(std::un
ique_ptr<ExecutionContextTask> task, bool isInstrumented) | 129 std::unique_ptr<CrossThreadClosure> WorkerThread::createWorkerThreadTask(std::un
ique_ptr<ExecutionContextTask> task, bool isInstrumented) |
| 120 { | 130 { |
| 121 if (isInstrumented) | 131 if (isInstrumented) |
| 122 isInstrumented = !task->taskNameForInstrumentation().isEmpty(); | 132 isInstrumented = !task->taskNameForInstrumentation().isEmpty(); |
| 123 if (isInstrumented) { | 133 if (isInstrumented) { |
| 124 DCHECK(isCurrentThread()); | 134 DCHECK(isCurrentThread()); |
| 125 InspectorInstrumentation::asyncTaskScheduled(workerGlobalScope(), "Worke
r task", task.get()); | 135 InspectorInstrumentation::asyncTaskScheduled(workerGlobalScope(), "Worke
r task", task.get()); |
| 126 } | 136 } |
| 127 return threadSafeBind(&WorkerThread::performTask, AllowCrossThreadAccess(thi
s), passed(std::move(task)), isInstrumented); | 137 return threadSafeBind(&WorkerThread::performTask, AllowCrossThreadAccess(thi
s), passed(std::move(task)), isInstrumented); |
| 128 } | 138 } |
| 129 | 139 |
| 130 WorkerThread::WorkerThread(PassRefPtr<WorkerLoaderProxy> workerLoaderProxy, Work
erReportingProxy& workerReportingProxy) | 140 WorkerThread::WorkerThread(PassRefPtr<WorkerLoaderProxy> workerLoaderProxy, Work
erReportingProxy& workerReportingProxy) |
| 131 : m_started(false) | 141 : m_inspectorTaskRunner(adoptPtr(new InspectorTaskRunner())) |
| 132 , m_terminated(false) | |
| 133 , m_shutdown(false) | |
| 134 , m_pausedInDebugger(false) | |
| 135 , m_runningDebuggerTask(false) | |
| 136 , m_shouldTerminateV8Execution(false) | |
| 137 , m_inspectorTaskRunner(adoptPtr(new InspectorTaskRunner())) | |
| 138 , m_workerLoaderProxy(workerLoaderProxy) | 142 , m_workerLoaderProxy(workerLoaderProxy) |
| 139 , m_workerReportingProxy(workerReportingProxy) | 143 , m_workerReportingProxy(workerReportingProxy) |
| 140 , m_terminationEvent(adoptPtr(new WaitableEvent( | 144 , m_terminationEvent(adoptPtr(new WaitableEvent( |
| 141 WaitableEvent::ResetPolicy::Manual, | 145 WaitableEvent::ResetPolicy::Manual, |
| 142 WaitableEvent::InitialState::NonSignaled))) | 146 WaitableEvent::InitialState::NonSignaled))) |
| 143 , m_shutdownEvent(adoptPtr(new WaitableEvent( | 147 , m_shutdownEvent(adoptPtr(new WaitableEvent( |
| 144 WaitableEvent::ResetPolicy::Manual, | 148 WaitableEvent::ResetPolicy::Manual, |
| 145 WaitableEvent::InitialState::NonSignaled))) | 149 WaitableEvent::InitialState::NonSignaled))) |
| 146 { | 150 { |
| 147 MutexLocker lock(threadSetMutex()); | 151 MutexLocker lock(threadSetMutex()); |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 227 } | 231 } |
| 228 | 232 |
| 229 CachedMetadataHandler* handler = workerGlobalScope()->createWorkerScriptCach
edMetadataHandler(scriptURL, cachedMetaData.get()); | 233 CachedMetadataHandler* handler = workerGlobalScope()->createWorkerScriptCach
edMetadataHandler(scriptURL, cachedMetaData.get()); |
| 230 bool success = m_workerGlobalScope->scriptController()->evaluate(ScriptSourc
eCode(sourceCode, scriptURL), nullptr, handler, v8CacheOptions); | 234 bool success = m_workerGlobalScope->scriptController()->evaluate(ScriptSourc
eCode(sourceCode, scriptURL), nullptr, handler, v8CacheOptions); |
| 231 m_workerGlobalScope->didEvaluateWorkerScript(); | 235 m_workerGlobalScope->didEvaluateWorkerScript(); |
| 232 m_workerReportingProxy.didEvaluateWorkerScript(success); | 236 m_workerReportingProxy.didEvaluateWorkerScript(success); |
| 233 | 237 |
| 234 postInitialize(); | 238 postInitialize(); |
| 235 } | 239 } |
| 236 | 240 |
| 237 void WorkerThread::shutdown() | |
| 238 { | |
| 239 DCHECK(isCurrentThread()); | |
| 240 { | |
| 241 MutexLocker lock(m_threadStateMutex); | |
| 242 if (m_shutdown) | |
| 243 return; | |
| 244 m_shutdown = true; | |
| 245 } | |
| 246 | |
| 247 // This should be called before we start the shutdown procedure. | |
| 248 workerReportingProxy().willDestroyWorkerGlobalScope(); | |
| 249 | |
| 250 InspectorInstrumentation::allAsyncTasksCanceled(workerGlobalScope()); | |
| 251 workerGlobalScope()->dispose(); | |
| 252 | |
| 253 workerBackingThread().backingThread().removeTaskObserver(m_microtaskRunner.g
et()); | |
| 254 postTask(BLINK_FROM_HERE, createSameThreadTask(&WorkerThread::performShutdow
nTask, this)); | |
| 255 } | |
| 256 | |
| 257 void WorkerThread::performShutdownTask() | 241 void WorkerThread::performShutdownTask() |
| 258 { | 242 { |
| 259 DCHECK(isCurrentThread()); | 243 DCHECK(isCurrentThread()); |
| 244 #if DCHECK_IS_ON |
| 245 { |
| 246 MutexLocker lock(m_threadStateMutex); |
| 247 DCHECK(m_terminated); |
| 248 DCHECK(m_readyToShutdown); |
| 249 } |
| 250 #endif |
| 260 | 251 |
| 261 // The below assignment will destroy the context, which will in turn notify | 252 // The below assignment will destroy the context, which will in turn notify |
| 262 // messaging proxy. We cannot let any objects survive past thread exit, | 253 // messaging proxy. We cannot let any objects survive past thread exit, |
| 263 // because no other thread will run GC or otherwise destroy them. If Oilpan | 254 // because no other thread will run GC or otherwise destroy them. If Oilpan |
| 264 // is enabled, we detach of the context/global scope, with the final heap | 255 // is enabled, we detach of the context/global scope, with the final heap |
| 265 // cleanup below sweeping it out. | 256 // cleanup below sweeping it out. |
| 266 m_workerGlobalScope->notifyContextDestroyed(); | 257 m_workerGlobalScope->notifyContextDestroyed(); |
| 267 m_workerGlobalScope = nullptr; | 258 m_workerGlobalScope = nullptr; |
| 268 | 259 |
| 269 workerBackingThread().detach(); | 260 workerBackingThread().detach(); |
| (...skipping 21 matching lines...) Expand all Loading... |
| 291 | 282 |
| 292 // If terminate has already been called, just return. | 283 // If terminate has already been called, just return. |
| 293 if (m_terminated) | 284 if (m_terminated) |
| 294 return; | 285 return; |
| 295 m_terminated = true; | 286 m_terminated = true; |
| 296 | 287 |
| 297 // Signal the thread to notify that the thread's stopping. | 288 // Signal the thread to notify that the thread's stopping. |
| 298 if (m_terminationEvent) | 289 if (m_terminationEvent) |
| 299 m_terminationEvent->signal(); | 290 m_terminationEvent->signal(); |
| 300 | 291 |
| 301 // If the thread has already initiated shutdown, just return. | |
| 302 if (m_shutdown) | |
| 303 return; | |
| 304 | |
| 305 // If the worker thread was never initialized, don't start another | 292 // If the worker thread was never initialized, don't start another |
| 306 // shutdown, but still wait for the thread to signal when shutdown has | 293 // shutdown, but still wait for the thread to signal when shutdown has |
| 307 // completed on initialize(). | 294 // completed on initialize(). |
| 308 if (!m_workerGlobalScope) | 295 if (!m_workerGlobalScope) |
| 309 return; | 296 return; |
| 310 | 297 |
| 311 // Ensure that tasks are being handled by thread event loop. If script | 298 if (!m_readyToShutdown) { |
| 312 // execution weren't forbidden, a while(1) loop in JS could keep the thread | 299 // Ensure that tasks are being handled by thread event loop. If script |
| 313 // alive forever. | 300 // execution weren't forbidden, a while(1) loop in JS could keep the |
| 314 m_workerGlobalScope->scriptController()->willScheduleExecutionTermination(); | 301 // thread alive forever. |
| 302 // If |m_readyToShutdown| is set, the worker thread has already noticed |
| 303 // that the thread is about to be terminated and the worker global scope |
| 304 // is already disposed, so we don't have to explicitly terminate the |
| 305 // execution. |
| 306 m_workerGlobalScope->scriptController()->willScheduleExecutionTerminatio
n(); |
| 315 | 307 |
| 316 if (workerBackingThread().workerScriptCount() == 1) { | 308 // This condition is not entirely correct because other scripts can |
| 317 // This condition is not entirely correct because other scripts | 309 // be being initialized or terminated simuletaneously. Though this |
| 318 // can be being initialized or terminated simuletaneously. Though this | |
| 319 // function itself is protected by a mutex, it is possible that | 310 // function itself is protected by a mutex, it is possible that |
| 320 // |workerScriptCount()| here is not consistent with that in | 311 // |workerScriptCount()| here is not consistent with that in |
| 321 // |initialize| and |shutdown|. | 312 // |initialize| and |shutdown|. |
| 322 // TODO(yhirano): TerminateExecution should be called more carefully. | 313 if (workerBackingThread().workerScriptCount() == 1) { |
| 323 // https://crbug.com/413518 | 314 if (m_runningDebuggerTask) { |
| 324 if (m_runningDebuggerTask) { | 315 // Terminating during debugger task may lead to crash due to |
| 325 // Terminating during debugger task may lead to crash due to heavy | 316 // heavy use of v8 api in debugger. Any debugger task is |
| 326 // use of v8 api in debugger. Any debugger task is guaranteed to | 317 // guaranteed to finish, so we can postpone termination after |
| 327 // finish, so we can postpone termination after task has finished. | 318 // task has finished. |
| 328 // Note: m_runningDebuggerTask and m_shouldTerminateV8Execution | 319 // Note: m_runningDebuggerTask and m_shouldTerminateV8Execution |
| 329 // access must be guarded by the lock. | 320 // access must be guarded by the lock. |
| 330 m_shouldTerminateV8Execution = true; | 321 m_shouldTerminateV8Execution = true; |
| 331 } else { | 322 } else { |
| 332 isolate()->TerminateExecution(); | 323 // TODO(yhirano): TerminateExecution should be called more |
| 324 // carefully (https://crbug.com/413518). |
| 325 isolate()->TerminateExecution(); |
| 326 } |
| 333 } | 327 } |
| 334 } | 328 } |
| 335 | 329 |
| 336 m_inspectorTaskRunner->kill(); | 330 m_inspectorTaskRunner->kill(); |
| 337 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi
nd(&WorkerThread::shutdown, AllowCrossThreadAccess(this))); | 331 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi
nd(&WorkerThread::prepareForShutdown, AllowCrossThreadAccess(this))); |
| 332 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi
nd(&WorkerThread::performShutdownTask, AllowCrossThreadAccess(this))); |
| 338 } | 333 } |
| 339 | 334 |
| 340 void WorkerThread::terminateAndWait() | 335 void WorkerThread::terminateAndWait() |
| 341 { | 336 { |
| 342 DCHECK(isMainThread()); | 337 DCHECK(isMainThread()); |
| 343 terminate(); | 338 terminate(); |
| 344 m_shutdownEvent->wait(); | 339 m_shutdownEvent->wait(); |
| 345 } | 340 } |
| 346 | 341 |
| 347 void WorkerThread::terminateAndWaitForAllWorkers() | 342 void WorkerThread::terminateAndWaitForAllWorkers() |
| 348 { | 343 { |
| 349 DCHECK(isMainThread()); | 344 DCHECK(isMainThread()); |
| 350 | 345 |
| 351 // Keep this lock to prevent WorkerThread instances from being destroyed. | 346 // Keep this lock to prevent WorkerThread instances from being destroyed. |
| 352 MutexLocker lock(threadSetMutex()); | 347 MutexLocker lock(threadSetMutex()); |
| 353 HashSet<WorkerThread*> threads = workerThreads(); | 348 HashSet<WorkerThread*> threads = workerThreads(); |
| 354 for (WorkerThread* thread : threads) | 349 for (WorkerThread* thread : threads) |
| 355 thread->terminate(); | 350 thread->terminate(); |
| 356 | 351 |
| 357 for (WorkerThread* thread : threads) | 352 for (WorkerThread* thread : threads) |
| 358 thread->m_shutdownEvent->wait(); | 353 thread->m_shutdownEvent->wait(); |
| 359 } | 354 } |
| 360 | 355 |
| 361 void WorkerThread::terminateFromWorkerThread() | 356 void WorkerThread::prepareForShutdown() |
| 362 { | 357 { |
| 363 DCHECK(isCurrentThread()); | 358 DCHECK(isCurrentThread()); |
| 364 shutdown(); | 359 { |
| 360 MutexLocker lock(m_threadStateMutex); |
| 361 if (m_readyToShutdown) |
| 362 return; |
| 363 m_readyToShutdown = true; |
| 364 } |
| 365 |
| 366 workerReportingProxy().willDestroyWorkerGlobalScope(); |
| 367 InspectorInstrumentation::allAsyncTasksCanceled(workerGlobalScope()); |
| 368 workerGlobalScope()->dispose(); |
| 369 workerBackingThread().backingThread().removeTaskObserver(m_microtaskRunner.g
et()); |
| 365 } | 370 } |
| 366 | 371 |
| 367 WorkerGlobalScope* WorkerThread::workerGlobalScope() | 372 WorkerGlobalScope* WorkerThread::workerGlobalScope() |
| 368 { | 373 { |
| 369 DCHECK(isCurrentThread()); | 374 DCHECK(isCurrentThread()); |
| 370 return m_workerGlobalScope.get(); | 375 return m_workerGlobalScope.get(); |
| 371 } | 376 } |
| 372 | 377 |
| 373 bool WorkerThread::terminated() | 378 bool WorkerThread::terminated() |
| 374 { | 379 { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 396 DCHECK(isCurrentThread()); | 401 DCHECK(isCurrentThread()); |
| 397 std::unique_ptr<CrossThreadClosure> task = m_inspectorTaskRunner->takeNextTa
sk(InspectorTaskRunner::DontWaitForTask); | 402 std::unique_ptr<CrossThreadClosure> task = m_inspectorTaskRunner->takeNextTa
sk(InspectorTaskRunner::DontWaitForTask); |
| 398 if (task) | 403 if (task) |
| 399 (*task)(); | 404 (*task)(); |
| 400 } | 405 } |
| 401 | 406 |
| 402 void WorkerThread::appendDebuggerTask(std::unique_ptr<CrossThreadClosure> task) | 407 void WorkerThread::appendDebuggerTask(std::unique_ptr<CrossThreadClosure> task) |
| 403 { | 408 { |
| 404 { | 409 { |
| 405 MutexLocker lock(m_threadStateMutex); | 410 MutexLocker lock(m_threadStateMutex); |
| 406 if (m_shutdown) | 411 if (m_readyToShutdown) |
| 407 return; | 412 return; |
| 408 } | 413 } |
| 409 m_inspectorTaskRunner->appendTask(threadSafeBind(&WorkerThread::runDebuggerT
ask, AllowCrossThreadAccess(this), passed(std::move(task)))); | 414 m_inspectorTaskRunner->appendTask(threadSafeBind(&WorkerThread::runDebuggerT
ask, AllowCrossThreadAccess(this), passed(std::move(task)))); |
| 410 { | 415 { |
| 411 MutexLocker lock(m_threadStateMutex); | 416 MutexLocker lock(m_threadStateMutex); |
| 412 if (isolate()) | 417 if (isolate()) |
| 413 m_inspectorTaskRunner->interruptAndRunAllTasksDontWait(isolate()); | 418 m_inspectorTaskRunner->interruptAndRunAllTasksDontWait(isolate()); |
| 414 } | 419 } |
| 415 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi
nd(&WorkerThread::runDebuggerTaskDontWait, AllowCrossThreadAccess(this))); | 420 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi
nd(&WorkerThread::runDebuggerTaskDontWait, AllowCrossThreadAccess(this))); |
| 416 } | 421 } |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 452 } while (task && m_pausedInDebugger); | 457 } while (task && m_pausedInDebugger); |
| 453 ThreadDebugger::idleFinished(isolate()); | 458 ThreadDebugger::idleFinished(isolate()); |
| 454 } | 459 } |
| 455 | 460 |
| 456 void WorkerThread::stopRunningDebuggerTasksOnPause() | 461 void WorkerThread::stopRunningDebuggerTasksOnPause() |
| 457 { | 462 { |
| 458 m_pausedInDebugger = false; | 463 m_pausedInDebugger = false; |
| 459 } | 464 } |
| 460 | 465 |
| 461 } // namespace blink | 466 } // namespace blink |
| OLD | NEW |