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