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
|
# 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/caller 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/+/main/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
|