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 | |
16 | namespace v8 { |
17 | namespace internal { |
18 | |
19 | class Cancelable; |
20 | class 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. |
28 | enum 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. |
32 | class 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 | |
95 | class 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. |
146 | class 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. |
166 | class 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 | |