You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
232 lines
9.0 KiB
232 lines
9.0 KiB
// Copyright 2014 The Chromium 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 MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_
|
|
#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include "base/callback_forward.h"
|
|
#include "base/logging.h"
|
|
#include "base/macros.h"
|
|
#include "base/memory/ref_counted.h"
|
|
#include "base/sequenced_task_runner.h"
|
|
#include "mojo/public/cpp/bindings/connection_error_callback.h"
|
|
#include "mojo/public/cpp/bindings/interface_ptr_info.h"
|
|
#include "mojo/public/cpp/bindings/lib/interface_ptr_state.h"
|
|
|
|
namespace mojo {
|
|
|
|
// A pointer to a local proxy of a remote Interface implementation. Uses a
|
|
// message pipe to communicate with the remote implementation, and automatically
|
|
// closes the pipe and deletes the proxy on destruction. The pointer must be
|
|
// bound to a message pipe before the interface methods can be called. Once a
|
|
// pointer is destroyed, it is guaranteed that pending callbacks as well as the
|
|
// connection error handler (if registered) won't be called.
|
|
//
|
|
// This class is thread hostile, as is the local proxy it manages, while bound
|
|
// to a message pipe. All calls to this class or the proxy should be from the
|
|
// same sequence that bound it. If you need to move the proxy to a different
|
|
// sequence, extract the InterfacePtrInfo (containing just the message pipe and
|
|
// any version information) using PassInterface() on the original sequence, pass
|
|
// it to a different sequence, and create and bind a new InterfacePtr from that
|
|
// sequence. If an InterfacePtr is not bound to a message pipe, it may be bound
|
|
// or destroyed on any sequence.
|
|
template <typename Interface>
|
|
class InterfacePtr {
|
|
public:
|
|
using InterfaceType = Interface;
|
|
using PtrInfoType = InterfacePtrInfo<Interface>;
|
|
using Proxy = typename Interface::Proxy_;
|
|
|
|
// Constructs an unbound InterfacePtr.
|
|
InterfacePtr() {}
|
|
InterfacePtr(decltype(nullptr)) {}
|
|
|
|
// Takes over the binding of another InterfacePtr.
|
|
InterfacePtr(InterfacePtr&& other) noexcept {
|
|
internal_state_.Swap(&other.internal_state_);
|
|
}
|
|
|
|
explicit InterfacePtr(PtrInfoType&& info) noexcept { Bind(std::move(info)); }
|
|
|
|
// Takes over the binding of another InterfacePtr, and closes any message pipe
|
|
// already bound to this pointer.
|
|
InterfacePtr& operator=(InterfacePtr&& other) noexcept {
|
|
reset();
|
|
internal_state_.Swap(&other.internal_state_);
|
|
return *this;
|
|
}
|
|
|
|
// Assigning nullptr to this class causes it to close the currently bound
|
|
// message pipe (if any) and returns the pointer to the unbound state.
|
|
InterfacePtr& operator=(decltype(nullptr)) {
|
|
reset();
|
|
return *this;
|
|
}
|
|
|
|
// Closes the bound message pipe (if any) on destruction.
|
|
~InterfacePtr() {}
|
|
|
|
// Binds the InterfacePtr to a remote implementation of Interface.
|
|
//
|
|
// Calling with an invalid |info| (containing an invalid message pipe handle)
|
|
// has the same effect as reset(). In this case, the InterfacePtr is not
|
|
// considered as bound.
|
|
//
|
|
// Optionally, |runner| is a SequencedTaskRunner bound to the current sequence
|
|
// on which all callbacks and connection error notifications will be
|
|
// dispatched. It is only useful to specify this to use a different
|
|
// SequencedTaskRunner than SequencedTaskRunnerHandle::Get().
|
|
void Bind(InterfacePtrInfo<Interface> info,
|
|
scoped_refptr<base::SequencedTaskRunner> runner = nullptr) {
|
|
reset();
|
|
if (info.is_valid())
|
|
internal_state_.Bind(std::move(info), std::move(runner));
|
|
}
|
|
|
|
// Returns whether or not this InterfacePtr is bound to a message pipe.
|
|
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.
|
|
Proxy* get() const { return internal_state_.instance(); }
|
|
|
|
// Functions like a pointer to Interface. Must already be bound.
|
|
Proxy* operator->() const { return get(); }
|
|
Proxy& operator*() const { return *get(); }
|
|
|
|
// Returns the version number of the interface that the remote side supports.
|
|
uint32_t version() const { return internal_state_.version(); }
|
|
|
|
// Queries the max version that the remote side supports. On completion, the
|
|
// result will be returned as the input of |callback|. The version number of
|
|
// this interface pointer will also be updated.
|
|
void QueryVersion(const base::Callback<void(uint32_t)>& callback) {
|
|
internal_state_.QueryVersion(callback);
|
|
}
|
|
|
|
// If the remote side doesn't support the specified version, it will close its
|
|
// end of the message pipe 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);
|
|
}
|
|
|
|
// Sends a no-op message on the underlying message pipe and runs the current
|
|
// message loop until its response is received. This can be used in tests to
|
|
// verify that no message was sent on a message pipe in response to some
|
|
// stimulus.
|
|
void FlushForTesting() { internal_state_.FlushForTesting(); }
|
|
|
|
// Closes the bound message pipe, if any.
|
|
void reset() {
|
|
State doomed;
|
|
internal_state_.Swap(&doomed);
|
|
}
|
|
|
|
// Similar to the method above, but also specifies a disconnect reason.
|
|
void ResetWithReason(uint32_t custom_reason, const std::string& description) {
|
|
if (internal_state_.is_bound())
|
|
internal_state_.CloseWithReason(custom_reason, description);
|
|
reset();
|
|
}
|
|
|
|
// Whether there are any associated interfaces running on the pipe currently.
|
|
bool HasAssociatedInterfaces() const {
|
|
return internal_state_.HasAssociatedInterfaces();
|
|
}
|
|
|
|
// Returns true if bound and awaiting a response to a message.
|
|
bool IsExpectingResponse() { return internal_state_.has_pending_callbacks(); }
|
|
|
|
// Indicates whether the message pipe 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 sequence that owns this InterfacePtr.
|
|
//
|
|
// This method may only be called after the InterfacePtr has been bound to a
|
|
// message pipe.
|
|
void set_connection_error_handler(base::OnceClosure error_handler) {
|
|
internal_state_.set_connection_error_handler(std::move(error_handler));
|
|
}
|
|
|
|
void set_connection_error_with_reason_handler(
|
|
ConnectionErrorWithReasonCallback error_handler) {
|
|
internal_state_.set_connection_error_with_reason_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 sequence (see class comments for details).
|
|
//
|
|
// It is an error to call PassInterface() while:
|
|
// - there are pending responses; or
|
|
// TODO: fix this restriction, it's not always obvious when there is a
|
|
// pending response.
|
|
// - there are associated interfaces running.
|
|
// TODO(yzshen): For now, users need to make sure there is no one holding
|
|
// on to associated interface endpoint handles at both sides of the
|
|
// message pipe in order to call this method. We need a way to forcefully
|
|
// invalidate associated interface endpoint handles.
|
|
InterfacePtrInfo<Interface> PassInterface() {
|
|
CHECK(!HasAssociatedInterfaces());
|
|
CHECK(!internal_state_.has_pending_callbacks());
|
|
State state;
|
|
internal_state_.Swap(&state);
|
|
|
|
return state.PassInterface();
|
|
}
|
|
|
|
bool Equals(const InterfacePtr& other) const {
|
|
if (this == &other)
|
|
return true;
|
|
|
|
// Now that the two refer to different objects, they are equivalent if
|
|
// and only if they are both null.
|
|
return !(*this) && !other;
|
|
}
|
|
|
|
// DO NOT USE. Exposed only for internal use and for testing.
|
|
internal::InterfacePtrState<Interface>* internal_state() {
|
|
return &internal_state_;
|
|
}
|
|
|
|
// Allow InterfacePtr<> to be used in boolean expressions.
|
|
explicit operator bool() const { return internal_state_.is_bound(); }
|
|
|
|
private:
|
|
typedef internal::InterfacePtrState<Interface> State;
|
|
mutable State internal_state_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(InterfacePtr);
|
|
};
|
|
|
|
// If |info| is valid (containing a valid message pipe handle), returns an
|
|
// InterfacePtr bound to it. Otherwise, returns an unbound InterfacePtr.
|
|
template <typename Interface>
|
|
InterfacePtr<Interface> MakeProxy(
|
|
InterfacePtrInfo<Interface> info,
|
|
scoped_refptr<base::SequencedTaskRunner> runner = nullptr) {
|
|
InterfacePtr<Interface> ptr;
|
|
if (info.is_valid())
|
|
ptr.Bind(std::move(info), std::move(runner));
|
|
return std::move(ptr);
|
|
}
|
|
|
|
} // namespace mojo
|
|
|
|
#endif // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_
|