blob: 3b948ca8f195cc02b28643e10ded8877d77f4d73 [file] [log] [blame]
Artem Titovc374d112022-06-16 21:27:45 +02001/*
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
Danil Chapovalova7e15a22022-07-05 16:03:03 +020016#include "absl/functional/any_invocable.h"
Artem Titovc374d112022-06-16 21:27:45 +020017#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
23namespace 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// ....
Danil Chapovalova7e15a22022-07-05 16:03:03 +020041// rtc::scoped_refptr<PendingTaskSafetyFlag> flag = safety_flag_;
42// my_task_queue_->PostTask(
43// [flag = std::move(flag), this] {
Artem Titovc374d112022-06-16 21:27:45 +020044// // Now running on the main thread.
Danil Chapovalova7e15a22022-07-05 16:03:03 +020045// if (!flag->alive())
Artem Titovc374d112022-06-16 21:27:45 +020046// return;
47// MyMethod();
Danil Chapovalova7e15a22022-07-05 16:03:03 +020048// });
Artem Titovc374d112022-06-16 21:27:45 +020049// ....
50// ~ExampleClass() {
Danil Chapovalova7e15a22022-07-05 16:03:03 +020051// safety_flag_->SetNotAlive();
Artem Titovc374d112022-06-16 21:27:45 +020052// }
Danil Chapovalova7e15a22022-07-05 16:03:03 +020053// scoped_refptr<PendingTaskSafetyFlag> safety_flag_
Artem Titovc374d112022-06-16 21:27:45 +020054// = PendingTaskSafetyFlag::Create();
55// }
56//
Danil Chapovalova7e15a22022-07-05 16:03:03 +020057// SafeTask makes this check automatic:
Artem Titovc374d112022-06-16 21:27:45 +020058//
Danil Chapovalova7e15a22022-07-05 16:03:03 +020059// my_task_queue_->PostTask(SafeTask(safety_flag_, [this] { MyMethod(); }));
Artem Titovc374d112022-06-16 21:27:45 +020060//
61class 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:
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//
Artem Titovc374d112022-06-16 21:27:45 +0200109// Example usage:
110//
Danil Chapovalova7e15a22022-07-05 16:03:03 +0200111// my_task_queue->PostTask(SafeTask(scoped_task_safety.flag(),
112// [this] {
Artem Titovc374d112022-06-16 21:27:45 +0200113// // 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.
119class ScopedTaskSafety final {
120 public:
121 ScopedTaskSafety() = default;
122 explicit ScopedTaskSafety(rtc::scoped_refptr<PendingTaskSafetyFlag> flag)
123 : flag_(std::move(flag)) {}
124 ~ScopedTaskSafety() { flag_->SetNotAlive(); }
125
126 // Returns a new reference to the safety 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.
143class ScopedTaskSafetyDetached final {
144 public:
145 ScopedTaskSafetyDetached() = default;
146 ~ScopedTaskSafetyDetached() { flag_->SetNotAlive(); }
147
148 // Returns a new reference to the safety flag.
149 rtc::scoped_refptr<PendingTaskSafetyFlag> flag() const { return flag_; }
150
151 private:
152 rtc::scoped_refptr<PendingTaskSafetyFlag> flag_ =
153 PendingTaskSafetyFlag::CreateDetached();
154};
155
Danil Chapovalova7e15a22022-07-05 16:03:03 +0200156inline 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
Artem Titovc374d112022-06-16 21:27:45 +0200166} // namespace webrtc
167
168#endif // API_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H_