pending_task_safety_flag.h (6805B)
1 /* 2 * Copyright 2020 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #ifndef API_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H_ 12 #define API_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H_ 13 14 #include <utility> 15 16 #include "absl/base/nullability.h" 17 #include "absl/functional/any_invocable.h" 18 #include "api/ref_counted_base.h" 19 #include "api/scoped_refptr.h" 20 #include "api/sequence_checker.h" 21 #include "api/task_queue/task_queue_base.h" 22 #include "rtc_base/system/no_unique_address.h" 23 #include "rtc_base/system/rtc_export.h" 24 25 namespace webrtc { 26 27 // The PendingTaskSafetyFlag and the ScopedTaskSafety are designed to address 28 // the issue where you have a task to be executed later that has references, 29 // but cannot guarantee that the referenced object is alive when the task is 30 // executed. 31 32 // This mechanism can be used with tasks that are created and destroyed 33 // on a single thread / task queue, and with tasks posted to the same 34 // thread/task queue, but tasks can be posted from any thread/TQ. 35 36 // Typical usage: 37 // When posting a task, post a copy (capture by-value in a lambda) of the flag 38 // reference and before performing the work, check the `alive()` state. Abort if 39 // alive() returns `false`: 40 // 41 // class ExampleClass { 42 // .... 43 // scoped_refptr<PendingTaskSafetyFlag> flag = safety_flag_; 44 // my_task_queue_->PostTask( 45 // [flag = std::move(flag), this] { 46 // // Now running on the main thread. 47 // if (!flag->alive()) 48 // return; 49 // MyMethod(); 50 // }); 51 // .... 52 // ~ExampleClass() { 53 // safety_flag_->SetNotAlive(); 54 // } 55 // scoped_refptr<PendingTaskSafetyFlag> safety_flag_ 56 // = PendingTaskSafetyFlag::Create(); 57 // } 58 // 59 // SafeTask makes this check automatic: 60 // 61 // my_task_queue_->PostTask(SafeTask(safety_flag_, [this] { MyMethod(); })); 62 // 63 class RTC_EXPORT PendingTaskSafetyFlag final 64 : public RefCountedNonVirtual<PendingTaskSafetyFlag> { 65 public: 66 static scoped_refptr<PendingTaskSafetyFlag> Create(); 67 68 // Creates a flag, but with its SequenceChecker initially detached. Hence, it 69 // may be created on a different thread than the flag will be used on. 70 static scoped_refptr<PendingTaskSafetyFlag> CreateDetached(); 71 72 // Creates a flag, but with its SequenceChecker explicitly initialized for 73 // a given task queue and the `alive()` flag specified. 74 static scoped_refptr<PendingTaskSafetyFlag> CreateAttachedToTaskQueue( 75 bool alive, 76 TaskQueueBase* absl_nonnull attached_queue); 77 78 // Same as `CreateDetached()` except the initial state of the returned flag 79 // will be `!alive()`. 80 static scoped_refptr<PendingTaskSafetyFlag> CreateDetachedInactive(); 81 82 ~PendingTaskSafetyFlag() = default; 83 84 void SetNotAlive(); 85 // The SetAlive method is intended to support Start/Stop/Restart usecases. 86 // When a class has called SetNotAlive on a flag used for posted tasks, and 87 // decides it wants to post new tasks and have them run, there are two 88 // reasonable ways to do that: 89 // 90 // (i) Use the below SetAlive method. One subtlety is that any task posted 91 // prior to SetNotAlive, and still in the queue, is resurrected and will 92 // run. 93 // 94 // (ii) Create a fresh flag, and just drop the reference to the old one. This 95 // avoids the above problem, and ensures that tasks poster prior to 96 // SetNotAlive stay cancelled. Instead, there's a potential data race on 97 // the flag pointer itself. Some synchronization is required between the 98 // thread overwriting the flag pointer, and the threads that want to post 99 // tasks and therefore read that same pointer. 100 void SetAlive(); 101 bool alive() const; 102 103 protected: 104 explicit PendingTaskSafetyFlag(bool alive) : alive_(alive) {} 105 PendingTaskSafetyFlag(bool alive, TaskQueueBase* absl_nonnull attached_queue) 106 : alive_(alive), main_sequence_(attached_queue) {} 107 108 private: 109 static scoped_refptr<PendingTaskSafetyFlag> CreateInternal(bool alive); 110 111 bool alive_ = true; 112 RTC_NO_UNIQUE_ADDRESS SequenceChecker main_sequence_; 113 }; 114 115 // The ScopedTaskSafety makes using PendingTaskSafetyFlag very simple. 116 // It does automatic PTSF creation and signalling of destruction when the 117 // ScopedTaskSafety instance goes out of scope. 118 // 119 // Example usage: 120 // 121 // my_task_queue->PostTask(SafeTask(scoped_task_safety.flag(), 122 // [this] { 123 // // task goes here 124 // } 125 // 126 // This should be used by the class that wants tasks dropped after destruction. 127 // The requirement is that the instance has to be constructed and destructed on 128 // the same thread as the potentially dropped tasks would be running on. 129 class RTC_EXPORT ScopedTaskSafety final { 130 public: 131 ScopedTaskSafety() = default; 132 explicit ScopedTaskSafety(scoped_refptr<PendingTaskSafetyFlag> flag) 133 : flag_(std::move(flag)) {} 134 ~ScopedTaskSafety() { flag_->SetNotAlive(); } 135 136 // Returns a new reference to the safety flag. 137 scoped_refptr<PendingTaskSafetyFlag> flag() const { return flag_; } 138 139 // Marks the current flag as not-alive and attaches to a new one. 140 void reset(scoped_refptr<PendingTaskSafetyFlag> new_flag = 141 PendingTaskSafetyFlag::Create()) { 142 flag_->SetNotAlive(); 143 flag_ = std::move(new_flag); 144 } 145 146 private: 147 scoped_refptr<PendingTaskSafetyFlag> flag_ = PendingTaskSafetyFlag::Create(); 148 }; 149 150 // Like ScopedTaskSafety, but allows construction on a different thread than 151 // where the flag will be used. 152 class RTC_EXPORT ScopedTaskSafetyDetached final { 153 public: 154 ScopedTaskSafetyDetached() = default; 155 ~ScopedTaskSafetyDetached() { flag_->SetNotAlive(); } 156 157 // Returns a new reference to the safety flag. 158 scoped_refptr<PendingTaskSafetyFlag> flag() const { return flag_; } 159 160 private: 161 scoped_refptr<PendingTaskSafetyFlag> flag_ = 162 PendingTaskSafetyFlag::CreateDetached(); 163 }; 164 165 inline absl::AnyInvocable<void() &&> SafeTask( 166 scoped_refptr<PendingTaskSafetyFlag> flag, 167 absl::AnyInvocable<void() &&> task) { 168 return [flag = std::move(flag), task = std::move(task)]() mutable { 169 if (flag->alive()) { 170 std::move(task)(); 171 } 172 }; 173 } 174 175 // Safely execute an Invocable that can be used multiple times. 176 inline absl::AnyInvocable<void()> SafeInvocable( 177 scoped_refptr<PendingTaskSafetyFlag> flag, 178 absl::AnyInvocable<void()> task) { 179 return [flag = std::move(flag), task = std::move(task)]() mutable { 180 if (flag->alive()) { 181 task(); 182 } 183 }; 184 } 185 186 } // namespace webrtc 187 188 #endif // API_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H_