122版本自带的mojom通信例子channel-associated-interface 仅供学习参考:
codelabs\mojo_examples\03-channel-associated-interface-freezing
其余定义参考上一篇文章:
Chromium Mojo(IPC)进程通信演示 c++(2)-CSDN博客
03-mojo-browser.exe 与 03-mojo-renderer.exe进程通信完整例子。
一、目录结构:
二、03-mojo-browser.exe
codelabs\mojo_examples\03-channel-associated-interface-freezing\browser.cc
// 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 "base/command_line.h"
#include "base/logging.h"
#include "base/message_loop/message_pump.h"
#include "base/process/launch.h"
#include "base/run_loop.h"
#include "base/task/sequence_manager/sequence_manager.h"
#include "base/threading/thread.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 "mojo/core/embedder/embedder.h"
#include "mojo/core/embedder/scoped_ipc_support.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/message_pipe.h"
mojo::ScopedMessagePipeHandle LaunchAndConnect() {
// Under the hood, this is essentially always an OS pipe (domain socket pair,
// Windows named pipe, Fuchsia channel, etc).
mojo::PlatformChannel channel;
mojo::OutgoingInvitation invitation;
// Attach a message pipe to be extracted by the receiver. The other end of the
// pipe is returned for us to use locally.
mojo::ScopedMessagePipeHandle ipc_bootstrap_pipe =
invitation.AttachMessagePipe("ipc_bootstrap_pipe");
base::LaunchOptions options;
// This is the relative path to the mock "renderer process" binary. We pass it
// into `base::LaunchProcess` to run the binary in a new process.
static const base::CommandLine::CharType* argv[] = {
FILE_PATH_LITERAL("./03-mojo-renderer")};
base::CommandLine command_line(1, argv);
channel.PrepareToPassRemoteEndpoint(&options, &command_line);
LOG(INFO) << "Browser: " << command_line.GetCommandLineString();
base::Process child_process = base::LaunchProcess(command_line, options);
channel.RemoteProcessLaunchAttempted();
mojo::OutgoingInvitation::Send(std::move(invitation), child_process.Handle(),
channel.TakeLocalEndpoint());
return ipc_bootstrap_pipe;
}
class BrowserIPCListener : public IPC::Listener {
public:
BrowserIPCListener(mojo::ScopedMessagePipeHandle ipc_bootstrap_pipe,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
: IPC::Listener() {
// This program differs from `02-associated-interface-freezing`, because
// we're using channel-associated interfaces as opposed to non-channel
// associated interfaces. This means we need to set up an
// `IPC::ChannelProxy` in addition to the regular mojo stuff. The sequence
// of events will look like so:
// 1.) Bootstrap the IPC channel. This is actually pretty easy. We just a
// pull a message pipe handle off of the mojo invitation we sent to
// the renderer process earlier, and feed that pipe handle into the
// IPC::ChannelProxy. From there, we can start requesting remote
// associated interfaces directly from the IPC channel.
// 2.) Requesting a remote channel-associated interface from the
// IPC::Channel mojo connection, for interface
// `codelabs::mojom::ObjectA`
// 3.) Do the same thing as (2) but for `codelabs::mojom::ObjectB`. Both
// of our `ObjectA` and `ObjectB` connections are channel-associated,
// however the remote "renderer" process will bind the backing
// mojo::AssociatedReceiver for each of these to different TaskQueues.
//
// We first send a message to `ObjectA`, whose backing
// mojo::AssociatedReceiver is bound to a TaskQueue that is initially
// frozen
//
// We then send a message to `ObjectB`, whose backing
// mojo::AssociatedReceiver is bound to a normal unfrozen TaskQueue.
//
// From this we see two results:
// - The message for `ObjectA` is not delayed, despite its
// AssociatedReceiver being bound to a frozen TaskQueue. This is
// because we cannot delay channel-associated message from being
// delivered due to legacy IPC deadlock reasons
// - If you then comment out the part of the renderer code that
// binds the `ObjectA` interface (so that you prevent it from ever
// being bound to an implementation), you then observe that the
// `ObjectB` message is not blocked at all on the `ObjectA`
// message, for the same reasons above.
// 1.) Bootstrap the IPC Channel.
std::unique_ptr<IPC::ChannelFactory> channel_factory =
IPC::ChannelMojo::CreateServerFactory(
std::move(ipc_bootstrap_pipe), io_task_runner,
base::SingleThreadTaskRunner::GetCurrentDefault());
channel_proxy_ = IPC::ChannelProxy::Create(
std::move(channel_factory), this, /*ipc_task_runner=*/io_task_runner,
/*listener_task_runner=*/
base::SingleThreadTaskRunner::GetCurrentDefault());
// 2.) Bind and send an IPC to ObjectA.
mojo::AssociatedRemote<codelabs::mojom::ObjectA> remote_a;
channel_proxy_->GetRemoteAssociatedInterface(&remote_a);
remote_a->DoA();
// 3.) Bind and send an IPC to ObjectB.
mojo::AssociatedRemote<codelabs::mojom::ObjectB> remote_b;
channel_proxy_->GetRemoteAssociatedInterface(&remote_b);
remote_b->DoB();
}
// IPC::Listener implementation.
bool OnMessageReceived(const IPC::Message& msg) override {
CHECK(false) << "The browser should not receive messages";
return false;
}
void OnAssociatedInterfaceRequest(
const std::string& interface_name,
mojo::ScopedInterfaceEndpointHandle handle) override {
CHECK(false)
<< "The browser should not receive associated interface requests";
}
private:
std::unique_ptr<IPC::ChannelProxy> channel_proxy_;
};
int main(int argc, char** argv) {
LOG(INFO) << "Browser process starting up";
base::CommandLine::Init(argc, argv);
ProcessBootstrapper bootstrapper;
// The IO thread that the `BrowserIPCListener` ChannelProxy listens for
// messages on *must* be different than the main thread, so in this example
// (and in the corresponding "renderer.cc") we initialize the main thread with
// a "DEFAULT" (i.e., non-IO-capable) main thread. This will automatically
// give us a separate dedicated IO thread for Mojo and the IPC infrastructure
// to use.
bootstrapper.InitMainThread(base::MessagePumpType::DEFAULT);
bootstrapper.InitMojo(/*as_browser_process=*/true);
mojo::ScopedMessagePipeHandle handle = LaunchAndConnect();
// Create a new `BrowserIPCListener` to sponsor communication coming from the
// "browser" process. The rest of the program will execute there.
std::unique_ptr<BrowserIPCListener> browser_ipc_listener =
std::make_unique<BrowserIPCListener>(std::move(handle),
bootstrapper.io_task_runner);
base::RunLoop run_loop;
// Delay shutdown of the browser process for visual effects, as well as to
// ensure the browser process doesn't die while the IPC message is still being
// sent to the target process asynchronously, which would prevent its
// delivery. This delay is an arbitrary 5 seconds, which just needs to be
// longer than the renderer's 3 seconds, which is used to show visually via
// logging, how the ordering of IPCs can be effected by a frozen task queue
// that gets unfrozen 3 seconds later.
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(
[](base::OnceClosure quit_closure) {
LOG(INFO) << "'Browser process' shutting down";
std::move(quit_closure).Run();
},
run_loop.QuitClosure()),
base::Seconds(5));
run_loop.Run();
return 0;
}
三、03-mojo-renderer.exe
codelabs\mojo_examples\03-channel-associated-interface-freezing\renderer.cc
// 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;
}
四、编译
1、gn gen out/debug
2、 ninja -C out/debug 03-mojo-browser
生成03-mojo-browser.exe
3、ninja -C out/debug 03-mojo-renderer
生成03-mojo-renderer.exe