1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119

content / services / auction_worklet / debug_command_queue.cc [blame]

// Copyright 2021 The Chromium Authors
// 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/containers/contains.h"
#include "base/task/sequenced_task_runner.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 (base::Contains(aborted_context_group_ids_, context_group_id)) {
    // 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