blob: 7b06f6eb827775bcf410bd56fc9f9dc3498a3a2f [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_INTERFACE_PTR_H_
#define LIB_FIDL_CPP_INTERFACE_PTR_H_
#include <lib/fit/function.h>
#include <lib/zx/channel.h>
#include <zircon/assert.h>
#include <algorithm>
#include <cstddef>
#include <utility>
#include "lib/fidl/cpp/interface_handle.h"
#include "lib/fidl/cpp/interface_request.h"
#include "lib/fidl/cpp/internal/proxy_controller.h"
namespace fidl {
// A client interface to a remote implementation of |Interface|.
//
// An |InterfacePtr| implements |Interface| by proxying calls through a
// |channel| to a remote implementation of |Interface|. Method calls on the
// |Interface| proxy are encoded and sent through the bound channel to the
// remote endpoint, which processes them. If the remote endpoint has not yet
// been bound to an implementation, messages sent on the channel are buffered
// by the channel, allowing for *pipelined* operation.
//
// The |InterfacePtr| also keeps state about the connection and about
// outstanding request transactions that are expecting replies. When the
// |InterfacePtr| receives a reply to an outstanding transaction, the
// |InterfacePtr| decodes the reply and calls the appropriate callback on the
// thread to which the |InterfacePtr| was bound.
//
// You need to bind the |InterfacePtr| before calling any |Interface| methods.
// There are a number of ways to bind the |InterfacePtr|. See |NewRequest|,
// |Bind|, and the |Bind| method on |InterfaceHandle|.
//
// If the underlying channel experiences an error, the |InterfacePtr| will
// unbind from the channel and call its error handler.
//
// This class is thread-hostile, as is the local proxy it manages. All calls to
// this class or the proxy should be from the thread to which the
// |InterfacePtr| was bound. If you need to move the proxy to a different
// thread, extract the |InterfaceHandle| by calling |Unbind|, and pass the
// |InterfaceHandle| to a different thread, which the |InterfaceHandle| can be
// bound to an |InterfacePtr| again. This operation destroys the state about
// outstanding request transactions that are expecting replies.
//
// See also:
//
// * |Binding|, which is the server analog of an |InterfacePtr|.
// * |SynchronousInterfacePtr|, which is a synchronous client interface to a
// remote implementation.
template <typename Interface>
class InterfacePtr final {
public:
using Proxy = typename Interface::Proxy_;
// Creates an unbound |InterfacePtr|.
InterfacePtr() : impl_(new Impl) {}
InterfacePtr(std::nullptr_t) : InterfacePtr() {}
InterfacePtr(const InterfacePtr& other) = delete;
InterfacePtr& operator=(const InterfacePtr& other) = delete;
InterfacePtr(InterfacePtr&& other) : impl_(std::move(other.impl_)) {
other.impl_.reset(new Impl);
}
InterfacePtr& operator=(InterfacePtr&& other) {
if (this != &other) {
impl_ = std::move(other.impl_);
other.impl_.reset(new Impl);
}
return *this;
}
// Bind the |InterfacePtr| to one endpoint of a newly created channel and
// return the other endpoint as an |InterfaceRequest|.
//
// Typically, the returned |InterfaceRequest| will be sent to a remote process
// to be bound to an implementation of |Interface| using a |Binding| object.
//
// After calling this method, clients can start calling methods on this
// |InterfacePtr|. The methods will write messages into the underlying
// channel created by |NewRequest|, where they will be buffered by the
// underlying channel until the |InterfaceRequest| is bound to an
// implementation of |Interface|, potentially in a remote process.
//
// Uses the given async_dispatcher_t in order to read messages from the
// channel and to monitor the channel for |ZX_CHANNEL_PEER_CLOSED|. If
// |dispatcher| is null, the current thread must have a default
// async_dispatcher_t.
//
// # Example
//
// Given the following interface:
//
// interface Database {
// OpenTable(request<Table> table);
// };
//
// The client can use the |NewRequest| method to create the |InterfaceRequest|
// object needed by the |OpenTable| method:
//
// DatabasePtr database = ...; // Connect to database.
// TablePtr table;
// database->OpenTable(table.NewRequest());
//
// The client can call methods on |table| immediately.
InterfaceRequest<Interface> NewRequest(async_dispatcher_t* dispatcher = nullptr) {
zx::channel h1;
zx::channel h2;
if (zx::channel::create(0, &h1, &h2) != ZX_OK || Bind(std::move(h1), dispatcher) != ZX_OK)
return nullptr;
return InterfaceRequest<Interface>(std::move(h2));
}
// Binds the |InterfacePtr| to the given |channel|.
//
// The |InterfacePtr| expects the remote end of the |channel| to speak the
// protocol defined by |Interface|. Unlike the |Bind| overload that takes a
// |InterfaceHandle| parameter, this |Bind| overload lacks type safety.
//
// If the |InterfacePtr| was prevously bound to another channel, that channel
// is closed. If the |channel| is invalid, then this method will effectively
// unbind the |InterfacePtr|. A more direct way to have that effect is to call
// |Unbind|.
//
// Uses the given async_dispatcher_t in order to read messages from the
// channel and to monitor the channel for |ZX_CHANNEL_PEER_CLOSED|. If
// |dispatcher| is null, the current thread must have a default
// async_dispatcher_t.
//
// Returns an error if the binding was not able to be created (e.g., because
// the |channel| lacks |ZX_RIGHT_WAIT|).
zx_status_t Bind(zx::channel channel, async_dispatcher_t* dispatcher = nullptr) {
return impl_->controller.reader().Bind(std::move(channel), dispatcher);
}
// Binds the |InterfacePtr| to the given |InterfaceHandle|.
//
// The |InterfacePtr| expects the remote end of the |channel| to speak the
// protocol defined by |Interface|. Unlike the |Bind| overload that takes a
// |channel| parameter, this |Bind| overload provides type safety.
//
// If the |InterfacePtr| was prevously bound to another channel, that channel
// is closed. If the |InterfaceHandle| is invalid, then this method will
// effectively unbind the |InterfacePtr|. A more direct way to have that
// effect is to call |Unbind|.
//
// Uses the given async_dispatcher_t in order to read messages from the
// channel and to monitor the channel for |ZX_CHANNEL_PEER_CLOSED|. If
// |dispatcher| is null, the current thread must have a default
// async_dispatcher_t.
//
// Returns an error if the binding was not able to be created (e.g., because
// the |channel| lacks |ZX_RIGHT_WAIT|).
zx_status_t Bind(InterfaceHandle<Interface> handle, async_dispatcher_t* dispatcher = nullptr) {
return Bind(handle.TakeChannel(), dispatcher);
}
// Unbinds the underlying channel from the |InterfacePtr|.
//
// The underlying channel is returned as an |InterfaceHandle|, which is safe
// to transport to another thread or process. Any callbacks waiting for
// replies from the remote endpoint are discarded and any outstanding
// transaction state is erased.
//
// After this method returns, a subsequent call to |Bind| is required before
// calling any additional |Interface| methods.
InterfaceHandle<Interface> Unbind() {
return InterfaceHandle<Interface>(impl_->controller.reader().Unbind());
}
// Whether this |InterfacePtr| is currently bound to a channel.
//
// If the |InterfacePtr| is bound to a channel, the |InterfacePtr| has
// affinity for the thread to which it was bound and calls to |Interface|
// methods are proxied to the remote endpoint of the channel.
//
// See also:
//
// * |Bind|, which binds a channel to this |InterfacePtr|.
// * |Unbind|, which unbinds a channel from this |InterfacePtr|.
bool is_bound() const { return impl_->controller.reader().is_bound(); }
// Whether this |InterfacePtr| is currently bound to a channel.
//
// See |is_bound| for details.
explicit operator bool() const { return is_bound(); }
// The |Interface| proxy associated with this |InterfacePtr|.
//
// When this |InterfacePtr| is bound, method calls on this |Interface| will
// be proxied to the remote endpoint of the connection. Methods that expect
// replies will retain the supplied callbacks until the |InterfacePtr| either
// receives a reply to that transaction or the |InterfacePtr| is unbound from
// the channel.
//
// When this |InterfacePtr| is not bound, calling methods on the returned
// |Interface| simply discards the arguments and closes any handles contained
// in those arguments.
//
// The returned |Interface| is thread-hostile and can be used only on the
// thread to which the |InterfacePtr| was bound.
Interface* get() const { return &impl_->proxy; }
Interface* operator->() const { return get(); }
Interface& operator*() const { return *get(); }
// An object on which to register for FIDL events.
//
// Arriving events are dispatched to the callbacks stored on this object.
// Events for unbound callbacks are ignored.
Proxy& events() const { return impl_->proxy; }
// Sets an error handler that will be called if an error causes the underlying
// channel to be closed.
//
// If the error is being reported because an error occurred on the local side
// of the channel, the zx_status_t of that error will be passed as the
// parameter to the handler.
//
// If an Epitaph was present on the channel, its error value will be passed as
// the parameter. See the FIDL language specification for more detail on
// Epitaphs.
//
// For example, the error handler will be called if the remote side of the
// channel sends an invalid message. When the error handler is called, the
// |Binding| will no longer be bound to the channel.
//
// WARNING: The |error_handler| is often called from the thread to which the
// |InterfacePtr| was bound, but the function can also be called from another
// thread if the |InterfacePtr| is still bound to the thread when the
// |async::Loop| for the thread is shutdown.
void set_error_handler(fit::function<void(zx_status_t)> error_handler) {
impl_->controller.reader().set_error_handler(std::move(error_handler));
}
// The underlying channel.
const zx::channel& channel() const { return impl_->controller.reader().channel(); }
// The |async_dispatcher_t| to which this interface is bound, if any.
async_dispatcher_t* dispatcher() const { return impl_->controller.reader().dispatcher(); }
private:
struct Impl;
std::unique_ptr<Impl> impl_;
};
template <typename T>
struct InterfacePtr<T>::Impl {
Impl() : proxy(&controller) { controller.set_proxy(&proxy); }
internal::ProxyController controller;
mutable Proxy proxy;
};
} // namespace fidl
#endif // LIB_FIDL_CPP_INTERFACE_PTR_H_