Commit f38b35b8 authored by kouhei's avatar kouhei Committed by Commit Bot

AgentSchedulingGroupHost: Introduce lifecycle statemachine

This CL introduces a lifecycle statemachine to AgentSchedulingGroupHost.
The `AgentSchedulingGroupHost::state_` makes it explicit which lifestage
the AgentSchedulingGroupHost is currently at, and checks if each method
is called on one of the assumed states.

This is a preparatory work to introducing AgentSchedulingGroupHost
shutdown, which will add more states to the state machine.

Bug: 1141459
Change-Id: Ic8c4f4f85322b52612510b3354d5ae6828311263
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2492281
Commit-Queue: Kouhei Ueno <kouhei@chromium.org>
Auto-Submit: Kouhei Ueno <kouhei@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarDominic Farolino <dom@chromium.org>
Reviewed-by: default avatarTal Pressman <talp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#826664}
parent 186ae3d5
......@@ -6,10 +6,13 @@
#include <memory>
#include "base/feature_list.h"
#include "base/no_destructor.h"
#include "base/stl_util.h"
#include "base/supports_user_data.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/common/agent_scheduling_group.mojom.h"
#include "content/common/renderer.mojom.h"
#include "content/common/state_transitions.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/content_features.h"
#include "ipc/ipc_message.h"
......@@ -174,11 +177,14 @@ AgentSchedulingGroupHost::AgentSchedulingGroupHost(RenderProcessHost& process,
// DO NOT USE |process_| HERE! At this point it (or at least parts of it) is no
// longer valid.
AgentSchedulingGroupHost::~AgentSchedulingGroupHost() = default;
AgentSchedulingGroupHost::~AgentSchedulingGroupHost() {
DCHECK_EQ(state_, LifecycleState::kRenderProcessHostDestroyed);
}
void AgentSchedulingGroupHost::RenderProcessExited(
RenderProcessHost* host,
const ChildProcessTerminationInfo& info) {
SetState(LifecycleState::kRenderProcessExited);
DCHECK_EQ(host, &process_);
// We mirror the RenderProcessHost flow here by resetting our mojos, and
......@@ -200,11 +206,26 @@ void AgentSchedulingGroupHost::RenderProcessExited(
void AgentSchedulingGroupHost::RenderProcessHostDestroyed(
RenderProcessHost* host) {
if (RenderProcessHost::run_renderer_in_process()) {
// In single process mode, RenderProcessExited call is sometimes omitted.
if (state_ != LifecycleState::kBound) {
RenderProcessExited(host, ChildProcessTerminationInfo());
}
}
DCHECK_EQ(state_, LifecycleState::kBound);
DCHECK_EQ(host, &process_);
process_.RemoveObserver(this);
SetState(LifecycleState::kRenderProcessHostDestroyed);
}
RenderProcessHost* AgentSchedulingGroupHost::GetProcess() {
// TODO(crbug.com/1111231): Make the condition below hold.
// Currently the DCHECK doesn't hold, since RenderViewHostImpl outlives
// its associated AgentSchedulingGroupHost, and the dtor queries the
// associated RenderProcessHost to remove itself from the
// PerProcessRenderViewHostSet and RemoveObserver() itself.
// DCHECK_NE(state_, LifecycleState::kRenderProcessHostDestroyed);
return &process_;
}
......@@ -217,41 +238,48 @@ bool AgentSchedulingGroupHost::Init() {
// interfaces.
DCHECK(process_.GetRendererInterface());
DCHECK(mojo_remote_.is_bound());
DCHECK_EQ(state_, LifecycleState::kBound);
return process_.Init();
}
ChannelProxy* AgentSchedulingGroupHost::GetChannel() {
DCHECK_EQ(state_, LifecycleState::kBound);
return process_.GetChannel();
}
bool AgentSchedulingGroupHost::Send(IPC::Message* message) {
DCHECK_EQ(state_, LifecycleState::kBound);
return process_.Send(message);
}
void AgentSchedulingGroupHost::AddRoute(int32_t routing_id,
Listener* listener) {
DCHECK_EQ(state_, LifecycleState::kBound);
DCHECK(!listener_map_.Lookup(routing_id));
listener_map_.AddWithID(listener, routing_id);
process_.AddRoute(routing_id, listener);
}
void AgentSchedulingGroupHost::RemoveRoute(int32_t routing_id) {
DCHECK(listener_map_.Lookup(routing_id));
DCHECK_EQ(state_, LifecycleState::kBound);
listener_map_.Remove(routing_id);
process_.RemoveRoute(routing_id);
}
mojom::RouteProvider* AgentSchedulingGroupHost::GetRemoteRouteProvider() {
DCHECK_EQ(state_, LifecycleState::kBound);
return remote_route_provider_.get();
}
void AgentSchedulingGroupHost::CreateFrame(mojom::CreateFrameParamsPtr params) {
DCHECK_EQ(state_, LifecycleState::kBound);
DCHECK(process_.IsInitializedAndNotDead());
DCHECK(mojo_remote_.is_bound());
mojo_remote_.get()->CreateFrame(std::move(params));
}
void AgentSchedulingGroupHost::CreateView(mojom::CreateViewParamsPtr params) {
DCHECK_EQ(state_, LifecycleState::kBound);
DCHECK(process_.IsInitializedAndNotDead());
DCHECK(mojo_remote_.is_bound());
mojo_remote_.get()->CreateView(std::move(params));
......@@ -260,6 +288,7 @@ void AgentSchedulingGroupHost::CreateView(mojom::CreateViewParamsPtr params) {
void AgentSchedulingGroupHost::DestroyView(
int32_t routing_id,
mojom::AgentSchedulingGroup::DestroyViewCallback callback) {
DCHECK_EQ(state_, LifecycleState::kBound);
if (mojo_remote_.is_bound())
mojo_remote_.get()->DestroyView(routing_id, std::move(callback));
else
......@@ -274,6 +303,7 @@ void AgentSchedulingGroupHost::CreateFrameProxy(
const FrameReplicationState& replicated_state,
const base::UnguessableToken& frame_token,
const base::UnguessableToken& devtools_frame_token) {
DCHECK_EQ(state_, LifecycleState::kBound);
mojo_remote_.get()->CreateFrameProxy(
routing_id, render_view_routing_id, opener_frame_token, parent_routing_id,
replicated_state, frame_token, devtools_frame_token);
......@@ -283,6 +313,7 @@ void AgentSchedulingGroupHost::GetRoute(
int32_t routing_id,
mojo::PendingAssociatedReceiver<blink::mojom::AssociatedInterfaceProvider>
receiver) {
DCHECK_EQ(state_, LifecycleState::kBound);
DCHECK(receiver.is_valid());
associated_interface_provider_receivers_.Add(this, std::move(receiver),
routing_id);
......@@ -292,6 +323,7 @@ void AgentSchedulingGroupHost::GetAssociatedInterface(
const std::string& name,
mojo::PendingAssociatedReceiver<blink::mojom::AssociatedInterface>
receiver) {
DCHECK_EQ(state_, LifecycleState::kBound);
int32_t routing_id =
associated_interface_provider_receivers_.current_context();
......@@ -300,6 +332,7 @@ void AgentSchedulingGroupHost::GetAssociatedInterface(
}
void AgentSchedulingGroupHost::ResetMojo() {
DCHECK_EQ(state_, LifecycleState::kRenderProcessExited);
receiver_.reset();
mojo_remote_.reset();
remote_route_provider_.reset();
......@@ -319,13 +352,21 @@ void AgentSchedulingGroupHost::SetUpMojoIfNeeded() {
DCHECK(process_.GetRendererInterface());
// Make sure that the bind state of all of this class's mojos are equivalent.
DCHECK_EQ(mojo_remote_.is_bound(), receiver_.is_bound());
DCHECK_EQ(receiver_.is_bound(), remote_route_provider_.is_bound());
DCHECK_EQ(remote_route_provider_.is_bound(),
route_provider_receiver_.is_bound());
if (receiver_.is_bound())
if (state_ == LifecycleState::kBound) {
DCHECK(mojo_remote_.is_bound());
DCHECK(receiver_.is_bound());
DCHECK(remote_route_provider_.is_bound());
DCHECK(route_provider_receiver_.is_bound());
return;
}
DCHECK(!mojo_remote_.is_bound());
DCHECK(!receiver_.is_bound());
DCHECK(!remote_route_provider_.is_bound());
DCHECK(!route_provider_receiver_.is_bound());
DCHECK(state_ == LifecycleState::kNewborn ||
state_ == LifecycleState::kRenderProcessExited);
if (should_associate_) {
process_.GetRendererInterface()->CreateAssociatedAgentSchedulingGroup(
......@@ -340,6 +381,47 @@ void AgentSchedulingGroupHost::SetUpMojoIfNeeded() {
mojo_remote_.get()->BindAssociatedRouteProvider(
route_provider_receiver_.BindNewEndpointAndPassRemote(),
remote_route_provider_.BindNewEndpointAndPassReceiver());
SetState(LifecycleState::kBound);
}
void AgentSchedulingGroupHost::SetState(
AgentSchedulingGroupHost::LifecycleState state) {
static const base::NoDestructor<StateTransitions<LifecycleState>> transitions(
StateTransitions<LifecycleState>({
{LifecycleState::kNewborn, {LifecycleState::kBound}},
{LifecycleState::kBound,
{LifecycleState::kRenderProcessExited,
// Note: kRenderProcessHostDestroyed is only reached through kBound
// state. Upon kRenderProcessExited, we immediately setup a
// unclaimed mojo endpoint to be consumed by the next
// renderer process.
LifecycleState::kRenderProcessHostDestroyed}},
{LifecycleState::kRenderProcessExited, {LifecycleState::kBound}},
}));
DCHECK_STATE_TRANSITION(transitions, state_, state);
state_ = state;
}
std::ostream& operator<<(std::ostream& os,
AgentSchedulingGroupHost::LifecycleState state) {
switch (state) {
case AgentSchedulingGroupHost::LifecycleState::kNewborn:
os << "Newborn";
break;
case AgentSchedulingGroupHost::LifecycleState::kBound:
os << "Bound";
break;
case AgentSchedulingGroupHost::LifecycleState::kRenderProcessExited:
os << "RenderProcessExited";
break;
case AgentSchedulingGroupHost::LifecycleState::kRenderProcessHostDestroyed:
os << "RenderProcessHostDestroyed";
break;
default:
os << "<invalid value: " << static_cast<int>(state) << ">";
}
return os;
}
Listener* AgentSchedulingGroupHost::GetListener(int32_t routing_id) {
......
......@@ -12,6 +12,7 @@
#include "content/common/associated_interfaces.mojom.h"
#include "content/common/content_export.h"
#include "content/common/renderer.mojom-forward.h"
#include "content/common/state_transitions.h"
#include "content/public/browser/render_process_host_observer.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/associated_receiver_set.h"
......@@ -58,11 +59,6 @@ class CONTENT_EXPORT AgentSchedulingGroupHost
explicit AgentSchedulingGroupHost(RenderProcessHost& process);
~AgentSchedulingGroupHost() override;
// RenderProcessHostObserver:
void RenderProcessExited(RenderProcessHost* host,
const ChildProcessTerminationInfo& info) override;
void RenderProcessHostDestroyed(RenderProcessHost* host) override;
RenderProcessHost* GetProcess();
// Ensure that the process this AgentSchedulingGroupHost belongs to is alive.
// Returns |false| if any part of the initialization failed.
......@@ -92,6 +88,24 @@ class CONTENT_EXPORT AgentSchedulingGroupHost
const base::UnguessableToken& devtools_frame_token);
private:
enum class LifecycleState {
// Just instantiated, no route assigned yet.
kNewborn,
// Bound mojo connection to the renderer.
kBound,
// Intermediate state between renderer process exit and rebinding mojo
// connections.
kRenderProcessExited,
// RenderProcessHost is destroyed, and `this` is pending for deletion.
// kRenderProcessHostDestroyed is the terminal state of the state machine.
kRenderProcessHostDestroyed,
};
friend StateTransitions<LifecycleState>;
friend std::ostream& operator<<(std::ostream& os, LifecycleState state);
// `MaybeAssociatedReceiver` and `MaybeAssociatedRemote` are temporary helper
// classes that allow us to switch between using associated and non-associated
// mojo interfaces. This behavior is controlled by the
......@@ -173,9 +187,16 @@ class CONTENT_EXPORT AgentSchedulingGroupHost
mojo::PendingAssociatedReceiver<blink::mojom::AssociatedInterface>
receiver) override;
// RenderProcessHostObserver:
void RenderProcessExited(RenderProcessHost* host,
const ChildProcessTerminationInfo& info) override;
void RenderProcessHostDestroyed(RenderProcessHost* host) override;
void ResetMojo();
void SetUpMojoIfNeeded();
void SetState(LifecycleState state);
IPC::Listener* GetListener(int32_t routing_id);
// The RenderProcessHost this AgentSchedulingGroup is assigned to.
......@@ -207,8 +228,12 @@ class CONTENT_EXPORT AgentSchedulingGroupHost
mojo::AssociatedReceiverSet<blink::mojom::AssociatedInterfaceProvider,
int32_t>
associated_interface_provider_receivers_;
LifecycleState state_{LifecycleState::kNewborn};
};
std::ostream& operator<<(std::ostream& os,
AgentSchedulingGroupHost::LifecycleState state);
} // namespace content
#endif
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