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 <unordered_map> 9 10 #include "include/v8-platform.h" 11 #include "src/base/atomic-utils.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 23 // Keeps track of cancelable tasks. It is possible to register and remove tasks 24 // from any fore- and background task/thread. 25 class V8_EXPORT_PRIVATE CancelableTaskManager { 26 public: 27 using Id = uint64_t; 28 29 CancelableTaskManager(); 30 31 // Registers a new cancelable {task}. Returns the unique {id} of the task that 32 // can be used to try to abort a task by calling {Abort}. 33 // Must not be called after CancelAndWait. 34 Id Register(Cancelable* task); 35 36 // Try to abort running a task identified by {id}. The possible outcomes are: 37 // (1) The task is already finished running or was canceled before and 38 // thus has been removed from the manager. 39 // (2) The task is currently running and cannot be canceled anymore. 40 // (3) The task is not yet running (or finished) so it is canceled and 41 // removed. 42 // 43 enum TryAbortResult { kTaskRemoved, kTaskRunning, kTaskAborted }; 44 TryAbortResult TryAbort(Id id); 45 46 // Cancels all remaining registered tasks and waits for tasks that are 47 // already running. This disallows subsequent Register calls. 48 void CancelAndWait(); 49 50 // Tries to cancel all remaining registered tasks. The return value indicates 51 // whether 52 // 53 // 1) No tasks were registered (kTaskRemoved), or 54 // 55 // 2) There is at least one remaining task that couldn't be cancelled 56 // (kTaskRunning), or 57 // 58 // 3) All registered tasks were cancelled (kTaskAborted). 59 TryAbortResult TryAbortAll(); 60 61 private: 62 // Only called by {Cancelable} destructor. The task is done with executing, 63 // but needs to be removed. 64 void RemoveFinishedTask(Id id); 65 66 // To mitigate the ABA problem, the api refers to tasks through an id. 67 Id task_id_counter_; 68 69 // A set of cancelable tasks that are currently registered. 70 std::unordered_map<Id, Cancelable*> cancelable_tasks_; 71 72 // Mutex and condition variable enabling concurrent register and removing, as 73 // well as waiting for background tasks on {CancelAndWait}. 74 base::ConditionVariable cancelable_tasks_barrier_; 75 base::Mutex mutex_; 76 77 bool canceled_; 78 79 friend class Cancelable; 80 81 DISALLOW_COPY_AND_ASSIGN(CancelableTaskManager); 82 }; 83 84 class V8_EXPORT_PRIVATE Cancelable { 85 public: 86 explicit Cancelable(CancelableTaskManager* parent); 87 virtual ~Cancelable(); 88 89 // Never invoke after handing over the task to the platform! The reason is 90 // that {Cancelable} is used in combination with {v8::Task} and handed to 91 // a platform. This step transfers ownership to the platform, which destroys 92 // the task after running it. Since the exact time is not known, we cannot 93 // access the object after handing it to a platform. id()94 CancelableTaskManager::Id id() { return id_; } 95 96 protected: TryRun()97 bool TryRun() { return status_.TrySetValue(kWaiting, kRunning); } IsRunning()98 bool IsRunning() { return status_.Value() == kRunning; } CancelAttempts()99 intptr_t CancelAttempts() { return cancel_counter_; } 100 101 private: 102 // Identifies the state a cancelable task is in: 103 // |kWaiting|: The task is scheduled and waiting to be executed. {TryRun} will 104 // succeed. 105 // |kCanceled|: The task has been canceled. {TryRun} will fail. 106 // |kRunning|: The task is currently running and cannot be canceled anymore. 107 enum Status { 108 kWaiting, 109 kCanceled, 110 kRunning, 111 }; 112 113 // Use {CancelableTaskManager} to abort a task that has not yet been 114 // executed. Cancel()115 bool Cancel() { 116 if (status_.TrySetValue(kWaiting, kCanceled)) { 117 return true; 118 } 119 cancel_counter_++; 120 return false; 121 } 122 123 CancelableTaskManager* parent_; 124 base::AtomicValue<Status> status_; 125 CancelableTaskManager::Id id_; 126 127 // The counter is incremented for failing tries to cancel a task. This can be 128 // used by the task itself as an indication how often external entities tried 129 // to abort it. 130 std::atomic<intptr_t> cancel_counter_; 131 132 friend class CancelableTaskManager; 133 134 DISALLOW_COPY_AND_ASSIGN(Cancelable); 135 }; 136 137 138 // Multiple inheritance can be used because Task is a pure interface. 139 class V8_EXPORT_PRIVATE CancelableTask : public Cancelable, 140 NON_EXPORTED_BASE(public Task) { 141 public: 142 explicit CancelableTask(Isolate* isolate); 143 explicit CancelableTask(CancelableTaskManager* manager); 144 145 // Task overrides. Run()146 void Run() final { 147 if (TryRun()) { 148 RunInternal(); 149 } 150 } 151 152 virtual void RunInternal() = 0; 153 154 private: 155 DISALLOW_COPY_AND_ASSIGN(CancelableTask); 156 }; 157 158 159 // Multiple inheritance can be used because IdleTask is a pure interface. 160 class CancelableIdleTask : public Cancelable, public IdleTask { 161 public: 162 explicit CancelableIdleTask(Isolate* isolate); 163 explicit CancelableIdleTask(CancelableTaskManager* manager); 164 165 // IdleTask overrides. Run(double deadline_in_seconds)166 void Run(double deadline_in_seconds) final { 167 if (TryRun()) { 168 RunInternal(deadline_in_seconds); 169 } 170 } 171 172 virtual void RunInternal(double deadline_in_seconds) = 0; 173 174 private: 175 DISALLOW_COPY_AND_ASSIGN(CancelableIdleTask); 176 }; 177 178 179 } // namespace internal 180 } // namespace v8 181 182 #endif // V8_CANCELABLE_TASK_H_ 183