blob: be6805b1321a850604bd004324c2b1c8df877f74 [file] [log] [blame]
// Copyright 2014 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_BINDINGS_INTERFACE_PTR_H_
#define LIB_FIDL_CPP_BINDINGS_INTERFACE_PTR_H_
#include <algorithm>
#include <cstddef>
#include <functional>
#include <utility>
#include <zx/channel.h>
#include "lib/fidl/cpp/bindings/interface_handle.h"
#include "lib/fidl/cpp/bindings/internal/interface_ptr_internal.h"
#include "lib/fidl/cpp/bindings/macros.h"
#include "lib/fxl/functional/closure.h"
#include "lib/fxl/macros.h"
#include "lib/fxl/time/time_delta.h"
namespace fidl {
template <typename Interface>
class InterfaceRequest;
// A pointer to a local proxy of a remote Interface implementation. Uses a
// channel to communicate with the remote implementation, and automatically
// closes the channel and deletes the proxy on destruction. The pointer must be
// bound to a channel before the interface methods can be called.
//
// This class is thread hostile, as is the local proxy it manages. All calls to
// this class or the proxy should be from the same thread that created it. If
// you need to move the proxy to a different thread, extract the
// InterfaceHandle (containing just the channel and any version
// information) using PassInterfaceHandle(), pass it to a different thread, and
// create and bind a new InterfacePtr from that thread.
template <typename Interface>
class InterfacePtr {
public:
// Constructs an unbound InterfacePtr.
InterfacePtr() {}
InterfacePtr(std::nullptr_t) {}
// Takes over the binding of another InterfacePtr.
InterfacePtr(InterfacePtr&& other) {
internal_state_.Swap(&other.internal_state_);
}
// Takes over the binding of another InterfacePtr, and closes any channel
// already bound to this pointer.
InterfacePtr& operator=(InterfacePtr&& other) {
reset();
internal_state_.Swap(&other.internal_state_);
return *this;
}
// Assigning nullptr to this class causes it to close the currently bound
// channel (if any) and returns the pointer to the unbound state.
InterfacePtr& operator=(std::nullptr_t) {
reset();
return *this;
}
// Closes the bound channel (if any) on destruction.
~InterfacePtr() {}
// If |info| is valid (containing a valid channel handle), returns an
// InterfacePtr bound to it. Otherwise, returns an unbound InterfacePtr.
static InterfacePtr<Interface> Create(InterfaceHandle<Interface> info) {
InterfacePtr<Interface> ptr;
if (info.is_valid())
ptr.Bind(std::move(info));
return ptr;
}
// Creates a new pair of channels, one end bound to this InterfacePtr<>, and
// returns the other end as a InterfaceRequest<>. InterfaceRequest<> should
// be passed to whatever will provide the implementation.
//
// Example. Given the following interface:
//
// interface Database {
// OpenTable(Table& table);
// }
//
// The client would have code similar to the following:
//
// DatabasePtr database = ...; // Connect to database.
// TablePtr table;
// database->OpenTable(table.NewRequest());
//
// Upon return from .NewRequest(), |table| is ready to have methods called
// on it.
InterfaceRequest<Interface> NewRequest() {
FXL_DCHECK(!is_bound()) << "An existing handle is already bound.";
zx::channel endpoint0;
zx::channel endpoint1;
zx::channel::create(0, &endpoint0, &endpoint1);
Bind(InterfaceHandle<Interface>(std::move(endpoint0)));
return InterfaceRequest<Interface>(std::move(endpoint1));
}
// Binds the InterfacePtr to a remote implementation of Interface.
//
// Calling with an invalid |info| (containing an invalid channel handle)
// has the same effect as reset(). In this case, the InterfacePtr is not
// considered as bound.
void Bind(InterfaceHandle<Interface> handle) {
reset();
if (handle.is_valid())
internal_state_.Bind(std::move(handle));
}
// Returns whether or not this InterfacePtr is bound to a channel.
bool is_bound() const { return internal_state_.is_bound(); }
// Returns a raw pointer to the local proxy. Caller does not take ownership.
// Note that the local proxy is thread hostile, as stated above.
Interface* get() const { return internal_state_.instance(); }
// Functions like a pointer to Interface. Must already be bound.
Interface* operator->() const { return get(); }
Interface& operator*() const { return *get(); }
// Returns the version number of the interface that the remote side supports.
uint32_t version() const { return internal_state_.version(); }
// If the remote side doesn't support the specified version, it will close its
// end of the channel asynchronously. This does nothing if it's already
// known that the remote side supports the specified version, i.e., if
// |version <= this->version()|.
//
// After calling RequireVersion() with a version not supported by the remote
// side, all subsequent calls to interface methods will be ignored.
void RequireVersion(uint32_t version) {
internal_state_.RequireVersion(version);
}
// Closes the bound channel (if any) and returns the pointer to the
// unbound state.
void reset() {
State doomed;
internal_state_.Swap(&doomed);
}
// Tests as true if bound, false if not.
explicit operator bool() const { return internal_state_.is_bound(); }
// Blocks the current thread until the next incoming response callback arrives
// or an error occurs. Returns |true| if a response arrived, or |false| in
// case of error.
//
// This method may only be called after the InterfacePtr has been bound to a
// channel.
bool WaitForIncomingResponse() {
return internal_state_.WaitForIncomingResponse(fxl::TimeDelta::Max());
}
// Blocks the current thread until the next incoming response callback
// arrives, an error occurs, or the timeout exceeded. Returns |true| if a
// response arrived, or |false| otherwise. Use |encountered_error| to know
// if an error occurred, of if the timeout exceeded.
//
// This method may only be called after the InterfacePtr has been bound to a
// channel.
bool WaitForIncomingResponseWithTimeout(fxl::TimeDelta timeout) {
return internal_state_.WaitForIncomingResponse(timeout);
}
// Indicates whether the channel has encountered an error. If true,
// method calls made on this interface will be dropped (and may already have
// been dropped).
bool encountered_error() const { return internal_state_.encountered_error(); }
// Registers a handler to receive error notifications. The handler will be
// called from the thread that owns this InterfacePtr.
//
// This method may only be called after the InterfacePtr has been bound to a
// channel.
void set_connection_error_handler(fxl::Closure error_handler) {
internal_state_.set_connection_error_handler(std::move(error_handler));
}
// Unbinds the InterfacePtr and returns the information which could be used
// to setup an InterfacePtr again. This method may be used to move the proxy
// to a different thread (see class comments for details).
InterfaceHandle<Interface> PassInterfaceHandle() {
State state;
internal_state_.Swap(&state);
return state.PassInterfaceHandle();
}
// DO NOT USE. Exposed only for internal use and for testing.
internal::InterfacePtrState<Interface>* internal_state() {
return &internal_state_;
}
private:
typedef internal::InterfacePtrState<Interface> State;
mutable State internal_state_;
FIDL_MOVE_ONLY_TYPE(InterfacePtr);
};
} // namespace fidl
#endif // LIB_FIDL_CPP_BINDINGS_INTERFACE_PTR_H_