Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(27)

Unified Diff: mojo/public/c/system/README.md

Issue 2783223004: Adds lots of Mojo documentation (Closed)
Patch Set: Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: mojo/public/c/system/README.md
diff --git a/mojo/public/c/system/README.md b/mojo/public/c/system/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..2abe80ffc730aa3dad08d87f2bd98d05b77740d4
--- /dev/null
+++ b/mojo/public/c/system/README.md
@@ -0,0 +1,869 @@
+# ![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 is a lightweight API (with an eventually-stable ABI) upon
+which all higher layers of the Mojo system are built.
+
+This API exposes the fundamental capabilities to: create, read from, and write
+to **message pipes**; create, read from, and write to **data pipes**; create
+**shared buffers** and generate sharable handles to them; wrap platform-specific
+handle objects (such as **file descriptors**, **Windows handles**, and
+**Mach ports**) for seamless transit over message pipes; and efficiently watch
+handles for various types of state transitions.
+
+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/c/system](https://cs.chromium.org/chromium/src/mojo/public/c/system/).
+
+### A Note About Multithreading
+
+The Mojo C System API is entirely thread-agnostic. This means that all functions
+may be called from any thread in a process, and there are no restrictions on how
+many threads can use the same object at the same time.
+
+Of course this does not mean you can completely ignore potential concurrency
+issues -- such as a handle being closed on one thread while another thread is
+trying to perform an operation on the same handle -- but there is nothing
+fundamentally incorrect about using any given API or handle from multiple
+threads.
+
+### A Note About Synchronization
+
+Every Mojo API call is non-blocking and synchronously yields some kind of status
+result code, but the call's side effects -- such as affecting the state of
+one or more handles in the system -- may or may not occur asynchronously.
+
+Mojo objects can be observed for interesting state changes in a way that is
+thread-agnostic and in some ways similar to POSIX signal handlers: *i.e.*
+user-provided notification handlers may be invoked at any time on arbitrary
+threads in the process. It is entirely up to the API user to take appropriate
+measures to synchronize operations against other application state.
+
+The higher level [system](/mojo#High-Level-System-APIs) and
+[bindings](/mojo#High-Level-Bindings-APIs) APIs provide helpers to simplify Mojo
+usage in this regard, at the expense of some flexibility.
+
+## Result Codes
+
+Most API functions return a value of type `MojoResult`. This is an integral
+result code used to convey some meaningful level of detail about the result of a
+requested operation.
+
+See [//mojo/public/c/system/types.h](https://cs.chromium.org/chromium/src/mojo/public/c/system/types.h)
+for different possible values. See documentation for individual API calls for
+more specific contextual meaning of various result codes.
+
+## Handles
+
+Every Mojo IPC primitive is identified by a generic, opaque integer handle of
+type `MojoHandle`. Handles can be acquired by creating new objects using various
+API calls, or by reading messages which contain attached handles.
+
+A `MojoHandle` can represent a message pipe endpoint, a data pipe consumer,
+a data pipe producer, a shared buffer reference, a wrapped native platform
+handle such as a POSIX file descriptor or a Windows system handle, or a watcher
+object (see [Signals & Watchers](#Signals-Watchers) below.)
+
+All types of handles except for watchers (which are an inherently local concept)
+can be attached to messages and sent over message pipes.
+
+Any `MojoHandle` may be closed by calling `MojoClose`:
+
+``` c
+MojoHandle x = DoSomethingToGetAValidHandle();
+MojoResult result = MojoClose(x);
+```
+
+If the handle passed to `MojoClose` was a valid handle, it will be closed and
+`MojoClose` returns `MOJO_RESULT_OK`. Otherwise it returns
+`MOJO_RESULT_INVALID_ARGUMENT`.
+
+Similar to native system handles on various popular platforms, `MojoHandle`
+values may be reused over time. Thus it is important to avoid logical errors
+which lead to misplaced handle ownership, double-closes, *etc.*
+
+## Message Pipes
+
+A message pipe is a bidirectional messaging channel which can carry arbitrary
+unstructured binary messages with zero or more `MojoHandle` attachments to be
+transferred from one end of a pipe to the other. Message pipes work seamlessly
+across process boundaries or within a single process.
+
+The [Embedder Development Kit (EDK)](/mojo/edk/embedder) provides the means to
+bootstrap one or more primordial cross-process message pipes, and it's up to
+Mojo embedders to expose this capability in some useful way. Once such a pipe is
+established, additional handles -- including other message pipe handles -- may
+be sent to a remote process using that pipe (or in turn, over other pipes sent
+over that pipe, or pipes sent over *that* pipe, and so on...)
+
+The public C System API exposes the ability to read and write messages on pipes
+and to create new message pipes.
+
+See [//mojo/public/c/system/message_pipe.h](https://cs.chromium.org/chromium/src/mojo/public/c/system/message_pipe.h)
+for detailed message pipe API documentation.
+
+### Creating Message Pipes
+
+`MojoCreateMessagePipe` can be used to create a new message pipe:
+
+``` c
+MojoHandle a, b;
+MojoResult result = MojoCreateMessagePipe(NULL, &a, &b);
+```
+
+After this snippet, `result` should be `MOJO_RESULT_OK` (it's really hard for
+this to fail!), and `a` and `b` will contain valid Mojo handles, one for each
+end of the new message pipe.
+
+Any messages written to `a` are eventually readable from `b`, and any messages
+written to `b` are eventually readable from `a`. If `a` is closed at any point,
+`b` will eventually become aware of this fact; likewise if `b` is closed, `a`
+will become aware of that.
+
+The state of these conditions can be queried and watched asynchronously as
+described in the [Signals & Watchers](#Signals-Watchers) section below.
+
+### Allocating Messages
+
+In order to avoid redundant internal buffer copies, Mojo would like to allocate
+your message storage buffers for you. This is easy:
+
+``` c
+MojoMessageHandle message;
+MojoResult result = MojoAllocMessage(6, NULL, 0, MOJO_ALLOC_MESSAGE_FLAG_NONE,
+ &message);
+```
+
+Note that we have a special `MojoMessageHandle` type for message objects.
+
+The code above allocates a buffer for a message payload of 6 bytes with no
+handles attached.
+
+If we change our mind and decide not to send this message, we can delete it:
+
+``` c
+MojoResult result = MojoFreeMessage(message);
+```
+
+If we instead decide to send our newly allocated message, we first need to fill
+in the payload data with something interesting. How about a pleasant greeting:
+
+``` c
+void* buffer = NULL;
+MojoResult result = MojoGetMessageBuffer(message, &buffer);
+memcpy(buffer, "hello", 6);
+```
+
+Now we can write the message to a pipe. Note that attempting to write a message
+transfers ownership of the message object (and any attached handles) into the
+target pipe and there is therefore no need to subsequently call
+`MojoFreeMessage` on that message.
+
+### Writing Messages
+
+``` c
+result = MojoWriteMessageNew(a, message, MOJO_WRITE_MESSAGE_FLAG_NONE);
+```
+
+`MojoWriteMessage` is a *non-blocking* call: it always returns
+immediately. If its return code is `MOJO_RESULT_OK` the message will eventually
+find its way to the other end of the pipe -- assuming that end isn't closed
+first, of course. If the return code is anything else, the message is deleted
+and not transferred.
+
+In this case since we know `b` is still open, we also know the message will
+eventually arrive at `b`. `b` can be queried or watched to become aware of when
+the message arrives, but we'll ignore that complexity for now. See
+[Signals & Watchers](#Signals-Watchers) below for more information.
+
+*** aside
+**NOTE**: Although this is an implementation detail and not strictly guaranteed by the
+System API, it is true in the current implementation that the message will
+arrive at `b` before the above `MojoWriteMessage` call even returns, because `b`
+is in the same process as `a` and has never been transferred over another pipe.
+***
+
+### Reading Messages
+
+We can read a new message object from a pipe:
+
+``` c
+MojoMessageHandle message;
+uint32_t num_bytes;
+MojoResult result = MojoReadMessageNew(b, &message, &num_bytes, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE);
+```
+
+and map its buffer to retrieve the contents:
+
+``` c
+void* buffer = NULL;
+MojoResult result = MojoGetMessageBuffer(message, &buffer);
+printf("Pipe says: %s", (const char*)buffer);
+```
+
+`result` should be `MOJO_RESULT_OK` and this snippet should write `"hello"` to
+`stdout`.
+
+If we try were to try reading again now that there are no messages on `b`:
+
+``` c
+MojoMessageHandle message;
+MojoResult result = MojoReadMessageNew(b, &message, NULL, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE);
+```
+
+We'll get a `result` of `MOJO_RESULT_SHOULD_WAIT`, indicating that the pipe is
+not yet readable.
+
+### Messages With Handles
+
+Probably the most useful feature of Mojo IPC is that message pipes can carry
+arbitrary Mojo handles, including other message pipes. This is also
+straightforward.
+
+Here's an example which creates two pipes, using the first pipe to transfer
+one end of the second pipe. If you have a good imagination you can pretend the
+first pipe spans a process boundary, which makes the example more practically
+interesting:
+
+``` c
+MojoHandle a, b;
+MojoHandle c, d;
+MojoMessage message;
+
+// Allocate a message with an empty payload and handle |c| attached. Note that
+// this takes ownership of |c|, effectively invalidating its handle value.
+MojoResult result = MojoAllocMessage(0, &c, 1, MOJO_ALLOC_MESSAGE_FLAG_NONE,
+ message);
+
+result = MojoWriteMessageNew(a, message, MOJO_WRITE_MESSAGE_FLAG_NONE);
+
+// Some time later...
+uint32_t num_bytes;
+MojoHandle e;
+uint32_t num_handles = 1;
+MojoResult result = MojoReadMessageNew(b, &message, &num_bytes, &e,
+ &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE);
+```
+
+At this point the handle in `e` is now referencing the same message pipe
+endpoint which was originally referenced by `c`.
+
+Note that `num_handles` above is initialized to 1 before we pass its address to
+`MojoReadMessageNew`. This is to indicate how much `MojoHandle` storage is
+available at the output buffer we gave it (`&e` above).
+
+If we didn't know how many handles to expect in an incoming message -- which is
+often the case -- we can use `MojoReadMessageNew` to query for this information
+first:
+
+``` c
+MojoMessageHandle message;
+uint32_t num_bytes = 0;
+uint32_t num_handles = 0;
+MojoResult result = MojoReadMessageNew(b, &message, &num_bytes, NULL,
+ &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE);
+```
+
+If in this case there were a received message on `b` with some nonzero number
+of handles, `result` would be `MOJO_RESULT_RESOURCE_EXHAUSTED`, and both
+`num_bytes` and `num_handles` would be updated to reflect the payload size and
+number of attached handles on the next available message.
+
+It's also worth noting that if there did happen to be a message available with
+no payload and no handles (*i.e.* an empty message), this would actually return
+`MOJO_RESULT_OK`.
+
+## Data Pipes
+
+Data pipes provide an efficient unidirectional channel for moving large amounts
+of unframed data between two endpoints. Every data pipe has a fixed
+**element size** and **capacity**. Reads and writes must be done in sizes that
+are a multiple of the element size, and writes to the pipe can only be queued
+up to the pipe's capacity before reads must be done to make more space
+available.
+
+Every data pipe has a single **producer** handle used to write data into the
+pipe and a single **consumer** handle used to read data out of the pipe.
+
+Finally, data pipes support both immediate I/O -- reading into and writing out
+from user-supplied buffers -- as well as two-phase I/O, allowing callers to
+temporarily lock some portion of the data pipe in order to read or write its
+contents directly.
+
+See [//mojo/public/c/system/data_pipe.h](https://cs.chromium.org/chromium/src/mojo/public/c/system/data_pipe.h)
+for detailed data pipe API documentation.
+
+### Creating Data Pipes
+
+Use `MojoCreateDataPipe` to create a new data pipe. The
+`MojoCreateDataPipeOptions` structure is used to configure the new pipe, but
+this can be omitted to assume the default options of a single-byte element size
+and an implementation-defined default capacity (64 kB at the time of this
+writing.)
+
+``` c
+MojoHandle producer, consumer;
+MojoResult result = MojoCreateDataPipe(NULL, &producer, &consumer);
+```
+
+### Immediate I/O
+
+Data can be written into or read out of a data pipe using buffers provided by
+the caller. This is generally more convenient than two-phase I/O but is
+also less efficient due to extra copying.
+
+``` c
+uint32_t num_bytes = 12;
+MojoResult result = MojoWriteData(producer, "datadatadata", &num_bytes,
+ MOJO_WRITE_DATA_FLAG_NONE);
+```
+
+The above snippet will attempt to write 12 bytes into the data pipe, which
+should succeed and return `MOJO_RESULT_OK`. If the available capacity on the
+pipe was less than the amount requested (the input value of `*num_bytes`) this
+will copy what it can into the pipe and return the number of bytes written in
+`*num_bytes`. If no data could be copied this will instead return
+`MOJO_RESULT_SHOULD_WAIT`.
+
+Reading from the consumer is a similar operation.
+
+``` c
+char buffer[64];
+uint32_t num_bytes = 64;
+MojoResult result = MojoReadData(consumer, buffer, &num_bytes,
+ MOJO_READ_DATA_FLAG_NONE);
+```
+
+This will attempt to read up to 64 bytes, returning the actual number of bytes
+read in `*num_bytes`.
+
+`MojoReadData` supports a number of interesting flags to change the behavior:
+you can peek at the data (copy bytes out without removing them from the pipe),
+query the number of bytes available without doing any actual reading of the
+contents, or discard data from the pipe without bothering to copy it anywhere.
+
+This also supports a `MOJO_READ_DATA_FLAG_ALL_OR_NONE` which ensures that the
+call succeeds **only** if the exact number of bytes requested could be read.
+Otherwise such a request will fail with `MOJO_READ_DATA_OUT_OF_RANGE`.
+
+### Two-Phase I/O
+
+Data pipes also support two-phase I/O operations, allowing a caller to
+temporarily lock a portion of the data pipe's storage for direct memory access.
+
+``` c
+void* buffer;
+uint32_t num_bytes = 1024;
+MojoResult result = MojoBeginWriteData(producer, &buffer, &num_bytes,
+ MOJO_WRITE_DATA_FLAG_NONE);
+```
+
+This requests write access to a region of up to 1024 bytes of the data pipe's
+next available capacity. Upon success, `buffer` will point to the writable
+storage and `num_bytes` will indicate the size of the buffer there.
+
+The caller should then write some data into the memory region and release it
+ASAP, indicating the number of bytes actually written:
+
+``` c
+memcpy(buffer, "hello", 6);
+MojoResult result = MojoEndWriteData(producer, 6);
+```
+
+Two-phase reads look similar:
+
+``` c
+void* buffer;
+uint32_t num_bytes = 1024;
+MojoResult result = MojoBeginReadData(consumer, &buffer, &num_bytes,
+ MOJO_READ_DATA_FLAG_NONE);
+// result should be MOJO_RESULT_OK, since there is some data available.
+
+printf("Pipe says: %s", (const char*)buffer); // Should say "hello".
+
+result = MojoEndReadData(consumer, 1); // Say we only consumed one byte.
+
+num_bytes = 1024;
+result = MojoBeginReadData(consumer, &buffer, &num_bytes,
+ MOJO_READ_DATA_FLAG_NONE);
+printf("Pipe says: %s", (const char*)buffer); // Should say "ello".
+result = MojoEndReadData(consumer, 5);
+```
+
+## Shared Buffers
+
+Shared buffers are chunks of memory which can be mapped simultaneously by
+multiple processes. Mojo provides a simple API to make these available to
+applications.
+
+See [//mojo/public/c/system/buffer.h](https://cs.chromium.org/chromium/src/mojo/public/c/system/buffer.h)
+for detailed shared buffer API documentation.
+
+### Creating Buffer Handles
+
+Usage is straightforward. You can create a new buffer:
+
+``` c
+// Allocate a shared buffer of 4 kB.
+MojoHandle buffer;
+MojoResult result = MojoCreateSharedBuffer(NULL, 4096, &buffer);
+```
+
+You can also duplicate an existing shared buffer handle:
+
+``` c
+MojoHandle another_name_for_buffer;
+MojoResult result = MojoDuplicateBufferHandle(buffer, NULL,
+ &another_name_for_buffer);
+```
+
+This is useful if you want to retain a handle to the buffer while also sharing
+handles with one or more other clients. The allocated buffer remains valid as
+long as at least one shared buffer handle exists to reference it.
+
+### Mapping Buffers
+
+You can map (and later unmap) a specified range of the buffer to get direct
+memory access to its contents:
+
+``` c
+void* data;
+MojoResult result = MojoMapBuffer(buffer, 0, 64, &data,
+ MOJO_MAP_BUFFER_FLAG_NONE);
+
+*(int*)data = 42;
+result = MojoUnmapBuffer(data);
+```
+
+A buffer may have any number of active mappings at a time, in any number of
+processes.
+
+### Read-Only Handles
+
+An option can also be specified on `MojoDuplicateBufferHandle` to ensure
+that the newly duplicated handle can only be mapped to read-only memory:
+
+``` c
+MojoHandle read_only_buffer;
+MojoDuplicateBufferHandleOptions options;
+options.struct_size = sizeof(options);
+options.flags = MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY;
+MojoResult result = MojoDuplicateBufferHandle(buffer, &options,
+ &read_only_buffer);
+
+// Attempt to map and write to the buffer using the read-only handle:
+void* data;
+result = MojoMapBuffer(read_only_buffer, 0, 64, &data,
+ MOJO_MAP_BUFFER_FLAG_NONE);
+*(int*)data = 42; // CRASH
+```
+
+*** note
+**NOTE:** One important limitation of the current implementation is that
+read-only handles can only be produced from a handle that was originally created
+by `MojoCreateSharedBuffer` (*i.e.*, you cannot create a read-only duplicate
+from a non-read-only duplicate), and the handle cannot have been transferred
+over a message pipe first.
+***
+
+## Native Platform Handles (File Descriptors, Windows Handles, *etc.*)
+
+Native platform handles to system objects can be wrapped as Mojo handles for
+seamless transit over message pipes. Mojo currently supports wrapping POSIX
+file descriptors, Windows handles, and Mach ports.
+
+See [//mojo/public/c/system/platform_handle.h](https://cs.chromium.org/chromium/src/mojo/public/c/system/platform_handle.h)
+for detailed platform handle API documentation.
+
+### Wrapping Basic Handle Types
+
+Wrapping a POSIX file descriptor is simple:
+
+``` c
+MojoPlatformHandle platform_handle;
+platform_handle.struct_size = sizeof(platform_handle);
+platform_handle.type = MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR;
+platform_handle.value = (uint64_t)fd;
+MojoHandle handle;
+MojoResult result = MojoWrapPlatformHandle(&platform_handle, &handle);
+```
+
+Note that at this point `handle` effectively owns the file descriptor
+and if you were to call `MojoClose(handle)`, the file descriptor would be closed
+too; but we're not going to close it here! We're going to pretend we've sent it
+over a message pipe, and now we want to unwrap it on the other side:
+
+``` c
+MojoPlatformHandle platform_handle;
+platform_handle.struct_size = sizeof(platform_handle);
+MojoResult result = MojoUnwrapPlatformHandle(handle, &platform_handle);
+int fd = (int)platform_handle.value;
+```
+
+The situation looks nearly identical for wrapping and unwrapping Windows handles
+and Mach ports.
+
+### Wrapping Shared Buffer Handles
+
+Unlike other handle types, shared buffers have special meaning in Mojo, and it
+may be desirable to wrap a native platform handle -- along with some extra
+metadata -- such that be treated like a real Mojo shared buffer handle.
+Conversely it can also be useful to unpack a Mojo shared buffer handle into
+a native platform handle which references the buffer object. Both of these
+things can be done using the `MojoWrapPlatformSharedBuffer` and
+`MojoUnwrapPlatformSharedBuffer` APIs.
+
+On Windows, the wrapped platform handle must always be a Windows handle to
+a file mapping object.
+
+On OS X, the wrapped platform handle must be a memory-object send right.
+
+On all other POSIX systems, the wrapped platform handle must be a file
+descriptor for a shared memory object.
+
+## Signals & Watchers
+
+Message pipe and data pipe (producer and consumer) handles can change state in
+ways that may be interesting to a Mojo API user. For example, you may wish to
+know when a message pipe handle has messages available to be read or when its
+peer has been closed. Such states are reflected by a fixed set of boolean
+signals on each pipe handle.
+
+### Signals
+
+Every message pipe and data pipe handle maintains a notion of
+**signaling state** which may be queried at any time. For example:
+
+``` c
+MojoHandle a, b;
+MojoCreateMessagePipe(NULL, &a, &b);
+
+MojoHandleSignalsState state;
+MojoResult result = MojoQueryHandleSignalsState(a, &state);
+```
+
+The `MojoHandleSignalsState` structure exposes two fields: `satisfied_signals`
+and `satisfiable_signals`. Both of these are bitmasks of the type
+`MojoHandleSignals` (see [//mojo/public/c/system/types.h](https://cs.chromium.org/chromium/src/mojo/public/c/system/types.h)
+for more details.)
+
+The `satisfied_signals` bitmask indicates signals which were satisfied on the
+handle at the time of the call, while the `satisfiable_signals` bitmask
+indicates signals which were still possible to satisfy at the time of the call.
+It is thus by definition always true that:
+
+``` c
+(satisfied_signals | satisfiable_signals) == satisfiable_signals
+```
+
+In other words a signal obviously cannot be satisfied if it is no longer
+satisfiable. Furthermore once a signal is unsatisfiable, *i.e.* is no longer
+set in `sastisfiable_signals`, it can **never** become satisfiable again.
+
+To illustrate this more clearly, consider the message pipe created above. Both
+ends of the pipe are still open and neither has been written to yet. Thus both
+handles start out with the same signaling state:
+
+| Field | State |
+|-----------------------|-------|
+| `satisfied_signals` | `MOJO_HANDLE_SIGNAL_WRITABLE`
+| `satisfiable_signals` | `MOJO_HANDLE_SIGNAL_READABLE + MOJO_HANDLE_SIGNAL_WRITABLE + MOJO_HANDLE_SIGNAL_PEER_CLOSED`
+
+Writing a message to handle `b` will eventually alter the signaling state of `a`
+such that `MOJO_HANDLE_SIGNAL_READABLE` also becomes satisfied. If we were to
+then close `b`, the signaling state of `a` would look like:
+
+| Field | State |
+|-----------------------|-------|
+| `satisfied_signals` | `MOJO_HANDLE_SIGNAL_READABLE + MOJO_HANDLE_SIGNAL_PEER_CLOSED`
+| `satisfiable_signals` | `MOJO_HANDLE_SIGNAL_READABLE + MOJO_HANDLE_SIGNAL_PEER_CLOSED`
+
+Note that even though `a`'s peer is known to be closed (hence making `a`
+permanently unwritable) it remains readable because there's still an unread
+received message waiting to be read from `a`.
+
+Finally if we read the last message from `a` its signaling state becomes:
+
+| Field | State |
+|-----------------------|-------|
+| `satisfied_signals` | `MOJO_HANDLE_SIGNAL_PEER_CLOSED`
+| `satisfiable_signals` | `MOJO_HANDLE_SIGNAL_PEER_CLOSED`
+
+and we know definitively that `a` can never be read from again.
+
+### Watching Signals
+
+The ability to query a handle's signaling state can be useful, but it's not
+sufficient to support robust and efficient pipe usage. Mojo watchers empower
+users with the ability to **watch** a handle's signaling state for interesting
+changes and automatically invoke a notification handler in response.
+
+When a watcher is created it must be bound to a function pointer matching
+the following signature, defined in
+[//mojo/public/c/system/watcher.h](https://cs.chromium.org/chromium/src/mojo/public/c/system/watcher.h):
+
+``` c
+typedef void (*MojoWatcherNotificationCallback)(
+ uintptr_t context,
+ MojoResult result,
+ MojoHandleSignalsState signals_state,
+ MojoWatcherNotificationFlags flags);
+```
+
+The `context` argument corresponds to a specific handle being watched by the
+watcher (read more below), and the remaining arguments provide details regarding
+the specific reason for the notification. It's important to be aware that a
+watcher's registered handler may be called **at any time** and
+**on any thread**.
+
+It's also helpful to understand a bit about the mechanism by which the handler
+can be invoked. Essentially, any Mojo C System API call may elicit a handle
+state change of some kind. If such a change is relevant to conditions watched by
+a watcher, and that watcher is in a state which allows it raise a corresponding
+notification, its notification handler will be invoked synchronously some time
+before the outermost System API call on the current thread's stack returns.
+
+Handle state changes can also occur as a result of incoming IPC from an external
+process. If a pipe in the current process is connected to an endpoint in another
+process and the internal Mojo system receives an incoming message bound for the
+local endpoint, the arrival of that message will trigger a state change on the
+receiving handle and may thus invoke one or more watchers' notification handlers
+as a result.
+
+The `MOJO_WATCHER_NOTIFICATION_FLAG_FROM_SYSTEM` flag on the notification
+handler's `flags` argument is used to indicate whether the handler was invoked
+due to such an internal system IPC event (if the flag is set), or if it was
+invoked synchronously due to some local API call (if the flag is unset.)
+This distinction can be useful to make in certain cases to *e.g.* avoid
+accidental reentrancy in user code.
+
+### Creating a Watcher
+
+Creating a watcher is simple:
+
+``` c
+
+void OnNotification(uintptr_t context,
+ MojoResult result,
+ MojoHandleSignalsState signals_state,
+ MojoWatcherNotificationFlags flags) {
+ // ...
+}
+
+MojoHandle w;
+MojoResult result = MojoCreateWatcher(&OnNotification, &w);
+```
+
+Like all other `MojoHandle` types, watchers may be destroyed by closing them
+with `MojoClose`. Unlike other `MojoHandle` types, watcher handles are **not**
+transferrable across message pipes.
+
+In order for a watcher to be useful, it has to watch at least one handle.
+
+### Adding a Handle to a Watcher
+
+Any given watcher can watch any given (message or data pipe) handle for some set
+of signaling conditions. A handle may be watched simultaneously by multiple
+watchers, and a single watcher can watch multiple different handles
+simultaneously.
+
+``` c
+MojoHandle a, b;
+MojoCreateMessagePipe(NULL, &a, &b);
+
+// Watch handle |a| for readability.
+const uintptr_t context = 1234;
+MojoResult result = MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, context);
+```
+
+We've successfully instructed watcher `w` to begin watching pipe handle `a` for
+readability. However, our recently created watcher is still in a **disarmed**
+state, meaning that it will never fire a notification pertaining to this watched
+signaling condition. It must be **armed** before that can happen.
+
+### Arming a Watcher
+
+In order for a watcher to invoke its notification handler in response to a
+relevant signaling state change on a watched handle, it must first be armed. A
+watcher may only be armed if none of its watched handles would elicit a
+notification immediately once armed.
+
+In this case `a` is clearly not yet readable, so arming should succeed:
+
+``` c
+MojoResult result = MojoArmWatcher(w, NULL, NULL, NULL, NULL);
+```
+
+Now we can write to `b` to make `a` readable:
+
+``` c
+MojoWriteMessage(b, NULL, 0, NULL, 0, MOJO_WRITE_MESSAGE_NONE);
+```
+
+Eventually -- and in practice possibly before `MojoWriteMessage` even
+returns -- this will cause `OnNotification` to be invoked on the calling thread
+with the `context` value (*i.e.* 1234) that was given when the handle was added
+to the watcher.
+
+The `result` parameter will be `MOJO_RESULT_OK` to indicate that the watched
+signaling condition has been *satisfied*. If the watched condition had instead
+become permanently *unsatisfiable* (*e.g.*, if `b` were instead closed), `result`
+would instead indicate `MOJO_RESULT_FAILED_PRECONDITION`.
+
+**NOTE:** Immediately before a watcher decides to invoke its notification
+handler, it automatically disarms itself to prevent another state change from
+eliciting another notification. Therefore a watcher must be repeatedly rearmed
+in order to continue dispatching signaling notifications.
+
+As noted above, arming a watcher may fail if any of the watched conditions for
+a handle are already partially satisfied or fully unsatisfiable. In that case
+the caller may provide buffers for `MojoArmWatcher` to store information about
+a subset of the relevant watches which caused it to fail:
+
+``` c
+// Provide some storage for information about watches that are already ready.
+uint32_t num_ready_contexts = 4;
+uintptr_t ready_contexts[4];
+MojoResult ready_results[4];
+struct MojoHandleSignalsStates ready_states[4];
+MojoResult result = MojoArmWatcher(w, &num_ready_contexts, ready_contexts,
+ ready_results, ready_states);
+```
+
+Because `a` is still readable this operation will fail with
+`MOJO_RESULT_FAILED_PRECONDITION`. The input value of `num_ready_contexts`
+informs `MojoArmWatcher` that it may store information regarding up to 4 watches
+which currently prevent arming. In this case of course there is only one active
+watch, so upon return we will see:
+
+* `num_ready_contexts` is `1`.
+* `ready_contexts[0]` is `1234`.
+* `ready_results[0]` is `MOJO_RESULT_OK`
+* `ready_states[0]` is the last known signaling state of handle `a`.
+
+In other words the stored information mirrors what would have been the
+notification handler's arguments if the watcher were allowed to arm and thus
+notify immediately.
+
+### Cancelling a Watch
+
+There are three ways a watch can be cancelled:
+
+* The watched handle is closed
+* The watcher handle is closed (in which case all of its watches are cancelled.)
+* `MojoCancelWatch` is explicitly called for a given `context`.
+
+In the above example this means any of the following operations will cancel the
+watch on `a`:
+
+``` c
+// Close the watched handle...
+MojoClose(a);
+
+// OR close the watcher handle...
+MojoClose(w);
+
+// OR explicitly cancel.
+MojoResult result = MojoCancelWatch(w, 1234);
+```
+
+In every case the watcher's notification handler is invoked for the cancelled
+watch(es) regardless of whether or not the watcher is or was armed at the time.
+The notification handler receives a `result` of `MOJO_RESULT_CANCELLED` for
+these notifications, and this is guaranteed to be the final notification for any
+given watch context.
+
+### Practical Watch Context Usage
+
+It is common and probably wise to treat a watch's `context` value as an opaque
+pointer to some thread-safe state associated in some way with the handle being
+watched. Here's a small example which uses a single watcher to watch both ends
+of a message pipe and accumulate a count of messages received at each end.
+
+``` c
+// NOTE: For the sake of simplicity this example code is not in fact
+// thread-safe. As long as there's only one thread running in the process and
+// no external process connections, this is fine.
+
+struct WatchedHandleState {
+ MojoHandle watcher;
+ MojoHandle handle;
+ int message_count;
+};
+
+void OnNotification(uintptr_t context,
+ MojoResult result,
+ MojoHandleSignalsState signals_state,
+ MojoWatcherNotificationFlags flags) {
+ struct WatchedHandleState* state = (struct WatchedHandleState*)(context);
+ MojoResult rv;
+
+ if (result == MOJO_RESULT_CANCELLED) {
+ // Cancellation is always the last notification and is guaranteed to
+ // eventually happen for every context, assuming no handles are leaked. We
+ // treat this as an opportunity to free the WatchedHandleState.
+ free(state);
+ return;
+ }
+
+ if (result == MOJO_RESULT_FAILED_PRECONDITION) {
+ // No longer readable, i.e. the other handle must have been closed. Better
+ // cancel. Note that we could also just call MojoClose(state->watcher) here
+ // since we know |context| is its only registered watch.
+ MojoCancelWatch(state->watcher, context);
+ return;
+ }
+
+ // This is the only handle watched by the watcher, so as long as we can't arm
+ // the watcher we know something's up with this handle. Try to read messages
+ // until we can successfully arm again or something goes terribly wrong.
+ while (MojoArmWatcher(state->watcher, NULL, NULL, NULL, NULL) ==
+ MOJO_RESULT_FAILED_PRECONDITION) {
+ rv = MojoReadMessageNew(state->handle, NULL, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_MAY_DISCARD);
+ if (rv == MOJO_RESULT_OK) {
+ state->message_count++;
+ } else if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
+ MojoCancelWatch(state->watcher, context);
+ return;
+ }
+ }
+}
+
+MojoHandle a, b;
+MojoCreateMessagePipe(NULL, &a, &b);
+
+MojoHandle a_watcher, b_watcher;
+MojoCreateWatcher(&OnNotification, &a_watcher);
+MojoCreateWatcher(&OnNotification, &b_watcher)
+
+struct WatchedHandleState* a_state = malloc(sizeof(struct WatchedHandleState));
+a_state->watcher = a_watcher;
+a_state->handle = a;
+a_state->message_count = 0;
+
+struct WatchedHandleState* b_state = malloc(sizeof(struct WatchedHandleState));
+b_state->watcher = b_watcher;
+b_state->handle = b;
+b_state->message_count = 0;
+
+MojoWatch(a_watcher, a, MOJO_HANDLE_SIGNAL_READABLE, (uintptr_t)a_state);
+MojoWatch(b_watcher, b, MOJO_HANDLE_SIGNAL_READABLE, (uintptr_t)b_state);
+
+MojoArmWatcher(a_watcher, NULL, NULL, NULL, NULL);
+MojoArmWatcher(b_watcher, NULL, NULL, NULL, NULL);
+```
+
+Now any writes to `a` will increment `message_count` in `b_state`, and any
+writes to `b` will increment `message_count` in `a_state`.
+
+If either `a` or `b` is closed, both watches will be cancelled - one because
+watch cancellation is implicit in handle closure, and the other because its
+watcher will eventually detect that the handle is no longer readable.
« ipc/README.md ('K') | « mojo/public/c/README.md ('k') | mojo/public/c/system/buffer.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698