• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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   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.
canceled()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 
94 class V8_EXPORT_PRIVATE Cancelable {
95  public:
Cancelable(CancelableTaskManager * parent)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.
id()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.
Cancel()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.
145 class 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.
Run()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.
164 class 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.
Run(double deadline_in_seconds)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