| // Copyright 2018 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef LIB_FIDL_CPP_BINDING_SET_H_ | |
| #define LIB_FIDL_CPP_BINDING_SET_H_ | |
| #include <lib/fit/function.h> | |
| #include <algorithm> | |
| #include <iostream> | |
| #include <memory> | |
| #include <utility> | |
| #include <vector> | |
| #include "lib/fidl/cpp/binding.h" | |
| namespace fidl { | |
| // Manages a set of bindings to implementations owned by the bound channels. | |
| // | |
| // The implementation pointer type of the binding is also parameterized, | |
| // allowing the use of smart pointer types such as |std::unique_ptr<>| to | |
| // reference the implementation. | |
| // | |
| // See also: | |
| // | |
| // * |InterfacePtrSet|, which is the client analog of |BindingSet|. | |
| template <typename Interface, typename ImplPtr = Interface*> | |
| class BindingSet final { | |
| public: | |
| using Binding = ::fidl::Binding<Interface, ImplPtr>; | |
| using StorageType = std::vector<std::unique_ptr<Binding>>; | |
| using ErrorHandler = fit::function<void(zx_status_t)>; | |
| using iterator = typename StorageType::iterator; | |
| BindingSet() = default; | |
| BindingSet(const BindingSet&) = delete; | |
| BindingSet& operator=(const BindingSet&) = delete; | |
| // The implementation of this class provides external references to class members via pointers. | |
| // As a result, instances cannot be move-constructed or move-assigned. | |
| BindingSet(BindingSet&&) = delete; | |
| BindingSet& operator=(BindingSet&&) = delete; | |
| // Adds a binding to the set. | |
| // | |
| // The given |ImplPtr| is bound to the channel underlying the | |
| // |InterfaceRequest|. The binding is removed (and the |~ImplPtr| called) | |
| // when the created binding has an error (e.g., if the remote endpoint of | |
| // the channel sends an invalid message). The binding can also be removed | |
| // by calling |RemoveBinding()|. | |
| // | |
| // Whether this method takes ownership of |impl| depends on |ImplPtr|. If | |
| // |ImplPtr| is a raw pointer, then this method does not take ownership of | |
| // |impl|. If |ImplPtr| is a |unique_ptr|, then running |~ImplPtr| when the | |
| // binding generates an error will delete |impl| because |~ImplPtr| is | |
| // |~unique_ptr|, which deletes |impl|. | |
| void AddBinding(ImplPtr impl, InterfaceRequest<Interface> request, | |
| async_dispatcher_t* dispatcher = nullptr, ErrorHandler handler = nullptr) { | |
| bindings_.push_back( | |
| std::make_unique<Binding>(std::forward<ImplPtr>(impl), std::move(request), dispatcher)); | |
| auto* binding = bindings_.back().get(); | |
| // Set the connection error handler for the newly added Binding to be a | |
| // function that will erase it from the vector. | |
| binding->set_error_handler( | |
| [binding, handler = std::move(handler), this](zx_status_t status) mutable { | |
| // This error handler is not thread-safe and assumes that concurrent calls to the lambda | |
| // are not possible. | |
| // | |
| // All actions done in this lambda must not modify the error handler of |binding|, as | |
| // |binding| owns the captured contents of this lambda. For example, resetting the error | |
| // handler anywhere before the end of this function will cause undefined behavior within | |
| // this function. | |
| std::unique_ptr<Binding> removed_binding = this->RemoveOnError(binding); | |
| // The removed binding must have an error handler (which is this lambda). | |
| ZX_DEBUG_ASSERT(removed_binding->has_error_handler()); | |
| if (handler) { | |
| // Invoke the (real) handler provided by the client. | |
| handler(status); | |
| } | |
| }); | |
| } | |
| // Adds a binding to the set for the given implementation. | |
| // | |
| // Creates a channel for the binding and returns the client endpoint of | |
| // the channel as an |InterfaceHandle|. If |AddBinding| fails to create the | |
| // underlying channel, the returned |InterfaceHandle| will return false from | |
| // |is_valid()|. | |
| // | |
| // The given |ImplPtr| is bound to the newly created channel. The binding is | |
| // removed (and the |~ImplPtr| called) when the created binding has an error | |
| // (e.g., if the remote endpoint of the channel sends an invalid message). | |
| // | |
| // Whether this method takes ownership of |impl| depends on |ImplPtr|. If | |
| // |ImplPtr| is a raw pointer, then this method does not take ownership of | |
| // |impl|. If |ImplPtr| is a |unique_ptr|, then running |~ImplPtr| when the | |
| // binding generates an error will delete |impl| because |~ImplPtr| is | |
| // |~unique_ptr|, which deletes |impl|. | |
| InterfaceHandle<Interface> AddBinding(ImplPtr impl, async_dispatcher_t* dispatcher = nullptr, | |
| ErrorHandler handler = nullptr) { | |
| InterfaceHandle<Interface> handle; | |
| InterfaceRequest<Interface> request = handle.NewRequest(); | |
| if (!request) | |
| return nullptr; | |
| AddBinding(std::forward<ImplPtr>(impl), std::move(request), dispatcher, std::move(handler)); | |
| return handle; | |
| } | |
| // Removes a binding from the set. | |
| // | |
| // Returns true iff the binding was successfully found and removed. | |
| // Upon removal, the server endpoint of the channel is closed without sending an epitaph. | |
| template <class T> | |
| bool RemoveBinding(const T& impl) { | |
| return RemoveMatchedBinding([&impl](const std::unique_ptr<Binding>& b) { | |
| return ResolvePtr(impl) == ResolvePtr(b->impl()); | |
| }) != nullptr; | |
| } | |
| // Removes a binding from the set. | |
| // | |
| // Returns true iff the binding was successfully found and removed. | |
| // Upon removal, the server endpoint of the channel is closed and the epitaph provided is sent. | |
| template <class T> | |
| bool CloseBinding(const T& impl, zx_status_t epitaph_value) { | |
| auto binding = ExtractMatchedBinding([&impl](const std::unique_ptr<Binding>& b) { | |
| return ResolvePtr(impl) == ResolvePtr(b->impl()); | |
| }); | |
| if (binding == nullptr) | |
| return false; | |
| binding->Close(epitaph_value); | |
| CheckIfEmpty(); | |
| return true; | |
| } | |
| // Returns an InterfaceRequestHandler that binds the incoming | |
| // InterfaceRequests this object. | |
| InterfaceRequestHandler<Interface> GetHandler(ImplPtr impl, | |
| async_dispatcher_t* dispatcher = nullptr) { | |
| return [this, impl, dispatcher](InterfaceRequest<Interface> request) { | |
| AddBinding(impl, std::move(request), dispatcher); | |
| }; | |
| } | |
| // Removes all the bindings from the set. | |
| // | |
| // Closes all the channels associated with this |BindingSet|. | |
| // Bindings are destroyed AFTER it is removed from the bindings set. An | |
| // example of when this is useful is if an error handler on a binding has | |
| // some behavior where it needs to read from the binding set; the set would | |
| // then properly reflect that the binding is not present in the set. | |
| void CloseAll() { | |
| auto bindings_local = std::move(bindings_); | |
| bindings_.clear(); | |
| } | |
| // Removes all the bindings from the set using the provided epitaph. | |
| // | |
| // Closes all the channels associated with this |BindingSet| after sending | |
| // an epitaph. As with CloseAll(void) above, bindings are destroyed after they | |
| // are removed from the bindings set. | |
| void CloseAll(zx_status_t epitaph_value) { | |
| auto bindings_local = std::move(bindings_); | |
| bindings_.clear(); | |
| for (const auto& binding : bindings_local) { | |
| binding->Close(epitaph_value); | |
| } | |
| } | |
| // The number of bindings in this |BindingSet|. | |
| size_t size() const { return bindings_.size(); } | |
| // Called when the last binding has been removed from this |BindingSet|. | |
| // | |
| // This function is not called by |CloseAll| or by |~BindingSet|. | |
| void set_empty_set_handler(fit::closure empty_set_handler) { | |
| empty_set_handler_ = std::move(empty_set_handler); | |
| } | |
| // The bindings stored in this set. | |
| // | |
| // This collection of bindings can be invalidated when a |Binding| in the | |
| // set encounters a connection error because connection errors causes the | |
| // |BindingSet| to remove the |Binding| from the set. | |
| const StorageType& bindings() const { return bindings_; } | |
| private: | |
| // Resolve smart pointers with get methods (e.g. shared_ptr, unique_ptr, etc). | |
| template <class T, std::enable_if_t<!std::is_pointer<T>::value>* = nullptr> | |
| static void* ResolvePtr(T& p) { | |
| return reinterpret_cast<void*>(p.get()); | |
| } | |
| // Resolve raw pointers. | |
| template <class T, std::enable_if_t<std::is_pointer<T>::value>* = nullptr> | |
| static void* ResolvePtr(T p) { | |
| return reinterpret_cast<void*>(p); | |
| } | |
| // Called when a binding has an error to remove the binding from the set. | |
| std::unique_ptr<Binding> RemoveOnError(Binding* binding) { | |
| std::unique_ptr<Binding> removed_binding = RemoveMatchedBinding( | |
| [binding](const std::unique_ptr<Binding>& b) { return b.get() == binding; }); | |
| ZX_DEBUG_ASSERT(removed_binding != nullptr); | |
| return removed_binding; | |
| } | |
| std::unique_ptr<Binding> RemoveMatchedBinding( | |
| std::function<bool(const std::unique_ptr<Binding>&)> binding_matcher) { | |
| auto matching_binding = ExtractMatchedBinding(binding_matcher); | |
| if (matching_binding == nullptr) | |
| return nullptr; | |
| CheckIfEmpty(); | |
| return matching_binding; | |
| } | |
| std::unique_ptr<Binding> ExtractMatchedBinding( | |
| std::function<bool(const std::unique_ptr<Binding>&)> binding_matcher) { | |
| auto it = std::find_if(bindings_.begin(), bindings_.end(), binding_matcher); | |
| if (it == bindings_.end()) | |
| return nullptr; | |
| std::unique_ptr<Binding> binding_local = std::move(*it); | |
| bindings_.erase(it); | |
| return binding_local; | |
| } | |
| void CheckIfEmpty() { | |
| if (bindings_.empty() && empty_set_handler_) | |
| empty_set_handler_(); | |
| } | |
| StorageType bindings_; | |
| fit::closure empty_set_handler_; | |
| }; | |
| } // namespace fidl | |
| #endif // LIB_FIDL_CPP_BINDING_SET_H_ |