Add thunks for wait set operations, together with public tests.

R=vardhan@google.com
BUG=#350

Review URL: https://codereview.chromium.org/2106433002 .
diff --git a/mojo/edk/embedder/entrypoints.cc b/mojo/edk/embedder/entrypoints.cc
index c636e44..7f29a5c 100644
--- a/mojo/edk/embedder/entrypoints.cc
+++ b/mojo/edk/embedder/entrypoints.cc
@@ -218,4 +218,33 @@
   return g_core->UnmapBuffer(MakeUserPointer(buffer));
 }
 
+MojoResult MojoCreateWaitSet(const struct MojoCreateWaitSetOptions* options,
+                             MojoHandle* handle) {
+  return g_core->CreateWaitSet(MakeUserPointer(options),
+                               MakeUserPointer(handle));
+}
+
+MojoResult MojoWaitSetAdd(MojoHandle wait_set_handle,
+                          MojoHandle handle,
+                          MojoHandleSignals signals,
+                          uint64_t cookie,
+                          const struct MojoWaitSetAddOptions* options) {
+  return g_core->WaitSetAdd(wait_set_handle, handle, signals, cookie,
+                            MakeUserPointer(options));
+}
+
+MojoResult MojoWaitSetRemove(MojoHandle wait_set_handle, uint64_t cookie) {
+  return g_core->WaitSetRemove(wait_set_handle, cookie);
+}
+
+MojoResult MojoWaitSetWait(MojoHandle wait_set_handle,
+                           MojoDeadline deadline,
+                           uint32_t* num_results,
+                           struct MojoWaitSetResult* results,
+                           uint32_t* max_results) {
+  return g_core->WaitSetWait(
+      wait_set_handle, deadline, MakeUserPointer(num_results),
+      MakeUserPointer(results), MakeUserPointer(max_results));
+}
+
 }  // extern "C"
diff --git a/mojo/nacl/nonsfi/irt_mojo_nonsfi.cc b/mojo/nacl/nonsfi/irt_mojo_nonsfi.cc
index 33d7269..baad030 100644
--- a/mojo/nacl/nonsfi/irt_mojo_nonsfi.cc
+++ b/mojo/nacl/nonsfi/irt_mojo_nonsfi.cc
@@ -49,6 +49,10 @@
     MojoGetBufferInformation,
     MojoMapBuffer,
     MojoUnmapBuffer,
+    MojoCreateWaitSet,
+    MojoWaitSetAdd,
+    MojoWaitSetRemove,
+    MojoWaitSetWait,
 };
 
 const struct nacl_irt_mgl kIrtMGL = {
diff --git a/mojo/public/c/system/tests/BUILD.gn b/mojo/public/c/system/tests/BUILD.gn
index 95c8f4e..6703571 100644
--- a/mojo/public/c/system/tests/BUILD.gn
+++ b/mojo/public/c/system/tests/BUILD.gn
@@ -21,6 +21,7 @@
     "message_pipe_unittest.cc",
     "result_unittest.cc",
     "time_unittest.cc",
+    "wait_set_unittest.cc",
     "wait_unittest.cc",
   ]
 
diff --git a/mojo/public/c/system/tests/buffer_unittest.cc b/mojo/public/c/system/tests/buffer_unittest.cc
index 6e32f2c..9d29dc9 100644
--- a/mojo/public/c/system/tests/buffer_unittest.cc
+++ b/mojo/public/c/system/tests/buffer_unittest.cc
@@ -6,6 +6,7 @@
 // mojo/public/c/system/buffer.h).
 
 #include "mojo/public/c/system/buffer.h"
+
 #include "mojo/public/c/system/handle.h"
 #include "mojo/public/c/system/result.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/mojo/public/c/system/tests/data_pipe_unittest.cc b/mojo/public/c/system/tests/data_pipe_unittest.cc
index c319c36..fb89e31 100644
--- a/mojo/public/c/system/tests/data_pipe_unittest.cc
+++ b/mojo/public/c/system/tests/data_pipe_unittest.cc
@@ -5,9 +5,10 @@
 // This file tests the C data pipe API (the functions declared in
 // mojo/public/c/system/data_pipe.h).
 
