NaCl: create a separate namespace for Mojo handles.

BUG=401761
R=viettrungluu@chromium.org

Review URL: https://codereview.chromium.org/1052723003
diff --git a/mojo/edk/embedder/BUILD.gn b/mojo/edk/embedder/BUILD.gn
index 9650209..2953982 100644
--- a/mojo/edk/embedder/BUILD.gn
+++ b/mojo/edk/embedder/BUILD.gn
@@ -16,6 +16,7 @@
     "embedder.h",
     "embedder_internal.h",
     "entrypoints.cc",
+    "system_impl_private_entrypoints.cc",
 
     # Test-only code:
     # TODO(vtl): It's a little unfortunate that these end up in the same
@@ -37,6 +38,8 @@
     ":platform",
   ]
 
+  mojo_sdk_deps = [ "mojo/public/platform/native:system_impl_private_api" ]
+
   mojo_sdk_public_deps = [ "mojo/public/cpp/system" ]
 
   deps = [
diff --git a/mojo/edk/embedder/system_impl_private_entrypoints.cc b/mojo/edk/embedder/system_impl_private_entrypoints.cc
new file mode 100644
index 0000000..475ffa7
--- /dev/null
+++ b/mojo/edk/embedder/system_impl_private_entrypoints.cc
@@ -0,0 +1,260 @@
+// Copyright 2015 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/edk/embedder/embedder_internal.h"
+#include "mojo/edk/system/core.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/public/c/system/buffer.h"
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/c/system/functions.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/platform/native/system_impl_private.h"
+
+using mojo::embedder::internal::g_core;
+using mojo::system::Core;
+using mojo::system::Dispatcher;
+using mojo::system::MakeUserPointer;
+
+// Definitions of the system functions, but with an explicit parameter for the
+// core object rather than using the default singleton. Also includes functions
+// for manipulating core objects.
+extern "C" {
+
+MojoSystemImpl MojoSystemImplGetDefaultImpl() {
+  return static_cast<MojoSystemImpl>(g_core);
+}
+
+MojoSystemImpl MojoSystemImplCreateImpl() {
+  Core* created_core = new Core(g_core->platform_support());
+  return static_cast<MojoSystemImpl>(created_core);
+}
+
+MojoResult MojoSystemImplTransferHandle(MojoSystemImpl from_system,
+                                        MojoHandle handle,
+                                        MojoSystemImpl to_system,
+                                        MojoHandle* result_handle) {
+  Core* from_core = static_cast<Core*>(from_system);
+  if (from_core == nullptr)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (handle == MOJO_HANDLE_INVALID)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  Core* to_core = static_cast<Core*>(to_system);
+  if (to_core == nullptr)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (result_handle == nullptr)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  scoped_refptr<Dispatcher> d;
+  MojoResult result = from_core->GetAndRemoveDispatcher(handle, &d);
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  MojoHandle created_handle = to_core->AddDispatcher(d);
+  if (created_handle == MOJO_HANDLE_INVALID) {
+    // The handle has been lost, unfortunately. There's no guarentee we can put
+    // it back where it came from, or get the original ID back. Holding locks
+    // for multiple cores risks deadlock, so that isn't a solution. This case
+    // should not happen for reasonable uses of this API, however.
+    LOG(ERROR) << "Could not transfer handle";
+    d->Close();
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  }
+
+  MakeUserPointer(result_handle).Put(created_handle);
+  return MOJO_RESULT_OK;
+}
+
+MojoTimeTicks MojoSystemImplGetTimeTicksNow(MojoSystemImpl system) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->GetTimeTicksNow();
+}
+
+MojoResult MojoSystemImplClose(MojoSystemImpl system, MojoHandle handle) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->Close(handle);
+}
+
+MojoResult MojoSystemImplWait(MojoSystemImpl system,
+                              MojoHandle handle,
+                              MojoHandleSignals signals,
+                              MojoDeadline deadline,
+                              MojoHandleSignalsState* signals_state) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->Wait(handle, signals, deadline, MakeUserPointer(signals_state));
+}
+
+MojoResult MojoSystemImplWaitMany(MojoSystemImpl system,
+                                  const MojoHandle* handles,
+                                  const MojoHandleSignals* signals,
+                                  uint32_t num_handles,
+                                  MojoDeadline deadline,
+                                  uint32_t* result_index,
+                                  MojoHandleSignalsState* signals_states) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->WaitMany(MakeUserPointer(handles), MakeUserPointer(signals),
+                        num_handles, deadline, MakeUserPointer(result_index),
+                        MakeUserPointer(signals_states));
+}
+
+MojoResult MojoSystemImplCreateMessagePipe(
+    MojoSystemImpl system,
+    const MojoCreateMessagePipeOptions* options,
+    MojoHandle* message_pipe_handle0,
+    MojoHandle* message_pipe_handle1) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->CreateMessagePipe(MakeUserPointer(options),
+                                 MakeUserPointer(message_pipe_handle0),
+                                 MakeUserPointer(message_pipe_handle1));
+}
+
+MojoResult MojoSystemImplWriteMessage(MojoSystemImpl system,
+                                      MojoHandle message_pipe_handle,
+                                      const void* bytes,
+                                      uint32_t num_bytes,
+                                      const MojoHandle* handles,
+                                      uint32_t num_handles,
+                                      MojoWriteMessageFlags flags) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->WriteMessage(message_pipe_handle, MakeUserPointer(bytes),
+                            num_bytes, MakeUserPointer(handles), num_handles,
+                            flags);
+}
+
+MojoResult MojoSystemImplReadMessage(MojoSystemImpl system,
+                                     MojoHandle message_pipe_handle,
+                                     void* bytes,
+                                     uint32_t* num_bytes,
+                                     MojoHandle* handles,
+                                     uint32_t* num_handles,
+                                     MojoReadMessageFlags flags) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->ReadMessage(message_pipe_handle, MakeUserPointer(bytes),
+                           MakeUserPointer(num_bytes), MakeUserPointer(handles),
+                           MakeUserPointer(num_handles), flags);
+}
+
+MojoResult MojoSystemImplCreateDataPipe(
+    MojoSystemImpl system,
+    const MojoCreateDataPipeOptions* options,
+    MojoHandle* data_pipe_producer_handle,
+    MojoHandle* data_pipe_consumer_handle) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->CreateDataPipe(MakeUserPointer(options),
+                              MakeUserPointer(data_pipe_producer_handle),
+                              MakeUserPointer(data_pipe_consumer_handle));
+}
+
+MojoResult MojoSystemImplWriteData(MojoSystemImpl system,
+                                   MojoHandle data_pipe_producer_handle,
+                                   const void* elements,
+                                   uint32_t* num_elements,
+                                   MojoWriteDataFlags flags) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->WriteData(data_pipe_producer_handle, MakeUserPointer(elements),
+                         MakeUserPointer(num_elements), flags);
+}
+
+MojoResult MojoSystemImplBeginWriteData(MojoSystemImpl system,
+                                        MojoHandle data_pipe_producer_handle,
+                                        void** buffer,
+                                        uint32_t* buffer_num_elements,
+                                        MojoWriteDataFlags flags) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->BeginWriteData(data_pipe_producer_handle,
+                              MakeUserPointer(buffer),
+                              MakeUserPointer(buffer_num_elements), flags);
+}
+
+MojoResult MojoSystemImplEndWriteData(MojoSystemImpl system,
+                                      MojoHandle data_pipe_producer_handle,
+                                      uint32_t num_elements_written) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->EndWriteData(data_pipe_producer_handle, num_elements_written);
+}
+
+MojoResult MojoSystemImplReadData(MojoSystemImpl system,
+                                  MojoHandle data_pipe_consumer_handle,
+                                  void* elements,
+                                  uint32_t* num_elements,
+                                  MojoReadDataFlags flags) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->ReadData(data_pipe_consumer_handle, MakeUserPointer(elements),
+                        MakeUserPointer(num_elements), flags);
+}
+
+MojoResult MojoSystemImplBeginReadData(MojoSystemImpl system,
+                                       MojoHandle data_pipe_consumer_handle,
+                                       const void** buffer,
+                                       uint32_t* buffer_num_elements,
+                                       MojoReadDataFlags flags) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->BeginReadData(data_pipe_consumer_handle, MakeUserPointer(buffer),
+                             MakeUserPointer(buffer_num_elements), flags);
+}
+
+MojoResult MojoSystemImplEndReadData(MojoSystemImpl system,
+                                     MojoHandle data_pipe_consumer_handle,
+                                     uint32_t num_elements_read) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->EndReadData(data_pipe_consumer_handle, num_elements_read);
+}
+
+MojoResult MojoSystemImplCreateSharedBuffer(
+    MojoSystemImpl system,
+    const MojoCreateSharedBufferOptions* options,
+    uint64_t num_bytes,
+    MojoHandle* shared_buffer_handle) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->CreateSharedBuffer(MakeUserPointer(options), num_bytes,
+                                  MakeUserPointer(shared_buffer_handle));
+}
+
+MojoResult MojoSystemImplDuplicateBufferHandle(
+    MojoSystemImpl system,
+    MojoHandle buffer_handle,
+    const MojoDuplicateBufferHandleOptions* options,
+    MojoHandle* new_buffer_handle) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->DuplicateBufferHandle(buffer_handle, MakeUserPointer(options),
+                                     MakeUserPointer(new_buffer_handle));
+}
+
+MojoResult MojoSystemImplMapBuffer(MojoSystemImpl system,
+                                   MojoHandle buffer_handle,
+                                   uint64_t offset,
+                                   uint64_t num_bytes,
+                                   void** buffer,
+                                   MojoMapBufferFlags flags) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->MapBuffer(buffer_handle, offset, num_bytes,
+                         MakeUserPointer(buffer), flags);
+}
+
+MojoResult MojoSystemImplUnmapBuffer(MojoSystemImpl system, void* buffer) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->UnmapBuffer(MakeUserPointer(buffer));
+}
+
+}  // extern "C"
diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc
index 80c3d36..887a9a7 100644
--- a/mojo/edk/system/core.cc
+++ b/mojo/edk/system/core.cc
@@ -97,6 +97,15 @@
   return handle_table_.GetDispatcher(handle);
 }
 
