summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/chrome_common.gypi5
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/common/multi_process_notification.cc27
-rw-r--r--chrome/common/multi_process_notification.h87
-rw-r--r--chrome/common/multi_process_notification_linux.cc58
-rw-r--r--chrome/common/multi_process_notification_mac.mm156
-rw-r--r--chrome/common/multi_process_notification_unittest.cc222
-rw-r--r--chrome/common/multi_process_notification_win.cc58
8 files changed, 614 insertions, 0 deletions
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi
index a7cd981..7eed4a6 100644
--- a/chrome/chrome_common.gypi
+++ b/chrome/chrome_common.gypi
@@ -112,6 +112,11 @@
'common/multi_process_lock_linux.cc',
'common/multi_process_lock_mac.cc',
'common/multi_process_lock_win.cc',
+ 'common/multi_process_notification.h',
+ 'common/multi_process_notification.cc',
+ 'common/multi_process_notification_linux.cc',
+ 'common/multi_process_notification_mac.mm',
+ 'common/multi_process_notification_win.cc',
'common/nacl_cmd_line.cc',
'common/nacl_cmd_line.h',
'common/nacl_messages.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 415ab2f5..9e78410 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1646,6 +1646,7 @@
'common/json_value_serializer_unittest.cc',
'common/mru_cache_unittest.cc',
'common/multi_process_lock_unittest.cc',
+ 'common/multi_process_notification_unittest.cc',
'common/net/gaia/gaia_auth_fetcher_unittest.cc',
'common/net/gaia/gaia_auth_fetcher_unittest.h',
'common/net/gaia/gaia_authenticator_unittest.cc',
diff --git a/chrome/common/multi_process_notification.cc b/chrome/common/multi_process_notification.cc
new file mode 100644
index 0000000..8eae607
--- /dev/null
+++ b/chrome/common/multi_process_notification.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2011 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 "chrome/common/multi_process_notification.h"
+
+#include "base/task.h"
+
+namespace multi_process_notification {
+
+PerformTaskOnNotification::PerformTaskOnNotification(Task* task) : task_(task) {
+}
+
+PerformTaskOnNotification::~PerformTaskOnNotification() {
+}
+
+void PerformTaskOnNotification::OnNotificationReceived(
+ const std::string& name, Domain domain) {
+ task_->Run();
+ task_.reset();
+}
+
+bool PerformTaskOnNotification::WasNotificationReceived() {
+ return task_.get() == NULL;
+}
+
+} // namespace multi_process_notification
diff --git a/chrome/common/multi_process_notification.h b/chrome/common/multi_process_notification.h
new file mode 100644
index 0000000..91381a6
--- /dev/null
+++ b/chrome/common/multi_process_notification.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2011 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 CHROME_COMMON_MULTI_PROCESS_NOTIFICATION_H_
+#define CHROME_COMMON_MULTI_PROCESS_NOTIFICATION_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/scoped_ptr.h"
+
+class Task;
+class MessageLoop;
+
+// Platform abstraction for a notification that can be sent between processes.
+// Notifications are strings. The string will be prefixed accordingly per
+// platform (so on Mac OS X a "Happy" notification will become
+// "org.chromium.Happy").
+namespace multi_process_notification {
+
+class ListenerImpl;
+
+enum Domain {
+ // Notifications intended to be received by processes running with the
+ // same uid and same profile.
+ ProfileDomain,
+ // Notifications intended to be received by processes running with the
+ // same uid.
+ UserDomain,
+ // Notifications intended to be received by processes running on the
+ // same system.
+ SystemDomain
+};
+
+// Posts a notification |name| to |domain|.
+// Returns true if the notification was posted.
+bool Post(const std::string& name, Domain domain);
+
+// A notification listener. Will listen for a given notification and
+// call the delegate. Note that the delegate is not owned by the listener.
+class Listener {
+ public:
+ class Delegate {
+ public:
+ virtual ~Delegate() { }
+ virtual void OnNotificationReceived(const std::string& name,
+ Domain domain) = 0;
+ };
+
+ Listener(const std::string& name, Domain domain, Delegate* delegate);
+
+ // A destructor is required for scoped_ptr to compile.
+ ~Listener();
+
+ bool Start();
+
+ private:
+ scoped_ptr<ListenerImpl> impl_;
+
+ DISALLOW_COPY_AND_ASSIGN(Listener);
+};
+
+// A delegate implementation that performs a task when a notification is
+// received. Note that it does not check the notification, and will fire
+// for any notification it receives. It will only fire the task on the
+// first notification received.
+class PerformTaskOnNotification : public Listener::Delegate {
+ public:
+ explicit PerformTaskOnNotification(Task* task);
+ virtual ~PerformTaskOnNotification();
+
+ virtual void OnNotificationReceived(const std::string& name,
+ Domain domain) OVERRIDE;
+ bool WasNotificationReceived();
+
+ private:
+ scoped_ptr<Task> task_;
+
+ DISALLOW_COPY_AND_ASSIGN(PerformTaskOnNotification);
+};
+
+} // namespace multi_process_notification
+
+#endif // CHROME_COMMON_MULTI_PROCESS_NOTIFICATION_H_
diff --git a/chrome/common/multi_process_notification_linux.cc b/chrome/common/multi_process_notification_linux.cc
new file mode 100644
index 0000000..8f8370a
--- /dev/null
+++ b/chrome/common/multi_process_notification_linux.cc
@@ -0,0 +1,58 @@
+// Copyright (c) 2011 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 "chrome/common/multi_process_notification.h"
+
+#include "base/logging.h"
+
+namespace multi_process_notification {
+
+bool Post(const std::string& name, Domain domain) {
+ // TODO(dmaclach): Implement
+ NOTIMPLEMENTED();
+ return false;
+}
+
+class ListenerImpl {
+ public:
+ ListenerImpl(const std::string& name,
+ Domain domain,
+ Listener::Delegate* delegate);
+
+ bool Start();
+
+ private:
+ std::string name_;
+ Domain domain_;
+ Listener::Delegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(ListenerImpl);
+};
+
+ListenerImpl::ListenerImpl(const std::string& name,
+ Domain domain,
+ Listener::Delegate* delegate)
+ : name_(name), domain_(domain), delegate_(delegate) {
+}
+
+bool ListenerImpl::Start() {
+ // TODO(dmaclach): Implement
+ NOTIMPLEMENTED();
+ return false;
+}
+
+Listener::Listener(const std::string& name,
+ Domain domain,
+ Listener::Delegate* delegate)
+ : impl_(new ListenerImpl(name, domain, delegate)) {
+}
+
+Listener::~Listener() {
+}
+
+bool Listener::Start() {
+ return impl_->Start();
+}
+
+} // namespace multi_process_notification
diff --git a/chrome/common/multi_process_notification_mac.mm b/chrome/common/multi_process_notification_mac.mm
new file mode 100644
index 0000000..acbe89a
--- /dev/null
+++ b/chrome/common/multi_process_notification_mac.mm
@@ -0,0 +1,156 @@
+// Copyright (c) 2011 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 "chrome/common/multi_process_notification.h"
+
+#import <Foundation/Foundation.h>
+#include <notify.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/eintr_wrapper.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/message_loop.h"
+#include "base/message_pump_libevent.h"
+#include "base/path_service.h"
+#include "base/stringprintf.h"
+#include "base/sys_string_conversions.h"
+#include "base/task.h"
+#include "chrome/common/chrome_paths.h"
+
+namespace {
+
+std::string AddPrefixToNotification(const std::string& name,
+ multi_process_notification::Domain domain) {
+ // The ordering of the components in the string returned by this function
+ // is important. Read "NAMESPACE CONVENTIONS" in 'man 3 notify' for details.
+ base::mac::ScopedNSAutoreleasePool pool;
+ NSBundle *bundle = base::mac::MainAppBundle();
+ NSString *ns_bundle_id = [bundle bundleIdentifier];
+ std::string bundle_id = base::SysNSStringToUTF8(ns_bundle_id);
+ std::string domain_string;
+ switch (domain) {
+ case multi_process_notification::ProfileDomain: {
+ FilePath user_data_dir;
+ if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) {
+ NOTREACHED();
+ }
+ domain_string = StringPrintf("user.uid.%u.%s.",
+ getuid(), user_data_dir.value().c_str());
+ break;
+ }
+
+ case multi_process_notification::UserDomain:
+ domain_string = StringPrintf("user.uid.%u.", getuid());
+ break;
+
+ case multi_process_notification::SystemDomain:
+ break;
+ }
+ return domain_string + bundle_id + "." + name;
+}
+
+
+} // namespace
+
+namespace multi_process_notification {
+
+bool Post(const std::string& name, Domain domain) {
+ std::string notification = AddPrefixToNotification(name, domain);
+ uint32_t status = notify_post(notification.c_str());
+ DCHECK_EQ(status, static_cast<uint32_t>(NOTIFY_STATUS_OK));
+ return status == NOTIFY_STATUS_OK;
+}
+
+
+class ListenerImpl : public base::MessagePumpLibevent::Watcher {
+ public:
+ ListenerImpl(const std::string& name,
+ Domain domain,
+ Listener::Delegate* delegate);
+ virtual ~ListenerImpl();
+
+ bool Start();
+
+ virtual void OnFileCanReadWithoutBlocking(int fd);
+ virtual void OnFileCanWriteWithoutBlocking(int fd);
+
+ private:
+ std::string name_;
+ Domain domain_;
+ Listener::Delegate* delegate_;
+ int fd_;
+ int token_;
+ base::MessagePumpLibevent::FileDescriptorWatcher watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(ListenerImpl);
+};
+
+ListenerImpl::ListenerImpl(const std::string& name,
+ Domain domain,
+ Listener::Delegate* delegate)
+ : name_(name), domain_(domain), delegate_(delegate), fd_(-1), token_(-1) {
+}
+
+ListenerImpl::~ListenerImpl() {
+ if (token_ != -1) {
+ uint32_t status = notify_cancel(token_);
+ DCHECK_EQ(status, static_cast<uint32_t>(NOTIFY_STATUS_OK));
+ token_ = -1;
+ }
+}
+
+bool ListenerImpl::Start() {
+ DCHECK_EQ(fd_, -1);
+ DCHECK_EQ(token_, -1);
+ std::string notification = AddPrefixToNotification(name_, domain_);
+
+ uint32_t status = notify_register_file_descriptor(notification.c_str(), &fd_,
+ 0, &token_);
+ if (status != NOTIFY_STATUS_OK) {
+ LOG(ERROR) << "Unable to notify_register_file_descriptor for '"
+ << notification << "' Status: " << status;
+ return false;
+ }
+
+ MessageLoopForIO *io_loop = MessageLoopForIO::current();
+ return io_loop->WatchFileDescriptor(fd_, true, MessageLoopForIO::WATCH_READ,
+ &watcher_, this);
+}
+
+void ListenerImpl::OnFileCanReadWithoutBlocking(int fd) {
+ DCHECK_EQ(fd, fd_);
+ int token = 0;
+ if (HANDLE_EINTR(read(fd_, &token, sizeof(token))) >= 0) {
+ // Have to swap to native endianness <http://openradar.appspot.com/8821081>.
+ token = static_cast<int>(ntohl(token));
+ if (token == token_) {
+ delegate_->OnNotificationReceived(name_, domain_);
+ } else {
+ LOG(WARNING) << "Unexpected value " << token;
+ }
+ }
+}
+
+void ListenerImpl::OnFileCanWriteWithoutBlocking(int fd) {
+ NOTREACHED();
+}
+
+Listener::Listener(const std::string& name,
+ Domain domain,
+ Listener::Delegate* delegate)
+ : impl_(new ListenerImpl(name, domain, delegate)) {
+}
+
+Listener::~Listener() {
+}
+
+bool Listener::Start() {
+ return impl_->Start();
+}
+
+} // namespace multi_process_notification
diff --git a/chrome/common/multi_process_notification_unittest.cc b/chrome/common/multi_process_notification_unittest.cc
new file mode 100644
index 0000000..f25f441
--- /dev/null
+++ b/chrome/common/multi_process_notification_unittest.cc
@@ -0,0 +1,222 @@
+// Copyright (c) 2011 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 "chrome/common/multi_process_notification.h"
+
+#include "base/basictypes.h"
+#include "base/environment.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/test_timeouts.h"
+#include "base/time.h"
+#include "testing/multiprocess_func_list.h"
+
+#if defined(OS_MACOSX)
+// TODO(dmaclach): Remove defined(OS_MACOSX) once
+// MultiProcessNotification is implemented on Win/Linux.
+
+namespace {
+
+const char kStartedNotificationName[] = "MultiProcessTestStartedNotification";
+const char kQuitNotificationName[] = "MultiProcessTestQuitNotification";
+
+void SpinRunLoop(int milliseconds) {
+ MessageLoopForIO *loop = MessageLoopForIO::current();
+
+ // Post a quit task so that this loop eventually ends and we don't hang
+ // in the case of a bad test. Usually, the run loop will quit sooner than
+ // that because all tests use a MultiProcessNotificationTestQuit which quits
+ // the current run loop when it gets a notification.
+ loop->PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask(), milliseconds);
+ loop->Run();
+}
+
+int MultiProcessNotificationMain(multi_process_notification::Domain domain) {
+ MessageLoop io_loop(MessageLoop::TYPE_IO);
+ multi_process_notification::PerformTaskOnNotification quitter(
+ new MessageLoop::QuitTask());
+ multi_process_notification::Listener listener(
+ kQuitNotificationName, domain, &quitter);
+ EXPECT_TRUE(listener.Start());
+ EXPECT_TRUE(multi_process_notification::Post(kStartedNotificationName,
+ domain));
+ SpinRunLoop(TestTimeouts::action_max_timeout_ms());
+ EXPECT_TRUE(quitter.WasNotificationReceived());
+ return 0;
+}
+
+} // namespace
+
+class MultiProcessNotificationTest : public base::MultiProcessTest {
+ public:
+ MultiProcessNotificationTest();
+
+ void PostNotificationTest(multi_process_notification::Domain domain);
+ void CrossPostNotificationTest(multi_process_notification::Domain domain);
+
+ private:
+ MessageLoop io_loop_;
+};
+
+MultiProcessNotificationTest::MultiProcessNotificationTest()
+ : io_loop_(MessageLoop::TYPE_IO) {
+}
+
+void MultiProcessNotificationTest::PostNotificationTest(
+ multi_process_notification::Domain domain) {
+ multi_process_notification::PerformTaskOnNotification process_started(
+ new MessageLoop::QuitTask());
+ multi_process_notification::Listener listener(kStartedNotificationName,
+ domain,
+ &process_started);
+ ASSERT_TRUE(listener.Start());
+ std::string process_name;
+ switch (domain) {
+ case multi_process_notification::ProfileDomain:
+ process_name = "MultiProcessProfileNotificationMain";
+ break;
+
+ case multi_process_notification::UserDomain:
+ process_name = "MultiProcessUserNotificationMain";
+ break;
+
+ case multi_process_notification::SystemDomain:
+ process_name = "MultiProcessSystemNotificationMain";
+ break;
+ }
+ base::ProcessHandle handle = SpawnChild(process_name, false);
+ ASSERT_TRUE(handle);
+ SpinRunLoop(TestTimeouts::action_max_timeout_ms());
+ ASSERT_TRUE(process_started.WasNotificationReceived());
+ ASSERT_TRUE(multi_process_notification::Post(kQuitNotificationName, domain));
+ int exit_code = 0;
+ EXPECT_TRUE(base::WaitForExitCodeWithTimeout(
+ handle, &exit_code, TestTimeouts::action_max_timeout_ms()));
+}
+
+void MultiProcessNotificationTest::CrossPostNotificationTest(
+ multi_process_notification::Domain domain) {
+ // Check to make sure notifications sent to user domain aren't picked up
+ // by system domain listeners and vice versa.
+ std::string local_notification("QuitLocalNotification");
+ std::string final_notification("FinalQuitLocalNotification");
+ multi_process_notification::PerformTaskOnNotification profile_quitter(
+ new MessageLoop::QuitTask());
+ multi_process_notification::PerformTaskOnNotification user_quitter(
+ new MessageLoop::QuitTask());
+ multi_process_notification::PerformTaskOnNotification system_quitter(
+ new MessageLoop::QuitTask());
+ multi_process_notification::PerformTaskOnNotification final_quitter(
+ new MessageLoop::QuitTask());
+ multi_process_notification::Listener profile_listener(
+ local_notification, multi_process_notification::ProfileDomain,
+ &profile_quitter);
+ multi_process_notification::Listener user_listener(
+ local_notification, multi_process_notification::UserDomain,
+ &user_quitter);
+ multi_process_notification::Listener system_listener(
+ local_notification, multi_process_notification::SystemDomain,
+ &system_quitter);
+ multi_process_notification::Listener final_listener(
+ final_notification, multi_process_notification::UserDomain,
+ &final_quitter);
+
+ ASSERT_TRUE(profile_listener.Start());
+ ASSERT_TRUE(user_listener.Start());
+ ASSERT_TRUE(system_listener.Start());
+ ASSERT_TRUE(multi_process_notification::Post(local_notification, domain));
+ SpinRunLoop(TestTimeouts::action_timeout_ms());
+
+ // Now send out a final_notification to queue up a notification
+ // after the local_notification and make sure that all listeners have had a
+ // chance to process local_notification before we check to see if they
+ // were called.
+ ASSERT_TRUE(final_listener.Start());
+ ASSERT_TRUE(multi_process_notification::Post(
+ final_notification, multi_process_notification::UserDomain));
+ SpinRunLoop(TestTimeouts::action_timeout_ms());
+ ASSERT_TRUE(final_quitter.WasNotificationReceived());
+ switch (domain) {
+ case multi_process_notification::ProfileDomain:
+ ASSERT_TRUE(profile_quitter.WasNotificationReceived());
+ ASSERT_FALSE(user_quitter.WasNotificationReceived());
+ ASSERT_FALSE(system_quitter.WasNotificationReceived());
+ break;
+
+ case multi_process_notification::UserDomain:
+ ASSERT_FALSE(profile_quitter.WasNotificationReceived());
+ ASSERT_TRUE(user_quitter.WasNotificationReceived());
+ ASSERT_FALSE(system_quitter.WasNotificationReceived());
+ break;
+
+ case multi_process_notification::SystemDomain:
+ ASSERT_FALSE(profile_quitter.WasNotificationReceived());
+ ASSERT_FALSE(user_quitter.WasNotificationReceived());
+ ASSERT_TRUE(system_quitter.WasNotificationReceived());
+ break;
+ }
+}
+
+TEST_F(MultiProcessNotificationTest, BasicCreationTest) {
+ multi_process_notification::Listener local_listener(
+ "BasicCreationTest", multi_process_notification::UserDomain, NULL);
+ ASSERT_TRUE(local_listener.Start());
+ multi_process_notification::Listener system_listener(
+ "BasicCreationTest", multi_process_notification::SystemDomain, NULL);
+ ASSERT_TRUE(system_listener.Start());
+}
+
+TEST_F(MultiProcessNotificationTest, PostInProcessNotification) {
+ std::string local_notification("QuitLocalNotification");
+ multi_process_notification::PerformTaskOnNotification quitter(
+ new MessageLoop::QuitTask());
+ multi_process_notification::Listener listener(
+ local_notification, multi_process_notification::UserDomain, &quitter);
+
+ ASSERT_TRUE(listener.Start());
+ ASSERT_TRUE(multi_process_notification::Post(
+ local_notification, multi_process_notification::UserDomain));
+ SpinRunLoop(TestTimeouts::action_max_timeout_ms());
+ ASSERT_TRUE(quitter.WasNotificationReceived());
+}
+
+TEST_F(MultiProcessNotificationTest, PostProfileNotification) {
+ PostNotificationTest(multi_process_notification::ProfileDomain);
+}
+
+TEST_F(MultiProcessNotificationTest, PostUserNotification) {
+ PostNotificationTest(multi_process_notification::UserDomain);
+}
+
+TEST_F(MultiProcessNotificationTest, PostSystemNotification) {
+ PostNotificationTest(multi_process_notification::SystemDomain);
+}
+
+TEST_F(MultiProcessNotificationTest, ProfileCrossDomainPosting) {
+ CrossPostNotificationTest(multi_process_notification::ProfileDomain);
+}
+
+TEST_F(MultiProcessNotificationTest, UserCrossDomainPosting) {
+ CrossPostNotificationTest(multi_process_notification::UserDomain);
+}
+
+TEST_F(MultiProcessNotificationTest, SystemCrossDomainPosting) {
+ CrossPostNotificationTest(multi_process_notification::SystemDomain);
+}
+
+MULTIPROCESS_TEST_MAIN(MultiProcessProfileNotificationMain) {
+ return MultiProcessNotificationMain(
+ multi_process_notification::ProfileDomain);
+}
+
+MULTIPROCESS_TEST_MAIN(MultiProcessUserNotificationMain) {
+ return MultiProcessNotificationMain(multi_process_notification::UserDomain);
+}
+
+MULTIPROCESS_TEST_MAIN(MultiProcessSystemNotificationMain) {
+ return MultiProcessNotificationMain(multi_process_notification::SystemDomain);
+}
+
+#endif // defined(OS_MACOSX)
diff --git a/chrome/common/multi_process_notification_win.cc b/chrome/common/multi_process_notification_win.cc
new file mode 100644
index 0000000..8f8370a
--- /dev/null
+++ b/chrome/common/multi_process_notification_win.cc
@@ -0,0 +1,58 @@
+// Copyright (c) 2011 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 "chrome/common/multi_process_notification.h"
+
+#include "base/logging.h"
+
+namespace multi_process_notification {
+
+bool Post(const std::string& name, Domain domain) {
+ // TODO(dmaclach): Implement
+ NOTIMPLEMENTED();
+ return false;
+}
+
+class ListenerImpl {
+ public:
+ ListenerImpl(const std::string& name,
+ Domain domain,
+ Listener::Delegate* delegate);
+
+ bool Start();
+
+ private:
+ std::string name_;
+ Domain domain_;
+ Listener::Delegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(ListenerImpl);
+};
+
+ListenerImpl::ListenerImpl(const std::string& name,
+ Domain domain,
+ Listener::Delegate* delegate)
+ : name_(name), domain_(domain), delegate_(delegate) {
+}
+
+bool ListenerImpl::Start() {
+ // TODO(dmaclach): Implement
+ NOTIMPLEMENTED();
+ return false;
+}
+
+Listener::Listener(const std::string& name,
+ Domain domain,
+ Listener::Delegate* delegate)
+ : impl_(new ListenerImpl(name, domain, delegate)) {
+}
+
+Listener::~Listener() {
+}
+
+bool Listener::Start() {
+ return impl_->Start();
+}
+
+} // namespace multi_process_notification