Chromium Code Reviews| 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 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 65 DCHECK(!m_workerThread->workerGlobalScope() || !m_workerThread->workerGl obalScope()->isClosing()); | 65 DCHECK(!m_workerThread->workerGlobalScope() || !m_workerThread->workerGl obalScope()->isClosing()); |
| 66 } | 66 } |
| 67 | 67 |
| 68 void didProcessTask() override | 68 void didProcessTask() override |
| 69 { | 69 { |
| 70 Microtask::performCheckpoint(m_workerThread->isolate()); | 70 Microtask::performCheckpoint(m_workerThread->isolate()); |
| 71 if (WorkerGlobalScope* globalScope = m_workerThread->workerGlobalScope() ) { | 71 if (WorkerGlobalScope* globalScope = m_workerThread->workerGlobalScope() ) { |
| 72 if (WorkerOrWorkletScriptController* scriptController = globalScope- >scriptController()) | 72 if (WorkerOrWorkletScriptController* scriptController = globalScope- >scriptController()) |
| 73 scriptController->getRejectedPromises()->processQueue(); | 73 scriptController->getRejectedPromises()->processQueue(); |
| 74 if (globalScope->isClosing()) { | 74 if (globalScope->isClosing()) { |
| 75 // |m_workerThread| will eventually be requested to terminate. | |
| 75 m_workerThread->workerReportingProxy().workerGlobalScopeClosed() ; | 76 m_workerThread->workerReportingProxy().workerGlobalScopeClosed() ; |
| 76 m_workerThread->terminateFromWorkerThread(); | 77 |
| 78 // Dispose WorkerGlobalScope to avoid processing the next task. | |
| 79 m_workerThread->prepareForShutdown(); | |
| 77 } | 80 } |
| 78 } | 81 } |
| 79 } | 82 } |
| 80 | 83 |
| 81 private: | 84 private: |
| 82 // Thread owns the microtask runner; reference remains | 85 // Thread owns the microtask runner; reference remains |
| 83 // valid for the lifetime of this object. | 86 // valid for the lifetime of this object. |
| 84 WorkerThread* m_workerThread; | 87 WorkerThread* m_workerThread; |
| 85 }; | 88 }; |
| 86 | 89 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 122 if (isInstrumented) | 125 if (isInstrumented) |
| 123 isInstrumented = !task->taskNameForInstrumentation().isEmpty(); | 126 isInstrumented = !task->taskNameForInstrumentation().isEmpty(); |
| 124 if (isInstrumented) { | 127 if (isInstrumented) { |
| 125 DCHECK(isCurrentThread()); | 128 DCHECK(isCurrentThread()); |
| 126 InspectorInstrumentation::asyncTaskScheduled(workerGlobalScope(), "Worke r task", task.get()); | 129 InspectorInstrumentation::asyncTaskScheduled(workerGlobalScope(), "Worke r task", task.get()); |
| 127 } | 130 } |
| 128 return threadSafeBind(&WorkerThread::performTask, AllowCrossThreadAccess(thi s), passed(std::move(task)), isInstrumented); | 131 return threadSafeBind(&WorkerThread::performTask, AllowCrossThreadAccess(thi s), passed(std::move(task)), isInstrumented); |
| 129 } | 132 } |
| 130 | 133 |
| 131 WorkerThread::WorkerThread(PassRefPtr<WorkerLoaderProxy> workerLoaderProxy, Work erReportingProxy& workerReportingProxy) | 134 WorkerThread::WorkerThread(PassRefPtr<WorkerLoaderProxy> workerLoaderProxy, Work erReportingProxy& workerReportingProxy) |
| 132 : m_started(false) | 135 : m_inspectorTaskRunner(adoptPtr(new InspectorTaskRunner())) |
| 133 , m_terminated(false) | |
| 134 , m_shutdown(false) | |
| 135 , m_pausedInDebugger(false) | |
| 136 , m_runningDebuggerTask(false) | |
| 137 , m_shouldTerminateV8Execution(false) | |
| 138 , m_inspectorTaskRunner(adoptPtr(new InspectorTaskRunner())) | |
| 139 , m_workerLoaderProxy(workerLoaderProxy) | 136 , m_workerLoaderProxy(workerLoaderProxy) |
| 140 , m_workerReportingProxy(workerReportingProxy) | 137 , m_workerReportingProxy(workerReportingProxy) |
| 141 , m_webScheduler(nullptr) | |
| 142 , m_terminationEvent(adoptPtr(new WaitableEvent( | 138 , m_terminationEvent(adoptPtr(new WaitableEvent( |
| 143 WaitableEvent::ResetPolicy::Manual, | 139 WaitableEvent::ResetPolicy::Manual, |
| 144 WaitableEvent::InitialState::NonSignaled))) | 140 WaitableEvent::InitialState::NonSignaled))) |
| 145 , m_shutdownEvent(adoptPtr(new WaitableEvent( | 141 , m_shutdownEvent(adoptPtr(new WaitableEvent( |
| 146 WaitableEvent::ResetPolicy::Manual, | 142 WaitableEvent::ResetPolicy::Manual, |
| 147 WaitableEvent::InitialState::NonSignaled))) | 143 WaitableEvent::InitialState::NonSignaled))) |
| 148 { | 144 { |
| 149 MutexLocker lock(threadSetMutex()); | 145 MutexLocker lock(threadSetMutex()); |
| 150 workerThreads().add(this); | 146 workerThreads().add(this); |
| 151 } | 147 } |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 233 bool success = m_workerGlobalScope->scriptController()->evaluate(ScriptSourc eCode(sourceCode, scriptURL), nullptr, handler, v8CacheOptions); | 229 bool success = m_workerGlobalScope->scriptController()->evaluate(ScriptSourc eCode(sourceCode, scriptURL), nullptr, handler, v8CacheOptions); |
| 234 m_workerGlobalScope->didEvaluateWorkerScript(); | 230 m_workerGlobalScope->didEvaluateWorkerScript(); |
| 235 m_workerReportingProxy.didEvaluateWorkerScript(success); | 231 m_workerReportingProxy.didEvaluateWorkerScript(success); |
| 236 | 232 |
| 237 postInitialize(); | 233 postInitialize(); |
| 238 } | 234 } |
| 239 | 235 |
| 240 void WorkerThread::shutdown() | 236 void WorkerThread::shutdown() |
| 241 { | 237 { |
| 242 DCHECK(isCurrentThread()); | 238 DCHECK(isCurrentThread()); |
| 243 { | 239 prepareForShutdown(); |
| 244 MutexLocker lock(m_threadStateMutex); | |
| 245 if (m_shutdown) | |
| 246 return; | |
| 247 m_shutdown = true; | |
| 248 } | |
| 249 | |
| 250 // This should be called before we start the shutdown procedure. | |
| 251 workerReportingProxy().willDestroyWorkerGlobalScope(); | |
| 252 | |
| 253 workerGlobalScope()->dispose(); | |
| 254 | |
| 255 workerBackingThread().backingThread().removeTaskObserver(m_microtaskRunner.g et()); | |
| 256 postTask(BLINK_FROM_HERE, createSameThreadTask(&WorkerThread::performShutdow nTask, this)); | 240 postTask(BLINK_FROM_HERE, createSameThreadTask(&WorkerThread::performShutdow nTask, this)); |
| 257 } | 241 } |
| 258 | 242 |
| 259 void WorkerThread::performShutdownTask() | 243 void WorkerThread::performShutdownTask() |
| 260 { | 244 { |
| 261 DCHECK(isCurrentThread()); | 245 DCHECK(isCurrentThread()); |
|
yhirano
2016/05/23 10:05:19
It might be good to assert m_terminated && m_ready
nhiroki
2016/05/24 06:50:43
Done.
| |
| 262 | 246 |
| 263 // The below assignment will destroy the context, which will in turn notify | 247 // The below assignment will destroy the context, which will in turn notify |
| 264 // messaging proxy. We cannot let any objects survive past thread exit, | 248 // messaging proxy. We cannot let any objects survive past thread exit, |
| 265 // because no other thread will run GC or otherwise destroy them. If Oilpan | 249 // because no other thread will run GC or otherwise destroy them. If Oilpan |
| 266 // is enabled, we detach of the context/global scope, with the final heap | 250 // is enabled, we detach of the context/global scope, with the final heap |
| 267 // cleanup below sweeping it out. | 251 // cleanup below sweeping it out. |
| 268 m_workerGlobalScope->notifyContextDestroyed(); | 252 m_workerGlobalScope->notifyContextDestroyed(); |
| 269 m_workerGlobalScope = nullptr; | 253 m_workerGlobalScope = nullptr; |
| 270 | 254 |
| 271 workerBackingThread().detach(); | 255 workerBackingThread().detach(); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 303 // Keep this lock to prevent WorkerThread instances from being destroyed. | 287 // Keep this lock to prevent WorkerThread instances from being destroyed. |
| 304 MutexLocker lock(threadSetMutex()); | 288 MutexLocker lock(threadSetMutex()); |
| 305 HashSet<WorkerThread*> threads = workerThreads(); | 289 HashSet<WorkerThread*> threads = workerThreads(); |
| 306 for (WorkerThread* thread : threads) | 290 for (WorkerThread* thread : threads) |
| 307 thread->terminateInternal(); | 291 thread->terminateInternal(); |
| 308 | 292 |
| 309 for (WorkerThread* thread : threads) | 293 for (WorkerThread* thread : threads) |
| 310 thread->m_shutdownEvent->wait(); | 294 thread->m_shutdownEvent->wait(); |
| 311 } | 295 } |
| 312 | 296 |
| 313 void WorkerThread::terminateFromWorkerThread() | 297 void WorkerThread::prepareForShutdown() |
| 314 { | 298 { |
| 315 DCHECK(isCurrentThread()); | 299 DCHECK(isCurrentThread()); |
| 316 shutdown(); | 300 { |
| 301 MutexLocker lock(m_threadStateMutex); | |
| 302 if (m_readyToShutdown) | |
| 303 return; | |
| 304 m_readyToShutdown = true; | |
| 305 } | |
| 306 | |
| 307 workerReportingProxy().willDestroyWorkerGlobalScope(); | |
| 308 workerGlobalScope()->dispose(); | |
| 309 workerBackingThread().backingThread().removeTaskObserver(m_microtaskRunner.g et()); | |
| 317 } | 310 } |
| 318 | 311 |
| 319 WorkerGlobalScope* WorkerThread::workerGlobalScope() | 312 WorkerGlobalScope* WorkerThread::workerGlobalScope() |
| 320 { | 313 { |
| 321 DCHECK(isCurrentThread()); | 314 DCHECK(isCurrentThread()); |
| 322 return m_workerGlobalScope.get(); | 315 return m_workerGlobalScope.get(); |
| 323 } | 316 } |
| 324 | 317 |
| 325 bool WorkerThread::terminated() | 318 bool WorkerThread::terminated() |
| 326 { | 319 { |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 338 | 331 |
| 339 // If terminateInternal has already been called, just return. | 332 // If terminateInternal has already been called, just return. |
| 340 if (m_terminated) | 333 if (m_terminated) |
| 341 return; | 334 return; |
| 342 m_terminated = true; | 335 m_terminated = true; |
| 343 | 336 |
| 344 // Signal the thread to notify that the thread's stopping. | 337 // Signal the thread to notify that the thread's stopping. |
| 345 if (m_terminationEvent) | 338 if (m_terminationEvent) |
| 346 m_terminationEvent->signal(); | 339 m_terminationEvent->signal(); |
| 347 | 340 |
| 348 // If the thread has already initiated shutdown, just return. | |
| 349 if (m_shutdown) | |
| 350 return; | |
| 351 | |
| 352 // If the worker thread was never initialized, don't start another | 341 // If the worker thread was never initialized, don't start another |
| 353 // shutdown, but still wait for the thread to signal when shutdown has | 342 // shutdown, but still wait for the thread to signal when shutdown has |
| 354 // completed on initialize(). | 343 // completed on initialize(). |
| 355 if (!m_workerGlobalScope) | 344 if (!m_workerGlobalScope) |
| 356 return; | 345 return; |
| 357 | 346 |
| 358 // Ensure that tasks are being handled by thread event loop. If script | 347 // If |m_readyToShutdown| is set, scriptController() is already disposed. |
| 359 // execution weren't forbidden, a while(1) loop in JS could keep the thread | 348 if (!m_readyToShutdown) { |
| 360 // alive forever. | 349 DCHECK(m_workerGlobalScope->scriptController()); |
| 361 m_workerGlobalScope->scriptController()->willScheduleExecutionTermination(); | 350 |
| 351 // Ensure that tasks are being handled by thread event loop. If script | |
| 352 // execution weren't forbidden, a while(1) loop in JS could keep the thr ead | |
| 353 // alive forever. | |
| 354 m_workerGlobalScope->scriptController()->willScheduleExecutionTerminatio n(); | |
| 355 } | |
| 362 | 356 |
| 363 if (workerBackingThread().workerScriptCount() == 1) { | 357 if (workerBackingThread().workerScriptCount() == 1) { |
| 364 // This condition is not entirely correct because other scripts | 358 // This condition is not entirely correct because other scripts |
| 365 // can be being initialized or terminated simuletaneously. Though this | 359 // can be being initialized or terminated simuletaneously. Though this |
| 366 // function itself is protected by a mutex, it is possible that | 360 // function itself is protected by a mutex, it is possible that |
| 367 // |workerScriptCount()| here is not consistent with that in | 361 // |workerScriptCount()| here is not consistent with that in |
| 368 // |initialize| and |shutdown|. | 362 // |initialize| and |shutdown|. |
| 369 // TODO(yhirano): TerminateExecution should be called more carefully. | 363 // TODO(yhirano): TerminateExecution should be called more carefully. |
| 370 // https://crbug.com/413518 | 364 // https://crbug.com/413518 |
| 371 if (m_runningDebuggerTask) { | 365 if (m_runningDebuggerTask) { |
| 372 // Terminating during debugger task may lead to crash due to heavy | 366 // Terminating during debugger task may lead to crash due to heavy |
| 373 // use of v8 api in debugger. Any debugger task is guaranteed to | 367 // use of v8 api in debugger. Any debugger task is guaranteed to |
| 374 // finish, so we can postpone termination after task has finished. | 368 // finish, so we can postpone termination after task has finished. |
| 375 // Note: m_runningDebuggerTask and m_shouldTerminateV8Execution | 369 // Note: m_runningDebuggerTask and m_shouldTerminateV8Execution |
| 376 // access must be guarded by the lock. | 370 // access must be guarded by the lock. |
| 377 m_shouldTerminateV8Execution = true; | 371 m_shouldTerminateV8Execution = true; |
| 378 } else { | 372 } else { |
| 379 isolate()->TerminateExecution(); | 373 isolate()->TerminateExecution(); |
| 380 } | 374 } |
| 381 } | 375 } |
| 382 | 376 |
| 383 InspectorInstrumentation::allAsyncTasksCanceled(m_workerGlobalScope.get()); | 377 InspectorInstrumentation::allAsyncTasksCanceled(m_workerGlobalScope.get()); |
| 384 m_inspectorTaskRunner->kill(); | 378 m_inspectorTaskRunner->kill(); |
| 385 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi nd(&WorkerThread::shutdown, AllowCrossThreadAccess(this))); | 379 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi nd(&WorkerThread::shutdown, AllowCrossThreadAccess(this))); |
|
yhirano
2016/05/23 10:05:19
Can you post two tasks (prepareForShutdown and per
nhiroki
2016/05/24 06:50:43
Done.
| |
| 386 } | 380 } |
| 387 | 381 |
| 388 v8::Isolate* WorkerThread::isolate() | 382 v8::Isolate* WorkerThread::isolate() |
| 389 { | 383 { |
| 390 return workerBackingThread().isolate(); | 384 return workerBackingThread().isolate(); |
| 391 } | 385 } |
| 392 | 386 |
| 393 bool WorkerThread::isCurrentThread() | 387 bool WorkerThread::isCurrentThread() |
| 394 { | 388 { |
| 395 return m_started && workerBackingThread().backingThread().isCurrentThread(); | 389 return m_started && workerBackingThread().backingThread().isCurrentThread(); |
| 396 } | 390 } |
| 397 | 391 |
| 398 void WorkerThread::postTask(const WebTraceLocation& location, std::unique_ptr<Ex ecutionContextTask> task) | 392 void WorkerThread::postTask(const WebTraceLocation& location, std::unique_ptr<Ex ecutionContextTask> task) |
| 399 { | 393 { |
| 400 workerBackingThread().backingThread().postTask(location, createWorkerThreadT ask(std::move(task), true)); | 394 workerBackingThread().backingThread().postTask(location, createWorkerThreadT ask(std::move(task), true)); |
| 401 } | 395 } |
| 402 | 396 |
| 403 void WorkerThread::runDebuggerTaskDontWait() | 397 void WorkerThread::runDebuggerTaskDontWait() |
| 404 { | 398 { |
| 405 DCHECK(isCurrentThread()); | 399 DCHECK(isCurrentThread()); |
| 406 std::unique_ptr<CrossThreadClosure> task = m_inspectorTaskRunner->takeNextTa sk(InspectorTaskRunner::DontWaitForTask); | 400 std::unique_ptr<CrossThreadClosure> task = m_inspectorTaskRunner->takeNextTa sk(InspectorTaskRunner::DontWaitForTask); |
| 407 if (task) | 401 if (task) |
| 408 (*task)(); | 402 (*task)(); |
| 409 } | 403 } |
| 410 | 404 |
| 411 void WorkerThread::appendDebuggerTask(std::unique_ptr<CrossThreadClosure> task) | 405 void WorkerThread::appendDebuggerTask(std::unique_ptr<CrossThreadClosure> task) |
| 412 { | 406 { |
| 413 { | 407 { |
| 414 MutexLocker lock(m_threadStateMutex); | 408 MutexLocker lock(m_threadStateMutex); |
| 415 if (m_shutdown) | 409 if (m_readyToShutdown) |
| 416 return; | 410 return; |
| 417 } | 411 } |
| 418 m_inspectorTaskRunner->appendTask(threadSafeBind(&WorkerThread::runDebuggerT ask, AllowCrossThreadAccess(this), passed(std::move(task)))); | 412 m_inspectorTaskRunner->appendTask(threadSafeBind(&WorkerThread::runDebuggerT ask, AllowCrossThreadAccess(this), passed(std::move(task)))); |
| 419 { | 413 { |
| 420 MutexLocker lock(m_threadStateMutex); | 414 MutexLocker lock(m_threadStateMutex); |
| 421 if (isolate()) | 415 if (isolate()) |
| 422 m_inspectorTaskRunner->interruptAndRunAllTasksDontWait(isolate()); | 416 m_inspectorTaskRunner->interruptAndRunAllTasksDontWait(isolate()); |
| 423 } | 417 } |
| 424 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi nd(&WorkerThread::runDebuggerTaskDontWait, AllowCrossThreadAccess(this))); | 418 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi nd(&WorkerThread::runDebuggerTaskDontWait, AllowCrossThreadAccess(this))); |
| 425 } | 419 } |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 461 } while (task && m_pausedInDebugger); | 455 } while (task && m_pausedInDebugger); |
| 462 ThreadDebugger::idleFinished(isolate()); | 456 ThreadDebugger::idleFinished(isolate()); |
| 463 } | 457 } |
| 464 | 458 |
| 465 void WorkerThread::stopRunningDebuggerTasksOnPause() | 459 void WorkerThread::stopRunningDebuggerTasksOnPause() |
| 466 { | 460 { |
| 467 m_pausedInDebugger = false; | 461 m_pausedInDebugger = false; |
| 468 } | 462 } |
| 469 | 463 |
| 470 } // namespace blink | 464 } // namespace blink |
| OLD | NEW |