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