+#include "mojo/public/c/system/data_pipe.h"
+
 #include <string.h>
 
-#include "mojo/public/c/system/data_pipe.h"
 #include "mojo/public/c/system/handle.h"
 #include "mojo/public/c/system/result.h"
 #include "mojo/public/c/system/wait.h"
diff --git a/mojo/public/c/system/tests/handle_unittest.cc b/mojo/public/c/system/tests/handle_unittest.cc
index 0de5748..22098ea 100644
--- a/mojo/public/c/system/tests/handle_unittest.cc
+++ b/mojo/public/c/system/tests/handle_unittest.cc
@@ -8,6 +8,7 @@
 // handles.
 
 #include "mojo/public/c/system/handle.h"
+
 #include "mojo/public/c/system/message_pipe.h"
 #include "mojo/public/c/system/result.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/mojo/public/c/system/tests/message_pipe_unittest.cc b/mojo/public/c/system/tests/message_pipe_unittest.cc
index 9f6166f..2c61f9f 100644
--- a/mojo/public/c/system/tests/message_pipe_unittest.cc
+++ b/mojo/public/c/system/tests/message_pipe_unittest.cc
@@ -5,9 +5,10 @@
 // This file tests the C message pipe API (the functions declared in
 // mojo/public/c/system/message_pipe.h).
 
+#include "mojo/public/c/system/message_pipe.h"
+
 #include "mojo/public/c/system/buffer.h"
 #include "mojo/public/c/system/handle.h"
-#include "mojo/public/c/system/message_pipe.h"
 #include "mojo/public/c/system/result.h"
 #include "mojo/public/c/system/wait.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/mojo/public/c/system/tests/result_unittest.cc b/mojo/public/c/system/tests/result_unittest.cc
index fca6f04..1aa7934 100644
--- a/mojo/public/c/system/tests/result_unittest.cc
+++ b/mojo/public/c/system/tests/result_unittest.cc
@@ -6,6 +6,7 @@
 // mojo/public/c/system/result.h).
 
 #include "mojo/public/c/system/result.h"
+
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
diff --git a/mojo/public/c/system/tests/time_unittest.cc b/mojo/public/c/system/tests/time_unittest.cc
index cbf2c61..4a6467e 100644
--- a/mojo/public/c/system/tests/time_unittest.cc
+++ b/mojo/public/c/system/tests/time_unittest.cc
@@ -6,6 +6,7 @@
 // mojo/public/c/system/time.h).
 
 #include "mojo/public/c/system/time.h"
