
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: Kinuko Yasuda <kinuko@chromium.org> Cr-Commit-Position: refs/heads/master@{#821847}
83 lines
3.0 KiB
C++
83 lines
3.0 KiB
C++
// Copyright (c) 2012 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 CONTENT_UTILITY_UTILITY_THREAD_IMPL_H_
|
|
#define CONTENT_UTILITY_UTILITY_THREAD_IMPL_H_
|
|
|
|
#include <memory>
|
|
|
|
#include "base/callback.h"
|
|
#include "base/compiler_specific.h"
|
|
#include "base/macros.h"
|
|
#include "build/build_config.h"
|
|
#include "content/child/child_thread_impl.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"
|
|
|
|
namespace mojo {
|
|
class ServiceFactory;
|
|
}
|
|
|
|
namespace content {
|
|
class UtilityServiceFactory;
|
|
|
|
// This class represents the background thread where the utility task runs.
|
|
class UtilityThreadImpl : public UtilityThread,
|
|
public ChildThreadImpl {
|
|
public:
|
|
explicit UtilityThreadImpl(base::RepeatingClosure quit_closure);
|
|
// Constructor used when running in single process mode.
|
|
explicit UtilityThreadImpl(const InProcessChildThreadParams& params);
|
|
~UtilityThreadImpl() override;
|
|
void Shutdown() override;
|
|
|
|
// UtilityThread:
|
|
void ReleaseProcess() override;
|
|
void EnsureBlinkInitialized() override;
|
|
#if defined(OS_POSIX) && !defined(OS_ANDROID)
|
|
void EnsureBlinkInitializedWithSandboxSupport() override;
|
|
#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:
|
|
void EnsureBlinkInitializedInternal(bool sandbox_support);
|
|
void Init();
|
|
|
|
// ChildThreadImpl:
|
|
bool OnControlMessageReceived(const IPC::Message& msg) override;
|
|
void RunService(
|
|
const std::string& service_name,
|
|
mojo::PendingReceiver<service_manager::mojom::Service> receiver) override;
|
|
|
|
// blink::Platform implementation if needed.
|
|
std::unique_ptr<blink::Platform> blink_platform_impl_;
|
|
|
|
// 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_;
|
|
|
|
// 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);
|
|
};
|
|
|
|
} // namespace content
|
|
|
|
#endif // CONTENT_UTILITY_UTILITY_THREAD_IMPL_H_
|