• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..16-Feb-2021-

tests/H16-Feb-2021-2,6502,048

BUILD.gnH A D16-Feb-20211.7 KiB7165

README.mdH A D16-Feb-202120.1 KiB541422

buffer.ccH A D16-Feb-20211.7 KiB5541

buffer.hH A D16-Feb-20213 KiB9241

core.hH A D16-Feb-2021515 158

data_pipe.ccH A D16-Feb-20211.5 KiB5738

data_pipe.hH A D16-Feb-20215.6 KiB15494

data_pipe_drainer.ccH A D16-Feb-20211.5 KiB5036

data_pipe_drainer.hH A D16-Feb-20211.2 KiB4831

data_pipe_producer.ccH A D16-Feb-20217.2 KiB215159

data_pipe_producer.hH A D16-Feb-20214.1 KiB10741

data_pipe_utils.ccH A D16-Feb-20213.1 KiB9676

data_pipe_utils.hH A D16-Feb-2021991 3115

dynamic_library_support.ccH A D16-Feb-20212 KiB6544

dynamic_library_support.hH A D16-Feb-20211.9 KiB4616

file_data_source.ccH A D16-Feb-20213 KiB10784

file_data_source.hH A D16-Feb-20211.4 KiB4727

filtered_data_source.ccH A D16-Feb-20211 KiB4331

filtered_data_source.hH A D16-Feb-20212.3 KiB6431

functions.ccH A D16-Feb-20211.1 KiB3723

functions.hH A D16-Feb-20211.4 KiB4216

handle.hH A D16-Feb-20217.4 KiB229121

handle_signal_tracker.ccH A D16-Feb-20212.6 KiB7653

handle_signal_tracker.hH A D16-Feb-20212.5 KiB7739

handle_signals_state.hH A D16-Feb-20213.5 KiB10761

invitation.ccH A D16-Feb-202112.5 KiB327260

invitation.hH A D16-Feb-20218.5 KiB20581

isolated_connection.ccH A D16-Feb-20211.6 KiB4222

isolated_connection.hH A D16-Feb-20212.5 KiB6121

message.hH A D16-Feb-20213.3 KiB10681

message_pipe.ccH A D16-Feb-20213.1 KiB9171

message_pipe.hH A D16-Feb-20215.6 KiB15897

platform_handle.ccH A D16-Feb-202110.6 KiB276240

platform_handle.hH A D16-Feb-20213.5 KiB9048

scope_to_message_pipe.ccH A D16-Feb-20211.5 KiB4726

scope_to_message_pipe.hH A D16-Feb-20212 KiB6641

simple_watcher.ccH A D16-Feb-202110.4 KiB292208

simple_watcher.hH A D16-Feb-20219.8 KiB23976

string_data_source.ccH A D16-Feb-20211.5 KiB5138

string_data_source.hH A D16-Feb-20211.6 KiB4927

system_export.hH A D16-Feb-2021845 3520

trap.ccH A D16-Feb-2021569 2112

trap.hH A D16-Feb-20211.1 KiB3721

wait.ccH A D16-Feb-20216.2 KiB200140

wait.hH A D16-Feb-20214 KiB9027

wait_set.ccH A D16-Feb-202112.3 KiB366239

wait_set.hH A D16-Feb-20215.1 KiB12634

