From cf28cd1ed8bc378ba1260a94897ab8c9aecbc894 Mon Sep 17 00:00:00 2001 From: "davemoore@chromium.org" Date: Mon, 14 Apr 2014 20:21:40 +0000 Subject: Make mojo_system static and mojo_system_impl a component, never use both BUG= R=viettrungluu@chromium.org Review URL: https://codereview.chromium.org/231353002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@263717 0039d316-1c4b-4281-b951-d872f2087c98 --- mojo/embedder/embedder.cc | 17 +- mojo/embedder/test_embedder.cc | 15 +- mojo/mojo.gyp | 31 +- mojo/mojo_apps.gypi | 3 +- mojo/mojo_examples.gypi | 15 +- mojo/mojo_public.gypi | 45 +- mojo/mojo_services.gypi | 7 +- mojo/public/platform/native/system_thunks.cc | 168 +++++ mojo/public/platform/native/system_thunks.h | 136 ++++ mojo/public/system/core_private.cc | 177 ----- mojo/public/system/core_private.h | 87 --- mojo/shell/in_process_dynamic_service_runner.cc | 15 + mojo/system/core.cc | 579 ++++++++++++++++ mojo/system/core.h | 151 ++++ mojo/system/core_impl.cc | 580 ---------------- mojo/system/core_impl.h | 153 ---- mojo/system/core_impl_unittest.cc | 881 ------------------------ mojo/system/core_test_base.cc | 4 +- mojo/system/core_test_base.h | 6 +- mojo/system/core_unittest.cc | 881 ++++++++++++++++++++++++ mojo/system/data_pipe.h | 2 +- mojo/system/dispatcher.h | 6 +- mojo/system/entrypoints.cc | 150 ++++ mojo/system/entrypoints.h | 24 + mojo/system/handle_table.cc | 2 +- mojo/system/handle_table.h | 22 +- mojo/system/mapping_table.cc | 2 +- mojo/system/mapping_table.h | 13 +- mojo/system/message_pipe.cc | 6 +- mojo/system/message_pipe.h | 5 +- mojo/system/simple_dispatcher.h | 4 +- mojo/system/waiter_list.h | 4 +- 32 files changed, 2185 insertions(+), 2006 deletions(-) create mode 100644 mojo/public/platform/native/system_thunks.cc create mode 100644 mojo/public/platform/native/system_thunks.h delete mode 100644 mojo/public/system/core_private.cc delete mode 100644 mojo/public/system/core_private.h create mode 100644 mojo/system/core.cc create mode 100644 mojo/system/core.h delete mode 100644 mojo/system/core_impl.cc delete mode 100644 mojo/system/core_impl.h delete mode 100644 mojo/system/core_impl_unittest.cc create mode 100644 mojo/system/core_unittest.cc create mode 100644 mojo/system/entrypoints.cc create mode 100644 mojo/system/entrypoints.h (limited to 'mojo') diff --git a/mojo/embedder/embedder.cc b/mojo/embedder/embedder.cc index db8d930..7698160 100644 --- a/mojo/embedder/embedder.cc +++ b/mojo/embedder/embedder.cc @@ -9,7 +9,8 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "mojo/system/channel.h" -#include "mojo/system/core_impl.h" +#include "mojo/system/core.h" +#include "mojo/system/entrypoints.h" #include "mojo/system/message_pipe.h" #include "mojo/system/message_pipe_dispatcher.h" #include "mojo/system/raw_channel.h" @@ -17,6 +18,10 @@ namespace mojo { namespace embedder { +void Init() { + system::entrypoints::SetCore(new system::Core()); +} + struct ChannelInfo { scoped_refptr channel; }; @@ -55,10 +60,6 @@ static void CreateChannelOnIOThread( } } -void Init() { - Core::Init(new system::CoreImpl()); -} - ScopedMessagePipeHandle CreateChannel( ScopedPlatformHandle platform_handle, scoped_refptr io_thread_task_runner, @@ -70,10 +71,10 @@ ScopedMessagePipeHandle CreateChannel( scoped_refptr > remote_message_pipe = system::MessagePipeDispatcher::CreateRemoteMessagePipe(); - system::CoreImpl* core_impl = static_cast(Core::Get()); - DCHECK(core_impl); + system::Core* core = system::entrypoints::GetCore(); + DCHECK(core); ScopedMessagePipeHandle rv( - MessagePipeHandle(core_impl->AddDispatcher(remote_message_pipe.first))); + MessagePipeHandle(core->AddDispatcher(remote_message_pipe.first))); // TODO(vtl): Do we properly handle the failure case here? if (rv.is_valid()) { io_thread_task_runner->PostTask(FROM_HERE, diff --git a/mojo/embedder/test_embedder.cc b/mojo/embedder/test_embedder.cc index 855d1da..049855d 100644 --- a/mojo/embedder/test_embedder.cc +++ b/mojo/embedder/test_embedder.cc @@ -6,7 +6,8 @@ #include "base/logging.h" #include "base/macros.h" -#include "mojo/system/core_impl.h" +#include "mojo/system/core.h" +#include "mojo/system/entrypoints.h" #include "mojo/system/handle_table.h" namespace mojo { @@ -14,7 +15,7 @@ namespace mojo { namespace system { namespace internal { -bool ShutdownCheckNoLeaks(CoreImpl* core_impl) { +bool ShutdownCheckNoLeaks(Core* core_impl) { // No point in taking the lock. const HandleTable::HandleToEntryMap& handle_to_entry_map = core_impl->handle_table_.handle_to_entry_map_; @@ -38,12 +39,12 @@ namespace embedder { namespace test { bool Shutdown() { - system::CoreImpl* core_impl = static_cast(Core::Get()); - CHECK(core_impl); - Core::Reset(); + system::Core* core = system::entrypoints::GetCore(); + CHECK(core); + system::entrypoints::SetCore(NULL); - bool rv = system::internal::ShutdownCheckNoLeaks(core_impl); - delete core_impl; + bool rv = system::internal::ShutdownCheckNoLeaks(core); + delete core; return rv; } diff --git a/mojo/mojo.gyp b/mojo/mojo.gyp index c721cfa..27a2efe 100644 --- a/mojo/mojo.gyp +++ b/mojo/mojo.gyp @@ -36,6 +36,7 @@ 'mojo_js', 'mojo_js_unittests', 'mojo_message_generator', + 'mojo_native_viewport_service', 'mojo_pepper_container_app', 'mojo_public_test_utils', 'mojo_public_bindings_unittests', @@ -70,7 +71,6 @@ '../base/base.gyp:base', '../base/base.gyp:test_support_base', '../testing/gtest.gyp:gtest', - 'mojo_system', 'mojo_system_impl', 'mojo_test_support', 'mojo_test_support_impl', @@ -84,7 +84,6 @@ 'type': 'static_library', 'dependencies': [ '../base/base.gyp:test_support_base', - 'mojo_system', 'mojo_system_impl', 'mojo_test_support', 'mojo_test_support_impl', @@ -97,12 +96,12 @@ 'target_name': 'mojo_system_impl', 'type': '<(component)', 'dependencies': [ - 'mojo_system', '../base/base.gyp:base', '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', ], 'defines': [ 'MOJO_SYSTEM_IMPL_IMPLEMENTATION', + 'MOJO_SYSTEM_IMPLEMENTATION', ], 'sources': [ 'embedder/embedder.cc', @@ -119,8 +118,8 @@ 'system/channel.cc', 'system/channel.h', 'system/constants.h', - 'system/core_impl.cc', - 'system/core_impl.h', + 'system/core.cc', + 'system/core.h', 'system/data_pipe.cc', 'system/data_pipe.h', 'system/data_pipe_consumer_dispatcher.cc', @@ -129,6 +128,7 @@ 'system/data_pipe_producer_dispatcher.h', 'system/dispatcher.cc', 'system/dispatcher.h', + 'system/entrypoints.cc', 'system/handle_table.cc', 'system/handle_table.h', 'system/local_data_pipe.cc', @@ -179,16 +179,16 @@ 'target_name': 'mojo_system_unittests', 'type': 'executable', 'dependencies': [ + '../base/base.gyp:base', '../base/base.gyp:run_all_unittests', '../testing/gtest.gyp:gtest', 'mojo_common_test_support', - 'mojo_system', 'mojo_system_impl', ], 'sources': [ 'embedder/embedder_unittest.cc', 'embedder/platform_channel_pair_posix_unittest.cc', - 'system/core_impl_unittest.cc', + 'system/core_unittest.cc', 'system/core_test_base.cc', 'system/core_test_base.h', 'system/data_pipe_unittest.cc', @@ -223,6 +223,7 @@ 'mojo_gles2', 'mojo_gles2_bindings', 'mojo_environment_chromium', + 'mojo_system_impl', ], 'defines': [ 'MOJO_GLES2_IMPL_IMPLEMENTATION', @@ -257,7 +258,11 @@ 'dependencies': [ '../base/base.gyp:base', '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', - 'mojo_system', + 'mojo_system_impl', + ], + 'export_dependent_settings': [ + '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', + 'mojo_system_impl', ], 'sources': [ 'common/common_type_converters.cc', @@ -280,7 +285,6 @@ '../base/base.gyp:base', '../base/base.gyp:test_support_base', '../testing/gtest.gyp:gtest', - 'mojo_system', 'mojo_system_impl', ], 'sources': [ @@ -304,8 +308,6 @@ 'mojo_common_test_support', 'mojo_public_test_utils', 'mojo_run_all_unittests', - 'mojo_system', - 'mojo_system_impl', ], 'sources': [ 'common/common_type_converters_unittest.cc', @@ -368,6 +370,7 @@ 'mojo_common_lib', 'mojo_environment_chromium', 'mojo_shell_bindings', + 'mojo_system_impl', ], 'sources': [ 'service_manager/service_loader.h', @@ -405,7 +408,6 @@ 'mojo_gles2_impl', 'mojo_service_manager', 'mojo_shell_bindings', - 'mojo_system', 'mojo_system_impl', 'mojo_native_viewport_service', 'mojo_spy', @@ -466,7 +468,6 @@ 'mojo_environment_chromium', 'mojo_service_manager', 'mojo_shell_lib', - 'mojo_system', 'mojo_system_impl', ], 'sources': [ @@ -486,7 +487,6 @@ 'mojo_run_all_unittests', 'mojo_service_manager', 'mojo_shell_client', - 'mojo_system', ], 'variables': { 'mojom_base_output_dir': 'mojo', @@ -505,13 +505,11 @@ '../gin/gin.gyp:gin', '../v8/tools/gyp/v8.gyp:v8', 'mojo_common_lib', - 'mojo_system', ], 'export_dependent_settings': [ '../base/base.gyp:base', '../gin/gin.gyp:gin', 'mojo_common_lib', - 'mojo_system', ], 'sources': [ 'bindings/js/core.cc', @@ -546,7 +544,6 @@ 'mojo_bindings', 'mojo_common_lib', 'mojo_environment_chromium', - 'mojo_system', 'mojo_system_impl', ], 'sources': [ diff --git a/mojo/mojo_apps.gypi b/mojo/mojo_apps.gypi index 934d36a..62d904d 100644 --- a/mojo/mojo_apps.gypi +++ b/mojo/mojo_apps.gypi @@ -14,7 +14,6 @@ 'mojo_gles2_bindings', 'mojo_js_bindings_lib', 'mojo_native_viewport_bindings', - 'mojo_system', ], 'export_dependent_settings': [ '../base/base.gyp:base', @@ -23,7 +22,6 @@ 'mojo_gles2', 'mojo_gles2_bindings', 'mojo_native_viewport_bindings', - 'mojo_system', ], 'sources': [ 'apps/js/mojo_runner_delegate.cc', @@ -56,6 +54,7 @@ 'type': 'shared_library', 'dependencies': [ 'mojo_js_lib', + 'mojo_system_impl', ], 'sources': [ 'apps/js/main.cc', diff --git a/mojo/mojo_examples.gypi b/mojo/mojo_examples.gypi index 15b4f619..590285f 100644 --- a/mojo/mojo_examples.gypi +++ b/mojo/mojo_examples.gypi @@ -44,7 +44,6 @@ '../skia/skia.gyp:skia', '../gpu/gpu.gyp:gles2_implementation', 'mojo_gles2', - 'mojo_system', ], 'sources': [ 'examples/compositor_app/mojo_context_provider.cc', @@ -65,7 +64,7 @@ 'mojo_gles2', 'mojo_native_viewport_bindings', 'mojo_shell_client', - 'mojo_system', + 'mojo_system_impl', ], 'sources': [ 'examples/compositor_app/compositor_app.cc', @@ -94,7 +93,7 @@ 'mojo_gles2', 'mojo_native_viewport_bindings', 'mojo_shell_client', - 'mojo_system', + 'mojo_system_impl', ], 'defines': [ # We don't really want to export. We could change how @@ -215,7 +214,7 @@ 'mojo_environment_chromium', 'mojo_gles2', 'mojo_shell_client', - 'mojo_system', + 'mojo_system_impl' ], 'sources': [ 'examples/aura_demo/aura_demo.cc', @@ -240,11 +239,9 @@ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], 'export_dependent_settings': [ 'mojo_bindings', - 'mojo_system', ], 'dependencies': [ 'mojo_bindings', - 'mojo_system', ], }, { @@ -266,7 +263,7 @@ 'mojo_gles2', 'mojo_launcher_bindings', 'mojo_shell_client', - 'mojo_system', + 'mojo_system_impl', ], 'sources': [ 'examples/launcher/launcher.cc', @@ -291,11 +288,9 @@ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], 'export_dependent_settings': [ 'mojo_bindings', - 'mojo_system', ], 'dependencies': [ 'mojo_bindings', - 'mojo_system', ], }, { @@ -309,7 +304,7 @@ 'mojo_launcher_bindings', 'mojo_native_viewport_bindings', 'mojo_shell_client', - 'mojo_system', + 'mojo_system_impl', 'mojo_view_manager_bindings', ], 'sources': [ diff --git a/mojo/mojo_public.gypi b/mojo/mojo_public.gypi index 261170a..15eccb0 100644 --- a/mojo/mojo_public.gypi +++ b/mojo/mojo_public.gypi @@ -2,7 +2,7 @@ 'targets': [ { 'target_name': 'mojo_system', - 'type': '<(component)', + 'type': 'static_library', 'defines': [ 'MOJO_SYSTEM_IMPLEMENTATION', ], @@ -19,22 +19,8 @@ 'public/c/system/core.h', 'public/c/system/macros.h', 'public/c/system/system_export.h', - 'public/system/core_private.cc', - 'public/system/core_private.h', - ], - 'conditions': [ - ['OS=="mac"', { - 'xcode_settings': { - # Make it a run-path dependent library. - 'DYLIB_INSTALL_NAME_BASE': '@rpath', - }, - 'direct_dependent_settings': { - 'xcode_settings': { - # Look for run-path dependent libraries in the loader's directory. - 'LD_RUNPATH_SEARCH_PATHS': [ '@loader_path/.', ], - }, - }, - }], + 'public/platform/native/system_thunks.cc', + 'public/platform/native/system_thunks.h', ], }, { @@ -68,13 +54,7 @@ ['OS=="mac"', { 'xcode_settings': { # Make it a run-path dependent library. - 'DYLIB_INSTALL_NAME_BASE': '@rpath', - }, - 'direct_dependent_settings': { - 'xcode_settings': { - # Look for run-path dependent libraries in the loader's directory. - 'LD_RUNPATH_SEARCH_PATHS': [ '@loader_path/.', ], - }, + 'DYLIB_INSTALL_NAME_BASE': '@loader_path', }, }], ], @@ -103,13 +83,7 @@ ['OS=="mac"', { 'xcode_settings': { # Make it a run-path dependent library. - 'DYLIB_INSTALL_NAME_BASE': '@rpath', - }, - 'direct_dependent_settings': { - 'xcode_settings': { - # Look for run-path dependent libraries in the loader's directory. - 'LD_RUNPATH_SEARCH_PATHS': [ '@loader_path/.', ], - }, + 'DYLIB_INSTALL_NAME_BASE': '@loader_path', }, }], ], @@ -120,7 +94,6 @@ 'dependencies': [ '../base/base.gyp:base', '../testing/gtest.gyp:gtest', - 'mojo_system', 'mojo_test_support', ], 'sources': [ @@ -139,7 +112,6 @@ 'mojo_public_test_utils', 'mojo_run_all_unittests', 'mojo_sample_service', - 'mojo_system', 'mojo_utility', ], 'sources': [ @@ -171,7 +143,6 @@ 'mojo_environment_standalone', 'mojo_public_test_utils', 'mojo_run_all_unittests', - 'mojo_system', 'mojo_utility', ], 'sources': [ @@ -187,7 +158,6 @@ 'mojo_bindings', 'mojo_public_test_utils', 'mojo_run_all_unittests', - 'mojo_system', ], 'sources': [ 'public/c/system/tests/core_unittest.cc', @@ -206,7 +176,6 @@ 'mojo_bindings', 'mojo_public_test_utils', 'mojo_run_all_unittests', - 'mojo_system', 'mojo_utility', ], 'sources': [ @@ -232,7 +201,6 @@ '../testing/gtest.gyp:gtest', 'mojo_public_test_utils', 'mojo_run_all_perftests', - 'mojo_system', 'mojo_utility', ], 'sources': [ @@ -301,11 +269,9 @@ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], 'export_dependent_settings': [ 'mojo_bindings', - 'mojo_system', ], 'dependencies': [ 'mojo_bindings', - 'mojo_system', ], }, { @@ -366,7 +332,6 @@ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], 'dependencies': [ 'mojo_bindings', - 'mojo_system', ], 'export_dependent_settings': [ 'mojo_bindings', diff --git a/mojo/mojo_services.gypi b/mojo/mojo_services.gypi index f2106a7..208f14b 100644 --- a/mojo/mojo_services.gypi +++ b/mojo/mojo_services.gypi @@ -16,12 +16,10 @@ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], 'export_dependent_settings': [ 'mojo_bindings', - 'mojo_system', ], 'dependencies': [ '../gpu/gpu.gyp:command_buffer_common', 'mojo_bindings', - 'mojo_system', ], }, { @@ -55,16 +53,14 @@ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], 'export_dependent_settings': [ 'mojo_bindings', - 'mojo_system', ], 'dependencies': [ 'mojo_bindings', - 'mojo_system', ], }, { 'target_name': 'mojo_native_viewport_service', - 'type': '<(component)', + 'type': 'shared_library', 'dependencies': [ '../base/base.gyp:base', '../ui/events/events.gyp:events', @@ -75,6 +71,7 @@ 'mojo_gles2_service', 'mojo_native_viewport_bindings', 'mojo_shell_client', + 'mojo_system_impl', ], 'defines': [ 'MOJO_NATIVE_VIEWPORT_IMPLEMENTATION', diff --git a/mojo/public/platform/native/system_thunks.cc b/mojo/public/platform/native/system_thunks.cc new file mode 100644 index 0000000..bd82b00 --- /dev/null +++ b/mojo/public/platform/native/system_thunks.cc @@ -0,0 +1,168 @@ +// Copyright 2013 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_thunks.h" + +#include + +extern "C" { + +static MojoSystemThunks g_thunks = {0}; + +MojoTimeTicks MojoGetTimeTicksNow() { + assert(g_thunks.GetTimeTicksNow); + return g_thunks.GetTimeTicksNow(); +} + +MojoResult MojoClose(MojoHandle handle) { + assert(g_thunks.Close); + return g_thunks.Close(handle); +} + +MojoResult MojoWait(MojoHandle handle, + MojoWaitFlags flags, + MojoDeadline deadline) { + assert(g_thunks.Wait); + return g_thunks.Wait(handle, flags, deadline); +} + +MojoResult MojoWaitMany(const MojoHandle* handles, + const MojoWaitFlags* flags, + uint32_t num_handles, + MojoDeadline deadline) { + assert(g_thunks.WaitMany); + return g_thunks.WaitMany(handles, flags, num_handles, deadline); +} + +MojoResult MojoCreateMessagePipe(MojoHandle* message_pipe_handle0, + MojoHandle* message_pipe_handle1) { + assert(g_thunks.CreateMessagePipe); + return g_thunks.CreateMessagePipe(message_pipe_handle0, message_pipe_handle1); +} + +MojoResult MojoWriteMessage(MojoHandle message_pipe_handle, + const void* bytes, + uint32_t num_bytes, + const MojoHandle* handles, + uint32_t num_handles, + MojoWriteMessageFlags flags) { + assert(g_thunks.WriteMessage); + return g_thunks.WriteMessage(message_pipe_handle, bytes, num_bytes, handles, + num_handles, flags); +} + +MojoResult MojoReadMessage(MojoHandle message_pipe_handle, + void* bytes, + uint32_t* num_bytes, + MojoHandle* handles, + uint32_t* num_handles, + MojoReadMessageFlags flags) { + assert(g_thunks.ReadMessage); + return g_thunks.ReadMessage(message_pipe_handle, bytes, num_bytes, handles, + num_handles, flags); +} + +MojoResult MojoCreateDataPipe(const MojoCreateDataPipeOptions* options, + MojoHandle* data_pipe_producer_handle, + MojoHandle* data_pipe_consumer_handle) { + assert(g_thunks.CreateDataPipe); + return g_thunks.CreateDataPipe(options, data_pipe_producer_handle, + data_pipe_consumer_handle); +} + +MojoResult MojoWriteData(MojoHandle data_pipe_producer_handle, + const void* elements, + uint32_t* num_elements, + MojoWriteDataFlags flags) { + assert(g_thunks.WriteData); + return g_thunks.WriteData(data_pipe_producer_handle, elements, num_elements, + flags); +} + +MojoResult MojoBeginWriteData(MojoHandle data_pipe_producer_handle, + void** buffer, + uint32_t* buffer_num_elements, + MojoWriteDataFlags flags) { + assert(g_thunks.BeginWriteData); + return g_thunks.BeginWriteData(data_pipe_producer_handle, buffer, + buffer_num_elements, flags); +} + +MojoResult MojoEndWriteData(MojoHandle data_pipe_producer_handle, + uint32_t num_elements_written) { + assert(g_thunks.EndWriteData); + return g_thunks.EndWriteData(data_pipe_producer_handle, num_elements_written); +} + +MojoResult MojoReadData(MojoHandle data_pipe_consumer_handle, + void* elements, + uint32_t* num_elements, + MojoReadDataFlags flags) { + assert(g_thunks.ReadData); + return g_thunks.ReadData(data_pipe_consumer_handle, elements, num_elements, + flags); +} + +MojoResult MojoBeginReadData(MojoHandle data_pipe_consumer_handle, + const void** buffer, + uint32_t* buffer_num_elements, + MojoReadDataFlags flags) { + assert(g_thunks.BeginReadData); + return g_thunks.BeginReadData(data_pipe_consumer_handle, buffer, + buffer_num_elements, flags); +} + +MojoResult MojoEndReadData(MojoHandle data_pipe_consumer_handle, + uint32_t num_elements_read) { + assert(g_thunks.EndReadData); + return g_thunks.EndReadData(data_pipe_consumer_handle, num_elements_read); +} + +MojoResult MojoCreateSharedBuffer( + const struct MojoCreateSharedBufferOptions* options, + uint64_t num_bytes, + MojoHandle* shared_buffer_handle) { + assert(g_thunks.CreateSharedBuffer); + return g_thunks.CreateSharedBuffer(options, num_bytes, shared_buffer_handle); +} + +MojoResult MojoDuplicateBufferHandle( + MojoHandle buffer_handle, + const struct MojoDuplicateBufferHandleOptions* options, + MojoHandle* new_buffer_handle) { + assert(g_thunks.DuplicateBufferHandle); + return g_thunks.DuplicateBufferHandle(buffer_handle, options, + new_buffer_handle); +} + +MojoResult MojoMapBuffer(MojoHandle buffer_handle, + uint64_t offset, + uint64_t num_bytes, + void** buffer, + MojoMapBufferFlags flags) { + assert(g_thunks.MapBuffer); + return g_thunks.MapBuffer(buffer_handle, offset, num_bytes, buffer, flags); +} + +MojoResult MojoUnmapBuffer(void* buffer) { + assert(g_thunks.UnmapBuffer); + return g_thunks.UnmapBuffer(buffer); +} + +// Call this function by looking +// Always export this api. +#if defined(WIN32) +#define THUNK_EXPORT __declspec(dllexport) +#else +#define THUNK_EXPORT __attribute__((visibility("default"))) +#endif + +extern "C" THUNK_EXPORT size_t MojoSetSystemThunks( + const MojoSystemThunks* system_thunks) { + if (system_thunks->size >= sizeof(g_thunks)) + g_thunks = *system_thunks; + return sizeof(g_thunks); +} + +} // extern "C" diff --git a/mojo/public/platform/native/system_thunks.h b/mojo/public/platform/native/system_thunks.h new file mode 100644 index 0000000..9c366e5 --- /dev/null +++ b/mojo/public/platform/native/system_thunks.h @@ -0,0 +1,136 @@ +// 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_PLATFORM_NATIVE_SYSTEM_THUNKS_H_ +#define MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_THUNKS_H_ + +#include + +#include "mojo/public/c/system/core.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. +// The typical usage would look like: +// base::ScopedNativeLibrary app_library( +// base::LoadNativeLibrary(app_path_, &error)); +// typedef MojoResult (*MojoSetSystemThunksFn)(MojoSystemThunks*); +// MojoSetSystemThunksFn mojo_set_system_thunks_fn = +// reinterpret_cast(app_library.GetFunctionPointer( +// "MojoSetSystemThunks")); +// MojoSystemThunks system_thunks = MojoMakeSystemThunks(); +// size_t expected_size = mojo_set_system_thunks_fn(&system_thunks); +// if (expected_size > sizeof(MojoSystemThunks)) { +// LOG(ERROR) +// << "Invalid DSO. Expected MojoSystemThunks size: " +// << expected_size; +// break; +// } + +// 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 MojoSystemThunks { + size_t size; // Should be set to sizeof(MojoSystemThunks). + MojoTimeTicks (*GetTimeTicksNow)(); + MojoResult (*Close)(MojoHandle handle); + MojoResult (*Wait)(MojoHandle handle, + MojoWaitFlags flags, + MojoDeadline deadline); + MojoResult (*WaitMany)(const MojoHandle* handles, + const MojoWaitFlags* flags, + uint32_t num_handles, + MojoDeadline deadline); + MojoResult (*CreateMessagePipe)(MojoHandle* message_pipe_handle0, + MojoHandle* message_pipe_handle1); + MojoResult (*WriteMessage)(MojoHandle message_pipe_handle, + const void* bytes, + uint32_t num_bytes, + const MojoHandle* handles, + uint32_t num_handles, + MojoWriteMessageFlags flags); + MojoResult (*ReadMessage)(MojoHandle message_pipe_handle, + void* bytes, + uint32_t* num_bytes, + MojoHandle* handles, + uint32_t* num_handles, + MojoReadMessageFlags flags); + MojoResult (*CreateDataPipe)(const MojoCreateDataPipeOptions* options, + MojoHandle* data_pipe_producer_handle, + MojoHandle* data_pipe_consumer_handle); + MojoResult (*WriteData)(MojoHandle data_pipe_producer_handle, + const void* elements, + uint32_t* num_elements, + MojoWriteDataFlags flags); + MojoResult (*BeginWriteData)(MojoHandle data_pipe_producer_handle, + void** buffer, + uint32_t* buffer_num_elements, + MojoWriteDataFlags flags); + MojoResult (*EndWriteData)(MojoHandle data_pipe_producer_handle, + uint32_t num_elements_written); + MojoResult (*ReadData)(MojoHandle data_pipe_consumer_handle, + void* elements, + uint32_t* num_elements, + MojoReadDataFlags flags); + MojoResult (*BeginReadData)(MojoHandle data_pipe_consumer_handle, + const void** buffer, + uint32_t* buffer_num_elements, + MojoReadDataFlags flags); + MojoResult (*EndReadData)(MojoHandle data_pipe_consumer_handle, + uint32_t num_elements_read); + MojoResult (*CreateSharedBuffer)( + const MojoCreateSharedBufferOptions* options, + uint64_t num_bytes, + MojoHandle* shared_buffer_handle); + MojoResult (*DuplicateBufferHandle)( + MojoHandle buffer_handle, + const MojoDuplicateBufferHandleOptions* options, + MojoHandle* new_buffer_handle); + MojoResult (*MapBuffer)(MojoHandle buffer_handle, + uint64_t offset, + uint64_t num_bytes, + void** buffer, + MojoMapBufferFlags flags); + MojoResult (*UnmapBuffer)(void* buffer); +}; +#pragma pack(pop) + +// Intended to be called from the embedder. Returns a |MojoCore| initialized +// to contain pointers to each of the embedder's MojoCore functions. +inline MojoSystemThunks MojoMakeSystemThunks() { + MojoSystemThunks system_thunks = { + sizeof(MojoSystemThunks), + MojoGetTimeTicksNow, + MojoClose, + MojoWait, + MojoWaitMany, + MojoCreateMessagePipe, + MojoWriteMessage, + MojoReadMessage, + MojoCreateDataPipe, + MojoWriteData, + MojoBeginWriteData, + MojoEndWriteData, + MojoReadData, + MojoBeginReadData, + MojoEndReadData, + MojoCreateSharedBuffer, + MojoDuplicateBufferHandle, + MojoMapBuffer, + MojoUnmapBuffer + }; + return system_thunks; +} + +// Use this type for the function found by dynamically discovering it in +// a DSO linked with mojo_system. For example: +// MojoSetSystemThunksFn mojo_set_system_thunks_fn = +// reinterpret_cast(app_library.GetFunctionPointer( +// "MojoSetSystemThunks")); +// The expected size of |system_thunks} is returned. +// The contents of |system_thunks| are copied. +typedef size_t (*MojoSetSystemThunksFn)(const MojoSystemThunks* system_thunks); + +#endif // MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_THUNKS_H_ diff --git a/mojo/public/system/core_private.cc b/mojo/public/system/core_private.cc deleted file mode 100644 index bd9d6b8..0000000 --- a/mojo/public/system/core_private.cc +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2013 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/system/core_private.h" - -#include -#include - -static mojo::Core* g_core = NULL; - -extern "C" { - -MojoTimeTicks MojoGetTimeTicksNow() { - assert(g_core); - return g_core->GetTimeTicksNow(); -} - -MojoResult MojoClose(MojoHandle handle) { - assert(g_core); - return g_core->Close(handle); -} - -MojoResult MojoWait(MojoHandle handle, - MojoWaitFlags flags, - MojoDeadline deadline) { - assert(g_core); - return g_core->Wait(handle, flags, deadline); -} - -MojoResult MojoWaitMany(const MojoHandle* handles, - const MojoWaitFlags* flags, - uint32_t num_handles, - MojoDeadline deadline) { - assert(g_core); - return g_core->WaitMany(handles, flags, num_handles, deadline); -} - -MojoResult MojoCreateMessagePipe(MojoHandle* message_pipe_handle0, - MojoHandle* message_pipe_handle1) { - assert(g_core); - return g_core->CreateMessagePipe(message_pipe_handle0, message_pipe_handle1); -} - -MojoResult MojoWriteMessage(MojoHandle message_pipe_handle, - const void* bytes, - uint32_t num_bytes, - const MojoHandle* handles, - uint32_t num_handles, - MojoWriteMessageFlags flags) { - assert(g_core); - return g_core->WriteMessage(message_pipe_handle, bytes, num_bytes, handles, - num_handles, flags); -} - -MojoResult MojoReadMessage(MojoHandle message_pipe_handle, - void* bytes, - uint32_t* num_bytes, - MojoHandle* handles, - uint32_t* num_handles, - MojoReadMessageFlags flags) { - assert(g_core); - return g_core->ReadMessage(message_pipe_handle, bytes, num_bytes, handles, - num_handles, flags); -} - -MojoResult MojoCreateDataPipe(const MojoCreateDataPipeOptions* options, - MojoHandle* data_pipe_producer_handle, - MojoHandle* data_pipe_consumer_handle) { - assert(g_core); - return g_core->CreateDataPipe(options, data_pipe_producer_handle, - data_pipe_consumer_handle); -} - -MojoResult MojoWriteData(MojoHandle data_pipe_producer_handle, - const void* elements, - uint32_t* num_elements, - MojoWriteDataFlags flags) { - assert(g_core); - return g_core->WriteData(data_pipe_producer_handle, elements, num_elements, - flags); -} - -MojoResult MojoBeginWriteData(MojoHandle data_pipe_producer_handle, - void** buffer, - uint32_t* buffer_num_elements, - MojoWriteDataFlags flags) { - assert(g_core); - return g_core->BeginWriteData(data_pipe_producer_handle, buffer, - buffer_num_elements, flags); -} - -MojoResult MojoEndWriteData(MojoHandle data_pipe_producer_handle, - uint32_t num_elements_written) { - assert(g_core); - return g_core->EndWriteData(data_pipe_producer_handle, num_elements_written); -} - -MojoResult MojoReadData(MojoHandle data_pipe_consumer_handle, - void* elements, - uint32_t* num_elements, - MojoReadDataFlags flags) { - assert(g_core); - return g_core->ReadData(data_pipe_consumer_handle, elements, num_elements, - flags); -} - -MojoResult MojoBeginReadData(MojoHandle data_pipe_consumer_handle, - const void** buffer, - uint32_t* buffer_num_elements, - MojoReadDataFlags flags) { - assert(g_core); - return g_core->BeginReadData(data_pipe_consumer_handle, buffer, - buffer_num_elements, flags); -} - -MojoResult MojoEndReadData(MojoHandle data_pipe_consumer_handle, - uint32_t num_elements_read) { - assert(g_core); - return g_core->EndReadData(data_pipe_consumer_handle, num_elements_read); -} - -MojoResult MojoCreateSharedBuffer( - const struct MojoCreateSharedBufferOptions* options, - uint64_t num_bytes, - MojoHandle* shared_buffer_handle) { - assert(g_core); - return g_core->CreateSharedBuffer(options, num_bytes, shared_buffer_handle); -} - -MojoResult MojoDuplicateBufferHandle( - MojoHandle buffer_handle, - const struct MojoDuplicateBufferHandleOptions* options, - MojoHandle* new_buffer_handle) { - assert(g_core); - return g_core->DuplicateBufferHandle(buffer_handle, options, - new_buffer_handle); -} - -MojoResult MojoMapBuffer(MojoHandle buffer_handle, - uint64_t offset, - uint64_t num_bytes, - void** buffer, - MojoMapBufferFlags flags) { - assert(g_core); - return g_core->MapBuffer(buffer_handle, offset, num_bytes, buffer, flags); -} - -MojoResult MojoUnmapBuffer(void* buffer) { - assert(g_core); - return g_core->UnmapBuffer(buffer); -} - -} // extern "C" - -namespace mojo { - -Core::~Core() { -} - -// static -void Core::Init(Core* core) { - assert(!g_core); - g_core = core; -} - -// static -Core* Core::Get() { - return g_core; -} - -// static -void Core::Reset() { - g_core = NULL; -} - -} // namespace mojo diff --git a/mojo/public/system/core_private.h b/mojo/public/system/core_private.h deleted file mode 100644 index 057bfca..0000000 --- a/mojo/public/system/core_private.h +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2013 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_SYSTEM_CORE_PRIVATE_H_ -#define MOJO_PUBLIC_SYSTEM_CORE_PRIVATE_H_ - -#include "mojo/public/c/system/core.h" - -namespace mojo { - -// Implementors of the core APIs can use this interface to install their -// implementation into the mojo_system dynamic library. Mojo clients should not -// call these functions directly. -class MOJO_SYSTEM_EXPORT Core { - public: - virtual ~Core(); - - static void Init(Core* core); - static Core* Get(); - static void Reset(); - - virtual MojoTimeTicks GetTimeTicksNow() = 0; - virtual MojoResult Close(MojoHandle handle) = 0; - virtual MojoResult Wait(MojoHandle handle, - MojoWaitFlags flags, - MojoDeadline deadline) = 0; - virtual MojoResult WaitMany(const MojoHandle* handles, - const MojoWaitFlags* flags, - uint32_t num_handles, - MojoDeadline deadline) = 0; - virtual MojoResult CreateMessagePipe(MojoHandle* message_pipe_handle0, - MojoHandle* message_pipe_handle1) = 0; - virtual MojoResult WriteMessage(MojoHandle message_pipe_handle, - const void* bytes, - uint32_t num_bytes, - const MojoHandle* handles, - uint32_t num_handles, - MojoWriteMessageFlags flags) = 0; - virtual MojoResult ReadMessage(MojoHandle message_pipe_handle, - void* bytes, - uint32_t* num_bytes, - MojoHandle* handles, - uint32_t* num_handles, - MojoReadMessageFlags flags) = 0; - virtual MojoResult CreateDataPipe(const MojoCreateDataPipeOptions* options, - MojoHandle* data_pipe_producer_handle, - MojoHandle* data_pipe_consumer_handle) = 0; - virtual MojoResult WriteData(MojoHandle data_pipe_producer_handle, - const void* elements, - uint32_t* num_elements, - MojoWriteDataFlags flags) = 0; - virtual MojoResult BeginWriteData(MojoHandle data_pipe_producer_handle, - void** buffer, - uint32_t* buffer_num_elements, - MojoWriteDataFlags flags) = 0; - virtual MojoResult EndWriteData(MojoHandle data_pipe_producer_handle, - uint32_t num_elements_written) = 0; - virtual MojoResult ReadData(MojoHandle data_pipe_consumer_handle, - void* elements, - uint32_t* num_elements, - MojoReadDataFlags flags) = 0; - virtual MojoResult BeginReadData(MojoHandle data_pipe_consumer_handle, - const void** buffer, - uint32_t* buffer_num_elements, - MojoReadDataFlags flags) = 0; - virtual MojoResult EndReadData(MojoHandle data_pipe_consumer_handle, - uint32_t num_elements_read) = 0; - virtual MojoResult CreateSharedBuffer( - const MojoCreateSharedBufferOptions* options, - uint64_t num_bytes, - MojoHandle* shared_buffer_handle) = 0; - virtual MojoResult DuplicateBufferHandle( - MojoHandle buffer_handle, - const MojoDuplicateBufferHandleOptions* options, - MojoHandle* new_buffer_handle) = 0; - virtual MojoResult MapBuffer(MojoHandle buffer_handle, - uint64_t offset, - uint64_t num_bytes, - void** buffer, - MojoMapBufferFlags flags) = 0; - virtual MojoResult UnmapBuffer(void* buffer) = 0; -}; - -} // namespace mojo - -#endif // MOJO_PUBLIC_SYSTEM_CORE_PRIVATE_H_ diff --git a/mojo/shell/in_process_dynamic_service_runner.cc b/mojo/shell/in_process_dynamic_service_runner.cc index 9030d59..6604180 100644 --- a/mojo/shell/in_process_dynamic_service_runner.cc +++ b/mojo/shell/in_process_dynamic_service_runner.cc @@ -11,6 +11,7 @@ #include "base/logging.h" #include "base/message_loop/message_loop_proxy.h" #include "base/scoped_native_library.h" +#include "mojo/public/platform/native/system_thunks.h" namespace mojo { namespace shell { @@ -63,6 +64,20 @@ void InProcessDynamicServiceRunner::Run() { break; } + MojoSetSystemThunksFn mojo_set_system_thunks_fn = + reinterpret_cast(app_library.GetFunctionPointer( + "MojoSetSystemThunks")); + if (mojo_set_system_thunks_fn) { + MojoSystemThunks system_thunks = MojoMakeSystemThunks(); + size_t expected_size = mojo_set_system_thunks_fn(&system_thunks); + if (expected_size > sizeof(MojoSystemThunks)) { + LOG(ERROR) + << "Invalid DSO. Expected MojoSystemThunks size: " + << expected_size; + break; + } + } + typedef MojoResult (*MojoMainFunction)(MojoHandle); MojoMainFunction main_function = reinterpret_cast( app_library.GetFunctionPointer("MojoMain")); diff --git a/mojo/system/core.cc b/mojo/system/core.cc new file mode 100644 index 0000000..dd8fb57 --- /dev/null +++ b/mojo/system/core.cc @@ -0,0 +1,579 @@ +// Copyright 2013 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/system/core.h" + +#include + +#include "base/logging.h" +#include "base/time/time.h" +#include "mojo/public/c/system/core.h" +#include "mojo/system/constants.h" +#include "mojo/system/data_pipe.h" +#include "mojo/system/data_pipe_consumer_dispatcher.h" +#include "mojo/system/data_pipe_producer_dispatcher.h" +#include "mojo/system/dispatcher.h" +#include "mojo/system/local_data_pipe.h" +#include "mojo/system/memory.h" +#include "mojo/system/message_pipe.h" +#include "mojo/system/message_pipe_dispatcher.h" +#include "mojo/system/raw_shared_buffer.h" +#include "mojo/system/shared_buffer_dispatcher.h" +#include "mojo/system/waiter.h" + +namespace mojo { +namespace system { + +// Implementation notes +// +// Mojo primitives are implemented by the singleton |Core| object. Most +// calls are for a "primary" handle (the first argument). +// |Core::GetDispatcher()| is used to look up a |Dispatcher| object for a +// given handle. That object implements most primitives for that object. The +// wait primitives are not attached to objects and are implemented by |Core| +// itself. +// +// Some objects have multiple handles associated to them, e.g., message pipes +// (which have two). In such a case, there is still a |Dispatcher| (e.g., +// |MessagePipeDispatcher|) for each handle, with each handle having a strong +// reference to the common "secondary" object (e.g., |MessagePipe|). This +// secondary object does NOT have any references to the |Dispatcher|s (even if +// it did, it wouldn't be able to do anything with them due to lock order +// requirements -- see below). +// +// Waiting is implemented by having the thread that wants to wait call the +// |Dispatcher|s for the handles that it wants to wait on with a |Waiter| +// object; this |Waiter| object may be created on the stack of that thread or be +// kept in thread local storage for that thread (TODO(vtl): future improvement). +// The |Dispatcher| then adds the |Waiter| to a |WaiterList| that's either owned +// by that |Dispatcher| (see |SimpleDispatcher|) or by a secondary object (e.g., +// |MessagePipe|). To signal/wake a |Waiter|, the object in question -- either a +// |SimpleDispatcher| or a secondary object -- talks to its |WaiterList|. + +// Thread-safety notes +// +// Mojo primitives calls are thread-safe. We achieve this with relatively +// fine-grained locking. There is a global handle table lock. This lock should +// be held as briefly as possible (TODO(vtl): a future improvement would be to +// switch it to a reader-writer lock). Each |Dispatcher| object then has a lock +// (which subclasses can use to protect their data). +// +// The lock ordering is as follows: +// 1. global handle table lock, global mapping table lock +// 2. |Dispatcher| locks +// 3. secondary object locks +// ... +// INF. |Waiter| locks +// +// Notes: +// - While holding a |Dispatcher| lock, you may not unconditionally attempt +// to take another |Dispatcher| lock. (This has consequences on the +// concurrency semantics of |MojoWriteMessage()| when passing handles.) +// Doing so would lead to deadlock. +// - Locks at the "INF" level may not have any locks taken while they are +// held. + +Core::HandleTableEntry::HandleTableEntry() + : busy(false) { +} + +Core::HandleTableEntry::HandleTableEntry( + const scoped_refptr& dispatcher) + : dispatcher(dispatcher), + busy(false) { +} + +Core::HandleTableEntry::~HandleTableEntry() { + DCHECK(!busy); +} + +Core::Core() { +} + +Core::~Core() { +} + +MojoHandle Core::AddDispatcher( + const scoped_refptr& dispatcher) { + base::AutoLock locker(handle_table_lock_); + return handle_table_.AddDispatcher(dispatcher); +} + +MojoTimeTicks Core::GetTimeTicksNow() { + return base::TimeTicks::Now().ToInternalValue(); +} + +MojoResult Core::Close(MojoHandle handle) { + if (handle == MOJO_HANDLE_INVALID) + return MOJO_RESULT_INVALID_ARGUMENT; + + scoped_refptr dispatcher; + { + base::AutoLock locker(handle_table_lock_); + MojoResult result = handle_table_.GetAndRemoveDispatcher(handle, + &dispatcher); + if (result != MOJO_RESULT_OK) + return result; + } + + // The dispatcher doesn't have a say in being closed, but gets notified of it. + // Note: This is done outside of |handle_table_lock_|. As a result, there's a + // race condition that the dispatcher must handle; see the comment in + // |Dispatcher| in dispatcher.h. + return dispatcher->Close(); +} + +MojoResult Core::Wait(MojoHandle handle, + MojoWaitFlags flags, + MojoDeadline deadline) { + return WaitManyInternal(&handle, &flags, 1, deadline); +} + +MojoResult Core::WaitMany(const MojoHandle* handles, + const MojoWaitFlags* flags, + uint32_t num_handles, + MojoDeadline deadline) { + if (!VerifyUserPointer(handles, num_handles)) + return MOJO_RESULT_INVALID_ARGUMENT; + if (!VerifyUserPointer(flags, num_handles)) + return MOJO_RESULT_INVALID_ARGUMENT; + if (num_handles < 1) + return MOJO_RESULT_INVALID_ARGUMENT; + if (num_handles > kMaxWaitManyNumHandles) + return MOJO_RESULT_RESOURCE_EXHAUSTED; + return WaitManyInternal(handles, flags, num_handles, deadline); +} + +MojoResult Core::CreateMessagePipe(MojoHandle* message_pipe_handle0, + MojoHandle* message_pipe_handle1) { + if (!VerifyUserPointer(message_pipe_handle0, 1)) + return MOJO_RESULT_INVALID_ARGUMENT; + if (!VerifyUserPointer(message_pipe_handle1, 1)) + return MOJO_RESULT_INVALID_ARGUMENT; + + scoped_refptr dispatcher0(new MessagePipeDispatcher()); + scoped_refptr dispatcher1(new MessagePipeDispatcher()); + + std::pair handle_pair; + { + base::AutoLock locker(handle_table_lock_); + handle_pair = handle_table_.AddDispatcherPair(dispatcher0, dispatcher1); + } + if (handle_pair.first == MOJO_HANDLE_INVALID) { + DCHECK_EQ(handle_pair.second, MOJO_HANDLE_INVALID); + LOG(ERROR) << "Handle table full"; + dispatcher0->Close(); + dispatcher1->Close(); + return MOJO_RESULT_RESOURCE_EXHAUSTED; + } + + scoped_refptr message_pipe(new MessagePipe()); + dispatcher0->Init(message_pipe, 0); + dispatcher1->Init(message_pipe, 1); + + *message_pipe_handle0 = handle_pair.first; + *message_pipe_handle1 = handle_pair.second; + return MOJO_RESULT_OK; +} + +// Implementation note: To properly cancel waiters and avoid other races, this +// does not transfer dispatchers from one handle to another, even when sending a +// message in-process. Instead, it must transfer the "contents" of the +// dispatcher to a new dispatcher, and then close the old dispatcher. If this +// isn't done, in the in-process case, calls on the old handle may complete +// after the the message has been received and a new handle created (and +// possibly even after calls have been made on the new handle). +MojoResult Core::WriteMessage(MojoHandle message_pipe_handle, + const void* bytes, + uint32_t num_bytes, + const MojoHandle* handles, + uint32_t num_handles, + MojoWriteMessageFlags flags) { + scoped_refptr dispatcher(GetDispatcher(message_pipe_handle)); + if (!dispatcher.get()) + return MOJO_RESULT_INVALID_ARGUMENT; + + // Easy case: not sending any handles. + if (num_handles == 0) + return dispatcher->WriteMessage(bytes, num_bytes, NULL, flags); + + // We have to handle |handles| here, since we have to mark them busy in the + // global handle table. We can't delegate this to the dispatcher, since the + // handle table lock must be acquired before the dispatcher lock. + // + // (This leads to an oddity: |handles|/|num_handles| are always verified for + // validity, even for dispatchers that don't support |WriteMessage()| and will + // simply return failure unconditionally. It also breaks the usual + // left-to-right verification order of arguments.) + if (!VerifyUserPointer(handles, num_handles)) + return MOJO_RESULT_INVALID_ARGUMENT; + if (num_handles > kMaxMessageNumHandles) + return MOJO_RESULT_RESOURCE_EXHAUSTED; + + // We'll need to hold on to the dispatchers so that we can pass them on to + // |WriteMessage()| and also so that we can unlock their locks afterwards + // without accessing the handle table. These can be dumb pointers, since their + // entries in the handle table won't get removed (since they'll be marked as + // busy). + std::vector transports(num_handles); + + // When we pass handles, we have to try to take all their dispatchers' locks + // and mark the handles as busy. If the call succeeds, we then remove the + // handles from the handle table. + { + base::AutoLock locker(handle_table_lock_); + MojoResult result = handle_table_.MarkBusyAndStartTransport( + message_pipe_handle, handles, num_handles, &transports); + if (result != MOJO_RESULT_OK) + return result; + } + + MojoResult rv = dispatcher->WriteMessage(bytes, num_bytes, &transports, + flags); + + // We need to release the dispatcher locks before we take the handle table + // lock. + for (uint32_t i = 0; i < num_handles; i++) + transports[i].End(); + + { + base::AutoLock locker(handle_table_lock_); + if (rv == MOJO_RESULT_OK) + handle_table_.RemoveBusyHandles(handles, num_handles); + else + handle_table_.RestoreBusyHandles(handles, num_handles); + } + + return rv; +} + +MojoResult Core::ReadMessage(MojoHandle message_pipe_handle, + void* bytes, + uint32_t* num_bytes, + MojoHandle* handles, + uint32_t* num_handles, + MojoReadMessageFlags flags) { + scoped_refptr dispatcher(GetDispatcher(message_pipe_handle)); + if (!dispatcher.get()) + return MOJO_RESULT_INVALID_ARGUMENT; + + if (num_handles) { + if (!VerifyUserPointer(num_handles, 1)) + return MOJO_RESULT_INVALID_ARGUMENT; + if (!VerifyUserPointer(handles, *num_handles)) + return MOJO_RESULT_INVALID_ARGUMENT; + } + + // Easy case: won't receive any handles. + if (!num_handles || *num_handles == 0) + return dispatcher->ReadMessage(bytes, num_bytes, NULL, num_handles, flags); + + std::vector > dispatchers; + MojoResult rv = dispatcher->ReadMessage(bytes, num_bytes, + &dispatchers, num_handles, + flags); + if (!dispatchers.empty()) { + DCHECK_EQ(rv, MOJO_RESULT_OK); + DCHECK(num_handles); + DCHECK_LE(dispatchers.size(), static_cast(*num_handles)); + + bool success; + { + base::AutoLock locker(handle_table_lock_); + success = handle_table_.AddDispatcherVector(dispatchers, handles); + } + if (!success) { + LOG(ERROR) << "Received message with " << dispatchers.size() + << " handles, but handle table full"; + // Close dispatchers (outside the lock). + for (size_t i = 0; i < dispatchers.size(); i++) { + if (dispatchers[i]) + dispatchers[i]->Close(); + } + } + } + + return rv; +} + +MojoResult Core::CreateDataPipe(const MojoCreateDataPipeOptions* options, + MojoHandle* data_pipe_producer_handle, + MojoHandle* data_pipe_consumer_handle) { + if (options) { + // The |struct_size| field must be valid to read. + if (!VerifyUserPointer(&options->struct_size, 1)) + return MOJO_RESULT_INVALID_ARGUMENT; + // And then |options| must point to at least |options->struct_size| bytes. + if (!VerifyUserPointer(options, options->struct_size)) + return MOJO_RESULT_INVALID_ARGUMENT; + } + if (!VerifyUserPointer(data_pipe_producer_handle, 1)) + return MOJO_RESULT_INVALID_ARGUMENT; + if (!VerifyUserPointer(data_pipe_consumer_handle, 1)) + return MOJO_RESULT_INVALID_ARGUMENT; + + MojoCreateDataPipeOptions validated_options = { 0 }; + MojoResult result = DataPipe::ValidateOptions(options, &validated_options); + if (result != MOJO_RESULT_OK) + return result; + + scoped_refptr producer_dispatcher( + new DataPipeProducerDispatcher()); + scoped_refptr consumer_dispatcher( + new DataPipeConsumerDispatcher()); + + std::pair handle_pair; + { + base::AutoLock locker(handle_table_lock_); + handle_pair = handle_table_.AddDispatcherPair(producer_dispatcher, + consumer_dispatcher); + } + if (handle_pair.first == MOJO_HANDLE_INVALID) { + DCHECK_EQ(handle_pair.second, MOJO_HANDLE_INVALID); + LOG(ERROR) << "Handle table full"; + producer_dispatcher->Close(); + consumer_dispatcher->Close(); + return MOJO_RESULT_RESOURCE_EXHAUSTED; + } + DCHECK_NE(handle_pair.second, MOJO_HANDLE_INVALID); + + scoped_refptr data_pipe(new LocalDataPipe(validated_options)); + producer_dispatcher->Init(data_pipe); + consumer_dispatcher->Init(data_pipe); + + *data_pipe_producer_handle = handle_pair.first; + *data_pipe_consumer_handle = handle_pair.second; + return MOJO_RESULT_OK; +} + +MojoResult Core::WriteData(MojoHandle data_pipe_producer_handle, + const void* elements, + uint32_t* num_bytes, + MojoWriteDataFlags flags) { + scoped_refptr dispatcher( + GetDispatcher(data_pipe_producer_handle)); + if (!dispatcher.get()) + return MOJO_RESULT_INVALID_ARGUMENT; + + return dispatcher->WriteData(elements, num_bytes, flags); +} + +MojoResult Core::BeginWriteData(MojoHandle data_pipe_producer_handle, + void** buffer, + uint32_t* buffer_num_bytes, + MojoWriteDataFlags flags) { + scoped_refptr dispatcher( + GetDispatcher(data_pipe_producer_handle)); + if (!dispatcher.get()) + return MOJO_RESULT_INVALID_ARGUMENT; + + return dispatcher->BeginWriteData(buffer, buffer_num_bytes, flags); +} + +MojoResult Core::EndWriteData(MojoHandle data_pipe_producer_handle, + uint32_t num_bytes_written) { + scoped_refptr dispatcher( + GetDispatcher(data_pipe_producer_handle)); + if (!dispatcher.get()) + return MOJO_RESULT_INVALID_ARGUMENT; + + return dispatcher->EndWriteData(num_bytes_written); +} + +MojoResult Core::ReadData(MojoHandle data_pipe_consumer_handle, + void* elements, + uint32_t* num_bytes, + MojoReadDataFlags flags) { + scoped_refptr dispatcher( + GetDispatcher(data_pipe_consumer_handle)); + if (!dispatcher.get()) + return MOJO_RESULT_INVALID_ARGUMENT; + + return dispatcher->ReadData(elements, num_bytes, flags); +} + +MojoResult Core::BeginReadData(MojoHandle data_pipe_consumer_handle, + const void** buffer, + uint32_t* buffer_num_bytes, + MojoReadDataFlags flags) { + scoped_refptr dispatcher( + GetDispatcher(data_pipe_consumer_handle)); + if (!dispatcher.get()) + return MOJO_RESULT_INVALID_ARGUMENT; + + return dispatcher->BeginReadData(buffer, buffer_num_bytes, flags); +} + +MojoResult Core::EndReadData(MojoHandle data_pipe_consumer_handle, + uint32_t num_bytes_read) { + scoped_refptr dispatcher( + GetDispatcher(data_pipe_consumer_handle)); + if (!dispatcher.get()) + return MOJO_RESULT_INVALID_ARGUMENT; + + return dispatcher->EndReadData(num_bytes_read); +} + +MojoResult Core::CreateSharedBuffer( + const MojoCreateSharedBufferOptions* options, + uint64_t num_bytes, + MojoHandle* shared_buffer_handle) { + if (options) { + // The |struct_size| field must be valid to read. + if (!VerifyUserPointer(&options->struct_size, 1)) + return MOJO_RESULT_INVALID_ARGUMENT; + // And then |options| must point to at least |options->struct_size| bytes. + if (!VerifyUserPointer(options, options->struct_size)) + return MOJO_RESULT_INVALID_ARGUMENT; + } + if (!VerifyUserPointer(shared_buffer_handle, 1)) + return MOJO_RESULT_INVALID_ARGUMENT; + + MojoCreateSharedBufferOptions validated_options = { 0 }; + MojoResult result = + SharedBufferDispatcher::ValidateOptions(options, &validated_options); + if (result != MOJO_RESULT_OK) + return result; + + scoped_refptr dispatcher; + result = SharedBufferDispatcher::Create(validated_options, num_bytes, + &dispatcher); + if (result != MOJO_RESULT_OK) { + DCHECK(!dispatcher); + return result; + } + + MojoHandle h = AddDispatcher(dispatcher); + if (h == MOJO_HANDLE_INVALID) { + LOG(ERROR) << "Handle table full"; + dispatcher->Close(); + return MOJO_RESULT_RESOURCE_EXHAUSTED; + } + + *shared_buffer_handle = h; + return MOJO_RESULT_OK; +} + +MojoResult Core::DuplicateBufferHandle( + MojoHandle buffer_handle, + const MojoDuplicateBufferHandleOptions* options, + MojoHandle* new_buffer_handle) { + scoped_refptr dispatcher(GetDispatcher(buffer_handle)); + if (!dispatcher.get()) + return MOJO_RESULT_INVALID_ARGUMENT; + + // Don't verify |options| here; that's the dispatcher's job. + if (!VerifyUserPointer(new_buffer_handle, 1)) + return MOJO_RESULT_INVALID_ARGUMENT; + + scoped_refptr new_dispatcher; + MojoResult result = dispatcher->DuplicateBufferHandle(options, + &new_dispatcher); + if (result != MOJO_RESULT_OK) + return result; + + MojoHandle new_handle = AddDispatcher(new_dispatcher); + if (new_handle == MOJO_HANDLE_INVALID) { + LOG(ERROR) << "Handle table full"; + dispatcher->Close(); + return MOJO_RESULT_RESOURCE_EXHAUSTED; + } + + *new_buffer_handle = new_handle; + return MOJO_RESULT_OK; +} + +MojoResult Core::MapBuffer(MojoHandle buffer_handle, + uint64_t offset, + uint64_t num_bytes, + void** buffer, + MojoMapBufferFlags flags) { + scoped_refptr dispatcher(GetDispatcher(buffer_handle)); + if (!dispatcher.get()) + return MOJO_RESULT_INVALID_ARGUMENT; + + if (!VerifyUserPointer(buffer, 1)) + return MOJO_RESULT_INVALID_ARGUMENT; + + scoped_ptr mapping; + MojoResult result = dispatcher->MapBuffer(offset, num_bytes, flags, &mapping); + if (result != MOJO_RESULT_OK) + return result; + + DCHECK(mapping); + void* address = mapping->base(); + { + base::AutoLock locker(mapping_table_lock_); + result = mapping_table_.AddMapping(mapping.Pass()); + } + if (result != MOJO_RESULT_OK) + return result; + + *buffer = address; + return MOJO_RESULT_OK; +} + +MojoResult Core::UnmapBuffer(void* buffer) { + base::AutoLock locker(mapping_table_lock_); + return mapping_table_.RemoveMapping(buffer); +} + +scoped_refptr Core::GetDispatcher(MojoHandle handle) { + if (handle == MOJO_HANDLE_INVALID) + return NULL; + + base::AutoLock locker(handle_table_lock_); + return handle_table_.GetDispatcher(handle); +} + +// Note: We allow |handles| to repeat the same handle multiple times, since +// different flags may be specified. +// TODO(vtl): This incurs a performance cost in |RemoveWaiter()|. Analyze this +// more carefully and address it if necessary. +MojoResult Core::WaitManyInternal(const MojoHandle* handles, + const MojoWaitFlags* flags, + uint32_t num_handles, + MojoDeadline deadline) { + DCHECK_GT(num_handles, 0u); + + std::vector > dispatchers; + dispatchers.reserve(num_handles); + for (uint32_t i = 0; i < num_handles; i++) { + scoped_refptr dispatcher = GetDispatcher(handles[i]); + if (!dispatcher.get()) + return MOJO_RESULT_INVALID_ARGUMENT; + dispatchers.push_back(dispatcher); + } + + // TODO(vtl): Should make the waiter live (permanently) in TLS. + Waiter waiter; + waiter.Init(); + + uint32_t i; + MojoResult rv = MOJO_RESULT_OK; + for (i = 0; i < num_handles; i++) { + rv = dispatchers[i]->AddWaiter(&waiter, + flags[i], + static_cast(i)); + if (rv != MOJO_RESULT_OK) + break; + } + uint32_t num_added = i; + + if (rv == MOJO_RESULT_ALREADY_EXISTS) + rv = static_cast(i); // The i-th one is already "triggered". + else if (rv == MOJO_RESULT_OK) + rv = waiter.Wait(deadline); + + // Make sure no other dispatchers try to wake |waiter| for the current + // |Wait()|/|WaitMany()| call. (Only after doing this can |waiter| be + // destroyed, but this would still be required if the waiter were in TLS.) + for (i = 0; i < num_added; i++) + dispatchers[i]->RemoveWaiter(&waiter); + + return rv; +} + +} // namespace system +} // namespace mojo diff --git a/mojo/system/core.h b/mojo/system/core.h new file mode 100644 index 0000000..5d3a1cf --- /dev/null +++ b/mojo/system/core.h @@ -0,0 +1,151 @@ +// Copyright 2013 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_SYSTEM_CORE_H_ +#define MOJO_SYSTEM_CORE_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "mojo/system/handle_table.h" +#include "mojo/system/mapping_table.h" +#include "mojo/system/system_impl_export.h" + +namespace mojo { +namespace system { + +class Dispatcher; + +// |Core| is an object that implements the Mojo system calls. All public methods +// are thread-safe. +class MOJO_SYSTEM_IMPL_EXPORT Core { + public: + // These methods are only to be used by via the embedder API. + Core(); + virtual ~Core(); + MojoHandle AddDispatcher(const scoped_refptr& dispatcher); + + // System calls implementation. + MojoTimeTicks GetTimeTicksNow(); + MojoResult Close(MojoHandle handle); + MojoResult Wait(MojoHandle handle, + MojoWaitFlags flags, + MojoDeadline deadline); + MojoResult WaitMany(const MojoHandle* handles, + const MojoWaitFlags* flags, + uint32_t num_handles, + MojoDeadline deadline); + MojoResult CreateMessagePipe( + MojoHandle* message_pipe_handle0, + MojoHandle* message_pipe_handle1); + MojoResult WriteMessage(MojoHandle message_pipe_handle, + const void* bytes, + uint32_t num_bytes, + const MojoHandle* handles, + uint32_t num_handles, + MojoWriteMessageFlags flags); + MojoResult ReadMessage(MojoHandle message_pipe_handle, + void* bytes, + uint32_t* num_bytes, + MojoHandle* handles, + uint32_t* num_handles, + MojoReadMessageFlags flags); + MojoResult CreateDataPipe( + const MojoCreateDataPipeOptions* options, + MojoHandle* data_pipe_producer_handle, + MojoHandle* data_pipe_consumer_handle); + MojoResult WriteData(MojoHandle data_pipe_producer_handle, + const void* elements, + uint32_t* num_bytes, + MojoWriteDataFlags flags); + MojoResult BeginWriteData(MojoHandle data_pipe_producer_handle, + void** buffer, + uint32_t* buffer_num_bytes, + MojoWriteDataFlags flags); + MojoResult EndWriteData(MojoHandle data_pipe_producer_handle, + uint32_t num_bytes_written); + MojoResult ReadData(MojoHandle data_pipe_consumer_handle, + void* elements, + uint32_t* num_bytes, + MojoReadDataFlags flags); + MojoResult BeginReadData(MojoHandle data_pipe_consumer_handle, + const void** buffer, + uint32_t* buffer_num_bytes, + MojoReadDataFlags flags); + MojoResult EndReadData(MojoHandle data_pipe_consumer_handle, + uint32_t num_bytes_read); + MojoResult CreateSharedBuffer( + const MojoCreateSharedBufferOptions* options, + uint64_t num_bytes, + MojoHandle* shared_buffer_handle); + MojoResult DuplicateBufferHandle( + MojoHandle buffer_handle, + const MojoDuplicateBufferHandleOptions* options, + MojoHandle* new_buffer_handle); + MojoResult MapBuffer(MojoHandle buffer_handle, + uint64_t offset, + uint64_t num_bytes, + void** buffer, + MojoMapBufferFlags flags); + MojoResult UnmapBuffer(void* buffer); + + private: + friend bool internal::ShutdownCheckNoLeaks(Core*); + // The |busy| member is used only to deal with functions (in particular + // |WriteMessage()|) that want to hold on to a dispatcher and later remove it + // from the handle table, without holding on to the handle table lock. + // + // For example, if |WriteMessage()| is called with a handle to be sent, (under + // the handle table lock) it must first check that that handle is not busy (if + // it is busy, then it fails with |MOJO_RESULT_BUSY|) and then marks it as + // busy. To avoid deadlock, it should also try to acquire the locks for all + // the dispatchers for the handles that it is sending (and fail with + // |MOJO_RESULT_BUSY| if the attempt fails). At this point, it can release the + // handle table lock. + // + // If |Close()| is simultaneously called on that handle, it too checks if the + // handle is marked busy. If it is, it fails (with |MOJO_RESULT_BUSY|). This + // prevents |WriteMessage()| from sending a handle that has been closed (or + // learning about this too late). + struct HandleTableEntry { + HandleTableEntry(); + explicit HandleTableEntry(const scoped_refptr& dispatcher); + ~HandleTableEntry(); + + scoped_refptr dispatcher; + bool busy; + }; + typedef base::hash_map HandleTableMap; + + // Looks up the dispatcher for the given handle. Returns null if the handle is + // invalid. + scoped_refptr GetDispatcher(MojoHandle handle); + + // Internal implementation of |Wait()| and |WaitMany()|; doesn't do basic + // validation of arguments. + MojoResult WaitManyInternal(const MojoHandle* handles, + const MojoWaitFlags* flags, + uint32_t num_handles, + MojoDeadline deadline); + + // --------------------------------------------------------------------------- + + // TODO(vtl): |handle_table_lock_| should be a reader-writer lock (if only we + // had them). + base::Lock handle_table_lock_; // Protects |handle_table_|. + HandleTable handle_table_; + + base::Lock mapping_table_lock_; // Protects |mapping_table_|. + MappingTable mapping_table_; + + // --------------------------------------------------------------------------- + + DISALLOW_COPY_AND_ASSIGN(Core); +}; + +} // namespace system +} // namespace mojo + +#endif // MOJO_SYSTEM_CORE_H_ diff --git a/mojo/system/core_impl.cc b/mojo/system/core_impl.cc deleted file mode 100644 index 12c1be5..0000000 --- a/mojo/system/core_impl.cc +++ /dev/null @@ -1,580 +0,0 @@ -// Copyright 2013 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/system/core_impl.h" - -#include - -#include "base/logging.h" -#include "base/time/time.h" -#include "mojo/system/constants.h" -#include "mojo/system/data_pipe.h" -#include "mojo/system/data_pipe_consumer_dispatcher.h" -#include "mojo/system/data_pipe_producer_dispatcher.h" -#include "mojo/system/dispatcher.h" -#include "mojo/system/local_data_pipe.h" -#include "mojo/system/memory.h" -#include "mojo/system/message_pipe.h" -#include "mojo/system/message_pipe_dispatcher.h" -#include "mojo/system/raw_shared_buffer.h" -#include "mojo/system/shared_buffer_dispatcher.h" -#include "mojo/system/waiter.h" - -namespace mojo { -namespace system { - -// Implementation notes -// -// Mojo primitives are implemented by the singleton |CoreImpl| object. Most -// calls are for a "primary" handle (the first argument). -// |CoreImpl::GetDispatcher()| is used to look up a |Dispatcher| object for a -// given handle. That object implements most primitives for that object. The -// wait primitives are not attached to objects and are implemented by |CoreImpl| -// itself. -// -// Some objects have multiple handles associated to them, e.g., message pipes -// (which have two). In such a case, there is still a |Dispatcher| (e.g., -// |MessagePipeDispatcher|) for each handle, with each handle having a strong -// reference to the common "secondary" object (e.g., |MessagePipe|). This -// secondary object does NOT have any references to the |Dispatcher|s (even if -// it did, it wouldn't be able to do anything with them due to lock order -// requirements -- see below). -// -// Waiting is implemented by having the thread that wants to wait call the -// |Dispatcher|s for the handles that it wants to wait on with a |Waiter| -// object; this |Waiter| object may be created on the stack of that thread or be -// kept in thread local storage for that thread (TODO(vtl): future improvement). -// The |Dispatcher| then adds the |Waiter| to a |WaiterList| that's either owned -// by that |Dispatcher| (see |SimpleDispatcher|) or by a secondary object (e.g., -// |MessagePipe|). To signal/wake a |Waiter|, the object in question -- either a -// |SimpleDispatcher| or a secondary object -- talks to its |WaiterList|. - -// Thread-safety notes -// -// Mojo primitives calls are thread-safe. We achieve this with relatively -// fine-grained locking. There is a global handle table lock. This lock should -// be held as briefly as possible (TODO(vtl): a future improvement would be to -// switch it to a reader-writer lock). Each |Dispatcher| object then has a lock -// (which subclasses can use to protect their data). -// -// The lock ordering is as follows: -// 1. global handle table lock, global mapping table lock -// 2. |Dispatcher| locks -// 3. secondary object locks -// ... -// INF. |Waiter| locks -// -// Notes: -// - While holding a |Dispatcher| lock, you may not unconditionally attempt -// to take another |Dispatcher| lock. (This has consequences on the -// concurrency semantics of |MojoWriteMessage()| when passing handles.) -// Doing so would lead to deadlock. -// - Locks at the "INF" level may not have any locks taken while they are -// held. - -CoreImpl::HandleTableEntry::HandleTableEntry() - : busy(false) { -} - -CoreImpl::HandleTableEntry::HandleTableEntry( - const scoped_refptr& dispatcher) - : dispatcher(dispatcher), - busy(false) { -} - -CoreImpl::HandleTableEntry::~HandleTableEntry() { - DCHECK(!busy); -} - -CoreImpl::CoreImpl() { -} - -CoreImpl::~CoreImpl() { - // This should usually not be reached (the singleton lives forever), except in - // tests. -} - -MojoHandle CoreImpl::AddDispatcher( - const scoped_refptr& dispatcher) { - base::AutoLock locker(handle_table_lock_); - return handle_table_.AddDispatcher(dispatcher); -} - -MojoTimeTicks CoreImpl::GetTimeTicksNow() { - return base::TimeTicks::Now().ToInternalValue(); -} - -MojoResult CoreImpl::Close(MojoHandle handle) { - if (handle == MOJO_HANDLE_INVALID) - return MOJO_RESULT_INVALID_ARGUMENT; - - scoped_refptr dispatcher; - { - base::AutoLock locker(handle_table_lock_); - MojoResult result = handle_table_.GetAndRemoveDispatcher(handle, - &dispatcher); - if (result != MOJO_RESULT_OK) - return result; - } - - // The dispatcher doesn't have a say in being closed, but gets notified of it. - // Note: This is done outside of |handle_table_lock_|. As a result, there's a - // race condition that the dispatcher must handle; see the comment in - // |Dispatcher| in dispatcher.h. - return dispatcher->Close(); -} - -MojoResult CoreImpl::Wait(MojoHandle handle, - MojoWaitFlags flags, - MojoDeadline deadline) { - return WaitManyInternal(&handle, &flags, 1, deadline); -} - -MojoResult CoreImpl::WaitMany(const MojoHandle* handles, - const MojoWaitFlags* flags, - uint32_t num_handles, - MojoDeadline deadline) { - if (!VerifyUserPointer(handles, num_handles)) - return MOJO_RESULT_INVALID_ARGUMENT; - if (!VerifyUserPointer(flags, num_handles)) - return MOJO_RESULT_INVALID_ARGUMENT; - if (num_handles < 1) - return MOJO_RESULT_INVALID_ARGUMENT; - if (num_handles > kMaxWaitManyNumHandles) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - return WaitManyInternal(handles, flags, num_handles, deadline); -} - -MojoResult CoreImpl::CreateMessagePipe(MojoHandle* message_pipe_handle0, - MojoHandle* message_pipe_handle1) { - if (!VerifyUserPointer(message_pipe_handle0, 1)) - return MOJO_RESULT_INVALID_ARGUMENT; - if (!VerifyUserPointer(message_pipe_handle1, 1)) - return MOJO_RESULT_INVALID_ARGUMENT; - - scoped_refptr dispatcher0(new MessagePipeDispatcher()); - scoped_refptr dispatcher1(new MessagePipeDispatcher()); - - std::pair handle_pair; - { - base::AutoLock locker(handle_table_lock_); - handle_pair = handle_table_.AddDispatcherPair(dispatcher0, dispatcher1); - } - if (handle_pair.first == MOJO_HANDLE_INVALID) { - DCHECK_EQ(handle_pair.second, MOJO_HANDLE_INVALID); - LOG(ERROR) << "Handle table full"; - dispatcher0->Close(); - dispatcher1->Close(); - return MOJO_RESULT_RESOURCE_EXHAUSTED; - } - - scoped_refptr message_pipe(new MessagePipe()); - dispatcher0->Init(message_pipe, 0); - dispatcher1->Init(message_pipe, 1); - - *message_pipe_handle0 = handle_pair.first; - *message_pipe_handle1 = handle_pair.second; - return MOJO_RESULT_OK; -} - -// Implementation note: To properly cancel waiters and avoid other races, this -// does not transfer dispatchers from one handle to another, even when sending a -// message in-process. Instead, it must transfer the "contents" of the -// dispatcher to a new dispatcher, and then close the old dispatcher. If this -// isn't done, in the in-process case, calls on the old handle may complete -// after the the message has been received and a new handle created (and -// possibly even after calls have been made on the new handle). -MojoResult CoreImpl::WriteMessage(MojoHandle message_pipe_handle, - const void* bytes, - uint32_t num_bytes, - const MojoHandle* handles, - uint32_t num_handles, - MojoWriteMessageFlags flags) { - scoped_refptr dispatcher(GetDispatcher(message_pipe_handle)); - if (!dispatcher.get()) - return MOJO_RESULT_INVALID_ARGUMENT; - - // Easy case: not sending any handles. - if (num_handles == 0) - return dispatcher->WriteMessage(bytes, num_bytes, NULL, flags); - - // We have to handle |handles| here, since we have to mark them busy in the - // global handle table. We can't delegate this to the dispatcher, since the - // handle table lock must be acquired before the dispatcher lock. - // - // (This leads to an oddity: |handles|/|num_handles| are always verified for - // validity, even for dispatchers that don't support |WriteMessage()| and will - // simply return failure unconditionally. It also breaks the usual - // left-to-right verification order of arguments.) - if (!VerifyUserPointer(handles, num_handles)) - return MOJO_RESULT_INVALID_ARGUMENT; - if (num_handles > kMaxMessageNumHandles) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - - // We'll need to hold on to the dispatchers so that we can pass them on to - // |WriteMessage()| and also so that we can unlock their locks afterwards - // without accessing the handle table. These can be dumb pointers, since their - // entries in the handle table won't get removed (since they'll be marked as - // busy). - std::vector transports(num_handles); - - // When we pass handles, we have to try to take all their dispatchers' locks - // and mark the handles as busy. If the call succeeds, we then remove the - // handles from the handle table. - { - base::AutoLock locker(handle_table_lock_); - MojoResult result = handle_table_.MarkBusyAndStartTransport( - message_pipe_handle, handles, num_handles, &transports); - if (result != MOJO_RESULT_OK) - return result; - } - - MojoResult rv = dispatcher->WriteMessage(bytes, num_bytes, &transports, - flags); - - // We need to release the dispatcher locks before we take the handle table - // lock. - for (uint32_t i = 0; i < num_handles; i++) - transports[i].End(); - - { - base::AutoLock locker(handle_table_lock_); - if (rv == MOJO_RESULT_OK) - handle_table_.RemoveBusyHandles(handles, num_handles); - else - handle_table_.RestoreBusyHandles(handles, num_handles); - } - - return rv; -} - -MojoResult CoreImpl::ReadMessage(MojoHandle message_pipe_handle, - void* bytes, - uint32_t* num_bytes, - MojoHandle* handles, - uint32_t* num_handles, - MojoReadMessageFlags flags) { - scoped_refptr dispatcher(GetDispatcher(message_pipe_handle)); - if (!dispatcher.get()) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (num_handles) { - if (!VerifyUserPointer(num_handles, 1)) - return MOJO_RESULT_INVALID_ARGUMENT; - if (!VerifyUserPointer(handles, *num_handles)) - return MOJO_RESULT_INVALID_ARGUMENT; - } - - // Easy case: won't receive any handles. - if (!num_handles || *num_handles == 0) - return dispatcher->ReadMessage(bytes, num_bytes, NULL, num_handles, flags); - - std::vector > dispatchers; - MojoResult rv = dispatcher->ReadMessage(bytes, num_bytes, - &dispatchers, num_handles, - flags); - if (!dispatchers.empty()) { - DCHECK_EQ(rv, MOJO_RESULT_OK); - DCHECK(num_handles); - DCHECK_LE(dispatchers.size(), static_cast(*num_handles)); - - bool success; - { - base::AutoLock locker(handle_table_lock_); - success = handle_table_.AddDispatcherVector(dispatchers, handles); - } - if (!success) { - LOG(ERROR) << "Received message with " << dispatchers.size() - << " handles, but handle table full"; - // Close dispatchers (outside the lock). - for (size_t i = 0; i < dispatchers.size(); i++) { - if (dispatchers[i]) - dispatchers[i]->Close(); - } - } - } - - return rv; -} - -MojoResult CoreImpl::CreateDataPipe(const MojoCreateDataPipeOptions* options, - MojoHandle* data_pipe_producer_handle, - MojoHandle* data_pipe_consumer_handle) { - if (options) { - // The |struct_size| field must be valid to read. - if (!VerifyUserPointer(&options->struct_size, 1)) - return MOJO_RESULT_INVALID_ARGUMENT; - // And then |options| must point to at least |options->struct_size| bytes. - if (!VerifyUserPointer(options, options->struct_size)) - return MOJO_RESULT_INVALID_ARGUMENT; - } - if (!VerifyUserPointer(data_pipe_producer_handle, 1)) - return MOJO_RESULT_INVALID_ARGUMENT; - if (!VerifyUserPointer(data_pipe_consumer_handle, 1)) - return MOJO_RESULT_INVALID_ARGUMENT; - - MojoCreateDataPipeOptions validated_options = { 0 }; - MojoResult result = DataPipe::ValidateOptions(options, &validated_options); - if (result != MOJO_RESULT_OK) - return result; - - scoped_refptr producer_dispatcher( - new DataPipeProducerDispatcher()); - scoped_refptr consumer_dispatcher( - new DataPipeConsumerDispatcher()); - - std::pair handle_pair; - { - base::AutoLock locker(handle_table_lock_); - handle_pair = handle_table_.AddDispatcherPair(producer_dispatcher, - consumer_dispatcher); - } - if (handle_pair.first == MOJO_HANDLE_INVALID) { - DCHECK_EQ(handle_pair.second, MOJO_HANDLE_INVALID); - LOG(ERROR) << "Handle table full"; - producer_dispatcher->Close(); - consumer_dispatcher->Close(); - return MOJO_RESULT_RESOURCE_EXHAUSTED; - } - DCHECK_NE(handle_pair.second, MOJO_HANDLE_INVALID); - - scoped_refptr data_pipe(new LocalDataPipe(validated_options)); - producer_dispatcher->Init(data_pipe); - consumer_dispatcher->Init(data_pipe); - - *data_pipe_producer_handle = handle_pair.first; - *data_pipe_consumer_handle = handle_pair.second; - return MOJO_RESULT_OK; -} - -MojoResult CoreImpl::WriteData(MojoHandle data_pipe_producer_handle, - const void* elements, - uint32_t* num_bytes, - MojoWriteDataFlags flags) { - scoped_refptr dispatcher( - GetDispatcher(data_pipe_producer_handle)); - if (!dispatcher.get()) - return MOJO_RESULT_INVALID_ARGUMENT; - - return dispatcher->WriteData(elements, num_bytes, flags); -} - -MojoResult CoreImpl::BeginWriteData(MojoHandle data_pipe_producer_handle, - void** buffer, - uint32_t* buffer_num_bytes, - MojoWriteDataFlags flags) { - scoped_refptr dispatcher( - GetDispatcher(data_pipe_producer_handle)); - if (!dispatcher.get()) - return MOJO_RESULT_INVALID_ARGUMENT; - - return dispatcher->BeginWriteData(buffer, buffer_num_bytes, flags); -} - -MojoResult CoreImpl::EndWriteData(MojoHandle data_pipe_producer_handle, - uint32_t num_bytes_written) { - scoped_refptr dispatcher( - GetDispatcher(data_pipe_producer_handle)); - if (!dispatcher.get()) - return MOJO_RESULT_INVALID_ARGUMENT; - - return dispatcher->EndWriteData(num_bytes_written); -} - -MojoResult CoreImpl::ReadData(MojoHandle data_pipe_consumer_handle, - void* elements, - uint32_t* num_bytes, - MojoReadDataFlags flags) { - scoped_refptr dispatcher( - GetDispatcher(data_pipe_consumer_handle)); - if (!dispatcher.get()) - return MOJO_RESULT_INVALID_ARGUMENT; - - return dispatcher->ReadData(elements, num_bytes, flags); -} - -MojoResult CoreImpl::BeginReadData(MojoHandle data_pipe_consumer_handle, - const void** buffer, - uint32_t* buffer_num_bytes, - MojoReadDataFlags flags) { - scoped_refptr dispatcher( - GetDispatcher(data_pipe_consumer_handle)); - if (!dispatcher.get()) - return MOJO_RESULT_INVALID_ARGUMENT; - - return dispatcher->BeginReadData(buffer, buffer_num_bytes, flags); -} - -MojoResult CoreImpl::EndReadData(MojoHandle data_pipe_consumer_handle, - uint32_t num_bytes_read) { - scoped_refptr dispatcher( - GetDispatcher(data_pipe_consumer_handle)); - if (!dispatcher.get()) - return MOJO_RESULT_INVALID_ARGUMENT; - - return dispatcher->EndReadData(num_bytes_read); -} - -MojoResult CoreImpl::CreateSharedBuffer( - const MojoCreateSharedBufferOptions* options, - uint64_t num_bytes, - MojoHandle* shared_buffer_handle) { - if (options) { - // The |struct_size| field must be valid to read. - if (!VerifyUserPointer(&options->struct_size, 1)) - return MOJO_RESULT_INVALID_ARGUMENT; - // And then |options| must point to at least |options->struct_size| bytes. - if (!VerifyUserPointer(options, options->struct_size)) - return MOJO_RESULT_INVALID_ARGUMENT; - } - if (!VerifyUserPointer(shared_buffer_handle, 1)) - return MOJO_RESULT_INVALID_ARGUMENT; - - MojoCreateSharedBufferOptions validated_options = { 0 }; - MojoResult result = - SharedBufferDispatcher::ValidateOptions(options, &validated_options); - if (result != MOJO_RESULT_OK) - return result; - - scoped_refptr dispatcher; - result = SharedBufferDispatcher::Create(validated_options, num_bytes, - &dispatcher); - if (result != MOJO_RESULT_OK) { - DCHECK(!dispatcher); - return result; - } - - MojoHandle h = AddDispatcher(dispatcher); - if (h == MOJO_HANDLE_INVALID) { - LOG(ERROR) << "Handle table full"; - dispatcher->Close(); - return MOJO_RESULT_RESOURCE_EXHAUSTED; - } - - *shared_buffer_handle = h; - return MOJO_RESULT_OK; -} - -MojoResult CoreImpl::DuplicateBufferHandle( - MojoHandle buffer_handle, - const MojoDuplicateBufferHandleOptions* options, - MojoHandle* new_buffer_handle) { - scoped_refptr dispatcher(GetDispatcher(buffer_handle)); - if (!dispatcher.get()) - return MOJO_RESULT_INVALID_ARGUMENT; - - // Don't verify |options| here; that's the dispatcher's job. - if (!VerifyUserPointer(new_buffer_handle, 1)) - return MOJO_RESULT_INVALID_ARGUMENT; - - scoped_refptr new_dispatcher; - MojoResult result = dispatcher->DuplicateBufferHandle(options, - &new_dispatcher); - if (result != MOJO_RESULT_OK) - return result; - - MojoHandle new_handle = AddDispatcher(new_dispatcher); - if (new_handle == MOJO_HANDLE_INVALID) { - LOG(ERROR) << "Handle table full"; - dispatcher->Close(); - return MOJO_RESULT_RESOURCE_EXHAUSTED; - } - - *new_buffer_handle = new_handle; - return MOJO_RESULT_OK; -} - -MojoResult CoreImpl::MapBuffer(MojoHandle buffer_handle, - uint64_t offset, - uint64_t num_bytes, - void** buffer, - MojoMapBufferFlags flags) { - scoped_refptr dispatcher(GetDispatcher(buffer_handle)); - if (!dispatcher.get()) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (!VerifyUserPointer(buffer, 1)) - return MOJO_RESULT_INVALID_ARGUMENT; - - scoped_ptr mapping; - MojoResult result = dispatcher->MapBuffer(offset, num_bytes, flags, &mapping); - if (result != MOJO_RESULT_OK) - return result; - - DCHECK(mapping); - void* address = mapping->base(); - { - base::AutoLock locker(mapping_table_lock_); - result = mapping_table_.AddMapping(mapping.Pass()); - } - if (result != MOJO_RESULT_OK) - return result; - - *buffer = address; - return MOJO_RESULT_OK; -} - -MojoResult CoreImpl::UnmapBuffer(void* buffer) { - base::AutoLock locker(mapping_table_lock_); - return mapping_table_.RemoveMapping(buffer); -} - -scoped_refptr CoreImpl::GetDispatcher(MojoHandle handle) { - if (handle == MOJO_HANDLE_INVALID) - return NULL; - - base::AutoLock locker(handle_table_lock_); - return handle_table_.GetDispatcher(handle); -} - -// Note: We allow |handles| to repeat the same handle multiple times, since -// different flags may be specified. -// TODO(vtl): This incurs a performance cost in |RemoveWaiter()|. Analyze this -// more carefully and address it if necessary. -MojoResult CoreImpl::WaitManyInternal(const MojoHandle* handles, - const MojoWaitFlags* flags, - uint32_t num_handles, - MojoDeadline deadline) { - DCHECK_GT(num_handles, 0u); - - std::vector > dispatchers; - dispatchers.reserve(num_handles); - for (uint32_t i = 0; i < num_handles; i++) { - scoped_refptr dispatcher = GetDispatcher(handles[i]); - if (!dispatcher.get()) - return MOJO_RESULT_INVALID_ARGUMENT; - dispatchers.push_back(dispatcher); - } - - // TODO(vtl): Should make the waiter live (permanently) in TLS. - Waiter waiter; - waiter.Init(); - - uint32_t i; - MojoResult rv = MOJO_RESULT_OK; - for (i = 0; i < num_handles; i++) { - rv = dispatchers[i]->AddWaiter(&waiter, - flags[i], - static_cast(i)); - if (rv != MOJO_RESULT_OK) - break; - } - uint32_t num_added = i; - - if (rv == MOJO_RESULT_ALREADY_EXISTS) - rv = static_cast(i); // The i-th one is already "triggered". - else if (rv == MOJO_RESULT_OK) - rv = waiter.Wait(deadline); - - // Make sure no other dispatchers try to wake |waiter| for the current - // |Wait()|/|WaitMany()| call. (Only after doing this can |waiter| be - // destroyed, but this would still be required if the waiter were in TLS.) - for (i = 0; i < num_added; i++) - dispatchers[i]->RemoveWaiter(&waiter); - - return rv; -} - -} // namespace system -} // namespace mojo diff --git a/mojo/system/core_impl.h b/mojo/system/core_impl.h deleted file mode 100644 index e0cbd1e..0000000 --- a/mojo/system/core_impl.h +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2013 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_SYSTEM_CORE_IMPL_H_ -#define MOJO_SYSTEM_CORE_IMPL_H_ - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "mojo/public/system/core_private.h" -#include "mojo/system/handle_table.h" -#include "mojo/system/mapping_table.h" -#include "mojo/system/system_impl_export.h" - -namespace mojo { -namespace system { - -class Dispatcher; - -// |CoreImpl| is a singleton object that implements the Mojo system calls. All -// public methods are thread-safe. -class MOJO_SYSTEM_IMPL_EXPORT CoreImpl : public Core { - public: - // These methods are only to be used by via the embedder API. - CoreImpl(); - virtual ~CoreImpl(); - MojoHandle AddDispatcher(const scoped_refptr& dispatcher); - - // |CorePrivate| implementation: - virtual MojoTimeTicks GetTimeTicksNow() OVERRIDE; - virtual MojoResult Close(MojoHandle handle) OVERRIDE; - virtual MojoResult Wait(MojoHandle handle, - MojoWaitFlags flags, - MojoDeadline deadline) OVERRIDE; - virtual MojoResult WaitMany(const MojoHandle* handles, - const MojoWaitFlags* flags, - uint32_t num_handles, - MojoDeadline deadline) OVERRIDE; - virtual MojoResult CreateMessagePipe( - MojoHandle* message_pipe_handle0, - MojoHandle* message_pipe_handle1) OVERRIDE; - virtual MojoResult WriteMessage(MojoHandle message_pipe_handle, - const void* bytes, - uint32_t num_bytes, - const MojoHandle* handles, - uint32_t num_handles, - MojoWriteMessageFlags flags) OVERRIDE; - virtual MojoResult ReadMessage(MojoHandle message_pipe_handle, - void* bytes, - uint32_t* num_bytes, - MojoHandle* handles, - uint32_t* num_handles, - MojoReadMessageFlags flags) OVERRIDE; - virtual MojoResult CreateDataPipe( - const MojoCreateDataPipeOptions* options, - MojoHandle* data_pipe_producer_handle, - MojoHandle* data_pipe_consumer_handle) OVERRIDE; - virtual MojoResult WriteData(MojoHandle data_pipe_producer_handle, - const void* elements, - uint32_t* num_bytes, - MojoWriteDataFlags flags) OVERRIDE; - virtual MojoResult BeginWriteData(MojoHandle data_pipe_producer_handle, - void** buffer, - uint32_t* buffer_num_bytes, - MojoWriteDataFlags flags) OVERRIDE; - virtual MojoResult EndWriteData(MojoHandle data_pipe_producer_handle, - uint32_t num_bytes_written) OVERRIDE; - virtual MojoResult ReadData(MojoHandle data_pipe_consumer_handle, - void* elements, - uint32_t* num_bytes, - MojoReadDataFlags flags) OVERRIDE; - virtual MojoResult BeginReadData(MojoHandle data_pipe_consumer_handle, - const void** buffer, - uint32_t* buffer_num_bytes, - MojoReadDataFlags flags) OVERRIDE; - virtual MojoResult EndReadData(MojoHandle data_pipe_consumer_handle, - uint32_t num_bytes_read) OVERRIDE; - virtual MojoResult CreateSharedBuffer( - const MojoCreateSharedBufferOptions* options, - uint64_t num_bytes, - MojoHandle* shared_buffer_handle) OVERRIDE; - virtual MojoResult DuplicateBufferHandle( - MojoHandle buffer_handle, - const MojoDuplicateBufferHandleOptions* options, - MojoHandle* new_buffer_handle) OVERRIDE; - virtual MojoResult MapBuffer(MojoHandle buffer_handle, - uint64_t offset, - uint64_t num_bytes, - void** buffer, - MojoMapBufferFlags flags) OVERRIDE; - virtual MojoResult UnmapBuffer(void* buffer) OVERRIDE; - - private: - friend bool internal::ShutdownCheckNoLeaks(CoreImpl*); - - // The |busy| member is used only to deal with functions (in particular - // |WriteMessage()|) that want to hold on to a dispatcher and later remove it - // from the handle table, without holding on to the handle table lock. - // - // For example, if |WriteMessage()| is called with a handle to be sent, (under - // the handle table lock) it must first check that that handle is not busy (if - // it is busy, then it fails with |MOJO_RESULT_BUSY|) and then marks it as - // busy. To avoid deadlock, it should also try to acquire the locks for all - // the dispatchers for the handles that it is sending (and fail with - // |MOJO_RESULT_BUSY| if the attempt fails). At this point, it can release the - // handle table lock. - // - // If |Close()| is simultaneously called on that handle, it too checks if the - // handle is marked busy. If it is, it fails (with |MOJO_RESULT_BUSY|). This - // prevents |WriteMessage()| from sending a handle that has been closed (or - // learning about this too late). - struct HandleTableEntry { - HandleTableEntry(); - explicit HandleTableEntry(const scoped_refptr& dispatcher); - ~HandleTableEntry(); - - scoped_refptr dispatcher; - bool busy; - }; - typedef base::hash_map HandleTableMap; - - // Looks up the dispatcher for the given handle. Returns null if the handle is - // invalid. - scoped_refptr GetDispatcher(MojoHandle handle); - - // Internal implementation of |Wait()| and |WaitMany()|; doesn't do basic - // validation of arguments. - MojoResult WaitManyInternal(const MojoHandle* handles, - const MojoWaitFlags* flags, - uint32_t num_handles, - MojoDeadline deadline); - - // --------------------------------------------------------------------------- - - // TODO(vtl): |handle_table_lock_| should be a reader-writer lock (if only we - // had them). - base::Lock handle_table_lock_; // Protects |handle_table_|. - HandleTable handle_table_; - - base::Lock mapping_table_lock_; // Protects |mapping_table_|. - MappingTable mapping_table_; - - // --------------------------------------------------------------------------- - - DISALLOW_COPY_AND_ASSIGN(CoreImpl); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_SYSTEM_CORE_IMPL_H_ diff --git a/mojo/system/core_impl_unittest.cc b/mojo/system/core_impl_unittest.cc deleted file mode 100644 index 93dc860..0000000 --- a/mojo/system/core_impl_unittest.cc +++ /dev/null @@ -1,881 +0,0 @@ -// Copyright 2013 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/system/core_impl.h" - -#include - -#include "base/basictypes.h" -#include "base/threading/platform_thread.h" -#include "base/time/time.h" -#include "mojo/system/core_test_base.h" - -namespace mojo { -namespace system { -namespace { - -typedef test::CoreTestBase CoreImplTest; - -TEST_F(CoreImplTest, GetTimeTicksNow) { - const MojoTimeTicks start = core()->GetTimeTicksNow(); - EXPECT_NE(static_cast(0), start) - << "GetTimeTicksNow should return nonzero value"; - base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(15)); - const MojoTimeTicks finish = core()->GetTimeTicksNow(); - // Allow for some fuzz in sleep. - EXPECT_GE((finish - start), static_cast(8000)) - << "Sleeping should result in increasing time ticks"; -} - -TEST_F(CoreImplTest, Basic) { - MockHandleInfo info; - - EXPECT_EQ(0u, info.GetCtorCallCount()); - MojoHandle h = CreateMockHandle(&info); - EXPECT_EQ(1u, info.GetCtorCallCount()); - EXPECT_NE(h, MOJO_HANDLE_INVALID); - - EXPECT_EQ(0u, info.GetWriteMessageCallCount()); - EXPECT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h, NULL, 0, NULL, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(1u, info.GetWriteMessageCallCount()); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WriteMessage(h, NULL, 1, NULL, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(2u, info.GetWriteMessageCallCount()); - - EXPECT_EQ(0u, info.GetReadMessageCallCount()); - uint32_t num_bytes = 0; - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage(h, NULL, &num_bytes, NULL, NULL, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(1u, info.GetReadMessageCallCount()); - num_bytes = 1; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->ReadMessage(h, NULL, &num_bytes, NULL, NULL, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(2u, info.GetReadMessageCallCount()); - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage(h, NULL, NULL, NULL, NULL, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(3u, info.GetReadMessageCallCount()); - - EXPECT_EQ(0u, info.GetWriteDataCallCount()); - EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, - core()->WriteData(h, NULL, NULL, MOJO_WRITE_DATA_FLAG_NONE)); - EXPECT_EQ(1u, info.GetWriteDataCallCount()); - - EXPECT_EQ(0u, info.GetBeginWriteDataCallCount()); - EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, - core()->BeginWriteData(h, NULL, NULL, MOJO_WRITE_DATA_FLAG_NONE)); - EXPECT_EQ(1u, info.GetBeginWriteDataCallCount()); - - EXPECT_EQ(0u, info.GetEndWriteDataCallCount()); - EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, - core()->EndWriteData(h, 0)); - EXPECT_EQ(1u, info.GetEndWriteDataCallCount()); - - EXPECT_EQ(0u, info.GetReadDataCallCount()); - EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, - core()->ReadData(h, NULL, NULL, MOJO_READ_DATA_FLAG_NONE)); - EXPECT_EQ(1u, info.GetReadDataCallCount()); - - EXPECT_EQ(0u, info.GetBeginReadDataCallCount()); - EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, - core()->BeginReadData(h, NULL, NULL, MOJO_READ_DATA_FLAG_NONE)); - EXPECT_EQ(1u, info.GetBeginReadDataCallCount()); - - EXPECT_EQ(0u, info.GetEndReadDataCallCount()); - EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, - core()->EndReadData(h, 0)); - EXPECT_EQ(1u, info.GetEndReadDataCallCount()); - - EXPECT_EQ(0u, info.GetAddWaiterCallCount()); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(h, MOJO_WAIT_FLAG_EVERYTHING, - MOJO_DEADLINE_INDEFINITE)); - EXPECT_EQ(1u, info.GetAddWaiterCallCount()); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(h, MOJO_WAIT_FLAG_EVERYTHING, 0)); - EXPECT_EQ(2u, info.GetAddWaiterCallCount()); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(h, MOJO_WAIT_FLAG_EVERYTHING, 10 * 1000)); - EXPECT_EQ(3u, info.GetAddWaiterCallCount()); - MojoWaitFlags wait_flags = MOJO_WAIT_FLAG_EVERYTHING; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->WaitMany(&h, &wait_flags, 1, MOJO_DEADLINE_INDEFINITE)); - EXPECT_EQ(4u, info.GetAddWaiterCallCount()); - - EXPECT_EQ(0u, info.GetDtorCallCount()); - EXPECT_EQ(0u, info.GetCloseCallCount()); - EXPECT_EQ(0u, info.GetCancelAllWaitersCallCount()); - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h)); - EXPECT_EQ(1u, info.GetCancelAllWaitersCallCount()); - EXPECT_EQ(1u, info.GetCloseCallCount()); - EXPECT_EQ(1u, info.GetDtorCallCount()); - - // No waiters should ever have ever been added. - EXPECT_EQ(0u, info.GetRemoveWaiterCallCount()); -} - -TEST_F(CoreImplTest, InvalidArguments) { - // |Close()|: - { - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(MOJO_HANDLE_INVALID)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(10)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(1000000000)); - - // Test a double-close. - MockHandleInfo info; - MojoHandle h = CreateMockHandle(&info); - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h)); - EXPECT_EQ(1u, info.GetCloseCallCount()); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(h)); - EXPECT_EQ(1u, info.GetCloseCallCount()); - } - - // |Wait()|: - { - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->Wait(MOJO_HANDLE_INVALID, MOJO_WAIT_FLAG_EVERYTHING, - MOJO_DEADLINE_INDEFINITE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->Wait(10, MOJO_WAIT_FLAG_EVERYTHING, - MOJO_DEADLINE_INDEFINITE)); - } - - // |WaitMany()|: - { - MojoHandle handles[2] = { MOJO_HANDLE_INVALID, MOJO_HANDLE_INVALID }; - MojoWaitFlags flags[2] = { MOJO_WAIT_FLAG_EVERYTHING, - MOJO_WAIT_FLAG_EVERYTHING }; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WaitMany(handles, flags, 0, MOJO_DEADLINE_INDEFINITE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WaitMany(NULL, flags, 0, MOJO_DEADLINE_INDEFINITE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WaitMany(handles, NULL, 0, MOJO_DEADLINE_INDEFINITE)); - - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WaitMany(NULL, flags, 1, MOJO_DEADLINE_INDEFINITE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WaitMany(handles, NULL, 1, MOJO_DEADLINE_INDEFINITE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WaitMany(handles, flags, 1, MOJO_DEADLINE_INDEFINITE)); - - MockHandleInfo info[2]; - handles[0] = CreateMockHandle(&info[0]); - - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->WaitMany(handles, flags, 1, MOJO_DEADLINE_INDEFINITE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WaitMany(handles, flags, 2, MOJO_DEADLINE_INDEFINITE)); - handles[1] = handles[0] + 1; // Invalid handle. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WaitMany(handles, flags, 2, MOJO_DEADLINE_INDEFINITE)); - handles[1] = CreateMockHandle(&info[1]); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->WaitMany(handles, flags, 2, MOJO_DEADLINE_INDEFINITE)); - - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(handles[0])); - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(handles[1])); - } - - // |CreateMessagePipe()|: - { - MojoHandle h; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->CreateMessagePipe(NULL, NULL)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->CreateMessagePipe(&h, NULL)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->CreateMessagePipe(NULL, &h)); - } - - // |WriteMessage()|: - // Only check arguments checked by |CoreImpl|, namely |handle|, |handles|, and - // |num_handles|. - { - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WriteMessage(MOJO_HANDLE_INVALID, NULL, 0, NULL, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - MockHandleInfo info; - MojoHandle h = CreateMockHandle(&info); - MojoHandle handles[2] = { MOJO_HANDLE_INVALID, MOJO_HANDLE_INVALID }; - - // Null |handles| with nonzero |num_handles|. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WriteMessage(h, NULL, 0, NULL, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - // Checked by |CoreImpl|, shouldn't go through to the dispatcher. - EXPECT_EQ(0u, info.GetWriteMessageCallCount()); - - // Huge handle count (implausibly big on some systems -- more than can be - // stored in a 32-bit address space). - // Note: This may return either |MOJO_RESULT_INVALID_ARGUMENT| or - // |MOJO_RESULT_RESOURCE_EXHAUSTED|, depending on whether it's plausible or - // not. - EXPECT_NE(MOJO_RESULT_OK, - core()->WriteMessage(h, NULL, 0, handles, - std::numeric_limits::max(), - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(0u, info.GetWriteMessageCallCount()); - - // Huge handle count (plausibly big). - EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, - core()->WriteMessage(h, NULL, 0, handles, - std::numeric_limits::max() / - sizeof(handles[0]), - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(0u, info.GetWriteMessageCallCount()); - - // Invalid handle in |handles|. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WriteMessage(h, NULL, 0, handles, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(0u, info.GetWriteMessageCallCount()); - - // Two invalid handles in |handles|. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WriteMessage(h, NULL, 0, handles, 2, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(0u, info.GetWriteMessageCallCount()); - - // Can't send a handle over itself. - handles[0] = h; - EXPECT_EQ(MOJO_RESULT_BUSY, - core()->WriteMessage(h, NULL, 0, handles, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(0u, info.GetWriteMessageCallCount()); - - MockHandleInfo info2; - MojoHandle h2 = CreateMockHandle(&info2); - - // This is "okay", but |MockDispatcher| doesn't implement it. - handles[0] = h2; - EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, - core()->WriteMessage(h, NULL, 0, handles, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(1u, info.GetWriteMessageCallCount()); - - // One of the |handles| is still invalid. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WriteMessage(h, NULL, 0, handles, 2, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(1u, info.GetWriteMessageCallCount()); - - // One of the |handles| is the same as |handle|. - handles[1] = h; - EXPECT_EQ(MOJO_RESULT_BUSY, - core()->WriteMessage(h, NULL, 0, handles, 2, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(1u, info.GetWriteMessageCallCount()); - - // Can't send a handle twice in the same message. - handles[1] = h2; - EXPECT_EQ(MOJO_RESULT_BUSY, - core()->WriteMessage(h, NULL, 0, handles, 2, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(1u, info.GetWriteMessageCallCount()); - - // Note: Since we never successfully sent anything with it, |h2| should - // still be valid. - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h2)); - - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h)); - } - - // |ReadMessage()|: - // Only check arguments checked by |CoreImpl|, namely |handle|, |handles|, and - // |num_handles|. - { - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->ReadMessage(MOJO_HANDLE_INVALID, NULL, NULL, NULL, NULL, - MOJO_READ_MESSAGE_FLAG_NONE)); - - MockHandleInfo info; - MojoHandle h = CreateMockHandle(&info); - - uint32_t handle_count = 1; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->ReadMessage(h, NULL, NULL, NULL, &handle_count, - MOJO_READ_MESSAGE_FLAG_NONE)); - // Checked by |CoreImpl|, shouldn't go through to the dispatcher. - EXPECT_EQ(0u, info.GetReadMessageCallCount()); - - // Okay. - handle_count = 0; - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage(h, NULL, NULL, NULL, &handle_count, - MOJO_READ_MESSAGE_FLAG_NONE)); - // Checked by |CoreImpl|, shouldn't go through to the dispatcher. - EXPECT_EQ(1u, info.GetReadMessageCallCount()); - - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h)); - } -} - -// TODO(vtl): test |Wait()| and |WaitMany()| properly -// - including |WaitMany()| with the same handle more than once (with -// same/different flags) - -TEST_F(CoreImplTest, MessagePipe) { - MojoHandle h[2]; - - EXPECT_EQ(MOJO_RESULT_OK, core()->CreateMessagePipe(&h[0], &h[1])); - // Should get two distinct, valid handles. - EXPECT_NE(h[0], MOJO_HANDLE_INVALID); - EXPECT_NE(h[1], MOJO_HANDLE_INVALID); - EXPECT_NE(h[0], h[1]); - - // Neither should be readable. - MojoWaitFlags flags[2] = { MOJO_WAIT_FLAG_READABLE, MOJO_WAIT_FLAG_READABLE }; - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - core()->WaitMany(h, flags, 2, 0)); - - // Try to read anyway. - char buffer[1] = { 'a' }; - uint32_t buffer_size = 1; - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - core()->ReadMessage(h[0], buffer, &buffer_size, NULL, NULL, - MOJO_READ_MESSAGE_FLAG_NONE)); - // Check that it left its inputs alone. - EXPECT_EQ('a', buffer[0]); - EXPECT_EQ(1u, buffer_size); - - // Both should be writable. - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h[0], MOJO_WAIT_FLAG_WRITABLE, 1000000000)); - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h[1], MOJO_WAIT_FLAG_WRITABLE, 1000000000)); - - // Also check that |h[1]| is writable using |WaitMany()|. - flags[0] = MOJO_WAIT_FLAG_READABLE; - flags[1] = MOJO_WAIT_FLAG_WRITABLE; - EXPECT_EQ(1, core()->WaitMany(h, flags, 2, MOJO_DEADLINE_INDEFINITE)); - - // Write to |h[1]|. - buffer[0] = 'b'; - EXPECT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h[1], buffer, 1, NULL, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Check that |h[0]| is now readable. - flags[0] = MOJO_WAIT_FLAG_READABLE; - flags[1] = MOJO_WAIT_FLAG_READABLE; - EXPECT_EQ(0, core()->WaitMany(h, flags, 2, MOJO_DEADLINE_INDEFINITE)); - - // Read from |h[0]|. - // First, get only the size. - buffer_size = 0; - EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, - core()->ReadMessage(h[0], NULL, &buffer_size, NULL, NULL, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(1u, buffer_size); - // Then actually read it. - buffer[0] = 'c'; - buffer_size = 1; - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage(h[0], buffer, &buffer_size, NULL, NULL, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ('b', buffer[0]); - EXPECT_EQ(1u, buffer_size); - - // |h[0]| should no longer be readable. - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - core()->Wait(h[0], MOJO_WAIT_FLAG_READABLE, 0)); - - // Write to |h[0]|. - buffer[0] = 'd'; - EXPECT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h[0], buffer, 1, NULL, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Close |h[0]|. - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h[0])); - - // Check that |h[1]| is no longer writable (and will never be). - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(h[1], MOJO_WAIT_FLAG_WRITABLE, 1000000000)); - - // Check that |h[1]| is still readable (for the moment). - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h[1], MOJO_WAIT_FLAG_READABLE, 1000000000)); - - // Discard a message from |h[1]|. - EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, - core()->ReadMessage(h[1], NULL, NULL, NULL, NULL, - MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)); - - // |h[1]| is no longer readable (and will never be). - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(h[1], MOJO_WAIT_FLAG_READABLE, 1000000000)); - - // Try writing to |h[1]|. - buffer[0] = 'e'; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->WriteMessage(h[1], buffer, 1, NULL, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h[1])); -} - -// Tests passing a message pipe handle. -TEST_F(CoreImplTest, MessagePipeBasicLocalHandlePassing1) { - const char kHello[] = "hello"; - const uint32_t kHelloSize = static_cast(sizeof(kHello)); - const char kWorld[] = "world!!!"; - const uint32_t kWorldSize = static_cast(sizeof(kWorld)); - char buffer[100]; - const uint32_t kBufferSize = static_cast(sizeof(buffer)); - uint32_t num_bytes; - MojoHandle handles[10]; - uint32_t num_handles; - MojoHandle h_received; - - MojoHandle h_passing[2]; - EXPECT_EQ(MOJO_RESULT_OK, - core()->CreateMessagePipe(&h_passing[0], &h_passing[1])); - - // Make sure that |h_passing[]| work properly. - EXPECT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h_passing[0], - kHello, kHelloSize, - NULL, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h_passing[1], MOJO_WAIT_FLAG_READABLE, 1000000000)); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage(h_passing[1], - buffer, &num_bytes, - handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(kHelloSize, num_bytes); - EXPECT_STREQ(kHello, buffer); - EXPECT_EQ(0u, num_handles); - - // Make sure that you can't pass either of the message pipe's handles over - // itself. - EXPECT_EQ(MOJO_RESULT_BUSY, - core()->WriteMessage(h_passing[0], - kHello, kHelloSize, - &h_passing[0], 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WriteMessage(h_passing[0], - kHello, kHelloSize, - &h_passing[1], 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - MojoHandle h_passed[2]; - EXPECT_EQ(MOJO_RESULT_OK, - core()->CreateMessagePipe(&h_passed[0], &h_passed[1])); - - // Make sure that |h_passed[]| work properly. - EXPECT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h_passed[0], - kHello, kHelloSize, - NULL, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h_passed[1], MOJO_WAIT_FLAG_READABLE, 1000000000)); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage(h_passed[1], - buffer, &num_bytes, - handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(kHelloSize, num_bytes); - EXPECT_STREQ(kHello, buffer); - EXPECT_EQ(0u, num_handles); - - // Send |h_passed[1]| from |h_passing[0]| to |h_passing[1]|. - EXPECT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h_passing[0], - kWorld, kWorldSize, - &h_passed[1], 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h_passing[1], MOJO_WAIT_FLAG_READABLE, 1000000000)); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage(h_passing[1], - buffer, &num_bytes, - handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(kWorldSize, num_bytes); - EXPECT_STREQ(kWorld, buffer); - EXPECT_EQ(1u, num_handles); - h_received = handles[0]; - EXPECT_NE(h_received, MOJO_HANDLE_INVALID); - EXPECT_NE(h_received, h_passing[0]); - EXPECT_NE(h_received, h_passing[1]); - EXPECT_NE(h_received, h_passed[0]); - - // Note: We rely on the Mojo system not re-using handle values very often. - EXPECT_NE(h_received, h_passed[1]); - - // |h_passed[1]| should no longer be valid; check that trying to close it - // fails. See above note. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(h_passed[1])); - - // Write to |h_passed[0]|. Should receive on |h_received|. - EXPECT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h_passed[0], - kHello, kHelloSize, - NULL, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h_received, MOJO_WAIT_FLAG_READABLE, 1000000000)); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage(h_received, - buffer, &num_bytes, - handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(kHelloSize, num_bytes); - EXPECT_STREQ(kHello, buffer); - EXPECT_EQ(0u, num_handles); - - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[0])); - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[1])); - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passed[0])); - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_received)); -} - -TEST_F(CoreImplTest, DataPipe) { - MojoHandle ph, ch; // p is for producer and c is for consumer. - - EXPECT_EQ(MOJO_RESULT_OK, core()->CreateDataPipe(NULL, &ph, &ch)); - // Should get two distinct, valid handles. - EXPECT_NE(ph, MOJO_HANDLE_INVALID); - EXPECT_NE(ch, MOJO_HANDLE_INVALID); - EXPECT_NE(ph, ch); - - // Producer should be never-readable, but already writable. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(ph, MOJO_WAIT_FLAG_READABLE, 0)); - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(ph, MOJO_WAIT_FLAG_WRITABLE, 0)); - - // Consumer should be never-writable, and not yet readable. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(ch, MOJO_WAIT_FLAG_WRITABLE, 0)); - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - core()->Wait(ch, MOJO_WAIT_FLAG_READABLE, 0)); - - // Write. - char elements[2] = { 'A', 'B' }; - uint32_t num_bytes = 2u; - EXPECT_EQ(MOJO_RESULT_OK, - core()->WriteData(ph, elements, &num_bytes, - MOJO_WRITE_DATA_FLAG_NONE)); - EXPECT_EQ(2u, num_bytes); - - // Consumer should now be readable. - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(ch, MOJO_WAIT_FLAG_READABLE, 0)); - - // Read one character. - elements[0] = -1; - elements[1] = -1; - num_bytes = 1u; - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadData(ch, elements, &num_bytes, - MOJO_READ_DATA_FLAG_NONE)); - EXPECT_EQ('A', elements[0]); - EXPECT_EQ(-1, elements[1]); - - // Two-phase write. - void* write_ptr = NULL; - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, - core()->BeginWriteData(ph, &write_ptr, &num_bytes, - MOJO_WRITE_DATA_FLAG_NONE)); - // We count on the default options providing a decent buffer size. - ASSERT_GE(num_bytes, 3u); - - // Trying to do a normal write during a two-phase write should fail. - elements[0] = 'X'; - num_bytes = 1u; - EXPECT_EQ(MOJO_RESULT_BUSY, - core()->WriteData(ph, elements, &num_bytes, - MOJO_WRITE_DATA_FLAG_NONE)); - - // Actually write the data, and complete it now. - static_cast(write_ptr)[0] = 'C'; - static_cast(write_ptr)[1] = 'D'; - static_cast(write_ptr)[2] = 'E'; - EXPECT_EQ(MOJO_RESULT_OK, core()->EndWriteData(ph, 3u)); - - // Query how much data we have. - num_bytes = 0; - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadData(ch, NULL, &num_bytes, MOJO_READ_DATA_FLAG_QUERY)); - EXPECT_EQ(4u, num_bytes); - - // Try to discard ten characters, in all-or-none mode. Should fail. - num_bytes = 10; - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - core()->ReadData(ch, NULL, &num_bytes, - MOJO_READ_DATA_FLAG_DISCARD | - MOJO_READ_DATA_FLAG_ALL_OR_NONE)); - - // Discard two characters. - num_bytes = 2; - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadData(ch, NULL, &num_bytes, - MOJO_READ_DATA_FLAG_DISCARD | - MOJO_READ_DATA_FLAG_ALL_OR_NONE)); - - // Read the remaining two characters, in two-phase mode (all-or-none). - const void* read_ptr = NULL; - num_bytes = 2; - ASSERT_EQ(MOJO_RESULT_OK, - core()->BeginReadData(ch, &read_ptr, &num_bytes, - MOJO_READ_DATA_FLAG_ALL_OR_NONE)); - // Note: Count on still being able to do the contiguous read here. - ASSERT_EQ(2u, num_bytes); - - // Discarding right now should fail. - num_bytes = 1; - EXPECT_EQ(MOJO_RESULT_BUSY, - core()->ReadData(ch, NULL, &num_bytes, - MOJO_READ_DATA_FLAG_DISCARD)); - - // Actually check our data and end the two-phase read. - EXPECT_EQ('D', static_cast(read_ptr)[0]); - EXPECT_EQ('E', static_cast(read_ptr)[1]); - EXPECT_EQ(MOJO_RESULT_OK, core()->EndReadData(ch, 2u)); - - // Consumer should now be no longer readable. - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - core()->Wait(ch, MOJO_WAIT_FLAG_READABLE, 0)); - - // TODO(vtl): More. - - // Close the producer. - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ph)); - - // The consumer should now be never-readable. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(ch, MOJO_WAIT_FLAG_READABLE, 0)); - - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ch)); -} - -// Tests passing data pipe producer and consumer handles. -TEST_F(CoreImplTest, MessagePipeBasicLocalHandlePassing2) { - const char kHello[] = "hello"; - const uint32_t kHelloSize = static_cast(sizeof(kHello)); - const char kWorld[] = "world!!!"; - const uint32_t kWorldSize = static_cast(sizeof(kWorld)); - char buffer[100]; - const uint32_t kBufferSize = static_cast(sizeof(buffer)); - uint32_t num_bytes; - MojoHandle handles[10]; - uint32_t num_handles; - - MojoHandle h_passing[2]; - EXPECT_EQ(MOJO_RESULT_OK, - core()->CreateMessagePipe(&h_passing[0], &h_passing[1])); - - MojoHandle ph, ch; - EXPECT_EQ(MOJO_RESULT_OK, - core()->CreateDataPipe(NULL, &ph, &ch)); - - // Send |ch| from |h_passing[0]| to |h_passing[1]|. - EXPECT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h_passing[0], - kHello, kHelloSize, - &ch, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h_passing[1], MOJO_WAIT_FLAG_READABLE, 1000000000)); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage(h_passing[1], - buffer, &num_bytes, - handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(kHelloSize, num_bytes); - EXPECT_STREQ(kHello, buffer); - EXPECT_EQ(1u, num_handles); - MojoHandle ch_received = handles[0]; - EXPECT_NE(ch_received, MOJO_HANDLE_INVALID); - EXPECT_NE(ch_received, h_passing[0]); - EXPECT_NE(ch_received, h_passing[1]); - EXPECT_NE(ch_received, ph); - - // Note: We rely on the Mojo system not re-using handle values very often. - EXPECT_NE(ch_received, ch); - - // |ch| should no longer be valid; check that trying to close it fails. See - // above note. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(ch)); - - // Write to |ph|. Should receive on |ch_received|. - num_bytes = kWorldSize; - EXPECT_EQ(MOJO_RESULT_OK, - core()->WriteData(ph, kWorld, &num_bytes, - MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)); - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(ch_received, MOJO_WAIT_FLAG_READABLE, 1000000000)); - num_bytes = kBufferSize; - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadData(ch_received, buffer, &num_bytes, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(kWorldSize, num_bytes); - EXPECT_STREQ(kWorld, buffer); - - // Now pass |ph| in the same direction. - EXPECT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h_passing[0], - kWorld, kWorldSize, - &ph, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h_passing[1], MOJO_WAIT_FLAG_READABLE, 1000000000)); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage(h_passing[1], - buffer, &num_bytes, - handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(kWorldSize, num_bytes); - EXPECT_STREQ(kWorld, buffer); - EXPECT_EQ(1u, num_handles); - MojoHandle ph_received = handles[0]; - EXPECT_NE(ph_received, MOJO_HANDLE_INVALID); - EXPECT_NE(ph_received, h_passing[0]); - EXPECT_NE(ph_received, h_passing[1]); - EXPECT_NE(ph_received, ch_received); - - // Again, rely on the Mojo system not re-using handle values very often. - EXPECT_NE(ph_received, ph); - - // |ph| should no longer be valid; check that trying to close it fails. See - // above note. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(ph)); - - // Write to |ph_received|. Should receive on |ch_received|. - num_bytes = kHelloSize; - EXPECT_EQ(MOJO_RESULT_OK, - core()->WriteData(ph_received, kHello, &num_bytes, - MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)); - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(ch_received, MOJO_WAIT_FLAG_READABLE, 1000000000)); - num_bytes = kBufferSize; - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadData(ch_received, buffer, &num_bytes, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(kHelloSize, num_bytes); - EXPECT_STREQ(kHello, buffer); - - ph = ph_received; - ph_received = MOJO_HANDLE_INVALID; - ch = ch_received; - ch_received = MOJO_HANDLE_INVALID; - - // Make sure that |ph| can't be sent if it's in a two-phase write. - void* write_ptr = NULL; - num_bytes = 0; - ASSERT_EQ(MOJO_RESULT_OK, - core()->BeginWriteData(ph, &write_ptr, &num_bytes, - MOJO_WRITE_DATA_FLAG_NONE)); - ASSERT_GE(num_bytes, 1u); - EXPECT_EQ(MOJO_RESULT_BUSY, - core()->WriteMessage(h_passing[0], - kHello, kHelloSize, - &ph, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // But |ch| can, even if |ph| is in a two-phase write. - EXPECT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h_passing[0], - kHello, kHelloSize, - &ch, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ch = MOJO_HANDLE_INVALID; - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h_passing[1], MOJO_WAIT_FLAG_READABLE, 1000000000)); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage(h_passing[1], - buffer, &num_bytes, - handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(kHelloSize, num_bytes); - EXPECT_STREQ(kHello, buffer); - EXPECT_EQ(1u, num_handles); - ch = handles[0]; - EXPECT_NE(ch, MOJO_HANDLE_INVALID); - - // Complete the two-phase write. - static_cast(write_ptr)[0] = 'x'; - EXPECT_EQ(MOJO_RESULT_OK, core()->EndWriteData(ph, 1)); - - // Wait for |ch| to be readable. - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(ch, MOJO_WAIT_FLAG_READABLE, 1000000000)); - - // Make sure that |ch| can't be sent if it's in a two-phase read. - const void* read_ptr = NULL; - num_bytes = 1; - ASSERT_EQ(MOJO_RESULT_OK, - core()->BeginReadData(ch, &read_ptr, &num_bytes, - MOJO_READ_DATA_FLAG_ALL_OR_NONE)); - EXPECT_EQ(MOJO_RESULT_BUSY, - core()->WriteMessage(h_passing[0], - kHello, kHelloSize, - &ch, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // But |ph| can, even if |ch| is in a two-phase read. - EXPECT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h_passing[0], - kWorld, kWorldSize, - &ph, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ph = MOJO_HANDLE_INVALID; - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h_passing[1], MOJO_WAIT_FLAG_READABLE, 1000000000)); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage(h_passing[1], - buffer, &num_bytes, - handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(kWorldSize, num_bytes); - EXPECT_STREQ(kWorld, buffer); - EXPECT_EQ(1u, num_handles); - ph = handles[0]; - EXPECT_NE(ph, MOJO_HANDLE_INVALID); - - // Complete the two-phase read. - EXPECT_EQ('x', static_cast(read_ptr)[0]); - EXPECT_EQ(MOJO_RESULT_OK, core()->EndReadData(ch, 1)); - - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[0])); - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[1])); - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ph)); - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ch)); -} - -// TODO(vtl): Test |DuplicateBufferHandle()| and |MapBuffer()|. - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/system/core_test_base.cc b/mojo/system/core_test_base.cc index 89b8fb3..29c3c5c 100644 --- a/mojo/system/core_test_base.cc +++ b/mojo/system/core_test_base.cc @@ -10,7 +10,7 @@ #include "base/logging.h" #include "base/memory/ref_counted.h" #include "mojo/system/constants.h" -#include "mojo/system/core_impl.h" +#include "mojo/system/core.h" #include "mojo/system/dispatcher.h" #include "mojo/system/memory.h" @@ -168,7 +168,7 @@ CoreTestBase::~CoreTestBase() { } void CoreTestBase::SetUp() { - core_ = new CoreImpl(); + core_ = new Core(); } void CoreTestBase::TearDown() { diff --git a/mojo/system/core_test_base.h b/mojo/system/core_test_base.h index beb985b..b725938 100644 --- a/mojo/system/core_test_base.h +++ b/mojo/system/core_test_base.h @@ -14,7 +14,7 @@ namespace mojo { namespace system { -class CoreImpl; +class Core; namespace test { @@ -34,10 +34,10 @@ class CoreTestBase : public testing::Test { // |info| must remain alive until the returned handle is closed. MojoHandle CreateMockHandle(MockHandleInfo* info); - CoreImpl* core() { return core_; } + Core* core() { return core_; } private: - CoreImpl* core_; + Core* core_; DISALLOW_COPY_AND_ASSIGN(CoreTestBase); }; diff --git a/mojo/system/core_unittest.cc b/mojo/system/core_unittest.cc new file mode 100644 index 0000000..8fab233 --- /dev/null +++ b/mojo/system/core_unittest.cc @@ -0,0 +1,881 @@ +// Copyright 2013 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/system/core.h" + +#include + +#include "base/basictypes.h" +#include "base/threading/platform_thread.h" +#include "base/time/time.h" +#include "mojo/system/core_test_base.h" + +namespace mojo { +namespace system { +namespace { + +typedef test::CoreTestBase CoreTest; + +TEST_F(CoreTest, GetTimeTicksNow) { + const MojoTimeTicks start = core()->GetTimeTicksNow(); + EXPECT_NE(static_cast(0), start) + << "GetTimeTicksNow should return nonzero value"; + base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(15)); + const MojoTimeTicks finish = core()->GetTimeTicksNow(); + // Allow for some fuzz in sleep. + EXPECT_GE((finish - start), static_cast(8000)) + << "Sleeping should result in increasing time ticks"; +} + +TEST_F(CoreTest, Basic) { + MockHandleInfo info; + + EXPECT_EQ(0u, info.GetCtorCallCount()); + MojoHandle h = CreateMockHandle(&info); + EXPECT_EQ(1u, info.GetCtorCallCount()); + EXPECT_NE(h, MOJO_HANDLE_INVALID); + + EXPECT_EQ(0u, info.GetWriteMessageCallCount()); + EXPECT_EQ(MOJO_RESULT_OK, + core()->WriteMessage(h, NULL, 0, NULL, 0, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(1u, info.GetWriteMessageCallCount()); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->WriteMessage(h, NULL, 1, NULL, 0, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(2u, info.GetWriteMessageCallCount()); + + EXPECT_EQ(0u, info.GetReadMessageCallCount()); + uint32_t num_bytes = 0; + EXPECT_EQ(MOJO_RESULT_OK, + core()->ReadMessage(h, NULL, &num_bytes, NULL, NULL, + MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(1u, info.GetReadMessageCallCount()); + num_bytes = 1; + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->ReadMessage(h, NULL, &num_bytes, NULL, NULL, + MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(2u, info.GetReadMessageCallCount()); + EXPECT_EQ(MOJO_RESULT_OK, + core()->ReadMessage(h, NULL, NULL, NULL, NULL, + MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(3u, info.GetReadMessageCallCount()); + + EXPECT_EQ(0u, info.GetWriteDataCallCount()); + EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, + core()->WriteData(h, NULL, NULL, MOJO_WRITE_DATA_FLAG_NONE)); + EXPECT_EQ(1u, info.GetWriteDataCallCount()); + + EXPECT_EQ(0u, info.GetBeginWriteDataCallCount()); + EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, + core()->BeginWriteData(h, NULL, NULL, MOJO_WRITE_DATA_FLAG_NONE)); + EXPECT_EQ(1u, info.GetBeginWriteDataCallCount()); + + EXPECT_EQ(0u, info.GetEndWriteDataCallCount()); + EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, + core()->EndWriteData(h, 0)); + EXPECT_EQ(1u, info.GetEndWriteDataCallCount()); + + EXPECT_EQ(0u, info.GetReadDataCallCount()); + EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, + core()->ReadData(h, NULL, NULL, MOJO_READ_DATA_FLAG_NONE)); + EXPECT_EQ(1u, info.GetReadDataCallCount()); + + EXPECT_EQ(0u, info.GetBeginReadDataCallCount()); + EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, + core()->BeginReadData(h, NULL, NULL, MOJO_READ_DATA_FLAG_NONE)); + EXPECT_EQ(1u, info.GetBeginReadDataCallCount()); + + EXPECT_EQ(0u, info.GetEndReadDataCallCount()); + EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, + core()->EndReadData(h, 0)); + EXPECT_EQ(1u, info.GetEndReadDataCallCount()); + + EXPECT_EQ(0u, info.GetAddWaiterCallCount()); + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + core()->Wait(h, MOJO_WAIT_FLAG_EVERYTHING, + MOJO_DEADLINE_INDEFINITE)); + EXPECT_EQ(1u, info.GetAddWaiterCallCount()); + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + core()->Wait(h, MOJO_WAIT_FLAG_EVERYTHING, 0)); + EXPECT_EQ(2u, info.GetAddWaiterCallCount()); + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + core()->Wait(h, MOJO_WAIT_FLAG_EVERYTHING, 10 * 1000)); + EXPECT_EQ(3u, info.GetAddWaiterCallCount()); + MojoWaitFlags wait_flags = MOJO_WAIT_FLAG_EVERYTHING; + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + core()->WaitMany(&h, &wait_flags, 1, MOJO_DEADLINE_INDEFINITE)); + EXPECT_EQ(4u, info.GetAddWaiterCallCount()); + + EXPECT_EQ(0u, info.GetDtorCallCount()); + EXPECT_EQ(0u, info.GetCloseCallCount()); + EXPECT_EQ(0u, info.GetCancelAllWaitersCallCount()); + EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h)); + EXPECT_EQ(1u, info.GetCancelAllWaitersCallCount()); + EXPECT_EQ(1u, info.GetCloseCallCount()); + EXPECT_EQ(1u, info.GetDtorCallCount()); + + // No waiters should ever have ever been added. + EXPECT_EQ(0u, info.GetRemoveWaiterCallCount()); +} + +TEST_F(CoreTest, InvalidArguments) { + // |Close()|: + { + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(MOJO_HANDLE_INVALID)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(10)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(1000000000)); + + // Test a double-close. + MockHandleInfo info; + MojoHandle h = CreateMockHandle(&info); + EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h)); + EXPECT_EQ(1u, info.GetCloseCallCount()); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(h)); + EXPECT_EQ(1u, info.GetCloseCallCount()); + } + + // |Wait()|: + { + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->Wait(MOJO_HANDLE_INVALID, MOJO_WAIT_FLAG_EVERYTHING, + MOJO_DEADLINE_INDEFINITE)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->Wait(10, MOJO_WAIT_FLAG_EVERYTHING, + MOJO_DEADLINE_INDEFINITE)); + } + + // |WaitMany()|: + { + MojoHandle handles[2] = { MOJO_HANDLE_INVALID, MOJO_HANDLE_INVALID }; + MojoWaitFlags flags[2] = { MOJO_WAIT_FLAG_EVERYTHING, + MOJO_WAIT_FLAG_EVERYTHING }; + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->WaitMany(handles, flags, 0, MOJO_DEADLINE_INDEFINITE)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->WaitMany(NULL, flags, 0, MOJO_DEADLINE_INDEFINITE)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->WaitMany(handles, NULL, 0, MOJO_DEADLINE_INDEFINITE)); + + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->WaitMany(NULL, flags, 1, MOJO_DEADLINE_INDEFINITE)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->WaitMany(handles, NULL, 1, MOJO_DEADLINE_INDEFINITE)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->WaitMany(handles, flags, 1, MOJO_DEADLINE_INDEFINITE)); + + MockHandleInfo info[2]; + handles[0] = CreateMockHandle(&info[0]); + + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + core()->WaitMany(handles, flags, 1, MOJO_DEADLINE_INDEFINITE)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->WaitMany(handles, flags, 2, MOJO_DEADLINE_INDEFINITE)); + handles[1] = handles[0] + 1; // Invalid handle. + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->WaitMany(handles, flags, 2, MOJO_DEADLINE_INDEFINITE)); + handles[1] = CreateMockHandle(&info[1]); + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + core()->WaitMany(handles, flags, 2, MOJO_DEADLINE_INDEFINITE)); + + EXPECT_EQ(MOJO_RESULT_OK, core()->Close(handles[0])); + EXPECT_EQ(MOJO_RESULT_OK, core()->Close(handles[1])); + } + + // |CreateMessagePipe()|: + { + MojoHandle h; + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->CreateMessagePipe(NULL, NULL)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->CreateMessagePipe(&h, NULL)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->CreateMessagePipe(NULL, &h)); + } + + // |WriteMessage()|: + // Only check arguments checked by |Core|, namely |handle|, |handles|, and + // |num_handles|. + { + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->WriteMessage(MOJO_HANDLE_INVALID, NULL, 0, NULL, 0, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + + MockHandleInfo info; + MojoHandle h = CreateMockHandle(&info); + MojoHandle handles[2] = { MOJO_HANDLE_INVALID, MOJO_HANDLE_INVALID }; + + // Null |handles| with nonzero |num_handles|. + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->WriteMessage(h, NULL, 0, NULL, 1, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + // Checked by |Core|, shouldn't go through to the dispatcher. + EXPECT_EQ(0u, info.GetWriteMessageCallCount()); + + // Huge handle count (implausibly big on some systems -- more than can be + // stored in a 32-bit address space). + // Note: This may return either |MOJO_RESULT_INVALID_ARGUMENT| or + // |MOJO_RESULT_RESOURCE_EXHAUSTED|, depending on whether it's plausible or + // not. + EXPECT_NE(MOJO_RESULT_OK, + core()->WriteMessage(h, NULL, 0, handles, + std::numeric_limits::max(), + MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(0u, info.GetWriteMessageCallCount()); + + // Huge handle count (plausibly big). + EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, + core()->WriteMessage(h, NULL, 0, handles, + std::numeric_limits::max() / + sizeof(handles[0]), + MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(0u, info.GetWriteMessageCallCount()); + + // Invalid handle in |handles|. + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->WriteMessage(h, NULL, 0, handles, 1, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(0u, info.GetWriteMessageCallCount()); + + // Two invalid handles in |handles|. + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->WriteMessage(h, NULL, 0, handles, 2, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(0u, info.GetWriteMessageCallCount()); + + // Can't send a handle over itself. + handles[0] = h; + EXPECT_EQ(MOJO_RESULT_BUSY, + core()->WriteMessage(h, NULL, 0, handles, 1, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(0u, info.GetWriteMessageCallCount()); + + MockHandleInfo info2; + MojoHandle h2 = CreateMockHandle(&info2); + + // This is "okay", but |MockDispatcher| doesn't implement it. + handles[0] = h2; + EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, + core()->WriteMessage(h, NULL, 0, handles, 1, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(1u, info.GetWriteMessageCallCount()); + + // One of the |handles| is still invalid. + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->WriteMessage(h, NULL, 0, handles, 2, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(1u, info.GetWriteMessageCallCount()); + + // One of the |handles| is the same as |handle|. + handles[1] = h; + EXPECT_EQ(MOJO_RESULT_BUSY, + core()->WriteMessage(h, NULL, 0, handles, 2, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(1u, info.GetWriteMessageCallCount()); + + // Can't send a handle twice in the same message. + handles[1] = h2; + EXPECT_EQ(MOJO_RESULT_BUSY, + core()->WriteMessage(h, NULL, 0, handles, 2, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(1u, info.GetWriteMessageCallCount()); + + // Note: Since we never successfully sent anything with it, |h2| should + // still be valid. + EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h2)); + + EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h)); + } + + // |ReadMessage()|: + // Only check arguments checked by |Core|, namely |handle|, |handles|, and + // |num_handles|. + { + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->ReadMessage(MOJO_HANDLE_INVALID, NULL, NULL, NULL, NULL, + MOJO_READ_MESSAGE_FLAG_NONE)); + + MockHandleInfo info; + MojoHandle h = CreateMockHandle(&info); + + uint32_t handle_count = 1; + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->ReadMessage(h, NULL, NULL, NULL, &handle_count, + MOJO_READ_MESSAGE_FLAG_NONE)); + // Checked by |Core|, shouldn't go through to the dispatcher. + EXPECT_EQ(0u, info.GetReadMessageCallCount()); + + // Okay. + handle_count = 0; + EXPECT_EQ(MOJO_RESULT_OK, + core()->ReadMessage(h, NULL, NULL, NULL, &handle_count, + MOJO_READ_MESSAGE_FLAG_NONE)); + // Checked by |Core|, shouldn't go through to the dispatcher. + EXPECT_EQ(1u, info.GetReadMessageCallCount()); + + EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h)); + } +} + +// TODO(vtl): test |Wait()| and |WaitMany()| properly +// - including |WaitMany()| with the same handle more than once (with +// same/different flags) + +TEST_F(CoreTest, MessagePipe) { + MojoHandle h[2]; + + EXPECT_EQ(MOJO_RESULT_OK, core()->CreateMessagePipe(&h[0], &h[1])); + // Should get two distinct, valid handles. + EXPECT_NE(h[0], MOJO_HANDLE_INVALID); + EXPECT_NE(h[1], MOJO_HANDLE_INVALID); + EXPECT_NE(h[0], h[1]); + + // Neither should be readable. + MojoWaitFlags flags[2] = { MOJO_WAIT_FLAG_READABLE, MOJO_WAIT_FLAG_READABLE }; + EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, + core()->WaitMany(h, flags, 2, 0)); + + // Try to read anyway. + char buffer[1] = { 'a' }; + uint32_t buffer_size = 1; + EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, + core()->ReadMessage(h[0], buffer, &buffer_size, NULL, NULL, + MOJO_READ_MESSAGE_FLAG_NONE)); + // Check that it left its inputs alone. + EXPECT_EQ('a', buffer[0]); + EXPECT_EQ(1u, buffer_size); + + // Both should be writable. + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(h[0], MOJO_WAIT_FLAG_WRITABLE, 1000000000)); + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(h[1], MOJO_WAIT_FLAG_WRITABLE, 1000000000)); + + // Also check that |h[1]| is writable using |WaitMany()|. + flags[0] = MOJO_WAIT_FLAG_READABLE; + flags[1] = MOJO_WAIT_FLAG_WRITABLE; + EXPECT_EQ(1, core()->WaitMany(h, flags, 2, MOJO_DEADLINE_INDEFINITE)); + + // Write to |h[1]|. + buffer[0] = 'b'; + EXPECT_EQ(MOJO_RESULT_OK, + core()->WriteMessage(h[1], buffer, 1, NULL, 0, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + + // Check that |h[0]| is now readable. + flags[0] = MOJO_WAIT_FLAG_READABLE; + flags[1] = MOJO_WAIT_FLAG_READABLE; + EXPECT_EQ(0, core()->WaitMany(h, flags, 2, MOJO_DEADLINE_INDEFINITE)); + + // Read from |h[0]|. + // First, get only the size. + buffer_size = 0; + EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, + core()->ReadMessage(h[0], NULL, &buffer_size, NULL, NULL, + MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(1u, buffer_size); + // Then actually read it. + buffer[0] = 'c'; + buffer_size = 1; + EXPECT_EQ(MOJO_RESULT_OK, + core()->ReadMessage(h[0], buffer, &buffer_size, NULL, NULL, + MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ('b', buffer[0]); + EXPECT_EQ(1u, buffer_size); + + // |h[0]| should no longer be readable. + EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, + core()->Wait(h[0], MOJO_WAIT_FLAG_READABLE, 0)); + + // Write to |h[0]|. + buffer[0] = 'd'; + EXPECT_EQ(MOJO_RESULT_OK, + core()->WriteMessage(h[0], buffer, 1, NULL, 0, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + + // Close |h[0]|. + EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h[0])); + + // Check that |h[1]| is no longer writable (and will never be). + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + core()->Wait(h[1], MOJO_WAIT_FLAG_WRITABLE, 1000000000)); + + // Check that |h[1]| is still readable (for the moment). + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(h[1], MOJO_WAIT_FLAG_READABLE, 1000000000)); + + // Discard a message from |h[1]|. + EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, + core()->ReadMessage(h[1], NULL, NULL, NULL, NULL, + MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)); + + // |h[1]| is no longer readable (and will never be). + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + core()->Wait(h[1], MOJO_WAIT_FLAG_READABLE, 1000000000)); + + // Try writing to |h[1]|. + buffer[0] = 'e'; + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + core()->WriteMessage(h[1], buffer, 1, NULL, 0, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + + EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h[1])); +} + +// Tests passing a message pipe handle. +TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing1) { + const char kHello[] = "hello"; + const uint32_t kHelloSize = static_cast(sizeof(kHello)); + const char kWorld[] = "world!!!"; + const uint32_t kWorldSize = static_cast(sizeof(kWorld)); + char buffer[100]; + const uint32_t kBufferSize = static_cast(sizeof(buffer)); + uint32_t num_bytes; + MojoHandle handles[10]; + uint32_t num_handles; + MojoHandle h_received; + + MojoHandle h_passing[2]; + EXPECT_EQ(MOJO_RESULT_OK, + core()->CreateMessagePipe(&h_passing[0], &h_passing[1])); + + // Make sure that |h_passing[]| work properly. + EXPECT_EQ(MOJO_RESULT_OK, + core()->WriteMessage(h_passing[0], + kHello, kHelloSize, + NULL, 0, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(h_passing[1], MOJO_WAIT_FLAG_READABLE, 1000000000)); + num_bytes = kBufferSize; + num_handles = arraysize(handles); + EXPECT_EQ(MOJO_RESULT_OK, + core()->ReadMessage(h_passing[1], + buffer, &num_bytes, + handles, &num_handles, + MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(kHelloSize, num_bytes); + EXPECT_STREQ(kHello, buffer); + EXPECT_EQ(0u, num_handles); + + // Make sure that you can't pass either of the message pipe's handles over + // itself. + EXPECT_EQ(MOJO_RESULT_BUSY, + core()->WriteMessage(h_passing[0], + kHello, kHelloSize, + &h_passing[0], 1, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->WriteMessage(h_passing[0], + kHello, kHelloSize, + &h_passing[1], 1, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + + MojoHandle h_passed[2]; + EXPECT_EQ(MOJO_RESULT_OK, + core()->CreateMessagePipe(&h_passed[0], &h_passed[1])); + + // Make sure that |h_passed[]| work properly. + EXPECT_EQ(MOJO_RESULT_OK, + core()->WriteMessage(h_passed[0], + kHello, kHelloSize, + NULL, 0, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(h_passed[1], MOJO_WAIT_FLAG_READABLE, 1000000000)); + num_bytes = kBufferSize; + num_handles = arraysize(handles); + EXPECT_EQ(MOJO_RESULT_OK, + core()->ReadMessage(h_passed[1], + buffer, &num_bytes, + handles, &num_handles, + MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(kHelloSize, num_bytes); + EXPECT_STREQ(kHello, buffer); + EXPECT_EQ(0u, num_handles); + + // Send |h_passed[1]| from |h_passing[0]| to |h_passing[1]|. + EXPECT_EQ(MOJO_RESULT_OK, + core()->WriteMessage(h_passing[0], + kWorld, kWorldSize, + &h_passed[1], 1, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(h_passing[1], MOJO_WAIT_FLAG_READABLE, 1000000000)); + num_bytes = kBufferSize; + num_handles = arraysize(handles); + EXPECT_EQ(MOJO_RESULT_OK, + core()->ReadMessage(h_passing[1], + buffer, &num_bytes, + handles, &num_handles, + MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(kWorldSize, num_bytes); + EXPECT_STREQ(kWorld, buffer); + EXPECT_EQ(1u, num_handles); + h_received = handles[0]; + EXPECT_NE(h_received, MOJO_HANDLE_INVALID); + EXPECT_NE(h_received, h_passing[0]); + EXPECT_NE(h_received, h_passing[1]); + EXPECT_NE(h_received, h_passed[0]); + + // Note: We rely on the Mojo system not re-using handle values very often. + EXPECT_NE(h_received, h_passed[1]); + + // |h_passed[1]| should no longer be valid; check that trying to close it + // fails. See above note. + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(h_passed[1])); + + // Write to |h_passed[0]|. Should receive on |h_received|. + EXPECT_EQ(MOJO_RESULT_OK, + core()->WriteMessage(h_passed[0], + kHello, kHelloSize, + NULL, 0, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(h_received, MOJO_WAIT_FLAG_READABLE, 1000000000)); + num_bytes = kBufferSize; + num_handles = arraysize(handles); + EXPECT_EQ(MOJO_RESULT_OK, + core()->ReadMessage(h_received, + buffer, &num_bytes, + handles, &num_handles, + MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(kHelloSize, num_bytes); + EXPECT_STREQ(kHello, buffer); + EXPECT_EQ(0u, num_handles); + + EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[0])); + EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[1])); + EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passed[0])); + EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_received)); +} + +TEST_F(CoreTest, DataPipe) { + MojoHandle ph, ch; // p is for producer and c is for consumer. + + EXPECT_EQ(MOJO_RESULT_OK, core()->CreateDataPipe(NULL, &ph, &ch)); + // Should get two distinct, valid handles. + EXPECT_NE(ph, MOJO_HANDLE_INVALID); + EXPECT_NE(ch, MOJO_HANDLE_INVALID); + EXPECT_NE(ph, ch); + + // Producer should be never-readable, but already writable. + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + core()->Wait(ph, MOJO_WAIT_FLAG_READABLE, 0)); + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(ph, MOJO_WAIT_FLAG_WRITABLE, 0)); + + // Consumer should be never-writable, and not yet readable. + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + core()->Wait(ch, MOJO_WAIT_FLAG_WRITABLE, 0)); + EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, + core()->Wait(ch, MOJO_WAIT_FLAG_READABLE, 0)); + + // Write. + char elements[2] = { 'A', 'B' }; + uint32_t num_bytes = 2u; + EXPECT_EQ(MOJO_RESULT_OK, + core()->WriteData(ph, elements, &num_bytes, + MOJO_WRITE_DATA_FLAG_NONE)); + EXPECT_EQ(2u, num_bytes); + + // Consumer should now be readable. + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(ch, MOJO_WAIT_FLAG_READABLE, 0)); + + // Read one character. + elements[0] = -1; + elements[1] = -1; + num_bytes = 1u; + EXPECT_EQ(MOJO_RESULT_OK, + core()->ReadData(ch, elements, &num_bytes, + MOJO_READ_DATA_FLAG_NONE)); + EXPECT_EQ('A', elements[0]); + EXPECT_EQ(-1, elements[1]); + + // Two-phase write. + void* write_ptr = NULL; + num_bytes = 0u; + ASSERT_EQ(MOJO_RESULT_OK, + core()->BeginWriteData(ph, &write_ptr, &num_bytes, + MOJO_WRITE_DATA_FLAG_NONE)); + // We count on the default options providing a decent buffer size. + ASSERT_GE(num_bytes, 3u); + + // Trying to do a normal write during a two-phase write should fail. + elements[0] = 'X'; + num_bytes = 1u; + EXPECT_EQ(MOJO_RESULT_BUSY, + core()->WriteData(ph, elements, &num_bytes, + MOJO_WRITE_DATA_FLAG_NONE)); + + // Actually write the data, and complete it now. + static_cast(write_ptr)[0] = 'C'; + static_cast(write_ptr)[1] = 'D'; + static_cast(write_ptr)[2] = 'E'; + EXPECT_EQ(MOJO_RESULT_OK, core()->EndWriteData(ph, 3u)); + + // Query how much data we have. + num_bytes = 0; + EXPECT_EQ(MOJO_RESULT_OK, + core()->ReadData(ch, NULL, &num_bytes, MOJO_READ_DATA_FLAG_QUERY)); + EXPECT_EQ(4u, num_bytes); + + // Try to discard ten characters, in all-or-none mode. Should fail. + num_bytes = 10; + EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, + core()->ReadData(ch, NULL, &num_bytes, + MOJO_READ_DATA_FLAG_DISCARD | + MOJO_READ_DATA_FLAG_ALL_OR_NONE)); + + // Discard two characters. + num_bytes = 2; + EXPECT_EQ(MOJO_RESULT_OK, + core()->ReadData(ch, NULL, &num_bytes, + MOJO_READ_DATA_FLAG_DISCARD | + MOJO_READ_DATA_FLAG_ALL_OR_NONE)); + + // Read the remaining two characters, in two-phase mode (all-or-none). + const void* read_ptr = NULL; + num_bytes = 2; + ASSERT_EQ(MOJO_RESULT_OK, + core()->BeginReadData(ch, &read_ptr, &num_bytes, + MOJO_READ_DATA_FLAG_ALL_OR_NONE)); + // Note: Count on still being able to do the contiguous read here. + ASSERT_EQ(2u, num_bytes); + + // Discarding right now should fail. + num_bytes = 1; + EXPECT_EQ(MOJO_RESULT_BUSY, + core()->ReadData(ch, NULL, &num_bytes, + MOJO_READ_DATA_FLAG_DISCARD)); + + // Actually check our data and end the two-phase read. + EXPECT_EQ('D', static_cast(read_ptr)[0]); + EXPECT_EQ('E', static_cast(read_ptr)[1]); + EXPECT_EQ(MOJO_RESULT_OK, core()->EndReadData(ch, 2u)); + + // Consumer should now be no longer readable. + EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, + core()->Wait(ch, MOJO_WAIT_FLAG_READABLE, 0)); + + // TODO(vtl): More. + + // Close the producer. + EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ph)); + + // The consumer should now be never-readable. + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + core()->Wait(ch, MOJO_WAIT_FLAG_READABLE, 0)); + + EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ch)); +} + +// Tests passing data pipe producer and consumer handles. +TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing2) { + const char kHello[] = "hello"; + const uint32_t kHelloSize = static_cast(sizeof(kHello)); + const char kWorld[] = "world!!!"; + const uint32_t kWorldSize = static_cast(sizeof(kWorld)); + char buffer[100]; + const uint32_t kBufferSize = static_cast(sizeof(buffer)); + uint32_t num_bytes; + MojoHandle handles[10]; + uint32_t num_handles; + + MojoHandle h_passing[2]; + EXPECT_EQ(MOJO_RESULT_OK, + core()->CreateMessagePipe(&h_passing[0], &h_passing[1])); + + MojoHandle ph, ch; + EXPECT_EQ(MOJO_RESULT_OK, + core()->CreateDataPipe(NULL, &ph, &ch)); + + // Send |ch| from |h_passing[0]| to |h_passing[1]|. + EXPECT_EQ(MOJO_RESULT_OK, + core()->WriteMessage(h_passing[0], + kHello, kHelloSize, + &ch, 1, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(h_passing[1], MOJO_WAIT_FLAG_READABLE, 1000000000)); + num_bytes = kBufferSize; + num_handles = arraysize(handles); + EXPECT_EQ(MOJO_RESULT_OK, + core()->ReadMessage(h_passing[1], + buffer, &num_bytes, + handles, &num_handles, + MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(kHelloSize, num_bytes); + EXPECT_STREQ(kHello, buffer); + EXPECT_EQ(1u, num_handles); + MojoHandle ch_received = handles[0]; + EXPECT_NE(ch_received, MOJO_HANDLE_INVALID); + EXPECT_NE(ch_received, h_passing[0]); + EXPECT_NE(ch_received, h_passing[1]); + EXPECT_NE(ch_received, ph); + + // Note: We rely on the Mojo system not re-using handle values very often. + EXPECT_NE(ch_received, ch); + + // |ch| should no longer be valid; check that trying to close it fails. See + // above note. + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(ch)); + + // Write to |ph|. Should receive on |ch_received|. + num_bytes = kWorldSize; + EXPECT_EQ(MOJO_RESULT_OK, + core()->WriteData(ph, kWorld, &num_bytes, + MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)); + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(ch_received, MOJO_WAIT_FLAG_READABLE, 1000000000)); + num_bytes = kBufferSize; + EXPECT_EQ(MOJO_RESULT_OK, + core()->ReadData(ch_received, buffer, &num_bytes, + MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(kWorldSize, num_bytes); + EXPECT_STREQ(kWorld, buffer); + + // Now pass |ph| in the same direction. + EXPECT_EQ(MOJO_RESULT_OK, + core()->WriteMessage(h_passing[0], + kWorld, kWorldSize, + &ph, 1, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(h_passing[1], MOJO_WAIT_FLAG_READABLE, 1000000000)); + num_bytes = kBufferSize; + num_handles = arraysize(handles); + EXPECT_EQ(MOJO_RESULT_OK, + core()->ReadMessage(h_passing[1], + buffer, &num_bytes, + handles, &num_handles, + MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(kWorldSize, num_bytes); + EXPECT_STREQ(kWorld, buffer); + EXPECT_EQ(1u, num_handles); + MojoHandle ph_received = handles[0]; + EXPECT_NE(ph_received, MOJO_HANDLE_INVALID); + EXPECT_NE(ph_received, h_passing[0]); + EXPECT_NE(ph_received, h_passing[1]); + EXPECT_NE(ph_received, ch_received); + + // Again, rely on the Mojo system not re-using handle values very often. + EXPECT_NE(ph_received, ph); + + // |ph| should no longer be valid; check that trying to close it fails. See + // above note. + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(ph)); + + // Write to |ph_received|. Should receive on |ch_received|. + num_bytes = kHelloSize; + EXPECT_EQ(MOJO_RESULT_OK, + core()->WriteData(ph_received, kHello, &num_bytes, + MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)); + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(ch_received, MOJO_WAIT_FLAG_READABLE, 1000000000)); + num_bytes = kBufferSize; + EXPECT_EQ(MOJO_RESULT_OK, + core()->ReadData(ch_received, buffer, &num_bytes, + MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(kHelloSize, num_bytes); + EXPECT_STREQ(kHello, buffer); + + ph = ph_received; + ph_received = MOJO_HANDLE_INVALID; + ch = ch_received; + ch_received = MOJO_HANDLE_INVALID; + + // Make sure that |ph| can't be sent if it's in a two-phase write. + void* write_ptr = NULL; + num_bytes = 0; + ASSERT_EQ(MOJO_RESULT_OK, + core()->BeginWriteData(ph, &write_ptr, &num_bytes, + MOJO_WRITE_DATA_FLAG_NONE)); + ASSERT_GE(num_bytes, 1u); + EXPECT_EQ(MOJO_RESULT_BUSY, + core()->WriteMessage(h_passing[0], + kHello, kHelloSize, + &ph, 1, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + + // But |ch| can, even if |ph| is in a two-phase write. + EXPECT_EQ(MOJO_RESULT_OK, + core()->WriteMessage(h_passing[0], + kHello, kHelloSize, + &ch, 1, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + ch = MOJO_HANDLE_INVALID; + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(h_passing[1], MOJO_WAIT_FLAG_READABLE, 1000000000)); + num_bytes = kBufferSize; + num_handles = arraysize(handles); + EXPECT_EQ(MOJO_RESULT_OK, + core()->ReadMessage(h_passing[1], + buffer, &num_bytes, + handles, &num_handles, + MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(kHelloSize, num_bytes); + EXPECT_STREQ(kHello, buffer); + EXPECT_EQ(1u, num_handles); + ch = handles[0]; + EXPECT_NE(ch, MOJO_HANDLE_INVALID); + + // Complete the two-phase write. + static_cast(write_ptr)[0] = 'x'; + EXPECT_EQ(MOJO_RESULT_OK, core()->EndWriteData(ph, 1)); + + // Wait for |ch| to be readable. + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(ch, MOJO_WAIT_FLAG_READABLE, 1000000000)); + + // Make sure that |ch| can't be sent if it's in a two-phase read. + const void* read_ptr = NULL; + num_bytes = 1; + ASSERT_EQ(MOJO_RESULT_OK, + core()->BeginReadData(ch, &read_ptr, &num_bytes, + MOJO_READ_DATA_FLAG_ALL_OR_NONE)); + EXPECT_EQ(MOJO_RESULT_BUSY, + core()->WriteMessage(h_passing[0], + kHello, kHelloSize, + &ch, 1, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + + // But |ph| can, even if |ch| is in a two-phase read. + EXPECT_EQ(MOJO_RESULT_OK, + core()->WriteMessage(h_passing[0], + kWorld, kWorldSize, + &ph, 1, + MOJO_WRITE_MESSAGE_FLAG_NONE)); + ph = MOJO_HANDLE_INVALID; + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(h_passing[1], MOJO_WAIT_FLAG_READABLE, 1000000000)); + num_bytes = kBufferSize; + num_handles = arraysize(handles); + EXPECT_EQ(MOJO_RESULT_OK, + core()->ReadMessage(h_passing[1], + buffer, &num_bytes, + handles, &num_handles, + MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ(kWorldSize, num_bytes); + EXPECT_STREQ(kWorld, buffer); + EXPECT_EQ(1u, num_handles); + ph = handles[0]; + EXPECT_NE(ph, MOJO_HANDLE_INVALID); + + // Complete the two-phase read. + EXPECT_EQ('x', static_cast(read_ptr)[0]); + EXPECT_EQ(MOJO_RESULT_OK, core()->EndReadData(ch, 1)); + + EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[0])); + EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[1])); + EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ph)); + EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ch)); +} + +// TODO(vtl): Test |DuplicateBufferHandle()| and |MapBuffer()|. + +} // namespace +} // namespace system +} // namespace mojo diff --git a/mojo/system/data_pipe.h b/mojo/system/data_pipe.h index 8d6fcaf..9abd81e 100644 --- a/mojo/system/data_pipe.h +++ b/mojo/system/data_pipe.h @@ -19,7 +19,7 @@ class Waiter; class WaiterList; // |DataPipe| is a base class for secondary objects implementing data pipes, -// similar to |MessagePipe| (see the explanatory comment in core_impl.cc). It is +// similar to |MessagePipe| (see the explanatory comment in core.cc). It is // typically owned by the dispatcher(s) corresponding to the local endpoints. // Its subclasses implement the three cases: local producer and consumer, local // producer and remote consumer, and remote producer and local consumer. This diff --git a/mojo/system/dispatcher.h b/mojo/system/dispatcher.h index 5bb6b3e..504e33a 100644 --- a/mojo/system/dispatcher.h +++ b/mojo/system/dispatcher.h @@ -22,7 +22,7 @@ namespace mojo { namespace system { class Channel; -class CoreImpl; +class Core; class Dispatcher; class DispatcherTransport; class HandleTable; @@ -135,9 +135,9 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher : // has been called. class HandleTableAccess { private: - friend class CoreImpl; + friend class Core; friend class HandleTable; - // Tests also need this, to avoid needing |CoreImpl|. + // Tests also need this, to avoid needing |Core|. friend DispatcherTransport test::DispatcherTryStartTransport(Dispatcher*); // This must be called under the handle table lock and only if the handle diff --git a/mojo/system/entrypoints.cc b/mojo/system/entrypoints.cc new file mode 100644 index 0000000..096fd02 --- /dev/null +++ b/mojo/system/entrypoints.cc @@ -0,0 +1,150 @@ +// 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. + +#include "mojo/system/core.h" + +static mojo::system::Core* g_core = NULL; + +namespace mojo { +namespace system { +namespace entrypoints { + +void SetCore(Core* core) { + g_core = core; +} + +Core* GetCore() { + return g_core; +} + +} // namespace entrypoints +} // namepace system +} // namespace mojo + +// Definitions of the system functions. +extern "C" { + +MojoTimeTicks MojoGetTimeTicksNow() { + return g_core->GetTimeTicksNow(); +} + +MojoResult MojoClose(MojoHandle handle) { + return g_core->Close(handle); +} + +MojoResult MojoWait(MojoHandle handle, + MojoWaitFlags flags, + MojoDeadline deadline) { + return g_core->Wait(handle, flags, deadline); +} + +MojoResult MojoWaitMany(const MojoHandle* handles, + const MojoWaitFlags* flags, + uint32_t num_handles, + MojoDeadline deadline) { + return g_core->WaitMany(handles, flags, num_handles, deadline); +} + +MojoResult MojoCreateMessagePipe(MojoHandle* message_pipe_handle0, + MojoHandle* message_pipe_handle1) { + return g_core->CreateMessagePipe(message_pipe_handle0, message_pipe_handle1); +} + +MojoResult MojoWriteMessage(MojoHandle message_pipe_handle, + const void* bytes, + uint32_t num_bytes, + const MojoHandle* handles, + uint32_t num_handles, + MojoWriteMessageFlags flags) { + return g_core->WriteMessage( + message_pipe_handle, bytes, num_bytes, handles, num_handles, flags); +} + +MojoResult MojoReadMessage(MojoHandle message_pipe_handle, + void* bytes, + uint32_t* num_bytes, + MojoHandle* handles, + uint32_t* num_handles, + MojoReadMessageFlags flags) { + return g_core->ReadMessage( + message_pipe_handle, bytes, num_bytes, handles, num_handles, flags); +} + +MojoResult MojoCreateDataPipe(const MojoCreateDataPipeOptions* options, + MojoHandle* data_pipe_producer_handle, + MojoHandle* data_pipe_consumer_handle) { + return g_core->CreateDataPipe( + options, data_pipe_producer_handle, data_pipe_consumer_handle); +} + +MojoResult MojoWriteData(MojoHandle data_pipe_producer_handle, + const void* elements, + uint32_t* num_elements, + MojoWriteDataFlags flags) { + return g_core->WriteData( + data_pipe_producer_handle, elements, num_elements, flags); +} + +MojoResult MojoBeginWriteData(MojoHandle data_pipe_producer_handle, + void** buffer, + uint32_t* buffer_num_elements, + MojoWriteDataFlags flags) { + return g_core->BeginWriteData( + data_pipe_producer_handle, buffer, buffer_num_elements, flags); +} + +MojoResult MojoEndWriteData(MojoHandle data_pipe_producer_handle, + uint32_t num_elements_written) { + return g_core->EndWriteData(data_pipe_producer_handle, num_elements_written); +} + +MojoResult MojoReadData(MojoHandle data_pipe_consumer_handle, + void* elements, + uint32_t* num_elements, + MojoReadDataFlags flags) { + return g_core->ReadData( + data_pipe_consumer_handle, elements, num_elements, flags); +} + +MojoResult MojoBeginReadData(MojoHandle data_pipe_consumer_handle, + const void** buffer, + uint32_t* buffer_num_elements, + MojoReadDataFlags flags) { + return g_core->BeginReadData( + data_pipe_consumer_handle, buffer, buffer_num_elements, flags); +} + +MojoResult MojoEndReadData(MojoHandle data_pipe_consumer_handle, + uint32_t num_elements_read) { + return g_core->EndReadData(data_pipe_consumer_handle, num_elements_read); +} + +MojoResult MojoCreateSharedBuffer( + const struct MojoCreateSharedBufferOptions* options, + uint64_t num_bytes, + MojoHandle* shared_buffer_handle) { + return g_core->CreateSharedBuffer(options, num_bytes, shared_buffer_handle); +} + +MojoResult MojoDuplicateBufferHandle( + MojoHandle buffer_handle, + const struct MojoDuplicateBufferHandleOptions* options, + MojoHandle* new_buffer_handle) { + return g_core->DuplicateBufferHandle( + buffer_handle, options, new_buffer_handle); +} + +MojoResult MojoMapBuffer(MojoHandle buffer_handle, + uint64_t offset, + uint64_t num_bytes, + void** buffer, + MojoMapBufferFlags flags) { + return g_core->MapBuffer(buffer_handle, offset, num_bytes, buffer, flags); +} + +MojoResult MojoUnmapBuffer(void* buffer) { + return g_core->UnmapBuffer(buffer); +} + +} // extern "C" diff --git a/mojo/system/entrypoints.h b/mojo/system/entrypoints.h new file mode 100644 index 0000000..65a0363 --- /dev/null +++ b/mojo/system/entrypoints.h @@ -0,0 +1,24 @@ +// 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_SYSTEM_ENTRYPOINTS_H +#define MOJO_SYSTEM_ENTRYPOINTS_H + +namespace mojo { +namespace system { + +class Core; + +namespace entrypoints { + +// Sets the instance of Core to be used by system functions. +void SetCore(Core* core); +// Gets the instance of Core to be used by system functions. +Core* GetCore(); + +} // namespace entrypoints +} // namepace system +} // namespace mojo + +#endif // MOJO_SYSTEM_ENTRYPOINTS_H diff --git a/mojo/system/handle_table.cc b/mojo/system/handle_table.cc index 798b610..b863e81 100644 --- a/mojo/system/handle_table.cc +++ b/mojo/system/handle_table.cc @@ -31,7 +31,7 @@ HandleTable::HandleTable() HandleTable::~HandleTable() { // This should usually not be reached (the only instance should be owned by - // the singleton |CoreImpl|, which lives forever), except in tests. + // the singleton |Core|, which lives forever), except in tests. } Dispatcher* HandleTable::GetDispatcher(MojoHandle handle) { diff --git a/mojo/system/handle_table.h b/mojo/system/handle_table.h index 4bcf02c..408fae2 100644 --- a/mojo/system/handle_table.h +++ b/mojo/system/handle_table.h @@ -17,23 +17,23 @@ namespace mojo { namespace system { -class CoreImpl; +class Core; class Dispatcher; class DispatcherTransport; // Test-only function (defined/used in embedder/test_embedder.cc). Declared here // so it can be friended. namespace internal { -bool ShutdownCheckNoLeaks(CoreImpl*); +bool ShutdownCheckNoLeaks(Core*); } -// This class provides the (global) handle table (owned by |CoreImpl|), which -// maps (valid) |MojoHandle|s to |Dispatcher|s. This is abstracted so that, -// e.g., caching may be added. +// This class provides the (global) handle table (owned by |Core|), which maps +// (valid) |MojoHandle|s to |Dispatcher|s. This is abstracted so that, e.g., +// caching may be added. // -// This class is NOT thread-safe; locking is left to |CoreImpl| (since it may -// need to make several changes -- "atomically" or in rapid successsion, in -// which case the extra locking/unlocking would be unnecessary overhead). +// This class is NOT thread-safe; locking is left to |Core| (since it may need +// to make several changes -- "atomically" or in rapid successsion, in which +// case the extra locking/unlocking would be unnecessary overhead). class MOJO_SYSTEM_IMPL_EXPORT HandleTable { public: @@ -44,8 +44,8 @@ class MOJO_SYSTEM_IMPL_EXPORT HandleTable { // |MOJO_HANDLE_INVALID|). Returns null if there's no dispatcher for the given // handle. // WARNING: For efficiency, this returns a dumb pointer. If you're going to - // use the result outside |CoreImpl|'s lock, you MUST take a reference (e.g., - // by storing the result inside a |scoped_refptr|). + // use the result outside |Core|'s lock, you MUST take a reference (e.g., by + // storing the result inside a |scoped_refptr|). Dispatcher* GetDispatcher(MojoHandle handle); // On success, gets the dispatcher for a given handle (which should not be @@ -98,7 +98,7 @@ class MOJO_SYSTEM_IMPL_EXPORT HandleTable { void RestoreBusyHandles(const MojoHandle* handles, uint32_t num_handles); private: - friend bool internal::ShutdownCheckNoLeaks(CoreImpl*); + friend bool internal::ShutdownCheckNoLeaks(Core*); struct Entry { Entry(); diff --git a/mojo/system/mapping_table.cc b/mojo/system/mapping_table.cc index 39b79bc..a6e5bb3 100644 --- a/mojo/system/mapping_table.cc +++ b/mojo/system/mapping_table.cc @@ -16,7 +16,7 @@ MappingTable::MappingTable() { MappingTable::~MappingTable() { // This should usually not be reached (the only instance should be owned by - // the singleton |CoreImpl|, which lives forever), except in tests. + // the singleton |Core|, which lives forever), except in tests. } MojoResult MappingTable::AddMapping( diff --git a/mojo/system/mapping_table.h b/mojo/system/mapping_table.h index bbae097..1b14890 100644 --- a/mojo/system/mapping_table.h +++ b/mojo/system/mapping_table.h @@ -18,20 +18,19 @@ namespace mojo { namespace system { -class CoreImpl; +class Core; class RawSharedBufferMapping; // Test-only function (defined/used in embedder/test_embedder.cc). Declared here // so it can be friended. namespace internal { -bool ShutdownCheckNoLeaks(CoreImpl*); +bool ShutdownCheckNoLeaks(Core*); } -// This class provides the (global) table of memory mappings (owned by -// |CoreImpl|), which maps mapping base addresses to -// |RawSharedBuffer::Mapping|s. +// This class provides the (global) table of memory mappings (owned by |Core|), +// which maps mapping base addresses to |RawSharedBuffer::Mapping|s. // -// This class is NOT thread-safe; locking is left to |CoreImpl|. +// This class is NOT thread-safe; locking is left to |Core|. class MOJO_SYSTEM_IMPL_EXPORT MappingTable { public: MappingTable(); @@ -43,7 +42,7 @@ class MOJO_SYSTEM_IMPL_EXPORT MappingTable { MojoResult RemoveMapping(void* address); private: - friend bool internal::ShutdownCheckNoLeaks(CoreImpl*); + friend bool internal::ShutdownCheckNoLeaks(Core*); typedef base::hash_map AddressToMappingMap; diff --git a/mojo/system/message_pipe.cc b/mojo/system/message_pipe.cc index e450569..6f2e5cb 100644 --- a/mojo/system/message_pipe.cc +++ b/mojo/system/message_pipe.cc @@ -181,7 +181,7 @@ MojoResult MessagePipe::EnqueueMessage( // You're not allowed to send either handle to a message pipe over the // message pipe, so check for this. (The case of trying to write a handle to - // itself is taken care of by |CoreImpl|. That case kind of makes sense, but + // itself is taken care of by |Core|. That case kind of makes sense, but // leads to complications if, e.g., both sides try to do the same thing with // their respective handles simultaneously. The other case, of trying to // write the peer handle to a handle, doesn't make sense -- since no handle @@ -192,8 +192,8 @@ MojoResult MessagePipe::EnqueueMessage( if ((*transports)[i].GetType() == Dispatcher::kTypeMessagePipe) { MessagePipeDispatcherTransport mp_transport((*transports)[i]); if (mp_transport.GetMessagePipe() == this) { - // The other case should have been disallowed by |CoreImpl|. (Note: - // |port| is the peer port of the handle given to |WriteMessage()|.) + // The other case should have been disallowed by |Core|. (Note: |port| + // is the peer port of the handle given to |WriteMessage()|.) DCHECK_EQ(mp_transport.GetPort(), port); return MOJO_RESULT_INVALID_ARGUMENT; } diff --git a/mojo/system/message_pipe.h b/mojo/system/message_pipe.h index de42c44..85cce0a 100644 --- a/mojo/system/message_pipe.h +++ b/mojo/system/message_pipe.h @@ -24,9 +24,8 @@ class Channel; class Waiter; // |MessagePipe| is the secondary object implementing a message pipe (see the -// explanatory comment in core_impl.cc). It is typically owned by the -// dispatcher(s) corresponding to the local endpoints. This class is -// thread-safe. +// explanatory comment in core.cc). It is typically owned by the dispatcher(s) +// corresponding to the local endpoints. This class is thread-safe. class MOJO_SYSTEM_IMPL_EXPORT MessagePipe : public base::RefCountedThreadSafe { public: diff --git a/mojo/system/simple_dispatcher.h b/mojo/system/simple_dispatcher.h index d2934fa..15a64d5 100644 --- a/mojo/system/simple_dispatcher.h +++ b/mojo/system/simple_dispatcher.h @@ -17,8 +17,8 @@ namespace system { // A base class for simple dispatchers. "Simple" means that there's a one-to-one // correspondence between handles and dispatchers (see the explanatory comment -// in core_impl.cc). This class implements the standard waiter-signalling -// mechanism in that case. +// in core.cc). This class implements the standard waiter-signalling mechanism +// in that case. class MOJO_SYSTEM_IMPL_EXPORT SimpleDispatcher : public Dispatcher { protected: SimpleDispatcher(); diff --git a/mojo/system/waiter_list.h b/mojo/system/waiter_list.h index 1d35b94..f6a69ed 100644 --- a/mojo/system/waiter_list.h +++ b/mojo/system/waiter_list.h @@ -20,8 +20,8 @@ class Waiter; // handle/|Dispatcher|. There should be a |WaiterList| for each handle that can // be waited on (in any way). In the simple case, the |WaiterList| is owned by // the |Dispatcher|, whereas in more complex cases it is owned by the secondary -// object (see simple_dispatcher.* and the explanatory comment in core_impl.cc). -// This class is thread-unsafe (all concurrent access must be protected by some +// object (see simple_dispatcher.* and the explanatory comment in core.cc). This +// class is thread-unsafe (all concurrent access must be protected by some // lock). class MOJO_SYSTEM_IMPL_EXPORT WaiterList { public: -- cgit v1.1