summaryrefslogtreecommitdiff
path: root/mojo/public/cpp/system
diff options
context:
space:
mode:
Diffstat (limited to 'mojo/public/cpp/system')
-rw-r--r--mojo/public/cpp/system/BUILD.gn57
-rw-r--r--mojo/public/cpp/system/README.md396
-rw-r--r--mojo/public/cpp/system/buffer.cc46
-rw-r--r--mojo/public/cpp/system/buffer.h82
-rw-r--r--mojo/public/cpp/system/core.h14
-rw-r--r--mojo/public/cpp/system/data_pipe.h163
-rw-r--r--mojo/public/cpp/system/functions.h26
-rw-r--r--mojo/public/cpp/system/handle.h220
-rw-r--r--mojo/public/cpp/system/handle_signals_state.h83
-rw-r--r--mojo/public/cpp/system/message.cc13
-rw-r--r--mojo/public/cpp/system/message.h83
-rw-r--r--mojo/public/cpp/system/message_pipe.h158
-rw-r--r--mojo/public/cpp/system/platform_handle.cc178
-rw-r--r--mojo/public/cpp/system/platform_handle.h92
-rw-r--r--mojo/public/cpp/system/simple_watcher.cc279
-rw-r--r--mojo/public/cpp/system/simple_watcher.h215
-rw-r--r--mojo/public/cpp/system/system_export.h34
-rw-r--r--mojo/public/cpp/system/tests/BUILD.gn23
-rw-r--r--mojo/public/cpp/system/tests/core_unittest.cc510
-rw-r--r--mojo/public/cpp/system/tests/handle_signals_state_unittest.cc42
-rw-r--r--mojo/public/cpp/system/tests/simple_watcher_unittest.cc277
-rw-r--r--mojo/public/cpp/system/tests/wait_set_unittest.cc376
-rw-r--r--mojo/public/cpp/system/tests/wait_unittest.cc321
-rw-r--r--mojo/public/cpp/system/wait.cc200
-rw-r--r--mojo/public/cpp/system/wait.h75
-rw-r--r--mojo/public/cpp/system/wait_set.cc371
-rw-r--r--mojo/public/cpp/system/wait_set.h124
-rw-r--r--mojo/public/cpp/system/watcher.cc20
-rw-r--r--mojo/public/cpp/system/watcher.h37
29 files changed, 4515 insertions, 0 deletions
diff --git a/mojo/public/cpp/system/BUILD.gn b/mojo/public/cpp/system/BUILD.gn
new file mode 100644
index 0000000000..35087ef6f1
--- /dev/null
+++ b/mojo/public/cpp/system/BUILD.gn
@@ -0,0 +1,57 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Deletes libsystem.dylib from the build dir, since it shadows
+# /usr/lib/libSystem.dylib on macOS.
+# TODO(thakis): Remove this after a while.
+action("clean_up_old_dylib") {
+ script = "//build/rm.py"
+ stamp = "$target_gen_dir/clean_up_stamp"
+ outputs = [
+ stamp,
+ ]
+ args = [
+ "--stamp",
+ rebase_path(stamp, root_build_dir),
+ "-f",
+ "libsystem.dylib",
+ ]
+}
+
+component("system") {
+ output_name = "mojo_public_system_cpp"
+
+ sources = [
+ "buffer.cc",
+ "buffer.h",
+ "core.h",
+ "data_pipe.h",
+ "functions.h",
+ "handle.h",
+ "handle_signals_state.h",
+ "message.h",
+ "message_pipe.h",
+ "platform_handle.cc",
+ "platform_handle.h",
+ "simple_watcher.cc",
+ "simple_watcher.h",
+ "system_export.h",
+ "wait.cc",
+ "wait.h",
+ "wait_set.cc",
+ "wait_set.h",
+ "watcher.cc",
+ "watcher.h",
+ ]
+
+ public_deps = [
+ "//base",
+ "//mojo/public/c/system",
+ ]
+ deps = [
+ ":clean_up_old_dylib",
+ ]
+
+ defines = [ "MOJO_CPP_SYSTEM_IMPLEMENTATION" ]
+}
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.
+}
+```
diff --git a/mojo/public/cpp/system/buffer.cc b/mojo/public/cpp/system/buffer.cc
new file mode 100644
index 0000000000..49f45d8498
--- /dev/null
+++ b/mojo/public/cpp/system/buffer.cc
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/system/buffer.h"
+
+namespace mojo {
+
+// static
+ScopedSharedBufferHandle SharedBufferHandle::Create(uint64_t num_bytes) {
+ MojoCreateSharedBufferOptions options = {
+ sizeof(options), MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE};
+ SharedBufferHandle handle;
+ MojoCreateSharedBuffer(&options, num_bytes, handle.mutable_value());
+ return MakeScopedHandle(handle);
+}
+
+ScopedSharedBufferHandle SharedBufferHandle::Clone(
+ SharedBufferHandle::AccessMode access_mode) const {
+ ScopedSharedBufferHandle result;
+ if (!is_valid())
+ return result;
+
+ MojoDuplicateBufferHandleOptions options = {
+ sizeof(options), MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE};
+ if (access_mode == AccessMode::READ_ONLY)
+ options.flags |= MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY;
+ SharedBufferHandle result_handle;
+ MojoDuplicateBufferHandle(value(), &options, result_handle.mutable_value());
+ result.reset(result_handle);
+ return result;
+}
+
+ScopedSharedBufferMapping SharedBufferHandle::Map(uint64_t size) const {
+ return MapAtOffset(size, 0);
+}
+
+ScopedSharedBufferMapping SharedBufferHandle::MapAtOffset(
+ uint64_t size,
+ uint64_t offset) const {
+ void* buffer = nullptr;
+ MojoMapBuffer(value(), offset, size, &buffer, MOJO_MAP_BUFFER_FLAG_NONE);
+ return ScopedSharedBufferMapping(buffer);
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/system/buffer.h b/mojo/public/cpp/system/buffer.h
new file mode 100644
index 0000000000..1ae923cb75
--- /dev/null
+++ b/mojo/public/cpp/system/buffer.h
@@ -0,0 +1,82 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file provides a C++ wrapping around the Mojo C API for shared buffers,
+// replacing the prefix of "Mojo" with a "mojo" namespace, and using more
+// strongly-typed representations of |MojoHandle|s.
+//
+// Please see "mojo/public/c/system/buffer.h" for complete documentation of the
+// API.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_BUFFER_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_BUFFER_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "mojo/public/c/system/buffer.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/system_export.h"
+
+namespace mojo {
+namespace internal {
+
+struct Unmapper {
+ void operator()(void* buffer) {
+ MojoResult result = MojoUnmapBuffer(buffer);
+ DCHECK_EQ(MOJO_RESULT_OK, result);
+ }
+};
+
+} // namespace internal
+
+using ScopedSharedBufferMapping = std::unique_ptr<void, internal::Unmapper>;
+
+class SharedBufferHandle;
+
+typedef ScopedHandleBase<SharedBufferHandle> ScopedSharedBufferHandle;
+
+// A strongly-typed representation of a |MojoHandle| referring to a shared
+// buffer.
+class MOJO_CPP_SYSTEM_EXPORT SharedBufferHandle
+ : NON_EXPORTED_BASE(public Handle) {
+ public:
+ enum class AccessMode {
+ READ_WRITE,
+ READ_ONLY,
+ };
+
+ SharedBufferHandle() {}
+ explicit SharedBufferHandle(MojoHandle value) : Handle(value) {}
+
+ // Copying and assignment allowed.
+
+ // Creates a new SharedBufferHandle. Returns an invalid handle on failure.
+ static ScopedSharedBufferHandle Create(uint64_t num_bytes);
+
+ // Clones this shared buffer handle. If |access_mode| is READ_ONLY or this is
+ // a read-only handle, the new handle will be read-only. On failure, this will
+ // return an empty result.
+ ScopedSharedBufferHandle Clone(AccessMode = AccessMode::READ_WRITE) const;
+
+ // Maps |size| bytes of this shared buffer. On failure, this will return a
+ // null mapping.
+ ScopedSharedBufferMapping Map(uint64_t size) const;
+
+ // Maps |size| bytes of this shared buffer, starting |offset| bytes into the
+ // buffer. On failure, this will return a null mapping.
+ ScopedSharedBufferMapping MapAtOffset(uint64_t size, uint64_t offset) const;
+};
+
+static_assert(sizeof(SharedBufferHandle) == sizeof(Handle),
+ "Bad size for C++ SharedBufferHandle");
+static_assert(sizeof(ScopedSharedBufferHandle) == sizeof(SharedBufferHandle),
+ "Bad size for C++ ScopedSharedBufferHandle");
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_BUFFER_H_
diff --git a/mojo/public/cpp/system/core.h b/mojo/public/cpp/system/core.h
new file mode 100644
index 0000000000..f1d18d977f
--- /dev/null
+++ b/mojo/public/cpp/system/core.h
@@ -0,0 +1,14 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_CORE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_CORE_H_
+
+#include "mojo/public/cpp/system/buffer.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "mojo/public/cpp/system/functions.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_CORE_H_
diff --git a/mojo/public/cpp/system/data_pipe.h b/mojo/public/cpp/system/data_pipe.h
new file mode 100644
index 0000000000..0dbc3c74e5
--- /dev/null
+++ b/mojo/public/cpp/system/data_pipe.h
@@ -0,0 +1,163 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file provides a C++ wrapping around the Mojo C API for data pipes,
+// replacing the prefix of "Mojo" with a "mojo" namespace, and using more
+// strongly-typed representations of |MojoHandle|s.
+//
+// Please see "mojo/public/c/system/data_pipe.h" for complete documentation of
+// the API.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_H_
+
+#include <stdint.h>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/cpp/system/handle.h"
+
+namespace mojo {
+
+// A strongly-typed representation of a |MojoHandle| to the producer end of a
+// data pipe.
+class DataPipeProducerHandle : public Handle {
+ public:
+ DataPipeProducerHandle() {}
+ explicit DataPipeProducerHandle(MojoHandle value) : Handle(value) {}
+
+ // Copying and assignment allowed.
+};
+
+static_assert(sizeof(DataPipeProducerHandle) == sizeof(Handle),
+ "Bad size for C++ DataPipeProducerHandle");
+
+typedef ScopedHandleBase<DataPipeProducerHandle> ScopedDataPipeProducerHandle;
+static_assert(sizeof(ScopedDataPipeProducerHandle) ==
+ sizeof(DataPipeProducerHandle),
+ "Bad size for C++ ScopedDataPipeProducerHandle");
+
+// A strongly-typed representation of a |MojoHandle| to the consumer end of a
+// data pipe.
+class DataPipeConsumerHandle : public Handle {
+ public:
+ DataPipeConsumerHandle() {}
+ explicit DataPipeConsumerHandle(MojoHandle value) : Handle(value) {}
+
+ // Copying and assignment allowed.
+};
+
+static_assert(sizeof(DataPipeConsumerHandle) == sizeof(Handle),
+ "Bad size for C++ DataPipeConsumerHandle");
+
+typedef ScopedHandleBase<DataPipeConsumerHandle> ScopedDataPipeConsumerHandle;
+static_assert(sizeof(ScopedDataPipeConsumerHandle) ==
+ sizeof(DataPipeConsumerHandle),
+ "Bad size for C++ ScopedDataPipeConsumerHandle");
+
+// Creates a new data pipe. See |MojoCreateDataPipe()| for complete
+// documentation.
+inline MojoResult CreateDataPipe(
+ const MojoCreateDataPipeOptions* options,
+ ScopedDataPipeProducerHandle* data_pipe_producer,
+ ScopedDataPipeConsumerHandle* data_pipe_consumer) {
+ DCHECK(data_pipe_producer);
+ DCHECK(data_pipe_consumer);
+ DataPipeProducerHandle producer_handle;
+ DataPipeConsumerHandle consumer_handle;
+ MojoResult rv = MojoCreateDataPipe(options,
+ producer_handle.mutable_value(),
+ consumer_handle.mutable_value());
+ // Reset even on failure (reduces the chances that a "stale"/incorrect handle
+ // will be used).
+ data_pipe_producer->reset(producer_handle);
+ data_pipe_consumer->reset(consumer_handle);
+ return rv;
+}
+
+// Writes to a data pipe. See |MojoWriteData| for complete documentation.
+inline MojoResult WriteDataRaw(DataPipeProducerHandle data_pipe_producer,
+ const void* elements,
+ uint32_t* num_bytes,
+ MojoWriteDataFlags flags) {
+ return MojoWriteData(data_pipe_producer.value(), elements, num_bytes, flags);
+}
+
+// Begins a two-phase write to a data pipe. See |MojoBeginWriteData()| for
+// complete documentation.
+inline MojoResult BeginWriteDataRaw(DataPipeProducerHandle data_pipe_producer,
+ void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoWriteDataFlags flags) {
+ return MojoBeginWriteData(
+ data_pipe_producer.value(), buffer, buffer_num_bytes, flags);
+}
+
+// Completes a two-phase write to a data pipe. See |MojoEndWriteData()| for
+// complete documentation.
+inline MojoResult EndWriteDataRaw(DataPipeProducerHandle data_pipe_producer,
+ uint32_t num_bytes_written) {
+ return MojoEndWriteData(data_pipe_producer.value(), num_bytes_written);
+}
+
+// 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 from a data pipe. See |MojoBeginReadData()| for
+// complete documentation.
+inline MojoResult BeginReadDataRaw(DataPipeConsumerHandle data_pipe_consumer,
+ const void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoReadDataFlags flags) {
+ return MojoBeginReadData(
+ data_pipe_consumer.value(), buffer, buffer_num_bytes, flags);
+}
+
+// Completes a two-phase read from a data pipe. See |MojoEndReadData()| for
+// complete documentation.
+inline MojoResult EndReadDataRaw(DataPipeConsumerHandle data_pipe_consumer,
+ uint32_t num_bytes_read) {
+ return MojoEndReadData(data_pipe_consumer.value(), num_bytes_read);
+}
+
+// A wrapper class that automatically creates a data pipe and owns both handles.
+// TODO(vtl): Make an even more friendly version? (Maybe templatized for a
+// particular type instead of some "element"? Maybe functions that take
+// vectors?)
+class DataPipe {
+ public:
+ DataPipe();
+ explicit DataPipe(const MojoCreateDataPipeOptions& options);
+ ~DataPipe();
+
+ ScopedDataPipeProducerHandle producer_handle;
+ ScopedDataPipeConsumerHandle consumer_handle;
+};
+
+inline DataPipe::DataPipe() {
+ MojoResult result =
+ CreateDataPipe(nullptr, &producer_handle, &consumer_handle);
+ ALLOW_UNUSED_LOCAL(result);
+ DCHECK_EQ(MOJO_RESULT_OK, result);
+}
+
+inline DataPipe::DataPipe(const MojoCreateDataPipeOptions& options) {
+ MojoResult result =
+ CreateDataPipe(&options, &producer_handle, &consumer_handle);
+ ALLOW_UNUSED_LOCAL(result);
+ DCHECK_EQ(MOJO_RESULT_OK, result);
+}
+
+inline DataPipe::~DataPipe() {
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_H_
diff --git a/mojo/public/cpp/system/functions.h b/mojo/public/cpp/system/functions.h
new file mode 100644
index 0000000000..31edf57ab5
--- /dev/null
+++ b/mojo/public/cpp/system/functions.h
@@ -0,0 +1,26 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file provides a C++ wrapping around the standalone functions of the Mojo
+// C API, replacing the prefix of "Mojo" with a "mojo" namespace.
+//
+// Please see "mojo/public/c/system/functions.h" for complete documentation of
+// the API.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_FUNCTIONS_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_FUNCTIONS_H_
+
+#include "mojo/public/c/system/functions.h"
+
+namespace mojo {
+
+// Returns the current |MojoTimeTicks| value. See |MojoGetTimeTicksNow()| for
+// complete documentation.
+inline MojoTimeTicks GetTimeTicksNow() {
+ return MojoGetTimeTicksNow();
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_FUNCTIONS_H_
diff --git a/mojo/public/cpp/system/handle.h b/mojo/public/cpp/system/handle.h
new file mode 100644
index 0000000000..781944eb76
--- /dev/null
+++ b/mojo/public/cpp/system/handle.h
@@ -0,0 +1,220 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_HANDLE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_HANDLE_H_
+
+#include <stdint.h>
+#include <limits>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "mojo/public/c/system/functions.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/handle_signals_state.h"
+
+namespace mojo {
+
+// OVERVIEW
+//
+// |Handle| and |...Handle|:
+//
+// |Handle| is a simple, copyable wrapper for the C type |MojoHandle| (which is
+// just an integer). Its purpose is to increase type-safety, not provide
+// lifetime management. For the same purpose, we have trivial *subclasses* of
+// |Handle|, e.g., |MessagePipeHandle| and |DataPipeProducerHandle|. |Handle|
+// and its subclasses impose *no* extra overhead over using |MojoHandle|s
+// directly.
+//
+// Note that though we provide constructors for |Handle|/|...Handle| from a
+// |MojoHandle|, we do not provide, e.g., a constructor for |MessagePipeHandle|
+// from a |Handle|. This is for type safety: If we did, you'd then be able to
+// construct a |MessagePipeHandle| from, e.g., a |DataPipeProducerHandle| (since
+// it's a |Handle|).
+//
+// |ScopedHandleBase| and |Scoped...Handle|:
+//
+// |ScopedHandleBase<HandleType>| is a templated scoped wrapper, for the handle
+// types above (in the same sense that a C++11 |unique_ptr<T>| is a scoped
+// wrapper for a |T*|). It provides lifetime management, closing its owned
+// handle on destruction. It also provides (emulated) move semantics, again
+// along the lines of C++11's |unique_ptr| (and exactly like Chromium's
+// |scoped_ptr|).
+//
+// |ScopedHandle| is just (a typedef of) a |ScopedHandleBase<Handle>|.
+// Similarly, |ScopedMessagePipeHandle| is just a
+// |ScopedHandleBase<MessagePipeHandle>|. Etc. Note that a
+// |ScopedMessagePipeHandle| is *not* a (subclass of) |ScopedHandle|.
+//
+// Wrapper functions:
+//
+// We provide simple wrappers for the |Mojo...()| functions (in
+// mojo/public/c/system/core.h -- see that file for details on individual
+// functions).
+//
+// The general guideline is functions that imply ownership transfer of a handle
+// should take (or produce) an appropriate |Scoped...Handle|, while those that
+// don't take a |...Handle|. For example, |CreateMessagePipe()| has two
+// |ScopedMessagePipe| "out" parameters, whereas |Wait()| and |WaitMany()| take
+// |Handle| parameters. Some, have both: e.g., |DuplicatedBuffer()| takes a
+// suitable (unscoped) handle (e.g., |SharedBufferHandle|) "in" parameter and
+// produces a suitable scoped handle (e.g., |ScopedSharedBufferHandle| a.k.a.
+// |ScopedHandleBase<SharedBufferHandle>|) as an "out" parameter.
+//
+// An exception are some of the |...Raw()| functions. E.g., |CloseRaw()| takes a
+// |Handle|, leaving the user to discard the wrapper.
+//
+// ScopedHandleBase ------------------------------------------------------------
+
+// Scoper for the actual handle types defined further below. It's move-only,
+// like the C++11 |unique_ptr|.
+template <class HandleType>
+class ScopedHandleBase {
+ public:
+ using RawHandleType = HandleType;
+
+ ScopedHandleBase() {}
+ explicit ScopedHandleBase(HandleType handle) : handle_(handle) {}
+ ~ScopedHandleBase() { CloseIfNecessary(); }
+
+ template <class CompatibleHandleType>
+ explicit ScopedHandleBase(ScopedHandleBase<CompatibleHandleType> other)
+ : handle_(other.release()) {}
+
+ // Move-only constructor and operator=.
+ ScopedHandleBase(ScopedHandleBase&& other) : handle_(other.release()) {}
+ ScopedHandleBase& operator=(ScopedHandleBase&& other) {
+ if (&other != this) {
+ CloseIfNecessary();
+ handle_ = other.release();
+ }
+ return *this;
+ }
+
+ const HandleType& get() const { return handle_; }
+ const HandleType* operator->() const { return &handle_; }
+
+ template <typename PassedHandleType>
+ static ScopedHandleBase<HandleType> From(
+ ScopedHandleBase<PassedHandleType> other) {
+ static_assert(
+ sizeof(static_cast<PassedHandleType*>(static_cast<HandleType*>(0))),
+ "HandleType is not a subtype of PassedHandleType");
+ return ScopedHandleBase<HandleType>(
+ static_cast<HandleType>(other.release().value()));
+ }
+
+ void swap(ScopedHandleBase& other) { handle_.swap(other.handle_); }
+
+ HandleType release() WARN_UNUSED_RESULT {
+ HandleType rv;
+ rv.swap(handle_);
+ return rv;
+ }
+
+ void reset(HandleType handle = HandleType()) {
+ CloseIfNecessary();
+ handle_ = handle;
+ }
+
+ bool is_valid() const { return handle_.is_valid(); }
+
+ bool operator==(const ScopedHandleBase& other) const {
+ return handle_.value() == other.get().value();
+ }
+
+ private:
+ void CloseIfNecessary() {
+ if (handle_.is_valid())
+ handle_.Close();
+ }
+
+ HandleType handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedHandleBase);
+};
+
+template <typename HandleType>
+inline ScopedHandleBase<HandleType> MakeScopedHandle(HandleType handle) {
+ return ScopedHandleBase<HandleType>(handle);
+}
+
+// Handle ----------------------------------------------------------------------
+
+const MojoHandle kInvalidHandleValue = MOJO_HANDLE_INVALID;
+
+// Wrapper base class for |MojoHandle|.
+class Handle {
+ public:
+ Handle() : value_(kInvalidHandleValue) {}
+ explicit Handle(MojoHandle value) : value_(value) {}
+ ~Handle() {}
+
+ void swap(Handle& other) {
+ MojoHandle temp = value_;
+ value_ = other.value_;
+ other.value_ = temp;
+ }
+
+ bool is_valid() const { return value_ != kInvalidHandleValue; }
+
+ const MojoHandle& value() const { return value_; }
+ MojoHandle* mutable_value() { return &value_; }
+ void set_value(MojoHandle value) { value_ = value; }
+
+ void Close() {
+ DCHECK(is_valid());
+ MojoResult result = MojoClose(value_);
+ ALLOW_UNUSED_LOCAL(result);
+ DCHECK_EQ(MOJO_RESULT_OK, result);
+ }
+
+ HandleSignalsState QuerySignalsState() const {
+ HandleSignalsState signals_state;
+ MojoResult result = MojoQueryHandleSignalsState(
+ value_, static_cast<MojoHandleSignalsState*>(&signals_state));
+ DCHECK_EQ(MOJO_RESULT_OK, result);
+ return signals_state;
+ }
+
+ private:
+ MojoHandle value_;
+
+ // Copying and assignment allowed.
+};
+
+// Should have zero overhead.
+static_assert(sizeof(Handle) == sizeof(MojoHandle), "Bad size for C++ Handle");
+
+// The scoper should also impose no more overhead.
+typedef ScopedHandleBase<Handle> ScopedHandle;
+static_assert(sizeof(ScopedHandle) == sizeof(Handle),
+ "Bad size for C++ ScopedHandle");
+
+// |Close()| takes ownership of the handle, since it'll invalidate it.
+// Note: There's nothing to do, since the argument will be destroyed when it
+// goes out of scope.
+template <class HandleType>
+inline void Close(ScopedHandleBase<HandleType> /*handle*/) {
+}
+
+// Most users should typically use |Close()| (above) instead.
+inline MojoResult CloseRaw(Handle handle) {
+ return MojoClose(handle.value());
+}
+
+// Strict weak ordering, so that |Handle|s can be used as keys in |std::map|s,
+inline bool operator<(const Handle a, const Handle b) {
+ return a.value() < b.value();
+}
+
+// Comparison, so that |Handle|s can be used as keys in hash maps.
+inline bool operator==(const Handle a, const Handle b) {
+ return a.value() == b.value();
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_HANDLE_H_
diff --git a/mojo/public/cpp/system/handle_signals_state.h b/mojo/public/cpp/system/handle_signals_state.h
new file mode 100644
index 0000000000..9e2430f15a
--- /dev/null
+++ b/mojo/public/cpp/system/handle_signals_state.h
@@ -0,0 +1,83 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_HANDLE_SIGNALS_STATE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_HANDLE_SIGNALS_STATE_H_
+
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/system_export.h"
+
+namespace mojo {
+
+// A convenience wrapper around the MojoHandleSignalsState struct.
+struct MOJO_CPP_SYSTEM_EXPORT HandleSignalsState final
+ : public MojoHandleSignalsState {
+ HandleSignalsState() {
+ satisfied_signals = MOJO_HANDLE_SIGNAL_NONE;
+ satisfiable_signals = MOJO_HANDLE_SIGNAL_NONE;
+ }
+
+ HandleSignalsState(MojoHandleSignals satisfied,
+ MojoHandleSignals satisfiable) {
+ satisfied_signals = satisfied;
+ satisfiable_signals = satisfiable;
+ }
+
+ bool operator==(const HandleSignalsState& other) const {
+ return satisfied_signals == other.satisfied_signals &&
+ satisfiable_signals == other.satisfiable_signals;
+ }
+
+ // TODO(rockot): Remove this in favor of operator==.
+ bool equals(const HandleSignalsState& other) const {
+ return satisfied_signals == other.satisfied_signals &&
+ satisfiable_signals == other.satisfiable_signals;
+ }
+
+ bool satisfies(MojoHandleSignals signals) const {
+ return !!(satisfied_signals & signals);
+ }
+
+ bool can_satisfy(MojoHandleSignals signals) const {
+ return !!(satisfiable_signals & signals);
+ }
+
+ // The handle is currently readable. May apply to a message pipe handle or
+ // data pipe consumer handle.
+ bool readable() const { return satisfies(MOJO_HANDLE_SIGNAL_READABLE); }
+
+ // The handle is currently writable. May apply to a message pipe handle or
+ // data pipe producer handle.
+ bool writable() const { return satisfies(MOJO_HANDLE_SIGNAL_WRITABLE); }
+
+ // The handle's peer is closed. May apply to any message pipe or data pipe
+ // handle.
+ bool peer_closed() const { return satisfies(MOJO_HANDLE_SIGNAL_PEER_CLOSED); }
+
+ // The handle will never be |readable()| again.
+ bool never_readable() const {
+ return !can_satisfy(MOJO_HANDLE_SIGNAL_READABLE);
+ }
+
+ // The handle will never be |writable()| again.
+ bool never_writable() const {
+ return !can_satisfy(MOJO_HANDLE_SIGNAL_WRITABLE);
+ }
+
+ // The handle can never indicate |peer_closed()|. Never true for message pipe
+ // or data pipe handles (they can always signal peer closure), but always true
+ // for other types of handles (they have no peer.)
+ bool never_peer_closed() const {
+ return !can_satisfy(MOJO_HANDLE_SIGNAL_PEER_CLOSED);
+ }
+
+ // (Copy and assignment allowed.)
+};
+
+static_assert(sizeof(HandleSignalsState) == sizeof(MojoHandleSignalsState),
+ "HandleSignalsState should add no overhead");
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_HANDLE_SIGNALS_STATE_H_
diff --git a/mojo/public/cpp/system/message.cc b/mojo/public/cpp/system/message.cc
new file mode 100644
index 0000000000..09d8d46e6d
--- /dev/null
+++ b/mojo/public/cpp/system/message.cc
@@ -0,0 +1,13 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/system/message.h"
+
+namespace mojo {
+
+ScopedMessageHandle::~ScopedMessageHandle() {
+
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/system/message.h b/mojo/public/cpp/system/message.h
new file mode 100644
index 0000000000..d4406ee808
--- /dev/null
+++ b/mojo/public/cpp/system/message.h
@@ -0,0 +1,83 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_H_
+
+#include <limits>
+
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/cpp/system/handle.h"
+
+namespace mojo {
+
+const MojoMessageHandle kInvalidMessageHandleValue =
+ MOJO_MESSAGE_HANDLE_INVALID;
+
+// Handle wrapper base class for a |MojoMessageHandle|.
+class MessageHandle {
+ public:
+ MessageHandle() : value_(kInvalidMessageHandleValue) {}
+ explicit MessageHandle(MojoMessageHandle value) : value_(value) {}
+ ~MessageHandle() {}
+
+ void swap(MessageHandle& other) {
+ MojoMessageHandle temp = value_;
+ value_ = other.value_;
+ other.value_ = temp;
+ }
+
+ bool is_valid() const { return value_ != kInvalidMessageHandleValue; }
+
+ const MojoMessageHandle& value() const { return value_; }
+ MojoMessageHandle* mutable_value() { return &value_; }
+ void set_value(MojoMessageHandle value) { value_ = value; }
+
+ void Close() {
+ DCHECK(is_valid());
+ MojoResult result = MojoFreeMessage(value_);
+ ALLOW_UNUSED_LOCAL(result);
+ DCHECK_EQ(MOJO_RESULT_OK, result);
+ }
+
+ private:
+ MojoMessageHandle value_;
+};
+
+using ScopedMessageHandle = ScopedHandleBase<MessageHandle>;
+
+inline MojoResult AllocMessage(size_t num_bytes,
+ const MojoHandle* handles,
+ size_t num_handles,
+ MojoAllocMessageFlags flags,
+ ScopedMessageHandle* handle) {
+ DCHECK_LE(num_bytes, std::numeric_limits<uint32_t>::max());
+ DCHECK_LE(num_handles, std::numeric_limits<uint32_t>::max());
+ MojoMessageHandle raw_handle;
+ MojoResult rv = MojoAllocMessage(static_cast<uint32_t>(num_bytes), handles,
+ static_cast<uint32_t>(num_handles), flags,
+ &raw_handle);
+ if (rv != MOJO_RESULT_OK)
+ return rv;
+
+ handle->reset(MessageHandle(raw_handle));
+ return MOJO_RESULT_OK;
+}
+
+inline MojoResult GetMessageBuffer(MessageHandle message, void** buffer) {
+ DCHECK(message.is_valid());
+ return MojoGetMessageBuffer(message.value(), buffer);
+}
+
+inline MojoResult NotifyBadMessage(MessageHandle message,
+ const base::StringPiece& error) {
+ DCHECK(message.is_valid());
+ return MojoNotifyBadMessage(message.value(), error.data(), error.size());
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_H_
diff --git a/mojo/public/cpp/system/message_pipe.h b/mojo/public/cpp/system/message_pipe.h
new file mode 100644
index 0000000000..7fbe43f7f4
--- /dev/null
+++ b/mojo/public/cpp/system/message_pipe.h
@@ -0,0 +1,158 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file provides a C++ wrapping around the Mojo C API for message pipes,
+// replacing the prefix of "Mojo" with a "mojo" namespace, and using more
+// strongly-typed representations of |MojoHandle|s.
+//
+// Please see "mojo/public/c/system/message_pipe.h" for complete documentation
+// of the API.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_PIPE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_PIPE_H_
+
+#include <stdint.h>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/message.h"
+
+namespace mojo {
+
+// A strongly-typed representation of a |MojoHandle| to one end of a message
+// pipe.
+class MessagePipeHandle : public Handle {
+ public:
+ MessagePipeHandle() {}
+ explicit MessagePipeHandle(MojoHandle value) : Handle(value) {}
+
+ // Copying and assignment allowed.
+};
+
+static_assert(sizeof(MessagePipeHandle) == sizeof(Handle),
+ "Bad size for C++ MessagePipeHandle");
+
+typedef ScopedHandleBase<MessagePipeHandle> ScopedMessagePipeHandle;
+static_assert(sizeof(ScopedMessagePipeHandle) == sizeof(MessagePipeHandle),
+ "Bad size for C++ ScopedMessagePipeHandle");
+
+// Creates a message pipe. See |MojoCreateMessagePipe()| for complete
+// documentation.
+inline MojoResult CreateMessagePipe(const MojoCreateMessagePipeOptions* options,
+ ScopedMessagePipeHandle* message_pipe0,
+ ScopedMessagePipeHandle* message_pipe1) {
+ DCHECK(message_pipe0);
+ DCHECK(message_pipe1);
+ MessagePipeHandle handle0;
+ MessagePipeHandle handle1;
+ MojoResult rv = MojoCreateMessagePipe(
+ options, handle0.mutable_value(), handle1.mutable_value());
+ // Reset even on failure (reduces the chances that a "stale"/incorrect handle
+ // will be used).
+ message_pipe0->reset(handle0);
+ message_pipe1->reset(handle1);
+ return rv;
+}
+
+// The following "...Raw" versions fully expose the underlying API, and don't
+// help with ownership of handles (especially when writing messages). It is
+// expected that in most cases these methods will be called through generated
+// bindings anyway.
+// TODO(vtl): Write friendlier versions of these functions (using scoped
+// handles and/or vectors) if there is a demonstrated need for them.
+
+// Writes to a message pipe. If handles are attached, on success the handles
+// will no longer be valid (the receiver will receive equivalent, but logically
+// different, handles). See |MojoWriteMessage()| for complete documentation.
+inline MojoResult WriteMessageRaw(MessagePipeHandle message_pipe,
+ const void* bytes,
+ uint32_t num_bytes,
+ const MojoHandle* handles,
+ uint32_t num_handles,
+ MojoWriteMessageFlags flags) {
+ return MojoWriteMessage(
+ message_pipe.value(), bytes, num_bytes, handles, num_handles, flags);
+}
+
+// Reads from a message pipe. See |MojoReadMessage()| for complete
+// documentation.
+inline MojoResult ReadMessageRaw(MessagePipeHandle message_pipe,
+ void* bytes,
+ uint32_t* num_bytes,
+ MojoHandle* handles,
+ uint32_t* num_handles,
+ MojoReadMessageFlags flags) {
+ return MojoReadMessage(
+ message_pipe.value(), bytes, num_bytes, handles, num_handles, flags);
+}
+
+// Writes to a message pipe. Takes ownership of |message| and any attached
+// handles.
+inline MojoResult WriteMessageNew(MessagePipeHandle message_pipe,
+ ScopedMessageHandle message,
+ MojoWriteMessageFlags flags) {
+ return MojoWriteMessageNew(
+ message_pipe.value(), message.release().value(), flags);
+}
+
+// Reads from a message pipe. See |MojoReadMessageNew()| for complete
+// documentation.
+inline MojoResult ReadMessageNew(MessagePipeHandle message_pipe,
+ ScopedMessageHandle* message,
+ uint32_t* num_bytes,
+ MojoHandle* handles,
+ uint32_t* num_handles,
+ MojoReadMessageFlags flags) {
+ MojoMessageHandle raw_message;
+ MojoResult rv = MojoReadMessageNew(message_pipe.value(), &raw_message,
+ num_bytes, handles, num_handles, flags);
+ if (rv != MOJO_RESULT_OK)
+ return rv;
+
+ message->reset(MessageHandle(raw_message));
+ return MOJO_RESULT_OK;
+}
+
+// Fuses two message pipes together at the given handles. See
+// |MojoFuseMessagePipes()| for complete documentation.
+inline MojoResult FuseMessagePipes(ScopedMessagePipeHandle message_pipe0,
+ ScopedMessagePipeHandle message_pipe1) {
+ return MojoFuseMessagePipes(message_pipe0.release().value(),
+ message_pipe1.release().value());
+}
+
+// A wrapper class that automatically creates a message pipe and owns both
+// handles.
+class MessagePipe {
+ public:
+ MessagePipe();
+ explicit MessagePipe(const MojoCreateMessagePipeOptions& options);
+ ~MessagePipe();
+
+ ScopedMessagePipeHandle handle0;
+ ScopedMessagePipeHandle handle1;
+};
+
+inline MessagePipe::MessagePipe() {
+ MojoResult result = CreateMessagePipe(nullptr, &handle0, &handle1);
+ DCHECK_EQ(MOJO_RESULT_OK, result);
+ DCHECK(handle0.is_valid());
+ DCHECK(handle1.is_valid());
+}
+
+inline MessagePipe::MessagePipe(const MojoCreateMessagePipeOptions& options) {
+ MojoResult result = CreateMessagePipe(&options, &handle0, &handle1);
+ DCHECK_EQ(MOJO_RESULT_OK, result);
+ DCHECK(handle0.is_valid());
+ DCHECK(handle1.is_valid());
+}
+
+inline MessagePipe::~MessagePipe() {
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_PIPE_H_
diff --git a/mojo/public/cpp/system/platform_handle.cc b/mojo/public/cpp/system/platform_handle.cc
new file mode 100644
index 0000000000..42e4abac83
--- /dev/null
+++ b/mojo/public/cpp/system/platform_handle.cc
@@ -0,0 +1,178 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/system/platform_handle.h"
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+#include <mach/mach.h>
+#include "base/mac/mach_logging.h"
+#endif
+
+namespace mojo {
+
+namespace {
+
+uint64_t PlatformHandleValueFromPlatformFile(base::PlatformFile file) {
+#if defined(OS_WIN)
+ return reinterpret_cast<uint64_t>(file);
+#else
+ return static_cast<uint64_t>(file);
+#endif
+}
+
+base::PlatformFile PlatformFileFromPlatformHandleValue(uint64_t value) {
+#if defined(OS_WIN)
+ return reinterpret_cast<base::PlatformFile>(value);
+#else
+ return static_cast<base::PlatformFile>(value);
+#endif
+}
+
+} // namespace
+
+ScopedHandle WrapPlatformFile(base::PlatformFile platform_file) {
+ MojoPlatformHandle platform_handle;
+ platform_handle.struct_size = sizeof(MojoPlatformHandle);
+ platform_handle.type = kPlatformFileHandleType;
+ platform_handle.value = PlatformHandleValueFromPlatformFile(platform_file);
+
+ MojoHandle mojo_handle;
+ MojoResult result = MojoWrapPlatformHandle(&platform_handle, &mojo_handle);
+ CHECK_EQ(result, MOJO_RESULT_OK);
+
+ return ScopedHandle(Handle(mojo_handle));
+}
+
+MojoResult UnwrapPlatformFile(ScopedHandle handle, base::PlatformFile* file) {
+ MojoPlatformHandle platform_handle;
+ platform_handle.struct_size = sizeof(MojoPlatformHandle);
+ MojoResult result = MojoUnwrapPlatformHandle(handle.release().value(),
+ &platform_handle);
+ if (result != MOJO_RESULT_OK)
+ return result;
+
+ if (platform_handle.type == MOJO_PLATFORM_HANDLE_TYPE_INVALID) {
+ *file = base::kInvalidPlatformFile;
+ } else {
+ CHECK_EQ(platform_handle.type, kPlatformFileHandleType);
+ *file = PlatformFileFromPlatformHandleValue(platform_handle.value);
+ }
+
+ return MOJO_RESULT_OK;
+}
+
+ScopedSharedBufferHandle WrapSharedMemoryHandle(
+ const base::SharedMemoryHandle& memory_handle,
+ size_t size,
+ bool read_only) {
+#if defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS))
+ if (memory_handle.fd == base::kInvalidPlatformFile)
+ return ScopedSharedBufferHandle();
+#else
+ if (!memory_handle.IsValid())
+ return ScopedSharedBufferHandle();
+#endif
+ MojoPlatformHandle platform_handle;
+ platform_handle.struct_size = sizeof(MojoPlatformHandle);
+ platform_handle.type = kPlatformSharedBufferHandleType;
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+ platform_handle.value =
+ static_cast<uint64_t>(memory_handle.GetMemoryObject());
+#elif defined(OS_POSIX)
+ platform_handle.value = PlatformHandleValueFromPlatformFile(memory_handle.fd);
+#elif defined(OS_WIN)
+ platform_handle.value =
+ PlatformHandleValueFromPlatformFile(memory_handle.GetHandle());
+#endif
+
+ MojoPlatformSharedBufferHandleFlags flags =
+ MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_NONE;
+ if (read_only)
+ flags |= MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_READ_ONLY;
+
+ MojoHandle mojo_handle;
+ MojoResult result = MojoWrapPlatformSharedBufferHandle(
+ &platform_handle, size, flags, &mojo_handle);
+ CHECK_EQ(result, MOJO_RESULT_OK);
+
+ return ScopedSharedBufferHandle(SharedBufferHandle(mojo_handle));
+}
+
+MojoResult UnwrapSharedMemoryHandle(ScopedSharedBufferHandle handle,
+ base::SharedMemoryHandle* memory_handle,
+ size_t* size,
+ bool* read_only) {
+ if (!handle.is_valid())
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ MojoPlatformHandle platform_handle;
+ platform_handle.struct_size = sizeof(MojoPlatformHandle);
+
+ MojoPlatformSharedBufferHandleFlags flags;
+ size_t num_bytes;
+ MojoResult result = MojoUnwrapPlatformSharedBufferHandle(
+ handle.release().value(), &platform_handle, &num_bytes, &flags);
+ if (result != MOJO_RESULT_OK)
+ return result;
+
+ if (size)
+ *size = num_bytes;
+
+ if (read_only)
+ *read_only = flags & MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_READ_ONLY;
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+ CHECK_EQ(platform_handle.type, MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT);
+ *memory_handle = base::SharedMemoryHandle(
+ static_cast<mach_port_t>(platform_handle.value), num_bytes,
+ base::GetCurrentProcId());
+#elif defined(OS_POSIX)
+ CHECK_EQ(platform_handle.type, MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR);
+ *memory_handle = base::SharedMemoryHandle(
+ static_cast<int>(platform_handle.value), false);
+#elif defined(OS_WIN)
+ CHECK_EQ(platform_handle.type, MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE);
+ *memory_handle = base::SharedMemoryHandle(
+ reinterpret_cast<HANDLE>(platform_handle.value),
+ base::GetCurrentProcId());
+#endif
+
+ return MOJO_RESULT_OK;
+}
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+ScopedHandle WrapMachPort(mach_port_t port) {
+ kern_return_t kr =
+ mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, 1);
+ MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)
+ << "MachPortAttachmentMac mach_port_mod_refs";
+ if (kr != KERN_SUCCESS)
+ return ScopedHandle();
+
+ MojoPlatformHandle platform_handle;
+ platform_handle.struct_size = sizeof(MojoPlatformHandle);
+ platform_handle.type = MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT;
+ platform_handle.value = static_cast<uint64_t>(port);
+
+ MojoHandle mojo_handle;
+ MojoResult result = MojoWrapPlatformHandle(&platform_handle, &mojo_handle);
+ CHECK_EQ(result, MOJO_RESULT_OK);
+
+ return ScopedHandle(Handle(mojo_handle));
+}
+
+MojoResult UnwrapMachPort(ScopedHandle handle, mach_port_t* port) {
+ MojoPlatformHandle platform_handle;
+ platform_handle.struct_size = sizeof(MojoPlatformHandle);
+ MojoResult result =
+ MojoUnwrapPlatformHandle(handle.release().value(), &platform_handle);
+ if (result != MOJO_RESULT_OK)
+ return result;
+
+ CHECK_EQ(platform_handle.type, MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT);
+ *port = static_cast<mach_port_t>(platform_handle.value);
+ return MOJO_RESULT_OK;
+}
+#endif // defined(OS_MACOSX) && !defined(OS_IOS)
+
+} // namespace mojo
diff --git a/mojo/public/cpp/system/platform_handle.h b/mojo/public/cpp/system/platform_handle.h
new file mode 100644
index 0000000000..801264efce
--- /dev/null
+++ b/mojo/public/cpp/system/platform_handle.h
@@ -0,0 +1,92 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file provides a C++ wrapping around the Mojo C API for platform handles,
+// replacing the prefix of "Mojo" with a "mojo" namespace.
+//
+// Please see "mojo/public/c/system/platform_handle.h" for complete
+// documentation of the API.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_PLATFORM_HANDLE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_PLATFORM_HANDLE_H_
+
+#include <stdint.h>
+
+#include "base/compiler_specific.h"
+#include "base/files/file.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/shared_memory_handle.h"
+#include "base/process/process_handle.h"
+#include "mojo/public/c/system/platform_handle.h"
+#include "mojo/public/cpp/system/buffer.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/system_export.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace mojo {
+
+#if defined(OS_POSIX)
+const MojoPlatformHandleType kPlatformFileHandleType =
+ MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR;
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+const MojoPlatformHandleType kPlatformSharedBufferHandleType =
+ MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT;
+#else
+const MojoPlatformHandleType kPlatformSharedBufferHandleType =
+ MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR;
+#endif // defined(OS_MACOSX) && !defined(OS_IOS)
+
+#elif defined(OS_WIN)
+const MojoPlatformHandleType kPlatformFileHandleType =
+ MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE;
+
+const MojoPlatformHandleType kPlatformSharedBufferHandleType =
+ MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE;
+#endif // defined(OS_POSIX)
+
+// Wraps a PlatformFile as a Mojo handle. Takes ownership of the file object.
+MOJO_CPP_SYSTEM_EXPORT
+ScopedHandle WrapPlatformFile(base::PlatformFile platform_file);
+
+// Unwraps a PlatformFile from a Mojo handle.
+MOJO_CPP_SYSTEM_EXPORT
+MojoResult UnwrapPlatformFile(ScopedHandle handle, base::PlatformFile* file);
+
+// Wraps a base::SharedMemoryHandle as a Mojo handle. Takes ownership of the
+// SharedMemoryHandle. Note that |read_only| is only an indicator of whether
+// |memory_handle| only supports read-only mapping. It does NOT have any
+// influence on the access control of the shared buffer object.
+MOJO_CPP_SYSTEM_EXPORT
+ScopedSharedBufferHandle WrapSharedMemoryHandle(
+ const base::SharedMemoryHandle& memory_handle,
+ size_t size,
+ bool read_only);
+
+// Unwraps a base::SharedMemoryHandle from a Mojo handle. The caller assumes
+// responsibility for the lifetime of the SharedMemoryHandle.
+MOJO_CPP_SYSTEM_EXPORT MojoResult
+UnwrapSharedMemoryHandle(ScopedSharedBufferHandle handle,
+ base::SharedMemoryHandle* memory_handle,
+ size_t* size,
+ bool* read_only);
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+// Wraps a mach_port_t as a Mojo handle. This takes a reference to the
+// Mach port.
+MOJO_CPP_SYSTEM_EXPORT ScopedHandle WrapMachPort(mach_port_t port);
+
+// Unwraps a mach_port_t from a Mojo handle. The caller gets ownership of the
+// Mach port.
+MOJO_CPP_SYSTEM_EXPORT MojoResult UnwrapMachPort(ScopedHandle handle,
+ mach_port_t* port);
+#endif // defined(OS_MACOSX) && !defined(OS_IOS)
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_PLATFORM_HANDLE_H_
diff --git a/mojo/public/cpp/system/simple_watcher.cc b/mojo/public/cpp/system/simple_watcher.cc
new file mode 100644
index 0000000000..ae96faa395
--- /dev/null
+++ b/mojo/public/cpp/system/simple_watcher.cc
@@ -0,0 +1,279 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/system/simple_watcher.h"
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/trace_event/heap_profiler.h"
+#include "mojo/public/c/system/watcher.h"
+
+namespace mojo {
+
+// Thread-safe Context object used to dispatch watch notifications from a
+// arbitrary threads.
+class SimpleWatcher::Context : public base::RefCountedThreadSafe<Context> {
+ public:
+ // Creates a |Context| instance for a new watch on |watcher|, to watch
+ // |handle| for |signals|.
+ static scoped_refptr<Context> Create(
+ base::WeakPtr<SimpleWatcher> watcher,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ WatcherHandle watcher_handle,
+ Handle handle,
+ MojoHandleSignals signals,
+ int watch_id,
+ MojoResult* watch_result) {
+ scoped_refptr<Context> context =
+ new Context(watcher, task_runner, watch_id);
+
+ // If MojoWatch succeeds, it assumes ownership of a reference to |context|.
+ // In that case, this reference is balanced in CallNotify() when |result| is
+ // |MOJO_RESULT_CANCELLED|.
+ context->AddRef();
+
+ *watch_result = MojoWatch(watcher_handle.value(), handle.value(), signals,
+ context->value());
+ if (*watch_result != MOJO_RESULT_OK) {
+ // Balanced by the AddRef() above since watching failed.
+ context->Release();
+ return nullptr;
+ }
+
+ return context;
+ }
+
+ static void CallNotify(uintptr_t context_value,
+ MojoResult result,
+ MojoHandleSignalsState signals_state,
+ MojoWatcherNotificationFlags flags) {
+ auto* context = reinterpret_cast<Context*>(context_value);
+ context->Notify(result, signals_state, flags);
+
+ // That was the last notification for the context. We can release the ref
+ // owned by the watch, which may in turn delete the Context.
+ if (result == MOJO_RESULT_CANCELLED)
+ context->Release();
+ }
+
+ uintptr_t value() const { return reinterpret_cast<uintptr_t>(this); }
+
+ void DisableCancellationNotifications() {
+ base::AutoLock lock(lock_);
+ enable_cancellation_notifications_ = false;
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<Context>;
+
+ Context(base::WeakPtr<SimpleWatcher> weak_watcher,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ int watch_id)
+ : weak_watcher_(weak_watcher),
+ task_runner_(task_runner),
+ watch_id_(watch_id) {}
+ ~Context() {}
+
+ void Notify(MojoResult result,
+ MojoHandleSignalsState signals_state,
+ MojoWatcherNotificationFlags flags) {
+ if (result == MOJO_RESULT_CANCELLED) {
+ // The SimpleWatcher may have explicitly cancelled this watch, so we don't
+ // bother dispatching the notification - it would be ignored anyway.
+ //
+ // TODO(rockot): This shouldn't really be necessary, but there are already
+ // instances today where bindings object may be bound and subsequently
+ // closed due to pipe error, all before the thread's TaskRunner has been
+ // properly initialized.
+ base::AutoLock lock(lock_);
+ if (!enable_cancellation_notifications_)
+ return;
+ }
+
+ if ((flags & MOJO_WATCHER_NOTIFICATION_FLAG_FROM_SYSTEM) &&
+ task_runner_->RunsTasksOnCurrentThread() && weak_watcher_ &&
+ weak_watcher_->is_default_task_runner_) {
+ // System notifications will trigger from the task runner passed to
+ // mojo::edk::InitIPCSupport(). In Chrome this happens to always be the
+ // default task runner for the IO thread.
+ weak_watcher_->OnHandleReady(watch_id_, result);
+ } else {
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(&SimpleWatcher::OnHandleReady, weak_watcher_,
+ watch_id_, result));
+ }
+ }
+
+ const base::WeakPtr<SimpleWatcher> weak_watcher_;
+ const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ const int watch_id_;
+
+ base::Lock lock_;
+ bool enable_cancellation_notifications_ = true;
+
+ DISALLOW_COPY_AND_ASSIGN(Context);
+};
+
+SimpleWatcher::SimpleWatcher(const tracked_objects::Location& from_here,
+ ArmingPolicy arming_policy,
+ scoped_refptr<base::SingleThreadTaskRunner> runner)
+ : arming_policy_(arming_policy),
+ task_runner_(std::move(runner)),
+ is_default_task_runner_(task_runner_ ==
+ base::ThreadTaskRunnerHandle::Get()),
+ heap_profiler_tag_(from_here.file_name()),
+ weak_factory_(this) {
+ MojoResult rv = CreateWatcher(&Context::CallNotify, &watcher_handle_);
+ DCHECK_EQ(MOJO_RESULT_OK, rv);
+ DCHECK(task_runner_->BelongsToCurrentThread());
+}
+
+SimpleWatcher::~SimpleWatcher() {
+ if (IsWatching())
+ Cancel();
+}
+
+bool SimpleWatcher::IsWatching() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return context_ != nullptr;
+}
+
+MojoResult SimpleWatcher::Watch(Handle handle,
+ MojoHandleSignals signals,
+ const ReadyCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!IsWatching());
+ DCHECK(!callback.is_null());
+
+ callback_ = callback;
+ handle_ = handle;
+ watch_id_ += 1;
+
+ MojoResult watch_result = MOJO_RESULT_UNKNOWN;
+ context_ = Context::Create(weak_factory_.GetWeakPtr(), task_runner_,
+ watcher_handle_.get(), handle_, signals, watch_id_,
+ &watch_result);
+ if (!context_) {
+ handle_.set_value(kInvalidHandleValue);
+ callback_.Reset();
+ DCHECK_EQ(MOJO_RESULT_INVALID_ARGUMENT, watch_result);
+ return watch_result;
+ }
+
+ if (arming_policy_ == ArmingPolicy::AUTOMATIC)
+ ArmOrNotify();
+
+ return MOJO_RESULT_OK;
+}
+
+void SimpleWatcher::Cancel() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // The watcher may have already been cancelled if the handle was closed.
+ if (!context_)
+ return;
+
+ // Prevent the cancellation notification from being dispatched to
+ // OnHandleReady() when cancellation is explicit. See the note in the
+ // implementation of DisableCancellationNotifications() above.
+ context_->DisableCancellationNotifications();
+
+ handle_.set_value(kInvalidHandleValue);
+ callback_.Reset();
+
+ // Ensure |context_| is unset by the time we call MojoCancelWatch, as may
+ // re-enter the notification callback and we want to ensure |context_| is
+ // unset by then.
+ scoped_refptr<Context> context;
+ std::swap(context, context_);
+ MojoResult rv =
+ MojoCancelWatch(watcher_handle_.get().value(), context->value());
+
+ // It's possible this cancellation could race with a handle closure
+ // notification, in which case the watch may have already been implicitly
+ // cancelled.
+ DCHECK(rv == MOJO_RESULT_OK || rv == MOJO_RESULT_NOT_FOUND);
+}
+
+MojoResult SimpleWatcher::Arm(MojoResult* ready_result) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ uint32_t num_ready_contexts = 1;
+ uintptr_t ready_context;
+ MojoResult local_ready_result;
+ MojoHandleSignalsState ready_state;
+ MojoResult rv =
+ MojoArmWatcher(watcher_handle_.get().value(), &num_ready_contexts,
+ &ready_context, &local_ready_result, &ready_state);
+ if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
+ DCHECK(context_);
+ DCHECK_EQ(1u, num_ready_contexts);
+ DCHECK_EQ(context_->value(), ready_context);
+ if (ready_result)
+ *ready_result = local_ready_result;
+ }
+
+ return rv;
+}
+
+void SimpleWatcher::ArmOrNotify() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Already cancelled, nothing to do.
+ if (!IsWatching())
+ return;
+
+ MojoResult ready_result;
+ MojoResult rv = Arm(&ready_result);
+ if (rv == MOJO_RESULT_OK)
+ return;
+
+ DCHECK_EQ(MOJO_RESULT_FAILED_PRECONDITION, rv);
+ task_runner_->PostTask(FROM_HERE, base::Bind(&SimpleWatcher::OnHandleReady,
+ weak_factory_.GetWeakPtr(),
+ watch_id_, ready_result));
+}
+
+void SimpleWatcher::OnHandleReady(int watch_id, MojoResult result) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // This notification may be for a previously watched context, in which case
+ // we just ignore it.
+ if (watch_id != watch_id_)
+ return;
+
+ ReadyCallback callback = callback_;
+ if (result == MOJO_RESULT_CANCELLED) {
+ // Implicit cancellation due to someone closing the watched handle. We clear
+ // the SimppleWatcher's state before dispatching this.
+ context_ = nullptr;
+ handle_.set_value(kInvalidHandleValue);
+ callback_.Reset();
+ }
+
+ // NOTE: It's legal for |callback| to delete |this|.
+ if (!callback.is_null()) {
+ TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event(heap_profiler_tag_);
+
+ base::WeakPtr<SimpleWatcher> weak_self = weak_factory_.GetWeakPtr();
+ callback.Run(result);
+ if (!weak_self)
+ return;
+
+ if (unsatisfiable_)
+ return;
+
+ // Prevent |MOJO_RESULT_FAILED_PRECONDITION| task spam by only notifying
+ // at most once in AUTOMATIC arming mode.
+ if (result == MOJO_RESULT_FAILED_PRECONDITION)
+ unsatisfiable_ = true;
+
+ if (arming_policy_ == ArmingPolicy::AUTOMATIC && IsWatching())
+ ArmOrNotify();
+ }
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/system/simple_watcher.h b/mojo/public/cpp/system/simple_watcher.h
new file mode 100644
index 0000000000..9001884c97
--- /dev/null
+++ b/mojo/public/cpp/system/simple_watcher.h
@@ -0,0 +1,215 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_SIMPLE_WATCHER_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_SIMPLE_WATCHER_H_
+
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/system_export.h"
+#include "mojo/public/cpp/system/watcher.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace mojo {
+
+// This provides a convenient thread-bound watcher implementation to safely
+// watch a single handle, dispatching state change notifications to an arbitrary
+// SingleThreadTaskRunner running on the same thread as the SimpleWatcher.
+//
+// SimpleWatcher exposes the concept of "arming" from the low-level Watcher API.
+// In general, a SimpleWatcher must be "armed" in order to dispatch a single
+// notification, and must then be rearmed before it will dispatch another. For
+// more details, see the documentation for ArmingPolicy and the Arm() and
+// ArmOrNotify() methods below.
+class MOJO_CPP_SYSTEM_EXPORT SimpleWatcher {
+ public:
+ // A callback to be called any time a watched handle changes state in some
+ // interesting way. The |result| argument indicates one of the following
+ // conditions depending on its value:
+ //
+ // |MOJO_RESULT_OK|: One or more of the signals being watched is satisfied.
+ //
+ // |MOJO_RESULT_FAILED_PRECONDITION|: None of the signals being watched can
+ // ever be satisfied again.
+ //
+ // |MOJO_RESULT_CANCELLED|: The watched handle has been closed. No further
+ // notifications will be fired, as this equivalent to an implicit
+ // CancelWatch().
+ //
+ // Note that unlike the first two conditions, this callback may be invoked
+ // with |MOJO_RESULT_CANCELLED| even while the SimpleWatcher is disarmed.
+ using ReadyCallback = base::Callback<void(MojoResult result)>;
+
+ // Selects how this SimpleWatcher is to be armed.
+ enum class ArmingPolicy {
+ // The SimpleWatcher is armed automatically on Watch() and rearmed again
+ // after every invocation of the ReadyCallback. There is no need to manually
+ // call Arm() on a SimpleWatcher using this policy. This mode is equivalent
+ // to calling ArmOrNotify() once after Watch() and once again after every
+ // dispatched notification in MANUAL mode.
+ //
+ // This provides a reasonable approximation of edge-triggered behavior,
+ // mitigating (but not completely eliminating) the potential for redundant
+ // notifications.
+ //
+ // NOTE: It is important when using AUTOMATIC policy that your ReadyCallback
+ // always attempt to change the state of the handle (e.g. read available
+ // messages on a message pipe.) Otherwise this will result in a potentially
+ // large number of avoidable redundant tasks.
+ //
+ // For perfect edge-triggered behavior, use MANUAL policy and manually Arm()
+ // the SimpleWatcher as soon as it becomes possible to do so again.
+ AUTOMATIC,
+
+ // The SimpleWatcher is never armed automatically. Arm() or ArmOrNotify()
+ // must be called manually before any non-cancellation notification can be
+ // dispatched to the ReadyCallback. See the documentation for Arm() and
+ // ArmNotify() methods below for more details.
+ MANUAL,
+ };
+
+ SimpleWatcher(const tracked_objects::Location& from_here,
+ ArmingPolicy arming_policy,
+ scoped_refptr<base::SingleThreadTaskRunner> runner =
+ base::ThreadTaskRunnerHandle::Get());
+ ~SimpleWatcher();
+
+ // Indicates if the SimpleWatcher is currently watching a handle.
+ bool IsWatching() const;
+
+ // Starts watching |handle|. A SimpleWatcher may only watch one handle at a
+ // time, but it is safe to call this more than once as long as the previous
+ // watch has been cancelled (i.e. |IsWatching()| returns |false|.)
+ //
+ // If |handle| is not a valid watchable (message or data pipe) handle or
+ // |signals| is not a valid set of signals to watch, this returns
+ // |MOJO_RESULT_INVALID_ARGUMENT|.
+ //
+ // Otherwise |MOJO_RESULT_OK| is returned and the handle will be watched until
+ // either |handle| is closed, the SimpleWatcher is destroyed, or Cancel() is
+ // explicitly called.
+ //
+ // Once the watch is started, |callback| may be called at any time on the
+ // current thread until |Cancel()| is called or the handle is closed. Note
+ // that |callback| can be called for results other than
+ // |MOJO_RESULT_CANCELLED| only if the SimpleWatcher is currently armed. Use
+ // ArmingPolicy to configure how a SimpleWatcher is armed.
+ //
+ // |MOJO_RESULT_CANCELLED| may be dispatched even while the SimpleWatcher
+ // is disarmed, and no further notifications will be dispatched after that.
+ //
+ // Destroying the SimpleWatcher implicitly calls |Cancel()|.
+ MojoResult Watch(Handle handle,
+ MojoHandleSignals signals,
+ const ReadyCallback& callback);
+
+ // Cancels the current watch. Once this returns, the ReadyCallback previously
+ // passed to |Watch()| will never be called again for this SimpleWatcher.
+ //
+ // Note that when cancelled with an explicit call to |Cancel()| the
+ // ReadyCallback will not be invoked with a |MOJO_RESULT_CANCELLED| result.
+ void Cancel();
+
+ // Manually arms the SimpleWatcher.
+ //
+ // Arming the SimpleWatcher allows it to fire a single notification regarding
+ // some future relevant change in the watched handle's state. It's only valid
+ // to call Arm() while a handle is being watched (see Watch() above.)
+ //
+ // SimpleWatcher is always disarmed immediately before invoking its
+ // ReadyCallback and must be rearmed again before another notification can
+ // fire.
+ //
+ // If the watched handle already meets the watched signaling conditions -
+ // i.e., if it would have notified immediately once armed - the SimpleWatcher
+ // is NOT armed, and this call fails with a return value of
+ // |MOJO_RESULT_FAILED_PRECONDITION|. In that case, what would have been the
+ // result code for that immediate notification is instead placed in
+ // |*ready_result| if |ready_result| is non-null.
+ //
+ // If the watcher is successfully armed, this returns |MOJO_RESULT_OK| and
+ // |ready_result| is ignored.
+ MojoResult Arm(MojoResult* ready_result = nullptr);
+
+ // Manually arms the SimpleWatcher OR posts a task to invoke the ReadyCallback
+ // with the ready result of the failed arming attempt.
+ //
+ // This is meant as a convenient helper for a common usage of Arm(), and it
+ // ensures that the ReadyCallback will be invoked asynchronously again as soon
+ // as the watch's conditions are satisfied, assuming the SimpleWatcher isn't
+ // cancelled first.
+ //
+ // Unlike Arm() above, this can never fail.
+ void ArmOrNotify();
+
+ Handle handle() const { return handle_; }
+ ReadyCallback ready_callback() const { return callback_; }
+
+ // Sets the tag used by the heap profiler.
+ // |tag| must be a const string literal.
+ void set_heap_profiler_tag(const char* heap_profiler_tag) {
+ heap_profiler_tag_ = heap_profiler_tag;
+ }
+
+ private:
+ class Context;
+
+ void OnHandleReady(int watch_id, MojoResult result);
+
+ base::ThreadChecker thread_checker_;
+
+ // The policy used to determine how this SimpleWatcher is armed.
+ const ArmingPolicy arming_policy_;
+
+ // The TaskRunner of this SimpleWatcher's owning thread. This field is safe to
+ // access from any thread.
+ const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+ // Whether |task_runner_| is the same as base::ThreadTaskRunnerHandle::Get()
+ // for the thread.
+ const bool is_default_task_runner_;
+
+ ScopedWatcherHandle watcher_handle_;
+
+ // A thread-safe context object corresponding to the currently active watch,
+ // if any.
+ scoped_refptr<Context> context_;
+
+ // Fields below must only be accessed on the SimpleWatcher's owning thread.
+
+ // The handle currently under watch. Not owned.
+ Handle handle_;
+
+ // A simple counter to disambiguate notifications from multiple watch contexts
+ // in the event that this SimpleWatcher cancels and watches multiple times.
+ int watch_id_ = 0;
+
+ // The callback to call when the handle is signaled.
+ ReadyCallback callback_;
+
+ // Tracks if the SimpleWatcher has already notified of unsatisfiability. This
+ // is used to prevent redundant notifications in AUTOMATIC mode.
+ bool unsatisfiable_ = false;
+
+ // Tag used to ID memory allocations that originated from notifications in
+ // this watcher.
+ const char* heap_profiler_tag_ = nullptr;
+
+ base::WeakPtrFactory<SimpleWatcher> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SimpleWatcher);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_SIMPLE_WATCHER_H_
diff --git a/mojo/public/cpp/system/system_export.h b/mojo/public/cpp/system/system_export.h
new file mode 100644
index 0000000000..c9bb140db3
--- /dev/null
+++ b/mojo/public/cpp/system/system_export.h
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_SYSTEM_EXPORT_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_SYSTEM_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_CPP_SYSTEM_IMPLEMENTATION)
+#define MOJO_CPP_SYSTEM_EXPORT __declspec(dllexport)
+#else
+#define MOJO_CPP_SYSTEM_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_CPP_SYSTEM_IMPLEMENTATION)
+#define MOJO_CPP_SYSTEM_EXPORT __attribute((visibility("default")))
+#else
+#define MOJO_CPP_SYSTEM_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#else // !defined(COMPONENT_BUILD)
+
+#define MOJO_CPP_SYSTEM_EXPORT
+
+#endif // defined(COMPONENT_BUILD)
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_SYSTEM_EXPORT_H_
diff --git a/mojo/public/cpp/system/tests/BUILD.gn b/mojo/public/cpp/system/tests/BUILD.gn
new file mode 100644
index 0000000000..705d009c9c
--- /dev/null
+++ b/mojo/public/cpp/system/tests/BUILD.gn
@@ -0,0 +1,23 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("tests") {
+ testonly = true
+
+ sources = [
+ "core_unittest.cc",
+ "handle_signals_state_unittest.cc",
+ "simple_watcher_unittest.cc",
+ "wait_set_unittest.cc",
+ "wait_unittest.cc",
+ ]
+
+ deps = [
+ "//base",
+ "//mojo/public/c/system/tests",
+ "//mojo/public/cpp/system",
+ "//mojo/public/cpp/test_support:test_utils",
+ "//testing/gtest",
+ ]
+}
diff --git a/mojo/public/cpp/system/tests/core_unittest.cc b/mojo/public/cpp/system/tests/core_unittest.cc
new file mode 100644
index 0000000000..40a94f008f
--- /dev/null
+++ b/mojo/public/cpp/system/tests/core_unittest.cc
@@ -0,0 +1,510 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file tests the C++ Mojo system core wrappers.
+// TODO(vtl): Maybe rename "CoreCppTest" -> "CoreTest" if/when this gets
+// compiled into a different binary from the C API tests.
+
+#include "mojo/public/cpp/system/core.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <map>
+#include <utility>
+
+#include "mojo/public/cpp/system/wait.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+const MojoHandleSignals kSignalReadableWritable =
+ MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE;
+
+const MojoHandleSignals kSignalAll = MOJO_HANDLE_SIGNAL_READABLE |
+ MOJO_HANDLE_SIGNAL_WRITABLE |
+ MOJO_HANDLE_SIGNAL_PEER_CLOSED;
+
+TEST(CoreCppTest, GetTimeTicksNow) {
+ const MojoTimeTicks start = GetTimeTicksNow();
+ EXPECT_NE(static_cast<MojoTimeTicks>(0), start)
+ << "GetTimeTicksNow should return nonzero value";
+}
+
+TEST(CoreCppTest, Basic) {
+ // Basic |Handle| implementation:
+ {
+ EXPECT_EQ(MOJO_HANDLE_INVALID, kInvalidHandleValue);
+
+ Handle h0;
+ EXPECT_EQ(kInvalidHandleValue, h0.value());
+ EXPECT_EQ(kInvalidHandleValue, *h0.mutable_value());
+ EXPECT_FALSE(h0.is_valid());
+
+ Handle h1(static_cast<MojoHandle>(123));
+ EXPECT_EQ(static_cast<MojoHandle>(123), h1.value());
+ EXPECT_EQ(static_cast<MojoHandle>(123), *h1.mutable_value());
+ EXPECT_TRUE(h1.is_valid());
+ *h1.mutable_value() = static_cast<MojoHandle>(456);
+ EXPECT_EQ(static_cast<MojoHandle>(456), h1.value());
+ EXPECT_TRUE(h1.is_valid());
+
+ h1.swap(h0);
+ EXPECT_EQ(static_cast<MojoHandle>(456), h0.value());
+ EXPECT_TRUE(h0.is_valid());
+ EXPECT_FALSE(h1.is_valid());
+
+ h1.set_value(static_cast<MojoHandle>(789));
+ h0.swap(h1);
+ EXPECT_EQ(static_cast<MojoHandle>(789), h0.value());
+ EXPECT_TRUE(h0.is_valid());
+ EXPECT_EQ(static_cast<MojoHandle>(456), h1.value());
+ EXPECT_TRUE(h1.is_valid());
+
+ // Make sure copy constructor works.
+ Handle h2(h0);
+ EXPECT_EQ(static_cast<MojoHandle>(789), h2.value());
+ // And assignment.
+ h2 = h1;
+ EXPECT_EQ(static_cast<MojoHandle>(456), h2.value());
+
+ // Make sure that we can put |Handle|s into |std::map|s.
+ h0 = Handle(static_cast<MojoHandle>(987));
+ h1 = Handle(static_cast<MojoHandle>(654));
+ h2 = Handle(static_cast<MojoHandle>(321));
+ Handle h3;
+ std::map<Handle, int> handle_to_int;
+ handle_to_int[h0] = 0;
+ handle_to_int[h1] = 1;
+ handle_to_int[h2] = 2;
+ handle_to_int[h3] = 3;
+
+ EXPECT_EQ(4u, handle_to_int.size());
+ EXPECT_FALSE(handle_to_int.find(h0) == handle_to_int.end());
+ EXPECT_EQ(0, handle_to_int[h0]);
+ EXPECT_FALSE(handle_to_int.find(h1) == handle_to_int.end());
+ EXPECT_EQ(1, handle_to_int[h1]);
+ EXPECT_FALSE(handle_to_int.find(h2) == handle_to_int.end());
+ EXPECT_EQ(2, handle_to_int[h2]);
+ EXPECT_FALSE(handle_to_int.find(h3) == handle_to_int.end());
+ EXPECT_EQ(3, handle_to_int[h3]);
+ EXPECT_TRUE(handle_to_int.find(Handle(static_cast<MojoHandle>(13579))) ==
+ handle_to_int.end());
+
+ // TODO(vtl): With C++11, support |std::unordered_map|s, etc. (Or figure out
+ // how to support the variations of |hash_map|.)
+ }
+
+ // |Handle|/|ScopedHandle| functions:
+ {
+ ScopedHandle h;
+
+ EXPECT_EQ(kInvalidHandleValue, h.get().value());
+
+ // This should be a no-op.
+ Close(std::move(h));
+
+ // It should still be invalid.
+ EXPECT_EQ(kInvalidHandleValue, h.get().value());
+
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ Wait(h.get(), ~MOJO_HANDLE_SIGNAL_NONE));
+
+ std::vector<Handle> wh;
+ wh.push_back(h.get());
+ std::vector<MojoHandleSignals> sigs;
+ sigs.push_back(~MOJO_HANDLE_SIGNAL_NONE);
+ size_t result_index;
+ MojoResult rv = WaitMany(wh.data(), sigs.data(), wh.size(), &result_index);
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, rv);
+ }
+
+ // |MakeScopedHandle| (just compilation tests):
+ {
+ EXPECT_FALSE(MakeScopedHandle(Handle()).is_valid());
+ EXPECT_FALSE(MakeScopedHandle(MessagePipeHandle()).is_valid());
+ EXPECT_FALSE(MakeScopedHandle(DataPipeProducerHandle()).is_valid());
+ EXPECT_FALSE(MakeScopedHandle(DataPipeConsumerHandle()).is_valid());
+ EXPECT_FALSE(MakeScopedHandle(SharedBufferHandle()).is_valid());
+ }
+
+ // |MessagePipeHandle|/|ScopedMessagePipeHandle| functions:
+ {
+ MessagePipeHandle h_invalid;
+ EXPECT_FALSE(h_invalid.is_valid());
+ EXPECT_EQ(
+ MOJO_RESULT_INVALID_ARGUMENT,
+ WriteMessageRaw(
+ h_invalid, nullptr, 0, nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE));
+ char buffer[10] = {0};
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ WriteMessageRaw(h_invalid,
+ buffer,
+ sizeof(buffer),
+ nullptr,
+ 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ ReadMessageRaw(h_invalid,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ ReadMessageRaw(h_invalid,
+ buffer,
+ &buffer_size,
+ nullptr,
+ nullptr,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+
+ // Basic tests of waiting and closing.
+ MojoHandle hv0 = kInvalidHandleValue;
+ {
+ ScopedMessagePipeHandle h0;
+ ScopedMessagePipeHandle h1;
+ EXPECT_FALSE(h0.get().is_valid());
+ EXPECT_FALSE(h1.get().is_valid());
+
+ CreateMessagePipe(nullptr, &h0, &h1);
+ EXPECT_TRUE(h0.get().is_valid());
+ EXPECT_TRUE(h1.get().is_valid());
+ EXPECT_NE(h0.get().value(), h1.get().value());
+ // Save the handle values, so we can check that things got closed
+ // correctly.
+ hv0 = h0.get().value();
+ MojoHandle hv1 = h1.get().value();
+ MojoHandleSignalsState state = h0->QuerySignalsState();
+
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
+ EXPECT_EQ(kSignalAll, state.satisfiable_signals);
+
+ std::vector<Handle> wh;
+ wh.push_back(h0.get());
+ wh.push_back(h1.get());
+ std::vector<MojoHandleSignals> sigs;
+ sigs.push_back(MOJO_HANDLE_SIGNAL_READABLE);
+ sigs.push_back(MOJO_HANDLE_SIGNAL_WRITABLE);
+ std::vector<MojoHandleSignalsState> states(sigs.size());
+
+ size_t result_index;
+ MojoResult rv = WaitMany(wh.data(), sigs.data(), wh.size(), &result_index,
+ states.data());
+ EXPECT_EQ(MOJO_RESULT_OK, rv);
+ EXPECT_EQ(1u, result_index);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, states[0].satisfied_signals);
+ EXPECT_EQ(kSignalAll, states[0].satisfiable_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, states[1].satisfied_signals);
+ EXPECT_EQ(kSignalAll, states[1].satisfiable_signals);
+
+ // Test closing |h1| explicitly.
+ Close(std::move(h1));
+ EXPECT_FALSE(h1.get().is_valid());
+
+ // Make sure |h1| is closed.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ Wait(Handle(hv1), ~MOJO_HANDLE_SIGNAL_NONE));
+
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ Wait(h0.get(), MOJO_HANDLE_SIGNAL_READABLE, &state));
+
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals);
+ }
+ // |hv0| should have been closed when |h0| went out of scope, so this close
+ // should fail.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(hv0));
+
+ // Actually test writing/reading messages.
+ {
+ ScopedMessagePipeHandle h0;
+ ScopedMessagePipeHandle h1;
+ CreateMessagePipe(nullptr, &h0, &h1);
+
+ const char kHello[] = "hello";
+ const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ WriteMessageRaw(h0.get(),
+ kHello,
+ kHelloSize,
+ nullptr,
+ 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ MojoHandleSignalsState state;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ Wait(h1.get(), MOJO_HANDLE_SIGNAL_READABLE, &state));
+ EXPECT_EQ(kSignalReadableWritable, state.satisfied_signals);
+ EXPECT_EQ(kSignalAll, state.satisfiable_signals);
+
+ char buffer[10] = {0};
+ uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ ReadMessageRaw(h1.get(),
+ buffer,
+ &buffer_size,
+ nullptr,
+ nullptr,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kHelloSize, buffer_size);
+ EXPECT_STREQ(kHello, buffer);
+
+ // Send a handle over the previously-establish message pipe. Use the
+ // |MessagePipe| wrapper (to test it), which automatically creates a
+ // message pipe.
+ MessagePipe mp;
+
+ // Write a message to |mp.handle0|, before we send |mp.handle1|.
+ const char kWorld[] = "world!";
+ const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ WriteMessageRaw(mp.handle0.get(),
+ kWorld,
+ kWorldSize,
+ nullptr,
+ 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Send |mp.handle1| over |h1| to |h0|.
+ MojoHandle handles[5];
+ handles[0] = mp.handle1.release().value();
+ EXPECT_NE(kInvalidHandleValue, handles[0]);
+ EXPECT_FALSE(mp.handle1.get().is_valid());
+ uint32_t handles_count = 1;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ WriteMessageRaw(h1.get(),
+ kHello,
+ kHelloSize,
+ handles,
+ handles_count,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ // |handles[0]| should actually be invalid now.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(handles[0]));
+
+ // Read "hello" and the sent handle.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ Wait(h0.get(), MOJO_HANDLE_SIGNAL_READABLE, &state));
+ EXPECT_EQ(kSignalReadableWritable, state.satisfied_signals);
+ EXPECT_EQ(kSignalAll, state.satisfiable_signals);
+
+ memset(buffer, 0, sizeof(buffer));
+ buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ for (size_t i = 0; i < arraysize(handles); i++)
+ handles[i] = kInvalidHandleValue;
+ handles_count = static_cast<uint32_t>(arraysize(handles));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ ReadMessageRaw(h0.get(),
+ buffer,
+ &buffer_size,
+ handles,
+ &handles_count,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kHelloSize, buffer_size);
+ EXPECT_STREQ(kHello, buffer);
+ EXPECT_EQ(1u, handles_count);
+ EXPECT_NE(kInvalidHandleValue, handles[0]);
+
+ // Read from the sent/received handle.
+ mp.handle1.reset(MessagePipeHandle(handles[0]));
+ // Save |handles[0]| to check that it gets properly closed.
+ hv0 = handles[0];
+
+ EXPECT_EQ(MOJO_RESULT_OK,
+ Wait(mp.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE, &state));
+ EXPECT_EQ(kSignalReadableWritable, state.satisfied_signals);
+ EXPECT_EQ(kSignalAll, state.satisfiable_signals);
+
+ memset(buffer, 0, sizeof(buffer));
+ buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ for (size_t i = 0; i < arraysize(handles); i++)
+ handles[i] = kInvalidHandleValue;
+ handles_count = static_cast<uint32_t>(arraysize(handles));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ ReadMessageRaw(mp.handle1.get(),
+ buffer,
+ &buffer_size,
+ handles,
+ &handles_count,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kWorldSize, buffer_size);
+ EXPECT_STREQ(kWorld, buffer);
+ EXPECT_EQ(0u, handles_count);
+ }
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(hv0));
+ }
+
+ // TODO(vtl): Test |CloseRaw()|.
+ // TODO(vtl): Test |reset()| more thoroughly?
+}
+
+TEST(CoreCppTest, TearDownWithMessagesEnqueued) {
+ // Tear down a message pipe which still has a message enqueued, with the
+ // message also having a valid message pipe handle.
+ {
+ ScopedMessagePipeHandle h0;
+ ScopedMessagePipeHandle h1;
+ CreateMessagePipe(nullptr, &h0, &h1);
+
+ // Send a handle over the previously-establish message pipe.
+ ScopedMessagePipeHandle h2;
+ ScopedMessagePipeHandle h3;
+ if (CreateMessagePipe(nullptr, &h2, &h3) != MOJO_RESULT_OK)
+ CreateMessagePipe(nullptr, &h2, &h3); // Must be old EDK.
+
+ // Write a message to |h2|, before we send |h3|.
+ const char kWorld[] = "world!";
+ const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ WriteMessageRaw(h2.get(),
+ kWorld,
+ kWorldSize,
+ nullptr,
+ 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ // And also a message to |h3|.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ WriteMessageRaw(h3.get(),
+ kWorld,
+ kWorldSize,
+ nullptr,
+ 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Send |h3| over |h1| to |h0|.
+ const char kHello[] = "hello";
+ const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello));
+ MojoHandle h3_value;
+ h3_value = h3.release().value();
+ EXPECT_NE(kInvalidHandleValue, h3_value);
+ EXPECT_FALSE(h3.get().is_valid());
+ EXPECT_EQ(MOJO_RESULT_OK,
+ WriteMessageRaw(h1.get(),
+ kHello,
+ kHelloSize,
+ &h3_value,
+ 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ // |h3_value| should actually be invalid now.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(h3_value));
+
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0.release().value()));
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1.release().value()));
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h2.release().value()));
+ }
+
+ // Do this in a different order: make the enqueued message pipe handle only
+ // half-alive.
+ {
+ ScopedMessagePipeHandle h0;
+ ScopedMessagePipeHandle h1;
+ CreateMessagePipe(nullptr, &h0, &h1);
+
+ // Send a handle over the previously-establish message pipe.
+ ScopedMessagePipeHandle h2;
+ ScopedMessagePipeHandle h3;
+ if (CreateMessagePipe(nullptr, &h2, &h3) != MOJO_RESULT_OK)
+ CreateMessagePipe(nullptr, &h2, &h3); // Must be old EDK.
+
+ // Write a message to |h2|, before we send |h3|.
+ const char kWorld[] = "world!";
+ const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ WriteMessageRaw(h2.get(),
+ kWorld,
+ kWorldSize,
+ nullptr,
+ 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ // And also a message to |h3|.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ WriteMessageRaw(h3.get(),
+ kWorld,
+ kWorldSize,
+ nullptr,
+ 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Send |h3| over |h1| to |h0|.
+ const char kHello[] = "hello";
+ const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello));
+ MojoHandle h3_value;
+ h3_value = h3.release().value();
+ EXPECT_NE(kInvalidHandleValue, h3_value);
+ EXPECT_FALSE(h3.get().is_valid());
+ EXPECT_EQ(MOJO_RESULT_OK,
+ WriteMessageRaw(h1.get(),
+ kHello,
+ kHelloSize,
+ &h3_value,
+ 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ // |h3_value| should actually be invalid now.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(h3_value));
+
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h2.release().value()));
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0.release().value()));
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1.release().value()));
+ }
+}
+
+TEST(CoreCppTest, ScopedHandleMoveCtor) {
+ ScopedSharedBufferHandle buffer1 = SharedBufferHandle::Create(1024);
+ EXPECT_TRUE(buffer1.is_valid());
+
+ ScopedSharedBufferHandle buffer2 = SharedBufferHandle::Create(1024);
+ EXPECT_TRUE(buffer2.is_valid());
+
+ // If this fails to close buffer1, ScopedHandleBase::CloseIfNecessary() will
+ // assert.
+ buffer1 = std::move(buffer2);
+
+ EXPECT_TRUE(buffer1.is_valid());
+ EXPECT_FALSE(buffer2.is_valid());
+}
+
+TEST(CoreCppTest, BasicSharedBuffer) {
+ ScopedSharedBufferHandle h0 = SharedBufferHandle::Create(100);
+ ASSERT_TRUE(h0.is_valid());
+
+ // Map everything.
+ ScopedSharedBufferMapping mapping = h0->Map(100);
+ ASSERT_TRUE(mapping);
+ static_cast<char*>(mapping.get())[50] = 'x';
+
+ // Duplicate |h0| to |h1|.
+ ScopedSharedBufferHandle h1 =
+ h0->Clone(SharedBufferHandle::AccessMode::READ_ONLY);
+ ASSERT_TRUE(h1.is_valid());
+
+ // Close |h0|.
+ h0.reset();
+
+ // The mapping should still be good.
+ static_cast<char*>(mapping.get())[51] = 'y';
+
+ // Unmap it.
+ mapping.reset();
+
+ // Map half of |h1|.
+ mapping = h1->MapAtOffset(50, 50);
+ ASSERT_TRUE(mapping);
+
+ // It should have what we wrote.
+ EXPECT_EQ('x', static_cast<char*>(mapping.get())[0]);
+ EXPECT_EQ('y', static_cast<char*>(mapping.get())[1]);
+
+ // Unmap it.
+ mapping.reset();
+ h1.reset();
+
+ // Creating a 1 EB shared buffer should fail without crashing.
+ EXPECT_FALSE(SharedBufferHandle::Create(1ULL << 60).is_valid());
+}
+
+// TODO(vtl): Write data pipe tests.
+
+} // namespace
+} // namespace mojo
diff --git a/mojo/public/cpp/system/tests/handle_signals_state_unittest.cc b/mojo/public/cpp/system/tests/handle_signals_state_unittest.cc
new file mode 100644
index 0000000000..82f538e17a
--- /dev/null
+++ b/mojo/public/cpp/system/tests/handle_signals_state_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/system/handle_signals_state.h"
+
+#include "mojo/public/c/system/types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+using HandleSignalsStateTest = testing::Test;
+
+TEST_F(HandleSignalsStateTest, SanityCheck) {
+ // There's not much to test here. Just a quick sanity check to make sure the
+ // code compiles and the helper methods do what they're supposed to do.
+
+ HandleSignalsState empty_signals(MOJO_HANDLE_SIGNAL_NONE,
+ MOJO_HANDLE_SIGNAL_NONE);
+ EXPECT_FALSE(empty_signals.readable());
+ EXPECT_FALSE(empty_signals.writable());
+ EXPECT_FALSE(empty_signals.peer_closed());
+ EXPECT_TRUE(empty_signals.never_readable());
+ EXPECT_TRUE(empty_signals.never_writable());
+ EXPECT_TRUE(empty_signals.never_peer_closed());
+
+ HandleSignalsState all_signals(
+ MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+ MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+ MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+ MOJO_HANDLE_SIGNAL_PEER_CLOSED);
+ EXPECT_TRUE(all_signals.readable());
+ EXPECT_TRUE(all_signals.writable());
+ EXPECT_TRUE(all_signals.peer_closed());
+ EXPECT_FALSE(all_signals.never_readable());
+ EXPECT_FALSE(all_signals.never_writable());
+ EXPECT_FALSE(all_signals.never_peer_closed());
+}
+
+} // namespace
+} // namespace mojo
diff --git a/mojo/public/cpp/system/tests/simple_watcher_unittest.cc b/mojo/public/cpp/system/tests/simple_watcher_unittest.cc
new file mode 100644
index 0000000000..795f262c4e
--- /dev/null
+++ b/mojo/public/cpp/system/tests/simple_watcher_unittest.cc
@@ -0,0 +1,277 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/system/simple_watcher.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+template <typename Handler>
+void RunResultHandler(Handler f, MojoResult result) {
+ f(result);
+}
+
+template <typename Handler>
+SimpleWatcher::ReadyCallback OnReady(Handler f) {
+ return base::Bind(&RunResultHandler<Handler>, f);
+}
+
+SimpleWatcher::ReadyCallback NotReached() {
+ return OnReady([](MojoResult) { NOTREACHED(); });
+}
+
+class SimpleWatcherTest : public testing::Test {
+ public:
+ SimpleWatcherTest() {}
+ ~SimpleWatcherTest() override {}
+
+ private:
+ base::MessageLoop message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(SimpleWatcherTest);
+};
+
+TEST_F(SimpleWatcherTest, WatchBasic) {
+ ScopedMessagePipeHandle a, b;
+ CreateMessagePipe(nullptr, &a, &b);
+
+ bool notified = false;
+ base::RunLoop run_loop;
+ SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ OnReady([&](MojoResult result) {
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ notified = true;
+ run_loop.Quit();
+ })));
+ EXPECT_TRUE(b_watcher.IsWatching());
+
+ EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ run_loop.Run();
+ EXPECT_TRUE(notified);
+
+ b_watcher.Cancel();
+}
+
+TEST_F(SimpleWatcherTest, WatchUnsatisfiable) {
+ ScopedMessagePipeHandle a, b;
+ CreateMessagePipe(nullptr, &a, &b);
+ a.reset();
+
+ SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::MANUAL);
+ EXPECT_EQ(
+ MOJO_RESULT_OK,
+ b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE, NotReached()));
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, b_watcher.Arm());
+}
+
+TEST_F(SimpleWatcherTest, WatchInvalidHandle) {
+ ScopedMessagePipeHandle a, b;
+ CreateMessagePipe(nullptr, &a, &b);
+ a.reset();
+ b.reset();
+
+ SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC);
+ EXPECT_EQ(
+ MOJO_RESULT_INVALID_ARGUMENT,
+ b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE, NotReached()));
+ EXPECT_FALSE(b_watcher.IsWatching());
+}
+
+TEST_F(SimpleWatcherTest, Cancel) {
+ ScopedMessagePipeHandle a, b;
+ CreateMessagePipe(nullptr, &a, &b);
+
+ base::RunLoop run_loop;
+ SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC);
+ EXPECT_EQ(
+ MOJO_RESULT_OK,
+ b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE, NotReached()));
+ EXPECT_TRUE(b_watcher.IsWatching());
+ b_watcher.Cancel();
+ EXPECT_FALSE(b_watcher.IsWatching());
+
+ // This should never trigger the watcher.
+ EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ run_loop.QuitClosure());
+ run_loop.Run();
+}
+
+TEST_F(SimpleWatcherTest, CancelOnClose) {
+ ScopedMessagePipeHandle a, b;
+ CreateMessagePipe(nullptr, &a, &b);
+
+ base::RunLoop run_loop;
+ SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ OnReady([&](MojoResult result) {
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+ run_loop.Quit();
+ })));
+ EXPECT_TRUE(b_watcher.IsWatching());
+
+ // This should trigger the watcher above.
+ b.reset();
+
+ run_loop.Run();
+
+ EXPECT_FALSE(b_watcher.IsWatching());
+}
+
+TEST_F(SimpleWatcherTest, CancelOnDestruction) {
+ ScopedMessagePipeHandle a, b;
+ CreateMessagePipe(nullptr, &a, &b);
+ base::RunLoop run_loop;
+ {
+ SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC);
+ EXPECT_EQ(
+ MOJO_RESULT_OK,
+ b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE, NotReached()));
+ EXPECT_TRUE(b_watcher.IsWatching());
+
+ // |b_watcher| should be cancelled when it goes out of scope.
+ }
+
+ // This should never trigger the watcher above.
+ EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ run_loop.QuitClosure());
+ run_loop.Run();
+}
+
+TEST_F(SimpleWatcherTest, CloseAndCancel) {
+ ScopedMessagePipeHandle a, b;
+ CreateMessagePipe(nullptr, &a, &b);
+
+ SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ OnReady([](MojoResult result) { FAIL(); })));
+ EXPECT_TRUE(b_watcher.IsWatching());
+
+ // This should trigger the watcher above...
+ b.reset();
+ // ...but the watcher is cancelled first.
+ b_watcher.Cancel();
+
+ EXPECT_FALSE(b_watcher.IsWatching());
+
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(SimpleWatcherTest, UnarmedCancel) {
+ ScopedMessagePipeHandle a, b;
+ CreateMessagePipe(nullptr, &a, &b);
+
+ SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::MANUAL);
+ base::RunLoop loop;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ base::Bind(
+ [](base::RunLoop* loop, MojoResult result) {
+ EXPECT_EQ(result, MOJO_RESULT_CANCELLED);
+ loop->Quit();
+ },
+ &loop)));
+
+ // This message write will not wake up the watcher since the watcher isn't
+ // armed. Instead, the cancellation will dispatch due to the reset below.
+ EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ b.reset();
+ loop.Run();
+}
+
+TEST_F(SimpleWatcherTest, ManualArming) {
+ ScopedMessagePipeHandle a, b;
+ CreateMessagePipe(nullptr, &a, &b);
+
+ SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::MANUAL);
+ base::RunLoop loop;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ base::Bind(
+ [](base::RunLoop* loop, MojoResult result) {
+ EXPECT_EQ(result, MOJO_RESULT_OK);
+ loop->Quit();
+ },
+ &loop)));
+ EXPECT_EQ(MOJO_RESULT_OK, b_watcher.Arm());
+
+ EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ loop.Run();
+}
+
+TEST_F(SimpleWatcherTest, ManualArmOrNotifyWhileSignaled) {
+ ScopedMessagePipeHandle a, b;
+ CreateMessagePipe(nullptr, &a, &b);
+
+ base::RunLoop loop1;
+ SimpleWatcher b_watcher1(FROM_HERE, SimpleWatcher::ArmingPolicy::MANUAL);
+ bool notified1 = false;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ b_watcher1.Watch(
+ b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ base::Bind(
+ [](base::RunLoop* loop, bool* notified, MojoResult result) {
+ EXPECT_EQ(result, MOJO_RESULT_OK);
+ *notified = true;
+ loop->Quit();
+ },
+ &loop1, &notified1)));
+
+ base::RunLoop loop2;
+ SimpleWatcher b_watcher2(FROM_HERE, SimpleWatcher::ArmingPolicy::MANUAL);
+ bool notified2 = false;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ b_watcher2.Watch(
+ b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ base::Bind(
+ [](base::RunLoop* loop, bool* notified, MojoResult result) {
+ EXPECT_EQ(result, MOJO_RESULT_OK);
+ *notified = true;
+ loop->Quit();
+ },
+ &loop2, &notified2)));
+
+ // First ensure that |b| is readable.
+ EXPECT_EQ(MOJO_RESULT_OK, b_watcher1.Arm());
+ EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ loop1.Run();
+
+ EXPECT_TRUE(notified1);
+ EXPECT_FALSE(notified2);
+ notified1 = false;
+
+ // Now verify that ArmOrNotify results in a notification.
+ b_watcher2.ArmOrNotify();
+ loop2.Run();
+
+ EXPECT_FALSE(notified1);
+ EXPECT_TRUE(notified2);
+}
+
+} // namespace
+} // namespace mojo
diff --git a/mojo/public/cpp/system/tests/wait_set_unittest.cc b/mojo/public/cpp/system/tests/wait_set_unittest.cc
new file mode 100644
index 0000000000..d60cb45924
--- /dev/null
+++ b/mojo/public/cpp/system/tests/wait_set_unittest.cc
@@ -0,0 +1,376 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/system/wait_set.h"
+
+#include <set>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/ptr_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/simple_thread.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "mojo/public/cpp/system/wait.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+using WaitSetTest = testing::Test;
+
+void WriteMessage(const ScopedMessagePipeHandle& handle,
+ const base::StringPiece& message) {
+ MojoResult rv = WriteMessageRaw(handle.get(), message.data(),
+ static_cast<uint32_t>(message.size()),
+ nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE);
+ CHECK_EQ(MOJO_RESULT_OK, rv);
+}
+
+std::string ReadMessage(const ScopedMessagePipeHandle& handle) {
+ uint32_t num_bytes = 0;
+ uint32_t num_handles = 0;
+ MojoResult rv = ReadMessageRaw(handle.get(), nullptr, &num_bytes, nullptr,
+ &num_handles, MOJO_READ_MESSAGE_FLAG_NONE);
+ CHECK_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, rv);
+ CHECK_EQ(0u, num_handles);
+
+ std::vector<char> buffer(num_bytes);
+ rv = ReadMessageRaw(handle.get(), buffer.data(), &num_bytes, nullptr,
+ &num_handles, MOJO_READ_MESSAGE_FLAG_NONE);
+ CHECK_EQ(MOJO_RESULT_OK, rv);
+ return std::string(buffer.data(), buffer.size());
+}
+
+class ThreadedRunner : public base::SimpleThread {
+ public:
+ explicit ThreadedRunner(const base::Closure& callback)
+ : SimpleThread("ThreadedRunner"), callback_(callback) {}
+ ~ThreadedRunner() override { Join(); }
+
+ void Run() override { callback_.Run(); }
+
+ private:
+ const base::Closure callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadedRunner);
+};
+
+TEST_F(WaitSetTest, Satisfied) {
+ WaitSet wait_set;
+ MessagePipe p;
+
+ const char kTestMessage1[] = "hello wake up";
+
+ // Watch only one handle and write to the other.
+
+ wait_set.AddHandle(p.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE);
+ WriteMessage(p.handle0, kTestMessage1);
+
+ size_t num_ready_handles = 2;
+ Handle ready_handles[2];
+ MojoResult ready_results[2] = {MOJO_RESULT_UNKNOWN, MOJO_RESULT_UNKNOWN};
+ HandleSignalsState hss[2];
+ wait_set.Wait(nullptr, &num_ready_handles, ready_handles, ready_results, hss);
+
+ EXPECT_EQ(1u, num_ready_handles);
+ EXPECT_EQ(p.handle1.get(), ready_handles[0]);
+ EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
+ EXPECT_TRUE(hss[0].readable() && hss[0].writable() && !hss[0].peer_closed());
+
+ wait_set.RemoveHandle(p.handle1.get());
+
+ // Now watch only the other handle and write to the first one.
+
+ wait_set.AddHandle(p.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
+ WriteMessage(p.handle1, kTestMessage1);
+
+ num_ready_handles = 2;
+ ready_results[0] = MOJO_RESULT_UNKNOWN;
+ ready_results[1] = MOJO_RESULT_UNKNOWN;
+ wait_set.Wait(nullptr, &num_ready_handles, ready_handles, ready_results, hss);
+
+ EXPECT_EQ(1u, num_ready_handles);
+ EXPECT_EQ(p.handle0.get(), ready_handles[0]);
+ EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
+ EXPECT_TRUE(hss[0].readable() && hss[0].writable() && !hss[0].peer_closed());
+
+ // Now wait on both of them.
+ wait_set.AddHandle(p.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE);
+
+ num_ready_handles = 2;
+ ready_results[0] = MOJO_RESULT_UNKNOWN;
+ ready_results[1] = MOJO_RESULT_UNKNOWN;
+ wait_set.Wait(nullptr, &num_ready_handles, ready_handles, ready_results, hss);
+ EXPECT_EQ(2u, num_ready_handles);
+ EXPECT_TRUE((ready_handles[0] == p.handle0.get() &&
+ ready_handles[1] == p.handle1.get()) ||
+ (ready_handles[0] == p.handle1.get() &&
+ ready_handles[1] == p.handle0.get()));
+ EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
+ EXPECT_EQ(MOJO_RESULT_OK, ready_results[1]);
+ EXPECT_TRUE(hss[0].readable() && hss[0].writable() && !hss[0].peer_closed());
+ EXPECT_TRUE(hss[1].readable() && hss[1].writable() && !hss[1].peer_closed());
+
+ // Wait on both again, but with only enough output space for one result.
+ num_ready_handles = 1;
+ ready_results[0] = MOJO_RESULT_UNKNOWN;
+ wait_set.Wait(nullptr, &num_ready_handles, ready_handles, ready_results, hss);
+ EXPECT_EQ(1u, num_ready_handles);
+ EXPECT_TRUE(ready_handles[0] == p.handle0.get() ||
+ ready_handles[0] == p.handle1.get());
+ EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
+
+ // Remove the ready handle from the set and wait one more time.
+ EXPECT_EQ(MOJO_RESULT_OK, wait_set.RemoveHandle(ready_handles[0]));
+
+ num_ready_handles = 1;
+ ready_results[0] = MOJO_RESULT_UNKNOWN;
+ wait_set.Wait(nullptr, &num_ready_handles, ready_handles, ready_results, hss);
+ EXPECT_EQ(1u, num_ready_handles);
+ EXPECT_TRUE(ready_handles[0] == p.handle0.get() ||
+ ready_handles[0] == p.handle1.get());
+ EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
+
+ EXPECT_EQ(MOJO_RESULT_OK, wait_set.RemoveHandle(ready_handles[0]));
+
+ // The wait set should be empty now. Nothing to wait on.
+ num_ready_handles = 2;
+ wait_set.Wait(nullptr, &num_ready_handles, ready_handles, ready_results);
+ EXPECT_EQ(0u, num_ready_handles);
+}
+
+TEST_F(WaitSetTest, Unsatisfiable) {
+ MessagePipe p, q;
+ WaitSet wait_set;
+
+ wait_set.AddHandle(q.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
+ wait_set.AddHandle(q.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE);
+ wait_set.AddHandle(p.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
+
+ size_t num_ready_handles = 2;
+ Handle ready_handles[2];
+ MojoResult ready_results[2] = {MOJO_RESULT_UNKNOWN, MOJO_RESULT_UNKNOWN};
+
+ p.handle1.reset();
+ wait_set.Wait(nullptr, &num_ready_handles, ready_handles, ready_results);
+ EXPECT_EQ(1u, num_ready_handles);
+ EXPECT_EQ(p.handle0.get(), ready_handles[0]);
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, ready_results[0]);
+}
+
+TEST_F(WaitSetTest, CloseWhileWaiting) {
+ MessagePipe p;
+ WaitSet wait_set;
+
+ wait_set.AddHandle(p.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
+
+ const Handle handle0_value = p.handle0.get();
+ ThreadedRunner close_after_delay(base::Bind(
+ [](ScopedMessagePipeHandle* handle) {
+ // Wait a little while, then close the handle.
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
+ handle->reset();
+ },
+ &p.handle0));
+ close_after_delay.Start();
+
+ size_t num_ready_handles = 2;
+ Handle ready_handles[2];
+ MojoResult ready_results[2] = {MOJO_RESULT_UNKNOWN, MOJO_RESULT_UNKNOWN};
+ wait_set.Wait(nullptr, &num_ready_handles, ready_handles, ready_results);
+ EXPECT_EQ(1u, num_ready_handles);
+ EXPECT_EQ(handle0_value, ready_handles[0]);
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, ready_results[0]);
+
+ EXPECT_EQ(MOJO_RESULT_NOT_FOUND, wait_set.RemoveHandle(handle0_value));
+}
+
+TEST_F(WaitSetTest, CloseBeforeWaiting) {
+ MessagePipe p;
+ WaitSet wait_set;
+
+ wait_set.AddHandle(p.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
+ wait_set.AddHandle(p.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE);
+
+ Handle handle0_value = p.handle0.get();
+ Handle handle1_value = p.handle1.get();
+
+ p.handle0.reset();
+ p.handle1.reset();
+
+ // Ensure that the WaitSet user is always made aware of all cancellations even
+ // if they happen while not waiting, or they have to be returned over the span
+ // of multiple Wait() calls due to insufficient output storage.
+
+ size_t num_ready_handles = 1;
+ Handle ready_handle;
+ MojoResult ready_result = MOJO_RESULT_UNKNOWN;
+ wait_set.Wait(nullptr, &num_ready_handles, &ready_handle, &ready_result);
+ EXPECT_EQ(1u, num_ready_handles);
+ EXPECT_TRUE(ready_handle == handle0_value || ready_handle == handle1_value);
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, ready_result);
+ EXPECT_EQ(MOJO_RESULT_NOT_FOUND, wait_set.RemoveHandle(handle0_value));
+
+ wait_set.Wait(nullptr, &num_ready_handles, &ready_handle, &ready_result);
+ EXPECT_EQ(1u, num_ready_handles);
+ EXPECT_TRUE(ready_handle == handle0_value || ready_handle == handle1_value);
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, ready_result);
+ EXPECT_EQ(MOJO_RESULT_NOT_FOUND, wait_set.RemoveHandle(handle0_value));
+
+ // Nothing more to wait on.
+ wait_set.Wait(nullptr, &num_ready_handles, &ready_handle, &ready_result);
+ EXPECT_EQ(0u, num_ready_handles);
+}
+
+TEST_F(WaitSetTest, SatisfiedThenUnsatisfied) {
+ MessagePipe p;
+ WaitSet wait_set;
+
+ wait_set.AddHandle(p.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
+ wait_set.AddHandle(p.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE);
+
+ const char kTestMessage1[] = "testing testing testing";
+ WriteMessage(p.handle0, kTestMessage1);
+
+ size_t num_ready_handles = 2;
+ Handle ready_handles[2];
+ MojoResult ready_results[2] = {MOJO_RESULT_UNKNOWN, MOJO_RESULT_UNKNOWN};
+ wait_set.Wait(nullptr, &num_ready_handles, ready_handles, ready_results);
+ EXPECT_EQ(1u, num_ready_handles);
+ EXPECT_EQ(p.handle1.get(), ready_handles[0]);
+ EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
+
+ EXPECT_EQ(kTestMessage1, ReadMessage(p.handle1));
+
+ ThreadedRunner write_after_delay(base::Bind(
+ [](ScopedMessagePipeHandle* handle) {
+ // Wait a little while, then write a message.
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
+ WriteMessage(*handle, "wakey wakey");
+ },
+ &p.handle1));
+ write_after_delay.Start();
+
+ num_ready_handles = 2;
+ wait_set.Wait(nullptr, &num_ready_handles, ready_handles, ready_results);
+ EXPECT_EQ(1u, num_ready_handles);
+ EXPECT_EQ(p.handle0.get(), ready_handles[0]);
+ EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
+}
+
+TEST_F(WaitSetTest, EventOnly) {
+ base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::SIGNALED);
+ WaitSet wait_set;
+ wait_set.AddEvent(&event);
+
+ base::WaitableEvent* ready_event = nullptr;
+ size_t num_ready_handles = 1;
+ Handle ready_handle;
+ MojoResult ready_result = MOJO_RESULT_UNKNOWN;
+ wait_set.Wait(&ready_event, &num_ready_handles, &ready_handle, &ready_result);
+ EXPECT_EQ(0u, num_ready_handles);
+ EXPECT_EQ(&event, ready_event);
+}
+
+TEST_F(WaitSetTest, EventAndHandle) {
+ const char kTestMessage[] = "hello hello";
+
+ MessagePipe p;
+ WriteMessage(p.handle0, kTestMessage);
+
+ base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+ WaitSet wait_set;
+ wait_set.AddHandle(p.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE);
+ wait_set.AddEvent(&event);
+
+ base::WaitableEvent* ready_event = nullptr;
+ size_t num_ready_handles = 1;
+ Handle ready_handle;
+ MojoResult ready_result = MOJO_RESULT_UNKNOWN;
+ wait_set.Wait(&ready_event, &num_ready_handles, &ready_handle, &ready_result);
+ EXPECT_EQ(1u, num_ready_handles);
+ EXPECT_EQ(nullptr, ready_event);
+ EXPECT_EQ(p.handle1.get(), ready_handle);
+ EXPECT_EQ(MOJO_RESULT_OK, ready_result);
+
+ EXPECT_EQ(kTestMessage, ReadMessage(p.handle1));
+
+ ThreadedRunner signal_after_delay(base::Bind(
+ [](base::WaitableEvent* event) {
+ // Wait a little while, then close the handle.
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
+ event->Signal();
+ },
+ &event));
+ signal_after_delay.Start();
+
+ wait_set.Wait(&ready_event, &num_ready_handles, &ready_handle, &ready_result);
+ EXPECT_EQ(0u, num_ready_handles);
+ EXPECT_EQ(&event, ready_event);
+}
+
+TEST_F(WaitSetTest, NoStarvation) {
+ const char kTestMessage[] = "wait for it";
+ const size_t kNumTestPipes = 50;
+ const size_t kNumTestEvents = 10;
+
+ // Create a bunch of handles and events which are always ready and add them
+ // to a shared WaitSet.
+
+ WaitSet wait_set;
+
+ MessagePipe pipes[kNumTestPipes];
+ for (size_t i = 0; i < kNumTestPipes; ++i) {
+ WriteMessage(pipes[i].handle0, kTestMessage);
+ Wait(pipes[i].handle1.get(), MOJO_HANDLE_SIGNAL_READABLE);
+
+ WriteMessage(pipes[i].handle1, kTestMessage);
+ Wait(pipes[i].handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
+
+ wait_set.AddHandle(pipes[i].handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
+ wait_set.AddHandle(pipes[i].handle1.get(), MOJO_HANDLE_SIGNAL_READABLE);
+ }
+
+ std::vector<std::unique_ptr<base::WaitableEvent>> events(kNumTestEvents);
+ for (auto& event_ptr : events) {
+ event_ptr = base::MakeUnique<base::WaitableEvent>(
+ base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ event_ptr->Signal();
+ wait_set.AddEvent(event_ptr.get());
+ }
+
+ // Now verify that all handle and event signals are deteceted within a finite
+ // number of consecutive Wait() calls. Do it a few times for good measure.
+ for (size_t i = 0; i < 3; ++i) {
+ std::set<base::WaitableEvent*> ready_events;
+ std::set<Handle> ready_handles;
+ while (ready_events.size() < kNumTestEvents ||
+ ready_handles.size() < kNumTestPipes * 2) {
+ base::WaitableEvent* ready_event = nullptr;
+ size_t num_ready_handles = 1;
+ Handle ready_handle;
+ MojoResult ready_result = MOJO_RESULT_UNKNOWN;
+ wait_set.Wait(&ready_event, &num_ready_handles, &ready_handle,
+ &ready_result);
+ if (ready_event)
+ ready_events.insert(ready_event);
+
+ if (num_ready_handles) {
+ EXPECT_EQ(1u, num_ready_handles);
+ EXPECT_EQ(MOJO_RESULT_OK, ready_result);
+ ready_handles.insert(ready_handle);
+ }
+ }
+ }
+}
+
+} // namespace
+} // namespace mojo
diff --git a/mojo/public/cpp/system/tests/wait_unittest.cc b/mojo/public/cpp/system/tests/wait_unittest.cc
new file mode 100644
index 0000000000..1d9d3c69bc
--- /dev/null
+++ b/mojo/public/cpp/system/tests/wait_unittest.cc
@@ -0,0 +1,321 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/system/wait.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/strings/string_piece.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/simple_thread.h"
+#include "base/time/time.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/handle_signals_state.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+using WaitTest = testing::Test;
+using WaitManyTest = testing::Test;
+
+void WriteMessage(const ScopedMessagePipeHandle& handle,
+ const base::StringPiece& message) {
+ MojoResult rv = WriteMessageRaw(handle.get(), message.data(),
+ static_cast<uint32_t>(message.size()),
+ nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE);
+ CHECK_EQ(MOJO_RESULT_OK, rv);
+}
+
+std::string ReadMessage(const ScopedMessagePipeHandle& handle) {
+ uint32_t num_bytes = 0;
+ uint32_t num_handles = 0;
+ MojoResult rv = ReadMessageRaw(handle.get(), nullptr, &num_bytes, nullptr,
+ &num_handles, MOJO_READ_MESSAGE_FLAG_NONE);
+ CHECK_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, rv);
+ CHECK_EQ(0u, num_handles);
+
+ std::vector<char> buffer(num_bytes);
+ rv = ReadMessageRaw(handle.get(), buffer.data(), &num_bytes, nullptr,
+ &num_handles, MOJO_READ_MESSAGE_FLAG_NONE);
+ CHECK_EQ(MOJO_RESULT_OK, rv);
+ return std::string(buffer.data(), buffer.size());
+}
+
+class ThreadedRunner : public base::SimpleThread {
+ public:
+ explicit ThreadedRunner(const base::Closure& callback)
+ : SimpleThread("ThreadedRunner"), callback_(callback) {}
+ ~ThreadedRunner() override { Join(); }
+
+ void Run() override { callback_.Run(); }
+
+ private:
+ const base::Closure callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadedRunner);
+};
+
+TEST_F(WaitTest, InvalidArguments) {
+ Handle invalid_handle;
+
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ Wait(invalid_handle, MOJO_HANDLE_SIGNAL_READABLE));
+
+ MessagePipe p;
+ Handle valid_handles[2] = {p.handle0.get(), p.handle1.get()};
+ Handle invalid_handles[2];
+ MojoHandleSignals signals[2] = {MOJO_HANDLE_SIGNAL_NONE,
+ MOJO_HANDLE_SIGNAL_NONE};
+ size_t result_index = 0;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ WaitMany(invalid_handles, signals, 2, &result_index));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ WaitMany(nullptr, signals, 2, &result_index));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ WaitMany(valid_handles, nullptr, 2, &result_index));
+}
+
+TEST_F(WaitTest, Basic) {
+ MessagePipe p;
+
+ // Write to one end of the pipe and wait on the other.
+ const char kTestMessage1[] = "how about a nice game of chess?";
+ WriteMessage(p.handle0, kTestMessage1);
+ EXPECT_EQ(MOJO_RESULT_OK, Wait(p.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE));
+
+ // And make sure we can also grab the handle signals state (with both the C
+ // and C++ library structs.)
+
+ MojoHandleSignalsState c_hss = {0, 0};
+ EXPECT_EQ(MOJO_RESULT_OK,
+ Wait(p.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE, &c_hss));
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+ c_hss.satisfied_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+ MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+ c_hss.satisfiable_signals);
+
+ HandleSignalsState hss;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ Wait(p.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE, &hss));
+ EXPECT_TRUE(hss.readable() && hss.writable() && !hss.peer_closed());
+ EXPECT_FALSE(hss.never_readable() || hss.never_writable() ||
+ hss.never_peer_closed());
+
+ // Now close the writing end and wait for peer closure.
+
+ p.handle0.reset();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ Wait(p.handle1.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss));
+
+ // Still readable as there's still a message queued. No longer writable as
+ // peer closure has been detected.
+ EXPECT_TRUE(hss.readable() && hss.peer_closed() && !hss.writable());
+ EXPECT_TRUE(hss.never_writable() && !hss.never_readable() &&
+ !hss.never_peer_closed());
+
+ // Read the message and wait for readable again. Waiting should fail since
+ // there are no more messages and the peer is closed.
+ EXPECT_EQ(kTestMessage1, ReadMessage(p.handle1));
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ Wait(p.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE, &hss));
+
+ // Sanity check the signals state again.
+ EXPECT_TRUE(hss.peer_closed() && !hss.readable() && !hss.writable());
+ EXPECT_TRUE(hss.never_readable() && hss.never_writable() &&
+ !hss.never_peer_closed());
+}
+
+TEST_F(WaitTest, DelayedWrite) {
+ MessagePipe p;
+
+ ThreadedRunner write_after_delay(base::Bind(
+ [](ScopedMessagePipeHandle* handle) {
+ // Wait a little while, then write a message.
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
+ WriteMessage(*handle, "wakey wakey");
+ },
+ &p.handle0));
+ write_after_delay.Start();
+
+ HandleSignalsState hss;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ Wait(p.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE, &hss));
+ EXPECT_TRUE(hss.readable() && hss.writable() && !hss.peer_closed());
+ EXPECT_TRUE(!hss.never_readable() && !hss.never_writable() &&
+ !hss.never_peer_closed());
+}
+
+TEST_F(WaitTest, DelayedPeerClosure) {
+ MessagePipe p;
+
+ ThreadedRunner close_after_delay(base::Bind(
+ [](ScopedMessagePipeHandle* handle) {
+ // Wait a little while, then close the handle.
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
+ handle->reset();
+ },
+ &p.handle0));
+ close_after_delay.Start();
+
+ HandleSignalsState hss;
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ Wait(p.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE, &hss));
+ EXPECT_TRUE(!hss.readable() && !hss.writable() && hss.peer_closed());
+ EXPECT_TRUE(hss.never_readable() && hss.never_writable() &&
+ !hss.never_peer_closed());
+}
+
+TEST_F(WaitTest, CloseWhileWaiting) {
+ MessagePipe p;
+ ThreadedRunner close_after_delay(base::Bind(
+ [](ScopedMessagePipeHandle* handle) {
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
+ handle->reset();
+ },
+ &p.handle0));
+ close_after_delay.Start();
+ EXPECT_EQ(MOJO_RESULT_CANCELLED,
+ Wait(p.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE));
+}
+
+TEST_F(WaitManyTest, Basic) {
+ MessagePipe p;
+
+ const char kTestMessage1[] = "hello";
+ WriteMessage(p.handle0, kTestMessage1);
+
+ // Wait for either handle to become readable. Wait twice, just to verify that
+ // we can use either the C or C++ signaling state structure for the last
+ // argument.
+
+ Handle handles[2] = {p.handle0.get(), p.handle1.get()};
+ MojoHandleSignals signals[2] = {MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_HANDLE_SIGNAL_READABLE};
+ size_t result_index = 0;
+ MojoHandleSignalsState c_hss[2];
+ HandleSignalsState hss[2];
+
+ EXPECT_EQ(MOJO_RESULT_OK,
+ WaitMany(handles, signals, 2, &result_index, c_hss));
+ EXPECT_EQ(1u, result_index);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, c_hss[0].satisfied_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+ MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+ c_hss[0].satisfiable_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+ c_hss[1].satisfied_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+ MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+ c_hss[1].satisfiable_signals);
+
+ EXPECT_EQ(MOJO_RESULT_OK, WaitMany(handles, signals, 2, &result_index, hss));
+ EXPECT_EQ(1u, result_index);
+ EXPECT_TRUE(!hss[0].readable() && hss[0].writable() && !hss[0].peer_closed());
+ EXPECT_TRUE(!hss[0].never_readable() && !hss[0].never_writable() &&
+ !hss[0].never_peer_closed());
+ EXPECT_TRUE(hss[1].readable() && hss[1].writable() && !hss[1].peer_closed());
+ EXPECT_TRUE(!hss[1].never_readable() && !hss[1].never_writable() &&
+ !hss[1].never_peer_closed());
+
+ // Close the writer and read the message. Try to wait again, and it should
+ // fail due to the conditions being unsatisfiable.
+
+ EXPECT_EQ(kTestMessage1, ReadMessage(p.handle1));
+ p.handle0.reset();
+
+ // handles[0] is invalid.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ WaitMany(handles, signals, 2, &result_index, hss));
+ handles[0] = handles[1];
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ WaitMany(handles, signals, 1, &result_index, hss));
+ EXPECT_EQ(0u, result_index);
+ EXPECT_TRUE(!hss[0].readable() && !hss[0].writable() && hss[0].peer_closed());
+ EXPECT_TRUE(hss[0].never_readable() && hss[0].never_writable() &&
+ !hss[0].never_peer_closed());
+}
+
+TEST_F(WaitManyTest, CloseWhileWaiting) {
+ MessagePipe p, q;
+
+ Handle handles[3] = {q.handle0.get(), q.handle1.get(), p.handle1.get()};
+ MojoHandleSignals signals[3] = {MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_HANDLE_SIGNAL_READABLE};
+
+ ThreadedRunner close_after_delay(base::Bind(
+ [](ScopedMessagePipeHandle* handle) {
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
+ handle->reset();
+ },
+ &p.handle1));
+ close_after_delay.Start();
+
+ size_t result_index = 0;
+ EXPECT_EQ(MOJO_RESULT_CANCELLED,
+ WaitMany(handles, signals, 3, &result_index));
+ EXPECT_EQ(2u, result_index);
+}
+
+TEST_F(WaitManyTest, DelayedWrite) {
+ MessagePipe p;
+
+ ThreadedRunner write_after_delay(base::Bind(
+ [](ScopedMessagePipeHandle* handle) {
+ // Wait a little while, then write a message.
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
+ WriteMessage(*handle, "wakey wakey");
+ },
+ &p.handle0));
+ write_after_delay.Start();
+
+ Handle handles[2] = {p.handle0.get(), p.handle1.get()};
+ MojoHandleSignals signals[2] = {MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_HANDLE_SIGNAL_READABLE};
+ size_t result_index = 0;
+ HandleSignalsState hss[2];
+ EXPECT_EQ(MOJO_RESULT_OK, WaitMany(handles, signals, 2, &result_index, hss));
+ EXPECT_EQ(1u, result_index);
+ EXPECT_TRUE(!hss[0].readable() && hss[0].writable() && !hss[0].peer_closed());
+ EXPECT_TRUE(!hss[0].never_readable() && !hss[0].never_writable() &&
+ !hss[0].never_peer_closed());
+ EXPECT_TRUE(hss[1].readable() && hss[1].writable() && !hss[1].peer_closed());
+ EXPECT_TRUE(!hss[1].never_readable() && !hss[1].never_writable() &&
+ !hss[1].never_peer_closed());
+}
+
+TEST_F(WaitManyTest, DelayedPeerClosure) {
+ MessagePipe p, q;
+
+ ThreadedRunner close_after_delay(base::Bind(
+ [](ScopedMessagePipeHandle* handle) {
+ // Wait a little while, then close the handle.
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
+ handle->reset();
+ },
+ &p.handle0));
+ close_after_delay.Start();
+
+ Handle handles[3] = {q.handle0.get(), q.handle1.get(), p.handle1.get()};
+ MojoHandleSignals signals[3] = {MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_HANDLE_SIGNAL_READABLE};
+ size_t result_index = 0;
+ HandleSignalsState hss[3];
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ WaitMany(handles, signals, 3, &result_index, hss));
+ EXPECT_EQ(2u, result_index);
+ EXPECT_TRUE(!hss[2].readable() && !hss[2].writable() && hss[2].peer_closed());
+ EXPECT_TRUE(hss[2].never_readable() && hss[2].never_writable() &&
+ !hss[2].never_peer_closed());
+}
+
+} // namespace
+} // namespace mojo
diff --git a/mojo/public/cpp/system/wait.cc b/mojo/public/cpp/system/wait.cc
new file mode 100644
index 0000000000..e4e124f25c
--- /dev/null
+++ b/mojo/public/cpp/system/wait.cc
@@ -0,0 +1,200 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/system/wait.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/waitable_event.h"
+#include "mojo/public/c/system/watcher.h"
+#include "mojo/public/cpp/system/watcher.h"
+
+namespace mojo {
+namespace {
+
+class WatchContext : public base::RefCountedThreadSafe<WatchContext> {
+ public:
+ WatchContext()
+ : event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ base::WaitableEvent::InitialState::NOT_SIGNALED) {}
+
+ base::WaitableEvent& event() { return event_; }
+ MojoResult wait_result() const { return wait_result_; }
+ MojoHandleSignalsState wait_state() const { return wait_state_; }
+ uintptr_t context_value() const { return reinterpret_cast<uintptr_t>(this); }
+
+ static void OnNotification(uintptr_t context_value,
+ MojoResult result,
+ MojoHandleSignalsState state,
+ MojoWatcherNotificationFlags flags) {
+ auto* context = reinterpret_cast<WatchContext*>(context_value);
+ context->Notify(result, state);
+ if (result == MOJO_RESULT_CANCELLED) {
+ // Balanced in Wait() or WaitMany().
+ context->Release();
+ }
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<WatchContext>;
+
+ ~WatchContext() {}
+
+ void Notify(MojoResult result, MojoHandleSignalsState state) {
+ if (wait_result_ == MOJO_RESULT_UNKNOWN) {
+ wait_result_ = result;
+ wait_state_ = state;
+ }
+ event_.Signal();
+ }
+
+ base::WaitableEvent event_;
+
+ // NOTE: Although these are modified in Notify() which may be called from any
+ // thread, Notify() is guaranteed to never run concurrently with itself.
+ // Furthermore, they are only modified once, before |event_| signals; so there
+ // is no need for a WatchContext user to synchronize access to these fields
+ // apart from waiting on |event()|.
+ MojoResult wait_result_ = MOJO_RESULT_UNKNOWN;
+ MojoHandleSignalsState wait_state_ = {0, 0};
+
+ DISALLOW_COPY_AND_ASSIGN(WatchContext);
+};
+
+} // namespace
+
+MojoResult Wait(Handle handle,
+ MojoHandleSignals signals,
+ MojoHandleSignalsState* signals_state) {
+ ScopedWatcherHandle watcher;
+ MojoResult rv = CreateWatcher(&WatchContext::OnNotification, &watcher);
+ DCHECK_EQ(MOJO_RESULT_OK, rv);
+
+ scoped_refptr<WatchContext> context = new WatchContext;
+
+ // Balanced in WatchContext::OnNotification if MojoWatch() is successful.
+ // Otherwise balanced immediately below.
+ context->AddRef();
+
+ rv = MojoWatch(watcher.get().value(), handle.value(), signals,
+ context->context_value());
+ if (rv == MOJO_RESULT_INVALID_ARGUMENT) {
+ // Balanced above.
+ context->Release();
+ return rv;
+ }
+ DCHECK_EQ(MOJO_RESULT_OK, rv);
+
+ uint32_t num_ready_contexts = 1;
+ uintptr_t ready_context;
+ MojoResult ready_result;
+ MojoHandleSignalsState ready_state;
+ rv = MojoArmWatcher(watcher.get().value(), &num_ready_contexts,
+ &ready_context, &ready_result, &ready_state);
+ if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
+ DCHECK_EQ(1u, num_ready_contexts);
+ if (signals_state)
+ *signals_state = ready_state;
+ return ready_result;
+ }
+
+ // Wait for the first notification only.
+ context->event().Wait();
+
+ ready_result = context->wait_result();
+ DCHECK_NE(MOJO_RESULT_UNKNOWN, ready_result);
+
+ if (signals_state)
+ *signals_state = context->wait_state();
+
+ return ready_result;
+}
+
+MojoResult WaitMany(const Handle* handles,
+ const MojoHandleSignals* signals,
+ size_t num_handles,
+ size_t* result_index,
+ MojoHandleSignalsState* signals_states) {
+ if (!handles || !signals)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ ScopedWatcherHandle watcher;
+ MojoResult rv = CreateWatcher(&WatchContext::OnNotification, &watcher);
+ DCHECK_EQ(MOJO_RESULT_OK, rv);
+
+ std::vector<scoped_refptr<WatchContext>> contexts(num_handles);
+ std::vector<base::WaitableEvent*> events(num_handles);
+ for (size_t i = 0; i < num_handles; ++i) {
+ contexts[i] = new WatchContext();
+
+ // Balanced in WatchContext::OnNotification if MojoWatch() is successful.
+ // Otherwise balanced immediately below.
+ contexts[i]->AddRef();
+
+ MojoResult rv = MojoWatch(watcher.get().value(), handles[i].value(),
+ signals[i], contexts[i]->context_value());
+ if (rv == MOJO_RESULT_INVALID_ARGUMENT) {
+ if (result_index)
+ *result_index = i;
+
+ // Balanced above.
+ contexts[i]->Release();
+
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ }
+
+ events[i] = &contexts[i]->event();
+ }
+
+ uint32_t num_ready_contexts = 1;
+ uintptr_t ready_context = 0;
+ MojoResult ready_result = MOJO_RESULT_UNKNOWN;
+ MojoHandleSignalsState ready_state{0, 0};
+ rv = MojoArmWatcher(watcher.get().value(), &num_ready_contexts,
+ &ready_context, &ready_result, &ready_state);
+
+ size_t index = num_handles;
+ if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
+ DCHECK_EQ(1u, num_ready_contexts);
+
+ // Most commonly we only watch a small number of handles. Just scan for
+ // the right index.
+ for (size_t i = 0; i < num_handles; ++i) {
+ if (contexts[i]->context_value() == ready_context) {
+ index = i;
+ break;
+ }
+ }
+ } else {
+ DCHECK_EQ(MOJO_RESULT_OK, rv);
+
+ // Wait for one of the contexts to signal. First one wins.
+ index = base::WaitableEvent::WaitMany(events.data(), events.size());
+ ready_result = contexts[index]->wait_result();
+ ready_state = contexts[index]->wait_state();
+ }
+
+ DCHECK_NE(MOJO_RESULT_UNKNOWN, ready_result);
+ DCHECK_LT(index, num_handles);
+
+ if (result_index)
+ *result_index = index;
+
+ if (signals_states) {
+ for (size_t i = 0; i < num_handles; ++i) {
+ if (i == index) {
+ signals_states[i] = ready_state;
+ } else {
+ signals_states[i] = handles[i].QuerySignalsState();
+ }
+ }
+ }
+
+ return ready_result;
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/system/wait.h b/mojo/public/cpp/system/wait.h
new file mode 100644
index 0000000000..808e44fc25
--- /dev/null
+++ b/mojo/public/cpp/system/wait.h
@@ -0,0 +1,75 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_WAIT_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_WAIT_H_
+
+#include <stddef.h>
+
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/system_export.h"
+
+namespace mojo {
+
+// Blocks the calling thread, waiting for one or more signals in |signals| to be
+// become satisfied -- or for all of them to become unsatisfiable -- on the
+// given Handle.
+//
+// If |signals_state| is non-null, |handle| is valid, the wait is not cancelled
+// (see return values below), the last known signaling state of |handle| is
+// written to |*signals_state| before returning.
+//
+// Return values:
+// |MOJO_RESULT_OK| if one or more signals in |signals| has been raised on
+// |handle| .
+// |MOJO_RESULT_FAILED_PRECONDITION| if the state of |handle| changes such
+// that no signals in |signals| can ever be raised again.
+// |MOJO_RESULT_INVALID_ARGUMENT| if |handle| is not a valid handle.
+// |MOJO_RESULT_CANCELLED| if the wait was cancelled because |handle| was
+// closed by some other thread while waiting.
+MOJO_CPP_SYSTEM_EXPORT MojoResult
+Wait(Handle handle,
+ MojoHandleSignals signals,
+ MojoHandleSignalsState* signals_state = nullptr);
+
+// Waits on |handles[0]|, ..., |handles[num_handles-1]| until:
+// - At least one handle satisfies a signal indicated in its respective
+// |signals[0]|, ..., |signals[num_handles-1]|.
+// - It becomes known that no signal in some |signals[i]| will ever be
+// satisfied.
+//
+// This means that |WaitMany()| behaves as if |Wait()| were called on each
+// handle/signals pair simultaneously, completing when the first |Wait()| would
+// complete.
+//
+// If |signals_states| is non-null, all other arguments are valid, and the wait
+// is not cancelled (see return values below), the last known signaling state of
+// each Handle |handles[i]| is written to its corresponding entry in
+// |signals_states[i]| before returning.
+//
+// Returns values:
+// |MOJO_RESULT_OK| if one of the Handles in |handles| had one or more of its
+// correpsonding signals satisfied. |*result_index| contains the index
+// of the Handle in question if |result_index| is non-null.
+// |MOJO_RESULT_FAILED_PRECONDITION| if one of the Handles in |handles|
+// changes state such that its corresponding signals become permanently
+// unsatisfiable. |*result_index| contains the index of the handle in
+// question if |result_index| is non-null.
+// |MOJO_RESULT_INVALID_ARGUMENT| if any Handle in |handles| is invalid,
+// or if either |handles| or |signals| is null.
+// |MOJO_RESULT_CANCELLED| if the wait was cancelled because a handle in
+// |handles| was closed by some other thread while waiting.
+// |*result_index| contains the index of the closed Handle if
+// |result_index| is non-null.
+MOJO_CPP_SYSTEM_EXPORT MojoResult
+WaitMany(const Handle* handles,
+ const MojoHandleSignals* signals,
+ size_t num_handles,
+ size_t* result_index = nullptr,
+ MojoHandleSignalsState* signals_states = nullptr);
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_WAIT_H_
diff --git a/mojo/public/cpp/system/wait_set.cc b/mojo/public/cpp/system/wait_set.cc
new file mode 100644
index 0000000000..1728f81b95
--- /dev/null
+++ b/mojo/public/cpp/system/wait_set.cc
@@ -0,0 +1,371 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/system/wait_set.h"
+
+#include <algorithm>
+#include <limits>
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/containers/stack_container.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "mojo/public/cpp/system/watcher.h"
+
+namespace mojo {
+
+class WaitSet::State : public base::RefCountedThreadSafe<State> {
+ public:
+ State()
+ : handle_event_(base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED) {
+ MojoResult rv = CreateWatcher(&Context::OnNotification, &watcher_handle_);
+ DCHECK_EQ(MOJO_RESULT_OK, rv);
+ }
+
+ void ShutDown() {
+ // NOTE: This may immediately invoke Notify for every context.
+ watcher_handle_.reset();
+ }
+
+ MojoResult AddEvent(base::WaitableEvent* event) {
+ auto result = user_events_.insert(event);
+ if (result.second)
+ return MOJO_RESULT_OK;
+ return MOJO_RESULT_ALREADY_EXISTS;
+ }
+
+ MojoResult RemoveEvent(base::WaitableEvent* event) {
+ auto it = user_events_.find(event);
+ if (it == user_events_.end())
+ return MOJO_RESULT_NOT_FOUND;
+ user_events_.erase(it);
+ return MOJO_RESULT_OK;
+ }
+
+ MojoResult AddHandle(Handle handle, MojoHandleSignals signals) {
+ DCHECK(watcher_handle_.is_valid());
+
+ scoped_refptr<Context> context = new Context(this, handle);
+
+ {
+ base::AutoLock lock(lock_);
+
+ if (handle_to_context_.count(handle))
+ return MOJO_RESULT_ALREADY_EXISTS;
+ DCHECK(!contexts_.count(context->context_value()));
+
+ handle_to_context_[handle] = context;
+ contexts_[context->context_value()] = context;
+ }
+
+ // Balanced in State::Notify() with MOJO_RESULT_CANCELLED if
+ // MojoWatch() succeeds. Otherwise balanced immediately below.
+ context->AddRef();
+
+ // This can notify immediately if the watcher is already armed. Don't hold
+ // |lock_| while calling it.
+ MojoResult rv = MojoWatch(watcher_handle_.get().value(), handle.value(),
+ signals, context->context_value());
+ if (rv == MOJO_RESULT_INVALID_ARGUMENT) {
+ base::AutoLock lock(lock_);
+ handle_to_context_.erase(handle);
+ contexts_.erase(context->context_value());
+
+ // Balanced above.
+ context->Release();
+ return rv;
+ }
+ DCHECK_EQ(MOJO_RESULT_OK, rv);
+
+ return rv;
+ }
+
+ MojoResult RemoveHandle(Handle handle) {
+ DCHECK(watcher_handle_.is_valid());
+
+ scoped_refptr<Context> context;
+ {
+ base::AutoLock lock(lock_);
+ auto it = handle_to_context_.find(handle);
+ if (it == handle_to_context_.end())
+ return MOJO_RESULT_NOT_FOUND;
+
+ context = std::move(it->second);
+ handle_to_context_.erase(it);
+
+ // Ensure that we never return this handle as a ready result again. Note
+ // that it's removal from |handle_to_context_| above ensures it will never
+ // be added back to this map.
+ ready_handles_.erase(handle);
+ }
+
+ // NOTE: This may enter the notification callback immediately, so don't hold
+ // |lock_| while calling it.
+ MojoResult rv = MojoCancelWatch(watcher_handle_.get().value(),
+ context->context_value());
+
+ // We don't really care whether or not this succeeds. In either case, the
+ // context was or will imminently be cancelled and moved from |contexts_|
+ // to |cancelled_contexts_|.
+ DCHECK(rv == MOJO_RESULT_OK || rv == MOJO_RESULT_NOT_FOUND);
+
+ {
+ // Always clear |cancelled_contexts_| in case it's accumulated any more
+ // entries since the last time we ran.
+ base::AutoLock lock(lock_);
+ cancelled_contexts_.clear();
+ }
+
+ return rv;
+ }
+
+ void Wait(base::WaitableEvent** ready_event,
+ size_t* num_ready_handles,
+ Handle* ready_handles,
+ MojoResult* ready_results,
+ MojoHandleSignalsState* signals_states) {
+ DCHECK(watcher_handle_.is_valid());
+ DCHECK(num_ready_handles);
+ DCHECK(ready_handles);
+ DCHECK(ready_results);
+ {
+ base::AutoLock lock(lock_);
+ if (ready_handles_.empty()) {
+ // No handles are currently in the ready set. Make sure the event is
+ // reset and try to arm the watcher.
+ handle_event_.Reset();
+
+ DCHECK_LE(*num_ready_handles, std::numeric_limits<uint32_t>::max());
+ uint32_t num_ready_contexts = static_cast<uint32_t>(*num_ready_handles);
+
+ base::StackVector<uintptr_t, 4> ready_contexts;
+ ready_contexts.container().resize(num_ready_contexts);
+ base::StackVector<MojoHandleSignalsState, 4> ready_states;
+ MojoHandleSignalsState* out_states = signals_states;
+ if (!out_states) {
+ // If the caller didn't provide a buffer for signal states, we provide
+ // our own locally. MojoArmWatcher() requires one if we want to handle
+ // arming failure properly.
+ ready_states.container().resize(num_ready_contexts);
+ out_states = ready_states.container().data();
+ }
+ MojoResult rv = MojoArmWatcher(
+ watcher_handle_.get().value(), &num_ready_contexts,
+ ready_contexts.container().data(), ready_results, out_states);
+
+ if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
+ // Simulate the handles becoming ready. We do this in lieu of
+ // returning the results immediately so as to avoid potentially
+ // starving user events. i.e., we always want to call WaitMany()
+ // below.
+ handle_event_.Signal();
+ for (size_t i = 0; i < num_ready_contexts; ++i) {
+ auto it = contexts_.find(ready_contexts.container()[i]);
+ DCHECK(it != contexts_.end());
+ ready_handles_[it->second->handle()] = {ready_results[i],
+ out_states[i]};
+ }
+ } else if (rv == MOJO_RESULT_NOT_FOUND) {
+ // Nothing to watch. If there are no user events, always signal to
+ // avoid deadlock.
+ if (user_events_.empty())
+ handle_event_.Signal();
+ } else {
+ // Watcher must be armed now. No need to manually signal.
+ DCHECK_EQ(MOJO_RESULT_OK, rv);
+ }
+ }
+ }
+
+ // Build a local contiguous array of events to wait on. These are rotated
+ // across Wait() calls to avoid starvation, by virtue of the fact that
+ // WaitMany guarantees left-to-right priority when multiple events are
+ // signaled.
+
+ base::StackVector<base::WaitableEvent*, 4> events;
+ events.container().resize(user_events_.size() + 1);
+ if (waitable_index_shift_ > user_events_.size())
+ waitable_index_shift_ = 0;
+
+ size_t dest_index = waitable_index_shift_++;
+ events.container()[dest_index] = &handle_event_;
+ for (auto* e : user_events_) {
+ dest_index = (dest_index + 1) % events.container().size();
+ events.container()[dest_index] = e;
+ }
+
+ size_t index = base::WaitableEvent::WaitMany(events.container().data(),
+ events.container().size());
+ base::AutoLock lock(lock_);
+
+ // Pop as many handles as we can out of the ready set and return them. Note
+ // that we do this regardless of which event signaled, as there may be
+ // ready handles in any case and they may be interesting to the caller.
+ *num_ready_handles = std::min(*num_ready_handles, ready_handles_.size());
+ for (size_t i = 0; i < *num_ready_handles; ++i) {
+ auto it = ready_handles_.begin();
+ ready_handles[i] = it->first;
+ ready_results[i] = it->second.result;
+ if (signals_states)
+ signals_states[i] = it->second.signals_state;
+ ready_handles_.erase(it);
+ }
+
+ // If the caller cares, let them know which user event unblocked us, if any.
+ if (ready_event) {
+ if (events.container()[index] == &handle_event_)
+ *ready_event = nullptr;
+ else
+ *ready_event = events.container()[index];
+ }
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<State>;
+
+ class Context : public base::RefCountedThreadSafe<Context> {
+ public:
+ Context(scoped_refptr<State> state, Handle handle)
+ : state_(state), handle_(handle) {}
+
+ Handle handle() const { return handle_; }
+
+ uintptr_t context_value() const {
+ return reinterpret_cast<uintptr_t>(this);
+ }
+
+ static void OnNotification(uintptr_t context,
+ MojoResult result,
+ MojoHandleSignalsState signals_state,
+ MojoWatcherNotificationFlags flags) {
+ reinterpret_cast<Context*>(context)->Notify(result, signals_state);
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<Context>;
+
+ ~Context() {}
+
+ void Notify(MojoResult result, MojoHandleSignalsState signals_state) {
+ state_->Notify(handle_, result, signals_state, this);
+ }
+
+ const scoped_refptr<State> state_;
+ const Handle handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(Context);
+ };
+
+ ~State() {}
+
+ void Notify(Handle handle,
+ MojoResult result,
+ MojoHandleSignalsState signals_state,
+ Context* context) {
+ base::AutoLock lock(lock_);
+
+ // This could be a cancellation notification following an explicit
+ // RemoveHandle(), in which case we really don't care and don't want to
+ // add it to the ready set. Only update and signal if that's not the case.
+ if (!handle_to_context_.count(handle)) {
+ DCHECK_EQ(MOJO_RESULT_CANCELLED, result);
+ } else {
+ ready_handles_[handle] = {result, signals_state};
+ handle_event_.Signal();
+ }
+
+ // Whether it's an implicit or explicit cancellation, erase from |contexts_|
+ // and append to |cancelled_contexts_|.
+ if (result == MOJO_RESULT_CANCELLED) {
+ contexts_.erase(context->context_value());
+ handle_to_context_.erase(handle);
+
+ // NOTE: We retain a context ref in |cancelled_contexts_| to ensure that
+ // this Context's heap address is not reused too soon. For example, it
+ // would otherwise be possible for the user to call AddHandle() from the
+ // WaitSet's thread immediately after this notification has fired on
+ // another thread, potentially reusing the same heap address for the newly
+ // added Context; and then they may call RemoveHandle() for this handle
+ // (not knowing its context has just been implicitly cancelled) and
+ // cause the new Context to be incorrectly removed from |contexts_|.
+ //
+ // This vector is cleared on the WaitSet's own thread every time
+ // RemoveHandle is called.
+ cancelled_contexts_.emplace_back(make_scoped_refptr(context));
+
+ // Balanced in State::AddHandle().
+ context->Release();
+ }
+ }
+
+ struct ReadyState {
+ ReadyState() = default;
+ ReadyState(MojoResult result, MojoHandleSignalsState signals_state)
+ : result(result), signals_state(signals_state) {}
+ ~ReadyState() = default;
+
+ MojoResult result = MOJO_RESULT_UNKNOWN;
+ MojoHandleSignalsState signals_state = {0, 0};
+ };
+
+ // Not guarded by lock. Must only be accessed from the WaitSet's owning
+ // thread.
+ ScopedWatcherHandle watcher_handle_;
+
+ base::Lock lock_;
+ std::map<uintptr_t, scoped_refptr<Context>> contexts_;
+ std::map<Handle, scoped_refptr<Context>> handle_to_context_;
+ std::map<Handle, ReadyState> ready_handles_;
+ std::vector<scoped_refptr<Context>> cancelled_contexts_;
+ std::set<base::WaitableEvent*> user_events_;
+
+ // Event signaled any time a handle notification is received.
+ base::WaitableEvent handle_event_;
+
+ // Offset by which to rotate the current set of waitable objects. This is used
+ // to guard against event starvation, as base::WaitableEvent::WaitMany gives
+ // preference to events in left-to-right order.
+ size_t waitable_index_shift_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(State);
+};
+
+WaitSet::WaitSet() : state_(new State) {}
+
+WaitSet::~WaitSet() {
+ state_->ShutDown();
+}
+
+MojoResult WaitSet::AddEvent(base::WaitableEvent* event) {
+ return state_->AddEvent(event);
+}
+
+MojoResult WaitSet::RemoveEvent(base::WaitableEvent* event) {
+ return state_->RemoveEvent(event);
+}
+
+MojoResult WaitSet::AddHandle(Handle handle, MojoHandleSignals signals) {
+ return state_->AddHandle(handle, signals);
+}
+
+MojoResult WaitSet::RemoveHandle(Handle handle) {
+ return state_->RemoveHandle(handle);
+}
+
+void WaitSet::Wait(base::WaitableEvent** ready_event,
+ size_t* num_ready_handles,
+ Handle* ready_handles,
+ MojoResult* ready_results,
+ MojoHandleSignalsState* signals_states) {
+ state_->Wait(ready_event, num_ready_handles, ready_handles, ready_results,
+ signals_states);
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/system/wait_set.h b/mojo/public/cpp/system/wait_set.h
new file mode 100644
index 0000000000..5047a86a48
--- /dev/null
+++ b/mojo/public/cpp/system/wait_set.h
@@ -0,0 +1,124 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_WAIT_SET_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_WAIT_SET_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/system_export.h"
+
+namespace base {
+class WaitableEvent;
+}
+
+namespace mojo {
+
+// WaitSet provides an efficient means of blocking a thread on any number of
+// events and Mojo handle state changes.
+//
+// Unlike WaitMany(), which incurs some extra setup cost for every call, a
+// WaitSet maintains some persistent accounting of the handles added or removed
+// from the set. A blocking wait operation (see the Wait() method below) can
+// then be performed multiple times for the same set of events and handles with
+// minimal additional setup per call.
+//
+// WaitSet is NOT thread-safe, so naturally handles and events may not be added
+// to or removed from the set while waiting.
+class MOJO_CPP_SYSTEM_EXPORT WaitSet {
+ public:
+ WaitSet();
+ ~WaitSet();
+
+ // Adds |event| to the set of events to wait on. If successful, any future
+ // Wait() on this WaitSet will wake up if the event is signaled.
+ //
+ // |event| is not owned.
+ //
+ // Return values:
+ // |MOJO_RESULT_OK| if |event| has been successfully added.
+ // |MOJO_RESULT_ALREADY_EXISTS| if |event| is already in this WaitSet.
+ MojoResult AddEvent(base::WaitableEvent* event);
+
+ // Removes |event| from the set of events to wait on.
+ //
+ // Return values:
+ // |MOJO_RESULT_OK| if |event| has been successfully added.
+ // |MOJO_RESULT_NOT_FOUND| if |event| was not in the set.
+ MojoResult RemoveEvent(base::WaitableEvent* event);
+
+ // Adds |handle| to the set of handles to wait on. If successful, any future
+ // Wait() on this WaitSet will wake up in the event that one or more signals
+ // in |signals| becomes satisfied on |handle| or all of them become
+ // permanently unsatisfiable.
+ //
+ // Return values:
+ // |MOJO_RESULT_OK| if |handle| has been successfully added.
+ // |MOJO_RESULT_ALREADY_EXISTS| if |handle| is already in this WaitSet.
+ // |MOJO_RESULT_INVALID_ARGUMENT| if |handle| is not a valid handle.
+ MojoResult AddHandle(Handle handle, MojoHandleSignals signals);
+
+ // Removes |handle| from the set of handles to wait on. Future calls to
+ // Wait() will be unaffected by the state of this handle.
+ //
+ // Return values:
+ // |MOJO_RESULT_OK| if |handle| has been successfully removed.
+ // |MOJO_RESULT_NOT_FOUND| if |handle| was not in the set.
+ MojoResult RemoveHandle(Handle handle);
+
+ // Waits on the current set of handles, waking up when one more of them meets
+ // the signaling conditions which were specified when they were added via
+ // AddHandle() above.
+ //
+ // |*num_ready_handles| on input must specify the number of entries available
+ // for output storage in |ready_handles| and |ready_result| (which must both
+ // be non-null). If |signals_states| is non-null it must also point to enough
+ // storage for |*num_ready_handles| MojoHandleSignalsState structures.
+ //
+ // Upon return, |*num_ready_handles| will contain the total number of handles
+ // whose information is stored in the given output buffers.
+ //
+ // If |ready_event| is non-null and the Wait() was unblocked by a user event
+ // signaling, the address of the event which signaled will be placed in
+ // |*ready_event|. Note that this is not necessarily exclusive to one or more
+ // handles also being ready. If |ready_event| is non-null and no user event
+ // was signaled for this Wait(), |*ready_event| will be null upon return.
+ //
+ // Every entry in |ready_handles| on output corresponds to one of the handles
+ // whose signaling state termianted the Wait() operation. Every corresponding
+ // entry in |ready_results| indicates the status of a ready handle according
+ // to the following result codes:
+ // |MOJO_RESULT_OK| one or more signals for the handle has been satisfied.
+ // |MOJO_RESULT_FAILED_PRECONDITION| all of the signals for the handle have
+ // become permanently unsatisfiable.
+ // |MOJO_RESULT_CANCELLED| if the handle has been closed from another
+ // thread. NOTE: It is important to recognize that this means the
+ // corresponding value in |ready_handles| is either invalid, or valid
+ // but referring to a different handle (i.e. has already been reused) by
+ // the time Wait() returns. The handle in question is automatically
+ // removed from the WaitSet.
+ void Wait(base::WaitableEvent** ready_event,
+ size_t* num_ready_handles,
+ Handle* ready_handles,
+ MojoResult* ready_results,
+ MojoHandleSignalsState* signals_states = nullptr);
+
+ private:
+ class State;
+
+ // Thread-safe state associated with this WaitSet. Used to aggregate
+ // notifications from watched handles.
+ scoped_refptr<State> state_;
+
+ DISALLOW_COPY_AND_ASSIGN(WaitSet);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_WAIT_SET_H_
diff --git a/mojo/public/cpp/system/watcher.cc b/mojo/public/cpp/system/watcher.cc
new file mode 100644
index 0000000000..0c62ba8e20
--- /dev/null
+++ b/mojo/public/cpp/system/watcher.cc
@@ -0,0 +1,20 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/system/watcher.h"
+
+#include "mojo/public/c/system/functions.h"
+
+namespace mojo {
+
+MojoResult CreateWatcher(MojoWatcherCallback callback,
+ ScopedWatcherHandle* watcher_handle) {
+ MojoHandle handle;
+ MojoResult rv = MojoCreateWatcher(callback, &handle);
+ if (rv == MOJO_RESULT_OK)
+ watcher_handle->reset(WatcherHandle(handle));
+ return rv;
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/system/watcher.h b/mojo/public/cpp/system/watcher.h
new file mode 100644
index 0000000000..d0a257814d
--- /dev/null
+++ b/mojo/public/cpp/system/watcher.h
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_WATCHER_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_WATCHER_H_
+
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/c/system/watcher.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/system_export.h"
+
+namespace mojo {
+
+// A strongly-typed representation of a |MojoHandle| for a watcher.
+class WatcherHandle : public Handle {
+ public:
+ WatcherHandle() = default;
+ explicit WatcherHandle(MojoHandle value) : Handle(value) {}
+
+ // Copying and assignment allowed.
+};
+
+static_assert(sizeof(WatcherHandle) == sizeof(Handle),
+ "Bad size for C++ WatcherHandle");
+
+typedef ScopedHandleBase<WatcherHandle> ScopedWatcherHandle;
+static_assert(sizeof(ScopedWatcherHandle) == sizeof(WatcherHandle),
+ "Bad size for C++ ScopedWatcherHandle");
+
+MOJO_CPP_SYSTEM_EXPORT MojoResult
+CreateWatcher(MojoWatcherCallback callback,
+ ScopedWatcherHandle* watcher_handle);
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_WATCHER_H_