| # Mojo Basics |
| |
| This document aims to provide a brief overview of the different concepts in Mojo |
| and how they work together. For more details about more complex and/or |
| Chrome-specific Mojo use cases, please consult [Intro to Mojo & Services](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/mojo_and_services.md). |
| |
| [TOC] |
| |
| ## Interfaces |
| |
| Mojo provides a [C++-like interface definition language][mojo-idl] for defining |
| interfaces for making interprocess calls (IPCs): |
| |
| ``` |
| module math.mojom; |
| |
| interface Math { |
| // Adds two int32s and returns the result as an int64 (to avoid |
| // overflow issues). |
| Add(int32 x, int32 y) => (int64 sum); |
| }; |
| ``` |
| |
| Interfaces are built using the `mojom` (or `mojom_component`) [GN |
| template][gn-template]: |
| ``` |
| mojom("mojom") { |
| sources = ["math.mojom"] |
| } |
| ``` |
| |
| This will generate C++ (and optionally, Java and JavaScript) interfaces. Writing |
| code to handle IPCs is a simple matter of implementing the generated interface: |
| |
| ```c++ |
| class MathImpl : public math::mojom::Math { |
| public: |
| explicit MathImpl(mojo::PendingReceiver<math::mojom::Math> receiver) |
| : receiver_(this, std::move(receiver)) {} |
| |
| // math::mojom::Math overrides: |
| // Note: AddCallback is a type alias for base::OnceCallback<void(int64_t)>. |
| // The parameters to the callback are the reply parameters specified in the |
| // Mojo IDL method definition. This is part of the boilerplate generated by |
| // Mojo: invoking |reply| will send a reply to the caller. |
| void Add(int32_t x, int32_t y, AddCallback reply) override { |
| // Note: Mojo always returns results via callback. While it is possible to |
| // make a sync IPC which blocks on the reply, the handler will always return |
| // the result via callback. |
| std::move(reply).Run(static_cast<int64_t>(x) + y); |
| } |
| |
| private: |
| // Wraps a message pipe endpoint that receives incoming messages. See the |
| // message pipes section below for more information. |
| mojo::Receiver<math::mojom::Math> receiver_; |
| }; |
| ``` |
| |
| Note: the build process also generates proxy classes (e.g. `MathProxy`) which |
| encapsulate the details of making the actual cross-process call. These are |
| used internally and are an implementation detail that can typically be ignored. |
| |
| ## Message Pipes |
| |
| Interfaces are layered on top of low-level [message pipes][message-pipe]. Each |
| message pipe has two bidirectional endpoints. The Mojo bindings enforce |
| additional conventions on top of message pipes, where one endpoint is the |
| sender/caller, represented as: |
| |
| ```c++ |
| // Wraps a message pipe endpoint for making remote calls. May only be used on |
| // the sequence where the mojo::Remote was bound. |
| mojo::Remote<math::mojom::Math> remote_math = ...; |
| ``` |
| |
| And the other endpoint is the receiving/callee, represented as: |
| |
| ```c++ |
| // Usually a class member. Wraps a message pipe endpoint that receives incoming |
| // messages. Routes and dispatches IPCs to the handler—typically |this|—on the |
| // sequence where the mojo::Receiver was bound. |
| mojo::Receiver<math::mojom::Math> receiver_; |
| ``` |
| |
| This allows limited bidirectional communication. For one interface, the sender |
| (A) may make any number of calls to the receiver (B). (B) may send a single |
| reply for each call from (A). More expressive APIs are often implemented as a |
| pair of interfaces (with two underlying message pipes), allowing calls to be |
| made in either direction between (A) and (B). |
| |
| Message pipe endpoints are typically created using one of: |
| |
| ### mojo::Remote<T>::BindNewPipeAndPassReceiver |
| |
| Used when the sender/caller creates the endpoints. One endpoint is retained for |
| itself to send IPCs, and the other endpoint is returned as an unbound |
| `mojo::PendingReceiver<T>` for the receiver/callee to bind to a |
| `mojo::Receiver<T>`. |
| |
| ```c++ |
| mojo::Remote<math::mojom::Math> remote_math; |
| |
| // BindNewPipeAndPassReceiver() returns a |
| // mojo::PendingReceiver<math::mojom::Math>. This may be bound to a |
| // mojo::Receiver<math::mojom::Math> to handle calls received from |
| // |remote_math|. |
| LaunchAndBindRemoteMath(remote_math.BindNewPipeAndPassReceiver()); |
| |
| // |remote_math| may be immediately used. The Add() call will be buffered by the |
| // receiving end and dispatched when mojo::PendingReceiver<math::mojom::Math> is |
| // bound to a mojo::Receiver<math::mojom::Math>. |
| remote_math->Add(2, 2, base::BindOnce(...)); |
| ``` |
| |
| ### mojo::Receiver<T>::BindNewPipeAndPassRemote |
| |
| Used when the receiver/callee creates the endpoints. One endpoint is retained |
| for itself to receive IPCs, and the other endpoint is returned as an unbound |
| `mojo::PendingRemote<T>` for the sender/callee to bind to a `mojo::Remote<T>`. |
| |
| ```c++ |
| class MathImpl : public math::mojom::MathImpl { |
| // ...addition to the previous MathImpl definition... |
| |
| mojo::PendingRemote<math::mojom::Math> GetRemoteMath() { |
| // BindNewPipeAndPassRemote() returns a |
| // `mojo::PendingRemote<math::mojom::Math>`. This may be bound to a |
| // `mojo::Remote<math::mojom::Math> which can be used to send IPCs that will |
| // be handled by |this|. |
| return receiver_.BindNewPipeAndPassRemote(); |
| } |
| }; |
| ``` |
| |
| ### mojo::PendingRemote<T>::InitWithNewPipeAndPassReceiver |
| |
| Less common, but similar to `mojo::Remote<T>::BindNewPipeAndPassReceiver()`. |
| Typically used by broker code that needs to hand off a `mojo::PendingRemote<T>` |
| to the sender/caller side and hand off a `mojo::PendingReceiver<T>` to the |
| receiver/callee side. |
| |
| ### mojo::Remote<T>/mojo::Receiver<T> and mojo::PendingRemote<T>/mojo::PendingReceiver<T> |
| |
| Both `mojo::Remote<T>` and `mojo::Receiver<T>` have a corresponding unbound |
| version: this allows either endpoint to be passed between sequences in the same |
| process or even between processes over IPC. |
| |
| ```c++ |
| mojo::Remote<math::mojom::MathImpl> remote = ...; |
| // |pending_remote| is movable and may be passed around. While unbound, the |
| // endpoint cannot be used to send IPCs. The pending remote may be passed to |
| // the mojo::Remote<T> constructor or mojo::Remote<T>::Bind() to rebind the |
| // endpoint. |
| mojo::PendingRemote<math::mojom::MathImpl> pending_remote = remote.Unbind(); |
| ``` |
| |
| ```c++ |
| mojo::Receiver<math::mojom::MathImpl> receiver = ...; |
| // |pending_receiver| is movable and may be passed around. While unbound, |
| // received IPCs are buffered and not processed. The pending receiver may be |
| // passed to the mojo::Receiver<T> constructor or mojo::Receiver<T>::Bind() to |
| // rebind the endpoint. |
| mojo::PendingReceiver<math::mojom::MathImpl> pending_receiver = receiver.Unbind(); |
| ``` |
| |
| [mojo-idl]: https://chromium.googlesource.com/chromium/src/+/master/mojo/public/tools/bindings/README.md |
| [gn-template]: https://cs.chromium.org/chromium/src/mojo/public/tools/bindings/mojom.gni |
| [message-pipe]: https://cs.chromium.org/chromium/src/mojo/public/cpp/system/message_pipe.h |