blob: 28015519ae198b76abdf071b1985cdb71ce692df [file]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/run_loop.h"
#include "base/task/sequence_manager/sequence_manager.h"
#include "base/task/sequence_manager/task_queue.h"
#include "codelabs/mojo_examples/mojo_impls.h"
#include "codelabs/mojo_examples/mojom/interface.mojom.h"
#include "codelabs/mojo_examples/process_bootstrapper.h"
#include "ipc/ipc_channel_factory.h"
#include "ipc/ipc_channel_proxy.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/message_pipe.h"
static ObjectAImpl g_object_a;
static ObjectBImpl g_object_b;
class CustomTaskQueue : public base::RefCounted<CustomTaskQueue> {
public:
CustomTaskQueue(base::sequence_manager::SequenceManager& sequence_manager,
const base::sequence_manager::TaskQueue::Spec& spec)
: task_queue_(sequence_manager.CreateTaskQueue(spec)),
voter_(task_queue_->CreateQueueEnabledVoter()) {}
void FreezeTaskQueue() { voter_->SetVoteToEnable(false); }
void UnfreezeTaskQueue() {
LOG(INFO) << "Unfreezing the task queue that `ObjectAImpl` is bound to.";
voter_->SetVoteToEnable(true);
}
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner() const {
return task_queue_->task_runner();
}
private:
~CustomTaskQueue() = default;
friend class base::RefCounted<CustomTaskQueue>;
base::sequence_manager::TaskQueue::Handle task_queue_;
// Used to enable/disable the underlying `TaskQueueImpl`.
std::unique_ptr<base::sequence_manager::TaskQueue::QueueEnabledVoter> voter_;
};
class ProcessImpl : public codelabs::mojom::Process {
public:
ProcessImpl(
scoped_refptr<base::SingleThreadTaskRunner> initially_frozen_task_runner)
: initially_frozen_task_runner_(initially_frozen_task_runner) {}
void SayHello() override {}
void GetAssociatedInterface(
const std::string& name,
mojo::PendingAssociatedReceiver<codelabs::mojom::GenericInterface>
receiver) override {
LOG(INFO) << "Renderer: GetAssociatedInterface() for " << name;
if (name == "ObjectA") {
mojo::PendingAssociatedReceiver<codelabs::mojom::ObjectA> pending_a(
receiver.PassHandle());
g_object_a.BindToFrozenTaskRunner(
std::move(pending_a), std::move(initially_frozen_task_runner_));
} else if (name == "ObjectB") {
mojo::PendingAssociatedReceiver<codelabs::mojom::ObjectB> pending_b(
receiver.PassHandle());
g_object_b.Bind(std::move(pending_b));
}
}
private:
scoped_refptr<base::SingleThreadTaskRunner> initially_frozen_task_runner_;
};
class AssociatedProcess : public codelabs::mojom::AssociatedProcess {
public:
AssociatedProcess(
scoped_refptr<base::SingleThreadTaskRunner> initially_frozen_task_runner)
: initially_frozen_task_runner_(initially_frozen_task_runner) {}
void SetProcess(
mojo::PendingReceiver<codelabs::mojom::Process> process) override {
LOG(INFO) << "Creating process handler";
MakeSelfOwnedReceiver(
std::make_unique<ProcessImpl>(initially_frozen_task_runner_),
std::move(process));
}
private:
scoped_refptr<base::SingleThreadTaskRunner> initially_frozen_task_runner_;
};
class RendererIPCListener : public IPC::Listener {
public:
RendererIPCListener(
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> initially_frozen_task_runner)
: initially_frozen_task_runner_(initially_frozen_task_runner) {
// See 03-channel-associated-interface-freezing/renderer.cc for relevant
// comments here.
// 1.) Create a new IPC::ChannelProxy.
channel_proxy_ = std::make_unique<IPC::ChannelProxy>(
this, io_task_runner,
base::SingleThreadTaskRunner::GetCurrentDefault());
// 2.) Accept the mojo invitation.
mojo::IncomingInvitation invitation = mojo::IncomingInvitation::Accept(
mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine(
*base::CommandLine::ForCurrentProcess()));
mojo::ScopedMessagePipeHandle ipc_bootstrap_pipe =
invitation.ExtractMessagePipe("ipc_bootstrap_pipe");
// Get ready to receive the invitation from the browser process, which bears
// a message pipe represented by `ipc_bootstrap_pipe`.
channel_proxy_->Init(
IPC::ChannelFactory::CreateClientFactory(
std::move(ipc_bootstrap_pipe), /*ipc_task_runner=*/io_task_runner,
/*proxy_task_runner=*/
base::SingleThreadTaskRunner::GetCurrentDefault()),
/*create_pipe_now=*/true);
}
private:
// IPC::Listener implementation.
void OnAssociatedInterfaceRequest(
const std::string& interface_name,
mojo::ScopedInterfaceEndpointHandle handle) override {
std::string tmp_name = interface_name;
LOG(WARNING) << "The renderer received an associated interface request for "
<< tmp_name.c_str();
if (interface_name == "codelabs.mojom.AssociatedProcess") {
mojo::PendingAssociatedReceiver<codelabs::mojom::AssociatedProcess>
pending_process(std::move(handle));
mojo::MakeSelfOwnedAssociatedReceiver(
std::make_unique<AssociatedProcess>(initially_frozen_task_runner_),
std::move(pending_process));
}
}
std::unique_ptr<IPC::ChannelProxy> channel_proxy_;
scoped_refptr<base::SingleThreadTaskRunner> initially_frozen_tq_;
base::WaitableEvent shutdown_event_{
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED};
scoped_refptr<base::SingleThreadTaskRunner> initially_frozen_task_runner_;
};
int main(int argc, char** argv) {
base::AtExitManager exit_manager;
base::CommandLine::Init(argc, argv);
LOG(INFO) << "Renderer: "
<< base::CommandLine::ForCurrentProcess()->GetCommandLineString();
ProcessBootstrapper bootstrapper;
// See the documentation above the corresponding "browser.cc".
bootstrapper.InitMainThread(base::MessagePumpType::DEFAULT);
bootstrapper.InitMojo(/*as_browser_process=*/false);
// This is the task queue that `ObjectAImpl`'s receiver will be bound to. We
// freeze it to demonstrate that channel-associated interfaces bound to frozen
// queues do not have their messages delivered. This is where we differ from
// 03-channel-associated-interface-freezing.
scoped_refptr<CustomTaskQueue> initially_frozen_tq =
base::MakeRefCounted<CustomTaskQueue>(
*bootstrapper.sequence_manager.get(),
base::sequence_manager::TaskQueue::Spec(
base::sequence_manager::QueueName::TEST_TQ));
initially_frozen_tq->FreezeTaskQueue();
// The rest of the magic happens in this object.
std::unique_ptr<RendererIPCListener> renderer_ipc_listener =
std::make_unique<RendererIPCListener>(
/*io_task_runner=*/bootstrapper.io_task_runner,
initially_frozen_tq->task_runner());
// Post a task for 3 seconds from now that will unfreeze the TaskRunner that
// the `codelabs::mojom::ObjectA` implementation is bound to. This blocks
// all IPC messages on the interfaces from being processed.
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(
[](scoped_refptr<CustomTaskQueue> initially_frozen_tq) {
LOG(INFO) << "Unfreezing frozen TaskRunner";
initially_frozen_tq->UnfreezeTaskQueue();
},
initially_frozen_tq),
base::Seconds(3));
// This task is posted first, but will not run until the task runner is
// unfrozen in ~3 seconds.
initially_frozen_tq->task_runner()->PostTask(
FROM_HERE, base::BindOnce([]() {
LOG(WARNING) << "Renderer: This is the first task posted to the frozen "
"TaskRunner. It shouldn't run within the first 2 "
"seconds of the program";
}));
base::RunLoop run_loop;
run_loop.Run();
return 0;
}