1// Copyright 2015 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_TASKS_CANCELABLE_TASK_H_
6#define V8_TASKS_CANCELABLE_TASK_H_
7
8#include <atomic>
9#include <unordered_map>
10
11#include "include/v8-platform.h"
12#include "src/base/macros.h"
13#include "src/base/platform/condition-variable.h"
14#include "src/common/globals.h"
15
16namespace v8 {
17namespace internal {
18
19class Cancelable;
20class Isolate;
21
22// The possible outcomes of trying to abort a job are:
23// (1) The task is already finished running or was canceled before and
24//     thus has been removed from the manager.
25// (2) The task is currently running and cannot be canceled anymore.
26// (3) The task is not yet running (or finished) so it is canceled and
27//     removed.
28enum class TryAbortResult { kTaskRemoved, kTaskRunning, kTaskAborted };
29
30// Keeps track of cancelable tasks. It is possible to register and remove tasks
31// from any fore- and background task/thread.
32class V8_EXPORT_PRIVATE CancelableTaskManager {
33 public:
34  using Id = uint64_t;
35  static constexpr Id kInvalidTaskId = 0;
36
37  CancelableTaskManager();
38
39  ~CancelableTaskManager();
40  CancelableTaskManager(const CancelableTaskManager&) = delete;
41  CancelableTaskManager& operator=(const CancelableTaskManager&) = delete;
42
43  // Registers a new cancelable {task}. Returns the unique {id} of the task that
44  // can be used to try to abort a task by calling {Abort}.
45  // If {Register} is called after {CancelAndWait}, then the task will be
46  // aborted immediately.
47  // {Register} should only be called by the thread which owns the
48  // {CancelableTaskManager}, or by a task which is managed by the
49  // {CancelableTaskManager}.
50  Id Register(Cancelable* task);
51
52  // Try to abort running a task identified by {id}.
53  TryAbortResult TryAbort(Id id);
54
55  // Tries to cancel all remaining registered tasks. The return value indicates
56  // whether
57  //
58  // 1) No tasks were registered (kTaskRemoved), or
59  //
60  // 2) There is at least one remaining task that couldn't be cancelled
61  // (kTaskRunning), or
62  //
63  // 3) All registered tasks were cancelled (kTaskAborted).
64  TryAbortResult TryAbortAll();
65
66  // Cancels all remaining registered tasks and waits for tasks that are
67  // already running. This disallows subsequent Register calls.
68  void CancelAndWait();
69
70  // Returns true of the task manager has been cancelled.
71  bool canceled() const { return canceled_; }
72
73 private:
74  // Only called by {Cancelable} destructor. The task is done with executing,
75  // but needs to be removed.
76  void RemoveFinishedTask(Id id);
77
78  // To mitigate the ABA problem, the api refers to tasks through an id.
79  Id task_id_counter_;
80
81  // A set of cancelable tasks that are currently registered.
82  std::unordered_map<Id, Cancelable*> cancelable_tasks_;
83
84  // Mutex and condition variable enabling concurrent register and removing, as
85  // well as waiting for background tasks on {CancelAndWait}.
86  base::ConditionVariable cancelable_tasks_barrier_;
87  base::Mutex mutex_;
88
89  bool canceled_;
90
91  friend class Cancelable;
92};
93
94class V8_EXPORT_PRIVATE Cancelable {
95 public:
96  explicit Cancelable(CancelableTaskManager* parent)
97      : parent_(parent), id_(parent->Register(this)) {}
98
99  virtual ~Cancelable();
100  Cancelable(const Cancelable&) = delete;
101  Cancelable& operator=(const Cancelable&) = delete;
102
103  // Never invoke after handing over the task to the platform! The reason is
104  // that {Cancelable} is used in combination with {v8::Task} and handed to
105  // a platform. This step transfers ownership to the platform, which destroys
106  // the task after running it. Since the exact time is not known, we cannot
107  // access the object after handing it to a platform.
108  CancelableTaskManager::Id id() { return id_; }
109
110 protected:
111  // Identifies the state a cancelable task is in:
112  // |kWaiting|: The task is scheduled and waiting to be executed. {TryRun} will
113  //   succeed.
114  // |kCanceled|: The task has been canceled. {TryRun} will fail.
115  // |kRunning|: The task is currently running and cannot be canceled anymore.
116  enum Status { kWaiting, kCanceled, kRunning };
117
118  bool TryRun(Status* previous = nullptr) {
119    return CompareExchangeStatus(kWaiting, kRunning, previous);
120  }
121
122 private:
123  friend class CancelableTaskManager;
124
125  // Use {CancelableTaskManager} to abort a task that has not yet been
126  // executed.
127  bool Cancel() { return CompareExchangeStatus(kWaiting, kCanceled); }
128
129  bool CompareExchangeStatus(Status expected, Status desired,
130                             Status* previous = nullptr) {
131    // {compare_exchange_strong} updates {expected}.
132    bool success = status_.compare_exchange_strong(expected, desired,
133                                                   std::memory_order_acq_rel,
134                                                   std::memory_order_acquire);
135    if (previous) *previous = expected;
136    return success;
137  }
138
139  CancelableTaskManager* const parent_;
140  std::atomic<Status> status_{kWaiting};
141  const CancelableTaskManager::Id id_;
142};
143
144// Multiple inheritance can be used because Task is a pure interface.
145class V8_EXPORT_PRIVATE CancelableTask : public Cancelable,
146                                         NON_EXPORTED_BASE(public Task) {
147 public:
148  explicit CancelableTask(Isolate* isolate);
149  explicit CancelableTask(CancelableTaskManager* manager);
150  CancelableTask(const CancelableTask&) = delete;
151  CancelableTask& operator=(const CancelableTask&) = delete;
152
153  // Task overrides.
154  void Run() final {
155    if (TryRun()) {
156      RunInternal();
157    }
158  }
159
160  virtual void RunInternal() = 0;
161};
162
163// Multiple inheritance can be used because IdleTask is a pure interface.
164class CancelableIdleTask : public Cancelable, public IdleTask {
165 public:
166  explicit CancelableIdleTask(Isolate* isolate);
167  explicit CancelableIdleTask(CancelableTaskManager* manager);
168  CancelableIdleTask(const CancelableIdleTask&) = delete;
169  CancelableIdleTask& operator=(const CancelableIdleTask&) = delete;
170
171  // IdleTask overrides.
172  void Run(double deadline_in_seconds) final {
173    if (TryRun()) {
174      RunInternal(deadline_in_seconds);
175    }
176  }
177
178  virtual void RunInternal(double deadline_in_seconds) = 0;
179};
180
181}  // namespace internal
182}  // namespace v8
183
184#endif  // V8_TASKS_CANCELABLE_TASK_H_
185