• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/functional/any_invocable.h"
17 #include "api/ref_counted_base.h"
18 #include "api/scoped_refptr.h"
19 #include "api/sequence_checker.h"
20 #include "rtc_base/checks.h"
21 #include "rtc_base/system/no_unique_address.h"
22 
23 namespace webrtc {
24 
25 // The PendingTaskSafetyFlag and the ScopedTaskSafety are designed to address
26 // the issue where you have a task to be executed later that has references,
27 // but cannot guarantee that the referenced object is alive when the task is
28 // executed.
29 
30 // This mechanism can be used with tasks that are created and destroyed
31 // on a single thread / task queue, and with tasks posted to the same
32 // thread/task queue, but tasks can be posted from any thread/TQ.
33 
34 // Typical usage:
35 // When posting a task, post a copy (capture by-value in a lambda) of the flag
36 // reference and before performing the work, check the `alive()` state. Abort if
37 // alive() returns `false`:
38 //
39 // class ExampleClass {
40 // ....
41 //    rtc::scoped_refptr<PendingTaskSafetyFlag> flag = safety_flag_;
42 //    my_task_queue_->PostTask(
43 //        [flag = std::move(flag), this] {
44 //          // Now running on the main thread.
45 //          if (!flag->alive())
46 //            return;
47 //          MyMethod();
48 //        });
49 //   ....
50 //   ~ExampleClass() {
51 //     safety_flag_->SetNotAlive();
52 //   }
53 //   scoped_refptr<PendingTaskSafetyFlag> safety_flag_
54 //        = PendingTaskSafetyFlag::Create();
55 // }
56 //
57 // SafeTask makes this check automatic:
58 //
59 //   my_task_queue_->PostTask(SafeTask(safety_flag_, [this] { MyMethod(); }));
60 //
61 class PendingTaskSafetyFlag final
62     : public rtc::RefCountedNonVirtual<PendingTaskSafetyFlag> {
63  public:
64   static rtc::scoped_refptr<PendingTaskSafetyFlag> Create();
65 
66   // Creates a flag, but with its SequenceChecker initially detached. Hence, it
67   // may be created on a different thread than the flag will be used on.
68   static rtc::scoped_refptr<PendingTaskSafetyFlag> CreateDetached();
69 
70   // Same as `CreateDetached()` except the initial state of the returned flag
71   // will be `!alive()`.
72   static rtc::scoped_refptr<PendingTaskSafetyFlag> CreateDetachedInactive();
73 
74   ~PendingTaskSafetyFlag() = default;
75 
76   void SetNotAlive();
77   // The SetAlive method is intended to support Start/Stop/Restart usecases.
78   // When a class has called SetNotAlive on a flag used for posted tasks, and
79   // decides it wants to post new tasks and have them run, there are two
80   // reasonable ways to do that:
81   //
82   // (i) Use the below SetAlive method. One subtlety is that any task posted
83   //     prior to SetNotAlive, and still in the queue, is resurrected and will
84   //     run.
85   //
86   // (ii) Create a fresh flag, and just drop the reference to the old one. This
87   //      avoids the above problem, and ensures that tasks poster prior to
88   //      SetNotAlive stay cancelled. Instead, there's a potential data race on
89   //      the flag pointer itself. Some synchronization is required between the
90   //      thread overwriting the flag pointer, and the threads that want to post
91   //      tasks and therefore read that same pointer.
92   void SetAlive();
93   bool alive() const;
94 
95  protected:
PendingTaskSafetyFlag(bool alive)96   explicit PendingTaskSafetyFlag(bool alive) : alive_(alive) {}
97 
98  private:
99   static rtc::scoped_refptr<PendingTaskSafetyFlag> CreateInternal(bool alive);
100 
101   bool alive_ = true;
102   RTC_NO_UNIQUE_ADDRESS SequenceChecker main_sequence_;
103 };
104 
105 // The ScopedTaskSafety makes using PendingTaskSafetyFlag very simple.
106 // It does automatic PTSF creation and signalling of destruction when the
107 // ScopedTaskSafety instance goes out of scope.
108 //
109 // Example usage:
110 //
111 //     my_task_queue->PostTask(SafeTask(scoped_task_safety.flag(),
112 //        [this] {
113 //             // task goes here
114 //        }
115 //
116 // This should be used by the class that wants tasks dropped after destruction.
117 // The requirement is that the instance has to be constructed and destructed on
118 // the same thread as the potentially dropped tasks would be running on.
119 class ScopedTaskSafety final {
120  public:
121   ScopedTaskSafety() = default;
ScopedTaskSafety(rtc::scoped_refptr<PendingTaskSafetyFlag> flag)122   explicit ScopedTaskSafety(rtc::scoped_refptr<PendingTaskSafetyFlag> flag)
123       : flag_(std::move(flag)) {}
~ScopedTaskSafety()124   ~ScopedTaskSafety() { flag_->SetNotAlive(); }
125 
126   // Returns a new reference to the safety flag.
flag()127   rtc::scoped_refptr<PendingTaskSafetyFlag> flag() const { return flag_; }
128 
129   // Marks the current flag as not-alive and attaches to a new one.
130   void reset(rtc::scoped_refptr<PendingTaskSafetyFlag> new_flag =
131                  PendingTaskSafetyFlag::Create()) {
132     flag_->SetNotAlive();
133     flag_ = std::move(new_flag);
134   }
135 
136  private:
137   rtc::scoped_refptr<PendingTaskSafetyFlag> flag_ =
138       PendingTaskSafetyFlag::Create();
139 };
140 
141 // Like ScopedTaskSafety, but allows construction on a different thread than
142 // where the flag will be used.
143 class ScopedTaskSafetyDetached final {
144  public:
145   ScopedTaskSafetyDetached() = default;
~ScopedTaskSafetyDetached()146   ~ScopedTaskSafetyDetached() { flag_->SetNotAlive(); }
147 
148   // Returns a new reference to the safety flag.
flag()149   rtc::scoped_refptr<PendingTaskSafetyFlag> flag() const { return flag_; }
150 
151  private:
152   rtc::scoped_refptr<PendingTaskSafetyFlag> flag_ =
153       PendingTaskSafetyFlag::CreateDetached();
154 };
155 
SafeTask(rtc::scoped_refptr<PendingTaskSafetyFlag> flag,absl::AnyInvocable<void ()&&> task)156 inline absl::AnyInvocable<void() &&> SafeTask(
157     rtc::scoped_refptr<PendingTaskSafetyFlag> flag,
158     absl::AnyInvocable<void() &&> task) {
159   return [flag = std::move(flag), task = std::move(task)]() mutable {
160     if (flag->alive()) {
161       std::move(task)();
162     }
163   };
164 }
165 
166 }  // namespace webrtc
167 
168 #endif  // API_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H_
169