| // 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_ |