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