Index: mojo/runner/child/runner_connection.cc |
diff --git a/mojo/runner/child/runner_connection.cc b/mojo/runner/child/runner_connection.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c7758c41141d271f28aa5e796e7d497e07dbaeab |
--- /dev/null |
+++ b/mojo/runner/child/runner_connection.cc |
@@ -0,0 +1,257 @@ |
+// Copyright 2015 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. |
+ |
+#include "mojo/runner/child/runner_connection.h" |
+ |
+#include "base/bind.h" |
+#include "base/callback.h" |
+#include "base/command_line.h" |
+#include "base/memory/ref_counted.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/thread_task_runner_handle.h" |
+#include "base/threading/thread.h" |
+#include "base/threading/thread_checker.h" |
+#include "mojo/message_pump/message_pump_mojo.h" |
+#include "mojo/public/cpp/bindings/binding.h" |
+#include "mojo/runner/child/child_controller.mojom.h" |
+#include "third_party/mojo/src/mojo/edk/embedder/embedder.h" |
+#include "third_party/mojo/src/mojo/edk/embedder/platform_channel_pair.h" |
+#include "third_party/mojo/src/mojo/edk/embedder/scoped_platform_handle.h" |
+ |
+namespace mojo { |
+namespace runner { |
+namespace { |
+ |
+// Blocks a thread until another thread unblocks it, at which point it unblocks |
+// and runs a closure provided by that thread. |
+class Blocker { |
+ public: |
+ class Unblocker { |
+ public: |
+ explicit Unblocker(Blocker* blocker = nullptr) : blocker_(blocker) {} |
+ ~Unblocker() {} |
+ |
+ void Unblock(base::Closure run_after) { |
+ DCHECK(blocker_); |
+ DCHECK(blocker_->run_after_.is_null()); |
+ blocker_->run_after_ = run_after; |
+ blocker_->event_.Signal(); |
+ blocker_ = nullptr; |
+ } |
+ |
+ private: |
+ Blocker* blocker_; |
+ |
+ // Copy and assign allowed. |
+ }; |
+ |
+ Blocker() : event_(true, false) {} |
+ ~Blocker() {} |
+ |
+ void Block() { |
+ DCHECK(run_after_.is_null()); |
+ event_.Wait(); |
+ if (!run_after_.is_null()) |
+ run_after_.Run(); |
+ } |
+ |
+ Unblocker GetUnblocker() { return Unblocker(this); } |
+ |
+ private: |
+ base::WaitableEvent event_; |
+ base::Closure run_after_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(Blocker); |
+}; |
+ |
+using GotApplicationRequestCallback = |
+ base::Callback<void(InterfaceRequest<Application>)>; |
+ |
+void OnGotApplicationRequest(InterfaceRequest<Application>* out_request, |
+ InterfaceRequest<Application> request) { |
+ *out_request = request.Pass(); |
+} |
+ |
+class ChildControllerImpl; |
+ |
+class RunnerConnectionImpl : public RunnerConnection { |
+ public: |
+ RunnerConnectionImpl() : controller_thread_("controller_thread") { |
+ StartControllerThread(); |
+ } |
+ ~RunnerConnectionImpl() override { |
+ controller_runner_->PostTask( |
+ FROM_HERE, base::Bind(&RunnerConnectionImpl::ShutdownOnControllerThread, |
+ base::Unretained(this))); |
+ controller_thread_.Stop(); |
+ } |
+ |
+ // Returns true if a connection to the runner has been established and |
+ // |request| has been modified, false if no connection was established. |
+ bool WaitForApplicationRequest(InterfaceRequest<Application>* request); |
+ |
+ ChildControllerImpl* controller() const { return controller_.get(); } |
+ |
+ void set_controller(scoped_ptr<ChildControllerImpl> controller) { |
+ controller_ = controller.Pass(); |
+ } |
+ |
+ private: |
+ void StartControllerThread() { |
+ base::Thread::Options controller_thread_options; |
+ controller_thread_options.message_loop_type = |
+ base::MessageLoop::TYPE_CUSTOM; |
+ controller_thread_options.message_pump_factory = |
+ base::Bind(&common::MessagePumpMojo::Create); |
+ CHECK(controller_thread_.StartWithOptions(controller_thread_options)); |
+ controller_runner_ = controller_thread_.task_runner().get(); |
+ CHECK(controller_runner_.get()); |
+ } |
+ |
+ void ShutdownOnControllerThread() { controller_.reset(); } |
+ |
+ base::Thread controller_thread_; |
+ scoped_refptr<base::SingleThreadTaskRunner> controller_runner_; |
+ |
+ // Accessed only on the controller thread. |
+ scoped_ptr<ChildControllerImpl> controller_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(RunnerConnectionImpl); |
+}; |
+ |
+class ChildControllerImpl : public ChildController { |
+ public: |
+ ~ChildControllerImpl() override { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ |
+ // TODO(vtl): Pass in the result from |MainMain()|. |
+ on_app_complete_.Run(MOJO_RESULT_UNIMPLEMENTED); |
+ } |
+ |
+ // To be executed on the controller thread. Creates the |ChildController|, |
+ // etc. |
+ static void Create(RunnerConnectionImpl* connection, |
+ const GotApplicationRequestCallback& callback, |
+ embedder::ScopedPlatformHandle platform_channel, |
+ const Blocker::Unblocker& unblocker) { |
+ DCHECK(connection); |
+ DCHECK(platform_channel.is_valid()); |
+ |
+ DCHECK(!connection->controller()); |
+ |
+ scoped_ptr<ChildControllerImpl> impl( |
+ new ChildControllerImpl(connection, callback, unblocker)); |
+ |
+ ScopedMessagePipeHandle host_message_pipe(embedder::CreateChannel( |
+ platform_channel.Pass(), |
+ base::Bind(&ChildControllerImpl::DidCreateChannel, |
+ base::Unretained(impl.get())), |
+ base::ThreadTaskRunnerHandle::Get())); |
+ |
+ impl->Bind(host_message_pipe.Pass()); |
+ |
+ connection->set_controller(impl.Pass()); |
+ } |
+ |
+ void Bind(ScopedMessagePipeHandle handle) { binding_.Bind(handle.Pass()); } |
+ |
+ void OnConnectionError() { |
+ // A connection error means the connection to the shell is lost. This is not |
+ // recoverable. |
+ LOG(ERROR) << "Connection error to the shell."; |
+ _exit(1); |
+ } |
+ |
+ // |ChildController| methods: |
+ void StartApp(InterfaceRequest<Application> application_request, |
+ const StartAppCallback& on_app_complete) override { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ |
+ on_app_complete_ = on_app_complete; |
+ unblocker_.Unblock( |
+ base::Bind(&ChildControllerImpl::ReturnApplicationRequestOnMainThread, |
+ callback_, base::Passed(&application_request))); |
+ } |
+ |
+ void ExitNow(int32_t exit_code) override { |
+ DVLOG(2) << "ChildControllerImpl::ExitNow(" << exit_code << ")"; |
+ _exit(exit_code); |
+ } |
+ |
+ private: |
+ ChildControllerImpl(RunnerConnectionImpl* connection, |
+ const GotApplicationRequestCallback& callback, |
+ const Blocker::Unblocker& unblocker) |
+ : connection_(connection), |
+ callback_(callback), |
+ unblocker_(unblocker), |
+ channel_info_(nullptr), |
+ binding_(this) { |
+ binding_.set_connection_error_handler([this]() { OnConnectionError(); }); |
+ } |
+ |
+ // Callback for |embedder::CreateChannel()|. |
+ void DidCreateChannel(embedder::ChannelInfo* channel_info) { |
+ DVLOG(2) << "ChildControllerImpl::DidCreateChannel()"; |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ channel_info_ = channel_info; |
+ } |
+ |
+ static void ReturnApplicationRequestOnMainThread( |
+ const GotApplicationRequestCallback& callback, |
+ InterfaceRequest<Application> application_request) { |
+ callback.Run(application_request.Pass()); |
+ } |
+ |
+ base::ThreadChecker thread_checker_; |
+ RunnerConnectionImpl* const connection_; |
+ GotApplicationRequestCallback callback_; |
+ Blocker::Unblocker unblocker_; |
+ StartAppCallback on_app_complete_; |
+ |
+ embedder::ChannelInfo* channel_info_; |
+ Binding<ChildController> binding_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ChildControllerImpl); |
+}; |
+ |
+bool RunnerConnectionImpl::WaitForApplicationRequest( |
+ InterfaceRequest<Application>* request) { |
+ embedder::ScopedPlatformHandle platform_channel = |
+ embedder::PlatformChannelPair::PassClientHandleFromParentProcess( |
+ *base::CommandLine::ForCurrentProcess()); |
+ if (!platform_channel.is_valid()) |
+ return false; |
+ |
+ Blocker blocker; |
+ controller_runner_->PostTask( |
+ FROM_HERE, |
+ base::Bind( |
+ &ChildControllerImpl::Create, base::Unretained(this), |
+ base::Bind(&OnGotApplicationRequest, base::Unretained(request)), |
+ base::Passed(&platform_channel), blocker.GetUnblocker())); |
+ blocker.Block(); |
+ |
+ return true; |
+} |
+ |
+} // namespace |
+ |
+RunnerConnection::~RunnerConnection() {} |
+ |
+// static |
+RunnerConnection* RunnerConnection::ConnectToRunner( |
+ InterfaceRequest<Application>* request) { |
+ RunnerConnectionImpl* connection = new RunnerConnectionImpl; |
+ if (!connection->WaitForApplicationRequest(request)) { |
+ delete connection; |
+ return nullptr; |
+ } |
+ return connection; |
+} |
+ |
+RunnerConnection::RunnerConnection() {} |
+ |
+} // namespace runner |
+} // namespace mojo |