Mojo Example that demos the difference between providing and requesting an interface.
BUG=
R=viettrungluu@chromium.org, yzshen@chromium.org
Committed: https://chromium.googlesource.com/external/mojo/+/405bc1690e199f5d4bf5d75b998c595c87f914f8
Review URL: https://codereview.chromium.org/733563002
diff --git a/examples/BUILD.gn b/examples/BUILD.gn
index a3e045d..7dc5072 100644
--- a/examples/BUILD.gn
+++ b/examples/BUILD.gn
@@ -13,6 +13,7 @@
"//examples/echo",
"//examples/ganesh_app",
"//examples/http_handler",
+ "//examples/indirect_service",
"//examples/png_viewer",
"//examples/recursive_content_handler",
"//examples/sample_app",
diff --git a/examples/indirect_service/BUILD.gn b/examples/indirect_service/BUILD.gn
new file mode 100644
index 0000000..0c56928
--- /dev/null
+++ b/examples/indirect_service/BUILD.gn
@@ -0,0 +1,63 @@
+# 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.
+
+import("//mojo/public/mojo_application.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
+
+group("indirect_service") {
+ deps = [
+ ":indirect_service_demo",
+ ":indirect_integer_service",
+ ":integer_service",
+ ]
+}
+
+mojo_native_application("indirect_service_demo") {
+ output_name = "indirect_service_demo"
+
+ sources = [ "indirect_service_demo.cc" ]
+
+ deps = [
+ ":bindings",
+ "//base",
+ "//mojo/application",
+ "//mojo/common",
+ "//mojo/environment:chromium",
+ "//mojo/public/c/system:for_shared_library",
+ "//mojo/public/cpp/bindings",
+ "//mojo/public/cpp/utility",
+ ]
+}
+
+mojo_native_application("integer_service") {
+ output_name = "integer_service"
+
+ sources = [ "integer_service.cc" ]
+
+ deps = [
+ ":bindings",
+ "//mojo/public/c/system:for_shared_library",
+ "//mojo/public/cpp/application:standalone",
+ "//mojo/public/cpp/bindings",
+ "//mojo/public/cpp/utility",
+ ]
+}
+
+mojo_native_application("indirect_integer_service") {
+ output_name = "indirect_integer_service"
+
+ sources = [ "indirect_integer_service.cc" ]
+
+ deps = [
+ ":bindings",
+ "//mojo/public/c/system:for_shared_library",
+ "//mojo/public/cpp/application:standalone",
+ "//mojo/public/cpp/bindings",
+ "//mojo/public/cpp/utility",
+ ]
+}
+
+mojom("bindings") {
+ sources = [ "indirect_service_demo.mojom" ]
+}
diff --git a/examples/indirect_service/README.md b/examples/indirect_service/README.md
new file mode 100644
index 0000000..e26b391
--- /dev/null
+++ b/examples/indirect_service/README.md
@@ -0,0 +1,27 @@
+Indirect Service Demo
+=====================
+
+This demo is intended to highlight the difference between requesting a service
+and providing one. The demo is based on two services: IntegerService and
+IndirectIntegerService.
+
+interface IntegerService {
+ Increment() => (int32 value);
+};
+
+This trival interface just manages a single internal integer that's initialized
+to 0. The Increment() method returns a new value.
+
+interface IndirectIntegerService {
+ Set(IntegerService? service);
+ Get(IntegerService&? service);
+};
+
+This service delegates to the one IntegerService provided by the Set() method.
+Clients use Get() to request a connection to an IntegerService that targets the
+delegate. This is roughly an IntegerService "pointer".
+
+The demo creates a set of threads all of which get their own connection to the
+shared IntegerService via the IndirectIntegerService. The threads all access
+the IntegerService at the same time and then display a little table of the
+results.
diff --git a/examples/indirect_service/indirect_integer_service.cc b/examples/indirect_service/indirect_integer_service.cc
new file mode 100644
index 0000000..72cc34e
--- /dev/null
+++ b/examples/indirect_service/indirect_integer_service.cc
@@ -0,0 +1,69 @@
+// 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.
+
+#include "examples/indirect_service/indirect_service_demo.mojom.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_runner.h"
+#include "mojo/public/cpp/application/interface_factory_impl.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace mojo {
+namespace examples {
+
+class IndirectIntegerServiceImpl :
+ public InterfaceImpl<IndirectIntegerService>, public IntegerService {
+ public:
+ IndirectIntegerServiceImpl() {}
+
+ ~IndirectIntegerServiceImpl() override {
+ for (auto itr = bindings_.begin(); itr < bindings_.end(); itr++)
+ delete *itr;
+ }
+
+ // IndirectIntegerService
+
+ void Set(IntegerServicePtr service) override {
+ integer_service_ = service.Pass();
+ }
+
+ void Get(InterfaceRequest<IntegerService> service) override {
+ bindings_.push_back(new Binding<IntegerService>(this, service.Pass()));
+ }
+
+ // IntegerService
+
+ void Increment(const Callback<void(int32_t)>& callback) override {
+ if (integer_service_.get())
+ integer_service_->Increment(callback);
+ }
+
+private:
+ IntegerServicePtr integer_service_;
+ std::vector<Binding<IntegerService>*> bindings_;
+};
+
+class IndirectIntegerServiceAppDelegate : public ApplicationDelegate {
+ public:
+ bool ConfigureIncomingConnection(
+ ApplicationConnection* connection) override {
+ connection->AddService(&indirect_integer_service_factory_);
+ return true;
+ }
+
+ private:
+ InterfaceFactoryImpl<IndirectIntegerServiceImpl>
+ indirect_integer_service_factory_;
+};
+
+} // namespace examples
+} // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+ mojo::ApplicationRunner runner(
+ new mojo::examples::IndirectIntegerServiceAppDelegate);
+ return runner.Run(shell_handle);
+}
+
diff --git a/examples/indirect_service/indirect_service_demo.cc b/examples/indirect_service/indirect_service_demo.cc
new file mode 100644
index 0000000..446df01
--- /dev/null
+++ b/examples/indirect_service/indirect_service_demo.cc
@@ -0,0 +1,156 @@
+// 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.
+
+#include <cstdlib>
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "examples/indirect_service/indirect_service_demo.mojom.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/common/message_pump_mojo.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+
+namespace mojo {
+namespace examples {
+
+class DemoTask;
+
+typedef typename base::Callback<void(DemoTask*, const std::vector<int32_t>&)>
+ DemoTaskFinishedCallback;
+
+// A thread that connects to the IndirectIntegerService, gets a connection
+// to its IntegerService, and then calls Increment() iteration_count times.
+// The results are saved and returned with the finished_callback.
+class DemoTask {
+ public:
+ DemoTask(ScopedMessagePipeHandle proxy_handle,
+ const DemoTaskFinishedCallback& finished_callback,
+ unsigned iteration_count)
+ : proxy_handle_(proxy_handle.Pass()),
+ thread_("DemoTask"),
+ finished_callback_(finished_callback),
+ iteration_count_(iteration_count) {
+
+ base::Thread::Options options;
+ options.message_loop_type = base::MessageLoop::TYPE_CUSTOM;
+ options.message_pump_factory = base::Bind(&common::MessagePumpMojo::Create);
+ CHECK(thread_.StartWithOptions(options));
+
+ thread_.message_loop()->PostTask(
+ FROM_HERE, base::Bind(&DemoTask::Run, base::Unretained(this)));
+ }
+
+ void Run() {
+ integer_service_.Bind(proxy_handle_.Pass());
+ base::Callback<void(int32_t)> callback =
+ base::Bind(&DemoTask::SaveResultAndFinish, base::Unretained(this));
+ for(int unsigned i = 0; i < iteration_count_; i++) {
+ integer_service_->Increment(callback);
+ // To ensure that the DemoTask threads' execution overlaps, sleep.
+ if (i < iteration_count_ - 1)
+ base::PlatformThread::Sleep(
+ base::TimeDelta::FromMilliseconds(rand() % 10));
+ }
+ }
+
+ private:
+ void SaveResultAndFinish(int32_t result) {
+ results_.push_back(result);
+ if (results_.size() == iteration_count_) {
+ integer_service_.reset(); // Must be done on thread_.
+ finished_callback_.Run(this, results_);
+ }
+ }
+
+ ScopedMessagePipeHandle proxy_handle_;
+ base::Thread thread_;
+ IntegerServicePtr integer_service_;
+ DemoTaskFinishedCallback finished_callback_;
+ unsigned iteration_count_;
+ std::vector<int32_t> results_;
+};
+
+// Connect to the IntegerService and give its proxy to the
+// IndirectIntegerService. Start kTaskCount DemoTask threads all of
+// which will use the IndirectIntegerService to get their own connection
+// to the (one) IntegerService. Each DemoTask will call the IntegerService's
+// Increment() method kTaskIterationCount times, collect the results in
+// a vector and return them to FinishDemoTask.
+//
+// The IntegerService, whose value is initially 0, will be called a total of
+// N = |kTaskCount * kTaskIterationCount| times. Each DemoTask's results
+// are displayed in array of length N. Digits appear in positions that
+// correspond to the results obtained by the DemoTask thread. The results
+// show that the DemoTask threads are accessing the Integer in parallel.
+// The fact that only one digit appears in each column shows that things
+// are working correctly.
+class IndirectServiceDemoAppDelegate : public ApplicationDelegate {
+ public:
+ void Initialize(ApplicationImpl* app) override {
+ IntegerServicePtr indirect_service_delegate;
+ app->ConnectToService("mojo:indirect_integer_service",
+ &indirect_integer_service_);
+ app->ConnectToService("mojo:integer_service", &indirect_service_delegate);
+ indirect_integer_service_->Set(indirect_service_delegate.Pass());
+
+ for (unsigned i = 0; i < kTaskCount; i++) {
+ IntegerServicePtr integer_service;
+ indirect_integer_service_->Get(GetProxy(&integer_service));
+ DemoTaskFinishedCallback finished_callback = base::Bind(
+ &IndirectServiceDemoAppDelegate::FinishDemoTask,
+ base::Unretained(this),
+ base::Unretained(base::MessageLoop::current()));
+ // We're passing the integer_service_ proxy to another thread, so
+ // use its MessagePipe.
+ tasks_.push_back(new DemoTask(integer_service.PassMessagePipe(),
+ finished_callback,
+ kTaskIterationCount));
+ }
+ }
+
+ private:
+ static const unsigned kTaskCount = 10;
+ static const unsigned kTaskIterationCount = 6;
+
+ // This method is called on a DemoTask thread. It just calls DoFinishDemoTask
+ // on the application's run loop. Doing so serializes the DoFinishDemoTask
+ // calls.
+ void FinishDemoTask(base::MessageLoop *run_loop,
+ DemoTask* task,
+ const std::vector<int32_t>& results) {
+ run_loop->PostTask(FROM_HERE, base::Bind(
+ &IndirectServiceDemoAppDelegate::DoFinishDemoTask,
+ base::Unretained(this),
+ base::Unretained(task),
+ results));
+ }
+
+ void DoFinishDemoTask(DemoTask* task, const std::vector<int32_t>& results) {
+ std::string display(kTaskCount * kTaskIterationCount, ' ');
+ for (unsigned i = 0; i < results.size(); i++)
+ display[results[i]] = '0' + (results[i] % 10);
+ printf("DemoTask Thread [%s]\n", display.c_str());
+ tasks_.erase(std::remove(tasks_.begin(), tasks_.end(), task), tasks_.end());
+ delete task; // Stop the DemoTask's thread etc.
+ if (tasks_.empty())
+ ApplicationImpl::Terminate();
+ }
+
+ IndirectIntegerServicePtr indirect_integer_service_;
+ std::vector<DemoTask*> tasks_;
+};
+
+
+} // namespace examples
+} // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+ mojo::ApplicationRunnerChromium runner(
+ new mojo::examples::IndirectServiceDemoAppDelegate);
+ return runner.Run(shell_handle);
+}
diff --git a/examples/indirect_service/indirect_service_demo.mojom b/examples/indirect_service/indirect_service_demo.mojom
new file mode 100644
index 0000000..830fdba
--- /dev/null
+++ b/examples/indirect_service/indirect_service_demo.mojom
@@ -0,0 +1,14 @@
+// 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.
+
+module mojo.examples;
+
+interface IntegerService {
+ Increment() => (int32 value);
+};
+
+interface IndirectIntegerService {
+ Set(IntegerService? service);
+ Get(IntegerService&? service);
+};
diff --git a/examples/indirect_service/integer_service.cc b/examples/indirect_service/integer_service.cc
new file mode 100644
index 0000000..b3a536f
--- /dev/null
+++ b/examples/indirect_service/integer_service.cc
@@ -0,0 +1,47 @@
+// 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.
+
+#include "examples/indirect_service/indirect_service_demo.mojom.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_runner.h"
+#include "mojo/public/cpp/application/interface_factory_impl.h"
+
+namespace mojo {
+namespace examples {
+
+class IntegerServiceImpl : public InterfaceImpl<IntegerService> {
+ public:
+ IntegerServiceImpl() : value_(0) {}
+ ~IntegerServiceImpl() override {}
+
+ void Increment(const Callback<void(int32_t)>& callback) override {
+ callback.Run(value_++);
+ }
+
+ private:
+ int32_t value_;
+};
+
+class IntegerServiceAppDelegate : public ApplicationDelegate {
+ public:
+ bool ConfigureIncomingConnection(
+ ApplicationConnection* connection) override {
+ connection->AddService(&integer_service_factory_);
+ return true;
+ }
+
+ private:
+ InterfaceFactoryImpl<IntegerServiceImpl> integer_service_factory_;
+};
+
+} // namespace examples
+} // namespace mojo
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+ mojo::ApplicationRunner runner(new mojo::examples::IntegerServiceAppDelegate);
+ return runner.Run(shell_handle);
+}
+