| /* |
| * Copyright (C) 2008 Apple Inc. All Rights Reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| */ |
| |
| #include "core/workers/WorkerThread.h" |
| |
| #include <limits.h> |
| #include <memory> |
| #include "bindings/core/v8/ScriptSourceCode.h" |
| #include "bindings/core/v8/WorkerOrWorkletScriptController.h" |
| #include "core/inspector/ConsoleMessageStorage.h" |
| #include "core/inspector/InspectorTaskRunner.h" |
| #include "core/inspector/WorkerInspectorController.h" |
| #include "core/inspector/WorkerThreadDebugger.h" |
| #include "core/probe/CoreProbes.h" |
| #include "core/workers/GlobalScopeCreationParams.h" |
| #include "core/workers/ThreadedWorkletGlobalScope.h" |
| #include "core/workers/WorkerBackingThread.h" |
| #include "core/workers/WorkerClients.h" |
| #include "core/workers/WorkerGlobalScope.h" |
| #include "core/workers/WorkerReportingProxy.h" |
| #include "platform/CrossThreadFunctional.h" |
| #include "platform/Histogram.h" |
| #include "platform/WaitableEvent.h" |
| #include "platform/WebThreadSupportingGC.h" |
| #include "platform/bindings/Microtask.h" |
| #include "platform/heap/SafePoint.h" |
| #include "platform/heap/ThreadState.h" |
| #include "platform/runtime_enabled_features.h" |
| #include "platform/scheduler/child/webthread_impl_for_worker_scheduler.h" |
| #include "platform/scheduler/child/worker_global_scope_scheduler.h" |
| #include "platform/weborigin/KURL.h" |
| #include "platform/wtf/Functional.h" |
| #include "platform/wtf/PtrUtil.h" |
| #include "platform/wtf/Threading.h" |
| #include "platform/wtf/text/WTFString.h" |
| #include "public/platform/Platform.h" |
| #include "public/platform/TaskType.h" |
| |
| namespace blink { |
| |
| using ExitCode = WorkerThread::ExitCode; |
| |
| namespace { |
| |
| // TODO(nhiroki): Adjust the delay based on UMA. |
| constexpr TimeDelta kForcibleTerminationDelay = TimeDelta::FromSeconds(2); |
| |
| } // namespace |
| |
| static Mutex& ThreadSetMutex() { |
| DEFINE_THREAD_SAFE_STATIC_LOCAL(Mutex, mutex, ()); |
| return mutex; |
| } |
| |
| static int GetNextWorkerThreadId() { |
| DCHECK(IsMainThread()); |
| static int next_worker_thread_id = 1; |
| CHECK_LT(next_worker_thread_id, std::numeric_limits<int>::max()); |
| return next_worker_thread_id++; |
| } |
| |
| WorkerThread::~WorkerThread() { |
| DCHECK(IsMainThread()); |
| MutexLocker lock(ThreadSetMutex()); |
| DCHECK(WorkerThreads().Contains(this)); |
| WorkerThreads().erase(this); |
| |
| DCHECK_NE(ExitCode::kNotTerminated, exit_code_); |
| DEFINE_THREAD_SAFE_STATIC_LOCAL( |
| EnumerationHistogram, exit_code_histogram, |
| ("WorkerThread.ExitCode", static_cast<int>(ExitCode::kLastEnum))); |
| exit_code_histogram.Count(static_cast<int>(exit_code_)); |
| } |
| |
| void WorkerThread::Start( |
| std::unique_ptr<GlobalScopeCreationParams> global_scope_creation_params, |
| const WTF::Optional<WorkerBackingThreadStartupData>& thread_startup_data, |
| WorkerInspectorProxy::PauseOnWorkerStart pause_on_start, |
| ParentFrameTaskRunners* parent_frame_task_runners) { |
| DCHECK(IsMainThread()); |
| DCHECK(!parent_frame_task_runners_); |
| parent_frame_task_runners_ = parent_frame_task_runners; |
| |
| // Synchronously initialize the per-global-scope scheduler to prevent someone |
| // from posting a task to the thread before the scheduler is ready. |
| WaitableEvent waitable_event; |
| GetWorkerBackingThread().BackingThread().PostTask( |
| FROM_HERE, |
| CrossThreadBind(&WorkerThread::InitializeSchedulerOnWorkerThread, |
| CrossThreadUnretained(this), |
| CrossThreadUnretained(&waitable_event))); |
| waitable_event.Wait(); |
| |
| GetWorkerBackingThread().BackingThread().PostTask( |
| FROM_HERE, |
| CrossThreadBind(&WorkerThread::InitializeOnWorkerThread, |
| CrossThreadUnretained(this), |
| WTF::Passed(std::move(global_scope_creation_params)), |
| thread_startup_data, pause_on_start)); |
| } |
| |
| void WorkerThread::EvaluateClassicScript( |
| const KURL& script_url, |
| const String& source_code, |
| std::unique_ptr<Vector<char>> cached_meta_data, |
| const v8_inspector::V8StackTraceId& stack_id) { |
| DCHECK(IsMainThread()); |
| GetTaskRunner(TaskType::kUnthrottled) |
| ->PostTask( |
| FROM_HERE, |
| CrossThreadBind(&WorkerThread::EvaluateClassicScriptOnWorkerThread, |
| CrossThreadUnretained(this), script_url, source_code, |
| WTF::Passed(std::move(cached_meta_data)), stack_id)); |
| } |
| |
| void WorkerThread::ImportModuleScript( |
| const KURL& script_url, |
| network::mojom::FetchCredentialsMode credentials_mode) { |
| DCHECK(IsMainThread()); |
| GetTaskRunner(TaskType::kUnthrottled) |
| ->PostTask(FROM_HERE, CrossThreadBind( |
| &WorkerThread::ImportModuleScriptOnWorkerThread, |
| CrossThreadUnretained(this), script_url, |
| credentials_mode)); |
| } |
| |
| void WorkerThread::Terminate() { |
| DCHECK(IsMainThread()); |
| |
| { |
| MutexLocker lock(thread_state_mutex_); |
| if (requested_to_terminate_) |
| return; |
| requested_to_terminate_ = true; |
| } |
| |
| // Schedule a task to forcibly terminate the script execution in case that the |
| // shutdown sequence does not start on the worker thread in a certain time |
| // period. |
| ScheduleToTerminateScriptExecution(); |
| |
| worker_thread_lifecycle_context_->NotifyContextDestroyed(); |
| inspector_task_runner_->Kill(); |
| |
| GetWorkerBackingThread().BackingThread().PostTask( |
| FROM_HERE, |
| CrossThreadBind(&WorkerThread::PrepareForShutdownOnWorkerThread, |
| CrossThreadUnretained(this))); |
| GetWorkerBackingThread().BackingThread().PostTask( |
| FROM_HERE, CrossThreadBind(&WorkerThread::PerformShutdownOnWorkerThread, |
| CrossThreadUnretained(this))); |
| } |
| |
| void WorkerThread::TerminateAllWorkersForTesting() { |
| DCHECK(IsMainThread()); |
| |
| // Keep this lock to prevent WorkerThread instances from being destroyed. |
| MutexLocker lock(ThreadSetMutex()); |
| HashSet<WorkerThread*> threads = WorkerThreads(); |
| |
| for (WorkerThread* thread : threads) { |
| // Schedule a regular async worker thread termination task, and forcibly |
| // terminate the V8 script execution to ensure the task runs. |
| thread->Terminate(); |
| thread->EnsureScriptExecutionTerminates(ExitCode::kSyncForciblyTerminated); |
| } |
| |
| for (WorkerThread* thread : threads) |
| thread->shutdown_event_->Wait(); |
| |
| // Destruct base::Thread and join the underlying system threads. |
| for (WorkerThread* thread : threads) |
| thread->ClearWorkerBackingThread(); |
| } |
| |
| void WorkerThread::WillProcessTask() { |
| DCHECK(IsCurrentThread()); |
| |
| // No tasks should get executed after we have closed. |
| DCHECK(!GlobalScope()->IsClosing()); |
| } |
| |
| void WorkerThread::DidProcessTask() { |
| DCHECK(IsCurrentThread()); |
| Microtask::PerformCheckpoint(GetIsolate()); |
| GlobalScope()->ScriptController()->GetRejectedPromises()->ProcessQueue(); |
| if (GlobalScope()->IsClosing()) { |
| // This WorkerThread will eventually be requested to terminate. |
| GetWorkerReportingProxy().DidCloseWorkerGlobalScope(); |
| |
| // Stop further worker tasks to run after this point. |
| PrepareForShutdownOnWorkerThread(); |
| } else if (IsForciblyTerminated()) { |
| // The script has been terminated forcibly, which means we need to |
| // ask objects in the thread to stop working as soon as possible. |
| PrepareForShutdownOnWorkerThread(); |
| } |
| } |
| |
| v8::Isolate* WorkerThread::GetIsolate() { |
| return GetWorkerBackingThread().GetIsolate(); |
| } |
| |
| bool WorkerThread::IsCurrentThread() { |
| return GetWorkerBackingThread().BackingThread().IsCurrentThread(); |
| } |
| |
| ThreadableLoadingContext* WorkerThread::GetLoadingContext() { |
| DCHECK(IsCurrentThread()); |
| // This should be never called after the termination sequence starts. |
| DCHECK(loading_context_); |
| return loading_context_; |
| } |
| |
| void WorkerThread::AppendDebuggerTask(CrossThreadClosure task) { |
| DCHECK(IsMainThread()); |
| if (requested_to_terminate_) |
| return; |
| inspector_task_runner_->AppendTask(CrossThreadBind( |
| &WorkerThread::PerformDebuggerTaskOnWorkerThread, |
| CrossThreadUnretained(this), WTF::Passed(std::move(task)))); |
| { |
| MutexLocker lock(thread_state_mutex_); |
| if (GetIsolate() && thread_state_ != ThreadState::kReadyToShutdown) |
| inspector_task_runner_->InterruptAndRunAllTasksDontWait(GetIsolate()); |
| } |
| GetTaskRunner(TaskType::kUnthrottled) |
| ->PostTask(FROM_HERE, |
| CrossThreadBind( |
| &WorkerThread::PerformDebuggerTaskDontWaitOnWorkerThread, |
| CrossThreadUnretained(this))); |
| } |
| |
| void WorkerThread::StartRunningDebuggerTasksOnPauseOnWorkerThread() { |
| DCHECK(IsCurrentThread()); |
| if (worker_inspector_controller_) |
| worker_inspector_controller_->FlushProtocolNotifications(); |
| paused_in_debugger_ = true; |
| ThreadDebugger::IdleStarted(GetIsolate()); |
| do { |
| CrossThreadClosure task = |
| inspector_task_runner_->TakeNextTask(InspectorTaskRunner::kWaitForTask); |
| if (!task) |
| break; |
| std::move(task).Run(); |
| // Keep waiting until execution is resumed. |
| } while (paused_in_debugger_); |
| ThreadDebugger::IdleFinished(GetIsolate()); |
| } |
| |
| void WorkerThread::StopRunningDebuggerTasksOnPauseOnWorkerThread() { |
| DCHECK(IsCurrentThread()); |
| paused_in_debugger_ = false; |
| } |
| |
| WorkerOrWorkletGlobalScope* WorkerThread::GlobalScope() { |
| DCHECK(IsCurrentThread()); |
| return global_scope_.Get(); |
| } |
| |
| WorkerInspectorController* WorkerThread::GetWorkerInspectorController() { |
| DCHECK(IsCurrentThread()); |
| return worker_inspector_controller_.Get(); |
| } |
| |
| unsigned WorkerThread::WorkerThreadCount() { |
| MutexLocker lock(ThreadSetMutex()); |
| return WorkerThreads().size(); |
| } |
| |
| HashSet<WorkerThread*>& WorkerThread::WorkerThreads() { |
| DCHECK(IsMainThread()); |
| DEFINE_STATIC_LOCAL(HashSet<WorkerThread*>, threads, ()); |
| return threads; |
| } |
| |
| PlatformThreadId WorkerThread::GetPlatformThreadId() { |
| return GetWorkerBackingThread().BackingThread().PlatformThread().ThreadId(); |
| } |
| |
| bool WorkerThread::IsForciblyTerminated() { |
| MutexLocker lock(thread_state_mutex_); |
| switch (exit_code_) { |
| case ExitCode::kNotTerminated: |
| case ExitCode::kGracefullyTerminated: |
| return false; |
| case ExitCode::kSyncForciblyTerminated: |
| case ExitCode::kAsyncForciblyTerminated: |
| return true; |
| case ExitCode::kLastEnum: |
| NOTREACHED() << static_cast<int>(exit_code_); |
| return false; |
| } |
| NOTREACHED() << static_cast<int>(exit_code_); |
| return false; |
| } |
| |
| ExitCode WorkerThread::GetExitCodeForTesting() { |
| MutexLocker lock(thread_state_mutex_); |
| return exit_code_; |
| } |
| |
| WorkerThread::WorkerThread(ThreadableLoadingContext* loading_context, |
| WorkerReportingProxy& worker_reporting_proxy) |
| : time_origin_(CurrentTimeTicksInSeconds()), |
| worker_thread_id_(GetNextWorkerThreadId()), |
| forcible_termination_delay_(kForcibleTerminationDelay), |
| inspector_task_runner_(std::make_unique<InspectorTaskRunner>()), |
| loading_context_(loading_context), |
| worker_reporting_proxy_(worker_reporting_proxy), |
| shutdown_event_(WTF::WrapUnique( |
| new WaitableEvent(WaitableEvent::ResetPolicy::kManual, |
| WaitableEvent::InitialState::kNonSignaled))), |
| worker_thread_lifecycle_context_(new WorkerThreadLifecycleContext) { |
| DCHECK(IsMainThread()); |
| MutexLocker lock(ThreadSetMutex()); |
| WorkerThreads().insert(this); |
| } |
| |
| void WorkerThread::ScheduleToTerminateScriptExecution() { |
| DCHECK(!forcible_termination_task_handle_.IsActive()); |
| forcible_termination_task_handle_ = |
| parent_frame_task_runners_->Get(TaskType::kUnspecedTimer) |
| ->PostDelayedCancellableTask( |
| FROM_HERE, |
| WTF::Bind(&WorkerThread::EnsureScriptExecutionTerminates, |
| WTF::Unretained(this), |
| ExitCode::kAsyncForciblyTerminated), |
| forcible_termination_delay_); |
| } |
| |
| bool WorkerThread::ShouldTerminateScriptExecution(const MutexLocker& lock) { |
| DCHECK(IsMainThread()); |
| DCHECK(IsThreadStateMutexLocked(lock)); |
| |
| switch (thread_state_) { |
| case ThreadState::kNotStarted: |
| // Shutdown sequence will surely start during initialization sequence |
| // on the worker thread. Don't have to schedule a termination task. |
| return false; |
| case ThreadState::kRunning: |
| // Terminating during debugger task may lead to crash due to heavy use |
| // of v8 api in debugger. Any debugger task is guaranteed to finish, so |
| // we can wait for the completion. |
| return !running_debugger_task_; |
| case ThreadState::kReadyToShutdown: |
| // Shutdown sequence will surely start soon. Don't have to schedule a |
| // termination task. |
| return false; |
| } |
| NOTREACHED(); |
| return false; |
| } |
| |
| void WorkerThread::EnsureScriptExecutionTerminates(ExitCode exit_code) { |
| DCHECK(IsMainThread()); |
| MutexLocker lock(thread_state_mutex_); |
| if (!ShouldTerminateScriptExecution(lock)) |
| return; |
| |
| DCHECK(exit_code == ExitCode::kSyncForciblyTerminated || |
| exit_code == ExitCode::kAsyncForciblyTerminated); |
| SetExitCode(lock, exit_code); |
| |
| GetIsolate()->TerminateExecution(); |
| forcible_termination_task_handle_.Cancel(); |
| } |
| |
| void WorkerThread::InitializeSchedulerOnWorkerThread( |
| WaitableEvent* waitable_event) { |
| DCHECK(IsCurrentThread()); |
| DCHECK(!global_scope_scheduler_); |
| scheduler::WebThreadImplForWorkerScheduler& web_thread_for_worker = |
| static_cast<scheduler::WebThreadImplForWorkerScheduler&>( |
| GetWorkerBackingThread().BackingThread().PlatformThread()); |
| global_scope_scheduler_ = |
| std::make_unique<scheduler::WorkerGlobalScopeScheduler>( |
| web_thread_for_worker.GetWorkerScheduler()); |
| waitable_event->Signal(); |
| } |
| |
| void WorkerThread::InitializeOnWorkerThread( |
| std::unique_ptr<GlobalScopeCreationParams> global_scope_creation_params, |
| const WTF::Optional<WorkerBackingThreadStartupData>& thread_startup_data, |
| WorkerInspectorProxy::PauseOnWorkerStart pause_on_start) { |
| DCHECK(IsCurrentThread()); |
| DCHECK_EQ(ThreadState::kNotStarted, thread_state_); |
| |
| KURL script_url = global_scope_creation_params->script_url; |
| |
| { |
| MutexLocker lock(thread_state_mutex_); |
| |
| if (IsOwningBackingThread()) { |
| DCHECK(thread_startup_data.has_value()); |
| GetWorkerBackingThread().InitializeOnBackingThread(*thread_startup_data); |
| } else { |
| DCHECK(!thread_startup_data.has_value()); |
| } |
| GetWorkerBackingThread().BackingThread().AddTaskObserver(this); |
| |
| console_message_storage_ = new ConsoleMessageStorage(); |
| global_scope_ = |
| CreateWorkerGlobalScope(std::move(global_scope_creation_params)); |
| worker_reporting_proxy_.DidCreateWorkerGlobalScope(GlobalScope()); |
| worker_inspector_controller_ = WorkerInspectorController::Create(this); |
| |
| // TODO(nhiroki): Handle a case where the script controller fails to |
| // initialize the context. |
| if (GlobalScope()->ScriptController()->InitializeContextIfNeeded( |
| String())) { |
| worker_reporting_proxy_.DidInitializeWorkerContext(); |
| v8::HandleScope handle_scope(GetIsolate()); |
| Platform::Current()->WorkerContextCreated( |
| GlobalScope()->ScriptController()->GetContext()); |
| } |
| |
| SetThreadState(lock, ThreadState::kRunning); |
| } |
| |
| if (pause_on_start == WorkerInspectorProxy::PauseOnWorkerStart::kPause) |
| StartRunningDebuggerTasksOnPauseOnWorkerThread(); |
| |
| if (CheckRequestedToTerminateOnWorkerThread()) { |
| // Stop further worker tasks from running after this point. WorkerThread |
| // was requested to terminate before initialization or during running |
| // debugger tasks. PerformShutdownOnWorkerThread() will be called soon. |
| PrepareForShutdownOnWorkerThread(); |
| return; |
| } |
| } |
| |
| void WorkerThread::EvaluateClassicScriptOnWorkerThread( |
| const KURL& script_url, |
| String source_code, |
| std::unique_ptr<Vector<char>> cached_meta_data, |
| const v8_inspector::V8StackTraceId& stack_id) { |
| DCHECK(GlobalScope()->IsWorkerGlobalScope()); |
| WorkerThreadDebugger* debugger = WorkerThreadDebugger::From(GetIsolate()); |
| debugger->ExternalAsyncTaskStarted(stack_id); |
| GlobalScope()->EvaluateClassicScript(script_url, std::move(source_code), |
| std::move(cached_meta_data)); |
| debugger->ExternalAsyncTaskFinished(stack_id); |
| } |
| |
| void WorkerThread::ImportModuleScriptOnWorkerThread( |
| const KURL& script_url, |
| network::mojom::FetchCredentialsMode credentials_mode) { |
| // Worklets have a different code path to import module scripts. |
| // TODO(nhiroki): Consider excluding this code path from WorkerThread like |
| // Worklets. |
| ToWorkerGlobalScope(GlobalScope()) |
| ->ImportModuleScript(script_url, credentials_mode); |
| } |
| |
| void WorkerThread::PrepareForShutdownOnWorkerThread() { |
| DCHECK(IsCurrentThread()); |
| { |
| MutexLocker lock(thread_state_mutex_); |
| if (thread_state_ == ThreadState::kReadyToShutdown) |
| return; |
| SetThreadState(lock, ThreadState::kReadyToShutdown); |
| if (exit_code_ == ExitCode::kNotTerminated) |
| SetExitCode(lock, ExitCode::kGracefullyTerminated); |
| } |
| |
| inspector_task_runner_->Kill(); |
| GetWorkerReportingProxy().WillDestroyWorkerGlobalScope(); |
| probe::AllAsyncTasksCanceled(GlobalScope()); |
| |
| GlobalScope()->NotifyContextDestroyed(); |
| if (worker_inspector_controller_) { |
| worker_inspector_controller_->Dispose(); |
| worker_inspector_controller_.Clear(); |
| } |
| global_scope_scheduler_->Dispose(); |
| GlobalScope()->Dispose(); |
| global_scope_ = nullptr; |
| |
| console_message_storage_.Clear(); |
| loading_context_.Clear(); |
| GetWorkerBackingThread().BackingThread().RemoveTaskObserver(this); |
| } |
| |
| void WorkerThread::PerformShutdownOnWorkerThread() { |
| DCHECK(IsCurrentThread()); |
| DCHECK(CheckRequestedToTerminateOnWorkerThread()); |
| DCHECK_EQ(ThreadState::kReadyToShutdown, thread_state_); |
| |
| if (IsOwningBackingThread()) |
| GetWorkerBackingThread().ShutdownOnBackingThread(); |
| // We must not touch workerBackingThread() from now on. |
| |
| // Notify the proxy that the WorkerOrWorkletGlobalScope has been disposed |
| // of. This can free this thread object, hence it must not be touched |
| // afterwards. |
| GetWorkerReportingProxy().DidTerminateWorkerThread(); |
| |
| shutdown_event_->Signal(); |
| } |
| |
| void WorkerThread::PerformDebuggerTaskOnWorkerThread(CrossThreadClosure task) { |
| DCHECK(IsCurrentThread()); |
| InspectorTaskRunner::IgnoreInterruptsScope scope( |
| inspector_task_runner_.get()); |
| { |
| MutexLocker lock(thread_state_mutex_); |
| DCHECK_EQ(ThreadState::kRunning, thread_state_); |
| running_debugger_task_ = true; |
| } |
| ThreadDebugger::IdleFinished(GetIsolate()); |
| { |
| DEFINE_THREAD_SAFE_STATIC_LOCAL( |
| CustomCountHistogram, scoped_us_counter, |
| ("WorkerThread.DebuggerTask.Time", 0, 10000000, 50)); |
| ScopedUsHistogramTimer timer(scoped_us_counter); |
| std::move(task).Run(); |
| } |
| ThreadDebugger::IdleStarted(GetIsolate()); |
| { |
| MutexLocker lock(thread_state_mutex_); |
| running_debugger_task_ = false; |
| } |
| } |
| |
| void WorkerThread::PerformDebuggerTaskDontWaitOnWorkerThread() { |
| DCHECK(IsCurrentThread()); |
| CrossThreadClosure task = inspector_task_runner_->TakeNextTask( |
| InspectorTaskRunner::kDontWaitForTask); |
| if (task) |
| std::move(task).Run(); |
| } |
| |
| void WorkerThread::SetThreadState(const MutexLocker& lock, |
| ThreadState next_thread_state) { |
| DCHECK(IsThreadStateMutexLocked(lock)); |
| switch (next_thread_state) { |
| case ThreadState::kNotStarted: |
| NOTREACHED(); |
| return; |
| case ThreadState::kRunning: |
| DCHECK_EQ(ThreadState::kNotStarted, thread_state_); |
| thread_state_ = next_thread_state; |
| return; |
| case ThreadState::kReadyToShutdown: |
| DCHECK_EQ(ThreadState::kRunning, thread_state_); |
| thread_state_ = next_thread_state; |
| return; |
| } |
| } |
| |
| void WorkerThread::SetExitCode(const MutexLocker& lock, ExitCode exit_code) { |
| DCHECK(IsThreadStateMutexLocked(lock)); |
| DCHECK_EQ(ExitCode::kNotTerminated, exit_code_); |
| exit_code_ = exit_code; |
| } |
| |
| bool WorkerThread::IsThreadStateMutexLocked(const MutexLocker& /* unused */) { |
| #if DCHECK_IS_ON() |
| // Mutex::locked() is available only if DCHECK_IS_ON() is true. |
| return thread_state_mutex_.Locked(); |
| #else |
| // Otherwise, believe the given MutexLocker holds |m_threadStateMutex|. |
| return true; |
| #endif |
| } |
| |
| bool WorkerThread::CheckRequestedToTerminateOnWorkerThread() { |
| MutexLocker lock(thread_state_mutex_); |
| return requested_to_terminate_; |
| } |
| |
| } // namespace blink |