1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
// 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_mojo.h"
#include "ipc/ipc_channel_proxy.h"
#include "ipc/ipc_sync_channel.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/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 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) {
// The sequence of events we'll need to perform are the following:
// 1.) Create the ChannelProxy (specifically a SyncChannel) for the
// receiving end of the IPC communication.
// 2.) Accept the incoming mojo invitation. From the invitation, we
// extract a message pipe that we will feed directly into the
// `IPC::ChannelProxy` to initialize it. This bootstraps the
// bidirectional IPC channel between browser <=> renderer.
// 1.) Create a new IPC::ChannelProxy.
channel_proxy_ = IPC::SyncChannel::Create(
this, io_task_runner, base::SingleThreadTaskRunner::GetCurrentDefault(),
&shutdown_event_);
// 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::ChannelMojo::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.
bool OnMessageReceived(const IPC::Message& msg) override {
LOG(WARNING) << "The renderer received a message";
return true;
}
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.ObjectA") {
// Amazingly enough, if you comment out all of this code, which causes the
// `ObjectA` interface to not get bound and therefore the `DoA()` message
// to never be delivered, the `DoB()` message still gets delivered and
// invoked on `ObjectB`. This is because channel-associated interface
// messages are dispatched very differently than non-channel-associated
// ones, because we can't block at all.
mojo::PendingAssociatedReceiver<codelabs::mojom::ObjectA> pending_a(
std::move(handle));
g_object_a.BindToFrozenTaskRunner(
std::move(pending_a), std::move(initially_frozen_task_runner_));
} else if (interface_name == "codelabs.mojom.ObjectB") {
mojo::PendingAssociatedReceiver<codelabs::mojom::ObjectB> pending_b(
std::move(handle));
g_object_b.Bind(std::move(pending_b));
}
}
std::unique_ptr<IPC::SyncChannel> 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 *still* have their messages delivered.
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 would
// normally block all messages from going to their corresponding
// implementations (i.e., messages bound for ObjectA would be blocked, and
// necessarily subsequent messages bound for ObjectB would *also* be blocked),
// however since the associated interfaces here are specifically
// *channel*-associated, we do not support blocking messages, so they're all
// delivered immediately.
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;
}
|