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