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 avoid processing the next task. | |
kinuko
2016/05/24 09:07:48
Could we make this comment a bit clearer, e.g. "to
nhiroki
2016/05/24 13:36:06
Done.
| |
78 m_workerThread->prepareForShutdown(); | |
76 } | 79 } |
77 } | 80 } |
78 } | 81 } |
79 | 82 |
80 private: | 83 private: |
81 // Thread owns the microtask runner; reference remains | 84 // Thread owns the microtask runner; reference remains |
82 // valid for the lifetime of this object. | 85 // valid for the lifetime of this object. |
83 WorkerThread* m_workerThread; | 86 WorkerThread* m_workerThread; |
84 }; | 87 }; |
85 | 88 |
(...skipping 20 matching lines...) Expand all Loading... | |
106 DCHECK(isCurrentThread()); | 109 DCHECK(isCurrentThread()); |
107 WorkerGlobalScope* globalScope = workerGlobalScope(); | 110 WorkerGlobalScope* globalScope = workerGlobalScope(); |
108 // If the thread is terminated before it had a chance initialize (see | 111 // If the thread is terminated before it had a chance initialize (see |
109 // WorkerThread::Initialize()), we mustn't run any of the posted tasks. | 112 // WorkerThread::Initialize()), we mustn't run any of the posted tasks. |
110 if (!globalScope) { | 113 if (!globalScope) { |
111 DCHECK(terminated()); | 114 DCHECK(terminated()); |
112 return; | 115 return; |
113 } | 116 } |
114 | 117 |
115 InspectorInstrumentation::AsyncTask asyncTask(globalScope, task.get(), isIns trumented); | 118 InspectorInstrumentation::AsyncTask asyncTask(globalScope, task.get(), isIns trumented); |
116 task->performTask(globalScope); | 119 task->performTask(globalScope); |
kinuko
2016/05/24 09:07:48
Going back to this method... how do we prevent tas
nhiroki
2016/05/24 13:36:06
We could just return if |m_readyToShutdown| is set
| |
117 } | 120 } |
118 | 121 |
119 std::unique_ptr<CrossThreadClosure> WorkerThread::createWorkerThreadTask(std::un ique_ptr<ExecutionContextTask> task, bool isInstrumented) | 122 std::unique_ptr<CrossThreadClosure> WorkerThread::createWorkerThreadTask(std::un ique_ptr<ExecutionContextTask> task, bool isInstrumented) |
120 { | 123 { |
121 if (isInstrumented) | 124 if (isInstrumented) |
122 isInstrumented = !task->taskNameForInstrumentation().isEmpty(); | 125 isInstrumented = !task->taskNameForInstrumentation().isEmpty(); |
123 if (isInstrumented) { | 126 if (isInstrumented) { |
124 DCHECK(isCurrentThread()); | 127 DCHECK(isCurrentThread()); |
125 InspectorInstrumentation::asyncTaskScheduled(workerGlobalScope(), "Worke r task", task.get()); | 128 InspectorInstrumentation::asyncTaskScheduled(workerGlobalScope(), "Worke r task", task.get()); |
126 } | 129 } |
127 return threadSafeBind(&WorkerThread::performTask, AllowCrossThreadAccess(thi s), passed(std::move(task)), isInstrumented); | 130 return threadSafeBind(&WorkerThread::performTask, AllowCrossThreadAccess(thi s), passed(std::move(task)), isInstrumented); |
128 } | 131 } |
129 | 132 |
130 WorkerThread::WorkerThread(PassRefPtr<WorkerLoaderProxy> workerLoaderProxy, Work erReportingProxy& workerReportingProxy) | 133 WorkerThread::WorkerThread(PassRefPtr<WorkerLoaderProxy> workerLoaderProxy, Work erReportingProxy& workerReportingProxy) |
131 : m_started(false) | 134 : 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) | 135 , m_workerLoaderProxy(workerLoaderProxy) |
139 , m_workerReportingProxy(workerReportingProxy) | 136 , m_workerReportingProxy(workerReportingProxy) |
140 , m_terminationEvent(adoptPtr(new WaitableEvent( | 137 , m_terminationEvent(adoptPtr(new WaitableEvent( |
141 WaitableEvent::ResetPolicy::Manual, | 138 WaitableEvent::ResetPolicy::Manual, |
142 WaitableEvent::InitialState::NonSignaled))) | 139 WaitableEvent::InitialState::NonSignaled))) |
143 , m_shutdownEvent(adoptPtr(new WaitableEvent( | 140 , m_shutdownEvent(adoptPtr(new WaitableEvent( |
144 WaitableEvent::ResetPolicy::Manual, | 141 WaitableEvent::ResetPolicy::Manual, |
145 WaitableEvent::InitialState::NonSignaled))) | 142 WaitableEvent::InitialState::NonSignaled))) |
146 { | 143 { |
147 MutexLocker lock(threadSetMutex()); | 144 MutexLocker lock(threadSetMutex()); |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
227 } | 224 } |
228 | 225 |
229 CachedMetadataHandler* handler = workerGlobalScope()->createWorkerScriptCach edMetadataHandler(scriptURL, cachedMetaData.get()); | 226 CachedMetadataHandler* handler = workerGlobalScope()->createWorkerScriptCach edMetadataHandler(scriptURL, cachedMetaData.get()); |
230 bool success = m_workerGlobalScope->scriptController()->evaluate(ScriptSourc eCode(sourceCode, scriptURL), nullptr, handler, v8CacheOptions); | 227 bool success = m_workerGlobalScope->scriptController()->evaluate(ScriptSourc eCode(sourceCode, scriptURL), nullptr, handler, v8CacheOptions); |
231 m_workerGlobalScope->didEvaluateWorkerScript(); | 228 m_workerGlobalScope->didEvaluateWorkerScript(); |
232 m_workerReportingProxy.didEvaluateWorkerScript(success); | 229 m_workerReportingProxy.didEvaluateWorkerScript(success); |
233 | 230 |
234 postInitialize(); | 231 postInitialize(); |
235 } | 232 } |
236 | 233 |
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() | 234 void WorkerThread::performShutdownTask() |
kinuko
2016/05/24 09:07:48
nit: maybe we could rename this to performShutdown
nhiroki
2016/05/24 13:36:06
I'll make a separate CL.
nhiroki
2016/05/25 01:58:33
Uploaded a CL: https://codereview.chromium.org/201
| |
258 { | 235 { |
259 DCHECK(isCurrentThread()); | 236 DCHECK(isCurrentThread()); |
237 #if DCHECK_IS_ON | |
238 { | |
239 MutexLocker lock(m_threadStateMutex); | |
240 DCHECK(m_terminated); | |
241 DCHECK(m_readyToShutdown); | |
242 } | |
243 #endif | |
260 | 244 |
261 // The below assignment will destroy the context, which will in turn notify | 245 // The below assignment will destroy the context, which will in turn notify |
262 // messaging proxy. We cannot let any objects survive past thread exit, | 246 // 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 | 247 // 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 | 248 // is enabled, we detach of the context/global scope, with the final heap |
265 // cleanup below sweeping it out. | 249 // cleanup below sweeping it out. |
266 m_workerGlobalScope->notifyContextDestroyed(); | 250 m_workerGlobalScope->notifyContextDestroyed(); |
267 m_workerGlobalScope = nullptr; | 251 m_workerGlobalScope = nullptr; |
268 | 252 |
269 workerBackingThread().detach(); | 253 workerBackingThread().detach(); |
(...skipping 21 matching lines...) Expand all Loading... | |
291 | 275 |
292 // If terminate has already been called, just return. | 276 // If terminate has already been called, just return. |
293 if (m_terminated) | 277 if (m_terminated) |
294 return; | 278 return; |
295 m_terminated = true; | 279 m_terminated = true; |
296 | 280 |
297 // Signal the thread to notify that the thread's stopping. | 281 // Signal the thread to notify that the thread's stopping. |
298 if (m_terminationEvent) | 282 if (m_terminationEvent) |
299 m_terminationEvent->signal(); | 283 m_terminationEvent->signal(); |
300 | 284 |
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 | 285 // If the worker thread was never initialized, don't start another |
306 // shutdown, but still wait for the thread to signal when shutdown has | 286 // shutdown, but still wait for the thread to signal when shutdown has |
307 // completed on initialize(). | 287 // completed on initialize(). |
308 if (!m_workerGlobalScope) | 288 if (!m_workerGlobalScope) |
309 return; | 289 return; |
310 | 290 |
311 // Ensure that tasks are being handled by thread event loop. If script | 291 // If |m_readyToShutdown| is set, scriptController() is already disposed. |
312 // execution weren't forbidden, a while(1) loop in JS could keep the thread | 292 if (!m_readyToShutdown) { |
313 // alive forever. | 293 DCHECK(m_workerGlobalScope->scriptController()); |
314 m_workerGlobalScope->scriptController()->willScheduleExecutionTermination(); | 294 |
295 // Ensure that tasks are being handled by thread event loop. If script | |
296 // execution weren't forbidden, a while(1) loop in JS could keep the | |
297 // thread alive forever. | |
298 m_workerGlobalScope->scriptController()->willScheduleExecutionTerminatio n(); | |
299 } | |
315 | 300 |
316 if (workerBackingThread().workerScriptCount() == 1) { | 301 if (workerBackingThread().workerScriptCount() == 1) { |
kinuko
2016/05/24 09:07:49
Actually if we've passed m_readyToShutdown we won'
nhiroki
2016/05/24 13:36:06
Right. We can assume that the execution context is
| |
317 // This condition is not entirely correct because other scripts | 302 // This condition is not entirely correct because other scripts |
318 // can be being initialized or terminated simuletaneously. Though this | 303 // can be being initialized or terminated simuletaneously. Though this |
319 // function itself is protected by a mutex, it is possible that | 304 // function itself is protected by a mutex, it is possible that |
320 // |workerScriptCount()| here is not consistent with that in | 305 // |workerScriptCount()| here is not consistent with that in |
321 // |initialize| and |shutdown|. | 306 // |initialize| and |shutdown|. |
322 // TODO(yhirano): TerminateExecution should be called more carefully. | 307 // TODO(yhirano): TerminateExecution should be called more carefully. |
323 // https://crbug.com/413518 | 308 // https://crbug.com/413518 |
324 if (m_runningDebuggerTask) { | 309 if (m_runningDebuggerTask) { |
325 // Terminating during debugger task may lead to crash due to heavy | 310 // Terminating during debugger task may lead to crash due to heavy |
326 // use of v8 api in debugger. Any debugger task is guaranteed to | 311 // use of v8 api in debugger. Any debugger task is guaranteed to |
327 // finish, so we can postpone termination after task has finished. | 312 // finish, so we can postpone termination after task has finished. |
328 // Note: m_runningDebuggerTask and m_shouldTerminateV8Execution | 313 // Note: m_runningDebuggerTask and m_shouldTerminateV8Execution |
329 // access must be guarded by the lock. | 314 // access must be guarded by the lock. |
330 m_shouldTerminateV8Execution = true; | 315 m_shouldTerminateV8Execution = true; |
331 } else { | 316 } else { |
332 isolate()->TerminateExecution(); | 317 isolate()->TerminateExecution(); |
333 } | 318 } |
334 } | 319 } |
335 | 320 |
336 m_inspectorTaskRunner->kill(); | 321 m_inspectorTaskRunner->kill(); |
337 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi nd(&WorkerThread::shutdown, AllowCrossThreadAccess(this))); | 322 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi nd(&WorkerThread::prepareForShutdown, AllowCrossThreadAccess(this))); |
323 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi nd(&WorkerThread::performShutdownTask, AllowCrossThreadAccess(this))); | |
338 } | 324 } |
339 | 325 |
340 void WorkerThread::terminateAndWait() | 326 void WorkerThread::terminateAndWait() |
341 { | 327 { |
342 DCHECK(isMainThread()); | 328 DCHECK(isMainThread()); |
343 terminate(); | 329 terminate(); |
344 m_shutdownEvent->wait(); | 330 m_shutdownEvent->wait(); |
345 } | 331 } |
346 | 332 |
347 void WorkerThread::terminateAndWaitForAllWorkers() | 333 void WorkerThread::terminateAndWaitForAllWorkers() |
348 { | 334 { |
349 DCHECK(isMainThread()); | 335 DCHECK(isMainThread()); |
350 | 336 |
351 // Keep this lock to prevent WorkerThread instances from being destroyed. | 337 // Keep this lock to prevent WorkerThread instances from being destroyed. |
352 MutexLocker lock(threadSetMutex()); | 338 MutexLocker lock(threadSetMutex()); |
353 HashSet<WorkerThread*> threads = workerThreads(); | 339 HashSet<WorkerThread*> threads = workerThreads(); |
354 for (WorkerThread* thread : threads) | 340 for (WorkerThread* thread : threads) |
355 thread->terminate(); | 341 thread->terminate(); |
356 | 342 |
357 for (WorkerThread* thread : threads) | 343 for (WorkerThread* thread : threads) |
358 thread->m_shutdownEvent->wait(); | 344 thread->m_shutdownEvent->wait(); |
359 } | 345 } |
360 | 346 |
361 void WorkerThread::terminateFromWorkerThread() | 347 void WorkerThread::prepareForShutdown() |
362 { | 348 { |
363 DCHECK(isCurrentThread()); | 349 DCHECK(isCurrentThread()); |
364 shutdown(); | 350 { |
351 MutexLocker lock(m_threadStateMutex); | |
352 if (m_readyToShutdown) | |
353 return; | |
354 m_readyToShutdown = true; | |
355 } | |
356 | |
357 workerReportingProxy().willDestroyWorkerGlobalScope(); | |
358 InspectorInstrumentation::allAsyncTasksCanceled(workerGlobalScope()); | |
359 workerGlobalScope()->dispose(); | |
360 workerBackingThread().backingThread().removeTaskObserver(m_microtaskRunner.g et()); | |
365 } | 361 } |
366 | 362 |
367 WorkerGlobalScope* WorkerThread::workerGlobalScope() | 363 WorkerGlobalScope* WorkerThread::workerGlobalScope() |
368 { | 364 { |
369 DCHECK(isCurrentThread()); | 365 DCHECK(isCurrentThread()); |
370 return m_workerGlobalScope.get(); | 366 return m_workerGlobalScope.get(); |
371 } | 367 } |
372 | 368 |
373 bool WorkerThread::terminated() | 369 bool WorkerThread::terminated() |
374 { | 370 { |
(...skipping 21 matching lines...) Expand all Loading... | |
396 DCHECK(isCurrentThread()); | 392 DCHECK(isCurrentThread()); |
397 std::unique_ptr<CrossThreadClosure> task = m_inspectorTaskRunner->takeNextTa sk(InspectorTaskRunner::DontWaitForTask); | 393 std::unique_ptr<CrossThreadClosure> task = m_inspectorTaskRunner->takeNextTa sk(InspectorTaskRunner::DontWaitForTask); |
398 if (task) | 394 if (task) |
399 (*task)(); | 395 (*task)(); |
400 } | 396 } |
401 | 397 |
402 void WorkerThread::appendDebuggerTask(std::unique_ptr<CrossThreadClosure> task) | 398 void WorkerThread::appendDebuggerTask(std::unique_ptr<CrossThreadClosure> task) |
403 { | 399 { |
404 { | 400 { |
405 MutexLocker lock(m_threadStateMutex); | 401 MutexLocker lock(m_threadStateMutex); |
406 if (m_shutdown) | 402 if (m_readyToShutdown) |
407 return; | 403 return; |
408 } | 404 } |
409 m_inspectorTaskRunner->appendTask(threadSafeBind(&WorkerThread::runDebuggerT ask, AllowCrossThreadAccess(this), passed(std::move(task)))); | 405 m_inspectorTaskRunner->appendTask(threadSafeBind(&WorkerThread::runDebuggerT ask, AllowCrossThreadAccess(this), passed(std::move(task)))); |
410 { | 406 { |
411 MutexLocker lock(m_threadStateMutex); | 407 MutexLocker lock(m_threadStateMutex); |
412 if (isolate()) | 408 if (isolate()) |
413 m_inspectorTaskRunner->interruptAndRunAllTasksDontWait(isolate()); | 409 m_inspectorTaskRunner->interruptAndRunAllTasksDontWait(isolate()); |
414 } | 410 } |
415 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi nd(&WorkerThread::runDebuggerTaskDontWait, AllowCrossThreadAccess(this))); | 411 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi nd(&WorkerThread::runDebuggerTaskDontWait, AllowCrossThreadAccess(this))); |
416 } | 412 } |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
452 } while (task && m_pausedInDebugger); | 448 } while (task && m_pausedInDebugger); |
453 ThreadDebugger::idleFinished(isolate()); | 449 ThreadDebugger::idleFinished(isolate()); |
454 } | 450 } |
455 | 451 |
456 void WorkerThread::stopRunningDebuggerTasksOnPause() | 452 void WorkerThread::stopRunningDebuggerTasksOnPause() |
457 { | 453 { |
458 m_pausedInDebugger = false; | 454 m_pausedInDebugger = false; |
459 } | 455 } |
460 | 456 |
461 } // namespace blink | 457 } // namespace blink |
OLD | NEW |