+
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
diff --git a/mojo/public/c/system/tests/wait_set_unittest.cc b/mojo/public/c/system/tests/wait_set_unittest.cc
new file mode 100644
index 0000000..91ca6dc
--- /dev/null
+++ b/mojo/public/c/system/tests/wait_set_unittest.cc
@@ -0,0 +1,390 @@
+// 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 tests the C wait set API (the functions declared in
+// mojo/public/c/system/wait_set.h).
+
+#include "mojo/public/c/system/wait_set.h"
+
+#include "mojo/public/c/system/handle.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/c/system/result.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const MojoHandleRights kDefaultWaitSetHandleRights =
+    MOJO_HANDLE_RIGHT_READ | MOJO_HANDLE_RIGHT_WRITE |
+    MOJO_HANDLE_RIGHT_GET_OPTIONS | MOJO_HANDLE_RIGHT_SET_OPTIONS;
+
+TEST(WaitSetTest, InvalidHandle) {
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoWaitSetAdd(MOJO_HANDLE_INVALID, MOJO_HANDLE_INVALID,
+                           MOJO_HANDLE_SIGNAL_READABLE, 123u, nullptr));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoWaitSetRemove(MOJO_HANDLE_INVALID, 123u));
+  uint32_t num_results = 10u;
+  MojoWaitSetResult results[10] = {};
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoWaitSetWait(MOJO_HANDLE_INVALID, MOJO_DEADLINE_INDEFINITE,
+                            &num_results, results, nullptr));
+
+  // Also check |MojoWaitSetAdd()| with a valid handle to be added.
+  MojoHandle mph0 = MOJO_HANDLE_INVALID;
+  MojoHandle mph1 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(nullptr, &mph0, &mph1));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoWaitSetAdd(MOJO_HANDLE_INVALID, mph0,
+                           MOJO_HANDLE_SIGNAL_READABLE, 123u, nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mph0));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mph1));
+}
+
+TEST(WaitSetTest, Create) {
+  // Invalid options.
+  {
+    static constexpr MojoCreateWaitSetOptions kOptions = {};
+    MojoHandle h = MOJO_HANDLE_INVALID;
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoCreateWaitSet(&kOptions, &h));
+    EXPECT_EQ(MOJO_HANDLE_INVALID, h);
+  }
+
+  // Options with unknown flags.
+  {
+    static constexpr MojoCreateWaitSetOptions kOptions = {
+        static_cast<uint32_t>(sizeof(MojoCreateWaitSetOptions)),
+        ~static_cast<MojoCreateWaitSetOptionsFlags>(0),
+    };
+    MojoHandle h = MOJO_HANDLE_INVALID;
+    EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, MojoCreateWaitSet(&kOptions, &h));
+    EXPECT_EQ(MOJO_HANDLE_INVALID, h);
+  }
+
+  // With non-null options.
+  {
+    static constexpr MojoCreateWaitSetOptions kOptions = {
+        static_cast<uint32_t>(sizeof(MojoCreateWaitSetOptions)),
+        static_cast<MojoCreateWaitSetOptionsFlags>(0),
+    };
+    MojoHandle h = MOJO_HANDLE_INVALID;
+    EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWaitSet(&kOptions, &h));
+    EXPECT_NE(h, MOJO_HANDLE_INVALID);
+
+    // Should have the correct rights.
+    MojoHandleRights rights = MOJO_HANDLE_RIGHT_NONE;
+    EXPECT_EQ(MOJO_RESULT_OK, MojoGetRights(h, &rights));
+    EXPECT_EQ(kDefaultWaitSetHandleRights, rights);
+
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h));
+  }
+
+  // With null options.
+  {
+    MojoHandle h = MOJO_HANDLE_INVALID;
+    EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWaitSet(nullptr, &h));
+    EXPECT_NE(h, MOJO_HANDLE_INVALID);
+
+    // Should have the correct rights.
+    MojoHandleRights rights = MOJO_HANDLE_RIGHT_NONE;
+    EXPECT_EQ(MOJO_RESULT_OK, MojoGetRights(h, &rights));
+    EXPECT_EQ(kDefaultWaitSetHandleRights, rights);
+
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h));
+  }
+}
+
+TEST(WaitSetTest, Add) {
+  MojoHandle h = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWaitSet(nullptr, &h));
+  EXPECT_NE(h, MOJO_HANDLE_INVALID);
+
+  // Add invalid handle.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoWaitSetAdd(h, MOJO_HANDLE_INVALID, MOJO_HANDLE_SIGNAL_READABLE,
+                           0u, nullptr));
+
+  // Some handles that we can add.
+  MojoHandle mph0 = MOJO_HANDLE_INVALID;
+  MojoHandle mph1 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(nullptr, &mph0, &mph1));
+
+  // Add with invalid options.
+  {
+    static constexpr MojoWaitSetAddOptions kOptions = {};
+    EXPECT_EQ(
+        MOJO_RESULT_INVALID_ARGUMENT,
+        MojoWaitSetAdd(h, mph0, MOJO_HANDLE_SIGNAL_READABLE, 0u, &kOptions));
+  }
+
+  // Add with options with unknown flags.
+  {
+    static constexpr MojoWaitSetAddOptions kOptions = {
+        static_cast<uint32_t>(sizeof(MojoWaitSetAddOptions)),
+        ~static_cast<MojoWaitSetAddOptionsFlags>(0),
+    };
+    EXPECT_EQ(
+        MOJO_RESULT_UNIMPLEMENTED,
+        MojoWaitSetAdd(h, mph0, MOJO_HANDLE_SIGNAL_READABLE, 0u, &kOptions));
+  }
+
+  // Add with options.
+  {
+    static constexpr MojoWaitSetAddOptions kOptions = {
+        static_cast<uint32_t>(sizeof(MojoWaitSetAddOptions)),
+        static_cast<MojoWaitSetAddOptionsFlags>(0),
+    };
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        MojoWaitSetAdd(h, mph0, MOJO_HANDLE_SIGNAL_READABLE, 0u, &kOptions));
+  }
+
+  // Add with null options.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWaitSetAdd(h, mph1, MOJO_HANDLE_SIGNAL_WRITABLE, 1u, nullptr));
+
+  // Add a handle that's already present, with a different cookie.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWaitSetAdd(h, mph0, MOJO_HANDLE_SIGNAL_READABLE, 2u, nullptr));
+
+  // Try to add a cookie that's already present.
+  EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+            MojoWaitSetAdd(h, mph1, MOJO_HANDLE_SIGNAL_READABLE, 0u, nullptr));
+
+  // Can close things in a wait set.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mph0));
+
+  // Can close a wait set with unclosed handles in it.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mph1));
+}
+
+TEST(WaitSetTest, Remove) {
+  MojoHandle h = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWaitSet(nullptr, &h));
+  EXPECT_NE(h, MOJO_HANDLE_INVALID);
+
+  // Some handles that we can add.
+  MojoHandle mph0 = MOJO_HANDLE_INVALID;
+  MojoHandle mph1 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(nullptr, &mph0, &mph1));
+
+  // Try to remove something that's not there.
+  EXPECT_EQ(MOJO_RESULT_NOT_FOUND, MojoWaitSetRemove(h, 12u));
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWaitSetAdd(h, mph0, MOJO_HANDLE_SIGNAL_READABLE, 12u, nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWaitSetAdd(h, mph1, MOJO_HANDLE_SIGNAL_READABLE, 34u, nullptr));
+
+  // Remove something.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWaitSetRemove(h, 12u));
+
+  // Can't remove it again.
+  EXPECT_EQ(MOJO_RESULT_NOT_FOUND, MojoWaitSetRemove(h, 12u));
+
+  // Now can add it again.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWaitSetAdd(h, mph0, MOJO_HANDLE_SIGNAL_WRITABLE, 12u, nullptr));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mph0));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mph1));
+}
+
+// Helper to check if an array of |MojoWaitSetResult|s has a result |r| for the
+// given cookie, in which case:
+//    - |r.wait_result| must equal |wait_result|.
+//    - If |wait_result| is |MOJO_RESULT_OK| or
+//      |MOJO_RESULT_FAILED_PRECONDITION|, then
+//        - |r.signals_state.satisfied_signals & signals| must equal
+//          |signals_state.satisfied_signals & signals|, and
+//        - |r.signals_state.satisfiable & signals| must equal
+//          |signals_state.satisfiable_signals & signals|.
+//    - Otherwise, |r.signals_state| must equals |signals_state|.
+// (This doesn't check that the result is unique; you should check |num_results|
+// versus the expect number and exhaustively check every expected result.)
+bool CheckHasResult(uint32_t num_results,
+                    const MojoWaitSetResult* results,
+                    uint64_t cookie,
+                    MojoHandleSignals signals,
+                    MojoResult wait_result,
+                    const MojoHandleSignalsState& signals_state) {
+  for (uint32_t i = 0; i < num_results; i++) {
+    if (results[i].cookie == cookie) {
+      EXPECT_EQ(wait_result, results[i].wait_result) << cookie;
+      EXPECT_EQ(0u, results[i].reserved) << cookie;
+      if (wait_result == MOJO_RESULT_OK ||
+          wait_result == MOJO_RESULT_FAILED_PRECONDITION) {
+        EXPECT_EQ(signals_state.satisfied_signals & signals,
+                  results[i].signals_state.satisfied_signals & signals)
+            << cookie;
+        EXPECT_EQ(signals_state.satisfiable_signals & signals,
+                  results[i].signals_state.satisfiable_signals & signals)
+            << cookie;
+      } else {
+        EXPECT_EQ(signals_state.satisfied_signals,
+                  results[i].signals_state.satisfied_signals)
+            << cookie;
+        EXPECT_EQ(signals_state.satisfiable_signals,
+                  results[i].signals_state.satisfiable_signals)
+            << cookie;
+      }
+      return true;
+    }
+  }
+  return false;
+}
+
+TEST(WaitSetTest, Wait) {
+  MojoHandle h = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWaitSet(nullptr, &h));
+  EXPECT_NE(h, MOJO_HANDLE_INVALID);
+
+  // Nothing in the wait set.
+  {
+    uint32_t num_results = 10u;
+    MojoWaitSetResult results[10] = {};
+    uint32_t max_results = 1234u;
+    EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+              MojoWaitSetWait(h, static_cast<MojoDeadline>(0), &num_results,
+                              results, &max_results));
+    EXPECT_EQ(10u, num_results);
+    EXPECT_EQ(1234u, max_results);
+  }
+
+  // Ditto, with non-zero deadline and null |max_results|.
+  {
+    uint32_t num_results = 10u;
+    MojoWaitSetResult results[10] = {};
+    EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+              MojoWaitSetWait(h, static_cast<MojoDeadline>(1000), &num_results,
+                              results, nullptr));
+    EXPECT_EQ(10u, num_results);
+  }
+
+  // Some handles that we can add.
+  MojoHandle mph0 = MOJO_HANDLE_INVALID;
+  MojoHandle mph1 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(nullptr, &mph0, &mph1));
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWaitSetAdd(h, mph0, MOJO_HANDLE_SIGNAL_READABLE, 1u, nullptr));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWaitSetAdd(h, mph1, MOJO_HANDLE_SIGNAL_READABLE, 2u, nullptr));
+
+  // Will still time out.
+  {
+    uint32_t num_results = 10u;
+    MojoWaitSetResult results[10] = {};
+    EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+              MojoWaitSetWait(h, static_cast<MojoDeadline>(0), &num_results,
+                              results, nullptr));
+    EXPECT_EQ(10u, num_results);
+  }
+
+  // Write to |mph1|.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWriteMessage(mph1, nullptr, 0, nullptr, 0,
+                                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // Should get cookie 1.
+  {
+    uint32_t num_results = 10u;
+    MojoWaitSetResult results[10] = {};
+    uint32_t max_results = 1234u;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoWaitSetWait(h, static_cast<MojoDeadline>(0), &num_results,
+                              results, &max_results));
+    EXPECT_EQ(1u, num_results);
+    EXPECT_TRUE(CheckHasResult(
+        num_results, results, 1u, MOJO_HANDLE_SIGNAL_READABLE, MOJO_RESULT_OK,
+        MojoHandleSignalsState{
+            MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE}));
+    EXPECT_EQ(1u, max_results);
+  }
+
+  // Non-zero deadline, null |max_results|; should still get cookie 1.
+  {
+    uint32_t num_results = 10u;
+    MojoWaitSetResult results[10] = {};
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoWaitSetWait(h, static_cast<MojoDeadline>(1000), &num_results,
+                              results, nullptr));
+    EXPECT_EQ(1u, num_results);
+    EXPECT_TRUE(CheckHasResult(
+        num_results, results, 1u, MOJO_HANDLE_SIGNAL_READABLE, MOJO_RESULT_OK,
+        MojoHandleSignalsState{
+            MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE}));
+  }
+
+  // Zero |num_results|.
+  {
+    uint32_t num_results = 0u;
+    uint32_t max_results = 1234u;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoWaitSetWait(h, static_cast<MojoDeadline>(0), &num_results,
+                              nullptr, &max_results));
+    EXPECT_EQ(0u, num_results);
+    EXPECT_EQ(1u, max_results);
+  }
+
+  // Add another entry waiting for readability on |mph0|.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWaitSetAdd(h, mph0, MOJO_HANDLE_SIGNAL_READABLE |
+                                        MOJO_HANDLE_SIGNAL_WRITABLE,
+                           3u, nullptr));
+
+  {
+    uint32_t num_results = 10u;
+    uint32_t max_results = 1234u;
+    MojoWaitSetResult results[10] = {};
+    EXPECT_EQ(MOJO_RESULT_OK,
+              MojoWaitSetWait(h, MOJO_DEADLINE_INDEFINITE, &num_results,
+                              results, &max_results));
+    EXPECT_EQ(2u, num_results);
+    EXPECT_TRUE(CheckHasResult(
+        num_results, results, 1u, MOJO_HANDLE_SIGNAL_READABLE, MOJO_RESULT_OK,
+        MojoHandleSignalsState{
+            MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE}));
+    EXPECT_TRUE(CheckHasResult(
+        num_results, results, 3u,
+        MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+        MOJO_RESULT_OK,
+        MojoHandleSignalsState{
+            MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE}));
+    EXPECT_EQ(2u, max_results);
+  }
+
+  // Close |mph0|.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mph0));
+
+  {
+    uint32_t num_results = 10u;
+    MojoWaitSetResult results[10] = {};
+    EXPECT_EQ(MOJO_RESULT_OK, MojoWaitSetWait(h, MOJO_DEADLINE_INDEFINITE,
+                                              &num_results, results, nullptr));
+    EXPECT_EQ(3u, num_results);
+    EXPECT_TRUE(
+        CheckHasResult(num_results, results, 1u, MOJO_HANDLE_SIGNAL_READABLE,
+                       MOJO_RESULT_CANCELLED, MojoHandleSignalsState()));
+    EXPECT_TRUE(CheckHasResult(
+        num_results, results, 2u, MOJO_HANDLE_SIGNAL_WRITABLE,
+        MOJO_RESULT_FAILED_PRECONDITION, MojoHandleSignalsState()));
+    EXPECT_TRUE(CheckHasResult(
+        num_results, results, 3u,
+        MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+        MOJO_RESULT_CANCELLED, MojoHandleSignalsState()));
+  }
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mph1));
+}
+
+// TODO(vtl): Add threaded tests, especially those that actually ... wait.
+
+}  // namespace
diff --git a/mojo/public/c/system/tests/wait_unittest.cc b/mojo/public/c/system/tests/wait_unittest.cc
index 1d26faa..541043e 100644
--- a/mojo/public/c/system/tests/wait_unittest.cc
+++ b/mojo/public/c/system/tests/wait_unittest.cc
@@ -5,9 +5,10 @@
 // This file tests the C wait API (the functions declared in
 // mojo/public/c/system/wait.h).
 
+#include "mojo/public/c/system/wait.h"
+
 #include "mojo/public/c/system/handle.h"
 #include "mojo/public/c/system/result.h"
-#include "mojo/public/c/system/wait.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
diff --git a/mojo/public/c/system/wait_set.h b/mojo/public/c/system/wait_set.h
index 4fe91ca..10e82b7 100644
--- a/mojo/public/c/system/wait_set.h
+++ b/mojo/public/c/system/wait_set.h
@@ -81,6 +81,8 @@
 //       |options| is non null and |*options| is invalid).
 //   |MOJO_RESULT_RESOURCE_EXHAUSTED| if a process/system/quota/etc. limit has
 //       been reached.
+//   |MOJO_ERROR_CODE_UNIMPLEMENTED| if some unknown/unsupported option has been
+//       specified in |*options|.
 MojoResult MojoCreateWaitSet(const struct MojoCreateWaitSetOptions*
                                  MOJO_RESTRICT options,  // Optional in.
                              MojoHandle* handle);        // Out.
@@ -108,6 +110,8 @@
 //       some transaction.
 //   |MOJO_RESULT_RESOURCE_EXHAUSTED| if the handle could not be added due to
 //       hitting a system or quota limitation.
+//   |MOJO_ERROR_CODE_UNIMPLEMENTED| if some unknown/unsupported option has been
+//       specified in |*options|.
 MojoResult MojoWaitSetAdd(MojoHandle wait_set_handle,  // In.
                           MojoHandle handle,           // In.
                           MojoHandleSignals signals,   // In.
diff --git a/mojo/public/platform/nacl/libmojo.cc b/mojo/public/platform/nacl/libmojo.cc
index 81fb436..7dbdff8 100644
--- a/mojo/public/platform/nacl/libmojo.cc
+++ b/mojo/public/platform/nacl/libmojo.cc
@@ -14,6 +14,7 @@
 #include "mojo/public/c/system/result.h"
 #include "mojo/public/c/system/time.h"
 #include "mojo/public/c/system/wait.h"
+#include "mojo/public/c/system/wait_set.h"
 #include "mojo/public/platform/nacl/mojo_irt.h"
 #include "native_client/src/untrusted/irt/irt.h"
 
@@ -312,3 +313,41 @@
   return irt_mojo->MojoUnmapBuffer(buffer);
 }
 
