Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(53)

Side by Side Diff: third_party/WebKit/Source/core/workers/WorkerThread.cpp

Issue 1978163002: Worker: Initiate worker thread shutdown always on the main thread (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: address kinuko's comments Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/WebKit/Source/core/workers/WorkerThread.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/workers/WorkerThread.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698