Commit 7c05e3de authored by Ken Rockot's avatar Ken Rockot Committed by Commit Bot

Refresh Mojo documentation

Updates some stale system API references, adds new stuff for invitations
and platform APIs, and deletes a bunch of EDK crap.

Also adds a new convenience API to PlatformChannel and uses it in a few
places, because it makes example code in the documentation less ugly. :)

Bug: 844763
Change-Id: I91fbff53659dc87fc6fd33a2e5a774c419ba87a7
Reviewed-on: https://chromium-review.googlesource.com/1110954
Commit-Queue: Ken Rockot <rockot@chromium.org>
Reviewed-by: default avatarJay Civelli <jcivelli@chromium.org>
Cr-Commit-Position: refs/heads/master@{#570310}
parent 11240791
...@@ -31,30 +31,46 @@ follows: ...@@ -31,30 +31,46 @@ follows:
![Mojo Library Layering: EDK on bottom, different language bindings on top, public system support APIs in the middle](https://docs.google.com/drawings/d/1RwhzKblXUZw-zhy_KDVobAYprYSqxZzopXTUsbwzDPw/pub?w=570&h=324) ![Mojo Library Layering: EDK on bottom, different language bindings on top, public system support APIs in the middle](https://docs.google.com/drawings/d/1RwhzKblXUZw-zhy_KDVobAYprYSqxZzopXTUsbwzDPw/pub?w=570&h=324)
## Embedder Development Kit (EDK) ## Embedder Development Kit (EDK)
Every process to be interconnected via Mojo IPC is called a **Mojo embedder** Most processes to be interconnected via Mojo IPC are called **Mojo embedders**
and needs to embed the and need to statically link against the
[**Embedder Development Kit (EDK)**](/mojo/edk/embedder/README.md) library. The [**Embedder Development Kit (EDK)**](/mojo/edk/embedder/README.md) library. The
EDK exposes the means for an embedder to physically connect one process to another EDK exposes a few very basic APIs to initialize the Mojo system and setup basic
using any supported native IPC primitive (*e.g.,* a UNIX domain socket or IPC support.
Windows named pipe) on the host platform.
Details regarding where and how an application process actually embeds and Details regarding where and how an application process actually embeds and
configures the EDK are generaly hidden from the rest of the application code, configures the EDK are generaly hidden from the rest of the application code,
and applications instead use the public System and Bindings APIs to get things and applications instead use the public System, Platform, and Bindings APIs to
done within processes that embed Mojo. get things done.
## Mojo Core Library
On some platforms, it is also possible for applications to rely on a
dynamically-linked `mojo_core` library in lieu of statically linking the EDK.
This requires that the corresponding library (*e.g.*, `libmojo_core.so`) be
present in the working directory or that the `MOJO_CORE_LIBRARY_PATH`
environment variable point to the absolute path of such a library.
When relying on `mojo_core`, an application may simply link against its own copy
of Mojo's static public libraries, and call `MojoInitialize()` to enable general
Mojo usage and IPC support. This is done *in exclusion* to direct EDK library
initialization described in the previous section.
## C System API ## C System API
Once the EDK is initialized within a process, the public Once Mojo is initialized within a process, the public
[**C System API**](/mojo/public/c/system/README.md) is usable on any thread for [**C System API**](/mojo/public/c/system/README.md) is usable on any thread for
the remainder of the process's lifetime. This is a lightweight API with a the remainder of the process's lifetime. This is a lightweight API with a
relatively small (and eventually stable) ABI. Typically this API is not used relatively small, stable, forward-compatible ABI. Typically this API is not used
directly, but it is the foundation upon which all remaining upper layers are directly, but it is the foundation upon which all higher-level public library
built. It exposes the fundamental capabilities to create and interact with APIs are built. It exposes the fundamental capabilities to create and interact
various types of Mojo handles including **message pipes**, **data pipes**, and with various types of Mojo handles including **message pipes**, **data pipes**,
**shared buffers**. and **shared buffers**, as well as APIs to help bootstrap Mojo IPC between two
processes.
## Platform Support API
Mojo provides a small collection of abstractions around platform-specific IPC
primitives to facilitate bootstrapping Mojo IPC between two processes. See the
[Platform API](/mojo/public/cpp/platform/README.md) documentation for details.
## High-Level System APIs ## High-Level System APIs
There is a relatively small, higher-level system API for each supported There is a relatively small, higher-level system API for each supported
language, built upon the low-level C API. Like the C API, direct usage of these language, built upon the low-level C API. Like the C API, direct usage of these
system APIs is rare compared to the bindings APIs, but it is sometimes desirable system APIs is rare compared to the bindings APIs, but it is sometimes desirable
......
This diff is collapsed.
...@@ -110,17 +110,7 @@ ScopedMessagePipeHandle MultiprocessTestHelper::StartChildWithExtraSwitch( ...@@ -110,17 +110,7 @@ ScopedMessagePipeHandle MultiprocessTestHelper::StartChildWithExtraSwitch(
mojo::NamedPlatformChannel::ServerName server_name; mojo::NamedPlatformChannel::ServerName server_name;
base::LaunchOptions options; base::LaunchOptions options;
if (launch_type == LaunchType::CHILD || launch_type == LaunchType::PEER) { if (launch_type == LaunchType::CHILD || launch_type == LaunchType::PEER) {
#if defined(OS_FUCHSIA) channel.PrepareToPassRemoteEndpoint(&options, &command_line);
channel.PrepareToPassRemoteEndpoint(&options.handles_to_transfer,
&command_line);
#elif defined(OS_POSIX)
channel.PrepareToPassRemoteEndpoint(&options.fds_to_remap, &command_line);
#elif defined(OS_WIN)
channel.PrepareToPassRemoteEndpoint(&options.handles_to_inherit,
&command_line);
#else
#error "Platform not yet supported."
#endif
} else if (launch_type == LaunchType::NAMED_CHILD || } else if (launch_type == LaunchType::NAMED_CHILD ||
launch_type == LaunchType::NAMED_PEER) { launch_type == LaunchType::NAMED_PEER) {
#if defined(OS_FUCHSIA) #if defined(OS_FUCHSIA)
......
This diff is collapsed.
# Mojo C++ Platform API
This document is a subset of the [Mojo documentation](/mojo/README.md).
[TOC]
## Overview
The Mojo C++ Platform API provides a lightweight set of abstractions around
stable platform primitive APIs like UNIX domain sockets and Windows named pipes.
This API is primarily useful in conjunction with Mojo
[Invitations](/mojo/public/cpp/system/README.md#Invitations) to bootstrap Mojo
IPC between two processes.
## Platform Handles
The `PlatformHandle` type provides a move-only wrapper around an owned,
platform-specific primitive handle types. The type of primitive it holds can be
any of the following:
* Windows HANDLE (Windows only)
* Fuchsia zx_handle_t (Fuchsia only)
* Mach send right (OSX only)
* POSIX file descriptor (POSIX systems only)
See the
[header](https://cs.chromium.org/src/mojo/public/cpp/platform/platform_handle.h)
for more details.
## Platform Channels
The `PlatformChannel` type abstracts a platform-specific IPC FIFO primitive
primarily for use with the Mojo
[Invitations](/mojo/public/cpp/system/README.md#Invitations) API. Constructing
a `PlatformChannel` instance creates the underlying system primitive with two
transferrable `PlatformHandle` instances, each thinly wrapped as a
`PlatformChannelEndpoint` for additional type-safety. One endpoint is designated
as **local** and the other **remote**, the intention being that the remote
endpoint will be transferred to another process in the system.
See the
[header](https://cs.chromium.org/src/mojo/public/cpp/platform/platform_channel.h)
for more details. See the
[Invitations](/mojo/public/cpp/system/README.md#Invitations) documentation for
an example of using `PlatformChannel` with an invitation to bootstrap IPC
between a process and one of its newly launched child processes.
## Named Platform Channels
For cases where it is not feasible to transfer a `PlatformHandle` from one
running process to another, the Platform API also provides
`NamedPlatformChannel`, which abstracts a named system resource that can
facilitate communication similarly to `PlatformChannel`.
A `NamedPlatformChannel` upon construction will begin listening on
platform-specific primitive (a named pipe server on Windows, a domain socket
server on POSIX, *etc.*). The globally reachable name of the server (*e.g.* the
socket path) can be specified at construction time via
`NamedPlatformChannel::Options::server_name`, but if no name is given, a
suitably random one is generated and used.
``` cpp
// In one process
mojo::NamedPlatformChannel::Options options;
mojo::NamedPlatformChannel named_channel(options);
OutgoingInvitation::Send(std::move(invitation),
named_channel.TakeServerEndpoint());
SendServerNameToRemoteProcessSomehow(named_channel.GetServerName());
// In the other process
void OnGotServerName(const mojo::NamedPlatformChannel::ServerName& name) {
// Connect to the server.
mojo::PlatformChannelEndpoint endpoint =
mojo::NamedPlatformChannel::ConnectToServer(name);
// Proceed normally with invitation acceptance.
auto invitation = mojo::IncomingInvitation::Accept(std::move(endpoint));
// ...
}
```
...@@ -208,6 +208,20 @@ void PlatformChannel::PrepareToPassRemoteEndpoint( ...@@ -208,6 +208,20 @@ void PlatformChannel::PrepareToPassRemoteEndpoint(
command_line->AppendSwitchASCII(kHandleSwitch, value); command_line->AppendSwitchASCII(kHandleSwitch, value);
} }
void PlatformChannel::PrepareToPassRemoteEndpoint(
base::LaunchOptions* options,
base::CommandLine* command_line) {
#if defined(OS_WIN)
PrepareToPassRemoteEndpoint(&options->handles_to_inherit, command_line);
#elif defined(OS_FUCHSIA)
PrepareToPassRemoteEndpoint(&options->handles_to_transfer, command_line);
#elif defined(OS_POSIX)
PrepareToPassRemoteEndpoint(&options->fds_to_remap, command_line);
#else
#error "Platform not supported."
#endif
}
void PlatformChannel::RemoteProcessLaunchAttempted() { void PlatformChannel::RemoteProcessLaunchAttempted() {
#if defined(OS_FUCHSIA) #if defined(OS_FUCHSIA)
// Unlike other platforms, Fuchsia transfers handle ownership to the new // Unlike other platforms, Fuchsia transfers handle ownership to the new
......
...@@ -84,6 +84,12 @@ class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) PlatformChannel { ...@@ -84,6 +84,12 @@ class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) PlatformChannel {
void PrepareToPassRemoteEndpoint(HandlePassingInfo* info, void PrepareToPassRemoteEndpoint(HandlePassingInfo* info,
base::CommandLine* command_line); base::CommandLine* command_line);
// Like above but adds handle-passing information directly to
// |*launch_options|, eliminating the potential need for callers to write
// platform-specific code to do the same.
void PrepareToPassRemoteEndpoint(base::LaunchOptions* options,
base::CommandLine* command_line);
// Must be called after the corresponding process launch attempt if // Must be called after the corresponding process launch attempt if
// |PrepareToPassRemoteEndpoint()| was used. // |PrepareToPassRemoteEndpoint()| was used.
void RemoteProcessLaunchAttempted(); void RemoteProcessLaunchAttempted();
......
...@@ -23,7 +23,8 @@ top-level `mojo` namespace. ...@@ -23,7 +23,8 @@ top-level `mojo` namespace.
All types of Mojo handles in the C API are simply opaque, integral `MojoHandle` All types of Mojo handles in the C API are simply opaque, integral `MojoHandle`
values. The C++ API has more strongly typed wrappers defined for different values. The C++ API has more strongly typed wrappers defined for different
handle types: `MessagePipeHandle`, `SharedBufferHandle`, handle types: `MessagePipeHandle`, `SharedBufferHandle`,
`DataPipeConsumerHandle`, `DataPipeProducerHandle`, and `WatcherHandle`. `DataPipeConsumerHandle`, `DataPipeProducerHandle`, `TrapHandle`, and
`InvitationHandle`.
Each of these also has a corresponding, move-only, scoped type for safer usage: Each of these also has a corresponding, move-only, scoped type for safer usage:
`ScopedMessagePipeHandle`, `ScopedSharedBufferHandle`, and so on. When a scoped `ScopedMessagePipeHandle`, `ScopedSharedBufferHandle`, and so on. When a scoped
...@@ -160,15 +161,17 @@ for detailed C++ shared buffer API documentation. ...@@ -160,15 +161,17 @@ for detailed C++ shared buffer API documentation.
The C++ library provides several helpers for wrapping system handle types. The C++ library provides several helpers for wrapping system handle types.
These are specifically useful when working with a few `//base` types, namely These are specifically useful when working with a few `//base` types, namely
`base::PlatformFile` and `base::SharedMemoryHandle`. See `base::PlatformFile`, `base::SharedMemoryHandle` (deprecated), and various
strongly-typed shared memory region types like
`base::ReadOnlySharedMemoryRegion`. See
[platform_handle.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/platform_handle.h) [platform_handle.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/platform_handle.h)
for detailed C++ platform handle API documentation. for detailed C++ platform handle API documentation.
## Signals & Watchers ## Signals & Traps
For an introduction to the concepts of handle signals and watchers, check out For an introduction to the concepts of handle signals and traps, check out
the C API's documentation on the C API's documentation on
[Signals & Watchers](/mojo/public/c/system/README.md#Signals-Watchers). [Signals & Traps](/mojo/public/c/system/README.md#Signals-Traps).
### Querying Signals ### Querying Signals
...@@ -206,7 +209,7 @@ if (message_pipe.handle0->QuerySignalsState().readable()) { ...@@ -206,7 +209,7 @@ if (message_pipe.handle0->QuerySignalsState().readable()) {
The [`mojo::SimpleWatcher`](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/simple_watcher.h) The [`mojo::SimpleWatcher`](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/simple_watcher.h)
class serves as a convenient helper for using the class serves as a convenient helper for using the
[low-level watcher API](/mojo/public/c/system/README.md#Signals-Watchers) [low-level traps API](/mojo/public/c/system/README.md#Signals-Traps)
to watch a handle for signaling state changes. A `SimpleWatcher` is bound to a to watch a handle for signaling state changes. A `SimpleWatcher` is bound to a
single sequence and always dispatches its notifications on a single sequence and always dispatches its notifications on a
`base::SequencedTaskRunner`. `base::SequencedTaskRunner`.
...@@ -218,7 +221,7 @@ time by the `mojo::SimpleWatcher::ArmingPolicy` enum: ...@@ -218,7 +221,7 @@ time by the `mojo::SimpleWatcher::ArmingPolicy` enum:
before any notifications will fire regarding the state of the watched handle. before any notifications will fire regarding the state of the watched handle.
Every time the notification callback is run, the `SimpleWatcher` must be Every time the notification callback is run, the `SimpleWatcher` must be
rearmed again before the next one can fire. See rearmed again before the next one can fire. See
[Arming a Watcher](/mojo/public/c/system/README.md#Arming-a-Watcher) and the [Arming a Trap](/mojo/public/c/system/README.md#Arming-a-Trap) and the
documentation in `SimpleWatcher`'s header. documentation in `SimpleWatcher`'s header.
* `AUTOMATIC` mode ensures that the `SimpleWatcher` always either is armed or * `AUTOMATIC` mode ensures that the `SimpleWatcher` always either is armed or
...@@ -274,7 +277,7 @@ WriteABunchOfStuff(pipe.handle1.get()); ...@@ -274,7 +277,7 @@ WriteABunchOfStuff(pipe.handle1.get());
The C++ System API defines some utilities to block a calling sequence while The C++ System API defines some utilities to block a calling sequence while
waiting for one or more handles to change signaling state in an interesting way. waiting for one or more handles to change signaling state in an interesting way.
These threads combine usage of the These threads combine usage of the
[low-level Watcher API](/mojo/public/c/system/README.md#Signals-Watchers) [low-level traps API](/mojo/public/c/system/README.md#Signals-Traps)
with common synchronization primitives (namely `base::WaitableEvent`.) with common synchronization primitives (namely `base::WaitableEvent`.)
While these API features should be used sparingly, they are sometimes necessary. While these API features should be used sparingly, they are sometimes necessary.
...@@ -388,3 +391,150 @@ if (num_ready_handles > 0) { ...@@ -388,3 +391,150 @@ if (num_ready_handles > 0) {
// signaling races with timeout, both things might be true. // signaling races with timeout, both things might be true.
} }
``` ```
## Invitations
Invitations are the means by which two processes can have Mojo IPC bootstrapped
between them. An invitation must be transmitted over some platform-specific IPC
primitive (*e.g.* a Windows named pipe or UNIX domain socket), and the public
[platform support library](/mojo/public/cpp/platform/README.md) provides some
lightweight, cross-platform abstractions for those primitives.
For any two processes looking to be connected, one must send an
`OutgoingInvitation` and the other must accept an `IncomingInvitation`. The
sender can attach named message pipe handles to the `OutgoingInvitation`, and
the receiver can extract them from its `IncomingInvitation`.
Basic usage might look something like this in the case where one process is
responsible for launching the other.
``` cpp
#include "base/command_line.h"
#include "base/process/launch.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/message_pipe.h"
mojo::ScopedMessagePipeHandle LaunchAndConnectSomething() {
// Under the hood, this is essentially always an OS pipe (domain socket pair,
// Windows named pipe, Fuchsia channel, etc).
mojo::PlatformChannel channel;
mojo::OutgoingInvitation invitation;
// Attach a message pipe to be extracted by the receiver. The other end of the
// pipe is returned for us to use locally.
mojo::ScopedMessagePipeHandle pipe =
invitation->AttachMessagePipe("arbitrary pipe name");
base::LaunchOptions options;
base::CommandLine command_line("some_executable")
channel.PrepareToPassRemoteEndpoint(&options, &command_line);
base::Process child_process = base::LaunchProcess(command_line, options);
channel.RemoteProcessLaunchAttempted();
OutgoingInvitation::Send(std::move(invitation), child_process.Handle(),
channel.TakeLocalEndpoint());
return pipe;
}
```
The launched process can in turn accept an `IncomingInvitation`:
``` cpp
#include "base/command_line.h"
#include "base/threading/thread.h"
#include "mojo/edk/embedder/embedder.h"
#include "mojo/edk/embedder/scoped_ipc_support.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/message_pipe.h"
int main(int argc, char** argv) {
// Basic Mojo initialization for a new process.
mojo::edk::Init();
base::Thread ipc_thread("ipc!");
ipc_thread.StartWithOptions(
base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
mojo::edk::ScopedIPCSupport ipc_support(
ipc_thread.task_runner(),
mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
// Accept an invitation.
mojo::IncomingInvitation invitation = mojo::IncomingInvitation::Accept(
mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine(
*base::CommandLine::ForCurrentProcess()));
mojo::ScopedMessagePipeHandle pipe =
invitation->ExtractMessagePipe("arbitrary pipe name");
// etc...
return GoListenForMessagesAndRunForever(std::move(pipe));
}
```
Now we have IPC initialized between the two processes.
Also keep in mind that bindings interfaces are just message pipes with some
semantic and syntactic sugar wrapping them, so you can use these primordial
message pipe handles as mojom interfaces. For example:
``` cpp
// Process A
mojo::OutgoingInvitation invitation;
auto pipe = invitation->AttachMessagePipe("x");
mojo::Binding<foo::mojom::Bar> binding(
&bar_impl,
foo::mojom::BarRequest(std::move(pipe)));
// Process B
auto invitation = mojo::IncomingInvitation::Accept(...);
auto pipe = invitation->ExtractMessagePipe("x");
foo::mojom::BarPtr bar(foo::mojom::BarPtrInfo(std::move(pipe), 0));
// Will asynchronously invoke bar_impl.DoSomething() in process A.
bar->DoSomething();
```
And just to be sure, the usage here could be reversed: the invitation sender
could just as well treat its pipe endpoint as a `BarPtr` while the receiver
treats theirs as a `BarRequest` to be bound.
### Process Networks
Accepting an invitation admits the accepting process into the sender's connected
network of processes. Once this is done, it's possible for the newly admitted
process to establish communication with any other process in that network via
normal message pipe passing.
This does not mean that the invited process can proactively locate and connect
to other processes without assistance; rather it means that Mojo handles created
by the process can safely be transferred to any other process in the network
over established message pipes, and similarly that Mojo handles created by any
other process in the network can be safely passed to the newly admitted process.
### Invitation Restrictions
A process may only belong to a single network at a time.
Additionally, once a process has joined a network, it cannot join another for
the remainder of its lifetime even if it has lost the connection to its original
network. This restriction will soon be lifted, but for now developers must be
mindful of it when authoring any long-running daemon process that will accept an
incoming invitation.
### Isolated Invitations
It is possible to have two independent networks of Mojo-connected processes; for
example, a long-running system daemon which uses Mojo to talk to child processes
of its own, as well as the Chrome browser process running with no common
ancestor, talking to its own child processes.
In this scenario it may be desirable to have a process in one network talk to a
process in the other network. Normal invitations cannot be used here since both
processes already belong to a network. In this case, an **isolated** invitation
can be used. These work just like regular invitations, except the sender must
call `OutgoingInvitation::SendIsolated` and the receiver must call
`IncomingInvitation::AcceptIsolated`.
Once a connection is established via isolated invitation, Mojo IPC can be used
normally, with the exception that transitive process connections are not
supported; that is, if process A sends a message pipe handle to process B via
an isolated connection, process B cannot reliably send that pipe handle onward
to another process in its own network. Isolated invitations therefore may only
be used to facilitate direct 1:1 communication between two processes.
...@@ -71,18 +71,9 @@ class InvitationCppTest : public testing::Test, ...@@ -71,18 +71,9 @@ class InvitationCppTest : public testing::Test,
command_line.AppendSwitchASCII(kTransportTypeSwitch, command_line.AppendSwitchASCII(kTransportTypeSwitch,
kTransportTypeChannel); kTransportTypeChannel);
channel.emplace(); channel.emplace();
#if defined(OS_FUCHSIA) channel->PrepareToPassRemoteEndpoint(&launch_options, &command_line);
channel->PrepareToPassRemoteEndpoint(&launch_options.handles_to_transfer, #if defined(OS_WIN)
&command_line);
#elif defined(OS_POSIX)
channel->PrepareToPassRemoteEndpoint(&launch_options.fds_to_remap,
&command_line);
#elif defined(OS_WIN)
launch_options.start_hidden = true; launch_options.start_hidden = true;
channel->PrepareToPassRemoteEndpoint(&launch_options.handles_to_inherit,
&command_line);
#else
#error "Unsupported platform."
#endif #endif
channel_endpoint = channel->TakeLocalEndpoint(); channel_endpoint = channel->TakeLocalEndpoint();
} else if (transport_type == TransportType::kChannelServer) { } else if (transport_type == TransportType::kChannelServer) {
......
...@@ -265,16 +265,7 @@ class ServiceManagerTest : public test::ServiceTest, ...@@ -265,16 +265,7 @@ class ServiceManagerTest : public test::ServiceTest,
// on the command line. // on the command line.
mojo::PlatformChannel channel; mojo::PlatformChannel channel;
base::LaunchOptions options; base::LaunchOptions options;
#if defined(OS_WIN) channel.PrepareToPassRemoteEndpoint(&options, &child_command_line);
channel.PrepareToPassRemoteEndpoint(&options.handles_to_inherit,
&child_command_line);
#elif defined(OS_FUCHSIA)
channel.PrepareToPassRemoteEndpoint(&options.handles_to_transfer,
&child_command_line);
#else
channel.PrepareToPassRemoteEndpoint(&options.fds_to_remap,
&child_command_line);
#endif
mojo::OutgoingInvitation invitation; mojo::OutgoingInvitation invitation;
service_manager::mojom::ServicePtr client = service_manager::mojom::ServicePtr client =
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment