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

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

Issue 2011763002: Worker: Attempt to gracefully terminate WorkerThread as much as possible (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@reorder_functions
Patch Set: Created 4 years, 7 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
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 24 matching lines...) Expand all
35 #include "core/inspector/WorkerThreadDebugger.h" 35 #include "core/inspector/WorkerThreadDebugger.h"
36 #include "core/workers/WorkerBackingThread.h" 36 #include "core/workers/WorkerBackingThread.h"
37 #include "core/workers/WorkerClients.h" 37 #include "core/workers/WorkerClients.h"
38 #include "core/workers/WorkerReportingProxy.h" 38 #include "core/workers/WorkerReportingProxy.h"
39 #include "core/workers/WorkerThreadStartupData.h" 39 #include "core/workers/WorkerThreadStartupData.h"
40 #include "platform/ThreadSafeFunctional.h" 40 #include "platform/ThreadSafeFunctional.h"
41 #include "platform/WaitableEvent.h" 41 #include "platform/WaitableEvent.h"
42 #include "platform/WebThreadSupportingGC.h" 42 #include "platform/WebThreadSupportingGC.h"
43 #include "platform/heap/SafePoint.h" 43 #include "platform/heap/SafePoint.h"
44 #include "platform/heap/ThreadState.h" 44 #include "platform/heap/ThreadState.h"
45 #include "platform/scheduler/CancellableTaskFactory.h"
45 #include "platform/weborigin/KURL.h" 46 #include "platform/weborigin/KURL.h"
46 #include "public/platform/WebThread.h" 47 #include "public/platform/WebThread.h"
47 #include "wtf/Functional.h" 48 #include "wtf/Functional.h"
48 #include "wtf/Noncopyable.h" 49 #include "wtf/Noncopyable.h"
49 #include "wtf/text/WTFString.h" 50 #include "wtf/text/WTFString.h"
50 #include <limits.h> 51 #include <limits.h>
51 52
52 namespace blink { 53 namespace blink {
53 54
55 const long long kForceTerminationDelayInMs = 3000; // 3 secs
56
57 // ForceTerminationTask is used for posting a delayed task to terminate an
58 // isolate from the main thread. This task is expected to run when the shutdown
59 // sequence does not start in a certain time period because of an inifite loop
60 // in the JS execution context etc. When the shutdown sequence is started before
61 // this task runs, the task is simply cancelled.
62 class WorkerThread::ForceTerminationTask {
yhirano 2016/05/27 11:04:43 +final
nhiroki 2016/05/30 06:18:47 Done.
63 public:
64 static PassOwnPtr<ForceTerminationTask> create(WorkerThread* workerThread)
65 {
66 return adoptPtr(new ForceTerminationTask(workerThread));
67 }
68
69 ~ForceTerminationTask()
70 {
71 DCHECK(isMainThread());
72 if (m_cancellableTaskFactory->isPending())
73 m_cancellableTaskFactory->cancel();
yhirano 2016/05/27 11:04:43 Is this needed? Doesn't m_cancellableTaskFactory c
nhiroki 2016/05/30 06:18:47 Ah, it's not necessary. Removed.
74 }
75
76 void schedule()
77 {
78 DCHECK(isMainThread());
79 Platform::current()->mainThread()->getWebTaskRunner()->postDelayedTask(B LINK_FROM_HERE, m_cancellableTaskFactory->cancelAndCreate(), m_workerThread->m_f orceTerminationDelayInMs);
80 }
81
82 private:
83 explicit ForceTerminationTask(WorkerThread* workerThread)
84 : m_workerThread(workerThread)
85 {
86 DCHECK(isMainThread());
87 m_cancellableTaskFactory = CancellableTaskFactory::create(this, &ForceTe rminationTask::run);
88 }
89
90 void run()
91 {
92 DCHECK(isMainThread());
93 MutexLocker lock(m_workerThread->m_threadStateMutex);
94 if (m_workerThread->m_readyToShutdown) {
95 // Shutdown sequence is now running. Just return.
96 return;
97 }
98
99 // TODO(yhirano): TerminateExecution should be called more carefully.
100 // (https://crbug.com/413518)
yhirano 2016/05/27 11:04:43 Is this comment still needed?
nhiroki 2016/05/30 06:18:47 Removed.
101 m_workerThread->m_workerGlobalScope->scriptController()->willScheduleExe cutionTermination();
102 m_workerThread->isolate()->TerminateExecution();
103 DCHECK_EQ(WorkerThread::ExitCode::NotTerminated, m_workerThread->m_exitC ode);
104 m_workerThread->m_exitCode = WorkerThread::ExitCode::AsyncForciblyTermin ated;
105 }
106
107 WorkerThread* m_workerThread;
108 OwnPtr<CancellableTaskFactory> m_cancellableTaskFactory;
109 };
110
54 class WorkerThread::WorkerMicrotaskRunner : public WebThread::TaskObserver { 111 class WorkerThread::WorkerMicrotaskRunner : public WebThread::TaskObserver {
55 public: 112 public:
56 explicit WorkerMicrotaskRunner(WorkerThread* workerThread) 113 explicit WorkerMicrotaskRunner(WorkerThread* workerThread)
57 : m_workerThread(workerThread) 114 : m_workerThread(workerThread)
58 { 115 {
59 } 116 }
60 117
61 void willProcessTask() override 118 void willProcessTask() override
62 { 119 {
63 // No tasks should get executed after we have closed. 120 // No tasks should get executed after we have closed.
(...skipping 29 matching lines...) Expand all
93 } 150 }
94 151
95 static HashSet<WorkerThread*>& workerThreads() 152 static HashSet<WorkerThread*>& workerThreads()
96 { 153 {
97 DEFINE_STATIC_LOCAL(HashSet<WorkerThread*>, threads, ()); 154 DEFINE_STATIC_LOCAL(HashSet<WorkerThread*>, threads, ());
98 return threads; 155 return threads;
99 } 156 }
100 157
101 WorkerThread::~WorkerThread() 158 WorkerThread::~WorkerThread()
102 { 159 {
160 DCHECK(isMainThread());
103 MutexLocker lock(threadSetMutex()); 161 MutexLocker lock(threadSetMutex());
104 DCHECK(workerThreads().contains(this)); 162 DCHECK(workerThreads().contains(this));
105 workerThreads().remove(this); 163 workerThreads().remove(this);
164
165 // TODO(nhiroki): Record how this thread is terminated (i.e. m_exitCode)
166 // in UMA.
167 DCHECK_NE(ExitCode::NotTerminated, m_exitCode);
106 } 168 }
107 169
108 void WorkerThread::start(PassOwnPtr<WorkerThreadStartupData> startupData) 170 void WorkerThread::start(PassOwnPtr<WorkerThreadStartupData> startupData)
109 { 171 {
110 DCHECK(isMainThread()); 172 DCHECK(isMainThread());
111 173
112 if (m_started) 174 if (m_started)
113 return; 175 return;
114 176
115 m_started = true; 177 m_started = true;
116 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi nd(&WorkerThread::initializeOnWorkerThread, AllowCrossThreadAccess(this), passed (std::move(startupData)))); 178 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi nd(&WorkerThread::initializeOnWorkerThread, AllowCrossThreadAccess(this), passed (std::move(startupData))));
117 } 179 }
118 180
119 void WorkerThread::terminate() 181 void WorkerThread::terminate()
120 { 182 {
121 DCHECK(isMainThread()); 183 DCHECK(isMainThread());
122 184 terminateInternal(TerminationMode::Graceful);
123 // Prevent the deadlock between GC and an attempt to terminate a thread.
124 SafePointScope safePointScope(BlinkGC::HeapPointersOnStack);
125
126 // Protect against this method, initializeOnWorkerThread() or termination
127 // via the global scope racing each other.
128 MutexLocker lock(m_threadStateMutex);
129
130 // If terminate has already been called, just return.
131 if (m_terminated)
132 return;
133 m_terminated = true;
134
135 // Signal the thread to notify that the thread's stopping.
136 if (m_terminationEvent)
137 m_terminationEvent->signal();
138
139 // If the worker thread was never initialized, don't start another
140 // shutdown, but still wait for the thread to signal when shutdown has
141 // completed on initializeOnWorkerThread().
142 if (!m_workerGlobalScope)
143 return;
144
145 // Determine if we should terminate the isolate so that the task can
146 // be handled by thread event loop. If script execution weren't forbidden,
147 // a while(1) loop in JS could keep the thread alive forever.
148 //
149 // (1) |m_readyToShutdown|: It this is set, the worker thread has already
150 // noticed that the thread is about to be terminated and the worker global
151 // scope is already disposed, so we don't have to explicitly terminate the
152 // isolate.
153 //
154 // (2) |workerScriptCount() == 1|: If other WorkerGlobalScopes are running
155 // on the worker thread, we should not terminate the isolate. This condition
156 // is not entirely correct because other scripts can be being initialized or
157 // terminated simuletaneously. Though this function itself is protected by a
158 // mutex, it is possible that |workerScriptCount()| here is not consistent
159 // with that in |initialize| and |shutdown|.
160 //
161 // (3) |m_runningDebuggerTask|: Terminating during debugger task may lead to
162 // crash due to heavy use of v8 api in debugger. Any debugger task is
163 // guaranteed to finish, so we can wait for the completion.
164 bool shouldTerminateIsolate = !m_readyToShutdown && (workerBackingThread().w orkerScriptCount() == 1) && !m_runningDebuggerTask;
165
166 if (shouldTerminateIsolate) {
167 // TODO(yhirano): TerminateExecution should be called more
168 // carefully (https://crbug.com/413518).
169 m_workerGlobalScope->scriptController()->willScheduleExecutionTerminatio n();
170 isolate()->TerminateExecution();
171 }
172
173 m_inspectorTaskRunner->kill();
174 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi nd(&WorkerThread::prepareForShutdownOnWorkerThread, AllowCrossThreadAccess(this) ));
175 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi nd(&WorkerThread::performShutdownOnWorkerThread, AllowCrossThreadAccess(this)));
176 } 185 }
177 186
178 void WorkerThread::terminateAndWait() 187 void WorkerThread::terminateAndWait()
179 { 188 {
180 DCHECK(isMainThread()); 189 DCHECK(isMainThread());
181 terminate(); 190
191 // The main thread will be blocked, so asynchronous graceful shutdown does
192 // not work.
193 terminateInternal(TerminationMode::Force);
182 m_shutdownEvent->wait(); 194 m_shutdownEvent->wait();
183 } 195 }
184 196
185 void WorkerThread::terminateAndWaitForAllWorkers() 197 void WorkerThread::terminateAndWaitForAllWorkers()
186 { 198 {
187 DCHECK(isMainThread()); 199 DCHECK(isMainThread());
188 200
189 // Keep this lock to prevent WorkerThread instances from being destroyed. 201 // Keep this lock to prevent WorkerThread instances from being destroyed.
190 MutexLocker lock(threadSetMutex()); 202 MutexLocker lock(threadSetMutex());
191 HashSet<WorkerThread*> threads = workerThreads(); 203 HashSet<WorkerThread*> threads = workerThreads();
204
205 // The main thread will be blocked, so asynchronous graceful shutdown does
206 // not work.
192 for (WorkerThread* thread : threads) 207 for (WorkerThread* thread : threads)
193 thread->terminate(); 208 thread->terminateInternal(TerminationMode::Force);
194 209
195 for (WorkerThread* thread : threads) 210 for (WorkerThread* thread : threads)
196 thread->m_shutdownEvent->wait(); 211 thread->m_shutdownEvent->wait();
197 } 212 }
198 213
199 v8::Isolate* WorkerThread::isolate() 214 v8::Isolate* WorkerThread::isolate()
200 { 215 {
201 return workerBackingThread().isolate(); 216 return workerBackingThread().isolate();
202 } 217 }
203 218
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
268 } 283 }
269 284
270 PlatformThreadId WorkerThread::platformThreadId() 285 PlatformThreadId WorkerThread::platformThreadId()
271 { 286 {
272 if (!m_started) 287 if (!m_started)
273 return 0; 288 return 0;
274 return workerBackingThread().backingThread().platformThread().threadId(); 289 return workerBackingThread().backingThread().platformThread().threadId();
275 } 290 }
276 291
277 WorkerThread::WorkerThread(PassRefPtr<WorkerLoaderProxy> workerLoaderProxy, Work erReportingProxy& workerReportingProxy) 292 WorkerThread::WorkerThread(PassRefPtr<WorkerLoaderProxy> workerLoaderProxy, Work erReportingProxy& workerReportingProxy)
278 : m_inspectorTaskRunner(adoptPtr(new InspectorTaskRunner())) 293 : m_forceTerminationDelayInMs(kForceTerminationDelayInMs)
294 , m_inspectorTaskRunner(adoptPtr(new InspectorTaskRunner()))
279 , m_workerLoaderProxy(workerLoaderProxy) 295 , m_workerLoaderProxy(workerLoaderProxy)
280 , m_workerReportingProxy(workerReportingProxy) 296 , m_workerReportingProxy(workerReportingProxy)
281 , m_terminationEvent(adoptPtr(new WaitableEvent( 297 , m_terminationEvent(adoptPtr(new WaitableEvent(
282 WaitableEvent::ResetPolicy::Manual, 298 WaitableEvent::ResetPolicy::Manual,
283 WaitableEvent::InitialState::NonSignaled))) 299 WaitableEvent::InitialState::NonSignaled)))
284 , m_shutdownEvent(adoptPtr(new WaitableEvent( 300 , m_shutdownEvent(adoptPtr(new WaitableEvent(
285 WaitableEvent::ResetPolicy::Manual, 301 WaitableEvent::ResetPolicy::Manual,
286 WaitableEvent::InitialState::NonSignaled))) 302 WaitableEvent::InitialState::NonSignaled)))
287 { 303 {
288 MutexLocker lock(threadSetMutex()); 304 MutexLocker lock(threadSetMutex());
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
354 } 370 }
355 371
356 CachedMetadataHandler* handler = workerGlobalScope()->createWorkerScriptCach edMetadataHandler(scriptURL, cachedMetaData.get()); 372 CachedMetadataHandler* handler = workerGlobalScope()->createWorkerScriptCach edMetadataHandler(scriptURL, cachedMetaData.get());
357 bool success = m_workerGlobalScope->scriptController()->evaluate(ScriptSourc eCode(sourceCode, scriptURL), nullptr, handler, v8CacheOptions); 373 bool success = m_workerGlobalScope->scriptController()->evaluate(ScriptSourc eCode(sourceCode, scriptURL), nullptr, handler, v8CacheOptions);
358 m_workerGlobalScope->didEvaluateWorkerScript(); 374 m_workerGlobalScope->didEvaluateWorkerScript();
359 m_workerReportingProxy.didEvaluateWorkerScript(success); 375 m_workerReportingProxy.didEvaluateWorkerScript(success);
360 376
361 postInitialize(); 377 postInitialize();
362 } 378 }
363 379
380 void WorkerThread::terminateInternal(TerminationMode mode)
381 {
382 // Prevent the deadlock between GC and an attempt to terminate a thread.
383 SafePointScope safePointScope(BlinkGC::HeapPointersOnStack);
384
385 // Protect against this method, initializeOnWorkerThread() or termination
386 // via the global scope racing each other.
387 MutexLocker lock(m_threadStateMutex);
388
389 // If terminate has already been called, just return.
390 if (m_terminated)
391 return;
392 m_terminated = true;
393
394 // Signal the thread to notify that the thread's stopping.
395 if (m_terminationEvent)
396 m_terminationEvent->signal();
397
398 // If the worker thread was never initialized, don't start another
399 // shutdown, but still wait for the thread to signal when shutdown has
400 // completed on initializeOnWorkerThread().
401 if (!m_workerGlobalScope) {
402 DCHECK_EQ(ExitCode::NotTerminated, m_exitCode);
403 m_exitCode = ExitCode::GracefullyTerminated;
404 return;
405 }
406
407 // Determine if we should synchronously terminate or schedule to terminate
408 // the isolate so that the task can be handled by thread event loop. If
409 // script execution weren't forbidden, a while(1) loop in JS could keep
410 // the thread alive forever.
411 //
412 // (1) |m_readyToShutdown|: It this is set, the worker thread has already
413 // noticed that the thread is about to be terminated and the worker global
414 // scope is already disposed, so we don't have to explicitly terminate the
415 // isolate.
416 //
417 // (2) |workerScriptCount() == 1|: If other WorkerGlobalScopes are running
418 // on the worker thread, we should not terminate the isolate. This condition
419 // is not entirely correct because other scripts can be being initialized or
420 // terminated simuletaneously. Though this function itself is protected by a
421 // mutex, it is possible that |workerScriptCount()| here is not consistent
422 // with that in |initialize| and |shutdown|.
423 //
424 // (3) |m_runningDebuggerTask|: Terminating during debugger task may lead to
425 // crash due to heavy use of v8 api in debugger. Any debugger task is
426 // guaranteed to finish, so we can wait for the completion.
427 bool shouldScheduleToTerminateIsolate = !m_readyToShutdown && (workerBacking Thread().workerScriptCount() == 1) && !m_runningDebuggerTask;
428
429 if (shouldScheduleToTerminateIsolate) {
430 if (mode == TerminationMode::Force) {
431 // TODO(yhirano): TerminateExecution should be called more
432 // carefully (https://crbug.com/413518).
433 m_workerGlobalScope->scriptController()->willScheduleExecutionTermin ation();
434 isolate()->TerminateExecution();
435 DCHECK_EQ(ExitCode::NotTerminated, m_exitCode);
436 m_exitCode = ExitCode::SyncForciblyTerminated;
437 } else {
438 DCHECK_EQ(TerminationMode::Graceful, mode);
439 m_scheduledForceTerminationTask = ForceTerminationTask::create(this) ;
yhirano 2016/05/27 11:04:43 I'm not sure if we should avoid scheduling the tas
nhiroki 2016/05/30 06:18:47 If we should avoid scheduling it on CW, we could a
flackr 2016/05/30 14:41:38 It seems like compositor worker currently isn't ev
nhiroki 2016/05/30 22:15:12 I understand the current CW behavior. Thank you fo
440 m_scheduledForceTerminationTask->schedule();
441 }
442 }
443
444 m_inspectorTaskRunner->kill();
445 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi nd(&WorkerThread::prepareForShutdownOnWorkerThread, AllowCrossThreadAccess(this) ));
446 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi nd(&WorkerThread::performShutdownOnWorkerThread, AllowCrossThreadAccess(this)));
447 }
448
364 void WorkerThread::prepareForShutdownOnWorkerThread() 449 void WorkerThread::prepareForShutdownOnWorkerThread()
365 { 450 {
366 DCHECK(isCurrentThread()); 451 DCHECK(isCurrentThread());
367 { 452 {
368 MutexLocker lock(m_threadStateMutex); 453 MutexLocker lock(m_threadStateMutex);
369 if (m_readyToShutdown) 454 if (m_readyToShutdown)
370 return; 455 return;
371 m_readyToShutdown = true; 456 m_readyToShutdown = true;
457 if (m_exitCode == ExitCode::NotTerminated)
458 m_exitCode = ExitCode::GracefullyTerminated;
372 } 459 }
373 460
374 workerReportingProxy().willDestroyWorkerGlobalScope(); 461 workerReportingProxy().willDestroyWorkerGlobalScope();
375 InspectorInstrumentation::allAsyncTasksCanceled(workerGlobalScope()); 462 InspectorInstrumentation::allAsyncTasksCanceled(workerGlobalScope());
376 workerGlobalScope()->dispose(); 463 workerGlobalScope()->dispose();
377 workerBackingThread().backingThread().removeTaskObserver(m_microtaskRunner.g et()); 464 workerBackingThread().backingThread().removeTaskObserver(m_microtaskRunner.g et());
378 } 465 }
379 466
380 void WorkerThread::performShutdownOnWorkerThread() 467 void WorkerThread::performShutdownOnWorkerThread()
381 { 468 {
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
453 } 540 }
454 541
455 void WorkerThread::runDebuggerTaskDontWaitOnWorkerThread() 542 void WorkerThread::runDebuggerTaskDontWaitOnWorkerThread()
456 { 543 {
457 DCHECK(isCurrentThread()); 544 DCHECK(isCurrentThread());
458 std::unique_ptr<CrossThreadClosure> task = m_inspectorTaskRunner->takeNextTa sk(InspectorTaskRunner::DontWaitForTask); 545 std::unique_ptr<CrossThreadClosure> task = m_inspectorTaskRunner->takeNextTa sk(InspectorTaskRunner::DontWaitForTask);
459 if (task) 546 if (task)
460 (*task)(); 547 (*task)();
461 } 548 }
462 549
550 WorkerThread::ExitCode WorkerThread::exitCode()
551 {
552 MutexLocker lock(m_threadStateMutex);
553 return m_exitCode;
554 }
555
463 } // namespace blink 556 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698