README.md

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// 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_handle);
99mojo::ScopedDataPipeConsumerHandle consumer = std::move(pipe.consumer_handle);
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 buffer 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())[0] = 42;
149
150mojo::ScopedSharedBufferMapping another_mapping = buffer->MapAtOffset(64, 4);
151static_cast<int*>(mapping.get())[0] = 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::BindRepeating(&PipeReader::OnReadable,
247                                       base::Unretained(this)));
248  }
249
250  ~PipeReader() {}
251
252 private:
253  void OnReadable(MojoResult result) {
254    while (result == MOJO_RESULT_OK) {
255      mojo::ScopedMessageHandle message;
256      uint32_t num_bytes;
257      result = mojo::ReadMessageNew(pipe_.get(), &message, &num_bytes, nullptr,
258                                    nullptr, MOJO_READ_MESSAGE_FLAG_NONE);
259      DCHECK_EQ(result, MOJO_RESULT_OK);
260      messages_.emplace_back(std::move(message));
261    }
262  }
263
264  mojo::ScopedMessagePipeHandle pipe_;
265  mojo::SimpleWatcher watcher_;
266  std::vector<mojo::ScopedMessageHandle> messages_;
267};
268
269mojo::MessagePipe pipe;
270PipeReader reader(std::move(pipe.handle0));
271
272// Written messages will asynchronously end up in |reader.messages_|.
273WriteABunchOfStuff(pipe.handle1.get());
274```
275
276## Synchronous Waiting
277
278The C++ System API defines some utilities to block a calling sequence while
279waiting for one or more handles to change signaling state in an interesting way.
280These threads combine usage of the
281[low-level traps API](/mojo/public/c/system/README.md#Signals-Traps)
282with common synchronization primitives (namely `base::WaitableEvent`.)
283
284While these API features should be used sparingly, they are sometimes necessary.
285
286See the documentation in
287[wait.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/wait.h)
288and [wait_set.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/wait_set.h)
289for a more detailed API reference.
290
291### Waiting On a Single Handle
292
293The `mojo::Wait` function simply blocks the calling sequence until a given
294signal mask is either partially satisfied or fully unsatisfiable on a given
295handle.
296
297``` cpp
298mojo::MessagePipe pipe;
299mojo::WriteMessageRaw(pipe.handle0.get(), "hey", 3, nullptr, nullptr,
300                      MOJO_WRITE_MESSAGE_FLAG_NONE);
301MojoResult result = mojo::Wait(pipe.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE);
302DCHECK_EQ(result, MOJO_RESULT_OK);
303
304// Guaranteed to succeed because we know |handle1| is readable now.
305mojo::ScopedMessageHandle message;
306uint32_t num_bytes;
307mojo::ReadMessageNew(pipe.handle1.get(), &num_bytes, nullptr, nullptr,
308                     MOJO_READ_MESSAGE_FLAG_NONE);
309```
310
311`mojo::Wait` is most typically useful in limited testing scenarios.
312
313### Waiting On Multiple Handles
314
315`mojo::WaitMany` provides a simple API to wait on multiple handles
316simultaneously, returning when any handle's given signal mask is either
317partially satisfied or fully unsatisfiable.
318
319``` cpp
320mojo::MessagePipe a, b;
321GoDoSomethingWithPipes(std:move(a.handle1), std::move(b.handle1));
322
323mojo::MessagePipeHandle handles[2] = {a.handle0.get(), b.handle0.get()};
324MojoHandleSignals signals[2] = {MOJO_HANDLE_SIGNAL_READABLE,
325                                MOJO_HANDLE_SIGNAL_READABLE};
326size_t ready_index;
327MojoResult result = mojo::WaitMany(handles, signals, 2, &ready_index);
328if (ready_index == 0) {
329  // a.handle0 was ready.
330} else {
331  // b.handle0 was ready.
332}
333```
334
335Similar to `mojo::Wait`, `mojo::WaitMany` is primarily useful in testing. When
336waiting on multiple handles in production code, you should almost always instead
337use a more efficient and more flexible `mojo::WaitSet` as described in the next
338section.
339
340### Waiting On Handles and Events Simultaneously
341
342Typically when waiting on one or more handles to signal, the set of handles and
343conditions being waited upon do not change much between consecutive blocking
344waits. It's also often useful to be able to interrupt the blocking operation
345as efficiently as possible.
346
347[`mojo::WaitSet`](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/wait_set.h)
348is designed with these conditions in mind. A `WaitSet` maintains a persistent
349set of (not-owned) Mojo handles and `base::WaitableEvent`s, which may be
350explicitly added to or removed from the set at any time.
351
352The `WaitSet` may be waited upon repeatedly, each time blocking the calling
353sequence until either one of the handles attains an interesting signaling state
354or one of the events is signaled. For example let's suppose we want to wait up
355to 5 seconds for either one of two handles to become readable:
356
357``` cpp
358base::WaitableEvent timeout_event(
359    base::WaitableEvent::ResetPolicy::MANUAL,
360    base::WaitableEvent::InitialState::NOT_SIGNALED);
361mojo::MessagePipe a, b;
362
363GoDoStuffWithPipes(std::move(a.handle1), std::move(b.handle1));
364
365mojo::WaitSet wait_set;
366wait_set.AddHandle(a.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
367wait_set.AddHandle(b.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
368wait_set.AddEvent(&timeout_event);
369
370// Ensure the Wait() lasts no more than 5 seconds.
371bg_thread->task_runner()->PostDelayedTask(FROM_HERE, base::BindOnce([](base::WaitableEvent* e) { e->Signal(); }, &timeout_event);
372    base::TimeDelta::FromSeconds(5));
373
374base::WaitableEvent* ready_event = nullptr;
375size_t num_ready_handles = 1;
376mojo::Handle ready_handle;
377MojoResult ready_result;
378wait_set.Wait(&ready_event, &num_ready_handles, &ready_handle, &ready_result);
379
380// The apex of thread-safety.
381bg_thread->Stop();
382
383if (ready_event) {
384  // The event signaled...
385}
386
387if (num_ready_handles > 0) {
388  // At least one of the handles signaled...
389  // NOTE: This and the above condition are not mutually exclusive. If handle
390  // signaling races with timeout, both things might be true.
391}
392```
393
394## Invitations
395Invitations are the means by which two processes can have Mojo IPC bootstrapped
396between them. An invitation must be transmitted over some platform-specific IPC
397primitive (*e.g.* a Windows named pipe or UNIX domain socket), and the public
398[platform support library](/mojo/public/cpp/platform/README.md) provides some
399lightweight, cross-platform abstractions for those primitives.
400
401For any two processes looking to be connected, one must send an
402`OutgoingInvitation` and the other must accept an `IncomingInvitation`. The
403sender can attach named message pipe handles to the `OutgoingInvitation`, and
404the receiver can extract them from its `IncomingInvitation`.
405
406Basic usage might look something like this in the case where one process is
407responsible for launching the other.
408
409``` cpp
410#include "base/command_line.h"
411#include "base/process/launch.h"
412#include "mojo/public/cpp/platform/platform_channel.h"
413#include "mojo/public/cpp/system/invitation.h"
414#include "mojo/public/cpp/system/message_pipe.h"
415
416mojo::ScopedMessagePipeHandle LaunchAndConnectSomething() {
417  // Under the hood, this is essentially always an OS pipe (domain socket pair,
418  // Windows named pipe, Fuchsia channel, etc).
419  mojo::PlatformChannel channel;
420
421  mojo::OutgoingInvitation invitation;
422
423  // Attach a message pipe to be extracted by the receiver. The other end of the
424  // pipe is returned for us to use locally.
425  mojo::ScopedMessagePipeHandle pipe =
426      invitation->AttachMessagePipe("arbitrary pipe name");
427
428  base::LaunchOptions options;
429  base::CommandLine command_line("some_executable")
430  channel.PrepareToPassRemoteEndpoint(&options, &command_line);
431  base::Process child_process = base::LaunchProcess(command_line, options);
432  channel.RemoteProcessLaunchAttempted();
433
434  OutgoingInvitation::Send(std::move(invitation), child_process.Handle(),
435                           channel.TakeLocalEndpoint());
436  return pipe;
437}
438```
439
440The launched process can in turn accept an `IncomingInvitation`:
441
442``` cpp
443#include "base/command_line.h"
444#include "base/threading/thread.h"
445#include "mojo/core/embedder/embedder.h"
446#include "mojo/core/embedder/scoped_ipc_support.h"
447#include "mojo/public/cpp/platform/platform_channel.h"
448#include "mojo/public/cpp/system/invitation.h"
449#include "mojo/public/cpp/system/message_pipe.h"
450
451int main(int argc, char** argv) {
452  // Basic Mojo initialization for a new process.
453  mojo::core::Init();
454  base::Thread ipc_thread("ipc!");
455  ipc_thread.StartWithOptions(
456      base::Thread::Options(base::MessagePumpType::IO, 0));
457  mojo::core::ScopedIPCSupport ipc_support(
458      ipc_thread.task_runner(),
459      mojo::core::ScopedIPCSupport::ShutdownPolicy::CLEAN);
460
461  // Accept an invitation.
462  mojo::IncomingInvitation invitation = mojo::IncomingInvitation::Accept(
463      mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine(
464          *base::CommandLine::ForCurrentProcess()));
465  mojo::ScopedMessagePipeHandle pipe =
466      invitation->ExtractMessagePipe("arbitrary pipe name");
467
468  // etc...
469  return GoListenForMessagesAndRunForever(std::move(pipe));
470}
471```
472
473Now we have IPC initialized between the two processes.
474
475Also keep in mind that bindings interfaces are just message pipes with some
476semantic and syntactic sugar wrapping them, so you can use these primordial
477message pipe handles as mojom interfaces. For example:
478
479``` cpp
480// Process A
481mojo::OutgoingInvitation invitation;
482auto pipe = invitation->AttachMessagePipe("x");
483mojo::Receiver<foo::mojom::Bar> receiver(
484    &bar_impl,
485    mojo::PendingReceiver<foo::mojom::Bar>(std::move(pipe)));
486
487// Process B
488auto invitation = mojo::IncomingInvitation::Accept(...);
489auto pipe = invitation->ExtractMessagePipe("x");
490mojo::Remote<foo::mojom::Bar> bar(
491    mojo::PendingRemote<foo::mojom::Bar>(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 `Remote<Bar>` while the receiver
499treats theirs as a `PendingReceiver<Bar>` 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