+MojoResult Core::GetAndRemoveDispatcher(MojoHandle handle,
+                                        scoped_refptr<Dispatcher>* dispatcher) {
+  if (handle == MOJO_HANDLE_INVALID)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  base::AutoLock locker(handle_table_lock_);
+  return handle_table_.GetAndRemoveDispatcher(handle, dispatcher);
+}
+
 MojoResult Core::AsyncWait(MojoHandle handle,
                            MojoHandleSignals signals,
                            const base::Callback<void(MojoResult)>& callback) {
diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h
index aadfb66..0c38008 100644
--- a/mojo/edk/system/core.h
+++ b/mojo/edk/system/core.h
@@ -52,6 +52,15 @@
   // invalid.
   scoped_refptr<Dispatcher> GetDispatcher(MojoHandle handle);
 
+  // Like |GetDispatcher()|, but also removes the handle from the handle table.
+  // On success, gets the dispatcher for a given handle (which should not be
+  // |MOJO_HANDLE_INVALID|) and removes it. (On failure, returns an appropriate
+  // result (and leaves |dispatcher| alone), namely
+  // |MOJO_RESULT_INVALID_ARGUMENT| if there's no dispatcher for the given
+  // handle or |MOJO_RESULT_BUSY| if the handle is marked as busy.)
+  MojoResult GetAndRemoveDispatcher(MojoHandle handle,
+                                    scoped_refptr<Dispatcher>* dispatcher);
+
   // Watches on the given handle for the given signals, calling |callback| when
   // a signal is satisfied or when all signals become unsatisfiable. |callback|
   // must satisfy stringent requirements -- see |Awakable::Awake()| in
diff --git a/mojo/edk/test/BUILD.gn b/mojo/edk/test/BUILD.gn
index e8f55ad..8735708 100644
--- a/mojo/edk/test/BUILD.gn
+++ b/mojo/edk/test/BUILD.gn
@@ -90,6 +90,7 @@
     ":mojo_public_system_perftests",
     ":mojo_public_system_unittests",
     ":mojo_public_utility_unittests",
+    ":mojo_system_impl_private_unittests",
   ]
 }
 
@@ -134,3 +135,10 @@
     "../../public/cpp/utility/tests",
   ]
 }
