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 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
46 #include "platform/scheduler/CancellableTaskFactory.h" | 46 #include "platform/scheduler/CancellableTaskFactory.h" |
47 #include "platform/weborigin/KURL.h" | 47 #include "platform/weborigin/KURL.h" |
48 #include "public/platform/WebThread.h" | 48 #include "public/platform/WebThread.h" |
49 #include "wtf/Functional.h" | 49 #include "wtf/Functional.h" |
50 #include "wtf/Noncopyable.h" | 50 #include "wtf/Noncopyable.h" |
51 #include "wtf/text/WTFString.h" | 51 #include "wtf/text/WTFString.h" |
52 #include <limits.h> | 52 #include <limits.h> |
53 | 53 |
54 namespace blink { | 54 namespace blink { |
55 | 55 |
56 // TODO(nhiroki): Adjust the delay based on UMA. | 56 class WorkerGlobalScopeFactory final : public WorkerOrWorkletThread::GlobalScope
Factory { |
57 const long long kForceTerminationDelayInMs = 2000; // 2 secs | |
58 | |
59 // ForceTerminationTask is used for posting a delayed task to terminate the | |
60 // worker execution from the main thread. This task is expected to run when the | |
61 // shutdown sequence does not start in a certain time period because of an | |
62 // inifite loop in the JS execution context etc. When the shutdown sequence is | |
63 // started before this task runs, the task is simply cancelled. | |
64 class WorkerThread::ForceTerminationTask final { | |
65 public: | 57 public: |
66 static PassOwnPtr<ForceTerminationTask> create(WorkerThread* workerThread) | 58 WorkerOrWorkletGlobalScope* create() const final |
67 { | 59 { |
68 return adoptPtr(new ForceTerminationTask(workerThread)); | 60 return m_workerThread.createWorkerGlobalScope(m_startupData.get()); |
69 } | 61 } |
70 | 62 |
71 void schedule() | 63 WorkerGlobalScopeFactory(WorkerThread& workerThread, PassOwnPtr<WorkerThread
StartupData> startupData) |
72 { | 64 : m_workerThread(workerThread) |
73 DCHECK(isMainThread()); | 65 , m_startupData(std::move(startupData)) { } |
74 Platform::current()->mainThread()->getWebTaskRunner()->postDelayedTask(B
LINK_FROM_HERE, m_cancellableTaskFactory->cancelAndCreate(), m_workerThread->m_f
orceTerminationDelayInMs); | 66 private: |
75 } | |
76 | 67 |
77 private: | 68 WorkerThread& m_workerThread; |
78 explicit ForceTerminationTask(WorkerThread* workerThread) | 69 OwnPtr<WorkerThreadStartupData> m_startupData; |
79 : m_workerThread(workerThread) | |
80 { | |
81 DCHECK(isMainThread()); | |
82 m_cancellableTaskFactory = CancellableTaskFactory::create(this, &ForceTe
rminationTask::run); | |
83 } | |
84 | |
85 void run() | |
86 { | |
87 DCHECK(isMainThread()); | |
88 MutexLocker lock(m_workerThread->m_threadStateMutex); | |
89 if (m_workerThread->m_readyToShutdown) { | |
90 // Shutdown sequence is now running. Just return. | |
91 return; | |
92 } | |
93 | |
94 m_workerThread->forciblyTerminateExecution(); | |
95 DCHECK_EQ(WorkerThread::ExitCode::NotTerminated, m_workerThread->m_exitC
ode); | |
96 m_workerThread->m_exitCode = WorkerThread::ExitCode::AsyncForciblyTermin
ated; | |
97 } | |
98 | |
99 WorkerThread* m_workerThread; | |
100 OwnPtr<CancellableTaskFactory> m_cancellableTaskFactory; | |
101 }; | 70 }; |
102 | 71 |
103 class WorkerThread::WorkerMicrotaskRunner final : public WebThread::TaskObserver
{ | |
104 public: | |
105 explicit WorkerMicrotaskRunner(WorkerThread* workerThread) | |
106 : m_workerThread(workerThread) | |
107 { | |
108 } | |
109 | |
110 void willProcessTask() override | |
111 { | |
112 // No tasks should get executed after we have closed. | |
113 DCHECK(!m_workerThread->workerGlobalScope() || !m_workerThread->workerGl
obalScope()->isClosing()); | |
114 } | |
115 | |
116 void didProcessTask() override | |
117 { | |
118 Microtask::performCheckpoint(m_workerThread->isolate()); | |
119 if (WorkerGlobalScope* globalScope = m_workerThread->workerGlobalScope()
) { | |
120 if (WorkerOrWorkletScriptController* scriptController = globalScope-
>scriptController()) | |
121 scriptController->getRejectedPromises()->processQueue(); | |
122 if (globalScope->isClosing()) { | |
123 // |m_workerThread| will eventually be requested to terminate. | |
124 m_workerThread->workerReportingProxy().workerGlobalScopeClosed()
; | |
125 | |
126 // Stop further worker tasks to run after this point. | |
127 m_workerThread->prepareForShutdownOnWorkerThread(); | |
128 } | |
129 } | |
130 } | |
131 | |
132 private: | |
133 // Thread owns the microtask runner; reference remains | |
134 // valid for the lifetime of this object. | |
135 WorkerThread* m_workerThread; | |
136 }; | |
137 | |
138 static Mutex& threadSetMutex() | |
139 { | |
140 DEFINE_THREAD_SAFE_STATIC_LOCAL(Mutex, mutex, new Mutex); | |
141 return mutex; | |
142 } | |
143 | |
144 static HashSet<WorkerThread*>& workerThreads() | |
145 { | |
146 DEFINE_STATIC_LOCAL(HashSet<WorkerThread*>, threads, ()); | |
147 return threads; | |
148 } | |
149 | |
150 WorkerThread::~WorkerThread() | |
151 { | |
152 DCHECK(isMainThread()); | |
153 MutexLocker lock(threadSetMutex()); | |
154 DCHECK(workerThreads().contains(this)); | |
155 workerThreads().remove(this); | |
156 | |
157 // TODO(nhiroki): Record how this thread is terminated (i.e. m_exitCode) | |
158 // in UMA. | |
159 DCHECK_NE(ExitCode::NotTerminated, m_exitCode); | |
160 } | |
161 | |
162 void WorkerThread::start(PassOwnPtr<WorkerThreadStartupData> startupData) | 72 void WorkerThread::start(PassOwnPtr<WorkerThreadStartupData> startupData) |
163 { | 73 { |
164 DCHECK(isMainThread()); | 74 DCHECK(isMainThread()); |
165 | 75 |
166 if (m_started) | 76 if (m_started) |
167 return; | 77 return; |
168 | 78 |
169 m_started = true; | 79 m_started = true; |
170 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi
nd(&WorkerThread::initializeOnWorkerThread, AllowCrossThreadAccess(this), passed
(std::move(startupData)))); | 80 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi
nd(&WorkerThread::initializeOnWorkerThread, AllowCrossThreadAccess(this), passed
(std::move(startupData)))); |
171 } | 81 } |
172 | 82 |
173 void WorkerThread::terminate() | 83 WorkerThread::WorkerThread(PassRefPtr<WorkerLoaderProxy> workerLoaderProxy, Work
erReportingProxy& workerReportingProxy) |
| 84 : WorkerOrWorkletThread(workerReportingProxy) |
| 85 , m_workerLoaderProxy(workerLoaderProxy) |
174 { | 86 { |
175 DCHECK(isMainThread()); | |
176 terminateInternal(TerminationMode::Graceful); | |
177 } | |
178 | |
179 void WorkerThread::terminateAndWait() | |
180 { | |
181 DCHECK(isMainThread()); | |
182 | |
183 // The main thread will be blocked, so asynchronous graceful shutdown does | |
184 // not work. | |
185 terminateInternal(TerminationMode::Forcible); | |
186 m_shutdownEvent->wait(); | |
187 } | |
188 | |
189 void WorkerThread::terminateAndWaitForAllWorkers() | |
190 { | |
191 DCHECK(isMainThread()); | |
192 | |
193 // Keep this lock to prevent WorkerThread instances from being destroyed. | |
194 MutexLocker lock(threadSetMutex()); | |
195 HashSet<WorkerThread*> threads = workerThreads(); | |
196 | |
197 // The main thread will be blocked, so asynchronous graceful shutdown does | |
198 // not work. | |
199 for (WorkerThread* thread : threads) | |
200 thread->terminateInternal(TerminationMode::Forcible); | |
201 | |
202 for (WorkerThread* thread : threads) | |
203 thread->m_shutdownEvent->wait(); | |
204 } | |
205 | |
206 v8::Isolate* WorkerThread::isolate() | |
207 { | |
208 return workerBackingThread().isolate(); | |
209 } | |
210 | |
211 bool WorkerThread::isCurrentThread() | |
212 { | |
213 return m_started && workerBackingThread().backingThread().isCurrentThread(); | |
214 } | |
215 | |
216 void WorkerThread::postTask(const WebTraceLocation& location, std::unique_ptr<Ex
ecutionContextTask> task) | |
217 { | |
218 workerBackingThread().backingThread().postTask(location, createWorkerThreadT
ask(std::move(task), true)); | |
219 } | |
220 | |
221 void WorkerThread::appendDebuggerTask(std::unique_ptr<CrossThreadClosure> task) | |
222 { | |
223 { | |
224 MutexLocker lock(m_threadStateMutex); | |
225 if (m_readyToShutdown) | |
226 return; | |
227 } | |
228 m_inspectorTaskRunner->appendTask(threadSafeBind(&WorkerThread::runDebuggerT
askOnWorkerThread, AllowCrossThreadAccess(this), passed(std::move(task)))); | |
229 { | |
230 MutexLocker lock(m_threadStateMutex); | |
231 if (isolate()) | |
232 m_inspectorTaskRunner->interruptAndRunAllTasksDontWait(isolate()); | |
233 } | |
234 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi
nd(&WorkerThread::runDebuggerTaskDontWaitOnWorkerThread, AllowCrossThreadAccess(
this))); | |
235 } | |
236 | |
237 void WorkerThread::startRunningDebuggerTasksOnPause() | |
238 { | |
239 m_pausedInDebugger = true; | |
240 ThreadDebugger::idleStarted(isolate()); | |
241 std::unique_ptr<CrossThreadClosure> task; | |
242 do { | |
243 { | |
244 SafePointScope safePointScope(BlinkGC::HeapPointersOnStack); | |
245 task = m_inspectorTaskRunner->takeNextTask(InspectorTaskRunner::Wait
ForTask); | |
246 } | |
247 if (task) | |
248 (*task)(); | |
249 // Keep waiting until execution is resumed. | |
250 } while (task && m_pausedInDebugger); | |
251 ThreadDebugger::idleFinished(isolate()); | |
252 } | |
253 | |
254 void WorkerThread::stopRunningDebuggerTasksOnPause() | |
255 { | |
256 m_pausedInDebugger = false; | |
257 } | |
258 | |
259 WorkerGlobalScope* WorkerThread::workerGlobalScope() | |
260 { | |
261 DCHECK(isCurrentThread()); | |
262 return m_workerGlobalScope.get(); | |
263 } | |
264 | |
265 bool WorkerThread::terminated() | |
266 { | |
267 MutexLocker lock(m_threadStateMutex); | |
268 return m_terminated; | |
269 } | |
270 | |
271 unsigned WorkerThread::workerThreadCount() | |
272 { | |
273 MutexLocker lock(threadSetMutex()); | |
274 return workerThreads().size(); | |
275 } | |
276 | |
277 PlatformThreadId WorkerThread::platformThreadId() | |
278 { | |
279 if (!m_started) | |
280 return 0; | |
281 return workerBackingThread().backingThread().platformThread().threadId(); | |
282 } | |
283 | |
284 WorkerThread::WorkerThread(PassRefPtr<WorkerLoaderProxy> workerLoaderProxy, Work
erReportingProxy& workerReportingProxy) | |
285 : m_forceTerminationDelayInMs(kForceTerminationDelayInMs) | |
286 , m_inspectorTaskRunner(adoptPtr(new InspectorTaskRunner())) | |
287 , m_workerLoaderProxy(workerLoaderProxy) | |
288 , m_workerReportingProxy(workerReportingProxy) | |
289 , m_terminationEvent(adoptPtr(new WaitableEvent( | |
290 WaitableEvent::ResetPolicy::Manual, | |
291 WaitableEvent::InitialState::NonSignaled))) | |
292 , m_shutdownEvent(adoptPtr(new WaitableEvent( | |
293 WaitableEvent::ResetPolicy::Manual, | |
294 WaitableEvent::InitialState::NonSignaled))) | |
295 { | |
296 MutexLocker lock(threadSetMutex()); | |
297 workerThreads().add(this); | |
298 } | |
299 | |
300 std::unique_ptr<CrossThreadClosure> WorkerThread::createWorkerThreadTask(std::un
ique_ptr<ExecutionContextTask> task, bool isInstrumented) | |
301 { | |
302 if (isInstrumented) | |
303 isInstrumented = !task->taskNameForInstrumentation().isEmpty(); | |
304 if (isInstrumented) { | |
305 DCHECK(isCurrentThread()); | |
306 InspectorInstrumentation::asyncTaskScheduled(workerGlobalScope(), "Worke
r task", task.get()); | |
307 } | |
308 return threadSafeBind(&WorkerThread::performTaskOnWorkerThread, AllowCrossTh
readAccess(this), passed(std::move(task)), isInstrumented); | |
309 } | |
310 | |
311 void WorkerThread::terminateInternal(TerminationMode mode) | |
312 { | |
313 DCHECK(m_started); | |
314 | |
315 // Prevent the deadlock between GC and an attempt to terminate a thread. | |
316 SafePointScope safePointScope(BlinkGC::HeapPointersOnStack); | |
317 | |
318 // Protect against this method, initializeOnWorkerThread() or termination | |
319 // via the global scope racing each other. | |
320 MutexLocker lock(m_threadStateMutex); | |
321 | |
322 // If terminate has already been called. | |
323 if (m_terminated) { | |
324 // The synchronous forcible termination request should overtake the | |
325 // scheduled termination task because the request will block the main | |
326 // thread and the scheduled termination task never runs. | |
327 if (mode == TerminationMode::Forcible && m_exitCode == ExitCode::NotTerm
inated) { | |
328 DCHECK(m_scheduledForceTerminationTask); | |
329 m_scheduledForceTerminationTask.reset(); | |
330 forciblyTerminateExecution(); | |
331 DCHECK_EQ(ExitCode::NotTerminated, m_exitCode); | |
332 m_exitCode = ExitCode::SyncForciblyTerminated; | |
333 } | |
334 return; | |
335 } | |
336 m_terminated = true; | |
337 | |
338 // Signal the thread to notify that the thread's stopping. | |
339 if (m_terminationEvent) | |
340 m_terminationEvent->signal(); | |
341 | |
342 // If the worker thread was never initialized, don't start another | |
343 // shutdown, but still wait for the thread to signal when shutdown has | |
344 // completed on initializeOnWorkerThread(). | |
345 if (!m_workerGlobalScope) { | |
346 DCHECK_EQ(ExitCode::NotTerminated, m_exitCode); | |
347 m_exitCode = ExitCode::GracefullyTerminated; | |
348 return; | |
349 } | |
350 | |
351 // Determine if we should synchronously terminate or schedule to terminate | |
352 // the worker execution so that the task can be handled by thread event | |
353 // loop. If script execution weren't forbidden, a while(1) loop in JS could | |
354 // keep the thread alive forever. | |
355 // | |
356 // (1) |m_readyToShutdown|: It this is set, the worker thread has already | |
357 // noticed that the thread is about to be terminated and the worker global | |
358 // scope is already disposed, so we don't have to explicitly terminate the | |
359 // worker execution. | |
360 // | |
361 // (2) |workerScriptCount() == 1|: If other WorkerGlobalScopes are running | |
362 // on the worker thread, we should not terminate the worker execution. This | |
363 // condition is not entirely correct because other scripts can be being | |
364 // initialized or terminated simuletaneously. Though this function itself is | |
365 // protected by a mutex, it is possible that |workerScriptCount()| here is | |
366 // not consistent with that in |initialize| and |shutdown|. | |
367 // | |
368 // (3) |m_runningDebuggerTask|: Terminating during debugger task may lead to | |
369 // crash due to heavy use of v8 api in debugger. Any debugger task is | |
370 // guaranteed to finish, so we can wait for the completion. | |
371 bool shouldScheduleToTerminateExecution = !m_readyToShutdown && (workerBacki
ngThread().workerScriptCount() == 1) && !m_runningDebuggerTask; | |
372 | |
373 if (shouldScheduleToTerminateExecution) { | |
374 if (mode == TerminationMode::Forcible) { | |
375 forciblyTerminateExecution(); | |
376 DCHECK_EQ(ExitCode::NotTerminated, m_exitCode); | |
377 m_exitCode = ExitCode::SyncForciblyTerminated; | |
378 } else { | |
379 DCHECK_EQ(TerminationMode::Graceful, mode); | |
380 DCHECK(!m_scheduledForceTerminationTask); | |
381 m_scheduledForceTerminationTask = ForceTerminationTask::create(this)
; | |
382 m_scheduledForceTerminationTask->schedule(); | |
383 } | |
384 } | |
385 | |
386 m_inspectorTaskRunner->kill(); | |
387 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi
nd(&WorkerThread::prepareForShutdownOnWorkerThread, AllowCrossThreadAccess(this)
)); | |
388 workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBi
nd(&WorkerThread::performShutdownOnWorkerThread, AllowCrossThreadAccess(this))); | |
389 } | |
390 | |
391 void WorkerThread::forciblyTerminateExecution() | |
392 { | |
393 DCHECK(m_workerGlobalScope); | |
394 m_workerGlobalScope->scriptController()->willScheduleExecutionTermination(); | |
395 isolate()->TerminateExecution(); | |
396 } | 87 } |
397 | 88 |
398 void WorkerThread::initializeOnWorkerThread(PassOwnPtr<WorkerThreadStartupData>
startupData) | 89 void WorkerThread::initializeOnWorkerThread(PassOwnPtr<WorkerThreadStartupData>
startupData) |
399 { | 90 { |
400 KURL scriptURL = startupData->m_scriptURL; | 91 KURL scriptURL = startupData->m_scriptURL; |
401 String sourceCode = startupData->m_sourceCode; | 92 String sourceCode = startupData->m_sourceCode; |
402 WorkerThreadStartMode startMode = startupData->m_startMode; | 93 WorkerThreadStartMode startMode = startupData->m_startMode; |
403 OwnPtr<Vector<char>> cachedMetaData = std::move(startupData->m_cachedMetaDat
a); | 94 OwnPtr<Vector<char>> cachedMetaData = std::move(startupData->m_cachedMetaDat
a); |
404 V8CacheOptions v8CacheOptions = startupData->m_v8CacheOptions; | 95 V8CacheOptions v8CacheOptions = startupData->m_v8CacheOptions; |
405 | 96 |
406 { | 97 WorkerGlobalScopeFactory factory(*this, std::move(startupData)); |
407 MutexLocker lock(m_threadStateMutex); | 98 initializeGlobalScope(factory); |
| 99 if (m_terminated) |
| 100 return; |
408 | 101 |
409 // The worker was terminated before the thread had a chance to run. | 102 toWorkerGlobalScope(globalScope())->scriptLoaded(sourceCode.length(), cached
MetaData.get() ? cachedMetaData->size() : 0); |
410 if (m_terminated) { | |
411 DCHECK_EQ(ExitCode::GracefullyTerminated, m_exitCode); | |
412 | |
413 // Notify the proxy that the WorkerGlobalScope has been disposed of. | |
414 // This can free this thread object, hence it must not be touched | |
415 // afterwards. | |
416 m_workerReportingProxy.workerThreadTerminated(); | |
417 | |
418 // Notify the main thread that it is safe to deallocate our | |
419 // resources. | |
420 m_shutdownEvent->signal(); | |
421 return; | |
422 } | |
423 | |
424 workerBackingThread().attach(); | |
425 | |
426 if (shouldAttachThreadDebugger()) | |
427 V8PerIsolateData::from(isolate())->setThreadDebugger(adoptPtr(new Wo
rkerThreadDebugger(this, isolate()))); | |
428 m_microtaskRunner = adoptPtr(new WorkerMicrotaskRunner(this)); | |
429 workerBackingThread().backingThread().addTaskObserver(m_microtaskRunner.
get()); | |
430 | |
431 // Optimize for memory usage instead of latency for the worker isolate. | |
432 isolate()->IsolateInBackgroundNotification(); | |
433 m_workerGlobalScope = createWorkerGlobalScope(std::move(startupData)); | |
434 m_workerGlobalScope->scriptLoaded(sourceCode.length(), cachedMetaData.ge
t() ? cachedMetaData->size() : 0); | |
435 | |
436 // Notify proxy that a new WorkerGlobalScope has been created and starte
d. | |
437 m_workerReportingProxy.workerGlobalScopeStarted(m_workerGlobalScope.get(
)); | |
438 | |
439 WorkerOrWorkletScriptController* scriptController = m_workerGlobalScope-
>scriptController(); | |
440 if (!scriptController->isExecutionForbidden()) { | |
441 scriptController->initializeContextIfNeeded(); | |
442 | |
443 // If Origin Trials have been registered before the V8 context was r
eady, | |
444 // then inject them into the context now | |
445 ExecutionContext* executionContext = m_workerGlobalScope->getExecuti
onContext(); | |
446 if (executionContext) { | |
447 OriginTrialContext* originTrialContext = OriginTrialContext::fro
m(executionContext); | |
448 if (originTrialContext) | |
449 originTrialContext->initializePendingFeatures(); | |
450 } | |
451 } | |
452 } | |
453 | 103 |
454 if (startMode == PauseWorkerGlobalScopeOnStart) | 104 if (startMode == PauseWorkerGlobalScopeOnStart) |
455 startRunningDebuggerTasksOnPause(); | 105 startRunningDebuggerTasksOnPause(); |
456 | 106 |
457 if (m_workerGlobalScope->scriptController()->isContextInitialized()) { | 107 CachedMetadataHandler* handler = toWorkerGlobalScope(globalScope())->createW
orkerScriptCachedMetadataHandler(scriptURL, cachedMetaData.get()); |
458 m_workerReportingProxy.didInitializeWorkerContext(); | 108 bool success = globalScope()->scriptController()->evaluate(ScriptSourceCode(
sourceCode, scriptURL), nullptr, handler, v8CacheOptions); |
459 v8::HandleScope handleScope(isolate()); | 109 toWorkerGlobalScope(globalScope())->didEvaluateWorkerScript(); |
460 Platform::current()->workerContextCreated(m_workerGlobalScope->scriptCon
troller()->context()); | 110 workerReportingProxy().didEvaluateWorkerScript(success); |
461 } | |
462 | |
463 CachedMetadataHandler* handler = workerGlobalScope()->createWorkerScriptCach
edMetadataHandler(scriptURL, cachedMetaData.get()); | |
464 bool success = m_workerGlobalScope->scriptController()->evaluate(ScriptSourc
eCode(sourceCode, scriptURL), nullptr, handler, v8CacheOptions); | |
465 m_workerGlobalScope->didEvaluateWorkerScript(); | |
466 m_workerReportingProxy.didEvaluateWorkerScript(success); | |
467 | 111 |
468 postInitialize(); | 112 postInitialize(); |
469 } | 113 } |
470 | 114 |
471 void WorkerThread::prepareForShutdownOnWorkerThread() | |
472 { | |
473 DCHECK(isCurrentThread()); | |
474 { | |
475 MutexLocker lock(m_threadStateMutex); | |
476 if (m_readyToShutdown) | |
477 return; | |
478 m_readyToShutdown = true; | |
479 if (m_exitCode == ExitCode::NotTerminated) | |
480 m_exitCode = ExitCode::GracefullyTerminated; | |
481 } | |
482 | |
483 workerReportingProxy().willDestroyWorkerGlobalScope(); | |
484 InspectorInstrumentation::allAsyncTasksCanceled(workerGlobalScope()); | |
485 workerGlobalScope()->dispose(); | |
486 workerBackingThread().backingThread().removeTaskObserver(m_microtaskRunner.g
et()); | |
487 } | |
488 | |
489 void WorkerThread::performShutdownOnWorkerThread() | |
490 { | |
491 DCHECK(isCurrentThread()); | |
492 #if DCHECK_IS_ON | |
493 { | |
494 MutexLocker lock(m_threadStateMutex); | |
495 DCHECK(m_terminated); | |
496 DCHECK(m_readyToShutdown); | |
497 } | |
498 #endif | |
499 | |
500 // The below assignment will destroy the context, which will in turn notify | |
501 // messaging proxy. We cannot let any objects survive past thread exit, | |
502 // because no other thread will run GC or otherwise destroy them. If Oilpan | |
503 // is enabled, we detach of the context/global scope, with the final heap | |
504 // cleanup below sweeping it out. | |
505 m_workerGlobalScope->notifyContextDestroyed(); | |
506 m_workerGlobalScope = nullptr; | |
507 | |
508 workerBackingThread().detach(); | |
509 // We must not touch workerBackingThread() from now on. | |
510 | |
511 m_microtaskRunner = nullptr; | |
512 | |
513 // Notify the proxy that the WorkerGlobalScope has been disposed of. | |
514 // This can free this thread object, hence it must not be touched | |
515 // afterwards. | |
516 workerReportingProxy().workerThreadTerminated(); | |
517 | |
518 m_shutdownEvent->signal(); | |
519 } | |
520 | |
521 void WorkerThread::performTaskOnWorkerThread(std::unique_ptr<ExecutionContextTas
k> task, bool isInstrumented) | |
522 { | |
523 DCHECK(isCurrentThread()); | |
524 { | |
525 MutexLocker lock(m_threadStateMutex); | |
526 if (m_readyToShutdown) | |
527 return; | |
528 } | |
529 | |
530 WorkerGlobalScope* globalScope = workerGlobalScope(); | |
531 // If the thread is terminated before it had a chance initialize (see | |
532 // WorkerThread::Initialize()), we mustn't run any of the posted tasks. | |
533 if (!globalScope) { | |
534 DCHECK(terminated()); | |
535 return; | |
536 } | |
537 | |
538 InspectorInstrumentation::AsyncTask asyncTask(globalScope, task.get(), isIns
trumented); | |
539 task->performTask(globalScope); | |
540 } | |
541 | |
542 void WorkerThread::runDebuggerTaskOnWorkerThread(std::unique_ptr<CrossThreadClos
ure> task) | |
543 { | |
544 DCHECK(isCurrentThread()); | |
545 InspectorTaskRunner::IgnoreInterruptsScope scope(m_inspectorTaskRunner.get()
); | |
546 { | |
547 MutexLocker lock(m_threadStateMutex); | |
548 m_runningDebuggerTask = true; | |
549 } | |
550 ThreadDebugger::idleFinished(isolate()); | |
551 (*task)(); | |
552 ThreadDebugger::idleStarted(isolate()); | |
553 { | |
554 MutexLocker lock(m_threadStateMutex); | |
555 m_runningDebuggerTask = false; | |
556 | |
557 if (!m_terminated) | |
558 return; | |
559 // terminate() was called. Shutdown sequence will start soon. | |
560 } | |
561 // Stop further worker tasks to run after this point. | |
562 prepareForShutdownOnWorkerThread(); | |
563 } | |
564 | |
565 void WorkerThread::runDebuggerTaskDontWaitOnWorkerThread() | |
566 { | |
567 DCHECK(isCurrentThread()); | |
568 std::unique_ptr<CrossThreadClosure> task = m_inspectorTaskRunner->takeNextTa
sk(InspectorTaskRunner::DontWaitForTask); | |
569 if (task) | |
570 (*task)(); | |
571 } | |
572 | |
573 WorkerThread::ExitCode WorkerThread::getExitCode() | |
574 { | |
575 MutexLocker lock(m_threadStateMutex); | |
576 return m_exitCode; | |
577 } | |
578 | |
579 } // namespace blink | 115 } // namespace blink |
OLD | NEW |