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