Line data Source code
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 3798234 : : 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 1896364 : 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 3783779 : 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 1884680 : void Run() final {
154 1884680 : if (TryRun()) {
155 1687068 : RunInternal();
156 : }
157 1884399 : }
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 7506 : class CancelableIdleTask : public Cancelable, public IdleTask {
167 : public:
168 : explicit CancelableIdleTask(Isolate* isolate);
169 : explicit CancelableIdleTask(CancelableTaskManager* manager);
170 :
171 : // IdleTask overrides.
172 3753 : void Run(double deadline_in_seconds) final {
173 3753 : if (TryRun()) {
174 3753 : RunInternal(deadline_in_seconds);
175 : }
176 3753 : }
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_
|