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