+MojoResult MojoCreateWaitSet(const struct MojoCreateWaitSetOptions* options,
+                             MojoHandle* handle) {
+  struct nacl_irt_mojo* irt_mojo = get_irt_mojo();
+  if (!irt_mojo)
+    abort();
+  return irt_mojo->MojoCreateWaitSet(options, handle);
+}
+
+MojoResult MojoWaitSetAdd(MojoHandle wait_set_handle,
+                          MojoHandle handle,
+                          MojoHandleSignals signals,
+                          uint64_t cookie,
+                          const struct MojoWaitSetAddOptions* options) {
+  struct nacl_irt_mojo* irt_mojo = get_irt_mojo();
+  if (!irt_mojo)
+    abort();
+  return irt_mojo->MojoWaitSetAdd(wait_set_handle, handle, signals, cookie,
+                                  options);
+}
+
+MojoResult MojoWaitSetRemove(MojoHandle wait_set_handle, uint64_t cookie) {
+  struct nacl_irt_mojo* irt_mojo = get_irt_mojo();
+  if (!irt_mojo)
+    abort();
+  return irt_mojo->MojoWaitSetRemove(wait_set_handle, cookie);
+}
+
+MojoResult MojoWaitSetWait(MojoHandle wait_set_handle,
+                           MojoDeadline deadline,
+                           uint32_t* num_results,
+                           struct MojoWaitSetResult* results,
+                           uint32_t* max_results) {
+  struct nacl_irt_mojo* irt_mojo = get_irt_mojo();
+  if (!irt_mojo)
+    abort();
+  return irt_mojo->MojoWaitSetWait(wait_set_handle, deadline, num_results,
+                                   results, max_results);
+}
diff --git a/mojo/public/platform/nacl/mojo_irt.h b/mojo/public/platform/nacl/mojo_irt.h
index bc84139..ad23993 100644
--- a/mojo/public/platform/nacl/mojo_irt.h
+++ b/mojo/public/platform/nacl/mojo_irt.h
@@ -11,6 +11,7 @@
 #include "mojo/public/c/system/message_pipe.h"
 #include "mojo/public/c/system/result.h"
 #include "mojo/public/c/system/time.h"
