| // Copyright 2021 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/services/auction_worklet/debug_command_queue.h" |
| |
| #include "base/threading/sequenced_task_runner_handle.h" |
| |
| namespace auction_worklet { |
| |
| DebugCommandQueue::DebugCommandQueue( |
| scoped_refptr<base::SequencedTaskRunner> v8_runner) |
| : v8_runner_(std::move(v8_runner)), wake_up_(&lock_) {} |
| |
| DebugCommandQueue::~DebugCommandQueue() = default; |
| |
| void DebugCommandQueue::PauseForDebuggerAndRunCommands( |
| int context_group_id, |
| base::OnceClosure abort_helper) { |
| DCHECK(v8_runner_->RunsTasksInCurrentSequence()); |
| DCHECK(abort_helper); |
| |
| base::AutoLock auto_lock(lock_); |
| CHECK(!v8_thread_paused_); |
| DCHECK(!pause_abort_helper_); |
| if (aborted_context_group_ids_.find(context_group_id) != |
| aborted_context_group_ids_.end()) { |
| // Pauses disallowed since worklet is in process of being destroyed |
| return; |
| } |
| |
| v8_thread_paused_ = true; |
| paused_context_group_id_ = context_group_id; |
| pause_abort_helper_ = std::move(abort_helper); |
| while (true) { |
| RunQueueWithLockHeld(); |
| if (v8_thread_paused_) |
| wake_up_.Wait(); |
| else |
| break; |
| } |
| pause_abort_helper_.Reset(); |
| } |
| |
| void DebugCommandQueue::AbortPauses(int context_group_id) { |
| base::AutoLock auto_lock(lock_); |
| aborted_context_group_ids_.insert(context_group_id); |
| |
| if (v8_thread_paused_ && paused_context_group_id_ == context_group_id) { |
| DCHECK(pause_abort_helper_); |
| queue_.push(std::move(pause_abort_helper_)); |
| wake_up_.Signal(); |
| } |
| } |
| |
| void DebugCommandQueue::RecycleContextGroupId(int context_group_id) { |
| base::AutoLock auto_lock(lock_); |
| size_t num_erased = aborted_context_group_ids_.erase(context_group_id); |
| DCHECK_EQ(num_erased, 1u) |
| << "DebugId::AbortDebuggerPauses must be called before ~DebugId."; |
| } |
| |
| void DebugCommandQueue::QuitPauseForDebugger() { |
| // Can be called from any thread. |
| base::AutoLock auto_lock(lock_); |
| v8_thread_paused_ = false; |
| wake_up_.Signal(); |
| } |
| |
| void DebugCommandQueue::QueueTaskForV8Thread(base::OnceClosure task) { |
| DCHECK(task); |
| // Can be called from any thread. |
| base::AutoLock auto_lock(lock_); |
| queue_.push(std::move(task)); |
| if (v8_thread_paused_) { |
| wake_up_.Signal(); |
| } else { |
| PostRunQueue(); |
| } |
| } |
| |
| void DebugCommandQueue::PostRunQueue() EXCLUSIVE_LOCKS_REQUIRED(lock_) { |
| if (!queue_.empty()) { |
| v8_runner_->PostTask(FROM_HERE, |
| base::BindOnce(&DebugCommandQueue::RunQueue, this)); |
| } |
| } |
| |
| void DebugCommandQueue::RunQueue() { |
| DCHECK(v8_runner_->RunsTasksInCurrentSequence()); |
| // Note: one of commands in the queue can cause PauseForDebuggerAndRunCommands |
| // to be entered. This is OK since we pull tasks off one-by-one and run them |
| // w/o a lock held. |
| base::AutoLock auto_lock(lock_); |
| RunQueueWithLockHeld(); |
| } |
| |
| void DebugCommandQueue::RunQueueWithLockHeld() EXCLUSIVE_LOCKS_REQUIRED(lock_) { |
| DCHECK(v8_runner_->RunsTasksInCurrentSequence()); |
| bool was_v8_thread_paused_ = v8_thread_paused_; |
| while (!queue_.empty()) { |
| base::OnceClosure to_run = std::move(queue_.front()); |
| queue_.pop(); |
| { |
| // Relinquish lock for running callback. |
| base::AutoUnlock temporary_unlock(lock_); |
| std::move(to_run).Run(); |
| } |
| // Need to re-asses state here since it may have changed while lock was |
| // released. |
| if (was_v8_thread_paused_ && !v8_thread_paused_) { |
| // QuitPauseForDebugger() was called, do the rest at top-level. |
| PostRunQueue(); |
| return; |
| } |
| } |
| } |
| |
| } // namespace auction_worklet |