1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
|
/*
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef RTC_BASE_CALLBACK_LIST_H_
#define RTC_BASE_CALLBACK_LIST_H_
#include <utility>
#include <vector>
#include "api/function_view.h"
#include "rtc_base/checks.h"
#include "rtc_base/system/inline.h"
#include "rtc_base/system/rtc_export.h"
#include "rtc_base/untyped_function.h"
namespace webrtc {
namespace callback_list_impl {
class RTC_EXPORT CallbackListReceivers {
public:
CallbackListReceivers();
CallbackListReceivers(const CallbackListReceivers&) = delete;
CallbackListReceivers& operator=(const CallbackListReceivers&) = delete;
CallbackListReceivers(CallbackListReceivers&&) = delete;
CallbackListReceivers& operator=(CallbackListReceivers&&) = delete;
~CallbackListReceivers();
template <typename UntypedFunctionArgsT>
RTC_NO_INLINE void AddReceiver(const void* removal_tag,
UntypedFunctionArgsT args) {
RTC_CHECK(!send_in_progress_);
RTC_DCHECK(removal_tag != nullptr);
receivers_.push_back({removal_tag, UntypedFunction::Create(args)});
}
template <typename UntypedFunctionArgsT>
RTC_NO_INLINE void AddReceiver(UntypedFunctionArgsT args) {
RTC_CHECK(!send_in_progress_);
receivers_.push_back({nullptr, UntypedFunction::Create(args)});
}
void RemoveReceivers(const void* removal_tag);
void Foreach(FunctionView<void(UntypedFunction&)> fv);
private:
// Special protected pointer value that's used as a removal_tag for
// receivers that want to unsubscribe from within a callback.
// Note we could use `&receivers_` too, but since it's the first member
// variable of the class, its address will be the same as the instance
// CallbackList instance, so we take an extra step to avoid collision.
const void* pending_removal_tag() const { return &send_in_progress_; }
struct Callback {
const void* removal_tag;
UntypedFunction function;
};
std::vector<Callback> receivers_;
bool send_in_progress_ = false;
};
extern template void CallbackListReceivers::AddReceiver(
const void*,
UntypedFunction::TrivialUntypedFunctionArgs<1>);
extern template void CallbackListReceivers::AddReceiver(
const void*,
UntypedFunction::TrivialUntypedFunctionArgs<2>);
extern template void CallbackListReceivers::AddReceiver(
const void*,
UntypedFunction::TrivialUntypedFunctionArgs<3>);
extern template void CallbackListReceivers::AddReceiver(
const void*,
UntypedFunction::TrivialUntypedFunctionArgs<4>);
extern template void CallbackListReceivers::AddReceiver(
const void*,
UntypedFunction::NontrivialUntypedFunctionArgs);
extern template void CallbackListReceivers::AddReceiver(
const void*,
UntypedFunction::FunctionPointerUntypedFunctionArgs);
extern template void CallbackListReceivers::AddReceiver(
UntypedFunction::TrivialUntypedFunctionArgs<1>);
extern template void CallbackListReceivers::AddReceiver(
UntypedFunction::TrivialUntypedFunctionArgs<2>);
extern template void CallbackListReceivers::AddReceiver(
UntypedFunction::TrivialUntypedFunctionArgs<3>);
extern template void CallbackListReceivers::AddReceiver(
UntypedFunction::TrivialUntypedFunctionArgs<4>);
extern template void CallbackListReceivers::AddReceiver(
UntypedFunction::NontrivialUntypedFunctionArgs);
extern template void CallbackListReceivers::AddReceiver(
UntypedFunction::FunctionPointerUntypedFunctionArgs);
} // namespace callback_list_impl
// A collection of receivers (callable objects) that can be called all at once.
// Optimized for minimal binary size. The template arguments dictate what
// signature the callbacks must have; for example, a CallbackList<int, float>
// will require callbacks with signature void(int, float).
//
// CallbackList is neither copyable nor movable (could easily be made movable if
// necessary). Callbacks must be movable, but need not be copyable.
//
// Usage example:
//
// // Declaration (usually a member variable).
// CallbackList<int, float> foo_;
//
// // Register callbacks. This can be done zero or more times. The
// // callbacks must accept the arguments types listed in the CallbackList's
// // template argument list, and must return void.
// foo_.AddReceiver([...](int a, float b) {...}); // Lambda.
// foo_.AddReceiver(SomeFunction); // Function pointer.
//
// // Call the zero or more receivers, one after the other.
// foo_.Send(17, 3.14);
//
// Callback lifetime considerations
// --------------------------------
//
// CallbackList::AddReceiver() takes ownership of the given callback by moving
// it in place. The callback can be any callable object; in particular, it may
// have a nontrivial destructor, which will be run when the CallbackList is
// destroyed. The callback may thus access data via any type of smart pointer,
// expressing e.g. unique, shared, or weak ownership. Of course, if the data is
// guaranteed to outlive the callback, a plain raw pointer can be used.
//
// Take care when trying to have the callback own reference-counted data. The
// CallbackList will keep the callback alive, and the callback will keep its
// data alive, so as usual with reference-counted ownership, keep an eye out for
// cycles!
//
// Thread safety
// -------------
//
// Like most C++ types, CallbackList is thread compatible: it's not safe to
// access it concurrently from multiple threads, but it can be made safe if it
// is protected by a mutex, for example.
//
// Excercise some care when deciding what mutexes to hold when you call
// CallbackList::Send(). In particular, do not hold mutexes that callbacks may
// need to grab. If a larger object has a CallbackList member and a single mutex
// that protects all of its data members, this may e.g. make it necessary to
// protect its CallbackList with a separate mutex; otherwise, there will be a
// deadlock if the callbacks try to access the object.
//
// CallbackList as a class data member
// -----------------------------------
//
// CallbackList is a normal C++ data type, and should be private when it is a
// data member of a class. For thread safety reasons (see above), it is likely
// best to not have an accessor for the entire CallbackList, and instead only
// allow callers to add callbacks:
//
// template <typename F>
// void AddFooCallback(F&& callback) {
// // Maybe grab a mutex here?
// foo_callbacks_.AddReceiver(std::forward<F>(callback));
// }
//
template <typename... ArgT>
class CallbackList {
public:
CallbackList() = default;
CallbackList(const CallbackList&) = delete;
CallbackList& operator=(const CallbackList&) = delete;
CallbackList(CallbackList&&) = delete;
CallbackList& operator=(CallbackList&&) = delete;
// Adds a new receiver. The receiver (a callable object or a function pointer)
// must be movable, but need not be copyable. Its call signature should be
// `void(ArgT...)`. The removal tag is a pointer to an arbitrary object that
// you own, and that will stay alive until the CallbackList is gone, or until
// all receivers using it as a removal tag have been removed; you can use it
// to remove the receiver.
template <typename F>
void AddReceiver(const void* removal_tag, F&& f) {
receivers_.AddReceiver(
removal_tag,
UntypedFunction::PrepareArgs<void(ArgT...)>(std::forward<F>(f)));
}
// Adds a new receiver with no removal tag.
template <typename F>
void AddReceiver(F&& f) {
receivers_.AddReceiver(
UntypedFunction::PrepareArgs<void(ArgT...)>(std::forward<F>(f)));
}
// Removes all receivers that were added with the given removal tag.
void RemoveReceivers(const void* removal_tag) {
receivers_.RemoveReceivers(removal_tag);
}
// Calls all receivers with the given arguments. While the Send is in
// progress, no method calls are allowed; specifically, this means that the
// callbacks may not do anything with this CallbackList instance.
//
// Note: Receivers are called serially, but not necessarily in the same order
// they were added.
template <typename... ArgU>
void Send(ArgU&&... args) {
receivers_.Foreach([&](UntypedFunction& f) {
f.Call<void(ArgT...)>(std::forward<ArgU>(args)...);
});
}
private:
callback_list_impl::CallbackListReceivers receivers_;
};
} // namespace webrtc
#endif // RTC_BASE_CALLBACK_LIST_H_
|