+#include "mojo/public/c/system/wait_set.h"
 
 #define NACL_IRT_MOJO_v0_1 "nacl-irt-mojo-0.1"
 
@@ -109,6 +110,20 @@
                               void** buffer,
                               MojoMapBufferFlags flags);
   MojoResult (*MojoUnmapBuffer)(void* buffer);
+  MojoResult (*MojoCreateWaitSet)(
+      const struct MojoCreateWaitSetOptions* options,
+      MojoHandle* handle);
+  MojoResult (*MojoWaitSetAdd)(MojoHandle wait_set_handle,
+                               MojoHandle handle,
+                               MojoHandleSignals signals,
+                               uint64_t cookie,
+                               const struct MojoWaitSetAddOptions* options);
+  MojoResult (*MojoWaitSetRemove)(MojoHandle wait_set_handle, uint64_t cookie);
+  MojoResult (*MojoWaitSetWait)(MojoHandle wait_set_handle,
+                                MojoDeadline deadline,
+                                uint32_t* num_results,
+                                struct MojoWaitSetResult* results,
+                                uint32_t* max_results);
 };
 
 #endif  // MOJO_PUBLIC_PLATFORM_NACL_MOJO_IRT_H_
diff --git a/mojo/public/platform/native/system_thunks.c b/mojo/public/platform/native/system_thunks.c
index c9517ff..2223c5c 100644
--- a/mojo/public/platform/native/system_thunks.c
+++ b/mojo/public/platform/native/system_thunks.c
@@ -226,6 +226,36 @@
   return g_thunks.UnmapBuffer(buffer);
 }
 
