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 |