diff options
Diffstat (limited to 'mojo/public/cpp/system/README.md')
-rw-r--r-- | mojo/public/cpp/system/README.md | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/mojo/public/cpp/system/README.md b/mojo/public/cpp/system/README.md new file mode 100644 index 0000000000..782744f0b1 --- /dev/null +++ b/mojo/public/cpp/system/README.md @@ -0,0 +1,396 @@ +# ![Mojo Graphic](https://goo.gl/6CdlbH) 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. +} +``` |