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_CANCELABLE_TASK_H_
6#define V8_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/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
36 CancelableTaskManager();
37
38 ~CancelableTaskManager();
39
40 // Registers a new cancelable {task}. Returns the unique {id} of the task that
41 // can be used to try to abort a task by calling {Abort}.
42 // If {Register} is called after {CancelAndWait}, then the task will be
43 // aborted immediately.
44 // {Register} should only be called by the thread which owns the
45 // {CancelableTaskManager}, or by a task which is managed by the
46 // {CancelableTaskManager}.
47 Id Register(Cancelable* task);
48
49 // Try to abort running a task identified by {id}.
50 TryAbortResult TryAbort(Id id);
51
52 // Tries to cancel all remaining registered tasks. The return value indicates
53 // whether
54 //
55 // 1) No tasks were registered (kTaskRemoved), or
56 //
57 // 2) There is at least one remaining task that couldn't be cancelled
58 // (kTaskRunning), or
59 //
60 // 3) All registered tasks were cancelled (kTaskAborted).
61 TryAbortResult TryAbortAll();
62
63 // Cancels all remaining registered tasks and waits for tasks that are
64 // already running. This disallows subsequent Register calls.
65 void CancelAndWait();
66
67 // Returns true of the task manager has been cancelled.
68 bool canceled() const { return canceled_; }
69
70 private:
71 static constexpr Id kInvalidTaskId = 0;
72
73 // Only called by {Cancelable} destructor. The task is done with executing,
74 // but needs to be removed.
75 void RemoveFinishedTask(Id id);
76
77 // To mitigate the ABA problem, the api refers to tasks through an id.
78 Id task_id_counter_;
79
80 // A set of cancelable tasks that are currently registered.
81 std::unordered_map<Id, Cancelable*> cancelable_tasks_;
82
83 // Mutex and condition variable enabling concurrent register and removing, as
84 // well as waiting for background tasks on {CancelAndWait}.
85 base::ConditionVariable cancelable_tasks_barrier_;
86 base::Mutex mutex_;
87
88 bool canceled_;
89
90 friend class Cancelable;
91
92 DISALLOW_COPY_AND_ASSIGN(CancelableTaskManager);
93};
94
95class V8_EXPORT_PRIVATE Cancelable {
96 public:
97 explicit Cancelable(CancelableTaskManager* parent)
98 : parent_(parent), id_(parent->Register(this)) {}
99
100 virtual ~Cancelable();
101
102 // Never invoke after handing over the task to the platform! The reason is
103 // that {Cancelable} is used in combination with {v8::Task} and handed to
104 // a platform. This step transfers ownership to the platform, which destroys
105 // the task after running it. Since the exact time is not known, we cannot
106 // access the object after handing it to a platform.
107 CancelableTaskManager::Id id() { return id_; }
108
109 protected:
110 // Identifies the state a cancelable task is in:
111 // |kWaiting|: The task is scheduled and waiting to be executed. {TryRun} will
112 // succeed.
113 // |kCanceled|: The task has been canceled. {TryRun} will fail.
114 // |kRunning|: The task is currently running and cannot be canceled anymore.
115 enum Status { kWaiting, kCanceled, kRunning };
116
117 bool TryRun(Status* previous = nullptr) {
118 return CompareExchangeStatus(kWaiting, kRunning, previous);
119 }
120
121 private:
122 friend class CancelableTaskManager;
123
124 // Use {CancelableTaskManager} to abort a task that has not yet been
125 // executed.
126 bool Cancel() { return CompareExchangeStatus(kWaiting, kCanceled); }
127
128 bool CompareExchangeStatus(Status expected, Status desired,
129 Status* previous = nullptr) {
130 // {compare_exchange_strong} updates {expected}.
131 bool success = status_.compare_exchange_strong(expected, desired,
132 std::memory_order_acq_rel,
133 std::memory_order_acquire);
134 if (previous) *previous = expected;
135 return success;
136 }
137
138 CancelableTaskManager* const parent_;
139 std::atomic<Status> status_{kWaiting};
140 const CancelableTaskManager::Id id_;
141
142 DISALLOW_COPY_AND_ASSIGN(Cancelable);
143};
144
145// Multiple inheritance can be used because Task is a pure interface.
146class V8_EXPORT_PRIVATE CancelableTask : public Cancelable,
147 NON_EXPORTED_BASE(public Task) {
148 public:
149 explicit CancelableTask(Isolate* isolate);
150 explicit CancelableTask(CancelableTaskManager* manager);
151
152 // Task overrides.
153 void Run() final {
154 if (TryRun()) {
155 RunInternal();
156 }
157 }
158
159 virtual void RunInternal() = 0;
160
161 private:
162 DISALLOW_COPY_AND_ASSIGN(CancelableTask);
163};
164
165// Multiple inheritance can be used because IdleTask is a pure interface.
166class CancelableIdleTask : public Cancelable, public IdleTask {
167 public:
168 explicit CancelableIdleTask(Isolate* isolate);
169 explicit CancelableIdleTask(CancelableTaskManager* manager);
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 private:
181 DISALLOW_COPY_AND_ASSIGN(CancelableIdleTask);
182};
183
184} // namespace internal
185} // namespace v8
186
187#endif // V8_CANCELABLE_TASK_H_
188