| /* | |
| * 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/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 <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(rtc::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_ |