Chromium Code Reviews| Index: mojo/public/cpp/bindings/interface_ptr.h |
| diff --git a/mojo/public/cpp/bindings/interface_ptr.h b/mojo/public/cpp/bindings/interface_ptr.h |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..1e0bc24b55f0da6ef05dd85a6147fe37bab608b8 |
| --- /dev/null |
| +++ b/mojo/public/cpp/bindings/interface_ptr.h |
| @@ -0,0 +1,221 @@ |
| +// 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 <assert.h> |
| +#include <stdio.h> |
| + |
| +#include <algorithm> |
| + |
| +#include "mojo/public/cpp/bindings/error_handler.h" |
| +#include "mojo/public/cpp/bindings/lib/interface_ptr_internal.h" |
| +#include "mojo/public/cpp/system/macros.h" |
| + |
| +namespace mojo { |
| + |
| +template <typename Interface> |
| +class InterfacePtr { |
| + MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(InterfacePtr, RValue) |
| + public: |
| + InterfacePtr() {} |
| + explicit InterfacePtr(Interface* impl) { state_.impl = impl; } |
| + |
| + InterfacePtr(RValue other) { other.object->state_.Swap(&state_); } |
| + InterfacePtr& operator=(RValue other) { |
| + other.object->state_.Swap(&state_); |
| + return *this; |
| + } |
| + |
| + ~InterfacePtr() {} |
| + |
| + Interface* get() const { |
| + return state_.impl; |
| + } |
| + Interface* operator->() const { return get(); } |
| + Interface* operator*() const { return get(); } |
| + |
| + void reset() { |
| + State doomed; |
| + state_.Swap(&doomed); |
| + } |
| + |
| + void Bind(Interface* impl) { |
| + reset(); |
| + state_.impl = impl; |
| + } |
| + |
| + bool encountered_error() const; |
| + |
| + void set_error_handler(ErrorHandler* error_handler) { |
| + assert(state_.context); |
| + state_.context->router()->set_error_handler(error_handler); |
| + } |
| + |
| + // XXX figure out how to hide these! |
| + |
| + bool has_context() const { return !!state_.context; } |
| + |
| + void ConfigureProxy(ScopedMessagePipeHandle handle, |
| + MojoAsyncWaiter* waiter = GetDefaultAsyncWaiter()) { |
| + assert(!state_.impl); |
| + assert(!state_.context); |
| + |
| + internal::InterfacePtrContext* context = |
| + new internal::InterfacePtrContext(handle.Pass(), waiter); |
| + ProxyWithStub* proxy = new ProxyWithStub(context->router()); |
| + context->router()->set_incoming_receiver(&proxy->stub); |
| + |
| + state_.impl = proxy; |
| + state_.context = context; |
| + } |
| + |
| + void ConfigureStub(ScopedMessagePipeHandle handle, |
| + MojoAsyncWaiter* waiter = GetDefaultAsyncWaiter()) { |
| + assert(state_.impl); // Should have already been set! |
| + assert(!state_.context); |
| + |
| + // Stub for binding to state_.impl |
| + // Proxy for communicating to the client on the other end of the pipe. |
| + |
| + internal::InterfacePtrContext* context = |
| + new internal::InterfacePtrContext(handle.Pass(), waiter); |
| + ClientProxyWithStub* proxy = new ClientProxyWithStub(context->router()); |
| + proxy->stub.set_sink(state_.impl); |
| + context->router()->set_incoming_receiver(&proxy->stub); |
| + |
| + state_.impl->SetClient(proxy); |
| + |
| + state_.client = proxy; |
| + state_.context = context; |
| + } |
| + |
| + void Detach() { |
| + new DetachedState(&state_); |
| + } |
| + |
| + internal::Router* router_for_testing() { return state_.context->router(); } |
| + |
| + private: |
| + class ProxyWithStub : public Interface::Proxy_ { |
| + public: |
| + explicit ProxyWithStub(MessageReceiver* receiver) |
| + : Interface::Proxy_(receiver) { |
| + } |
| + virtual void SetClient(typename Interface::Client_* client) MOJO_OVERRIDE { |
| + stub.set_sink(client); |
| + } |
| + typename Interface::Client_::Stub_ stub; |
| + private: |
| + MOJO_DISALLOW_COPY_AND_ASSIGN(ProxyWithStub); |
| + }; |
| + |
| + class ClientProxyWithStub : public Interface::Client_::Proxy_ { |
| + public: |
| + explicit ClientProxyWithStub(MessageReceiver* receiver) |
| + : Interface::Client_::Proxy_(receiver) { |
| + } |
| + typename Interface::Stub_ stub; |
| + private: |
| + MOJO_DISALLOW_COPY_AND_ASSIGN(ClientProxyWithStub); |
| + }; |
| + |
| + struct State { |
| + State() : impl(NULL), client(NULL), context(NULL) {} |
| + |
| + ~State() { |
| + // Destruction order matters here. We delete |impl| first, even though |
| + // |context| may have a reference to it, so that |~Interface| may have a |
| + // shot at generating new outbound messages (ie, nvoking client methods). |
| + delete impl; |
| + delete context; |
| + delete client; |
| + } |
| + |
| + void Swap(State* other) { |
| + std::swap(other->impl, impl); |
| + std::swap(other->client, client); |
| + std::swap(other->context, context); |
| + } |
| + |
| + Interface* impl; |
| + typename Interface::Client_* client; |
| + internal::InterfacePtrContext* context; |
| + |
| + MOJO_DISALLOW_COPY_AND_ASSIGN(State); |
| + }; |
| + |
| + class DetachedState : public ErrorHandler { |
| + public: |
| + explicit DetachedState(State* state) { |
| + internal::LogDetachedState(this, +1); |
| + |
| + state_.Swap(state); |
| + |
| + assert(state_.context); |
| + assert(!state_.context->router()->encountered_error()); |
| + |
| + // Register to observe when the pipe is broken. |
| + state_.context->router()->set_error_handler(this); |
| + } |
| + |
| + virtual void OnError() MOJO_OVERRIDE { |
| + internal::LogDetachedState(this, -1); |
| + |
| + delete this; |
| + } |
| + |
| + private: |
| + State state_; |
| + |
| + MOJO_DISALLOW_COPY_AND_ASSIGN(DetachedState); |
| + }; |
| + |
| + State state_; |
| +}; |
| + |
| +// Takes a handle to the proxy end-point of a pipe. On the other end is |
| +// presumed to be an interface implementation of type |Interface|. Returns a |
| +// generated proxy to that interface, which may be used on the current thread. |
| +// It is valid to call SetClient on the returned Interface to set an instance |
| +// of Interface::Client. |
| +// |
|
DaveMoore
2014/05/06 21:30:54
Maybe there should be a variant of this that takes
darin (slow to review)
2014/05/06 21:39:11
Yeah, that's something I've been considering as we
|
| +// XXX not sure if MakeProxy is the best name here. |
| +template <typename Interface> |
| +InterfacePtr<Interface> MakeProxy( |
| + ScopedMessagePipeHandle handle, |
| + MojoAsyncWaiter* waiter = GetDefaultAsyncWaiter()) { |
| + InterfacePtr<Interface> ptr; |
| + if (handle.is_valid()) |
| + ptr.ConfigureProxy(handle.Pass(), waiter); |
| + return ptr.Pass(); |
| +} |
| + |
| +// Takes a heap-allocated interface implementation and binds its lifetime to |
| +// that of a MessagePipe. A handle to the proxy end-point of the pipe is |
| +// returned. If that pipe is closed, then the interface implementation will be |
| +// deleted. |
| +// |
| +// The interface implementation is also bound to the current thread. Its |
| +// methods will only be called on the current thread, and if the current thread |
| +// exits, then it will also be deleted and its end point of the pipe will be |
| +// closed. |
| +// |
| +// Before returning the interface implementation will receive a SetClient call, |
| +// providing it with a proxy to the client on the other end of the pipe. |
| +// |
| +// XXX not sure if BindToPipe is the best name here. |
| +template <typename Interface> |
| +ScopedMessagePipeHandle BindToPipe(Interface* impl) { |
| + InterfacePtr<Interface> ptr(impl); |
| + MessagePipe pipe; |
| + ptr.ConfigureStub(pipe.handle0.Pass()); |
| + ptr.Detach(); |
| + return pipe.handle1.Pass(); |
| +} |
| + |
| +} // namespace mojo |
| + |
| +#endif // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_ |