+MojoResult MojoCreateWaitSet(const struct MojoCreateWaitSetOptions* options,
+                             MojoHandle* handle) {
+  assert(g_thunks.CreateWaitSet);
+  return g_thunks.CreateWaitSet(options, handle);
+}
+
+MojoResult MojoWaitSetAdd(MojoHandle wait_set_handle,
+                          MojoHandle handle,
+                          MojoHandleSignals signals,
+                          uint64_t cookie,
+                          const struct MojoWaitSetAddOptions* options) {
+  assert(g_thunks.WaitSetAdd);
+  return g_thunks.WaitSetAdd(wait_set_handle, handle, signals, cookie, options);
+}
+
+MojoResult MojoWaitSetRemove(MojoHandle wait_set_handle, uint64_t cookie) {
+  assert(g_thunks.WaitSetRemove);
+  return g_thunks.WaitSetRemove(wait_set_handle, cookie);
+}
+
+MojoResult MojoWaitSetWait(MojoHandle wait_set_handle,
+                           MojoDeadline deadline,
+                           uint32_t* num_results,
+                           struct MojoWaitSetResult* results,
+                           uint32_t* max_results) {
+  assert(g_thunks.WaitSetWait);
+  return g_thunks.WaitSetWait(wait_set_handle, deadline, num_results, results,
+                              max_results);
+}
+
 THUNK_EXPORT size_t
 MojoSetSystemThunks(const struct MojoSystemThunks* system_thunks) {
   if (system_thunks->size >= sizeof(g_thunks))
diff --git a/mojo/public/platform/native/system_thunks.h b/mojo/public/platform/native/system_thunks.h
index 03f2ccb..92260c7 100644
--- a/mojo/public/platform/native/system_thunks.h
+++ b/mojo/public/platform/native/system_thunks.h
@@ -16,6 +16,7 @@
 #include "mojo/public/c/system/result.h"
 #include "mojo/public/c/system/time.h"
 #include "mojo/public/c/system/wait.h"
+#include "mojo/public/c/system/wait_set.h"
 
 // The embedder needs to bind the basic Mojo Core functions of a DSO to those of
 // the embedder when loading a DSO that is dependent on mojo_system.
@@ -136,10 +137,22 @@
       MojoHandle handle,
       MojoHandleRights rights_to_remove,
       MojoHandle* replacement_handle);
