• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Mojo C++ System API
2This document is a subset of the [Mojo documentation](/mojo/README.md).
3
4[TOC]
5
6## Overview
7The Mojo C++ System API provides a convenient set of helper classes and
8functions for working with Mojo primitives. Unlike the low-level
9[C API](/mojo/public/c/system/README.md) (upon which this is built) this library
10takes advantage of C++ language features and common STL and `//base` types to
11provide a slightly more idiomatic interface to the Mojo system layer, making it
12generally easier to use.
13
14This document provides a brief guide to API usage with example code snippets.
15For a detailed API references please consult the headers in
16[//mojo/public/cpp/system](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/README.md).
17
18Note that all API symbols referenced in this document are implicitly in the
19top-level `mojo` namespace.
20
21## Scoped, Typed Handles
22
23All types of Mojo handles in the C API are simply opaque, integral `MojoHandle`
24values. The C++ API has more strongly typed wrappers defined for different
25handle types: `MessagePipeHandle`, `SharedBufferHandle`,
26`DataPipeConsumerHandle`, `DataPipeProducerHandle`, `TrapHandle`, and
27`InvitationHandle`.
28
29Each of these also has a corresponding, move-only, scoped type for safer usage:
30`ScopedMessagePipeHandle`, `ScopedSharedBufferHandle`, and so on. When a scoped
31handle type is destroyed, its handle is automatically closed via `MojoClose`.
32When working with raw handles you should **always** prefer to use one of the
33scoped types for ownership.
34
35Similar to `std::unique_ptr`, scoped handle types expose a `get()` method to get
36at the underlying unscoped handle type as well as the `->` operator to
37dereference the scoper and make calls directly on the underlying handle type.
38
39## Message Pipes
40
41There are two ways to create a new message pipe using the C++ API. You may
42construct a `MessagePipe` object:
43
44``` cpp
45mojo::MessagePipe pipe;
46
47// NOTE: Because pipes are bi-directional there is no implicit semantic
48// difference between |handle0| or |handle1| here. They're just two ends of a
49// pipe. The choice to treat one as a "client" and one as a "server" is entirely
50// a the API user's decision.
51mojo::ScopedMessagePipeHandle client = std::move(pipe.handle0);
52mojo::ScopedMessagePipeHandle server = std::move(pipe.handle1);
53```
54
55or you may call `CreateMessagePipe`:
56
57``` cpp
58mojo::ScopedMessagePipeHandle client;
59mojo::ScopedMessagePipeHandle server;
60mojo::CreateMessagePipe(nullptr, &client, &server);
61```
62
63There are also some helper functions for constructing message objects and
64reading/writing them on pipes using the library's more strongly-typed C++
65handles:
66
67``` cpp
68mojo::ScopedMessageHandle message;
69mojo::AllocMessage(6, nullptr, 0, MOJO_ALLOC_MESSAGE_FLAG_NONE, &message);
70
71void *buffer;
72mojo::GetMessageBuffer(message.get(), &buffer);
73
74const std::string kMessage = "hello";
75std::copy(kMessage.begin(), kMessage.end(), static_cast<char*>(buffer));
76
77mojo::WriteMessageNew(client.get(), std::move(message),
78                      MOJO_WRITE_MESSAGE_FLAG_NONE);
79
80// Some time later...
81
82mojo::ScopedMessageHandle received_message;
83uint32_t num_bytes;
84mojo::ReadMessageNew(server.get(), &received_message, &num_bytes, nullptr,
85                     nullptr, MOJO_READ_MESSAGE_FLAG_NONE);
86```
87
88See [message_pipe.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/message_pipe.h)
89for detailed C++ message pipe API documentation.
90
91## Data Pipes
92
93Similar to [Message Pipes](#Message-Pipes), the C++ library has some simple
94helpers for more strongly-typed data pipe usage:
95
96``` cpp
97mojo::DataPipe pipe;
98mojo::ScopedDataPipeProducerHandle producer = std::move(pipe.producer);
99mojo::ScopedDataPipeConsumerHandle consumer = std::move(pipe.consumer);
100
101// Or alternatively:
102mojo::ScopedDataPipeProducerHandle producer;
103mojo::ScopedDataPipeConsumerHandle consumer;
104mojo::CreateDataPipe(null, &producer, &consumer);
105```
106
107C++ helpers which correspond directly to the
108[Data Pipe C API](/mojo/public/c/system/README.md#Data-Pipes) for immediate and
109two-phase I/O are provided as well. For example:
110
111``` cpp
112uint32_t num_bytes = 7;
113producer.WriteData("hihihi", &num_bytes, MOJO_WRITE_DATA_FLAG_NONE);
114
115// Some time later...
116
117char buffer[64];
118uint32_t num_bytes = 64;
119consumer.ReadData(buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE);
120```
121
122See [data_pipe.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/data_pipe.h)
123for detailed C++ data pipe API documentation.
124
125## Shared Buffers
126
127A new shared buffers can be allocated like so:
128
129``` cpp
130mojo::ScopedSharedBufferHandle buffer =
131    mojo::SharedBufferHandle::Create(4096);
132```
133
134This new handle can be cloned arbitrarily many times by using the underlying
135handle's `Clone` method:
136
137``` cpp
138mojo::ScopedSharedBufferHandle another_handle = buffer->Clone();
139mojo::ScopedSharedBufferHandle read_only_handle =
140    buffer->Clone(mojo::SharedBufferHandle::AccessMode::READ_ONLY);
141```
142
143And finally the library also provides a scoper for mapping the shared buffer's
144memory:
145
146``` cpp
147mojo::ScopedSharedBufferMapping mapping = buffer->Map(64);
148static_cast<int*>(mapping.get()) = 42;
149
150mojo::ScopedSharedBufferMapping another_mapping = buffer->MapAtOffset(64, 4);
151static_cast<int*>(mapping.get()) = 43;
152```
153
154When `mapping` and `another_mapping` are destroyed, they automatically unmap
155their respective memory regions.
156
157See [buffer.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/buffer.h)
158for detailed C++ shared buffer API documentation.
159
160## Native Platform Handles (File Descriptors, Windows Handles, *etc.*)
161
162The C++ library provides several helpers for wrapping system handle types.
163These are specifically useful when working with a few `//base` types, namely
164`base::PlatformFile`, `base::SharedMemoryHandle` (deprecated), and various
165strongly-typed shared memory region types like
166`base::ReadOnlySharedMemoryRegion`. See
167[platform_handle.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/platform_handle.h)
168for detailed C++ platform handle API documentation.
169
170## Signals & Traps
171
172For an introduction to the concepts of handle signals and traps, check out
173the C API's documentation on
174[Signals & Traps](/mojo/public/c/system/README.md#Signals-Traps).
175
176### Querying Signals
177
178Any C++ handle type's last known signaling state can be queried by calling the
179`QuerySignalsState` method on the handle:
180
181``` cpp
182mojo::MessagePipe message_pipe;
183mojo::DataPipe data_pipe;
184mojo::HandleSignalsState a = message_pipe.handle0->QuerySignalsState();
185mojo::HandleSignalsState b = data_pipe.consumer->QuerySignalsState();
186```
187
188The `HandleSignalsState` is a thin wrapper interface around the C API's
189`MojoHandleSignalsState` structure with convenient accessors for testing
190the signal bitmasks. Whereas when using the C API you might write:
191
192``` c
193struct MojoHandleSignalsState state;
194MojoQueryHandleSignalsState(handle0, &state);
195if (state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE) {
196  // ...
197}
198```
199
200the C++ API equivalent would be:
201
202``` cpp
203if (message_pipe.handle0->QuerySignalsState().readable()) {
204  // ...
205}
206```
207
208### Watching Handles
209
210The [`mojo::SimpleWatcher`](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/simple_watcher.h)
211class serves as a convenient helper for using the
212[low-level traps API](/mojo/public/c/system/README.md#Signals-Traps)
213to watch a handle for signaling state changes. A `SimpleWatcher` is bound to a
214single sequence and always dispatches its notifications on a
215`base::SequencedTaskRunner`.
216
217`SimpleWatcher` has two possible modes of operation, selected at construction
218time by the `mojo::SimpleWatcher::ArmingPolicy` enum:
219
220* `MANUAL` mode requires the user to manually call `Arm` and/or `ArmOrNotify`
221  before any notifications will fire regarding the state of the watched handle.
222  Every time the notification callback is run, the `SimpleWatcher` must be
223  rearmed again before the next one can fire. See
224  [Arming a Trap](/mojo/public/c/system/README.md#Arming-a-Trap) and the
225  documentation in `SimpleWatcher`'s header.
226
227* `AUTOMATIC` mode ensures that the `SimpleWatcher` always either is armed or
228  has a pending notification task queued for execution.
229
230`AUTOMATIC` mode is more convenient but can result in redundant notification
231tasks, especially if the provided callback does not make a strong effort to
232return the watched handle to an uninteresting signaling state (by *e.g.*,
233reading all its available messages when notified of readability.)
234
235Example usage:
236
237``` cpp
238class PipeReader {
239 public:
240  PipeReader(mojo::ScopedMessagePipeHandle pipe)
241      : pipe_(std::move(pipe)),
242        watcher_(mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC) {
243    // NOTE: base::Unretained is safe because the callback can never be run
244    // after SimpleWatcher destruction.
245    watcher_.Watch(pipe_.get(), MOJO_HANDLE_SIGNAL_READABLE,
246                   base::Bind(&PipeReader::OnReadable, base::Unretained(this)));
247  }
248
249  ~PipeReader() {}
250
251 private:
252  void OnReadable(MojoResult result) {
253    while (result == MOJO_RESULT_OK) {
254      mojo::ScopedMessageHandle message;
255      uint32_t num_bytes;
256      result = mojo::ReadMessageNew(pipe_.get(), &message, &num_bytes, nullptr,
257                                    nullptr, MOJO_READ_MESSAGE_FLAG_NONE);
258      DCHECK_EQ(result, MOJO_RESULT_OK);
259      messages_.emplace_back(std::move(message));
260    }
261  }
262
263  mojo::ScopedMessagePipeHandle pipe_;
264  mojo::SimpleWatcher watcher_;
265  std::vector<mojo::ScopedMessageHandle> messages_;
266};
267
268mojo::MessagePipe pipe;
269PipeReader reader(std::move(pipe.handle0));
270
271// Written messages will asynchronously end up in |reader.messages_|.
272WriteABunchOfStuff(pipe.handle1.get());
273```
274
275## Synchronous Waiting
276
277The C++ System API defines some utilities to block a calling sequence while
278waiting for one or more handles to change signaling state in an interesting way.
279These threads combine usage of the
280[low-level traps API](/mojo/public/c/system/README.md#Signals-Traps)
281with common synchronization primitives (namely `base::WaitableEvent`.)
282
283While these API features should be used sparingly, they are sometimes necessary.
284
285See the documentation in
286[wait.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/wait.h)
287and [wait_set.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/wait_set.h)
288for a more detailed API reference.
289
290### Waiting On a Single Handle
291
292The `mojo::Wait` function simply blocks the calling sequence until a given
293signal mask is either partially satisfied or fully unsatisfiable on a given
294handle.
295
296``` cpp
297mojo::MessagePipe pipe;
298mojo::WriteMessageRaw(pipe.handle0.get(), "hey", 3, nullptr, nullptr,
299                      MOJO_WRITE_MESSAGE_FLAG_NONE);
300MojoResult result = mojo::Wait(pipe.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE);
301DCHECK_EQ(result, MOJO_RESULT_OK);
302
303// Guaranteed to succeed because we know |handle1| is readable now.
304mojo::ScopedMessageHandle message;
305uint32_t num_bytes;
306mojo::ReadMessageNew(pipe.handle1.get(), &num_bytes, nullptr, nullptr,
307                     MOJO_READ_MESSAGE_FLAG_NONE);
308```
309
310`mojo::Wait` is most typically useful in limited testing scenarios.
311
312### Waiting On Multiple Handles
313
314`mojo::WaitMany` provides a simple API to wait on multiple handles
315simultaneously, returning when any handle's given signal mask is either
316partially satisfied or fully unsatisfiable.
317
318``` cpp
319mojo::MessagePipe a, b;
320GoDoSomethingWithPipes(std:move(a.handle1), std::move(b.handle1));
321
322mojo::MessagePipeHandle handles[2] = {a.handle0.get(), b.handle0.get()};
323MojoHandleSignals signals[2] = {MOJO_HANDLE_SIGNAL_READABLE,
324                                MOJO_HANDLE_SIGNAL_READABLE};
325size_t ready_index;
326MojoResult result = mojo::WaitMany(handles, signals, 2, &ready_index);
327if (ready_index == 0) {
328  // a.handle0 was ready.
329} else {
330  // b.handle0 was ready.
331}
332```
333
334Similar to `mojo::Wait`, `mojo::WaitMany` is primarily useful in testing. When
335waiting on multiple handles in production code, you should almost always instead
336use a more efficient and more flexible `mojo::WaitSet` as described in the next
337section.
338
339### Waiting On Handles and Events Simultaneously
340
341Typically when waiting on one or more handles to signal, the set of handles and
342conditions being waited upon do not change much between consecutive blocking
343waits. It's also often useful to be able to interrupt the blocking operation
344as efficiently as possible.
345
346[`mojo::WaitSet`](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/wait_set.h)
347is designed with these conditions in mind. A `WaitSet` maintains a persistent
348set of (not-owned) Mojo handles and `base::WaitableEvent`s, which may be
349explicitly added to or removed from the set at any time.
350
351The `WaitSet` may be waited upon repeatedly, each time blocking the calling
352sequence until either one of the handles attains an interesting signaling state
353or one of the events is signaled. For example let's suppose we want to wait up
354to 5 seconds for either one of two handles to become readable:
355
356``` cpp
357base::WaitableEvent timeout_event(
358    base::WaitableEvent::ResetPolicy::MANUAL,
359    base::WaitableEvent::InitialState::NOT_SIGNALED);
360mojo::MessagePipe a, b;
361
362GoDoStuffWithPipes(std::move(a.handle1), std::move(b.handle1));
363
364mojo::WaitSet wait_set;
365wait_set.AddHandle(a.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
366wait_set.AddHandle(b.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
367wait_set.AddEvent(&timeout_event);
368
369// Ensure the Wait() lasts no more than 5 seconds.
370bg_thread->task_runner()->PostDelayedTask(
371    FROM_HERE,
372    base::Bind([](base::WaitableEvent* e) { e->Signal(); }, &timeout_event);
373    base::TimeDelta::FromSeconds(5));
374
375base::WaitableEvent* ready_event = nullptr;
376size_t num_ready_handles = 1;
377mojo::Handle ready_handle;
378MojoResult ready_result;
379wait_set.Wait(&ready_event, &num_ready_handles, &ready_handle, &ready_result);
380
381// The apex of thread-safety.
382bg_thread->Stop();
383
384if (ready_event) {
385  // The event signaled...
386}
387
388if (num_ready_handles > 0) {
389  // At least one of the handles signaled...
390  // NOTE: This and the above condition are not mutually exclusive. If handle
391  // signaling races with timeout, both things might be true.
392}
393```
394
395## Invitations
396Invitations are the means by which two processes can have Mojo IPC bootstrapped
397between them. An invitation must be transmitted over some platform-specific IPC
398primitive (*e.g.* a Windows named pipe or UNIX domain socket), and the public
399[platform support library](/mojo/public/cpp/platform/README.md) provides some
400lightweight, cross-platform abstractions for those primitives.
401
402For any two processes looking to be connected, one must send an
403`OutgoingInvitation` and the other must accept an `IncomingInvitation`. The
404sender can attach named message pipe handles to the `OutgoingInvitation`, and
405the receiver can extract them from its `IncomingInvitation`.
406
407Basic usage might look something like this in the case where one process is
408responsible for launching the other.
409
410``` cpp
411#include "base/command_line.h"
412#include "base/process/launch.h"
413#include "mojo/public/cpp/platform/platform_channel.h"
414#include "mojo/public/cpp/system/invitation.h"
415#include "mojo/public/cpp/system/message_pipe.h"
416
417mojo::ScopedMessagePipeHandle LaunchAndConnectSomething() {
418  // Under the hood, this is essentially always an OS pipe (domain socket pair,
419  // Windows named pipe, Fuchsia channel, etc).
420  mojo::PlatformChannel channel;
421
422  mojo::OutgoingInvitation invitation;
423
424  // Attach a message pipe to be extracted by the receiver. The other end of the
425  // pipe is returned for us to use locally.
426  mojo::ScopedMessagePipeHandle pipe =
427      invitation->AttachMessagePipe("arbitrary pipe name");
428
429  base::LaunchOptions options;
430  base::CommandLine command_line("some_executable")
431  channel.PrepareToPassRemoteEndpoint(&options, &command_line);
432  base::Process child_process = base::LaunchProcess(command_line, options);
433  channel.RemoteProcessLaunchAttempted();
434
435  OutgoingInvitation::Send(std::move(invitation), child_process.Handle(),
436                           channel.TakeLocalEndpoint());
437  return pipe;
438}
439```
440
441The launched process can in turn accept an `IncomingInvitation`:
442
443``` cpp
444#include "base/command_line.h"
445#include "base/threading/thread.h"
446#include "mojo/core/embedder/embedder.h"
447#include "mojo/core/embedder/scoped_ipc_support.h"
448#include "mojo/public/cpp/platform/platform_channel.h"
449#include "mojo/public/cpp/system/invitation.h"
450#include "mojo/public/cpp/system/message_pipe.h"
451
452int main(int argc, char** argv) {
453  // Basic Mojo initialization for a new process.
454  mojo::core::Init();
455  base::Thread ipc_thread("ipc!");
456  ipc_thread.StartWithOptions(
457      base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
458  mojo::core::ScopedIPCSupport ipc_support(
459      ipc_thread.task_runner(),
460      mojo::core::ScopedIPCSupport::ShutdownPolicy::CLEAN);
461
462  // Accept an invitation.
463  mojo::IncomingInvitation invitation = mojo::IncomingInvitation::Accept(
464      mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine(
465          *base::CommandLine::ForCurrentProcess()));
466  mojo::ScopedMessagePipeHandle pipe =
467      invitation->ExtractMessagePipe("arbitrary pipe name");
468
469  // etc...
470  return GoListenForMessagesAndRunForever(std::move(pipe));
471}
472```
473
474Now we have IPC initialized between the two processes.
475
476Also keep in mind that bindings interfaces are just message pipes with some
477semantic and syntactic sugar wrapping them, so you can use these primordial
478message pipe handles as mojom interfaces. For example:
479
480``` cpp
481// Process A
482mojo::OutgoingInvitation invitation;
483auto pipe = invitation->AttachMessagePipe("x");
484mojo::Binding<foo::mojom::Bar> binding(
485    &bar_impl,
486    foo::mojom::BarRequest(std::move(pipe)));
487
488// Process B
489auto invitation = mojo::IncomingInvitation::Accept(...);
490auto pipe = invitation->ExtractMessagePipe("x");
491foo::mojom::BarPtr bar(foo::mojom::BarPtrInfo(std::move(pipe), 0));
492
493// Will asynchronously invoke bar_impl.DoSomething() in process A.
494bar->DoSomething();
495```
496
497And just to be sure, the usage here could be reversed: the invitation sender
498could just as well treat its pipe endpoint as a `BarPtr` while the receiver
499treats theirs as a `BarRequest` to be bound.
500
501### Process Networks
502Accepting an invitation admits the accepting process into the sender's connected
503network of processes. Once this is done, it's possible for the newly admitted
504process to establish communication with any other process in that network via
505normal message pipe passing.
506
507This does not mean that the invited process can proactively locate and connect
508to other processes without assistance; rather it means that Mojo handles created
509by the process can safely be transferred to any other process in the network
510over established message pipes, and similarly that Mojo handles created by any
511other process in the network can be safely passed to the newly admitted process.
512
513### Invitation Restrictions
514A process may only belong to a single network at a time.
515
516Additionally, once a process has joined a network, it cannot join another for
517the remainder of its lifetime even if it has lost the connection to its original
518network. This restriction will soon be lifted, but for now developers must be
519mindful of it when authoring any long-running daemon process that will accept an
520incoming invitation.
521
522### Isolated Invitations
523It is possible to have two independent networks of Mojo-connected processes; for
524example, a long-running system daemon which uses Mojo to talk to child processes
525of its own, as well as the Chrome browser process running with no common
526ancestor, talking to its own child processes.
527
528In this scenario it may be desirable to have a process in one network talk to a
529process in the other network. Normal invitations cannot be used here since both
530processes already belong to a network. In this case, an **isolated** invitation
531can be used. These work just like regular invitations, except the sender must
532call `OutgoingInvitation::SendIsolated` and the receiver must call
533`IncomingInvitation::AcceptIsolated`.
534
535Once a connection is established via isolated invitation, Mojo IPC can be used
536normally, with the exception that transitive process connections are not
537supported; that is, if process A sends a message pipe handle to process B via
538an isolated connection, process B cannot reliably send that pipe handle onward
539to another process in its own network. Isolated invitations therefore may only
540be used to facilitate direct 1:1 communication between two processes.
541