Commit c18bc463 authored by Ken Rockot's avatar Ken Rockot Committed by Commit Bot

More consistent order for service process shutdown

Today, service processes self-terminate as soon as they detect peer
closure on their main service pipe. This is detected on the IO thread
and results in the immediate scheduling of a main-thread task to
terminate the process.

Meanwhile, service instances themselves also watch for peer closure on
the same pipe and are notified on whichever thread runs the service (IO
or main thread). This triggers immediate destruction of the service
instance.

Because there is no ordering guarantee between the two independent
signal handlers, the net result is that during clean shutdown of a
service process, the service instance's destructor may not run, or
may run after shutdown has already started. This can be problematic
if the service continues to operate and perform tasks that depend
on a now-partially-shut-down process environment like the task
scheduler.

As a separate but related issue, the pipe-watching logic has been
watching for peer closure when it should really be watching for an
unreadable state (i.e., peer closure AND empty inbound queue). This
means that service termination could race with messages still on the
pipe unless developers are careful to synchronize their browser-side
Remote's teardown against some kind of ack message from the service.

This change does a bunch of stuff all at once to solve these problems:

- Modifies ContentClient ServiceFactory APIs so that they populate a
  shared instance (two shared instances really, one for IO, one for
  main thread) rather than having embedders provide their own instance.
- Gives UtilityThreadImpl and its internal (IO-thread-bound)
  ServiceBinderImpl their own ServiceFactory instances with
  clearly-defined ownership and lifetime.
- Removes independent pipe watching logic from ServiceBinderImpl,
  instead having it track service instance lifetimes from both
  ServiceFactory instances.
- Modifies ServiceFactory's pipe watching logic to watch for an
  unreadable pipe rather than for peer closure.

The net result is that service processes which are cleanly shut down,
meaning they neither crashed nor were reaped during full browser
process shutdown, will always run their service's destructor before
initiating shutdown.

