| Index: mojo/public/cpp/system/README.md
|
| diff --git a/mojo/public/cpp/system/README.md b/mojo/public/cpp/system/README.md
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..782744f0b187782220d15bde939214605251ec05
|
| --- /dev/null
|
| +++ b/mojo/public/cpp/system/README.md
|
| @@ -0,0 +1,396 @@
|
| +#  Mojo C++ System API
|
| +This document is a subset of the [Mojo documentation](/mojo).
|
| +
|
| +[TOC]
|
| +
|
| +## Overview
|
| +The Mojo C++ System API provides a convenient set of helper classes and
|
| +functions for working with Mojo primitives. Unlike the low-level
|
| +[C API](/mojo/public/c/system) (upon which this is built) this library takes
|
| +advantage of C++ language features and common STL and `//base` types to provide
|
| +a slightly more idiomatic interface to the Mojo system layer, making it
|
| +generally easier to use.
|
| +
|
| +This document provides a brief guide to API usage with example code snippets.
|
| +For a detailed API references please consult the headers in
|
| +[//mojo/public/cpp/system](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/).
|
| +
|
| +Note that all API symbols referenced in this document are implicitly in the
|
| +top-level `mojo` namespace.
|
| +
|
| +## Scoped, Typed Handles
|
| +
|
| +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
|
| +handle types: `MessagePipeHandle`, `SharedBufferHandle`,
|
| +`DataPipeConsumerHandle`, `DataPipeProducerHandle`, and `WatcherHandle`.
|
| +
|
| +Each of these also has a corresponding, move-only, scoped type for safer usage:
|
| +`ScopedMessagePipeHandle`, `ScopedSharedBufferHandle`, and so on. When a scoped
|
| +handle type is destroyed, its handle is automatically closed via `MojoClose`.
|
| +When working with raw handles you should **always** prefer to use one of the
|
| +scoped types for ownership.
|
| +
|
| +Similar to `std::unique_ptr`, scoped handle types expose a `get()` method to get
|
| +at the underlying unscoped handle type as well as the `->` operator to
|
| +dereference the scoper and make calls directly on the underlying handle type.
|
| +
|
| +## Message Pipes
|
| +
|
| +There are two ways to create a new message pipe using the C++ API. You may
|
| +construct a `MessagePipe` object:
|
| +
|
| +``` cpp
|
| +mojo::MessagePipe pipe;
|
| +
|
| +// NOTE: Because pipes are bi-directional there is no implicit semantic
|
| +// difference between |handle0| or |handle1| here. They're just two ends of a
|
| +// pipe. The choice to treat one as a "client" and one as a "server" is entirely
|
| +// a the API user's decision.
|
| +mojo::ScopedMessagePipeHandle client = std::move(pipe.handle0);
|
| +mojo::ScopedMessagePipeHandle server = std::move(pipe.handle1);
|
| +```
|
| +
|
| +or you may call `CreateMessagePipe`:
|
| +
|
| +``` cpp
|
| +mojo::ScopedMessagePipeHandle client;
|
| +mojo::ScopedMessagePipeHandle server;
|
| +mojo::CreateMessagePipe(nullptr, &client, &server);
|
| +```
|
| +
|
| +There are also some helper functions for constructing message objects and
|
| +reading/writing them on pipes using the library's more strongly-typed C++
|
| +handles:
|
| +
|
| +``` cpp
|
| +mojo::ScopedMessageHandle message;
|
| +mojo::AllocMessage(6, nullptr, 0, MOJO_ALLOC_MESSAGE_FLAG_NONE, &message);
|
| +
|
| +void *buffer;
|
| +mojo::GetMessageBuffer(message.get(), &buffer);
|
| +
|
| +const std::string kMessage = "hello";
|
| +std::copy(kMessage.begin(), kMessage.end(), static_cast<char*>(buffer));
|
| +
|
| +mojo::WriteMessageNew(client.get(), std::move(message),
|
| + MOJO_WRITE_MESSAGE_FLAG_NONE);
|
| +
|
| +// Some time later...
|
| +
|
| +mojo::ScopedMessageHandle received_message;
|
| +uint32_t num_bytes;
|
| +mojo::ReadMessageNew(server.get(), &received_message, &num_bytes, nullptr,
|
| + nullptr, MOJO_READ_MESSAGE_FLAG_NONE);
|
| +```
|
| +
|
| +See [message_pipe.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/message_pipe.h)
|
| +for detailed C++ message pipe API documentation.
|
| +
|
| +## Data Pipes
|
| +
|
| +Similar to [Message Pipes](#Message-Pipes), the C++ library has some simple
|
| +helpers for more strongly-typed data pipe usage:
|
| +
|
| +``` cpp
|
| +mojo::DataPipe pipe;
|
| +mojo::ScopedDataPipeProducerHandle producer = std::move(pipe.producer);
|
| +mojo::ScopedDataPipeConsumerHandle consumer = std::move(pipe.consumer);
|
| +
|
| +// Or alternatively:
|
| +mojo::ScopedDataPipeProducerHandle producer;
|
| +mojo::ScopedDataPipeConsumerHandle consumer;
|
| +mojo::CreateDataPipe(null, &producer, &consumer);
|
| +```
|
| +
|
| +// Reads from a data pipe. See |MojoReadData()| for complete documentation.
|
| +inline MojoResult ReadDataRaw(DataPipeConsumerHandle data_pipe_consumer,
|
| + void* elements,
|
| + uint32_t* num_bytes,
|
| + MojoReadDataFlags flags) {
|
| + return MojoReadData(data_pipe_consumer.value(), elements, num_bytes, flags);
|
| +}
|
| +
|
| +// Begins a two-phase read
|
| +C++ helpers which correspond directly to the
|
| +[Data Pipe C API](/mojo/public/c/system#Data-Pipes) for immediate and two-phase
|
| +I/O are provided as well. For example:
|
| +
|
| +``` cpp
|
| +uint32_t num_bytes = 7;
|
| +mojo::WriteDataRaw(producer.get(), "hihihi",
|
| + &num_bytes, MOJO_WRITE_DATA_FLAG_NONE);
|
| +
|
| +// Some time later...
|
| +
|
| +char buffer[64];
|
| +uint32_t num_bytes = 64;
|
| +mojo::ReadDataRaw(consumer.get(), buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE);
|
| +```
|
| +
|
| +See [data_pipe.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/data_pipe.h)
|
| +for detailed C++ data pipe API documentation.
|
| +
|
| +## Shared Buffers
|
| +
|
| +A new shared buffers can be allocated like so:
|
| +
|
| +``` cpp
|
| +mojo::ScopedSharedBufferHandle buffer =
|
| + mojo::ScopedSharedBufferHandle::Create(4096);
|
| +```
|
| +
|
| +This new handle can be cloned arbitrarily many times by using the underlying
|
| +handle's `Clone` method:
|
| +
|
| +``` cpp
|
| +mojo::ScopedSharedBufferHandle another_handle = buffer->Clone();
|
| +mojo::ScopedSharedBufferHandle read_only_handle =
|
| + buffer->Clone(mojo::SharedBufferHandle::AccessMode::READ_ONLY);
|
| +```
|
| +
|
| +And finally the library also provides a scoper for mapping the shared buffer's
|
| +memory:
|
| +
|
| +``` cpp
|
| +mojo::ScopedSharedBufferMapping mapping = buffer->Map(64);
|
| +static_cast<int*>(mapping.get()) = 42;
|
| +
|
| +mojo::ScopedSharedBufferMapping another_mapping = buffer->MapAtOffset(64, 4);
|
| +static_cast<int*>(mapping.get()) = 43;
|
| +```
|
| +
|
| +When `mapping` and `another_mapping` are destroyed, they automatically unmap
|
| +their respective memory regions.
|
| +
|
| +See [buffer.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/buffer.h)
|
| +for detailed C++ shared buffer API documentation.
|
| +
|
| +## Native Platform Handles (File Descriptors, Windows Handles, *etc.*)
|
| +
|
| +The C++ library provides several helpers for wrapping system handle types.
|
| +These are specifically useful when working with a few `//base` types, namely
|
| +`base::PlatformFile` and `base::SharedMemoryHandle`. See
|
| +[platform_handle.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/platform_handle.h)
|
| +for detailed C++ platform handle API documentation.
|
| +
|
| +## Signals & Watchers
|
| +
|
| +For an introduction to the concepts of handle signals and watchers, check out
|
| +the C API's documentation on [Signals & Watchers](/mojo/public/c/system#Signals-Watchers).
|
| +
|
| +### Querying Signals
|
| +
|
| +Any C++ handle type's last known signaling state can be queried by calling the
|
| +`QuerySignalsState` method on the handle:
|
| +
|
| +``` cpp
|
| +mojo::MessagePipe message_pipe;
|
| +mojo::DataPipe data_pipe;
|
| +mojo::HandleSignalsState a = message_pipe.handle0->QuerySignalsState();
|
| +mojo::HandleSignalsState b = data_pipe.consumer->QuerySignalsState();
|
| +```
|
| +
|
| +The `HandleSignalsState` is a thin wrapper interface around the C API's
|
| +`MojoHandleSignalsState` structure with convenient accessors for testing
|
| +the signal bitmasks. Whereas when using the C API you might write:
|
| +
|
| +``` c
|
| +struct MojoHandleSignalsState state;
|
| +MojoQueryHandleSignalsState(handle0, &state);
|
| +if (state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE) {
|
| + // ...
|
| +}
|
| +```
|
| +
|
| +the C++ API equivalent would be:
|
| +
|
| +``` cpp
|
| +if (message_pipe.handle0->QuerySignalsState().readable()) {
|
| + // ...
|
| +}
|
| +```
|
| +
|
| +### Watching Handles
|
| +
|
| +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 [low-level watcher API](/mojo/public/c/system#Signals-Watchers)
|
| +to watch a handle for signaling state changes. A `SimpleWatcher` is bound to a
|
| +single thread and always dispatches its notifications on a
|
| +`base::SingleThreadTaskRunner`.
|
| +
|
| +`SimpleWatcher` has two possible modes of operation, selected at construction
|
| +time by the `mojo::SimpleWatcher::ArmingPolicy` enum:
|
| +
|
| +* `MANUAL` mode requires the user to manually call `Arm` and/or `ArmOrNotify`
|
| + before any notifications will fire regarding the state of the watched handle.
|
| + Every time the notification callback is run, the `SimpleWatcher` must be
|
| + rearmed again before the next one can fire. See
|
| + [Arming a Watcher](/mojo/public/c/system#Arming-a-Watcher) and the
|
| + documentation in `SimpleWatcher`'s header.
|
| +
|
| +* `AUTOMATIC` mode ensures that the `SimpleWatcher` always either is armed or
|
| + has a pending notification task queued for execution.
|
| +
|
| +`AUTOMATIC` mode is more convenient but can result in redundant notification
|
| +tasks, especially if the provided callback does not make a strong effort to
|
| +return the watched handle to an uninteresting signaling state (by *e.g.*,
|
| +reading all its available messages when notified of readability.)
|
| +
|
| +Example usage:
|
| +
|
| +``` cpp
|
| +class PipeReader {
|
| + public:
|
| + PipeReader(mojo::ScopedMessagePipeHandle pipe)
|
| + : pipe_(std::move(pipe)),
|
| + watcher_(mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC) {
|
| + // NOTE: base::Unretained is safe because the callback can never be run
|
| + // after SimpleWatcher destruction.
|
| + watcher_.Watch(pipe_.get(), MOJO_HANDLE_SIGNAL_READABLE,
|
| + base::Bind(&PipeReader::OnReadable, base::Unretained(this)));
|
| + }
|
| +
|
| + ~PipeReader() {}
|
| +
|
| + private:
|
| + void OnReadable(MojoResult result) {
|
| + while (result == MOJO_RESULT_OK) {
|
| + mojo::ScopedMessageHandle message;
|
| + uint32_t num_bytes;
|
| + result = mojo::ReadMessageNew(pipe_.get(), &message, &num_bytes, nullptr,
|
| + nullptr, MOJO_READ_MESSAGE_FLAG_NONE);
|
| + DCHECK_EQ(result, MOJO_RESULT_OK);
|
| + messages_.emplace_back(std::move(message));
|
| + }
|
| + }
|
| +
|
| + mojo::ScopedMessagePipeHandle pipe_;
|
| + mojo::SimpleWatcher watcher_;
|
| + std::vector<mojo::ScopedMessageHandle> messages_;
|
| +};
|
| +
|
| +mojo::MessagePipe pipe;
|
| +PipeReader reader(std::move(pipe.handle0));
|
| +
|
| +// Written messages will asynchronously end up in |reader.messages_|.
|
| +WriteABunchOfStuff(pipe.handle1.get());
|
| +```
|
| +
|
| +## Synchronous Waiting
|
| +
|
| +The C++ System API defines some utilities to block a calling thread while
|
| +waiting for one or more handles to change signaling state in an interesting way.
|
| +These threads combine usage of the [low-level Watcher API](/mojo/public/c/system#Signals-Watchers)
|
| +with common synchronization primitives (namely `base::WaitableEvent`.)
|
| +
|
| +While these API features should be used sparingly, they are sometimes necessary.
|
| +
|
| +See the documentation in
|
| +[wait.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/wait.h)
|
| +and [wait_set.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/wait_set.h)
|
| +for a more detailed API reference.
|
| +
|
| +### Waiting On a Single Handle
|
| +
|
| +The `mojo::Wait` function simply blocks the calling thread until a given signal
|
| +mask is either partially satisfied or fully unsatisfiable on a given handle.
|
| +
|
| +``` cpp
|
| +mojo::MessagePipe pipe;
|
| +mojo::WriteMessageRaw(pipe.handle0.get(), "hey", 3, nullptr, nullptr,
|
| + MOJO_WRITE_MESSAGE_FLAG_NONE);
|
| +MojoResult result = mojo::Wait(pipe.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE);
|
| +DCHECK_EQ(result, MOJO_RESULT_OK);
|
| +
|
| +// Guaranteed to succeed because we know |handle1| is readable now.
|
| +mojo::ScopedMessageHandle message;
|
| +uint32_t num_bytes;
|
| +mojo::ReadMessageNew(pipe.handle1.get(), &num_bytes, nullptr, nullptr,
|
| + MOJO_READ_MESSAGE_FLAG_NONE);
|
| +```
|
| +
|
| +`mojo::Wait` is most typically useful in limited testing scenarios.
|
| +
|
| +### Waiting On Multiple Handles
|
| +
|
| +`mojo::WaitMany` provides a simple API to wait on multiple handles
|
| +simultaneously, returning when any handle's given signal mask is either
|
| +partially satisfied or fully unsatisfiable.
|
| +
|
| +``` cpp
|
| +mojo::MessagePipe a, b;
|
| +GoDoSomethingWithPipes(std:move(a.handle1), std::move(b.handle1));
|
| +
|
| +mojo::MessagePipeHandle handles[2] = {a.handle0.get(), b.handle0.get()};
|
| +MojoHandleSignals signals[2] = {MOJO_HANDLE_SIGNAL_READABLE,
|
| + MOJO_HANDLE_SIGNAL_READABLE};
|
| +size_t ready_index;
|
| +MojoResult result = mojo::WaitMany(handles, signals, 2, &ready_index);
|
| +if (ready_index == 0) {
|
| + // a.handle0 was ready.
|
| +} else {
|
| + // b.handle0 was ready.
|
| +}
|
| +```
|
| +
|
| +Similar to `mojo::Wait`, `mojo::WaitMany` is primarily useful in testing. When
|
| +waiting on multiple handles in production code, you should almost always instead
|
| +use a more efficient and more flexible `mojo::WaitSet` as described in the next
|
| +section.
|
| +
|
| +### Waiting On Handles and Events Simultaneously
|
| +
|
| +Typically when waiting on one or more handles to signal, the set of handles and
|
| +conditions being waited upon do not change much between consecutive blocking
|
| +waits. It's also often useful to be able to interrupt the blocking operation
|
| +as efficiently as possible.
|
| +
|
| +[`mojo::WaitSet`](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/wait_set.h)
|
| +is designed with these conditions in mind. A `WaitSet` maintains a persistent
|
| +set of (not-owned) Mojo handles and `base::WaitableEvent`s, which may be
|
| +explicitly added to or removed from the set at any time.
|
| +
|
| +The `WaitSet` may be waited upon repeatedly, each time blocking the calling
|
| +thread until either one of the handles attains an interesting signaling state or
|
| +one of the events is signaled. For example let's suppose we want to wait up to 5
|
| +seconds for either one of two handles to become readable:
|
| +
|
| +``` cpp
|
| +base::WaitableEvent timeout_event(
|
| + base::WaitableEvent::ResetPolicy::MANUAL,
|
| + base::WaitableEvent::InitialState::NOT_SIGNALED);
|
| +mojo::MessagePipe a, b;
|
| +
|
| +GoDoStuffWithPipes(std::move(a.handle1), std::move(b.handle1));
|
| +
|
| +mojo::WaitSet wait_set;
|
| +wait_set.AddHandle(a.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
|
| +wait_set.AddHandle(b.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
|
| +wait_set.AddEvent(&timeout_event);
|
| +
|
| +// Ensure the Wait() lasts no more than 5 seconds.
|
| +bg_thread->task_runner()->PostDelayedTask(
|
| + FROM_HERE,
|
| + base::Bind([](base::WaitableEvent* e) { e->Signal(); }, &timeout_event);
|
| + base::TimeDelta::FromSeconds(5));
|
| +
|
| +base::WaitableEvent* ready_event = nullptr;
|
| +size_t num_ready_handles = 1;
|
| +mojo::Handle ready_handle;
|
| +MojoResult ready_result;
|
| +wait_set.Wait(&ready_event, &num_ready_handles, &ready_handle, &ready_result);
|
| +
|
| +// The apex of thread-safety.
|
| +bg_thread->Stop();
|
| +
|
| +if (ready_event) {
|
| + // The event signaled...
|
| +}
|
| +
|
| +if (num_ready_handles > 0) {
|
| + // At least one of the handles signaled...
|
| + // NOTE: This and the above condition are not mutually exclusive. If handle
|
| + // signaling races with timeout, both things might be true.
|
| +}
|
| +```
|
|
|