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