+
+test("mojo_system_impl_private_unittests") {
+  deps = [
+    ":run_all_unittests",
+    "../../public/platform/native:system_impl_private_tests",
+  ]
+}
diff --git a/mojo/public/platform/native/BUILD.gn b/mojo/public/platform/native/BUILD.gn
index 0e099f9..6a30d34 100644
--- a/mojo/public/platform/native/BUILD.gn
+++ b/mojo/public/platform/native/BUILD.gn
@@ -24,6 +24,42 @@
   # source_set here, this flag change is not needed.
 }
 
+# For internal use only.
+mojo_sdk_source_set("system_impl_private") {
+  sources = [
+    "system_impl_private_thunks.cc",
+    "system_impl_private_thunks.h",
+  ]
+  defines = [ "MOJO_SYSTEM_IMPLEMENTATION" ]
+  deps = [
+    ":system_impl_private_api",
+  ]
+  mojo_sdk_deps = [ "mojo/public/c/system" ]
+}
+
+# For internal use only.
+mojo_sdk_source_set("system_impl_private_api") {
+  sources = [
+    "system_impl_private.h",
+  ]
+  mojo_sdk_deps = [ "mojo/public/c/system" ]
+}
+
+mojo_sdk_source_set("system_impl_private_tests") {
+  testonly = true
+
+  sources = [
+    "system_impl_private_unittest.cc",
+  ]
+
+  deps = [
+    ":system_impl_private_api",
+    "//testing/gtest",
+  ]
+
+  mojo_sdk_deps = [ "mojo/public/c/system" ]
+}
+
 mojo_sdk_source_set("gles2") {
   sources = [
     "gles2_impl_chromium_miscellaneous_thunks.cc",
diff --git a/mojo/public/platform/native/system_impl_private.h b/mojo/public/platform/native/system_impl_private.h
new file mode 100644
index 0000000..ffb520d
--- /dev/null
+++ b/mojo/public/platform/native/system_impl_private.h
@@ -0,0 +1,142 @@
+// Copyright 2015 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.
+
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_IMPL_PRIVATE_H_
+#define MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_IMPL_PRIVATE_H_
+
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+// This interface provides the Mojo system API, but with the ability to confine
+// calls to a specific handle namespace. Handles in one namespace are unrelated
+// to handles in another namespace. Two ends of a pipe may live in different
+// handle namespaces, however.
+
+typedef void* MojoSystemImpl;
+
+extern "C" {
+// APIs for creating and manipulating MojoSystemImpls.
+
+// Returns the MojoSystemImpl implicitly used by the non-SystemImpl version of
+// the Mojo sytem APIs.
+MOJO_SYSTEM_EXPORT MojoSystemImpl MojoSystemImplGetDefaultImpl();
+
+// Creates and returns a new MojoSystemImpl. Currently there is no way to
+// destroy a MojoSystemImpl, once created.
+MOJO_SYSTEM_EXPORT MojoSystemImpl MojoSystemImplCreateImpl();
+
+// Moves a handle from one MojoSystemImpl to another.
+// On success, |result_handle| contains the name of the handle in the new
+// namespace.
+// If |MOJO_RESULT_RESOURCE_EXHAUSTED| is returned, the |handle| will have been
+// closed, and is now lost.
+// Busy handles cannot be transfered.
+// To avoid trouble, this API should only be used to bootstrap a newly created
+// |to_system| with a newly created |handle|.
+MOJO_SYSTEM_EXPORT MojoResult
+MojoSystemImplTransferHandle(MojoSystemImpl from_system,
+                             MojoHandle handle,
+                             MojoSystemImpl to_system,
+                             MojoHandle* result_handle);
+
+// APIs mirroring the Mojo system APIs, but also taking a MojoSystemImpl param.
+MOJO_SYSTEM_EXPORT MojoTimeTicks
+MojoSystemImplGetTimeTicksNow(MojoSystemImpl system);
+MOJO_SYSTEM_EXPORT MojoResult
+MojoSystemImplClose(MojoSystemImpl system, MojoHandle handle);
+MOJO_SYSTEM_EXPORT MojoResult
+MojoSystemImplWait(MojoSystemImpl system,
+                   MojoHandle handle,
+                   MojoHandleSignals signals,
+                   MojoDeadline deadline,
+                   struct MojoHandleSignalsState* signals_state);
+MOJO_SYSTEM_EXPORT MojoResult
+MojoSystemImplWaitMany(MojoSystemImpl system,
+                       const MojoHandle* handles,
+                       const MojoHandleSignals* signals,
+                       uint32_t num_handles,
+                       MojoDeadline deadline,
+                       uint32_t* result_index,
+                       struct MojoHandleSignalsState* signals_states);
+MOJO_SYSTEM_EXPORT MojoResult MojoSystemImplCreateMessagePipe(
+    MojoSystemImpl system,
+    const struct MojoCreateMessagePipeOptions* options,
+    MojoHandle* message_pipe_handle0,
+    MojoHandle* message_pipe_handle1);
+MOJO_SYSTEM_EXPORT MojoResult
+MojoSystemImplWriteMessage(MojoSystemImpl system,
+                           MojoHandle message_pipe_handle,
+                           const void* bytes,
+                           uint32_t num_bytes,
+                           const MojoHandle* handles,
+                           uint32_t num_handles,
+                           MojoWriteMessageFlags flags);
+MOJO_SYSTEM_EXPORT MojoResult
+MojoSystemImplReadMessage(MojoSystemImpl system,
+                          MojoHandle message_pipe_handle,
+                          void* bytes,
+                          uint32_t* num_bytes,
+                          MojoHandle* handles,
+                          uint32_t* num_handles,
+                          MojoReadMessageFlags flags);
+MOJO_SYSTEM_EXPORT MojoResult
+MojoSystemImplCreateDataPipe(MojoSystemImpl system,
+                             const struct MojoCreateDataPipeOptions* options,
+                             MojoHandle* data_pipe_producer_handle,
+                             MojoHandle* data_pipe_consumer_handle);
+MOJO_SYSTEM_EXPORT MojoResult
+MojoSystemImplWriteData(MojoSystemImpl system,
+                        MojoHandle data_pipe_producer_handle,
+                        const void* elements,
+                        uint32_t* num_elements,
+                        MojoWriteDataFlags flags);
+MOJO_SYSTEM_EXPORT MojoResult
+MojoSystemImplBeginWriteData(MojoSystemImpl system,
+                             MojoHandle data_pipe_producer_handle,
+                             void** buffer,
+                             uint32_t* buffer_num_elements,
+                             MojoWriteDataFlags flags);
+MOJO_SYSTEM_EXPORT MojoResult
+MojoSystemImplEndWriteData(MojoSystemImpl system,
+                           MojoHandle data_pipe_producer_handle,
+                           uint32_t num_elements_written);
+MOJO_SYSTEM_EXPORT MojoResult
+MojoSystemImplReadData(MojoSystemImpl system,
+                       MojoHandle data_pipe_consumer_handle,
+                       void* elements,
+                       uint32_t* num_elements,
+                       MojoReadDataFlags flags);
+MOJO_SYSTEM_EXPORT MojoResult
+MojoSystemImplBeginReadData(MojoSystemImpl system,
+                            MojoHandle data_pipe_consumer_handle,
+                            const void** buffer,
+                            uint32_t* buffer_num_elements,
+                            MojoReadDataFlags flags);
+MOJO_SYSTEM_EXPORT MojoResult
+MojoSystemImplEndReadData(MojoSystemImpl system,
+                          MojoHandle data_pipe_consumer_handle,
+                          uint32_t num_elements_read);
+MOJO_SYSTEM_EXPORT MojoResult MojoSystemImplCreateSharedBuffer(
+    MojoSystemImpl system,
+    const struct MojoCreateSharedBufferOptions* options,
+    uint64_t num_bytes,
+    MojoHandle* shared_buffer_handle);
+MOJO_SYSTEM_EXPORT MojoResult MojoSystemImplDuplicateBufferHandle(
+    MojoSystemImpl system,
+    MojoHandle buffer_handle,
+    const struct MojoDuplicateBufferHandleOptions* options,
+    MojoHandle* new_buffer_handle);
+MOJO_SYSTEM_EXPORT MojoResult MojoSystemImplMapBuffer(MojoSystemImpl system,
+                                                      MojoHandle buffer_handle,
+                                                      uint64_t offset,
+                                                      uint64_t num_bytes,
+                                                      void** buffer,
+                                                      MojoMapBufferFlags flags);
+MOJO_SYSTEM_EXPORT MojoResult
+MojoSystemImplUnmapBuffer(MojoSystemImpl system, void* buffer);
+}  // extern "C"
+
+#endif  // MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_IMPL_PRIVATE_H_
diff --git a/mojo/public/platform/native/system_impl_private_thunks.cc b/mojo/public/platform/native/system_impl_private_thunks.cc
new file mode 100644
index 0000000..7012e73
--- /dev/null
+++ b/mojo/public/platform/native/system_impl_private_thunks.cc
@@ -0,0 +1,220 @@
+// Copyright 2015 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/platform/native/system_impl_private_thunks.h"
+
+#include <assert.h>
+
+#include "mojo/public/platform/native/thunk_export.h"
+
+extern "C" {
+
+static MojoSystemImplControlThunksPrivate g_system_impl_control_thunks = {0};
+static MojoSystemImplThunksPrivate g_system_impl_thunks = {0};
+
+MojoSystemImpl MojoSystemImplGetDefaultImpl() {
+  assert(g_system_impl_control_thunks.GetDefaultSystemImpl);
+  return g_system_impl_control_thunks.GetDefaultSystemImpl();
+}
+
+MojoSystemImpl MojoSystemImplCreateImpl() {
+  assert(g_system_impl_control_thunks.CreateSystemImpl);
+  return g_system_impl_control_thunks.CreateSystemImpl();
+}
+
+MojoResult MojoSystemImplTransferHandle(MojoSystemImpl from_system,
+                                        MojoHandle handle,
+                                        MojoSystemImpl to_system,
+                                        MojoHandle* result_handle) {
+  assert(g_system_impl_control_thunks.TransferHandle);
+  return g_system_impl_control_thunks.TransferHandle(from_system, handle,
+                                                     to_system, result_handle);
+}
+
+MojoTimeTicks MojoSystemImplGetTimeTicksNow(MojoSystemImpl system) {
+  assert(g_system_impl_thunks.GetTimeTicksNow);
+  return g_system_impl_thunks.GetTimeTicksNow(system);
+}
+
+MojoResult MojoSystemImplClose(MojoSystemImpl system, MojoHandle handle) {
+  assert(g_system_impl_thunks.Close);
+  return g_system_impl_thunks.Close(system, handle);
+}
+
+MojoResult MojoSystemImplWait(MojoSystemImpl system,
+                              MojoHandle handle,
+                              MojoHandleSignals signals,
+                              MojoDeadline deadline,
+                              struct MojoHandleSignalsState* signals_state) {
+  assert(g_system_impl_thunks.Wait);
+  return g_system_impl_thunks.Wait(system, handle, signals, deadline,
+                                   signals_state);
+}
+
+MojoResult MojoSystemImplWaitMany(
+    MojoSystemImpl system,
+    const MojoHandle* handles,
+    const MojoHandleSignals* signals,
+    uint32_t num_handles,
+    MojoDeadline deadline,
+    uint32_t* result_index,
+    struct MojoHandleSignalsState* signals_states) {
+  assert(g_system_impl_thunks.WaitMany);
+  return g_system_impl_thunks.WaitMany(system, handles, signals, num_handles,
+                                       deadline, result_index, signals_states);
+}
+
+MojoResult MojoSystemImplCreateMessagePipe(
+    MojoSystemImpl system,
+    const struct MojoCreateMessagePipeOptions* options,
+    MojoHandle* message_pipe_handle0,
+    MojoHandle* message_pipe_handle1) {
+  assert(g_system_impl_thunks.CreateMessagePipe);
+  return g_system_impl_thunks.CreateMessagePipe(
+      system, options, message_pipe_handle0, message_pipe_handle1);
+}
+
+MojoResult MojoSystemImplWriteMessage(MojoSystemImpl system,
+                                      MojoHandle message_pipe_handle,
+                                      const void* bytes,
+                                      uint32_t num_bytes,
+                                      const MojoHandle* handles,
+                                      uint32_t num_handles,
+                                      MojoWriteMessageFlags flags) {
+  assert(g_system_impl_thunks.WriteMessage);
+  return g_system_impl_thunks.WriteMessage(system, message_pipe_handle, bytes,
+                                           num_bytes, handles, num_handles,
+                                           flags);
+}
+
+MojoResult MojoSystemImplReadMessage(MojoSystemImpl system,
+                                     MojoHandle message_pipe_handle,
+                                     void* bytes,
+                                     uint32_t* num_bytes,
+                                     MojoHandle* handles,
+                                     uint32_t* num_handles,
+                                     MojoReadMessageFlags flags) {
+  assert(g_system_impl_thunks.ReadMessage);
+  return g_system_impl_thunks.ReadMessage(system, message_pipe_handle, bytes,
+                                          num_bytes, handles, num_handles,
+                                          flags);
+}
+
+MojoResult MojoSystemImplCreateDataPipe(
+    MojoSystemImpl system,
+    const struct MojoCreateDataPipeOptions* options,
+    MojoHandle* data_pipe_producer_handle,
+    MojoHandle* data_pipe_consumer_handle) {
+  assert(g_system_impl_thunks.CreateDataPipe);
+  return g_system_impl_thunks.CreateDataPipe(
+      system, options, data_pipe_producer_handle, data_pipe_consumer_handle);
+}
+
+MojoResult MojoSystemImplWriteData(MojoSystemImpl system,
+                                   MojoHandle data_pipe_producer_handle,
+                                   const void* elements,
+                                   uint32_t* num_elements,
+                                   MojoWriteDataFlags flags) {
+  assert(g_system_impl_thunks.WriteData);
+  return g_system_impl_thunks.WriteData(system, data_pipe_producer_handle,
+                                        elements, num_elements, flags);
+}
+
+MojoResult MojoSystemImplBeginWriteData(MojoSystemImpl system,
+                                        MojoHandle data_pipe_producer_handle,
+                                        void** buffer,
+                                        uint32_t* buffer_num_elements,
+                                        MojoWriteDataFlags flags) {
+  assert(g_system_impl_thunks.BeginWriteData);
+  return g_system_impl_thunks.BeginWriteData(
+      system, data_pipe_producer_handle, buffer, buffer_num_elements, flags);
+}
+
+MojoResult MojoSystemImplEndWriteData(MojoSystemImpl system,
+                                      MojoHandle data_pipe_producer_handle,
+                                      uint32_t num_elements_written) {
+  assert(g_system_impl_thunks.EndWriteData);
+  return g_system_impl_thunks.EndWriteData(system, data_pipe_producer_handle,
+                                           num_elements_written);
+}
+
+MojoResult MojoSystemImplReadData(MojoSystemImpl system,
+                                  MojoHandle data_pipe_consumer_handle,
+                                  void* elements,
+                                  uint32_t* num_elements,
+                                  MojoReadDataFlags flags) {
+  assert(g_system_impl_thunks.ReadData);
+  return g_system_impl_thunks.ReadData(system, data_pipe_consumer_handle,
+                                       elements, num_elements, flags);
+}
+
+MojoResult MojoSystemImplBeginReadData(MojoSystemImpl system,
+                                       MojoHandle data_pipe_consumer_handle,
+                                       const void** buffer,
+                                       uint32_t* buffer_num_elements,
+                                       MojoReadDataFlags flags) {
+  assert(g_system_impl_thunks.BeginReadData);
+  return g_system_impl_thunks.BeginReadData(system, data_pipe_consumer_handle,
+                                            buffer, buffer_num_elements, flags);
+}
+
+MojoResult MojoSystemImplEndReadData(MojoSystemImpl system,
+                                     MojoHandle data_pipe_consumer_handle,
+                                     uint32_t num_elements_read) {
+  assert(g_system_impl_thunks.EndReadData);
+  return g_system_impl_thunks.EndReadData(system, data_pipe_consumer_handle,
+                                          num_elements_read);
+}
+
+MojoResult MojoSystemImplCreateSharedBuffer(
+    MojoSystemImpl system,
+    const struct MojoCreateSharedBufferOptions* options,
+    uint64_t num_bytes,
+    MojoHandle* shared_buffer_handle) {
+  assert(g_system_impl_thunks.CreateSharedBuffer);
+  return g_system_impl_thunks.CreateSharedBuffer(system, options, num_bytes,
+                                                 shared_buffer_handle);
+}
+
+MojoResult MojoSystemImplDuplicateBufferHandle(
+    MojoSystemImpl system,
+    MojoHandle buffer_handle,
+    const struct MojoDuplicateBufferHandleOptions* options,
+    MojoHandle* new_buffer_handle) {
+  assert(g_system_impl_thunks.DuplicateBufferHandle);
+  return g_system_impl_thunks.DuplicateBufferHandle(system, buffer_handle,
+                                                    options, new_buffer_handle);
+}
+
+MojoResult MojoSystemImplMapBuffer(MojoSystemImpl system,
+                                   MojoHandle buffer_handle,
+                                   uint64_t offset,
+                                   uint64_t num_bytes,
+                                   void** buffer,
+                                   MojoMapBufferFlags flags) {
+  assert(g_system_impl_thunks.MapBuffer);
+  return g_system_impl_thunks.MapBuffer(system, buffer_handle, offset,
+                                        num_bytes, buffer, flags);
+}
+
+MojoResult MojoSystemImplUnmapBuffer(MojoSystemImpl system, void* buffer) {
+  assert(g_system_impl_thunks.UnmapBuffer);
+  return g_system_impl_thunks.UnmapBuffer(system, buffer);
+}
+
+extern "C" THUNK_EXPORT size_t MojoSetSystemImplControlThunksPrivate(
+    const MojoSystemImplControlThunksPrivate* system_thunks) {
+  if (system_thunks->size >= sizeof(g_system_impl_control_thunks))
+    g_system_impl_control_thunks = *system_thunks;
+  return sizeof(g_system_impl_control_thunks);
+}
+
+extern "C" THUNK_EXPORT size_t MojoSetSystemImplThunksPrivate(
+    const MojoSystemImplThunksPrivate* system_thunks) {
+  if (system_thunks->size >= sizeof(g_system_impl_thunks))
+    g_system_impl_thunks = *system_thunks;
+  return sizeof(g_system_impl_thunks);
+}
+
+}  // extern "C"
diff --git a/mojo/public/platform/native/system_impl_private_thunks.h b/mojo/public/platform/native/system_impl_private_thunks.h
new file mode 100644
index 0000000..c45167d
--- /dev/null
+++ b/mojo/public/platform/native/system_impl_private_thunks.h
@@ -0,0 +1,150 @@
+// Copyright 2015 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.
+
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_IMPL_PRIVATE_THUNKS_H_
+#define MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_IMPL_PRIVATE_THUNKS_H_
+
+#include "mojo/public/c/system/core.h"
+#include "mojo/public/platform/native/system_impl_private.h"
+
+// Structure used to bind the basic Mojo Core functions of a DSO to those of
+// the embedder.
+// This is the ABI between the embedder and the DSO. It can only have new
+// functions added to the end. No other changes are supported.
+#pragma pack(push, 8)
+struct MojoSystemImplControlThunksPrivate {
+  size_t size;  // Should be set to sizeof(MojoSystemImplThunks).
+  MojoSystemImpl (*GetDefaultSystemImpl)();
+  MojoSystemImpl (*CreateSystemImpl)();
+  MojoResult (*TransferHandle)(MojoSystemImpl from_system,
+                               MojoHandle handle,
+                               MojoSystemImpl to_system,
+                               MojoHandle* result_handle);
+};
+
+struct MojoSystemImplThunksPrivate {
+  size_t size;  // Should be set to sizeof(MojoExplicitThunksPrivate).
+  MojoTimeTicks (*GetTimeTicksNow)(MojoSystemImpl system);
+  MojoResult (*Close)(MojoSystemImpl system, MojoHandle handle);
+  MojoResult (*Wait)(MojoSystemImpl system,
+                     MojoHandle handle,
+                     MojoHandleSignals signals,
+                     MojoDeadline deadline,
+                     struct MojoHandleSignalsState* signals_state);
+  MojoResult (*WaitMany)(MojoSystemImpl system,
+                         const MojoHandle* handles,
+                         const MojoHandleSignals* signals,
+                         uint32_t num_handles,
+                         MojoDeadline deadline,
+                         uint32_t* result_index,
+                         struct MojoHandleSignalsState* signals_states);
+  MojoResult (*CreateMessagePipe)(
+      MojoSystemImpl system,
+      const struct MojoCreateMessagePipeOptions* options,
+      MojoHandle* message_pipe_handle0,
+      MojoHandle* message_pipe_handle1);
+  MojoResult (*WriteMessage)(MojoSystemImpl system,
+                             MojoHandle message_pipe_handle,
+                             const void* bytes,
+                             uint32_t num_bytes,
+                             const MojoHandle* handles,
+                             uint32_t num_handles,
+                             MojoWriteMessageFlags flags);
+  MojoResult (*ReadMessage)(MojoSystemImpl system,
+                            MojoHandle message_pipe_handle,
+                            void* bytes,
+                            uint32_t* num_bytes,
+                            MojoHandle* handles,
+                            uint32_t* num_handles,
+                            MojoReadMessageFlags flags);
+  MojoResult (*CreateDataPipe)(MojoSystemImpl system,
+                               const struct MojoCreateDataPipeOptions* options,
+                               MojoHandle* data_pipe_producer_handle,
+                               MojoHandle* data_pipe_consumer_handle);
+  MojoResult (*WriteData)(MojoSystemImpl system,
+                          MojoHandle data_pipe_producer_handle,
+                          const void* elements,
+                          uint32_t* num_elements,
+                          MojoWriteDataFlags flags);
+  MojoResult (*BeginWriteData)(MojoSystemImpl system,
+                               MojoHandle data_pipe_producer_handle,
+                               void** buffer,
+                               uint32_t* buffer_num_elements,
+                               MojoWriteDataFlags flags);
+  MojoResult (*EndWriteData)(MojoSystemImpl system,
+                             MojoHandle data_pipe_producer_handle,
+                             uint32_t num_elements_written);
+  MojoResult (*ReadData)(MojoSystemImpl system,
+                         MojoHandle data_pipe_consumer_handle,
+                         void* elements,
+                         uint32_t* num_elements,
+                         MojoReadDataFlags flags);
+  MojoResult (*BeginReadData)(MojoSystemImpl system,
+                              MojoHandle data_pipe_consumer_handle,
+                              const void** buffer,
+                              uint32_t* buffer_num_elements,
+                              MojoReadDataFlags flags);
+  MojoResult (*EndReadData)(MojoSystemImpl system,
+                            MojoHandle data_pipe_consumer_handle,
+                            uint32_t num_elements_read);
+  MojoResult (*CreateSharedBuffer)(
+      MojoSystemImpl system,
+      const struct MojoCreateSharedBufferOptions* options,
+      uint64_t num_bytes,
+      MojoHandle* shared_buffer_handle);
+  MojoResult (*DuplicateBufferHandle)(
+      MojoSystemImpl system,
+      MojoHandle buffer_handle,
+      const struct MojoDuplicateBufferHandleOptions* options,
+      MojoHandle* new_buffer_handle);
+  MojoResult (*MapBuffer)(MojoSystemImpl system,
+                          MojoHandle buffer_handle,
+                          uint64_t offset,
+                          uint64_t num_bytes,
+                          void** buffer,
+                          MojoMapBufferFlags flags);
+  MojoResult (*UnmapBuffer)(MojoSystemImpl system, void* buffer);
+};
+#pragma pack(pop)
+
+#ifdef __cplusplus
+inline MojoSystemImplControlThunksPrivate
+MojoMakeSystemImplControlThunksPrivate() {
+  MojoSystemImplControlThunksPrivate system_thunks = {
+      sizeof(MojoSystemImplControlThunksPrivate),
+      MojoSystemImplGetDefaultImpl,
+      MojoSystemImplCreateImpl,
+      MojoSystemImplTransferHandle};
+  return system_thunks;
+}
+
+inline MojoSystemImplThunksPrivate MojoMakeSystemImplThunksPrivate() {
+  MojoSystemImplThunksPrivate system_thunks = {
+      sizeof(MojoSystemImplThunksPrivate),
+      MojoSystemImplGetTimeTicksNow,
+      MojoSystemImplClose,
+      MojoSystemImplWait,
+      MojoSystemImplWaitMany,
+      MojoSystemImplCreateMessagePipe,
+      MojoSystemImplWriteMessage,
+      MojoSystemImplReadMessage,
+      MojoSystemImplCreateDataPipe,
+      MojoSystemImplWriteData,
+      MojoSystemImplBeginWriteData,
+      MojoSystemImplEndWriteData,
+      MojoSystemImplReadData,
+      MojoSystemImplBeginReadData,
+      MojoSystemImplEndReadData,
+      MojoSystemImplCreateSharedBuffer,
+      MojoSystemImplDuplicateBufferHandle,
+      MojoSystemImplMapBuffer,
+      MojoSystemImplUnmapBuffer};
+  return system_thunks;
+}
+
+#endif
+
+#endif  // MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_IMPL_PRIVATE_THUNKS_H_
diff --git a/mojo/public/platform/native/system_impl_private_unittest.cc b/mojo/public/platform/native/system_impl_private_unittest.cc
new file mode 100644
index 0000000..4d0e1ba
--- /dev/null
+++ b/mojo/public/platform/native/system_impl_private_unittest.cc
@@ -0,0 +1,323 @@
+// Copyright 2015 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 API, but using the explicit MojoSystemImpl parameter.
+
+#include <string.h>
+
+#include "mojo/public/c/system/core.h"
+#include "mojo/public/platform/native/system_impl_private.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+const MojoHandleSignals kSignalReadadableWritable =
+    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(SystemImplTest, GetTimeTicksNow) {
+  MojoSystemImpl system = MojoSystemImplCreateImpl();
+  const MojoTimeTicks start = MojoSystemImplGetTimeTicksNow(system);
+  EXPECT_NE(static_cast<MojoTimeTicks>(0), start)
+      << "MojoGetTimeTicksNow should return nonzero value";
+
+  // SystemImpl is leaked...
+}
+
+TEST(SystemImplTest, BasicMessagePipe) {
+  MojoSystemImpl sys0 = MojoSystemImplCreateImpl();
+  MojoSystemImpl sys1 = MojoSystemImplCreateImpl();
+  EXPECT_NE(sys0, sys1);
+
+  MojoHandle h0, h1;
+  MojoHandleSignals sig;
+  char buffer[10] = {0};
+  uint32_t buffer_size;
+
+  h0 = MOJO_HANDLE_INVALID;
+  h1 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoSystemImplCreateMessagePipe(sys0, nullptr, &h0, &h1));
+  EXPECT_NE(h0, MOJO_HANDLE_INVALID);
+  EXPECT_NE(h1, MOJO_HANDLE_INVALID);
+
+  // Move the other end of the pipe to a different SystemImpl.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplTransferHandle(sys0, h1, sys1, &h1));
+  EXPECT_NE(h1, MOJO_HANDLE_INVALID);
+
+  // Shouldn't be readable, we haven't written anything.
+  MojoHandleSignalsState state;
+  EXPECT_EQ(
+      MOJO_RESULT_DEADLINE_EXCEEDED,
+      MojoSystemImplWait(sys0, h0, MOJO_HANDLE_SIGNAL_READABLE, 0, &state));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
+  EXPECT_EQ(kSignalAll, state.satisfiable_signals);
+
+  // Should be writable.
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      MojoSystemImplWait(sys0, h0, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &state));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
+  EXPECT_EQ(kSignalAll, state.satisfiable_signals);
+
+  // Last parameter is optional.
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      MojoSystemImplWait(sys0, h0, MOJO_HANDLE_SIGNAL_WRITABLE, 0, nullptr));
+
+  // Try to read.
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            MojoSystemImplReadMessage(sys0, h0, buffer, &buffer_size, nullptr,
+                                      nullptr, MOJO_READ_MESSAGE_FLAG_NONE));
+
+  // Write to |h1|.
+  static const char kHello[] = "hello";
+  buffer_size = static_cast<uint32_t>(sizeof(kHello));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoSystemImplWriteMessage(sys1, h1, kHello, buffer_size, nullptr,
+                                       0, MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // |h0| should be readable.
+  uint32_t result_index = 1;
+  MojoHandleSignalsState states[1];
+  sig = MOJO_HANDLE_SIGNAL_READABLE;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoSystemImplWaitMany(sys0, &h0, &sig, 1, MOJO_DEADLINE_INDEFINITE,
+                                   &result_index, states));
+
+  EXPECT_EQ(0u, result_index);
+  EXPECT_EQ(kSignalReadadableWritable, states[0].satisfied_signals);
+  EXPECT_EQ(kSignalAll, states[0].satisfiable_signals);
+
+  // Read from |h0|.
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoSystemImplReadMessage(sys0, h0, buffer, &buffer_size, nullptr,
+                                      nullptr, MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(static_cast<uint32_t>(sizeof(kHello)), buffer_size);
+  EXPECT_STREQ(kHello, buffer);
+
+  // |h0| should no longer be readable.
+  EXPECT_EQ(
+      MOJO_RESULT_DEADLINE_EXCEEDED,
+      MojoSystemImplWait(sys0, h0, MOJO_HANDLE_SIGNAL_READABLE, 10, &state));
+
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
+  EXPECT_EQ(kSignalAll, state.satisfiable_signals);
+
+  // Close |h0|.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplClose(sys0, h0));
+
+  // |h1| should no longer be readable or writable.
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoSystemImplWait(sys1, h1, MOJO_HANDLE_SIGNAL_READABLE |
+                                             MOJO_HANDLE_SIGNAL_WRITABLE,
+                               1000, &state));
+
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals);
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplClose(sys1, h1));
+
+  // 2 SystemImpls are leaked...
+}
+
+TEST(SystemImplTest, BasicDataPipe) {
+  MojoSystemImpl sys0 = MojoSystemImplCreateImpl();
+  MojoSystemImpl sys1 = MojoSystemImplCreateImpl();
+  EXPECT_NE(sys0, sys1);
+
+  MojoHandle hp, hc;
+  MojoHandleSignals sig;
+  char buffer[20] = {0};
+  uint32_t buffer_size;
+  void* write_pointer;
+  const void* read_pointer;
+
+  hp = MOJO_HANDLE_INVALID;
+  hc = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoSystemImplCreateDataPipe(sys0, nullptr, &hp, &hc));
+  EXPECT_NE(hp, MOJO_HANDLE_INVALID);
+  EXPECT_NE(hc, MOJO_HANDLE_INVALID);
+
+  // Move the other end of the pipe to a different SystemImpl.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplTransferHandle(sys0, hc, sys1, &hc));
+  EXPECT_NE(hc, MOJO_HANDLE_INVALID);
+
+  // The consumer |hc| shouldn't be readable.
+  MojoHandleSignalsState state;
+  EXPECT_EQ(
+      MOJO_RESULT_DEADLINE_EXCEEDED,
+      MojoSystemImplWait(sys1, hc, MOJO_HANDLE_SIGNAL_READABLE, 0, &state));
+
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_NONE, state.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            state.satisfiable_signals);
+
+  // The producer |hp| should be writable.
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      MojoSystemImplWait(sys0, hp, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &state));
+
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            state.satisfiable_signals);
+
+  // Try to read from |hc|.
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            MojoSystemImplReadData(sys1, hc, buffer, &buffer_size,
+                                   MOJO_READ_DATA_FLAG_NONE));
+
+  // Try to begin a two-phase read from |hc|.
+  read_pointer = nullptr;
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            MojoSystemImplBeginReadData(sys1, hc, &read_pointer, &buffer_size,
+                                        MOJO_READ_DATA_FLAG_NONE));
+
+  // Write to |hp|.
+  static const char kHello[] = "hello ";
+  // Don't include terminating null.
+  buffer_size = static_cast<uint32_t>(strlen(kHello));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoSystemImplWriteData(sys0, hp, kHello, &buffer_size,
+                                    MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // |hc| should be(come) readable.
+  uint32_t result_index = 1;
+  MojoHandleSignalsState states[1];
+  sig = MOJO_HANDLE_SIGNAL_READABLE;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoSystemImplWaitMany(sys1, &hc, &sig, 1, MOJO_DEADLINE_INDEFINITE,
+                                   &result_index, states));
+
+  EXPECT_EQ(0u, result_index);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, states[0].satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            states[0].satisfiable_signals);
+
+  // Do a two-phase write to |hp|.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoSystemImplBeginWriteData(sys0, hp, &write_pointer, &buffer_size,
+                                         MOJO_WRITE_DATA_FLAG_NONE));
+  static const char kWorld[] = "world";
+  ASSERT_GE(buffer_size, sizeof(kWorld));
+  // Include the terminating null.
+  memcpy(write_pointer, kWorld, sizeof(kWorld));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoSystemImplEndWriteData(sys0, hp,
+                                       static_cast<uint32_t>(sizeof(kWorld))));
+
+  // Read one character from |hc|.
+  memset(buffer, 0, sizeof(buffer));
+  buffer_size = 1;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoSystemImplReadData(sys1, hc, buffer, &buffer_size,
+                                   MOJO_READ_DATA_FLAG_NONE));
+
+  // Close |hp|.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplClose(sys0, hp));
+
+  // |hc| should still be readable.
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      MojoSystemImplWait(sys1, hc, MOJO_HANDLE_SIGNAL_READABLE, 0, &state));
+
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            state.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            state.satisfiable_signals);
+
+  // Do a two-phase read from |hc|.
+  read_pointer = nullptr;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoSystemImplBeginReadData(sys1, hc, &read_pointer, &buffer_size,
+                                        MOJO_READ_DATA_FLAG_NONE));
+  ASSERT_LE(buffer_size, sizeof(buffer) - 1);
+  memcpy(&buffer[1], read_pointer, buffer_size);
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplEndReadData(sys1, hc, buffer_size));
+  EXPECT_STREQ("hello world", buffer);
+
+  // |hc| should no longer be readable.
+  EXPECT_EQ(
+      MOJO_RESULT_FAILED_PRECONDITION,
+      MojoSystemImplWait(sys1, hc, MOJO_HANDLE_SIGNAL_READABLE, 1000, &state));
+
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals);
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplClose(sys1, hc));
+
+  // TODO(vtl): Test the other way around -- closing the consumer should make
+  // the producer never-writable?
+
+  // 2 SystemImpls are leaked...
+}
+
+TEST(SystemImplTest, BasicSharedBuffer) {
+  MojoSystemImpl sys0 = MojoSystemImplCreateImpl();
+  MojoSystemImpl sys1 = MojoSystemImplCreateImpl();
+  EXPECT_NE(sys0, sys1);
+
+  MojoHandle h0, h1;
+  void* pointer;
+
+  // Create a shared buffer (|h0|).
+  h0 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoSystemImplCreateSharedBuffer(sys0, nullptr, 100, &h0));
+  EXPECT_NE(h0, MOJO_HANDLE_INVALID);
+
+  // Map everything.
+  pointer = nullptr;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplMapBuffer(sys0, h0, 0, 100, &pointer,
+                                                    MOJO_MAP_BUFFER_FLAG_NONE));
+  ASSERT_TRUE(pointer);
+  static_cast<char*>(pointer)[50] = 'x';
+
+  // Duplicate |h0| to |h1|.
+  h1 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoSystemImplDuplicateBufferHandle(sys0, h0, nullptr, &h1));
+  EXPECT_NE(h1, MOJO_HANDLE_INVALID);
+
+  // Move the other end of the pipe to a different SystemImpl.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplTransferHandle(sys0, h1, sys1, &h1));
+  EXPECT_NE(h1, MOJO_HANDLE_INVALID);
+
+  // Close |h0|.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplClose(sys0, h0));
+
+  // The mapping should still be good.
+  static_cast<char*>(pointer)[51] = 'y';
+
+  // Unmap it.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplUnmapBuffer(sys0, pointer));
+
+  // Map half of |h1|.
+  pointer = nullptr;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplMapBuffer(sys1, h1, 50, 50, &pointer,
+                                                    MOJO_MAP_BUFFER_FLAG_NONE));
+  ASSERT_TRUE(pointer);
+
+  // It should have what we wrote.
+  EXPECT_EQ('x', static_cast<char*>(pointer)[0]);
+  EXPECT_EQ('y', static_cast<char*>(pointer)[1]);
+
+  // Unmap it.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplUnmapBuffer(sys1, pointer));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplClose(sys1, h1));
+
+  // 2 SystemImpls are leaked...
+}
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/tools/data/unittests b/mojo/tools/data/unittests
index bce098f..ecfd2f9 100644
--- a/mojo/tools/data/unittests
+++ b/mojo/tools/data/unittests
@@ -24,6 +24,9 @@
   {
     "test": "mojo_public_utility_unittests",
   },
+  {
+    "test": "mojo_system_impl_private_unittests",
+  },
 
   # Non-system, non-public tests:
   {