+  MojoResult (*CreateWaitSet)(const struct MojoCreateWaitSetOptions* options,
+                              MojoHandle* handle);
+  MojoResult (*WaitSetAdd)(MojoHandle wait_set_handle,
+                           MojoHandle handle,
+                           MojoHandleSignals signals,
+                           uint64_t cookie,
+                           const struct MojoWaitSetAddOptions* options);
+  MojoResult (*WaitSetRemove)(MojoHandle wait_set_handle, uint64_t cookie);
+  MojoResult (*WaitSetWait)(MojoHandle wait_set_handle,
+                            MojoDeadline deadline,
+                            uint32_t* num_results,
+                            struct MojoWaitSetResult* results,
+                            uint32_t* max_results);
 };
 #pragma pack(pop)
 
-
 #ifdef __cplusplus
 // Intended to be called from the embedder. Returns a |MojoCore| initialized
 // to contain pointers to each of the embedder's MojoCore functions.
@@ -173,6 +186,10 @@
       MojoDuplicateHandleWithReducedRights,
       MojoDuplicateHandle,
       MojoReplaceHandleWithReducedRights,
+      MojoCreateWaitSet,
+      MojoWaitSetAdd,
+      MojoWaitSetRemove,
+      MojoWaitSetWait,
   };
   return system_thunks;
 }