blob: 5c059175c56b20e34dfb500b6fae43eaf8a9a81a [file] [log] [blame]
// 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>>;
// TODO(FIDL-761) Use fit::function here instead of std::function.
using ErrorHandler = std::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, capture_handler = std::move(handler), this](zx_status_t status) {
// Subtle behavior: it is necessary to std::move into a local
// variable because the closure is deleted when RemoveOnError
// is called.
ErrorHandler handler(std::move(capture_handler));
this->RemoveOnError(binding);
if (handler) {
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());
});
}
// 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.
void RemoveOnError(Binding* binding) {
bool found = RemoveMatchedBinding(
[binding](const std::unique_ptr<Binding>& b) { return b.get() == binding; });
ZX_DEBUG_ASSERT(found);
}
bool RemoveMatchedBinding(std::function<bool(const std::unique_ptr<Binding>&)> binding_matcher) {
{
auto matching_binding = ExtractMatchedBinding(binding_matcher);
if (matching_binding == nullptr)
return false;
}
CheckIfEmpty();
return true;
}
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;
auto binding_local = std::move(*it);
binding_local->set_error_handler(nullptr);
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_