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 / docs / basics.md [blame]
# 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