Bug: 1135957
Change-Id: I16adbd7c98b4eb4333a92cd338643d4d5a9f2d6f
Tbr: caseq@chromium.org
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2503346
Commit-Queue: Ken Rockot <rockot@google.com>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Cr-Commit-Position: refs/heads/master@{#821847}
parent 81738229
...@@ -95,11 +95,11 @@ void ChromeContentUtilityClient::UtilityThreadStarted() { ...@@ -95,11 +95,11 @@ void ChromeContentUtilityClient::UtilityThreadStarted() {
} }
} }
mojo::ServiceFactory* void ChromeContentUtilityClient::RegisterMainThreadServices(
ChromeContentUtilityClient::GetMainThreadServiceFactory() { mojo::ServiceFactory& services) {
if (utility_process_running_elevated_) if (utility_process_running_elevated_)
return ::GetElevatedMainThreadServiceFactory(); return ::RegisterElevatedMainThreadServices(services);
return ::GetMainThreadServiceFactory(); return ::RegisterMainThreadServices(services);
} }
void ChromeContentUtilityClient::PostIOThreadCreated( void ChromeContentUtilityClient::PostIOThreadCreated(
...@@ -109,8 +109,9 @@ void ChromeContentUtilityClient::PostIOThreadCreated( ...@@ -109,8 +109,9 @@ void ChromeContentUtilityClient::PostIOThreadCreated(
metrics::CallStackProfileParams::IO_THREAD)); metrics::CallStackProfileParams::IO_THREAD));
} }
mojo::ServiceFactory* ChromeContentUtilityClient::GetIOThreadServiceFactory() { void ChromeContentUtilityClient::RegisterIOThreadServices(
return ::GetIOThreadServiceFactory(); mojo::ServiceFactory& services) {
return ::RegisterIOThreadServices(services);
} }
// static // static
......
...@@ -33,8 +33,8 @@ class ChromeContentUtilityClient : public content::ContentUtilityClient { ...@@ -33,8 +33,8 @@ class ChromeContentUtilityClient : public content::ContentUtilityClient {
void RegisterNetworkBinders( void RegisterNetworkBinders(
service_manager::BinderRegistry* registry) override; service_manager::BinderRegistry* registry) override;
void UtilityThreadStarted() override; void UtilityThreadStarted() override;
mojo::ServiceFactory* GetMainThreadServiceFactory() override; void RegisterMainThreadServices(mojo::ServiceFactory& services) override;
mojo::ServiceFactory* GetIOThreadServiceFactory() override; void RegisterIOThreadServices(mojo::ServiceFactory& services) override;
// See NetworkBinderProvider above. // See NetworkBinderProvider above.
static void SetNetworkBinderCreationCallback( static void SetNetworkBinderCreationCallback(
......
...@@ -252,91 +252,76 @@ auto RunAssistantAudioDecoder( ...@@ -252,91 +252,76 @@ auto RunAssistantAudioDecoder(
} // namespace } // namespace
mojo::ServiceFactory* GetElevatedMainThreadServiceFactory() { void RegisterElevatedMainThreadServices(mojo::ServiceFactory& services) {
// NOTE: This ServiceFactory is only used in utility processes which are run // NOTE: This ServiceFactory is only used in utility processes which are run
// with elevated system privileges. // with elevated system privileges.
// clang-format off
static base::NoDestructor<mojo::ServiceFactory> factory {
#if BUILDFLAG(ENABLE_EXTENSIONS) && defined(OS_WIN) #if BUILDFLAG(ENABLE_EXTENSIONS) && defined(OS_WIN)
// On non-Windows, this service runs in a regular utility process. // On non-Windows, this service runs in a regular utility process.
RunRemovableStorageWriter, services.Add(RunRemovableStorageWriter);
#endif #endif
};
// clang-format on
return factory.get();
} }
mojo::ServiceFactory* GetMainThreadServiceFactory() { void RegisterMainThreadServices(mojo::ServiceFactory& services) {
// clang-format off services.Add(RunFilePatcher);
static base::NoDestructor<mojo::ServiceFactory> factory { services.Add(RunUnzipper);
RunFilePatcher, services.Add(RunLanguageDetectionService);
RunUnzipper, services.Add(RunQRCodeGeneratorService);
RunLanguageDetectionService, services.Add(RunMachineLearningService);
RunQRCodeGeneratorService,
RunMachineLearningService,
#if !defined(OS_ANDROID) #if !defined(OS_ANDROID)
RunProfileImporter, services.Add(RunProfileImporter);
RunMirroringService, services.Add(RunMirroringService);
RunSpeechRecognitionService, services.Add(RunSpeechRecognitionService);
#endif #endif
#if defined(OS_WIN) #if defined(OS_WIN)
RunQuarantineService, services.Add(RunQuarantineService);
RunWindowsUtility, services.Add(RunWindowsUtility);
RunWindowsIconReader, services.Add(RunWindowsIconReader);
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
#if BUILDFLAG(ENABLE_PRINTING) && defined(OS_CHROMEOS) #if BUILDFLAG(ENABLE_PRINTING) && defined(OS_CHROMEOS)
RunCupsIppParser, services.Add(RunCupsIppParser);
#endif #endif
#if BUILDFLAG(FULL_SAFE_BROWSING) || defined(OS_CHROMEOS) #if BUILDFLAG(FULL_SAFE_BROWSING) || defined(OS_CHROMEOS)
RunFileUtil, services.Add(RunFileUtil);
#endif #endif
#if BUILDFLAG(ENABLE_EXTENSIONS) && !defined(OS_WIN) #if BUILDFLAG(ENABLE_EXTENSIONS) && !defined(OS_WIN)
// On Windows, this service runs in an elevated utility process. // On Windows, this service runs in an elevated utility process.
RunRemovableStorageWriter, services.Add(RunRemovableStorageWriter);
#endif #endif
#if BUILDFLAG(ENABLE_EXTENSIONS) || defined(OS_ANDROID) #if BUILDFLAG(ENABLE_EXTENSIONS) || defined(OS_ANDROID)
RunMediaParserFactory, services.Add(RunMediaParserFactory);
#endif #endif
#if BUILDFLAG(ENABLE_PRINT_PREVIEW) || \ #if BUILDFLAG(ENABLE_PRINT_PREVIEW) || \
(BUILDFLAG(ENABLE_PRINTING) && defined(OS_WIN)) (BUILDFLAG(ENABLE_PRINTING) && defined(OS_WIN))
RunPrintingService, services.Add(RunPrintingService);
#endif #endif
#if BUILDFLAG(ENABLE_PRINTING) #if BUILDFLAG(ENABLE_PRINTING)
RunPrintCompositor, services.Add(RunPrintCompositor);
#endif #endif
#if BUILDFLAG(ENABLE_PAINT_PREVIEW) #if BUILDFLAG(ENABLE_PAINT_PREVIEW)
RunPaintPreviewCompositor, services.Add(RunPaintPreviewCompositor);
#endif #endif
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
RunImeService, services.Add(RunImeService);
RunSharing, services.Add(RunSharing);
RunTtsService, services.Add(RunTtsService);
#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT) #if BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
RunAssistantAudioDecoder, services.Add(RunAssistantAudioDecoder);
#endif #endif
#endif #endif
};
// clang-format on
return factory.get();
} }
mojo::ServiceFactory* GetIOThreadServiceFactory() { void RegisterIOThreadServices(mojo::ServiceFactory& services) {
// clang-format off
static base::NoDestructor<mojo::ServiceFactory> factory {
#if !defined(OS_ANDROID) #if !defined(OS_ANDROID)
RunProxyResolver, services.Add(RunProxyResolver);
#endif // !defined(OS_ANDROID) #endif
};
// clang-format on
return factory.get();
} }
...@@ -12,8 +12,8 @@ class ServiceFactory; ...@@ -12,8 +12,8 @@ class ServiceFactory;
// Helpers to run out-of-process services in a dedicated utility process. All // Helpers to run out-of-process services in a dedicated utility process. All
// out-of-process services will need to have their implementation hooked up in // out-of-process services will need to have their implementation hooked up in
// one of these helpers. // one of these helpers.
mojo::ServiceFactory* GetElevatedMainThreadServiceFactory(); void RegisterElevatedMainThreadServices(mojo::ServiceFactory& services);
mojo::ServiceFactory* GetMainThreadServiceFactory(); void RegisterMainThreadServices(mojo::ServiceFactory& services);
mojo::ServiceFactory* GetIOThreadServiceFactory(); void RegisterIOThreadServices(mojo::ServiceFactory& services);
#endif // CHROME_UTILITY_SERVICES_H_ #endif // CHROME_UTILITY_SERVICES_H_
...@@ -2,8 +2,13 @@ ...@@ -2,8 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include <string.h>
#include "base/memory/shared_memory_mapping.h"
#include "base/memory/unsafe_shared_memory_region.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/test/bind_test_util.h" #include "base/test/bind_test_util.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "base/timer/elapsed_timer.h" #include "base/timer/elapsed_timer.h"
...@@ -88,6 +93,36 @@ IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest, RemoteDisconnectQuits) { ...@@ -88,6 +93,36 @@ IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest, RemoteDisconnectQuits) {
observer.WaitForDeath(); observer.WaitForDeath();
} }
IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest, AllMessagesReceived) {
// Verifies that messages sent right before disconnection are always received
// and dispatched by the service before it self-terminates.
EchoServiceProcessObserver observer;
auto echo_service = ServiceProcessHost::Launch<echo::mojom::EchoService>();
const size_t kBufferSize = 256;
const std::string kMessages[] = {
"I thought we were having steamed clams.",
"D'oh, no! I said steamed hams. That's what I call hamburgers.",
"You call hamburgers, \"steamed hams?\"",
"Yes. It's a regional dialect."};
auto region = base::UnsafeSharedMemoryRegion::Create(kBufferSize);
base::WritableSharedMemoryMapping mapping = region.Map();
memset(mapping.memory(), 0, kBufferSize);
// Send several messages, since it helps to verify a lack of raciness between
// service-side message dispatch and service termination.
for (const auto& message : kMessages) {
ASSERT_LE(message.size(), kBufferSize);
echo_service->EchoStringToSharedMemory(message, region.Duplicate());
}
echo_service.reset();
observer.WaitForDeath();
const std::string& kLastMessage = kMessages[base::size(kMessages) - 1];
EXPECT_EQ(0,
memcmp(mapping.memory(), kLastMessage.data(), kLastMessage.size()));
}
IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest, ObserveCrash) { IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest, ObserveCrash) {
EchoServiceProcessObserver observer; EchoServiceProcessObserver observer;
auto echo_service = ServiceProcessHost::Launch<echo::mojom::EchoService>(); auto echo_service = ServiceProcessHost::Launch<echo::mojom::EchoService>();
......
...@@ -16,12 +16,4 @@ bool ContentUtilityClient::HandleServiceRequest( ...@@ -16,12 +16,4 @@ bool ContentUtilityClient::HandleServiceRequest(
return false; return false;
} }
mojo::ServiceFactory* ContentUtilityClient::GetIOThreadServiceFactory() {
return nullptr;
}
mojo::ServiceFactory* ContentUtilityClient::GetMainThreadServiceFactory() {
return nullptr;
}
} // namespace content } // namespace content
...@@ -63,18 +63,16 @@ class CONTENT_EXPORT ContentUtilityClient { ...@@ -63,18 +63,16 @@ class CONTENT_EXPORT ContentUtilityClient {
mojo::PendingReceiver<service_manager::mojom::Service> receiver); mojo::PendingReceiver<service_manager::mojom::Service> receiver);
// Allows the embedder to handle an incoming service interface request to run // Allows the embedder to handle an incoming service interface request to run
// a service on the IO thread. Should return a ServiceFactory instance which // a service on the IO thread.
// lives at least as long as the IO thread, or nullptr.
// //
// Only called from the IO thread. // Only called from the IO thread.
virtual mojo::ServiceFactory* GetIOThreadServiceFactory(); virtual void RegisterIOThreadServices(mojo::ServiceFactory& services) {}
// Allows the embedder to handle an incoming service interface request to run // Allows the embedder to handle an incoming service interface request to run
// a service on the main thread. Should return a ServiceFactory instance which // a service on the main thread.
// which effectively lives forever, or nullptr.
// //
// Only called from the main thread. // Only called from the main thread.
virtual mojo::ServiceFactory* GetMainThreadServiceFactory(); virtual void RegisterMainThreadServices(mojo::ServiceFactory& services) {}
virtual void RegisterNetworkBinders( virtual void RegisterNetworkBinders(
service_manager::BinderRegistry* registry) {} service_manager::BinderRegistry* registry) {}
......
...@@ -172,11 +172,9 @@ bool ShellContentUtilityClient::HandleServiceRequest( ...@@ -172,11 +172,9 @@ bool ShellContentUtilityClient::HandleServiceRequest(
return false; return false;
} }
mojo::ServiceFactory* ShellContentUtilityClient::GetIOThreadServiceFactory() { void ShellContentUtilityClient::RegisterIOThreadServices(
static base::NoDestructor<mojo::ServiceFactory> factory{ mojo::ServiceFactory& services) {
RunEchoService, services.Add(RunEchoService);
};
return factory.get();
} }
void ShellContentUtilityClient::RegisterNetworkBinders( void ShellContentUtilityClient::RegisterNetworkBinders(
......
...@@ -23,7 +23,7 @@ class ShellContentUtilityClient : public ContentUtilityClient { ...@@ -23,7 +23,7 @@ class ShellContentUtilityClient : public ContentUtilityClient {
bool HandleServiceRequest( bool HandleServiceRequest(
const std::string& service_name, const std::string& service_name,
mojo::PendingReceiver<service_manager::mojom::Service> receiver) override; mojo::PendingReceiver<service_manager::mojom::Service> receiver) override;
mojo::ServiceFactory* GetIOThreadServiceFactory() override; void RegisterIOThreadServices(mojo::ServiceFactory& services) override;
void RegisterNetworkBinders( void RegisterNetworkBinders(
service_manager::BinderRegistry* registry) override; service_manager::BinderRegistry* registry) override;
......
...@@ -206,69 +206,35 @@ auto RunXrDeviceService( ...@@ -206,69 +206,35 @@ auto RunXrDeviceService(
} }
#endif #endif
mojo::ServiceFactory& GetIOThreadServiceFactory() { } // namespace
static base::NoDestructor<mojo::ServiceFactory> factory{
// The network service runs on the IO thread because it needs a message
// loop of type IO that can get notified when pipes have data.
RunNetworkService,
};
return *factory;
}
mojo::ServiceFactory& GetMainThreadServiceFactory() { void RegisterIOThreadServices(mojo::ServiceFactory& services) {
// clang-format off // The network service runs on the IO thread because it needs a message
static base::NoDestructor<mojo::ServiceFactory> factory{ // loop of type IO that can get notified when pipes have data.
RunAudio, services.Add(RunNetworkService);
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
RunCdmService, // Add new IO-thread services above this line.
#endif GetContentClient()->utility()->RegisterIOThreadServices(services);
RunDataDecoder,
RunStorageService,
RunTracing,
RunVideoCapture,
#if BUILDFLAG(ENABLE_VR) && !defined(OS_ANDROID)
RunXrDeviceService,
#endif
};
// clang-format on
return *factory;
} }
} // namespace void RegisterMainThreadServices(mojo::ServiceFactory& services) {
services.Add(RunAudio);
void HandleServiceRequestOnIOThread( services.Add(RunDataDecoder);
mojo::GenericPendingReceiver receiver, services.Add(RunStorageService);
base::SequencedTaskRunner* main_thread_task_runner) { services.Add(RunTracing);
if (GetIOThreadServiceFactory().MaybeRunService(&receiver)) services.Add(RunVideoCapture);
return;
// If the request was handled already, we should not reach this point.
DCHECK(receiver.is_valid());
auto* embedder_factory =
GetContentClient()->utility()->GetIOThreadServiceFactory();
if (embedder_factory && embedder_factory->MaybeRunService(&receiver))
return;
DCHECK(receiver.is_valid());
main_thread_task_runner->PostTask(
FROM_HERE,
base::BindOnce(&HandleServiceRequestOnMainThread, std::move(receiver)));
}
void HandleServiceRequestOnMainThread(mojo::GenericPendingReceiver receiver) { #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
if (GetMainThreadServiceFactory().MaybeRunService(&receiver)) services.Add(RunCdmService);
return; #endif
// If the request was handled already, we should not reach this point. #if BUILDFLAG(ENABLE_VR) && !defined(OS_ANDROID)
DCHECK(receiver.is_valid()); services.Add(RunXrDeviceService);
auto* embedder_factory = #endif
GetContentClient()->utility()->GetMainThreadServiceFactory();
if (embedder_factory && embedder_factory->MaybeRunService(&receiver))
return;
DCHECK(receiver.is_valid()); // Add new main-thread services above this line.
DLOG(ERROR) << "Unhandled out-of-process service request for " GetContentClient()->utility()->RegisterMainThreadServices(services);
<< receiver.interface_name().value();
} }
} // namespace content } // namespace content
...@@ -5,17 +5,14 @@ ...@@ -5,17 +5,14 @@
#ifndef CONTENT_UTILITY_SERVICES_H_ #ifndef CONTENT_UTILITY_SERVICES_H_
#define CONTENT_UTILITY_SERVICES_H_ #define CONTENT_UTILITY_SERVICES_H_
#include "base/memory/scoped_refptr.h" namespace mojo {
#include "base/sequenced_task_runner.h" class ServiceFactory;
#include "mojo/public/cpp/bindings/generic_pending_receiver.h" }
namespace content { namespace content {
void HandleServiceRequestOnIOThread( void RegisterIOThreadServices(mojo::ServiceFactory& services);
mojo::GenericPendingReceiver receiver, void RegisterMainThreadServices(mojo::ServiceFactory& services);
base::SequencedTaskRunner* main_thread_task_runner);
void HandleServiceRequestOnMainThread(mojo::GenericPendingReceiver receiver);
} // namespace content } // namespace content
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/containers/unique_ptr_adapters.h" #include "base/containers/unique_ptr_adapters.h"
#include "base/debug/crash_logging.h" #include "base/debug/crash_logging.h"
#include "base/memory/scoped_refptr.h" #include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/no_destructor.h" #include "base/no_destructor.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/sequenced_task_runner.h" #include "base/sequenced_task_runner.h"
...@@ -27,7 +28,7 @@ ...@@ -27,7 +28,7 @@
#include "ipc/ipc_sync_channel.h" #include "ipc/ipc_sync_channel.h"
#include "mojo/public/cpp/bindings/binder_map.h" #include "mojo/public/cpp/bindings/binder_map.h"
#include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/system/simple_watcher.h" #include "mojo/public/cpp/bindings/service_factory.h"
namespace content { namespace content {
...@@ -53,18 +54,34 @@ class ServiceBinderImpl { ...@@ -53,18 +54,34 @@ class ServiceBinderImpl {
if (trace_log->IsProcessNameEmpty()) if (trace_log->IsProcessNameEmpty())
trace_log->set_process_name("Service: " + service_name); trace_log->set_process_name("Service: " + service_name);
// We watch for and terminate on PEER_CLOSED, but we also terminate if the // Ensure the ServiceFactory is (lazily) initialized.
// watcher is cancelled (meaning the local endpoint was closed rather than if (!io_thread_services_) {
// the peer). Hence any breakage of the service pipe leads to termination. io_thread_services_ = std::make_unique<mojo::ServiceFactory>();
auto watcher = std::make_unique<mojo::SimpleWatcher>( RegisterIOThreadServices(*io_thread_services_);
FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC); }
watcher->Watch(receiver->pipe(), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, // Note that this is balanced by `termination_callback` below, which is
base::BindRepeating(&ServiceBinderImpl::OnServicePipeClosed, // always eventually run as long as the process does not begin shutting
base::Unretained(this), watcher.get())); // down beforehand.
service_pipe_watchers_.insert(std::move(watcher)); ++num_service_instances_;
HandleServiceRequestOnIOThread(std::move(*receiver),
main_thread_task_runner_.get()); auto termination_callback =
base::BindOnce(&ServiceBinderImpl::OnServiceTerminated,
weak_ptr_factory_.GetWeakPtr());
if (io_thread_services_->CanRunService(*receiver)) {
io_thread_services_->RunService(std::move(*receiver),
std::move(termination_callback));
return;
}
termination_callback =
base::BindOnce(base::IgnoreResult(&base::SequencedTaskRunner::PostTask),
base::ThreadTaskRunnerHandle::Get(), FROM_HERE,
std::move(termination_callback));
main_thread_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ServiceBinderImpl::TryRunMainThreadService,
std::move(*receiver), std::move(termination_callback)));
} }
static base::Optional<ServiceBinderImpl>& GetInstanceStorage() { static base::Optional<ServiceBinderImpl>& GetInstanceStorage() {
...@@ -73,21 +90,22 @@ class ServiceBinderImpl { ...@@ -73,21 +90,22 @@ class ServiceBinderImpl {
} }
private: private:
void OnServicePipeClosed(mojo::SimpleWatcher* which, static void TryRunMainThreadService(mojo::GenericPendingReceiver receiver,
MojoResult result, base::OnceClosure termination_callback) {
const mojo::HandleSignalsState& state) { // NOTE: UtilityThreadImpl is the only defined subclass of UtilityThread, so
// NOTE: It doesn't matter whether this was peer closure or local closure, // this cast is safe.
// and those are the only two ways this method can be invoked. auto* thread = static_cast<UtilityThreadImpl*>(UtilityThread::Get());
thread->HandleServiceRequest(std::move(receiver),
auto it = service_pipe_watchers_.find(which); std::move(termination_callback));
DCHECK(it != service_pipe_watchers_.end()); }
service_pipe_watchers_.erase(it);
void OnServiceTerminated() {
// No more services running in this process. if (--num_service_instances_ > 0)
if (service_pipe_watchers_.empty()) { return;
main_thread_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&ServiceBinderImpl::ShutDownProcess)); // There are no more services running in this process. Time to terminate.
} main_thread_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&ServiceBinderImpl::ShutDownProcess));
} }
static void ShutDownProcess() { static void ShutDownProcess() {
...@@ -100,12 +118,15 @@ class ServiceBinderImpl { ...@@ -100,12 +118,15 @@ class ServiceBinderImpl {
const scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner_; const scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner_;
// These trap signals on any (unowned) primordial service pipes. We don't // Tracks the number of service instances currently running (or pending
// actually care about the signals so these never get armed. We only watch for // creation) in this process. When the number transitions from non-zero to
// cancellation, because that means the service's primordial pipe handle was // zero, the process will self-terminate.
// closed locally and we treat that as the service calling it quits. int num_service_instances_ = 0;
std::set<std::unique_ptr<mojo::SimpleWatcher>, base::UniquePtrComparator>
service_pipe_watchers_; // Handles service requests for services that must run on the IO thread.
std::unique_ptr<mojo::ServiceFactory> io_thread_services_;
base::WeakPtrFactory<ServiceBinderImpl> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(ServiceBinderImpl); DISALLOW_COPY_AND_ASSIGN(ServiceBinderImpl);
}; };
...@@ -170,6 +191,24 @@ void UtilityThreadImpl::EnsureBlinkInitializedWithSandboxSupport() { ...@@ -170,6 +191,24 @@ void UtilityThreadImpl::EnsureBlinkInitializedWithSandboxSupport() {
} }
#endif #endif
void UtilityThreadImpl::HandleServiceRequest(
mojo::GenericPendingReceiver receiver,
base::OnceClosure termination_callback) {
if (!main_thread_services_) {
main_thread_services_ = std::make_unique<mojo::ServiceFactory>();
RegisterMainThreadServices(*main_thread_services_);
}
if (main_thread_services_->CanRunService(receiver)) {
main_thread_services_->RunService(std::move(receiver),
std::move(termination_callback));
return;
}
DLOG(ERROR) << "Cannot run unknown service: " << *receiver.interface_name();
std::move(termination_callback).Run();
}
void UtilityThreadImpl::EnsureBlinkInitializedInternal(bool sandbox_support) { void UtilityThreadImpl::EnsureBlinkInitializedInternal(bool sandbox_support) {
if (blink_platform_impl_) if (blink_platform_impl_)
return; return;
......
...@@ -7,13 +7,19 @@ ...@@ -7,13 +7,19 @@
#include <memory> #include <memory>
#include "base/callback.h"
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/macros.h" #include "base/macros.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "content/child/child_thread_impl.h" #include "content/child/child_thread_impl.h"
#include "content/public/utility/utility_thread.h" #include "content/public/utility/utility_thread.h"
#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
#include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/platform.h"
namespace mojo {
class ServiceFactory;
}
namespace content { namespace content {
class UtilityServiceFactory; class UtilityServiceFactory;
...@@ -34,6 +40,17 @@ class UtilityThreadImpl : public UtilityThread, ...@@ -34,6 +40,17 @@ class UtilityThreadImpl : public UtilityThread,
void EnsureBlinkInitializedWithSandboxSupport() override; void EnsureBlinkInitializedWithSandboxSupport() override;
#endif #endif
// Handles an incoming service interface receiver from a browser-side
// ServiceProcessHost. This is called only if `receiver` didn't first match
// any registered IO-thread service handlers in this process. If successful,
// `termination_callback` will eventually be invoked when the new service
// instance terminates.
//
// If there is no matching service, `receiver` is discarded and
// `termination_callback` is invoked immediately.
void HandleServiceRequest(mojo::GenericPendingReceiver receiver,
base::OnceClosure termination_callback);
private: private:
void EnsureBlinkInitializedInternal(bool sandbox_support); void EnsureBlinkInitializedInternal(bool sandbox_support);
void Init(); void Init();
...@@ -47,9 +64,16 @@ class UtilityThreadImpl : public UtilityThread, ...@@ -47,9 +64,16 @@ class UtilityThreadImpl : public UtilityThread,
// blink::Platform implementation if needed. // blink::Platform implementation if needed.
std::unique_ptr<blink::Platform> blink_platform_impl_; std::unique_ptr<blink::Platform> blink_platform_impl_;
// Helper to handle incoming RunService calls. // Helper to handle incoming RunService calls. Note that this is deprecated
// and only remains in support of some embedders which haven't migrated away
// from Service Manager-based services yet.
std::unique_ptr<UtilityServiceFactory> service_factory_; std::unique_ptr<UtilityServiceFactory> service_factory_;
// The ServiceFactory used to handle incoming service requests from a
// browser-side ServiceProcessHost. Any service registered here will run on
// the main thread of its service process.
std::unique_ptr<mojo::ServiceFactory> main_thread_services_;
DISALLOW_COPY_AND_ASSIGN(UtilityThreadImpl); DISALLOW_COPY_AND_ASSIGN(UtilityThreadImpl);
}; };
......
...@@ -48,14 +48,11 @@ HeadlessContentUtilityClient::HeadlessContentUtilityClient( ...@@ -48,14 +48,11 @@ HeadlessContentUtilityClient::HeadlessContentUtilityClient(
HeadlessContentUtilityClient::~HeadlessContentUtilityClient() = default; HeadlessContentUtilityClient::~HeadlessContentUtilityClient() = default;
mojo::ServiceFactory* void HeadlessContentUtilityClient::RegisterMainThreadServices(
HeadlessContentUtilityClient::GetMainThreadServiceFactory() { mojo::ServiceFactory& services) {
static base::NoDestructor<mojo::ServiceFactory> factory {
#if BUILDFLAG(ENABLE_PRINTING) #if BUILDFLAG(ENABLE_PRINTING)
RunPrintCompositor, services.Add(RunPrintCompositor);
#endif #endif
};
return factory.get();
} }
void HeadlessContentUtilityClient::RegisterNetworkBinders( void HeadlessContentUtilityClient::RegisterNetworkBinders(
......
...@@ -26,7 +26,7 @@ class HEADLESS_EXPORT HeadlessContentUtilityClient ...@@ -26,7 +26,7 @@ class HEADLESS_EXPORT HeadlessContentUtilityClient
~HeadlessContentUtilityClient() override; ~HeadlessContentUtilityClient() override;
// content::ContentUtilityClient: // content::ContentUtilityClient:
mojo::ServiceFactory* GetMainThreadServiceFactory() override; void RegisterMainThreadServices(mojo::ServiceFactory& services) override;
void RegisterNetworkBinders( void RegisterNetworkBinders(
service_manager::BinderRegistry* registry) override; service_manager::BinderRegistry* registry) override;
......
...@@ -5,33 +5,44 @@ ...@@ -5,33 +5,44 @@
#include "mojo/public/cpp/bindings/service_factory.h" #include "mojo/public/cpp/bindings/service_factory.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/stl_util.h"
namespace mojo { namespace mojo {
ServiceFactory::ServiceFactory() = default;
ServiceFactory::~ServiceFactory() = default; ServiceFactory::~ServiceFactory() = default;
bool ServiceFactory::MaybeRunService(mojo::GenericPendingReceiver* receiver) { bool ServiceFactory::CanRunService(
DCHECK(receiver->is_valid()); const GenericPendingReceiver& receiver) const {
DCHECK(receiver.is_valid());
return base::Contains(constructors_, *receiver.interface_name());
}
bool ServiceFactory::RunService(GenericPendingReceiver receiver,
base::OnceClosure termination_callback) {
DCHECK(receiver.is_valid());
// We grab a weak handle to the receiver's message pipe first. If any function // We grab a weak handle to the receiver's message pipe first. If any function
// accepts the receiver, we will tie its returned object's lifetime to the // accepts the receiver, we will tie its returned object's lifetime to the
// connection state of that pipe. // connection state of that pipe.
MessagePipeHandle pipe = receiver->pipe(); MessagePipeHandle pipe = receiver.pipe();
for (const auto& callback : callbacks_) {
if (auto instance = callback.Run(receiver)) {
DCHECK(!receiver->is_valid());
instance->WatchPipe(
pipe, base::BindOnce(&ServiceFactory::OnInstanceDisconnected,
weak_ptr_factory_.GetWeakPtr(), instance.get()));
instances_.insert(std::move(instance));
return true;
}
DCHECK(receiver->is_valid());
}
return false; auto it = constructors_.find(*receiver.interface_name());
if (it == constructors_.end())
return false;
auto instance = it->second.Run(std::move(receiver));
auto disconnect_callback =
base::BindOnce(&ServiceFactory::OnInstanceDisconnected,
weak_ptr_factory_.GetWeakPtr(), instance.get());
if (termination_callback) {
disconnect_callback =
std::move(disconnect_callback).Then(std::move(termination_callback));
}
instance->WatchPipe(pipe, std::move(disconnect_callback));
instances_.insert(std::move(instance));
return true;
} }
void ServiceFactory::OnInstanceDisconnected(InstanceHolderBase* instance) { void ServiceFactory::OnInstanceDisconnected(InstanceHolderBase* instance) {
...@@ -48,21 +59,26 @@ void ServiceFactory::InstanceHolderBase::WatchPipe( ...@@ -48,21 +59,26 @@ void ServiceFactory::InstanceHolderBase::WatchPipe(
base::OnceClosure disconnect_callback) { base::OnceClosure disconnect_callback) {
DCHECK(!disconnect_callback_); DCHECK(!disconnect_callback_);
disconnect_callback_ = std::move(disconnect_callback); disconnect_callback_ = std::move(disconnect_callback);
watcher_.Watch(pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED, watcher_.Watch(pipe, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
base::BindRepeating(&InstanceHolderBase::OnDisconnect, base::BindRepeating(&InstanceHolderBase::OnPipeSignaled,
base::Unretained(this))); base::Unretained(this)));
} }
void ServiceFactory::InstanceHolderBase::OnDisconnect( void ServiceFactory::InstanceHolderBase::OnPipeSignaled(
MojoResult result, MojoResult result,
const HandleSignalsState& state) { const HandleSignalsState& state) {
// It doesn't matter what the parameters are, since the only way the watcher // We only care about the two conditions below. FAILED_PRECONDITION implies
// can signal is if the peer was closed or the local pipe handle was closed. // that the peer was closed and all its sent messages have been read (and
// The callback always destroys |this| when run, so there's also no chance of // dispatched) locally, while CANCELLED implies that the service pipe was
// this method running more than once. // closed locally. In both cases, we run the callback which will delete
DCHECK(disconnect_callback_); // `this` and, ultimately, the service instance itself.
std::move(disconnect_callback_).Run(); if (result == MOJO_RESULT_FAILED_PRECONDITION ||
result == MOJO_RESULT_CANCELLED) {
watcher_.Cancel();
DCHECK(disconnect_callback_);
std::move(disconnect_callback_).Run();
}
} }
} // namespace mojo } // namespace mojo
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef MOJO_PUBLIC_CPP_BINDINGS_SERVICE_FACTORY_H_ #ifndef MOJO_PUBLIC_CPP_BINDINGS_SERVICE_FACTORY_H_
#define MOJO_PUBLIC_CPP_BINDINGS_SERVICE_FACTORY_H_ #define MOJO_PUBLIC_CPP_BINDINGS_SERVICE_FACTORY_H_
#include <map>
#include <memory> #include <memory>
#include "base/bind.h" #include "base/bind.h"
...@@ -35,11 +36,11 @@ struct ServiceFactoryTraits; ...@@ -35,11 +36,11 @@ struct ServiceFactoryTraits;
// where |T| is any type (generally an implementation of |Interface|), and // where |T| is any type (generally an implementation of |Interface|), and
// |Interface| is a mojom interface. // |Interface| is a mojom interface.
// //
// Any time |MaybeRunService()| is called on the ServiceFactory, it will match // Any time |RunService()| is called on the ServiceFactory, it will match the
// the GenericPendingReceiver argument's interface type against the list of // GenericPendingReceiver argument's interface type against the list of
// factories it has available, and if it finds a match it will run that function // factories it has available and run the corresponding function, retaining
// and retain ownership of the returned object until the corresponding receiver // ownership of the returned object until the corresponding receiver is
// is disconnected. // disconnected.
// //
// Typical usage might look something like: // Typical usage might look something like:
// //
...@@ -51,31 +52,54 @@ struct ServiceFactoryTraits; ...@@ -51,31 +52,54 @@ struct ServiceFactoryTraits;
// return std::make_unique<bar::BarImpl>(std::move(receiver)); // return std::make_unique<bar::BarImpl>(std::move(receiver));
// } // }
// //
// void HandleServiceRequest(mojo::GenericPendingReceiver receiver) { // void RegisterServices(mojo::ServiceFactory& services) {
// static base::NoDestructor<mojo::ServiceFactory> factory{ // services.Add(RunFooService);
// RunFooService, // services.Add(RunBarService);
// RunBarService, // }
// };
// //
// if (!factory->MaybeRunService(&receiver)) { // void HandleServiceRequest(const mojo::ServiceFactory& factory,
// // The receiver was for neither the Foo nor Bar service. Sad! // mojo::GenericPendingReceiver receiver) {
// LOG(ERROR) << "Unknown service: " << *receiver.interface_name(); // if (factory.CanRunService(receiver)) {
// factory.RunService(std::move(receiver), base::NullCallback());
// return;
// } // }
//
// // The receiver was for neither the Foo nor Bar service. Sad!
// LOG(ERROR) << "Unknown service: " << *receiver.interface_name();
// } // }
// //
class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) ServiceFactory { class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) ServiceFactory {
public: public:
template <typename... Funcs> ServiceFactory();
explicit ServiceFactory(Funcs... fns)
: callbacks_({base::BindRepeating(&RunFunction<Funcs>, fns)...}) {}
~ServiceFactory(); ~ServiceFactory();
// Attempts to run a service supported by this factory. // Adds a new service to the factory. The argument may be any function that
// accepts a single PendingReceiver<T> and returns a unique_ptr<T>, where T is
// a service interface (that is, a generated mojom interface class
// corresponding to some service's main interface.)
template <typename Func>
void Add(Func func) {
using Interface = typename internal::ServiceFactoryTraits<Func>::Interface;
constructors_[Interface::Name_] =
base::BindRepeating(&RunConstructor<Func>, func);
}
// If `receiver` is references an interface matching a service known to this
// factory, this returns true. Otherwise it returns false. `receiver` MUST be
// valid.
bool CanRunService(const GenericPendingReceiver& receiver) const;
// Consumes `receiver` and binds it to a new instance of the corresponding
// service, constructed using the service's registered function within this
// factory.
//
// `termination_callback`, if not null, will be invoked on the calling
// TaskRunner whenever the new service instance is eventually destroyed.
// //
// Returns |true| and consumes |*receiver| if it is a suitable match for some // If the service represented by `receiver` is not known to this factory, it
// function known by the factory; otherwise returns |false| and leaves // is discarded and `termination_callback` is never run.
// |*receiver| intact. bool RunService(GenericPendingReceiver receiver,
bool MaybeRunService(GenericPendingReceiver* receiver); base::OnceClosure termination_callback);
private: private:
class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) InstanceHolderBase { class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) InstanceHolderBase {
...@@ -87,7 +111,7 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) ServiceFactory { ...@@ -87,7 +111,7 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) ServiceFactory {
base::OnceClosure disconnect_callback); base::OnceClosure disconnect_callback);
private: private:
void OnDisconnect(MojoResult result, const HandleSignalsState& state); void OnPipeSignaled(MojoResult result, const HandleSignalsState& state);
SimpleWatcher watcher_; SimpleWatcher watcher_;
base::OnceClosure disconnect_callback_; base::OnceClosure disconnect_callback_;
...@@ -109,23 +133,20 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) ServiceFactory { ...@@ -109,23 +133,20 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) ServiceFactory {
}; };
template <typename Func> template <typename Func>
static std::unique_ptr<InstanceHolderBase> RunFunction( static std::unique_ptr<InstanceHolderBase> RunConstructor(
Func fn, Func fn,
GenericPendingReceiver* receiver) { GenericPendingReceiver receiver) {
using Interface = typename internal::ServiceFactoryTraits<Func>::Interface; using Interface = typename internal::ServiceFactoryTraits<Func>::Interface;
if (auto typed_receiver = receiver->As<Interface>()) { return std::make_unique<InstanceHolder<Interface>>(
return std::make_unique<InstanceHolder<Interface>>( fn(receiver.As<Interface>()));
fn(std::move(typed_receiver)));
}
return nullptr;
} }
void OnInstanceDisconnected(InstanceHolderBase* instance); void OnInstanceDisconnected(InstanceHolderBase* instance);
using GenericCallback = using Constructor =
base::RepeatingCallback<std::unique_ptr<InstanceHolderBase>( base::RepeatingCallback<std::unique_ptr<InstanceHolderBase>(
GenericPendingReceiver*)>; GenericPendingReceiver)>;
const std::vector<GenericCallback> callbacks_; std::map<std::string, Constructor> constructors_;
base::flat_set<std::unique_ptr<InstanceHolderBase>, base::UniquePtrComparator> base::flat_set<std::unique_ptr<InstanceHolderBase>, base::UniquePtrComparator>
instances_; instances_;
......
...@@ -40,20 +40,10 @@ class TestService1Impl : public mojom::TestService1 { ...@@ -40,20 +40,10 @@ class TestService1Impl : public mojom::TestService1 {
~TestService1Impl() override { ~TestService1Impl() override {
--num_instances_; --num_instances_;
if (destruction_wait_loop_)
destruction_wait_loop_->Quit();
} }
static int num_instances() { return num_instances_; } static int num_instances() { return num_instances_; }
static void WaitForInstanceDestruction() {
static base::NoDestructor<base::Optional<base::RunLoop>> loop;
loop->emplace();
destruction_wait_loop_ = &loop->value();
(*loop)->Run();
destruction_wait_loop_ = nullptr;
}
private: private:
// mojom::TestService1: // mojom::TestService1:
void GetIdentity(GetIdentityCallback callback) override { void GetIdentity(GetIdentityCallback callback) override {
...@@ -64,7 +54,6 @@ class TestService1Impl : public mojom::TestService1 { ...@@ -64,7 +54,6 @@ class TestService1Impl : public mojom::TestService1 {
Receiver<mojom::TestService1> receiver_; Receiver<mojom::TestService1> receiver_;
static int num_instances_; static int num_instances_;
static base::RunLoop* destruction_wait_loop_;
DISALLOW_COPY_AND_ASSIGN(TestService1Impl); DISALLOW_COPY_AND_ASSIGN(TestService1Impl);
}; };
...@@ -88,7 +77,6 @@ class TestService2Impl : public mojom::TestService2 { ...@@ -88,7 +77,6 @@ class TestService2Impl : public mojom::TestService2 {
}; };
int TestService1Impl::num_instances_ = 0; int TestService1Impl::num_instances_ = 0;
base::RunLoop* TestService1Impl::destruction_wait_loop_ = nullptr;
auto RunTestService1(PendingReceiver<mojom::TestService1> receiver) { auto RunTestService1(PendingReceiver<mojom::TestService1> receiver) {
return std::make_unique<TestService1Impl>(std::move(receiver)); return std::make_unique<TestService1Impl>(std::move(receiver));
...@@ -99,11 +87,13 @@ auto RunTestService2(PendingReceiver<mojom::TestService2> receiver) { ...@@ -99,11 +87,13 @@ auto RunTestService2(PendingReceiver<mojom::TestService2> receiver) {
} }
TEST_F(ServiceFactoryTest, BasicMatching) { TEST_F(ServiceFactoryTest, BasicMatching) {
ServiceFactory factory{RunTestService1, RunTestService2}; ServiceFactory factory;
factory.Add(RunTestService1);
factory.Add(RunTestService2);
Remote<mojom::TestService1> remote1; Remote<mojom::TestService1> remote1;
GenericPendingReceiver receiver = remote1.BindNewPipeAndPassReceiver(); GenericPendingReceiver receiver = remote1.BindNewPipeAndPassReceiver();
EXPECT_TRUE(factory.MaybeRunService(&receiver)); EXPECT_TRUE(factory.RunService(std::move(receiver), base::NullCallback()));
EXPECT_FALSE(receiver.is_valid()); EXPECT_FALSE(receiver.is_valid());
// Verify that we connected to an instance of TestService1. // Verify that we connected to an instance of TestService1.
...@@ -113,7 +103,7 @@ TEST_F(ServiceFactoryTest, BasicMatching) { ...@@ -113,7 +103,7 @@ TEST_F(ServiceFactoryTest, BasicMatching) {
Remote<mojom::TestService2> remote2; Remote<mojom::TestService2> remote2;
receiver = remote2.BindNewPipeAndPassReceiver(); receiver = remote2.BindNewPipeAndPassReceiver();
EXPECT_TRUE(factory.MaybeRunService(&receiver)); EXPECT_TRUE(factory.RunService(std::move(receiver), base::NullCallback()));
EXPECT_FALSE(receiver.is_valid()); EXPECT_FALSE(receiver.is_valid());
// Verify that we connected to an instance of TestService2. // Verify that we connected to an instance of TestService2.
...@@ -122,64 +112,73 @@ TEST_F(ServiceFactoryTest, BasicMatching) { ...@@ -122,64 +112,73 @@ TEST_F(ServiceFactoryTest, BasicMatching) {
Remote<mojom::TestService3> remote3; Remote<mojom::TestService3> remote3;
receiver = remote3.BindNewPipeAndPassReceiver(); receiver = remote3.BindNewPipeAndPassReceiver();
EXPECT_FALSE(factory.MaybeRunService(&receiver)); EXPECT_FALSE(factory.CanRunService(receiver));
EXPECT_TRUE(receiver.is_valid()); EXPECT_FALSE(factory.RunService(std::move(receiver), base::NullCallback()));
EXPECT_TRUE(receiver.As<mojom::TestService3>()); EXPECT_FALSE(receiver.is_valid());
} }
TEST_F(ServiceFactoryTest, DestroyInstanceOnClientDisconnect) { TEST_F(ServiceFactoryTest, DestroyInstanceOnClientDisconnect) {
ServiceFactory factory{RunTestService1}; ServiceFactory factory;
factory.Add(RunTestService1);
base::RunLoop loop1;
base::OnceClosure quit1 = loop1.QuitClosure();
Remote<mojom::TestService1> remote1; Remote<mojom::TestService1> remote1;
GenericPendingReceiver receiver = remote1.BindNewPipeAndPassReceiver(); GenericPendingReceiver receiver = remote1.BindNewPipeAndPassReceiver();
EXPECT_TRUE(factory.MaybeRunService(&receiver)); EXPECT_TRUE(factory.RunService(std::move(receiver), std::move(quit1)));
base::RunLoop loop2;
base::OnceClosure quit2 = loop2.QuitClosure();
Remote<mojom::TestService1> remote2; Remote<mojom::TestService1> remote2;
receiver = remote2.BindNewPipeAndPassReceiver(); receiver = remote2.BindNewPipeAndPassReceiver();
EXPECT_TRUE(factory.MaybeRunService(&receiver)); EXPECT_TRUE(factory.RunService(std::move(receiver), std::move(quit2)));
remote1.FlushForTesting(); remote1.FlushForTesting();
remote2.FlushForTesting(); remote2.FlushForTesting();
EXPECT_EQ(2, TestService1Impl::num_instances()); EXPECT_EQ(2, TestService1Impl::num_instances());
remote1.reset(); remote1.reset();
TestService1Impl::WaitForInstanceDestruction(); loop1.Run();
EXPECT_EQ(1, TestService1Impl::num_instances()); EXPECT_EQ(1, TestService1Impl::num_instances());
remote2.FlushForTesting(); remote2.FlushForTesting();
EXPECT_EQ(1, TestService1Impl::num_instances()); EXPECT_EQ(1, TestService1Impl::num_instances());
remote2.reset(); remote2.reset();
TestService1Impl::WaitForInstanceDestruction(); loop2.Run();
EXPECT_EQ(0, TestService1Impl::num_instances()); EXPECT_EQ(0, TestService1Impl::num_instances());
} }
TEST_F(ServiceFactoryTest, DestroyInstanceOnServiceDisconnect) { TEST_F(ServiceFactoryTest, DestroyInstanceOnServiceDisconnect) {
ServiceFactory factory{RunTestService1}; ServiceFactory factory;
factory.Add(RunTestService1);
base::RunLoop loop;
base::OnceClosure quit = loop.QuitClosure();
Remote<mojom::TestService1> remote; Remote<mojom::TestService1> remote;
GenericPendingReceiver receiver = remote.BindNewPipeAndPassReceiver(); GenericPendingReceiver receiver = remote.BindNewPipeAndPassReceiver();
EXPECT_TRUE(factory.MaybeRunService(&receiver)); EXPECT_TRUE(factory.RunService(std::move(receiver), std::move(quit)));
remote.FlushForTesting(); remote.FlushForTesting();
EXPECT_EQ(1, TestService1Impl::num_instances()); EXPECT_EQ(1, TestService1Impl::num_instances());
remote->Quit(); remote->Quit();
remote.FlushForTesting(); loop.Run();
EXPECT_EQ(0, TestService1Impl::num_instances()); EXPECT_EQ(0, TestService1Impl::num_instances());
} }
TEST_F(ServiceFactoryTest, DestroyInstancesOnFactoryDestruction) { TEST_F(ServiceFactoryTest, DestroyInstancesOnFactoryDestruction) {
base::Optional<ServiceFactory> factory{base::in_place, RunTestService1}; base::Optional<ServiceFactory> factory{base::in_place};
factory->Add(RunTestService1);
Remote<mojom::TestService1> remote1; Remote<mojom::TestService1> remote1;
GenericPendingReceiver receiver = remote1.BindNewPipeAndPassReceiver(); GenericPendingReceiver receiver = remote1.BindNewPipeAndPassReceiver();
EXPECT_TRUE(factory->MaybeRunService(&receiver)); EXPECT_TRUE(factory->RunService(std::move(receiver), base::NullCallback()));
remote1.FlushForTesting(); remote1.FlushForTesting();
EXPECT_EQ(1, TestService1Impl::num_instances()); EXPECT_EQ(1, TestService1Impl::num_instances());
Remote<mojom::TestService1> remote2; Remote<mojom::TestService1> remote2;
receiver = remote2.BindNewPipeAndPassReceiver(); receiver = remote2.BindNewPipeAndPassReceiver();
EXPECT_TRUE(factory->MaybeRunService(&receiver)); EXPECT_TRUE(factory->RunService(std::move(receiver), base::NullCallback()));
remote2.FlushForTesting(); remote2.FlushForTesting();
EXPECT_EQ(2, TestService1Impl::num_instances()); EXPECT_EQ(2, TestService1Impl::num_instances());
......
...@@ -224,7 +224,11 @@ void SimpleWatcher::ArmOrNotify() { ...@@ -224,7 +224,11 @@ void SimpleWatcher::ArmOrNotify() {
MojoResult ready_result; MojoResult ready_result;
HandleSignalsState ready_state; HandleSignalsState ready_state;
MojoResult rv = Arm(&ready_result, &ready_state); MojoResult rv = Arm(&ready_result, &ready_state);
if (rv == MOJO_RESULT_OK)
// NOTE: If the watched handle has been closed, the above call will result in
// MOJO_RESULT_NOT_FOUND. A MOJO_RESULT_CANCELLED notification will already
// have been posted to this object as a result, so there's nothing else to do.
if (rv == MOJO_RESULT_OK || rv == MOJO_RESULT_NOT_FOUND)
return; return;
DCHECK_EQ(MOJO_RESULT_FAILED_PRECONDITION, rv); DCHECK_EQ(MOJO_RESULT_FAILED_PRECONDITION, rv);
......
...@@ -4,7 +4,10 @@ ...@@ -4,7 +4,10 @@
#include "services/test/echo/echo_service.h" #include "services/test/echo/echo_service.h"
#include <string.h>
#include "base/immediate_crash.h" #include "base/immediate_crash.h"
#include "base/memory/shared_memory_mapping.h"
namespace echo { namespace echo {
...@@ -18,6 +21,13 @@ void EchoService::EchoString(const std::string& input, ...@@ -18,6 +21,13 @@ void EchoService::EchoString(const std::string& input,
std::move(callback).Run(input); std::move(callback).Run(input);
} }
void EchoService::EchoStringToSharedMemory(
const std::string& input,
base::UnsafeSharedMemoryRegion region) {
base::WritableSharedMemoryMapping mapping = region.Map();
memcpy(mapping.memory(), input.data(), input.size());
}
void EchoService::Quit() { void EchoService::Quit() {
receiver_.reset(); receiver_.reset();
} }
......
...@@ -21,6 +21,8 @@ class EchoService : public mojom::EchoService { ...@@ -21,6 +21,8 @@ class EchoService : public mojom::EchoService {
// mojom::EchoService: // mojom::EchoService:
void EchoString(const std::string& input, void EchoString(const std::string& input,
EchoStringCallback callback) override; EchoStringCallback callback) override;
void EchoStringToSharedMemory(const std::string& input,
base::UnsafeSharedMemoryRegion region) override;
void Quit() override; void Quit() override;
void Crash() override; void Crash() override;
......
...@@ -7,4 +7,5 @@ import("//mojo/public/tools/bindings/mojom.gni") ...@@ -7,4 +7,5 @@ import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojom") { mojom("mojom") {
generate_java = true generate_java = true
sources = [ "echo.mojom" ] sources = [ "echo.mojom" ]
public_deps = [ "//mojo/public/mojom/base" ]
} }
...@@ -4,11 +4,17 @@ ...@@ -4,11 +4,17 @@
module echo.mojom; module echo.mojom;
import "mojo/public/mojom/base/shared_memory.mojom";
// Echos its input. // Echos its input.
interface EchoService { interface EchoService {
// Echos the passed-in string. // Echos the passed-in string.
EchoString(string input) => (string echoed_input); EchoString(string input) => (string echoed_input);
// Echos the passed-in string into the provided shared memory buffer.
EchoStringToSharedMemory(string input,
mojo_base.mojom.UnsafeSharedMemoryRegion region);
// Causes the service to disconnect itself. // Causes the service to disconnect itself.
Quit(); Quit();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment