/* * 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 #include #include "api/function_view.h" #include "rtc_base/checks.h" #include "rtc_base/system/assume.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 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 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(rtc::FunctionView 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 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 // 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 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 // void AddFooCallback(F&& callback) { // // Maybe grab a mutex here? // foo_callbacks_.AddReceiver(std::forward(callback)); // } // template 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 void AddReceiver(const void* removal_tag, F&& f) { receivers_.AddReceiver( removal_tag, UntypedFunction::PrepareArgs(std::forward(f))); } // Adds a new receiver with no removal tag. template void AddReceiver(F&& f) { receivers_.AddReceiver( UntypedFunction::PrepareArgs(std::forward(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 void Send(ArgU&&... args) { receivers_.Foreach([&](UntypedFunction& f) { f.Call(std::forward(args)...); }); } private: callback_list_impl::CallbackListReceivers receivers_; }; } // namespace webrtc #endif // RTC_BASE_CALLBACK_LIST_H_