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_ |