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