diff options
Diffstat (limited to 'mojo/edk')
184 files changed, 0 insertions, 31524 deletions
diff --git a/mojo/edk/DEPS b/mojo/edk/DEPS deleted file mode 100644 index 9a51b60..0000000 --- a/mojo/edk/DEPS +++ /dev/null @@ -1,7 +0,0 @@ -include_rules = [ - "-mojo", - "+mojo/edk", - "+mojo/public", - - "+third_party/ashmem", -] diff --git a/mojo/edk/embedder/BUILD.gn b/mojo/edk/embedder/BUILD.gn deleted file mode 100644 index d9a6e69..0000000 --- a/mojo/edk/embedder/BUILD.gn +++ /dev/null @@ -1,114 +0,0 @@ -# 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. - -import("../mojo_edk.gni") - -mojo_edk_source_set("embedder") { - # This isn't really a standalone target; it must be linked into the - # mojo_system_impl component. - mojo_edk_visibility = [ "mojo/edk/system" ] - - sources = [ - "channel_info_forward.h", - "channel_init.cc", - "channel_init.h", - "configuration.h", - "embedder.cc", - "embedder.h", - "embedder_internal.h", - "entrypoints.cc", - - # Test-only code: - # TODO(vtl): It's a little unfortunate that these end up in the same - # component as non-test-only code. In the static build, this code should - # hopefully be dead-stripped. - "test_embedder.cc", - "test_embedder.h", - ] - - defines = [ - "MOJO_SYSTEM_IMPL_IMPLEMENTATION", - "MOJO_SYSTEM_IMPLEMENTATION", - ] - - mojo_edk_configs = [ "mojo/edk/system:system_config" ] - - public_deps = [ - ":platform", - ] - - mojo_sdk_public_deps = [ "mojo/public/cpp/system" ] - - deps = [ - "//base", - ] -} - -mojo_edk_source_set("platform") { - # This isn't really a standalone target; it must be linked into the - # mojo_system_impl component. - visibility = [ ":embedder" ] - - mojo_edk_visibility = [ "mojo/edk/system" ] - - sources = [ - "platform_channel_pair.cc", - "platform_channel_pair.h", - "platform_channel_pair_posix.cc", - "platform_channel_pair_win.cc", - "platform_channel_utils_posix.cc", - "platform_channel_utils_posix.h", - "platform_handle.cc", - "platform_handle.h", - "platform_handle_utils.h", - "platform_handle_utils_posix.cc", - "platform_handle_utils_win.cc", - "platform_handle_vector.h", - "platform_shared_buffer.h", - "platform_support.h", - "scoped_platform_handle.h", - "simple_platform_shared_buffer.cc", - "simple_platform_shared_buffer.h", - "simple_platform_shared_buffer_android.cc", - "simple_platform_shared_buffer_posix.cc", - "simple_platform_shared_buffer_win.cc", - "simple_platform_support.cc", - "simple_platform_support.h", - ] - - defines = [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ] - - mojo_edk_configs = [ "mojo/edk/system:system_config" ] - - deps = [ - "//base", - ] - - if (is_android) { - deps += [ "//third_party/ashmem" ] - } -} - -mojo_edk_source_set("embedder_unittests") { - testonly = true - mojo_edk_visibility = [ "mojo/edk/system:mojo_system_unittests" ] - - sources = [ - "embedder_unittest.cc", - "platform_channel_pair_posix_unittest.cc", - "simple_platform_shared_buffer_unittest.cc", - ] - - deps = [ - "//base", - "//base/test:test_support", - "//testing/gtest", - ] - - mojo_edk_deps = [ - "mojo/edk/test:test_support", - "mojo/edk/system", - "mojo/edk/system:test_utils", - ] -} diff --git a/mojo/edk/embedder/DEPS b/mojo/edk/embedder/DEPS deleted file mode 100644 index c3a0d22..0000000 --- a/mojo/edk/embedder/DEPS +++ /dev/null @@ -1,11 +0,0 @@ -include_rules = [ - "+mojo/edk/system/system_impl_export.h", -] - -specific_include_rules = { - # Implementation files may freely access mojo/edk/system, but we don't want to - # leak implementation details through the headers. - ".*\.cc": [ - "+mojo/edk/system", - ] -} diff --git a/mojo/edk/embedder/README.md b/mojo/edk/embedder/README.md deleted file mode 100644 index f976fcb..0000000 --- a/mojo/edk/embedder/README.md +++ /dev/null @@ -1,13 +0,0 @@ -Mojo Embedder API -================= - -The Mojo Embedder API is an unstable, internal API to the Mojo system -implementation. It should be used by code running on top of the system-level -APIs to set up the Mojo environment (instead of directly instantiating things -from src/mojo/edk/system). - -Example uses: Mojo shell, to set up the Mojo environment for Mojo apps; Chromium -code, to set up the Mojo IPC system for use between processes. Note that most -code should use the Mojo Public API (under src/mojo/public) instead. The -Embedder API should only be used to initialize the environment, set up the -initial MessagePipe between two processes, etc. diff --git a/mojo/edk/embedder/channel_info_forward.h b/mojo/edk/embedder/channel_info_forward.h deleted file mode 100644 index 8d23836..0000000 --- a/mojo/edk/embedder/channel_info_forward.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file simply (forward) declares |mojo::embedder::ChannelInfo|, which is -// meant to be opaque to users of the embedder API. - -#ifndef MOJO_EDK_EMBEDDER_CHANNEL_INFO_FORWARD_H_ -#define MOJO_EDK_EMBEDDER_CHANNEL_INFO_FORWARD_H_ - -namespace mojo { -namespace embedder { - -// This is an opaque type. The embedder API uses (returns and takes as -// arguments) pointers to this type. (We don't simply use |void*|, so that -// custom deleters and such can be used without additional wrappers. -struct ChannelInfo; - -} // namespace embedder -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_CHANNEL_INFO_FORWARD_H_ diff --git a/mojo/edk/embedder/channel_init.cc b/mojo/edk/embedder/channel_init.cc deleted file mode 100644 index 9a0bfce..0000000 --- a/mojo/edk/embedder/channel_init.cc +++ /dev/null @@ -1,55 +0,0 @@ -// 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/edk/embedder/channel_init.h" - -#include "base/bind.h" -#include "base/message_loop/message_loop.h" -#include "mojo/edk/embedder/embedder.h" - -namespace mojo { -namespace embedder { - -ChannelInit::ChannelInit() : channel_info_(nullptr), weak_factory_(this) { -} - -ChannelInit::~ChannelInit() { - if (channel_info_) - DestroyChannel(channel_info_); -} - -ScopedMessagePipeHandle ChannelInit::Init( - base::PlatformFile file, - scoped_refptr<base::TaskRunner> io_thread_task_runner) { - DCHECK(!io_thread_task_runner_); // Should only init once. - io_thread_task_runner_ = io_thread_task_runner; - ScopedMessagePipeHandle message_pipe = - CreateChannel( - ScopedPlatformHandle(PlatformHandle(file)), io_thread_task_runner, - base::Bind(&ChannelInit::OnCreatedChannel, weak_factory_.GetWeakPtr(), - io_thread_task_runner), - base::MessageLoop::current()->message_loop_proxy()).Pass(); - return message_pipe.Pass(); -} - -void ChannelInit::WillDestroySoon() { - if (channel_info_) - WillDestroyChannelSoon(channel_info_); -} - -// static -void ChannelInit::OnCreatedChannel(base::WeakPtr<ChannelInit> self, - scoped_refptr<base::TaskRunner> io_thread, - ChannelInfo* channel) { - // If |self| was already destroyed, shut the channel down. - if (!self) { - DestroyChannel(channel); - return; - } - - self->channel_info_ = channel; -} - -} // namespace embedder -} // namespace mojo diff --git a/mojo/edk/embedder/channel_init.h b/mojo/edk/embedder/channel_init.h deleted file mode 100644 index 59b6694..0000000 --- a/mojo/edk/embedder/channel_init.h +++ /dev/null @@ -1,60 +0,0 @@ -// 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_EDK_EMBEDDER_CHANNEL_INIT_H_ -#define MOJO_EDK_EMBEDDER_CHANNEL_INIT_H_ - -#include "base/files/file.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "mojo/edk/embedder/channel_info_forward.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/cpp/system/message_pipe.h" - -namespace base { -class MessageLoopProxy; -class TaskRunner; -} - -namespace mojo { -namespace embedder { - -// |ChannelInit| handles creation (and destruction) of the Mojo channel. It is -// not thread-safe, but may be used on any single thread (with a |MessageLoop|). -class MOJO_SYSTEM_IMPL_EXPORT ChannelInit { - public: - ChannelInit(); - ~ChannelInit(); - - // Initializes the channel. This takes ownership of |file|. Returns the - // primordial MessagePipe for the channel. - mojo::ScopedMessagePipeHandle Init( - base::PlatformFile file, - scoped_refptr<base::TaskRunner> io_thread_task_runner); - - // Notifies the channel that we (hence it) will soon be destroyed. - void WillDestroySoon(); - - private: - // Invoked on the thread on which this object lives once the channel has been - // established. (This is a static method that takes a weak pointer to self, - // since we want to destroy the channel even if we're destroyed.) - static void OnCreatedChannel(base::WeakPtr<ChannelInit> self, - scoped_refptr<base::TaskRunner> io_thread, - ChannelInfo* channel); - - scoped_refptr<base::TaskRunner> io_thread_task_runner_; - - // If non-null the channel has been established. - ChannelInfo* channel_info_; - - base::WeakPtrFactory<ChannelInit> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(ChannelInit); -}; - -} // namespace embedder -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_CHANNEL_INIT_H_ diff --git a/mojo/edk/embedder/configuration.h b/mojo/edk/embedder/configuration.h deleted file mode 100644 index 0f99e1f..0000000 --- a/mojo/edk/embedder/configuration.h +++ /dev/null @@ -1,68 +0,0 @@ -// 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_EDK_EMBEDDER_CONFIGURATION_H_ -#define MOJO_EDK_EMBEDDER_CONFIGURATION_H_ - -#include <stddef.h> - -namespace mojo { -namespace embedder { - -// A set of constants that the Mojo system internally uses. These values should -// be consistent across all processes on the same system. -// -// In general, there should be no need to change these values from their -// defaults. However, if you do change them, you must do so before -// initialization. -struct Configuration { - // Maximum number of open (Mojo) handles. The default is 1,000,000. - // - // TODO(vtl): This doesn't count "live" handles, some of which may live in - // messages. - size_t max_handle_table_size; - - // Maximum number of active memory mappings. The default is 1,000,000. - size_t max_mapping_table_sze; - - // Upper limit of |MojoWaitMany()|'s |num_handles|. The default is 1,000,000. - // Must be same as or smaller than |max_handle_table_size|. - size_t max_wait_many_num_handles; - - // Maximum data size of messages sent over message pipes, in bytes. The - // default is 4MB. - size_t max_message_num_bytes; - - // Maximum number of handles that can be attached to messages sent over - // message pipes. The default is 10,000. - size_t max_message_num_handles; - - // Maximum capacity of a data pipe, in bytes. The default is 256MB. This value - // must fit into a |uint32_t|. WARNING: If you bump it closer to 2^32, you - // must audit all the code to check that we don't overflow (2^31 would - // definitely be risky; up to 2^30 is probably okay). - size_t max_data_pipe_capacity_bytes; - - // Default data pipe capacity, if not specified explicitly in the creation - // options. The default is 1MB. - size_t default_data_pipe_capacity_bytes; - - // Alignment for the "start" of the data buffer used by data pipes. (The - // alignment of elements will depend on this and the element size.) The - // default is 16 bytes. - size_t data_pipe_buffer_alignment_bytes; - - // Maximum size of a single shared memory segment, in bytes. The default is - // 1GB. - // - // TODO(vtl): Set this hard limit appropriately (e.g., higher on 64-bit). - // (This will also entail some auditing to make sure I'm not messing up my - // checks anywhere.) - size_t max_shared_memory_num_bytes; -}; - -} // namespace embedder -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_CONFIGURATION_H_ diff --git a/mojo/edk/embedder/embedder.cc b/mojo/edk/embedder/embedder.cc deleted file mode 100644 index 1d12c1e..0000000 --- a/mojo/edk/embedder/embedder.cc +++ /dev/null @@ -1,207 +0,0 @@ -// 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/edk/embedder/embedder.h" - -#include "base/bind.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop_proxy.h" -#include "mojo/edk/embedder/embedder_internal.h" -#include "mojo/edk/embedder/platform_support.h" -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/channel_endpoint.h" -#include "mojo/edk/system/channel_manager.h" -#include "mojo/edk/system/configuration.h" -#include "mojo/edk/system/core.h" -#include "mojo/edk/system/message_pipe_dispatcher.h" -#include "mojo/edk/system/platform_handle_dispatcher.h" -#include "mojo/edk/system/raw_channel.h" - -namespace mojo { -namespace embedder { - -namespace { - -// Helper for |CreateChannel...()|. Returns 0 on failure. Called on the channel -// creation thread. -system::ChannelId MakeChannel( - ScopedPlatformHandle platform_handle, - scoped_refptr<system::ChannelEndpoint> channel_endpoint) { - DCHECK(platform_handle.is_valid()); - - // Create and initialize a |system::Channel|. - DCHECK(internal::g_core); - scoped_refptr<system::Channel> channel = - new system::Channel(internal::g_core->platform_support()); - channel->Init(system::RawChannel::Create(platform_handle.Pass())); - channel->SetBootstrapEndpoint(channel_endpoint); - - DCHECK(internal::g_channel_manager); - return internal::g_channel_manager->AddChannel( - channel, base::MessageLoopProxy::current()); -} - -// Helper for |CreateChannel()|. Called on the channel creation thread. -void CreateChannelHelper( - ScopedPlatformHandle platform_handle, - scoped_ptr<ChannelInfo> channel_info, - scoped_refptr<system::ChannelEndpoint> channel_endpoint, - DidCreateChannelCallback callback, - scoped_refptr<base::TaskRunner> callback_thread_task_runner) { - channel_info->channel_id = - MakeChannel(platform_handle.Pass(), channel_endpoint); - - // Hand the channel back to the embedder. - if (callback_thread_task_runner) { - callback_thread_task_runner->PostTask( - FROM_HERE, base::Bind(callback, channel_info.release())); - } else { - callback.Run(channel_info.release()); - } -} - -} // namespace - -namespace internal { - -// Declared in embedder_internal.h. -system::Core* g_core = nullptr; -system::ChannelManager* g_channel_manager = nullptr; - -} // namespace internal - -void Init(scoped_ptr<PlatformSupport> platform_support) { - DCHECK(!internal::g_core); - internal::g_core = new system::Core(platform_support.Pass()); - DCHECK(!internal::g_channel_manager); - internal::g_channel_manager = new system::ChannelManager(); -} - -Configuration* GetConfiguration() { - return system::GetMutableConfiguration(); -} - -// TODO(vtl): Write tests for this. -ScopedMessagePipeHandle CreateChannelOnIOThread( - ScopedPlatformHandle platform_handle, - ChannelInfo** channel_info) { - DCHECK(platform_handle.is_valid()); - DCHECK(channel_info); - - scoped_refptr<system::ChannelEndpoint> channel_endpoint; - scoped_refptr<system::MessagePipeDispatcher> dispatcher = - system::MessagePipeDispatcher::CreateRemoteMessagePipe(&channel_endpoint); - - DCHECK(internal::g_core); - ScopedMessagePipeHandle rv( - MessagePipeHandle(internal::g_core->AddDispatcher(dispatcher))); - - *channel_info = - new ChannelInfo(MakeChannel(platform_handle.Pass(), channel_endpoint)); - - return rv.Pass(); -} - -ScopedMessagePipeHandle CreateChannel( - ScopedPlatformHandle platform_handle, - scoped_refptr<base::TaskRunner> io_thread_task_runner, - DidCreateChannelCallback callback, - scoped_refptr<base::TaskRunner> callback_thread_task_runner) { - DCHECK(platform_handle.is_valid()); - DCHECK(io_thread_task_runner); - DCHECK(!callback.is_null()); - - scoped_refptr<system::ChannelEndpoint> channel_endpoint; - scoped_refptr<system::MessagePipeDispatcher> dispatcher = - system::MessagePipeDispatcher::CreateRemoteMessagePipe(&channel_endpoint); - - DCHECK(internal::g_core); - ScopedMessagePipeHandle rv( - MessagePipeHandle(internal::g_core->AddDispatcher(dispatcher))); - - // We'll have to set |channel_info->channel_id| on the I/O thread. - scoped_ptr<ChannelInfo> channel_info(new ChannelInfo()); - - if (rv.is_valid()) { - io_thread_task_runner->PostTask( - FROM_HERE, - base::Bind(&CreateChannelHelper, base::Passed(&platform_handle), - base::Passed(&channel_info), channel_endpoint, callback, - callback_thread_task_runner)); - } else { - (callback_thread_task_runner.get() ? callback_thread_task_runner - : io_thread_task_runner) - ->PostTask(FROM_HERE, base::Bind(callback, channel_info.release())); - } - - return rv.Pass(); -} - -// TODO(vtl): Write tests for this. -void DestroyChannel(ChannelInfo* channel_info) { - DCHECK(channel_info); - DCHECK(channel_info->channel_id); - DCHECK(internal::g_channel_manager); - // This will destroy the channel synchronously if called from the channel - // thread. - internal::g_channel_manager->ShutdownChannel(channel_info->channel_id); - delete channel_info; -} - -void WillDestroyChannelSoon(ChannelInfo* channel_info) { - DCHECK(channel_info); - DCHECK(internal::g_channel_manager); - internal::g_channel_manager->WillShutdownChannel(channel_info->channel_id); -} - -MojoResult CreatePlatformHandleWrapper( - ScopedPlatformHandle platform_handle, - MojoHandle* platform_handle_wrapper_handle) { - DCHECK(platform_handle_wrapper_handle); - - scoped_refptr<system::Dispatcher> dispatcher( - new system::PlatformHandleDispatcher(platform_handle.Pass())); - - DCHECK(internal::g_core); - MojoHandle h = internal::g_core->AddDispatcher(dispatcher); - if (h == MOJO_HANDLE_INVALID) { - LOG(ERROR) << "Handle table full"; - dispatcher->Close(); - return MOJO_RESULT_RESOURCE_EXHAUSTED; - } - - *platform_handle_wrapper_handle = h; - return MOJO_RESULT_OK; -} - -MojoResult PassWrappedPlatformHandle(MojoHandle platform_handle_wrapper_handle, - ScopedPlatformHandle* platform_handle) { - DCHECK(platform_handle); - - DCHECK(internal::g_core); - scoped_refptr<system::Dispatcher> dispatcher( - internal::g_core->GetDispatcher(platform_handle_wrapper_handle)); - if (!dispatcher) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (dispatcher->GetType() != system::Dispatcher::kTypePlatformHandle) - return MOJO_RESULT_INVALID_ARGUMENT; - - *platform_handle = - static_cast<system::PlatformHandleDispatcher*>(dispatcher.get()) - ->PassPlatformHandle() - .Pass(); - return MOJO_RESULT_OK; -} - -MojoResult AsyncWait(MojoHandle handle, - MojoHandleSignals signals, - base::Callback<void(MojoResult)> callback) { - return internal::g_core->AsyncWait(handle, signals, callback); -} - -} // namespace embedder -} // namespace mojo diff --git a/mojo/edk/embedder/embedder.h b/mojo/edk/embedder/embedder.h deleted file mode 100644 index 987c6879..0000000 --- a/mojo/edk/embedder/embedder.h +++ /dev/null @@ -1,132 +0,0 @@ -// 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_EDK_EMBEDDER_EMBEDDER_H_ -#define MOJO_EDK_EMBEDDER_EMBEDDER_H_ - -#include "base/callback.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/task_runner.h" -#include "mojo/edk/embedder/channel_info_forward.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/cpp/system/message_pipe.h" - -namespace mojo { -namespace embedder { - -struct Configuration; -class PlatformSupport; - -// Must be called first, or just after setting configuration parameters, -// to initialize the (global, singleton) system. -MOJO_SYSTEM_IMPL_EXPORT void Init(scoped_ptr<PlatformSupport> platform_support); - -// Returns the global configuration. In general there should be no need to -// change the configuration, but if you do so this must be done before calling -// |Init()|. -MOJO_SYSTEM_IMPL_EXPORT Configuration* GetConfiguration(); - -// A "channel" is a connection on top of an OS "pipe", on top of which Mojo -// message pipes (etc.) can be multiplexed. It must "live" on some I/O thread. -// -// There are two channel creation APIs: |CreateChannelOnIOThread()| creates a -// channel synchronously and must be called from the I/O thread, while -// |CreateChannel()| is asynchronous and may be called from any thread. -// |DestroyChannel()| is used to destroy the channel in either case and may be -// called from any thread, but completes synchronously when called from the I/O -// thread. -// -// Both creation functions have a |platform_handle| argument, which should be an -// OS-dependent handle to one side of a suitable bidirectional OS "pipe" (e.g., -// a file descriptor to a socket on POSIX, a handle to a named pipe on Windows); -// this "pipe" should be connected and ready for operation (e.g., to be written -// to or read from). -// -// Both (synchronously) return a handle to the bootstrap message pipe on the -// channel that was (or is to be) created, or |MOJO_HANDLE_INVALID| on error -// (but note that this will happen only if, e.g., the handle table is full). -// This message pipe may be used immediately, but since channel operation -// actually begins asynchronously, other errors may still occur (e.g., if the -// other end of the "pipe" is closed) and be reported in the usual way to the -// returned handle. -// -// (E.g., a message written immediately to the returned handle will be queued -// and the handle immediately closed, before the channel begins operation. In -// this case, the channel should connect as usual, send the queued message, and -// report that the handle was closed to the other side. The message sent may -// have other handles, so there may still be message pipes "on" this channel.) -// -// Both also produce a |ChannelInfo*| (a pointer to an opaque object) -- the -// first synchronously and second asynchronously. -// -// The destruction functions are similarly synchronous and asynchronous, -// respectively, and take the |ChannelInfo*| produced by the creation functions. -// -// TODO(vtl): Figure out channel teardown. - -// Creates a channel; must only be called from the I/O thread. |platform_handle| -// should be a handle to a connected OS "pipe". Eventually (even on failure), -// the "out" value |*channel_info| should be passed to |DestoryChannel()| to -// tear down the channel. Returns a handle to the bootstrap message pipe. -MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle -CreateChannelOnIOThread(ScopedPlatformHandle platform_handle, - ChannelInfo** channel_info); - -typedef base::Callback<void(ChannelInfo*)> DidCreateChannelCallback; -// Creates a channel asynchronously; may be called from any thread. -// |platform_handle| should be a handle to a connected OS "pipe". -// |io_thread_task_runner| should be the |TaskRunner| for the I/O thread. -// |callback| should be the callback to call with the |ChannelInfo*|, which -// should eventually be passed to |DestroyChannel()| to tear down the channel; -// the callback will be called using |callback_thread_task_runner| if that is -// non-null, or otherwise it will be called using |io_thread_task_runner|. -// Returns a handle to the bootstrap message pipe. -MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle -CreateChannel(ScopedPlatformHandle platform_handle, - scoped_refptr<base::TaskRunner> io_thread_task_runner, - DidCreateChannelCallback callback, - scoped_refptr<base::TaskRunner> callback_thread_task_runner); - -// Destroys a channel that was created using |CreateChannel()| (or -// |CreateChannelOnIOThread()|); may be called from any thread. |channel_info| -// should be the value provided to the callback to |CreateChannel()| (or -// returned by |CreateChannelOnIOThread()|). If called from the I/O thread, this -// will complete synchronously (in particular, it will post no tasks). -MOJO_SYSTEM_IMPL_EXPORT void DestroyChannel(ChannelInfo* channel_info); - -// Inform the channel that it will soon be destroyed (doing so is optional). -// This may be called from any thread, but the caller must ensure that this is -// called before |DestroyChannel()|. -MOJO_SYSTEM_IMPL_EXPORT void WillDestroyChannelSoon(ChannelInfo* channel_info); - -// Creates a |MojoHandle| that wraps the given |PlatformHandle| (taking -// ownership of it). This |MojoHandle| can then, e.g., be passed through message -// pipes. Note: This takes ownership (and thus closes) |platform_handle| even on -// failure, which is different from what you'd expect from a Mojo API, but it -// makes for a more convenient embedder API. -MOJO_SYSTEM_IMPL_EXPORT MojoResult -CreatePlatformHandleWrapper(ScopedPlatformHandle platform_handle, - MojoHandle* platform_handle_wrapper_handle); -// Retrieves the |PlatformHandle| that was wrapped into a |MojoHandle| (using -// |CreatePlatformHandleWrapper()| above). Note that the |MojoHandle| must still -// be closed separately. -MOJO_SYSTEM_IMPL_EXPORT MojoResult -PassWrappedPlatformHandle(MojoHandle platform_handle_wrapper_handle, - ScopedPlatformHandle* platform_handle); - -// Start waiting the handle asynchronously. On success, |callback| will be -// called exactly once, when |handle| satisfies a signal in |signals| or it -// becomes known that it will never do so. |callback| will be executed on an -// arbitrary thread. It must not call any Mojo system or embedder functions. -MOJO_SYSTEM_IMPL_EXPORT MojoResult -AsyncWait(MojoHandle handle, - MojoHandleSignals signals, - base::Callback<void(MojoResult)> callback); - -} // namespace embedder -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_EMBEDDER_H_ diff --git a/mojo/edk/embedder/embedder_internal.h b/mojo/edk/embedder/embedder_internal.h deleted file mode 100644 index ab8388a..0000000 --- a/mojo/edk/embedder/embedder_internal.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This header contains internal details for the *implementation* of the -// embedder API. It should not be included by any public header (nor by users of -// the embedder API). - -#ifndef MOJO_EDK_EMBEDDER_EMBEDDER_INTERNAL_H_ -#define MOJO_EDK_EMBEDDER_EMBEDDER_INTERNAL_H_ - -#include <stdint.h> - -namespace mojo { - -namespace system { - -class ChannelManager; -class Core; - -// Repeat a typedef in mojo/edk/system/channel_manager.h, to avoid including it. -typedef uintptr_t ChannelId; - -} // namespace system - -namespace embedder { - -// This is a type that's opaque to users of the embedder API (which only -// gives/takes |ChannelInfo*|s). We make it a struct to make it -// template-friendly. -struct ChannelInfo { - explicit ChannelInfo(system::ChannelId channel_id = 0) - : channel_id(channel_id) {} - - system::ChannelId channel_id; -}; - -namespace internal { - -// Instance of |Core| used by the system functions (|Mojo...()|). -extern system::Core* g_core; - -// Instance of |ChannelManager| used by the channel management functions -// (|mojo::embedder::CreateChannel()|, etc.). -extern system::ChannelManager* g_channel_manager; - -} // namespace internal - -} // namepace embedder - -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_EMBEDDER_INTERNAL_H_ diff --git a/mojo/edk/embedder/embedder_unittest.cc b/mojo/edk/embedder/embedder_unittest.cc deleted file mode 100644 index ed4ea2e..0000000 --- a/mojo/edk/embedder/embedder_unittest.cc +++ /dev/null @@ -1,639 +0,0 @@ -// 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/edk/embedder/embedder.h" - -#include <string.h> - -#include "base/bind.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "base/synchronization/waitable_event.h" -#include "base/test/test_io_thread.h" -#include "base/test/test_timeouts.h" -#include "mojo/edk/embedder/platform_channel_pair.h" -#include "mojo/edk/embedder/test_embedder.h" -#include "mojo/edk/system/test_utils.h" -#include "mojo/edk/test/multiprocess_test_helper.h" -#include "mojo/public/c/system/core.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace embedder { -namespace { - -const MojoHandleSignals kSignalReadadableWritable = - MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE; - -const MojoHandleSignals kSignalAll = MOJO_HANDLE_SIGNAL_READABLE | - MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED; - -class ScopedTestChannel { - public: - // Creates a channel that lives on a given I/O thread (determined by the given - // |TaskRunner|) attached to the given |platform_handle|. After construction, - // |bootstrap_message_pipe()| gives the Mojo handle for the bootstrap message - // pipe on this channel; it is up to the caller to close this handle. - // Note: The I/O thread must outlive this object (and its message loop must - // continue pumping messages while this object is alive). - ScopedTestChannel(scoped_refptr<base::TaskRunner> io_thread_task_runner, - ScopedPlatformHandle platform_handle) - : io_thread_task_runner_(io_thread_task_runner), - bootstrap_message_pipe_(MOJO_HANDLE_INVALID), - did_create_channel_event_(true, false), - channel_info_(nullptr) { - bootstrap_message_pipe_ = - CreateChannel(platform_handle.Pass(), io_thread_task_runner_, - base::Bind(&ScopedTestChannel::DidCreateChannel, - base::Unretained(this)), - nullptr) - .release() - .value(); - CHECK_NE(bootstrap_message_pipe_, MOJO_HANDLE_INVALID); - } - - // Destructor: Shuts down the channel. (As noted above, for this to happen, - // the I/O thread must be alive and pumping messages.) - ~ScopedTestChannel() { DestroyChannel(channel_info_); } - - // Waits for channel creation to be completed. - void WaitForChannelCreationCompletion() { did_create_channel_event_.Wait(); } - - MojoHandle bootstrap_message_pipe() const { return bootstrap_message_pipe_; } - - // Call only after |WaitForChannelCreationCompletion()|. Use only to check - // that it's not null. - const ChannelInfo* channel_info() const { return channel_info_; } - - private: - void DidCreateChannel(ChannelInfo* channel_info) { - CHECK(channel_info); - CHECK(!channel_info_); - channel_info_ = channel_info; - did_create_channel_event_.Signal(); - } - - scoped_refptr<base::TaskRunner> io_thread_task_runner_; - - // Valid from creation until whenever it gets closed (by the "owner" of this - // object). - // Note: We don't want use the C++ wrappers here, since we want to test the - // API at the lowest level. - MojoHandle bootstrap_message_pipe_; - - // Set after channel creation has been completed (i.e., the callback to - // |CreateChannel()| has been called). - base::WaitableEvent did_create_channel_event_; - - // Valid after channel creation completion until destruction. - ChannelInfo* channel_info_; - - DISALLOW_COPY_AND_ASSIGN(ScopedTestChannel); -}; - -class EmbedderTest : public testing::Test { - public: - EmbedderTest() : test_io_thread_(base::TestIOThread::kAutoStart) {} - ~EmbedderTest() override {} - - protected: - base::TestIOThread* test_io_thread() { return &test_io_thread_; } - - private: - base::TestIOThread test_io_thread_; - - DISALLOW_COPY_AND_ASSIGN(EmbedderTest); -}; - -TEST_F(EmbedderTest, ChannelsBasic) { - mojo::embedder::test::InitWithSimplePlatformSupport(); - - { - PlatformChannelPair channel_pair; - ScopedTestChannel server_channel(test_io_thread()->task_runner(), - channel_pair.PassServerHandle()); - MojoHandle server_mp = server_channel.bootstrap_message_pipe(); - EXPECT_NE(server_mp, MOJO_HANDLE_INVALID); - ScopedTestChannel client_channel(test_io_thread()->task_runner(), - channel_pair.PassClientHandle()); - MojoHandle client_mp = client_channel.bootstrap_message_pipe(); - EXPECT_NE(client_mp, MOJO_HANDLE_INVALID); - - // We can write to a message pipe handle immediately. - const char kHello[] = "hello"; - EXPECT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(server_mp, kHello, - static_cast<uint32_t>(sizeof(kHello)), nullptr, - 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Now wait for the other side to become readable. - MojoHandleSignalsState state; - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(client_mp, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); - EXPECT_EQ(kSignalReadadableWritable, state.satisfied_signals); - EXPECT_EQ(kSignalAll, state.satisfiable_signals); - - char buffer[1000] = {}; - uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoReadMessage(client_mp, buffer, &num_bytes, nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kHello), num_bytes); - EXPECT_STREQ(kHello, buffer); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(server_mp)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(client_mp)); - - // By this point, these waits should basically be no-ops (since we've waited - // for the client message pipe to become readable, which implies that both - // the server and client channels were completely created). - server_channel.WaitForChannelCreationCompletion(); - client_channel.WaitForChannelCreationCompletion(); - EXPECT_TRUE(server_channel.channel_info()); - EXPECT_TRUE(client_channel.channel_info()); - } - - EXPECT_TRUE(test::Shutdown()); -} - -class TestAsyncWaiter { - public: - TestAsyncWaiter() : event_(true, false), wait_result_(MOJO_RESULT_UNKNOWN) {} - - void Awake(MojoResult result) { - base::AutoLock l(wait_result_lock_); - wait_result_ = result; - event_.Signal(); - } - - bool TryWait() { return event_.TimedWait(TestTimeouts::action_timeout()); } - - MojoResult wait_result() const { - base::AutoLock l(wait_result_lock_); - return wait_result_; - } - - private: - base::WaitableEvent event_; - - mutable base::Lock wait_result_lock_; - MojoResult wait_result_; - - DISALLOW_COPY_AND_ASSIGN(TestAsyncWaiter); -}; - -void WriteHello(MessagePipeHandle pipe) { - static const char kHello[] = "hello"; - CHECK_EQ(MOJO_RESULT_OK, - WriteMessageRaw(pipe, kHello, static_cast<uint32_t>(sizeof(kHello)), - nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); -} - -void CloseScopedHandle(ScopedMessagePipeHandle handle) { - // Do nothing and the destructor will close it. -} - -TEST_F(EmbedderTest, AsyncWait) { - mojo::embedder::test::InitWithSimplePlatformSupport(); - - { - ScopedMessagePipeHandle client_mp; - ScopedMessagePipeHandle server_mp; - EXPECT_EQ(MOJO_RESULT_OK, - mojo::CreateMessagePipe(nullptr, &client_mp, &server_mp)); - - TestAsyncWaiter waiter; - EXPECT_EQ(MOJO_RESULT_OK, - AsyncWait(client_mp.get().value(), MOJO_HANDLE_SIGNAL_READABLE, - base::Bind(&TestAsyncWaiter::Awake, - base::Unretained(&waiter)))); - - test_io_thread()->task_runner()->PostTask( - FROM_HERE, base::Bind(&WriteHello, server_mp.get())); - EXPECT_TRUE(waiter.TryWait()); - EXPECT_EQ(MOJO_RESULT_OK, waiter.wait_result()); - - // If message is in the queue, it does't allow us to wait. - TestAsyncWaiter waiter_that_doesnt_wait; - EXPECT_EQ( - MOJO_RESULT_ALREADY_EXISTS, - AsyncWait(client_mp.get().value(), MOJO_HANDLE_SIGNAL_READABLE, - base::Bind(&TestAsyncWaiter::Awake, - base::Unretained(&waiter_that_doesnt_wait)))); - - char buffer[1000]; - uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer)); - CHECK_EQ(MOJO_RESULT_OK, - ReadMessageRaw(client_mp.get(), buffer, &num_bytes, nullptr, - nullptr, MOJO_READ_MESSAGE_FLAG_NONE)); - - TestAsyncWaiter unsatisfiable_waiter; - EXPECT_EQ(MOJO_RESULT_OK, - AsyncWait(client_mp.get().value(), MOJO_HANDLE_SIGNAL_READABLE, - base::Bind(&TestAsyncWaiter::Awake, - base::Unretained(&unsatisfiable_waiter)))); - - test_io_thread()->task_runner()->PostTask( - FROM_HERE, - base::Bind(&CloseScopedHandle, base::Passed(server_mp.Pass()))); - - EXPECT_TRUE(unsatisfiable_waiter.TryWait()); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - unsatisfiable_waiter.wait_result()); - } - - EXPECT_TRUE(test::Shutdown()); -} - -TEST_F(EmbedderTest, ChannelsHandlePassing) { - mojo::embedder::test::InitWithSimplePlatformSupport(); - - { - PlatformChannelPair channel_pair; - ScopedTestChannel server_channel(test_io_thread()->task_runner(), - channel_pair.PassServerHandle()); - MojoHandle server_mp = server_channel.bootstrap_message_pipe(); - EXPECT_NE(server_mp, MOJO_HANDLE_INVALID); - ScopedTestChannel client_channel(test_io_thread()->task_runner(), - channel_pair.PassClientHandle()); - MojoHandle client_mp = client_channel.bootstrap_message_pipe(); - EXPECT_NE(client_mp, MOJO_HANDLE_INVALID); - - MojoHandle h0, h1; - EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(nullptr, &h0, &h1)); - - // Write a message to |h0| (attaching nothing). - const char kHello[] = "hello"; - EXPECT_EQ( - MOJO_RESULT_OK, - MojoWriteMessage(h0, kHello, static_cast<uint32_t>(sizeof(kHello)), - nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Write one message to |server_mp|, attaching |h1|. - const char kWorld[] = "world!!!"; - EXPECT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(server_mp, kWorld, - static_cast<uint32_t>(sizeof(kWorld)), &h1, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - h1 = MOJO_HANDLE_INVALID; - - // Write another message to |h0|. - const char kFoo[] = "foo"; - EXPECT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(h0, kFoo, static_cast<uint32_t>(sizeof(kFoo)), - nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Wait for |client_mp| to become readable. - MojoHandleSignalsState state; - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(client_mp, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); - EXPECT_EQ(kSignalReadadableWritable, state.satisfied_signals); - EXPECT_EQ(kSignalAll, state.satisfiable_signals); - - // Read a message from |client_mp|. - char buffer[1000] = {}; - uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer)); - MojoHandle handles[10] = {}; - uint32_t num_handles = arraysize(handles); - EXPECT_EQ(MOJO_RESULT_OK, - MojoReadMessage(client_mp, buffer, &num_bytes, handles, - &num_handles, MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kWorld), num_bytes); - EXPECT_STREQ(kWorld, buffer); - EXPECT_EQ(1u, num_handles); - EXPECT_NE(handles[0], MOJO_HANDLE_INVALID); - h1 = handles[0]; - - // Wait for |h1| to become readable. - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(h1, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); - EXPECT_EQ(kSignalReadadableWritable, state.satisfied_signals); - EXPECT_EQ(kSignalAll, state.satisfiable_signals); - - // Read a message from |h1|. - memset(buffer, 0, sizeof(buffer)); - num_bytes = static_cast<uint32_t>(sizeof(buffer)); - memset(handles, 0, sizeof(handles)); - num_handles = arraysize(handles); - EXPECT_EQ(MOJO_RESULT_OK, - MojoReadMessage(h1, buffer, &num_bytes, handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kHello), num_bytes); - EXPECT_STREQ(kHello, buffer); - EXPECT_EQ(0u, num_handles); - - // Wait for |h1| to become readable (again). - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(h1, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); - EXPECT_EQ(kSignalReadadableWritable, state.satisfied_signals); - EXPECT_EQ(kSignalAll, state.satisfiable_signals); - - // Read the second message from |h1|. - memset(buffer, 0, sizeof(buffer)); - num_bytes = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoReadMessage(h1, buffer, &num_bytes, nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kFoo), num_bytes); - EXPECT_STREQ(kFoo, buffer); - - // Write a message to |h1|. - const char kBarBaz[] = "barbaz"; - EXPECT_EQ( - MOJO_RESULT_OK, - MojoWriteMessage(h1, kBarBaz, static_cast<uint32_t>(sizeof(kBarBaz)), - nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Wait for |h0| to become readable. - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(h0, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); - EXPECT_EQ(kSignalReadadableWritable, state.satisfied_signals); - EXPECT_EQ(kSignalAll, state.satisfiable_signals); - - // Read a message from |h0|. - memset(buffer, 0, sizeof(buffer)); - num_bytes = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoReadMessage(h0, buffer, &num_bytes, nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kBarBaz), num_bytes); - EXPECT_STREQ(kBarBaz, buffer); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(server_mp)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(client_mp)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1)); - - server_channel.WaitForChannelCreationCompletion(); - client_channel.WaitForChannelCreationCompletion(); - EXPECT_TRUE(server_channel.channel_info()); - EXPECT_TRUE(client_channel.channel_info()); - } - - EXPECT_TRUE(test::Shutdown()); -} - -// The sequence of messages sent is: -// server_mp client_mp mp0 mp1 mp2 mp3 -// 1. "hello" -// 2. "world!" -// 3. "FOO" -// 4. "Bar"+mp1 -// 5. (close) -// 6. (close) -// 7. "baz" -// 8. (closed) -// 9. "quux"+mp2 -// 10. (close) -// 11. (wait/cl.) -// 12. (wait/cl.) - -#if defined(OS_ANDROID) -// Android multi-process tests are not executing the new process. This is flaky. -#define MAYBE_MultiprocessChannels DISABLED_MultiprocessChannels -#else -#define MAYBE_MultiprocessChannels MultiprocessChannels -#endif // defined(OS_ANDROID) -TEST_F(EmbedderTest, MAYBE_MultiprocessChannels) { - mojo::embedder::test::InitWithSimplePlatformSupport(); - mojo::test::MultiprocessTestHelper multiprocess_test_helper; - multiprocess_test_helper.StartChild("MultiprocessChannelsClient"); - - { - ScopedTestChannel server_channel( - test_io_thread()->task_runner(), - multiprocess_test_helper.server_platform_handle.Pass()); - MojoHandle server_mp = server_channel.bootstrap_message_pipe(); - EXPECT_NE(server_mp, MOJO_HANDLE_INVALID); - server_channel.WaitForChannelCreationCompletion(); - EXPECT_TRUE(server_channel.channel_info()); - - // 1. Write a message to |server_mp| (attaching nothing). - const char kHello[] = "hello"; - EXPECT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(server_mp, kHello, - static_cast<uint32_t>(sizeof(kHello)), nullptr, - 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // TODO(vtl): If the scope were ended immediately here (maybe after closing - // |server_mp|), we die with a fatal error in |Channel::HandleLocalError()|. - - // 2. Read a message from |server_mp|. - MojoHandleSignalsState state; - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(server_mp, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); - EXPECT_EQ(kSignalReadadableWritable, state.satisfied_signals); - EXPECT_EQ(kSignalAll, state.satisfiable_signals); - - char buffer[1000] = {}; - uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoReadMessage(server_mp, buffer, &num_bytes, nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - const char kWorld[] = "world!"; - EXPECT_EQ(sizeof(kWorld), num_bytes); - EXPECT_STREQ(kWorld, buffer); - - // Create a new message pipe (endpoints |mp0| and |mp1|). - MojoHandle mp0, mp1; - EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(nullptr, &mp0, &mp1)); - - // 3. Write something to |mp0|. - const char kFoo[] = "FOO"; - EXPECT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(mp0, kFoo, static_cast<uint32_t>(sizeof(kFoo)), - nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // 4. Write a message to |server_mp|, attaching |mp1|. - const char kBar[] = "Bar"; - EXPECT_EQ( - MOJO_RESULT_OK, - MojoWriteMessage(server_mp, kBar, static_cast<uint32_t>(sizeof(kBar)), - &mp1, 1, MOJO_WRITE_MESSAGE_FLAG_NONE)); - mp1 = MOJO_HANDLE_INVALID; - - // 5. Close |server_mp|. - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(server_mp)); - - // 9. Read a message from |mp0|, which should have |mp2| attached. - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(mp0, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); - EXPECT_EQ(kSignalReadadableWritable, state.satisfied_signals); - EXPECT_EQ(kSignalAll, state.satisfiable_signals); - - memset(buffer, 0, sizeof(buffer)); - num_bytes = static_cast<uint32_t>(sizeof(buffer)); - MojoHandle mp2 = MOJO_HANDLE_INVALID; - uint32_t num_handles = 1; - EXPECT_EQ(MOJO_RESULT_OK, - MojoReadMessage(mp0, buffer, &num_bytes, &mp2, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - const char kQuux[] = "quux"; - EXPECT_EQ(sizeof(kQuux), num_bytes); - EXPECT_STREQ(kQuux, buffer); - EXPECT_EQ(1u, num_handles); - EXPECT_NE(mp2, MOJO_HANDLE_INVALID); - - // 7. Read a message from |mp2|. - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(mp2, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - state.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - state.satisfiable_signals); - - memset(buffer, 0, sizeof(buffer)); - num_bytes = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoReadMessage(mp2, buffer, &num_bytes, nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - const char kBaz[] = "baz"; - EXPECT_EQ(sizeof(kBaz), num_bytes); - EXPECT_STREQ(kBaz, buffer); - - // 10. Close |mp0|. - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mp0)); - -// 12. Wait on |mp2| (which should eventually fail) and then close it. -// TODO(vtl): crbug.com/351768 -#if 0 - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoWait(mp2, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, - &state)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_NONE, state.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_NONE, state.satisfiable_signals); -#endif - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mp2)); - } - - EXPECT_TRUE(multiprocess_test_helper.WaitForChildTestShutdown()); - EXPECT_TRUE(test::Shutdown()); -} - -MOJO_MULTIPROCESS_TEST_CHILD_TEST(MultiprocessChannelsClient) { - ScopedPlatformHandle client_platform_handle = - mojo::test::MultiprocessTestHelper::client_platform_handle.Pass(); - EXPECT_TRUE(client_platform_handle.is_valid()); - - base::TestIOThread test_io_thread(base::TestIOThread::kAutoStart); - mojo::embedder::test::InitWithSimplePlatformSupport(); - - { - ScopedTestChannel client_channel(test_io_thread.task_runner(), - client_platform_handle.Pass()); - MojoHandle client_mp = client_channel.bootstrap_message_pipe(); - EXPECT_NE(client_mp, MOJO_HANDLE_INVALID); - client_channel.WaitForChannelCreationCompletion(); - CHECK(client_channel.channel_info() != nullptr); - - // 1. Read the first message from |client_mp|. - MojoHandleSignalsState state; - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(client_mp, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); - EXPECT_EQ(kSignalReadadableWritable, state.satisfied_signals); - EXPECT_EQ(kSignalAll, state.satisfiable_signals); - - char buffer[1000] = {}; - uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoReadMessage(client_mp, buffer, &num_bytes, nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - const char kHello[] = "hello"; - EXPECT_EQ(sizeof(kHello), num_bytes); - EXPECT_STREQ(kHello, buffer); - - // 2. Write a message to |client_mp| (attaching nothing). - const char kWorld[] = "world!"; - EXPECT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(client_mp, kWorld, - static_cast<uint32_t>(sizeof(kWorld)), nullptr, - 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // 4. Read a message from |client_mp|, which should have |mp1| attached. - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(client_mp, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); - // The other end of the handle may or may not be closed at this point, so we - // can't test MOJO_HANDLE_SIGNAL_WRITABLE or MOJO_HANDLE_SIGNAL_PEER_CLOSED. - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, - state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, - state.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE); - // TODO(vtl): If the scope were to end here (and |client_mp| closed), we'd - // die (again due to |Channel::HandleLocalError()|). - memset(buffer, 0, sizeof(buffer)); - num_bytes = static_cast<uint32_t>(sizeof(buffer)); - MojoHandle mp1 = MOJO_HANDLE_INVALID; - uint32_t num_handles = 1; - EXPECT_EQ(MOJO_RESULT_OK, - MojoReadMessage(client_mp, buffer, &num_bytes, &mp1, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - const char kBar[] = "Bar"; - EXPECT_EQ(sizeof(kBar), num_bytes); - EXPECT_STREQ(kBar, buffer); - EXPECT_EQ(1u, num_handles); - EXPECT_NE(mp1, MOJO_HANDLE_INVALID); - // TODO(vtl): If the scope were to end here (and the two handles closed), - // we'd die due to |Channel::RunRemoteMessagePipeEndpoint()| not handling - // write errors (assuming the parent had closed the pipe). - - // 6. Close |client_mp|. - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(client_mp)); - - // Create a new message pipe (endpoints |mp2| and |mp3|). - MojoHandle mp2, mp3; - EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(nullptr, &mp2, &mp3)); - - // 7. Write a message to |mp3|. - const char kBaz[] = "baz"; - EXPECT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(mp3, kBaz, static_cast<uint32_t>(sizeof(kBaz)), - nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // 8. Close |mp3|. - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mp3)); - - // 9. Write a message to |mp1|, attaching |mp2|. - const char kQuux[] = "quux"; - EXPECT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(mp1, kQuux, static_cast<uint32_t>(sizeof(kQuux)), - &mp2, 1, MOJO_WRITE_MESSAGE_FLAG_NONE)); - mp2 = MOJO_HANDLE_INVALID; - - // 3. Read a message from |mp1|. - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(mp1, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); - EXPECT_EQ(kSignalReadadableWritable, state.satisfied_signals); - EXPECT_EQ(kSignalAll, state.satisfiable_signals); - - memset(buffer, 0, sizeof(buffer)); - num_bytes = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoReadMessage(mp1, buffer, &num_bytes, nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - const char kFoo[] = "FOO"; - EXPECT_EQ(sizeof(kFoo), num_bytes); - EXPECT_STREQ(kFoo, buffer); - - // 11. Wait on |mp1| (which should eventually fail) and then close it. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoWait(mp1, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mp1)); - } - - EXPECT_TRUE(test::Shutdown()); -} - -// TODO(vtl): Test immediate write & close. -// TODO(vtl): Test broken-connection cases. - -} // namespace -} // namespace embedder -} // namespace mojo diff --git a/mojo/edk/embedder/entrypoints.cc b/mojo/edk/embedder/entrypoints.cc deleted file mode 100644 index 9338cc7..0000000 --- a/mojo/edk/embedder/entrypoints.cc +++ /dev/null @@ -1,155 +0,0 @@ -// 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/edk/embedder/embedder_internal.h" -#include "mojo/edk/system/core.h" -#include "mojo/public/c/system/buffer.h" -#include "mojo/public/c/system/data_pipe.h" -#include "mojo/public/c/system/functions.h" -#include "mojo/public/c/system/message_pipe.h" - -using mojo::embedder::internal::g_core; -using mojo::system::MakeUserPointer; - -// 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, - MojoHandleSignals signals, - MojoDeadline deadline, - MojoHandleSignalsState* signals_state) { - return g_core->Wait(handle, signals, deadline, - MakeUserPointer(signals_state)); -} - -MojoResult MojoWaitMany(const MojoHandle* handles, - const MojoHandleSignals* signals, - uint32_t num_handles, - MojoDeadline deadline, - uint32_t* result_index, - MojoHandleSignalsState* signals_states) { - return g_core->WaitMany(MakeUserPointer(handles), MakeUserPointer(signals), - num_handles, deadline, MakeUserPointer(result_index), - MakeUserPointer(signals_states)); -} - -MojoResult MojoCreateMessagePipe(const MojoCreateMessagePipeOptions* options, - MojoHandle* message_pipe_handle0, - MojoHandle* message_pipe_handle1) { - return g_core->CreateMessagePipe(MakeUserPointer(options), - MakeUserPointer(message_pipe_handle0), - MakeUserPointer(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, MakeUserPointer(bytes), - num_bytes, MakeUserPointer(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, MakeUserPointer(bytes), MakeUserPointer(num_bytes), - MakeUserPointer(handles), MakeUserPointer(num_handles), flags); -} - -MojoResult MojoCreateDataPipe(const MojoCreateDataPipeOptions* options, - MojoHandle* data_pipe_producer_handle, - MojoHandle* data_pipe_consumer_handle) { - return g_core->CreateDataPipe(MakeUserPointer(options), - MakeUserPointer(data_pipe_producer_handle), - MakeUserPointer(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, MakeUserPointer(elements), - MakeUserPointer(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, - MakeUserPointer(buffer), - MakeUserPointer(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, MakeUserPointer(elements), - MakeUserPointer(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, - MakeUserPointer(buffer), - MakeUserPointer(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(MakeUserPointer(options), num_bytes, - MakeUserPointer(shared_buffer_handle)); -} - -MojoResult MojoDuplicateBufferHandle( - MojoHandle buffer_handle, - const struct MojoDuplicateBufferHandleOptions* options, - MojoHandle* new_buffer_handle) { - return g_core->DuplicateBufferHandle(buffer_handle, MakeUserPointer(options), - MakeUserPointer(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, - MakeUserPointer(buffer), flags); -} - -MojoResult MojoUnmapBuffer(void* buffer) { - return g_core->UnmapBuffer(MakeUserPointer(buffer)); -} - -} // extern "C" diff --git a/mojo/edk/embedder/platform_channel_pair.cc b/mojo/edk/embedder/platform_channel_pair.cc deleted file mode 100644 index 4e88bc9..0000000 --- a/mojo/edk/embedder/platform_channel_pair.cc +++ /dev/null @@ -1,32 +0,0 @@ -// 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/edk/embedder/platform_channel_pair.h" - -#include "base/logging.h" - -namespace mojo { -namespace embedder { - -const char PlatformChannelPair::kMojoPlatformChannelHandleSwitch[] = - "mojo-platform-channel-handle"; - -PlatformChannelPair::~PlatformChannelPair() { -} - -ScopedPlatformHandle PlatformChannelPair::PassServerHandle() { - return server_handle_.Pass(); -} - -ScopedPlatformHandle PlatformChannelPair::PassClientHandle() { - return client_handle_.Pass(); -} - -void PlatformChannelPair::ChildProcessLaunched() { - DCHECK(client_handle_.is_valid()); - client_handle_.reset(); -} - -} // namespace embedder -} // namespace mojo diff --git a/mojo/edk/embedder/platform_channel_pair.h b/mojo/edk/embedder/platform_channel_pair.h deleted file mode 100644 index d77c100..0000000 --- a/mojo/edk/embedder/platform_channel_pair.h +++ /dev/null @@ -1,94 +0,0 @@ -// 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_EDK_EMBEDDER_PLATFORM_CHANNEL_PAIR_H_ -#define MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_PAIR_H_ - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/process/launch.h" -#include "build/build_config.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace base { -class CommandLine; -} - -namespace mojo { -namespace embedder { - -// It would be nice to refactor base/process/launch.h to have a more platform- -// independent way of representing handles that are passed to child processes. -#if defined(OS_WIN) -typedef base::HandlesToInheritVector HandlePassingInformation; -#elif defined(OS_POSIX) -typedef base::FileHandleMappingVector HandlePassingInformation; -#else -#error "Unsupported." -#endif - -// This is used to create a pair of |PlatformHandle|s that are connected by a -// suitable (platform-specific) bidirectional "pipe" (e.g., socket on POSIX, -// named pipe on Windows). The resulting handles can then be used in the same -// process (e.g., in tests) or between processes. (The "server" handle is the -// one that will be used in the process that created the pair, whereas the -// "client" handle is the one that will be used in a different process.) -// -// This class provides facilities for passing the client handle to a child -// process. The parent should call |PrepareToPassClientHandlelToChildProcess()| -// to get the data needed to do this, spawn the child using that data, and then -// call |ChildProcessLaunched()|. Note that on Windows this facility (will) only -// work on Vista and later (TODO(vtl)). -// -// Note: |PlatformChannelPair()|, |PassClientHandleFromParentProcess()| and -// |PrepareToPassClientHandleToChildProcess()| have platform-specific -// implementations. -// -// Note: On POSIX platforms, to write to the "pipe", use -// |PlatformChannel{Write,Writev}()| (from platform_channel_utils_posix.h) -// instead of |write()|, |writev()|, etc. Otherwise, you have to worry about -// platform differences in suppressing |SIGPIPE|. -class MOJO_SYSTEM_IMPL_EXPORT PlatformChannelPair { - public: - PlatformChannelPair(); - ~PlatformChannelPair(); - - ScopedPlatformHandle PassServerHandle(); - - // For in-process use (e.g., in tests or to pass over another channel). - ScopedPlatformHandle PassClientHandle(); - - // To be called in the child process, after the parent process called - // |PrepareToPassClientHandleToChildProcess()| and launched the child (using - // the provided data), to create a client handle connected to the server - // handle (in the parent process). - static ScopedPlatformHandle PassClientHandleFromParentProcess( - const base::CommandLine& command_line); - - // Prepares to pass the client channel to a new child process, to be launched - // using |LaunchProcess()| (from base/launch.h). Modifies |*command_line| and - // |*handle_passing_info| as needed. - // Note: For Windows, this method only works on Vista and later. - void PrepareToPassClientHandleToChildProcess( - base::CommandLine* command_line, - HandlePassingInformation* handle_passing_info) const; - - // To be called once the child process has been successfully launched, to do - // any cleanup necessary. - void ChildProcessLaunched(); - - private: - static const char kMojoPlatformChannelHandleSwitch[]; - - ScopedPlatformHandle server_handle_; - ScopedPlatformHandle client_handle_; - - DISALLOW_COPY_AND_ASSIGN(PlatformChannelPair); -}; - -} // namespace embedder -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_PAIR_H_ diff --git a/mojo/edk/embedder/platform_channel_pair_posix.cc b/mojo/edk/embedder/platform_channel_pair_posix.cc deleted file mode 100644 index 2242cce..0000000 --- a/mojo/edk/embedder/platform_channel_pair_posix.cc +++ /dev/null @@ -1,111 +0,0 @@ -// 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/edk/embedder/platform_channel_pair.h" - -#include <fcntl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include "base/command_line.h" -#include "base/logging.h" -#include "base/posix/global_descriptors.h" -#include "base/strings/string_number_conversions.h" -#include "build/build_config.h" -#include "mojo/edk/embedder/platform_handle.h" - -namespace mojo { -namespace embedder { - -namespace { - -bool IsTargetDescriptorUsed( - const base::FileHandleMappingVector& file_handle_mapping, - int target_fd) { - for (size_t i = 0; i < file_handle_mapping.size(); i++) { - if (file_handle_mapping[i].second == target_fd) - return true; - } - return false; -} - -} // namespace - -PlatformChannelPair::PlatformChannelPair() { - // Create the Unix domain socket and set the ends to nonblocking. - int fds[2]; - // TODO(vtl): Maybe fail gracefully if |socketpair()| fails. - PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0); - PCHECK(fcntl(fds[0], F_SETFL, O_NONBLOCK) == 0); - PCHECK(fcntl(fds[1], F_SETFL, O_NONBLOCK) == 0); - -#if defined(OS_MACOSX) - // This turns off |SIGPIPE| when writing to a closed socket (causing it to - // fail with |EPIPE| instead). On Linux, we have to use |send...()| with - // |MSG_NOSIGNAL| -- which is not supported on Mac -- instead. - int no_sigpipe = 1; - PCHECK(setsockopt(fds[0], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe, - sizeof(no_sigpipe)) == 0); - PCHECK(setsockopt(fds[1], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe, - sizeof(no_sigpipe)) == 0); -#endif // defined(OS_MACOSX) - - server_handle_.reset(PlatformHandle(fds[0])); - DCHECK(server_handle_.is_valid()); - client_handle_.reset(PlatformHandle(fds[1])); - DCHECK(client_handle_.is_valid()); -} - -// static -ScopedPlatformHandle PlatformChannelPair::PassClientHandleFromParentProcess( - const base::CommandLine& command_line) { - std::string client_fd_string = - command_line.GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch); - int client_fd = -1; - if (client_fd_string.empty() || - !base::StringToInt(client_fd_string, &client_fd) || - client_fd < base::GlobalDescriptors::kBaseDescriptor) { - LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch; - return ScopedPlatformHandle(); - } - - return ScopedPlatformHandle(PlatformHandle(client_fd)); -} - -void PlatformChannelPair::PrepareToPassClientHandleToChildProcess( - base::CommandLine* command_line, - base::FileHandleMappingVector* handle_passing_info) const { - DCHECK(command_line); - DCHECK(handle_passing_info); - // This is an arbitrary sanity check. (Note that this guarantees that the loop - // below will terminate sanely.) - CHECK_LT(handle_passing_info->size(), 1000u); - - DCHECK(client_handle_.is_valid()); - - // Find a suitable FD to map our client handle to in the child process. - // This has quadratic time complexity in the size of |*handle_passing_info|, - // but |*handle_passing_info| should be very small (usually/often empty). - int target_fd = base::GlobalDescriptors::kBaseDescriptor; - while (IsTargetDescriptorUsed(*handle_passing_info, target_fd)) - target_fd++; - - handle_passing_info->push_back( - std::pair<int, int>(client_handle_.get().fd, target_fd)); - // Log a warning if the command line already has the switch, but "clobber" it - // anyway, since it's reasonably likely that all the switches were just copied - // from the parent. - LOG_IF(WARNING, command_line->HasSwitch(kMojoPlatformChannelHandleSwitch)) - << "Child command line already has switch --" - << kMojoPlatformChannelHandleSwitch << "=" - << command_line->GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch); - // (Any existing switch won't actually be removed from the command line, but - // the last one appended takes precedence.) - command_line->AppendSwitchASCII(kMojoPlatformChannelHandleSwitch, - base::IntToString(target_fd)); -} - -} // namespace embedder -} // namespace mojo diff --git a/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc b/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc deleted file mode 100644 index 56540d7..0000000 --- a/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc +++ /dev/null @@ -1,253 +0,0 @@ -// 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/edk/embedder/platform_channel_pair.h" - -#include <errno.h> -#include <poll.h> -#include <signal.h> -#include <stdio.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/uio.h> -#include <unistd.h> - -#include <deque> - -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/files/scoped_file.h" -#include "base/files/scoped_temp_dir.h" -#include "base/logging.h" -#include "base/macros.h" -#include "mojo/edk/embedder/platform_channel_utils_posix.h" -#include "mojo/edk/embedder/platform_handle.h" -#include "mojo/edk/embedder/platform_handle_vector.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/test/test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace embedder { -namespace { - -void WaitReadable(PlatformHandle h) { - struct pollfd pfds = {}; - pfds.fd = h.fd; - pfds.events = POLLIN; - CHECK_EQ(poll(&pfds, 1, -1), 1); -} - -class PlatformChannelPairPosixTest : public testing::Test { - public: - PlatformChannelPairPosixTest() {} - ~PlatformChannelPairPosixTest() override {} - - void SetUp() override { - // Make sure |SIGPIPE| isn't being ignored. - struct sigaction action = {}; - action.sa_handler = SIG_DFL; - ASSERT_EQ(0, sigaction(SIGPIPE, &action, &old_action_)); - } - - void TearDown() override { - // Restore the |SIGPIPE| handler. - ASSERT_EQ(0, sigaction(SIGPIPE, &old_action_, nullptr)); - } - - private: - struct sigaction old_action_; - - DISALLOW_COPY_AND_ASSIGN(PlatformChannelPairPosixTest); -}; - -TEST_F(PlatformChannelPairPosixTest, NoSigPipe) { - PlatformChannelPair channel_pair; - ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass(); - ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass(); - - // Write to the client. - static const char kHello[] = "hello"; - EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)), - write(client_handle.get().fd, kHello, sizeof(kHello))); - - // Close the client. - client_handle.reset(); - - // Read from the server; this should be okay. - char buffer[100] = {}; - EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)), - read(server_handle.get().fd, buffer, sizeof(buffer))); - EXPECT_STREQ(kHello, buffer); - - // Try reading again. - ssize_t result = read(server_handle.get().fd, buffer, sizeof(buffer)); - // We should probably get zero (for "end of file"), but -1 would also be okay. - EXPECT_TRUE(result == 0 || result == -1); - if (result == -1) - PLOG(WARNING) << "read (expected 0 for EOF)"; - - // Test our replacement for |write()|/|send()|. - result = PlatformChannelWrite(server_handle.get(), kHello, sizeof(kHello)); - EXPECT_EQ(-1, result); - if (errno != EPIPE) - PLOG(WARNING) << "write (expected EPIPE)"; - - // Test our replacement for |writev()|/|sendv()|. - struct iovec iov[2] = {{const_cast<char*>(kHello), sizeof(kHello)}, - {const_cast<char*>(kHello), sizeof(kHello)}}; - result = PlatformChannelWritev(server_handle.get(), iov, 2); - EXPECT_EQ(-1, result); - if (errno != EPIPE) - PLOG(WARNING) << "write (expected EPIPE)"; -} - -TEST_F(PlatformChannelPairPosixTest, SendReceiveData) { - PlatformChannelPair channel_pair; - ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass(); - ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass(); - - for (size_t i = 0; i < 10; i++) { - std::string send_string(1 << i, 'A' + i); - - EXPECT_EQ(static_cast<ssize_t>(send_string.size()), - PlatformChannelWrite(server_handle.get(), send_string.data(), - send_string.size())); - - WaitReadable(client_handle.get()); - - char buf[10000] = {}; - std::deque<PlatformHandle> received_handles; - ssize_t result = PlatformChannelRecvmsg(client_handle.get(), buf, - sizeof(buf), &received_handles); - EXPECT_EQ(static_cast<ssize_t>(send_string.size()), result); - EXPECT_EQ(send_string, std::string(buf, static_cast<size_t>(result))); - EXPECT_TRUE(received_handles.empty()); - } -} - -TEST_F(PlatformChannelPairPosixTest, SendReceiveFDs) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - static const char kHello[] = "hello"; - - PlatformChannelPair channel_pair; - ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass(); - ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass(); - - for (size_t i = 1; i < kPlatformChannelMaxNumHandles; i++) { - // Make |i| files, with the j-th file consisting of j copies of the digit - // |c|. - const char c = '0' + (i % 10); - ScopedPlatformHandleVectorPtr platform_handles(new PlatformHandleVector); - for (size_t j = 1; j <= i; j++) { - base::FilePath unused; - base::ScopedFILE fp( - base::CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused)); - ASSERT_TRUE(fp); - ASSERT_EQ(j, fwrite(std::string(j, c).data(), 1, j, fp.get())); - platform_handles->push_back( - test::PlatformHandleFromFILE(fp.Pass()).release()); - ASSERT_TRUE(platform_handles->back().is_valid()); - } - - // Send the FDs (+ "hello"). - struct iovec iov = {const_cast<char*>(kHello), sizeof(kHello)}; - // We assume that the |sendmsg()| actually sends all the data. - EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)), - PlatformChannelSendmsgWithHandles(server_handle.get(), &iov, 1, - &platform_handles->at(0), - platform_handles->size())); - - WaitReadable(client_handle.get()); - - char buf[10000] = {}; - std::deque<PlatformHandle> received_handles; - // We assume that the |recvmsg()| actually reads all the data. - EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)), - PlatformChannelRecvmsg(client_handle.get(), buf, sizeof(buf), - &received_handles)); - EXPECT_STREQ(kHello, buf); - EXPECT_EQ(i, received_handles.size()); - - for (size_t j = 0; !received_handles.empty(); j++) { - base::ScopedFILE fp(test::FILEFromPlatformHandle( - ScopedPlatformHandle(received_handles.front()), "rb")); - received_handles.pop_front(); - ASSERT_TRUE(fp); - rewind(fp.get()); - char read_buf[kPlatformChannelMaxNumHandles]; - size_t bytes_read = fread(read_buf, 1, sizeof(read_buf), fp.get()); - EXPECT_EQ(j + 1, bytes_read); - EXPECT_EQ(std::string(j + 1, c), std::string(read_buf, bytes_read)); - } - } -} - -TEST_F(PlatformChannelPairPosixTest, AppendReceivedFDs) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - static const char kHello[] = "hello"; - - PlatformChannelPair channel_pair; - ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass(); - ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass(); - - const std::string file_contents("hello world"); - - { - base::FilePath unused; - base::ScopedFILE fp( - base::CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused)); - ASSERT_TRUE(fp); - ASSERT_EQ(file_contents.size(), - fwrite(file_contents.data(), 1, file_contents.size(), fp.get())); - ScopedPlatformHandleVectorPtr platform_handles(new PlatformHandleVector); - platform_handles->push_back( - test::PlatformHandleFromFILE(fp.Pass()).release()); - ASSERT_TRUE(platform_handles->back().is_valid()); - - // Send the FD (+ "hello"). - struct iovec iov = {const_cast<char*>(kHello), sizeof(kHello)}; - // We assume that the |sendmsg()| actually sends all the data. - EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)), - PlatformChannelSendmsgWithHandles(server_handle.get(), &iov, 1, - &platform_handles->at(0), - platform_handles->size())); - } - - WaitReadable(client_handle.get()); - - // Start with an invalid handle in the deque. - std::deque<PlatformHandle> received_handles; - received_handles.push_back(PlatformHandle()); - - char buf[100] = {}; - // We assume that the |recvmsg()| actually reads all the data. - EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)), - PlatformChannelRecvmsg(client_handle.get(), buf, sizeof(buf), - &received_handles)); - EXPECT_STREQ(kHello, buf); - ASSERT_EQ(2u, received_handles.size()); - EXPECT_FALSE(received_handles[0].is_valid()); - EXPECT_TRUE(received_handles[1].is_valid()); - - { - base::ScopedFILE fp(test::FILEFromPlatformHandle( - ScopedPlatformHandle(received_handles[1]), "rb")); - received_handles[1] = PlatformHandle(); - ASSERT_TRUE(fp); - rewind(fp.get()); - char read_buf[100]; - size_t bytes_read = fread(read_buf, 1, sizeof(read_buf), fp.get()); - EXPECT_EQ(file_contents.size(), bytes_read); - EXPECT_EQ(file_contents, std::string(read_buf, bytes_read)); - } -} - -} // namespace -} // namespace embedder -} // namespace mojo diff --git a/mojo/edk/embedder/platform_channel_pair_win.cc b/mojo/edk/embedder/platform_channel_pair_win.cc deleted file mode 100644 index 25306a6..0000000 --- a/mojo/edk/embedder/platform_channel_pair_win.cc +++ /dev/null @@ -1,111 +0,0 @@ -// 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/edk/embedder/platform_channel_pair.h" - -#include <windows.h> - -#include <string> - -#include "base/command_line.h" -#include "base/logging.h" -#include "base/rand_util.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/stringprintf.h" -#include "base/win/windows_version.h" -#include "mojo/edk/embedder/platform_handle.h" - -namespace mojo { -namespace embedder { - -namespace { - -std::wstring GeneratePipeName() { - return base::StringPrintf(L"\\\\.\\pipe\\mojo.%u.%u.%I64u", - GetCurrentProcessId(), GetCurrentThreadId(), - base::RandUint64()); -} - -} // namespace - -PlatformChannelPair::PlatformChannelPair() { - std::wstring pipe_name = GeneratePipeName(); - - const DWORD kOpenMode = - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE; - const DWORD kPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE; - server_handle_.reset(PlatformHandle( - CreateNamedPipeW(pipe_name.c_str(), kOpenMode, kPipeMode, - 1, // Max instances. - 4096, // Out buffer size. - 4096, // In buffer size. - 5000, // Timeout in milliseconds. - nullptr))); // Default security descriptor. - PCHECK(server_handle_.is_valid()); - - const DWORD kDesiredAccess = GENERIC_READ | GENERIC_WRITE; - // The SECURITY_ANONYMOUS flag means that the server side cannot impersonate - // the client. - const DWORD kFlags = - SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS | FILE_FLAG_OVERLAPPED; - // Allow the handle to be inherited by child processes. - SECURITY_ATTRIBUTES security_attributes = { - sizeof(SECURITY_ATTRIBUTES), nullptr, TRUE}; - client_handle_.reset( - PlatformHandle(CreateFileW(pipe_name.c_str(), kDesiredAccess, - 0, // No sharing. - &security_attributes, OPEN_EXISTING, kFlags, - nullptr))); // No template file. - PCHECK(client_handle_.is_valid()); - - // Since a client has connected, ConnectNamedPipe() should return zero and - // GetLastError() should return ERROR_PIPE_CONNECTED. - CHECK(!ConnectNamedPipe(server_handle_.get().handle, nullptr)); - PCHECK(GetLastError() == ERROR_PIPE_CONNECTED); -} - -// static -ScopedPlatformHandle PlatformChannelPair::PassClientHandleFromParentProcess( - const base::CommandLine& command_line) { - std::string client_handle_string = - command_line.GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch); - - int client_handle_value = 0; - if (client_handle_string.empty() || - !base::StringToInt(client_handle_string, &client_handle_value)) { - LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch; - return ScopedPlatformHandle(); - } - - return ScopedPlatformHandle( - PlatformHandle(LongToHandle(client_handle_value))); -} - -void PlatformChannelPair::PrepareToPassClientHandleToChildProcess( - base::CommandLine* command_line, - base::HandlesToInheritVector* handle_passing_info) const { - DCHECK(command_line); - DCHECK(handle_passing_info); - DCHECK(client_handle_.is_valid()); - - CHECK_GE(base::win::GetVersion(), base::win::VERSION_VISTA); - - handle_passing_info->push_back(client_handle_.get().handle); - - // Log a warning if the command line already has the switch, but "clobber" it - // anyway, since it's reasonably likely that all the switches were just copied - // from the parent. - LOG_IF(WARNING, command_line->HasSwitch(kMojoPlatformChannelHandleSwitch)) - << "Child command line already has switch --" - << kMojoPlatformChannelHandleSwitch << "=" - << command_line->GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch); - // (Any existing switch won't actually be removed from the command line, but - // the last one appended takes precedence.) - command_line->AppendSwitchASCII( - kMojoPlatformChannelHandleSwitch, - base::IntToString(HandleToLong(client_handle_.get().handle))); -} - -} // namespace embedder -} // namespace mojo diff --git a/mojo/edk/embedder/platform_channel_utils_posix.cc b/mojo/edk/embedder/platform_channel_utils_posix.cc deleted file mode 100644 index 61b573b..0000000 --- a/mojo/edk/embedder/platform_channel_utils_posix.cc +++ /dev/null @@ -1,186 +0,0 @@ -// 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/edk/embedder/platform_channel_utils_posix.h" - -#include <sys/socket.h> -#include <sys/uio.h> -#include <unistd.h> - -#include "base/logging.h" -#include "base/posix/eintr_wrapper.h" -#include "build/build_config.h" - -namespace mojo { -namespace embedder { - -// On Linux, |SIGPIPE| is suppressed by passing |MSG_NOSIGNAL| to -// |send()|/|sendmsg()|. (There is no way of suppressing |SIGPIPE| on -// |write()|/|writev().) On Mac, |SIGPIPE| is suppressed by setting the -// |SO_NOSIGPIPE| option on the socket. -// -// Performance notes: -// - On Linux, we have to use |send()|/|sendmsg()| rather than -// |write()|/|writev()| in order to suppress |SIGPIPE|. This is okay, since -// |send()| is (slightly) faster than |write()| (!), while |sendmsg()| is -// quite comparable to |writev()|. -// - On Mac, we may use |write()|/|writev()|. Here, |write()| is considerably -// faster than |send()|, whereas |sendmsg()| is quite comparable to -// |writev()|. -// - On both platforms, an appropriate |sendmsg()|/|writev()| is considerably -// faster than two |send()|s/|write()|s. -// - Relative numbers (minimum real times from 10 runs) for one |write()| of -// 1032 bytes, one |send()| of 1032 bytes, one |writev()| of 32+1000 bytes, -// one |sendmsg()| of 32+1000 bytes, two |write()|s of 32 and 1000 bytes, two -// |send()|s of 32 and 1000 bytes: -// - Linux: 0.81 s, 0.77 s, 0.87 s, 0.89 s, 1.31 s, 1.22 s -// - Mac: 2.21 s, 2.91 s, 2.98 s, 3.08 s, 3.59 s, 4.74 s - -// Flags to use with calling |send()| or |sendmsg()| (see above). -#if defined(OS_MACOSX) -const int kSendFlags = 0; -#else -const int kSendFlags = MSG_NOSIGNAL; -#endif - -ssize_t PlatformChannelWrite(PlatformHandle h, - const void* bytes, - size_t num_bytes) { - DCHECK(h.is_valid()); - DCHECK(bytes); - DCHECK_GT(num_bytes, 0u); - -#if defined(OS_MACOSX) - return HANDLE_EINTR(write(h.fd, bytes, num_bytes)); -#else - return send(h.fd, bytes, num_bytes, kSendFlags); -#endif -} - -ssize_t PlatformChannelWritev(PlatformHandle h, - struct iovec* iov, - size_t num_iov) { - DCHECK(h.is_valid()); - DCHECK(iov); - DCHECK_GT(num_iov, 0u); - -#if defined(OS_MACOSX) - return HANDLE_EINTR(writev(h.fd, iov, static_cast<int>(num_iov))); -#else - struct msghdr msg = {}; - msg.msg_iov = iov; - msg.msg_iovlen = num_iov; - return HANDLE_EINTR(sendmsg(h.fd, &msg, kSendFlags)); -#endif -} - -ssize_t PlatformChannelSendmsgWithHandles(PlatformHandle h, - struct iovec* iov, - size_t num_iov, - PlatformHandle* platform_handles, - size_t num_platform_handles) { - DCHECK(iov); - DCHECK_GT(num_iov, 0u); - DCHECK(platform_handles); - DCHECK_GT(num_platform_handles, 0u); - DCHECK_LE(num_platform_handles, kPlatformChannelMaxNumHandles); - - char cmsg_buf[CMSG_SPACE(kPlatformChannelMaxNumHandles * sizeof(int))]; - struct msghdr msg = {}; - msg.msg_iov = iov; - msg.msg_iovlen = num_iov; - msg.msg_control = cmsg_buf; - msg.msg_controllen = CMSG_LEN(num_platform_handles * sizeof(int)); - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(num_platform_handles * sizeof(int)); - for (size_t i = 0; i < num_platform_handles; i++) { - DCHECK(platform_handles[i].is_valid()); - reinterpret_cast<int*>(CMSG_DATA(cmsg))[i] = platform_handles[i].fd; - } - - return HANDLE_EINTR(sendmsg(h.fd, &msg, kSendFlags)); -} - -bool PlatformChannelSendHandles(PlatformHandle h, - PlatformHandle* handles, - size_t num_handles) { - DCHECK(handles); - DCHECK_GT(num_handles, 0u); - DCHECK_LE(num_handles, kPlatformChannelMaxNumHandles); - - // Note: |sendmsg()| fails on Mac if we don't write at least one character. - struct iovec iov = {const_cast<char*>(""), 1}; - char cmsg_buf[CMSG_SPACE(kPlatformChannelMaxNumHandles * sizeof(int))]; - struct msghdr msg = {}; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = cmsg_buf; - msg.msg_controllen = CMSG_LEN(num_handles * sizeof(int)); - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(num_handles * sizeof(int)); - for (size_t i = 0; i < num_handles; i++) { - DCHECK(handles[i].is_valid()); - reinterpret_cast<int*>(CMSG_DATA(cmsg))[i] = handles[i].fd; - } - - ssize_t result = HANDLE_EINTR(sendmsg(h.fd, &msg, kSendFlags)); - if (result < 1) { - DCHECK_EQ(result, -1); - return false; - } - - for (size_t i = 0; i < num_handles; i++) - handles[i].CloseIfNecessary(); - return true; -} - -ssize_t PlatformChannelRecvmsg(PlatformHandle h, - void* buf, - size_t num_bytes, - std::deque<PlatformHandle>* platform_handles) { - DCHECK(buf); - DCHECK_GT(num_bytes, 0u); - DCHECK(platform_handles); - - struct iovec iov = {buf, num_bytes}; - char cmsg_buf[CMSG_SPACE(kPlatformChannelMaxNumHandles * sizeof(int))]; - struct msghdr msg = {}; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = cmsg_buf; - msg.msg_controllen = sizeof(cmsg_buf); - - ssize_t result = HANDLE_EINTR(recvmsg(h.fd, &msg, MSG_DONTWAIT)); - if (result < 0) - return result; - - // Success; no control messages. - if (msg.msg_controllen == 0) - return result; - - DCHECK(!(msg.msg_flags & MSG_CTRUNC)); - - for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg; - cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { - size_t payload_length = cmsg->cmsg_len - CMSG_LEN(0); - DCHECK_EQ(payload_length % sizeof(int), 0u); - size_t num_fds = payload_length / sizeof(int); - const int* fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); - for (size_t i = 0; i < num_fds; i++) { - platform_handles->push_back(PlatformHandle(fds[i])); - DCHECK(platform_handles->back().is_valid()); - } - } - } - - return result; -} - -} // namespace embedder -} // namespace mojo diff --git a/mojo/edk/embedder/platform_channel_utils_posix.h b/mojo/edk/embedder/platform_channel_utils_posix.h deleted file mode 100644 index 34efcad..0000000 --- a/mojo/edk/embedder/platform_channel_utils_posix.h +++ /dev/null @@ -1,76 +0,0 @@ -// 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_EDK_EMBEDDER_PLATFORM_CHANNEL_UTILS_POSIX_H_ -#define MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_UTILS_POSIX_H_ - -#include <stddef.h> -#include <sys/types.h> // For |ssize_t|. - -#include <deque> - -#include "base/memory/scoped_ptr.h" -#include "mojo/edk/embedder/platform_handle.h" -#include "mojo/edk/system/system_impl_export.h" - -struct iovec; // Declared in <sys/uio.h>. - -namespace mojo { -namespace embedder { - -// The maximum number of handles that can be sent "at once" using -// |PlatformChannelSendmsgWithHandles()|. -// TODO(vtl): This number is taken from ipc/file_descriptor_set_posix.h: -// |FileDescriptorSet::kMaxDescriptorsPerMessage|. Where does it come from? -const size_t kPlatformChannelMaxNumHandles = 7; - -// Use these to write to a socket created using |PlatformChannelPair| (or -// equivalent). These are like |write()| and |writev()|, but handle |EINTR| and -// never raise |SIGPIPE|. (Note: On Mac, the suppression of |SIGPIPE| is set up -// by |PlatformChannelPair|.) -MOJO_SYSTEM_IMPL_EXPORT ssize_t -PlatformChannelWrite(PlatformHandle h, const void* bytes, size_t num_bytes); -MOJO_SYSTEM_IMPL_EXPORT ssize_t -PlatformChannelWritev(PlatformHandle h, struct iovec* iov, size_t num_iov); - -// Writes data, and the given set of |PlatformHandle|s (i.e., file descriptors) -// over the Unix domain socket given by |h| (e.g., created using -// |PlatformChannelPair()|). All the handles must be valid, and there must be at -// least one and at most |kPlatformChannelMaxNumHandles| handles. The return -// value is as for |sendmsg()|, namely -1 on failure and otherwise the number of -// bytes of data sent on success (note that this may not be all the data -// specified by |iov|). (The handles are not closed, regardless of success or -// failure.) -MOJO_SYSTEM_IMPL_EXPORT ssize_t -PlatformChannelSendmsgWithHandles(PlatformHandle h, - struct iovec* iov, - size_t num_iov, - PlatformHandle* platform_handles, - size_t num_platform_handles); - -// TODO(vtl): Remove this once I've switched things over to -// |PlatformChannelSendmsgWithHandles()|. -// Sends |PlatformHandle|s (i.e., file descriptors) over the Unix domain socket -// (e.g., created using PlatformChannelPair|). (These will be sent in a single -// message having one null byte of data and one control message header with all -// the file descriptors.) All of the handles must be valid, and there must be at -// most |kPlatformChannelMaxNumHandles| (and at least one handle). Returns true -// on success, in which case it closes all the handles. -MOJO_SYSTEM_IMPL_EXPORT bool PlatformChannelSendHandles(PlatformHandle h, - PlatformHandle* handles, - size_t num_handles); - -// Wrapper around |recvmsg()|, which will extract any attached file descriptors -// (in the control message) to |PlatformHandle|s (and append them to -// |platform_handles|). (This also handles |EINTR|.) -MOJO_SYSTEM_IMPL_EXPORT ssize_t -PlatformChannelRecvmsg(PlatformHandle h, - void* buf, - size_t num_bytes, - std::deque<PlatformHandle>* platform_handles); - -} // namespace embedder -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_UTILS_POSIX_H_ diff --git a/mojo/edk/embedder/platform_handle.cc b/mojo/edk/embedder/platform_handle.cc deleted file mode 100644 index 4675714..0000000 --- a/mojo/edk/embedder/platform_handle.cc +++ /dev/null @@ -1,40 +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/edk/embedder/platform_handle.h" - -#include "build/build_config.h" -#if defined(OS_POSIX) -#include <unistd.h> -#elif defined(OS_WIN) -#include <windows.h> -#else -#error "Platform not yet supported." -#endif - -#include "base/compiler_specific.h" -#include "base/logging.h" - -namespace mojo { -namespace embedder { - -void PlatformHandle::CloseIfNecessary() { - if (!is_valid()) - return; - -#if defined(OS_POSIX) - bool success = (close(fd) == 0); - DPCHECK(success); - fd = -1; -#elif defined(OS_WIN) - bool success = !!CloseHandle(handle); - DPCHECK(success); - handle = INVALID_HANDLE_VALUE; -#else -#error "Platform not yet supported." -#endif -} - -} // namespace embedder -} // namespace mojo diff --git a/mojo/edk/embedder/platform_handle.h b/mojo/edk/embedder/platform_handle.h deleted file mode 100644 index 346301a..0000000 --- a/mojo/edk/embedder/platform_handle.h +++ /dev/null @@ -1,47 +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_EDK_EMBEDDER_PLATFORM_HANDLE_H_ -#define MOJO_EDK_EMBEDDER_PLATFORM_HANDLE_H_ - -#include "build/build_config.h" -#include "mojo/edk/system/system_impl_export.h" - -#if defined(OS_WIN) -#include <windows.h> -#endif - -namespace mojo { -namespace embedder { - -#if defined(OS_POSIX) -struct MOJO_SYSTEM_IMPL_EXPORT PlatformHandle { - PlatformHandle() : fd(-1) {} - explicit PlatformHandle(int fd) : fd(fd) {} - - void CloseIfNecessary(); - - bool is_valid() const { return fd != -1; } - - int fd; -}; -#elif defined(OS_WIN) -struct MOJO_SYSTEM_IMPL_EXPORT PlatformHandle { - PlatformHandle() : handle(INVALID_HANDLE_VALUE) {} - explicit PlatformHandle(HANDLE handle) : handle(handle) {} - - void CloseIfNecessary(); - - bool is_valid() const { return handle != INVALID_HANDLE_VALUE; } - - HANDLE handle; -}; -#else -#error "Platform not yet supported." -#endif - -} // namespace embedder -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_PLATFORM_HANDLE_H_ diff --git a/mojo/edk/embedder/platform_handle_utils.h b/mojo/edk/embedder/platform_handle_utils.h deleted file mode 100644 index b3d7a79..0000000 --- a/mojo/edk/embedder/platform_handle_utils.h +++ /dev/null @@ -1,33 +0,0 @@ -// 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_EDK_EMBEDDER_PLATFORM_HANDLE_UTILS_H_ -#define MOJO_EDK_EMBEDDER_PLATFORM_HANDLE_UTILS_H_ - -#include "mojo/edk/embedder/platform_handle.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace embedder { - -// Closes all the |PlatformHandle|s in the given container. -template <typename PlatformHandleContainer> -MOJO_SYSTEM_IMPL_EXPORT inline void CloseAllPlatformHandles( - PlatformHandleContainer* platform_handles) { - for (typename PlatformHandleContainer::iterator it = - platform_handles->begin(); - it != platform_handles->end(); ++it) - it->CloseIfNecessary(); -} - -// Duplicates the given |PlatformHandle| (which must be valid). (Returns an -// invalid |ScopedPlatformHandle| on failure.) -MOJO_SYSTEM_IMPL_EXPORT ScopedPlatformHandle -DuplicatePlatformHandle(PlatformHandle platform_handle); - -} // namespace embedder -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_PLATFORM_HANDLE_UTILS_H_ diff --git a/mojo/edk/embedder/platform_handle_utils_posix.cc b/mojo/edk/embedder/platform_handle_utils_posix.cc deleted file mode 100644 index b3be93f..0000000 --- a/mojo/edk/embedder/platform_handle_utils_posix.cc +++ /dev/null @@ -1,22 +0,0 @@ -// 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/edk/embedder/platform_handle_utils.h" - -#include <unistd.h> - -#include "base/logging.h" - -namespace mojo { -namespace embedder { - -ScopedPlatformHandle DuplicatePlatformHandle(PlatformHandle platform_handle) { - DCHECK(platform_handle.is_valid()); - // Note that |dup()| returns -1 on error (which is exactly the value we use - // for invalid |PlatformHandle| FDs). - return ScopedPlatformHandle(PlatformHandle(dup(platform_handle.fd))); -} - -} // namespace embedder -} // namespace mojo diff --git a/mojo/edk/embedder/platform_handle_utils_win.cc b/mojo/edk/embedder/platform_handle_utils_win.cc deleted file mode 100644 index 43d1331..0000000 --- a/mojo/edk/embedder/platform_handle_utils_win.cc +++ /dev/null @@ -1,27 +0,0 @@ -// 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/edk/embedder/platform_handle_utils.h" - -#include <windows.h> - -#include "base/logging.h" - -namespace mojo { -namespace embedder { - -ScopedPlatformHandle DuplicatePlatformHandle(PlatformHandle platform_handle) { - DCHECK(platform_handle.is_valid()); - - HANDLE new_handle; - if (!DuplicateHandle(GetCurrentProcess(), platform_handle.handle, - GetCurrentProcess(), &new_handle, 0, TRUE, - DUPLICATE_SAME_ACCESS)) - return ScopedPlatformHandle(); - DCHECK_NE(new_handle, INVALID_HANDLE_VALUE); - return ScopedPlatformHandle(PlatformHandle(new_handle)); -} - -} // namespace embedder -} // namespace mojo diff --git a/mojo/edk/embedder/platform_handle_vector.h b/mojo/edk/embedder/platform_handle_vector.h deleted file mode 100644 index 96356b7..0000000 --- a/mojo/edk/embedder/platform_handle_vector.h +++ /dev/null @@ -1,35 +0,0 @@ -// 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_EDK_EMBEDDER_PLATFORM_HANDLE_VECTOR_H_ -#define MOJO_EDK_EMBEDDER_PLATFORM_HANDLE_VECTOR_H_ - -#include <vector> - -#include "base/memory/scoped_ptr.h" -#include "mojo/edk/embedder/platform_handle.h" -#include "mojo/edk/embedder/platform_handle_utils.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace embedder { - -typedef std::vector<PlatformHandle> PlatformHandleVector; - -// A deleter (for use with |scoped_ptr|) which closes all handles and then -// |delete|s the |PlatformHandleVector|. -struct MOJO_SYSTEM_IMPL_EXPORT PlatformHandleVectorDeleter { - void operator()(PlatformHandleVector* platform_handles) const { - CloseAllPlatformHandles(platform_handles); - delete platform_handles; - } -}; - -typedef scoped_ptr<PlatformHandleVector, PlatformHandleVectorDeleter> - ScopedPlatformHandleVectorPtr; - -} // namespace embedder -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_PLATFORM_HANDLE_VECTOR_H_ diff --git a/mojo/edk/embedder/platform_shared_buffer.h b/mojo/edk/embedder/platform_shared_buffer.h deleted file mode 100644 index 07f8b90..0000000 --- a/mojo/edk/embedder/platform_shared_buffer.h +++ /dev/null @@ -1,102 +0,0 @@ -// 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_EDK_EMBEDDER_PLATFORM_SHARED_BUFFER_H_ -#define MOJO_EDK_EMBEDDER_PLATFORM_SHARED_BUFFER_H_ - -#include <stddef.h> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace embedder { - -class PlatformSharedBufferMapping; - -// |PlatformSharedBuffer| is an interface for a thread-safe, ref-counted wrapper -// around OS-specific shared memory. It has the following features: -// - A |PlatformSharedBuffer| simply represents a piece of shared memory that -// *may* be mapped and *may* be shared to another process. -// - A single |PlatformSharedBuffer| may be mapped multiple times. The -// lifetime of the mapping (owned by |PlatformSharedBufferMapping|) is -// separate from the lifetime of the |PlatformSharedBuffer|. -// - Sizes/offsets (of the shared memory and mappings) are arbitrary, and not -// restricted by page size. However, more memory may actually be mapped than -// requested. -// -// It currently does NOT support the following: -// - Sharing read-only. (This will probably eventually be supported.) -// -// TODO(vtl): Rectify this with |base::SharedMemory|. -class MOJO_SYSTEM_IMPL_EXPORT PlatformSharedBuffer - : public base::RefCountedThreadSafe<PlatformSharedBuffer> { - public: - // Gets the size of shared buffer (in number of bytes). - virtual size_t GetNumBytes() const = 0; - - // Maps (some) of the shared buffer into memory; [|offset|, |offset + length|] - // must be contained in [0, |num_bytes|], and |length| must be at least 1. - // Returns null on failure. - virtual scoped_ptr<PlatformSharedBufferMapping> Map(size_t offset, - size_t length) = 0; - - // Checks if |offset| and |length| are valid arguments. - virtual bool IsValidMap(size_t offset, size_t length) = 0; - - // Like |Map()|, but doesn't check its arguments (which should have been - // preflighted using |IsValidMap()|). - virtual scoped_ptr<PlatformSharedBufferMapping> MapNoCheck(size_t offset, - size_t length) = 0; - - // Duplicates the underlying platform handle and passes it to the caller. - // TODO(vtl): On POSIX, we'll need two FDs to support sharing read-only. - virtual ScopedPlatformHandle DuplicatePlatformHandle() = 0; - - // Passes the underlying platform handle to the caller. This should only be - // called if there's a unique reference to this object (owned by the caller). - // After calling this, this object should no longer be used, but should only - // be disposed of. - virtual ScopedPlatformHandle PassPlatformHandle() = 0; - - protected: - friend class base::RefCountedThreadSafe<PlatformSharedBuffer>; - - PlatformSharedBuffer() {} - virtual ~PlatformSharedBuffer() {} - - private: - DISALLOW_COPY_AND_ASSIGN(PlatformSharedBuffer); -}; - -// An interface for a mapping of a |PlatformSharedBuffer| (compararable to a -// "file view" in Windows); see above. Created by (implementations of) -// |PlatformSharedBuffer::Map()|. Automatically unmaps memory on destruction. -// -// Mappings are NOT thread-safe. -// -// Note: This is an entirely separate class (instead of -// |PlatformSharedBuffer::Mapping|) so that it can be forward-declared. -class MOJO_SYSTEM_IMPL_EXPORT PlatformSharedBufferMapping { - public: - // IMPORTANT: Implementations must implement a destructor that unmaps memory. - virtual ~PlatformSharedBufferMapping() {} - - virtual void* GetBase() const = 0; - virtual size_t GetLength() const = 0; - - protected: - PlatformSharedBufferMapping() {} - - private: - DISALLOW_COPY_AND_ASSIGN(PlatformSharedBufferMapping); -}; - -} // namespace embedder -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_PLATFORM_SHARED_BUFFER_H_ diff --git a/mojo/edk/embedder/platform_support.h b/mojo/edk/embedder/platform_support.h deleted file mode 100644 index 4556ee3..0000000 --- a/mojo/edk/embedder/platform_support.h +++ /dev/null @@ -1,40 +0,0 @@ -// 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_EDK_EMBEDDER_PLATFORM_SUPPORT_H_ -#define MOJO_EDK_EMBEDDER_PLATFORM_SUPPORT_H_ - -#include <stddef.h> - -#include "base/macros.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace embedder { - -class PlatformSharedBuffer; - -// This class is provided by the embedder to implement (typically -// platform-dependent) things needed by the Mojo system implementation. -class MOJO_SYSTEM_IMPL_EXPORT PlatformSupport { - public: - virtual ~PlatformSupport() {} - - virtual PlatformSharedBuffer* CreateSharedBuffer(size_t num_bytes) = 0; - virtual PlatformSharedBuffer* CreateSharedBufferFromHandle( - size_t num_bytes, - ScopedPlatformHandle platform_handle) = 0; - - protected: - PlatformSupport() {} - - private: - DISALLOW_COPY_AND_ASSIGN(PlatformSupport); -}; - -} // namespace embedder -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_PLATFORM_SUPPORT_H_ diff --git a/mojo/edk/embedder/scoped_platform_handle.h b/mojo/edk/embedder/scoped_platform_handle.h deleted file mode 100644 index 2919b04..0000000 --- a/mojo/edk/embedder/scoped_platform_handle.h +++ /dev/null @@ -1,59 +0,0 @@ -// 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_EDK_EMBEDDER_SCOPED_PLATFORM_HANDLE_H_ -#define MOJO_EDK_EMBEDDER_SCOPED_PLATFORM_HANDLE_H_ - -#include "base/compiler_specific.h" -#include "base/move.h" -#include "mojo/edk/embedder/platform_handle.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace embedder { - -class MOJO_SYSTEM_IMPL_EXPORT ScopedPlatformHandle { - MOVE_ONLY_TYPE_FOR_CPP_03(ScopedPlatformHandle, RValue) - - public: - ScopedPlatformHandle() {} - explicit ScopedPlatformHandle(PlatformHandle handle) : handle_(handle) {} - ~ScopedPlatformHandle() { handle_.CloseIfNecessary(); } - - // Move-only constructor and operator=. - ScopedPlatformHandle(RValue other) : handle_(other.object->release()) {} - ScopedPlatformHandle& operator=(RValue other) { - handle_ = other.object->release(); - return *this; - } - - const PlatformHandle& get() const { return handle_; } - - void swap(ScopedPlatformHandle& other) { - PlatformHandle temp = handle_; - handle_ = other.handle_; - other.handle_ = temp; - } - - PlatformHandle release() WARN_UNUSED_RESULT { - PlatformHandle rv = handle_; - handle_ = PlatformHandle(); - return rv; - } - - void reset(PlatformHandle handle = PlatformHandle()) { - handle_.CloseIfNecessary(); - handle_ = handle; - } - - bool is_valid() const { return handle_.is_valid(); } - - private: - PlatformHandle handle_; -}; - -} // namespace embedder -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_SCOPED_PLATFORM_HANDLE_H_ diff --git a/mojo/edk/embedder/simple_platform_shared_buffer.cc b/mojo/edk/embedder/simple_platform_shared_buffer.cc deleted file mode 100644 index 866250c..0000000 --- a/mojo/edk/embedder/simple_platform_shared_buffer.cc +++ /dev/null @@ -1,108 +0,0 @@ -// 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/edk/embedder/simple_platform_shared_buffer.h" - -#include "base/logging.h" -#include "mojo/edk/embedder/platform_handle_utils.h" - -namespace mojo { -namespace embedder { - -// static -SimplePlatformSharedBuffer* SimplePlatformSharedBuffer::Create( - size_t num_bytes) { - DCHECK_GT(num_bytes, 0u); - - SimplePlatformSharedBuffer* rv = new SimplePlatformSharedBuffer(num_bytes); - if (!rv->Init()) { - // We can't just delete it directly, due to the "in destructor" (debug) - // check. - scoped_refptr<SimplePlatformSharedBuffer> deleter(rv); - return nullptr; - } - - return rv; -} - -// static -SimplePlatformSharedBuffer* -SimplePlatformSharedBuffer::CreateFromPlatformHandle( - size_t num_bytes, - ScopedPlatformHandle platform_handle) { - DCHECK_GT(num_bytes, 0u); - - SimplePlatformSharedBuffer* rv = new SimplePlatformSharedBuffer(num_bytes); - if (!rv->InitFromPlatformHandle(platform_handle.Pass())) { - // We can't just delete it directly, due to the "in destructor" (debug) - // check. - scoped_refptr<SimplePlatformSharedBuffer> deleter(rv); - return nullptr; - } - - return rv; -} - -size_t SimplePlatformSharedBuffer::GetNumBytes() const { - return num_bytes_; -} - -scoped_ptr<PlatformSharedBufferMapping> SimplePlatformSharedBuffer::Map( - size_t offset, - size_t length) { - if (!IsValidMap(offset, length)) - return nullptr; - - return MapNoCheck(offset, length); -} - -bool SimplePlatformSharedBuffer::IsValidMap(size_t offset, size_t length) { - if (offset > num_bytes_ || length == 0) - return false; - - // Note: This is an overflow-safe check of |offset + length > num_bytes_| - // (that |num_bytes >= offset| is verified above). - if (length > num_bytes_ - offset) - return false; - - return true; -} - -scoped_ptr<PlatformSharedBufferMapping> SimplePlatformSharedBuffer::MapNoCheck( - size_t offset, - size_t length) { - DCHECK(IsValidMap(offset, length)); - return MapImpl(offset, length); -} - -ScopedPlatformHandle SimplePlatformSharedBuffer::DuplicatePlatformHandle() { - return mojo::embedder::DuplicatePlatformHandle(handle_.get()); -} - -ScopedPlatformHandle SimplePlatformSharedBuffer::PassPlatformHandle() { - DCHECK(HasOneRef()); - return handle_.Pass(); -} - -SimplePlatformSharedBuffer::SimplePlatformSharedBuffer(size_t num_bytes) - : num_bytes_(num_bytes) { -} - -SimplePlatformSharedBuffer::~SimplePlatformSharedBuffer() { -} - -SimplePlatformSharedBufferMapping::~SimplePlatformSharedBufferMapping() { - Unmap(); -} - -void* SimplePlatformSharedBufferMapping::GetBase() const { - return base_; -} - -size_t SimplePlatformSharedBufferMapping::GetLength() const { - return length_; -} - -} // namespace embedder -} // namespace mojo diff --git a/mojo/edk/embedder/simple_platform_shared_buffer.h b/mojo/edk/embedder/simple_platform_shared_buffer.h deleted file mode 100644 index b4958b9..0000000 --- a/mojo/edk/embedder/simple_platform_shared_buffer.h +++ /dev/null @@ -1,101 +0,0 @@ -// 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_EDK_EMBEDDER_SIMPLE_PLATFORM_SHARED_BUFFER_H_ -#define MOJO_EDK_EMBEDDER_SIMPLE_PLATFORM_SHARED_BUFFER_H_ - -#include <stddef.h> - -#include "base/macros.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace embedder { - -// A simple implementation of |PlatformSharedBuffer|. -class MOJO_SYSTEM_IMPL_EXPORT SimplePlatformSharedBuffer - : public PlatformSharedBuffer { - public: - // Creates a shared buffer of size |num_bytes| bytes (initially zero-filled). - // |num_bytes| must be nonzero. Returns null on failure. - static SimplePlatformSharedBuffer* Create(size_t num_bytes); - - static SimplePlatformSharedBuffer* CreateFromPlatformHandle( - size_t num_bytes, - ScopedPlatformHandle platform_handle); - - // |PlatformSharedBuffer| implementation: - size_t GetNumBytes() const override; - scoped_ptr<PlatformSharedBufferMapping> Map(size_t offset, - size_t length) override; - bool IsValidMap(size_t offset, size_t length) override; - scoped_ptr<PlatformSharedBufferMapping> MapNoCheck(size_t offset, - size_t length) override; - ScopedPlatformHandle DuplicatePlatformHandle() override; - ScopedPlatformHandle PassPlatformHandle() override; - - private: - explicit SimplePlatformSharedBuffer(size_t num_bytes); - ~SimplePlatformSharedBuffer() override; - - // Implemented in simple_platform_shared_buffer_{posix,win}.cc: - - // This is called by |Create()| before this object is given to anyone. - bool Init(); - - // This is like |Init()|, but for |CreateFromPlatformHandle()|. (Note: It - // should verify that |platform_handle| is an appropriate handle for the - // claimed |num_bytes_|.) - bool InitFromPlatformHandle(ScopedPlatformHandle platform_handle); - - // The platform-dependent part of |Map()|; doesn't check arguments. - scoped_ptr<PlatformSharedBufferMapping> MapImpl(size_t offset, size_t length); - - const size_t num_bytes_; - - // This is set in |Init()|/|InitFromPlatformHandle()| and never modified - // (except by |PassPlatformHandle()|; see the comments above its declaration), - // hence does not need to be protected by a lock. - ScopedPlatformHandle handle_; - - DISALLOW_COPY_AND_ASSIGN(SimplePlatformSharedBuffer); -}; - -// An implementation of |PlatformSharedBufferMapping|, produced by -// |SimplePlatformSharedBuffer|. -class MOJO_SYSTEM_IMPL_EXPORT SimplePlatformSharedBufferMapping - : public PlatformSharedBufferMapping { - public: - ~SimplePlatformSharedBufferMapping() override; - - void* GetBase() const override; - size_t GetLength() const override; - - private: - friend class SimplePlatformSharedBuffer; - - SimplePlatformSharedBufferMapping(void* base, - size_t length, - void* real_base, - size_t real_length) - : base_(base), - length_(length), - real_base_(real_base), - real_length_(real_length) {} - void Unmap(); - - void* const base_; - const size_t length_; - - void* const real_base_; - const size_t real_length_; - - DISALLOW_COPY_AND_ASSIGN(SimplePlatformSharedBufferMapping); -}; - -} // namespace embedder -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_SIMPLE_PLATFORM_SHARED_BUFFER_H_ diff --git a/mojo/edk/embedder/simple_platform_shared_buffer_android.cc b/mojo/edk/embedder/simple_platform_shared_buffer_android.cc deleted file mode 100644 index 3517db3..0000000 --- a/mojo/edk/embedder/simple_platform_shared_buffer_android.cc +++ /dev/null @@ -1,74 +0,0 @@ -// 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/edk/embedder/simple_platform_shared_buffer.h" - -#include <stdint.h> -#include <sys/mman.h> // For |mmap()|/|munmap()|. -#include <sys/types.h> // For |off_t|. -#include <unistd.h> - -#include <limits> - -#include "base/files/scoped_file.h" -#include "base/logging.h" -#include "base/macros.h" -#include "mojo/edk/embedder/platform_handle.h" -#include "third_party/ashmem/ashmem.h" - -namespace mojo { -namespace embedder { - -// SimplePlatformSharedBuffer -------------------------------------------------- - -bool SimplePlatformSharedBuffer::Init() { - DCHECK(!handle_.is_valid()); - - if (static_cast<uint64_t>(num_bytes_) > - static_cast<uint64_t>(std::numeric_limits<off_t>::max())) { - return false; - } - - base::ScopedFD fd(ashmem_create_region(nullptr, num_bytes_)); - if (!fd.is_valid()) { - DPLOG(ERROR) << "ashmem_create_region()"; - return false; - } - - if (ashmem_set_prot_region(fd.get(), PROT_READ | PROT_WRITE) < 0) { - DPLOG(ERROR) << "ashmem_set_prot_region()"; - return false; - } - - handle_.reset(PlatformHandle(fd.release())); - return true; -} - -bool SimplePlatformSharedBuffer::InitFromPlatformHandle( - ScopedPlatformHandle platform_handle) { - DCHECK(!handle_.is_valid()); - - if (static_cast<uint64_t>(num_bytes_) > - static_cast<uint64_t>(std::numeric_limits<off_t>::max())) { - return false; - } - - int size = ashmem_get_size_region(platform_handle.get().fd); - - if (size < 0) { - DPLOG(ERROR) << "ashmem_get_size_region()"; - return false; - } - - if (static_cast<size_t>(size) != num_bytes_) { - LOG(ERROR) << "Shared memory region has the wrong size"; - return false; - } - - handle_ = platform_handle.Pass(); - return true; -} - -} // namespace embedder -} // namespace mojo diff --git a/mojo/edk/embedder/simple_platform_shared_buffer_posix.cc b/mojo/edk/embedder/simple_platform_shared_buffer_posix.cc deleted file mode 100644 index a3d10ef..0000000 --- a/mojo/edk/embedder/simple_platform_shared_buffer_posix.cc +++ /dev/null @@ -1,159 +0,0 @@ -// 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/edk/embedder/simple_platform_shared_buffer.h" - -#include <stdint.h> -#include <stdio.h> // For |fileno()|. -#include <sys/mman.h> // For |mmap()|/|munmap()|. -#include <sys/stat.h> -#include <sys/types.h> // For |off_t|. -#include <unistd.h> - -#include <limits> - -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/files/scoped_file.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/posix/eintr_wrapper.h" -#include "base/sys_info.h" -#include "base/threading/thread_restrictions.h" -#include "mojo/edk/embedder/platform_handle.h" - -// We assume that |size_t| and |off_t| (type for |ftruncate()|) fits in a -// |uint64_t|. -static_assert(sizeof(size_t) <= sizeof(uint64_t), "size_t too big"); -static_assert(sizeof(off_t) <= sizeof(uint64_t), "off_t too big"); - -namespace mojo { -namespace embedder { - -// SimplePlatformSharedBuffer -------------------------------------------------- - -// The implementation for android uses ashmem to generate the file descriptor -// for the shared memory. See simple_platform_shared_buffer_android.cc -#if !defined(OS_ANDROID) - -bool SimplePlatformSharedBuffer::Init() { - DCHECK(!handle_.is_valid()); - - base::ThreadRestrictions::ScopedAllowIO allow_io; - - if (static_cast<uint64_t>(num_bytes_) > - static_cast<uint64_t>(std::numeric_limits<off_t>::max())) { - return false; - } - - // TODO(vtl): This is stupid. The implementation of - // |CreateAndOpenTemporaryFileInDir()| starts with an FD, |fdopen()|s to get a - // |FILE*|, and then we have to |dup(fileno(fp))| to get back to an FD that we - // can own. (base/memory/shared_memory_posix.cc does this too, with more - // |fstat()|s thrown in for good measure.) - base::FilePath shared_buffer_dir; - if (!base::GetShmemTempDir(false, &shared_buffer_dir)) { - LOG(ERROR) << "Failed to get temporary directory for shared memory"; - return false; - } - base::FilePath shared_buffer_file; - base::ScopedFILE fp(base::CreateAndOpenTemporaryFileInDir( - shared_buffer_dir, &shared_buffer_file)); - if (!fp) { - LOG(ERROR) << "Failed to create/open temporary file for shared memory"; - return false; - } - // Note: |unlink()| is not interruptible. - if (unlink(shared_buffer_file.value().c_str()) != 0) { - PLOG(WARNING) << "unlink"; - // This isn't "fatal" (e.g., someone else may have unlinked the file first), - // so we may as well continue. - } - - // Note: |dup()| is not interruptible (but |dup2()|/|dup3()| are). - base::ScopedFD fd(dup(fileno(fp.get()))); - if (!fd.is_valid()) { - PLOG(ERROR) << "dup"; - return false; - } - - if (HANDLE_EINTR(ftruncate(fd.get(), static_cast<off_t>(num_bytes_))) != 0) { - PLOG(ERROR) << "ftruncate"; - return false; - } - - handle_.reset(PlatformHandle(fd.release())); - return true; -} - -bool SimplePlatformSharedBuffer::InitFromPlatformHandle( - ScopedPlatformHandle platform_handle) { - DCHECK(!handle_.is_valid()); - - if (static_cast<uint64_t>(num_bytes_) > - static_cast<uint64_t>(std::numeric_limits<off_t>::max())) { - return false; - } - - struct stat sb = {}; - // Note: |fstat()| isn't interruptible. - if (fstat(platform_handle.get().fd, &sb) != 0) { - PLOG(ERROR) << "fstat"; - return false; - } - - if (!S_ISREG(sb.st_mode)) { - LOG(ERROR) << "Platform handle not to a regular file"; - return false; - } - - if (sb.st_size != static_cast<off_t>(num_bytes_)) { - LOG(ERROR) << "Shared memory file has the wrong size"; - return false; - } - - // TODO(vtl): More checks? - - handle_ = platform_handle.Pass(); - return true; -} - -#endif // !defined(OS_ANDROID) - -scoped_ptr<PlatformSharedBufferMapping> SimplePlatformSharedBuffer::MapImpl( - size_t offset, - size_t length) { - size_t offset_rounding = offset % base::SysInfo::VMAllocationGranularity(); - size_t real_offset = offset - offset_rounding; - size_t real_length = length + offset_rounding; - - // This should hold (since we checked |num_bytes| versus the maximum value of - // |off_t| on creation, but it never hurts to be paranoid. - DCHECK_LE(static_cast<uint64_t>(real_offset), - static_cast<uint64_t>(std::numeric_limits<off_t>::max())); - - void* real_base = - mmap(nullptr, real_length, PROT_READ | PROT_WRITE, MAP_SHARED, - handle_.get().fd, static_cast<off_t>(real_offset)); - // |mmap()| should return |MAP_FAILED| (a.k.a. -1) on error. But it shouldn't - // return null either. - if (real_base == MAP_FAILED || !real_base) { - PLOG(ERROR) << "mmap"; - return nullptr; - } - - void* base = static_cast<char*>(real_base) + offset_rounding; - return make_scoped_ptr(new SimplePlatformSharedBufferMapping( - base, length, real_base, real_length)); -} - -// SimplePlatformSharedBufferMapping ------------------------------------------- - -void SimplePlatformSharedBufferMapping::Unmap() { - int result = munmap(real_base_, real_length_); - PLOG_IF(ERROR, result != 0) << "munmap"; -} - -} // namespace embedder -} // namespace mojo diff --git a/mojo/edk/embedder/simple_platform_shared_buffer_unittest.cc b/mojo/edk/embedder/simple_platform_shared_buffer_unittest.cc deleted file mode 100644 index 4644b7b..0000000 --- a/mojo/edk/embedder/simple_platform_shared_buffer_unittest.cc +++ /dev/null @@ -1,187 +0,0 @@ -// 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/edk/embedder/simple_platform_shared_buffer.h" - -#include <limits> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace embedder { -namespace { - -TEST(SimplePlatformSharedBufferTest, Basic) { - const size_t kNumInts = 100; - const size_t kNumBytes = kNumInts * sizeof(int); - // A fudge so that we're not just writing zero bytes 75% of the time. - const int kFudge = 1234567890; - - // Make some memory. - scoped_refptr<SimplePlatformSharedBuffer> buffer( - SimplePlatformSharedBuffer::Create(kNumBytes)); - ASSERT_TRUE(buffer); - - // Map it all, scribble some stuff, and then unmap it. - { - EXPECT_TRUE(buffer->IsValidMap(0, kNumBytes)); - scoped_ptr<PlatformSharedBufferMapping> mapping(buffer->Map(0, kNumBytes)); - ASSERT_TRUE(mapping); - ASSERT_TRUE(mapping->GetBase()); - int* stuff = static_cast<int*>(mapping->GetBase()); - for (size_t i = 0; i < kNumInts; i++) - stuff[i] = static_cast<int>(i) + kFudge; - } - - // Map it all again, check that our scribbling is still there, then do a - // partial mapping and scribble on that, check that everything is coherent, - // unmap the first mapping, scribble on some of the second mapping, and then - // unmap it. - { - ASSERT_TRUE(buffer->IsValidMap(0, kNumBytes)); - // Use |MapNoCheck()| this time. - scoped_ptr<PlatformSharedBufferMapping> mapping1( - buffer->MapNoCheck(0, kNumBytes)); - ASSERT_TRUE(mapping1); - ASSERT_TRUE(mapping1->GetBase()); - int* stuff1 = static_cast<int*>(mapping1->GetBase()); - for (size_t i = 0; i < kNumInts; i++) - EXPECT_EQ(static_cast<int>(i) + kFudge, stuff1[i]) << i; - - scoped_ptr<PlatformSharedBufferMapping> mapping2( - buffer->Map((kNumInts / 2) * sizeof(int), 2 * sizeof(int))); - ASSERT_TRUE(mapping2); - ASSERT_TRUE(mapping2->GetBase()); - int* stuff2 = static_cast<int*>(mapping2->GetBase()); - EXPECT_EQ(static_cast<int>(kNumInts / 2) + kFudge, stuff2[0]); - EXPECT_EQ(static_cast<int>(kNumInts / 2) + 1 + kFudge, stuff2[1]); - - stuff2[0] = 123; - stuff2[1] = 456; - EXPECT_EQ(123, stuff1[kNumInts / 2]); - EXPECT_EQ(456, stuff1[kNumInts / 2 + 1]); - - mapping1.reset(); - - EXPECT_EQ(123, stuff2[0]); - EXPECT_EQ(456, stuff2[1]); - stuff2[1] = 789; - } - - // Do another partial mapping and check that everything is the way we expect - // it to be. - { - EXPECT_TRUE(buffer->IsValidMap(sizeof(int), kNumBytes - sizeof(int))); - scoped_ptr<PlatformSharedBufferMapping> mapping( - buffer->Map(sizeof(int), kNumBytes - sizeof(int))); - ASSERT_TRUE(mapping); - ASSERT_TRUE(mapping->GetBase()); - int* stuff = static_cast<int*>(mapping->GetBase()); - - for (size_t j = 0; j < kNumInts - 1; j++) { - int i = static_cast<int>(j) + 1; - if (i == kNumInts / 2) { - EXPECT_EQ(123, stuff[j]); - } else if (i == kNumInts / 2 + 1) { - EXPECT_EQ(789, stuff[j]); - } else { - EXPECT_EQ(i + kFudge, stuff[j]) << i; - } - } - } -} - -// TODO(vtl): Bigger buffers. - -TEST(SimplePlatformSharedBufferTest, InvalidMappings) { - scoped_refptr<SimplePlatformSharedBuffer> buffer( - SimplePlatformSharedBuffer::Create(100)); - ASSERT_TRUE(buffer); - - // Zero length not allowed. - EXPECT_FALSE(buffer->Map(0, 0)); - EXPECT_FALSE(buffer->IsValidMap(0, 0)); - - // Okay: - EXPECT_TRUE(buffer->Map(0, 100)); - EXPECT_TRUE(buffer->IsValidMap(0, 100)); - // Offset + length too big. - EXPECT_FALSE(buffer->Map(0, 101)); - EXPECT_FALSE(buffer->IsValidMap(0, 101)); - EXPECT_FALSE(buffer->Map(1, 100)); - EXPECT_FALSE(buffer->IsValidMap(1, 100)); - - // Okay: - EXPECT_TRUE(buffer->Map(50, 50)); - EXPECT_TRUE(buffer->IsValidMap(50, 50)); - // Offset + length too big. - EXPECT_FALSE(buffer->Map(50, 51)); - EXPECT_FALSE(buffer->IsValidMap(50, 51)); - EXPECT_FALSE(buffer->Map(51, 50)); - EXPECT_FALSE(buffer->IsValidMap(51, 50)); -} - -TEST(SimplePlatformSharedBufferTest, TooBig) { - // If |size_t| is 32-bit, it's quite possible/likely that |Create()| succeeds - // (since it only involves creating a 4 GB file). - const size_t kMaxSizeT = std::numeric_limits<size_t>::max(); - scoped_refptr<SimplePlatformSharedBuffer> buffer( - SimplePlatformSharedBuffer::Create(kMaxSizeT)); - // But, assuming |sizeof(size_t) == sizeof(void*)|, mapping all of it should - // always fail. - if (buffer) - EXPECT_FALSE(buffer->Map(0, kMaxSizeT)); -} - -// Tests that separate mappings get distinct addresses. -// Note: It's not inconceivable that the OS could ref-count identical mappings -// and reuse the same address, in which case we'd have to be more careful about -// using the address as the key for unmapping. -TEST(SimplePlatformSharedBufferTest, MappingsDistinct) { - scoped_refptr<SimplePlatformSharedBuffer> buffer( - SimplePlatformSharedBuffer::Create(100)); - scoped_ptr<PlatformSharedBufferMapping> mapping1(buffer->Map(0, 100)); - scoped_ptr<PlatformSharedBufferMapping> mapping2(buffer->Map(0, 100)); - EXPECT_NE(mapping1->GetBase(), mapping2->GetBase()); -} - -TEST(SimplePlatformSharedBufferTest, BufferZeroInitialized) { - static const size_t kSizes[] = {10, 100, 1000, 10000, 100000}; - for (size_t i = 0; i < arraysize(kSizes); i++) { - scoped_refptr<SimplePlatformSharedBuffer> buffer( - SimplePlatformSharedBuffer::Create(kSizes[i])); - scoped_ptr<PlatformSharedBufferMapping> mapping(buffer->Map(0, kSizes[i])); - for (size_t j = 0; j < kSizes[i]; j++) { - // "Assert" instead of "expect" so we don't spam the output with thousands - // of failures if we fail. - ASSERT_EQ('\0', static_cast<char*>(mapping->GetBase())[j]) - << "size " << kSizes[i] << ", offset " << j; - } - } -} - -TEST(SimplePlatformSharedBufferTest, MappingsOutliveBuffer) { - scoped_ptr<PlatformSharedBufferMapping> mapping1; - scoped_ptr<PlatformSharedBufferMapping> mapping2; - - { - scoped_refptr<SimplePlatformSharedBuffer> buffer( - SimplePlatformSharedBuffer::Create(100)); - mapping1 = buffer->Map(0, 100).Pass(); - mapping2 = buffer->Map(50, 50).Pass(); - static_cast<char*>(mapping1->GetBase())[50] = 'x'; - } - - EXPECT_EQ('x', static_cast<char*>(mapping2->GetBase())[0]); - - static_cast<char*>(mapping2->GetBase())[1] = 'y'; - EXPECT_EQ('y', static_cast<char*>(mapping1->GetBase())[51]); -} - -} // namespace -} // namespace embedder -} // namespace mojo diff --git a/mojo/edk/embedder/simple_platform_shared_buffer_win.cc b/mojo/edk/embedder/simple_platform_shared_buffer_win.cc deleted file mode 100644 index 0737465..0000000 --- a/mojo/edk/embedder/simple_platform_shared_buffer_win.cc +++ /dev/null @@ -1,89 +0,0 @@ -// 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/edk/embedder/simple_platform_shared_buffer.h" - -#include <windows.h> - -#include <limits> - -#include "base/logging.h" -#include "base/sys_info.h" -#include "mojo/edk/embedder/platform_handle.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" - -namespace mojo { -namespace embedder { - -// SimplePlatformSharedBuffer -------------------------------------------------- - -bool SimplePlatformSharedBuffer::Init() { - DCHECK(!handle_.is_valid()); - - // TODO(vtl): Currently, we only support mapping up to 2^32-1 bytes. - if (static_cast<uint64_t>(num_bytes_) > - static_cast<uint64_t>(std::numeric_limits<DWORD>::max())) { - return false; - } - - // IMPORTANT NOTE: Unnamed objects are NOT SECURABLE. Thus if we ever want to - // share read-only to other processes, we'll have to name our file mapping - // object. - // TODO(vtl): Unlike |base::SharedMemory|, we don't round up the size (to a - // multiple of 64 KB). This may cause problems with NaCl. Cross this bridge - // when we get there. crbug.com/210609 - handle_.reset(PlatformHandle( - CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, - static_cast<DWORD>(num_bytes_), nullptr))); - if (!handle_.is_valid()) { - PLOG(ERROR) << "CreateFileMapping"; - return false; - } - - return true; -} - -bool SimplePlatformSharedBuffer::InitFromPlatformHandle( - ScopedPlatformHandle platform_handle) { - DCHECK(!handle_.is_valid()); - - // TODO(vtl): Implement. - NOTIMPLEMENTED(); - return false; -} - -scoped_ptr<PlatformSharedBufferMapping> SimplePlatformSharedBuffer::MapImpl( - size_t offset, - size_t length) { - size_t offset_rounding = offset % base::SysInfo::VMAllocationGranularity(); - size_t real_offset = offset - offset_rounding; - size_t real_length = length + offset_rounding; - - // This should hold (since we checked |num_bytes| versus the maximum value of - // |off_t| on creation, but it never hurts to be paranoid. - DCHECK_LE(static_cast<uint64_t>(real_offset), - static_cast<uint64_t>(std::numeric_limits<DWORD>::max())); - - void* real_base = - MapViewOfFile(handle_.get().handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, - static_cast<DWORD>(real_offset), real_length); - if (!real_base) { - PLOG(ERROR) << "MapViewOfFile"; - return nullptr; - } - - void* base = static_cast<char*>(real_base) + offset_rounding; - return make_scoped_ptr(new SimplePlatformSharedBufferMapping( - base, length, real_base, real_length)); -} - -// SimplePlatformSharedBufferMapping ------------------------------------------- - -void SimplePlatformSharedBufferMapping::Unmap() { - BOOL result = UnmapViewOfFile(real_base_); - PLOG_IF(ERROR, !result) << "UnmapViewOfFile"; -} - -} // namespace embedder -} // namespace mojo diff --git a/mojo/edk/embedder/simple_platform_support.cc b/mojo/edk/embedder/simple_platform_support.cc deleted file mode 100644 index c59cfb3..0000000 --- a/mojo/edk/embedder/simple_platform_support.cc +++ /dev/null @@ -1,25 +0,0 @@ -// 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/edk/embedder/simple_platform_support.h" - -#include "mojo/edk/embedder/simple_platform_shared_buffer.h" - -namespace mojo { -namespace embedder { - -PlatformSharedBuffer* SimplePlatformSupport::CreateSharedBuffer( - size_t num_bytes) { - return SimplePlatformSharedBuffer::Create(num_bytes); -} - -PlatformSharedBuffer* SimplePlatformSupport::CreateSharedBufferFromHandle( - size_t num_bytes, - ScopedPlatformHandle platform_handle) { - return SimplePlatformSharedBuffer::CreateFromPlatformHandle( - num_bytes, platform_handle.Pass()); -} - -} // namespace embedder -} // namespace mojo diff --git a/mojo/edk/embedder/simple_platform_support.h b/mojo/edk/embedder/simple_platform_support.h deleted file mode 100644 index 2013f8d..0000000 --- a/mojo/edk/embedder/simple_platform_support.h +++ /dev/null @@ -1,37 +0,0 @@ -// 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_EDK_EMBEDDER_SIMPLE_PLATFORM_SUPPORT_H_ -#define MOJO_EDK_EMBEDDER_SIMPLE_PLATFORM_SUPPORT_H_ - -#include "base/macros.h" -#include "mojo/edk/embedder/platform_support.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace embedder { - -// A simple implementation of |PlatformSupport|, when sandboxing and -// multiprocess support are not issues (e.g., in most tests). Note: This class -// has no state, and different instances of |SimplePlatformSupport| are mutually -// compatible (i.e., you don't need to use a single instance of it everywhere -- -// you may simply create one whenever/wherever you need it). -class MOJO_SYSTEM_IMPL_EXPORT SimplePlatformSupport : public PlatformSupport { - public: - SimplePlatformSupport() {} - ~SimplePlatformSupport() override {} - - PlatformSharedBuffer* CreateSharedBuffer(size_t num_bytes) override; - PlatformSharedBuffer* CreateSharedBufferFromHandle( - size_t num_bytes, - ScopedPlatformHandle platform_handle) override; - - private: - DISALLOW_COPY_AND_ASSIGN(SimplePlatformSupport); -}; - -} // namespace embedder -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_SIMPLE_PLATFORM_SUPPORT_H_ diff --git a/mojo/edk/embedder/test_embedder.cc b/mojo/edk/embedder/test_embedder.cc deleted file mode 100644 index defab41..0000000 --- a/mojo/edk/embedder/test_embedder.cc +++ /dev/null @@ -1,63 +0,0 @@ -// 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/edk/embedder/test_embedder.h" - -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/embedder/embedder_internal.h" -#include "mojo/edk/embedder/simple_platform_support.h" -#include "mojo/edk/system/channel_manager.h" -#include "mojo/edk/system/core.h" -#include "mojo/edk/system/handle_table.h" - -namespace mojo { - -namespace system { -namespace internal { - -bool ShutdownCheckNoLeaks(Core* core) { - // No point in taking the lock. - const HandleTable::HandleToEntryMap& handle_to_entry_map = - core->handle_table_.handle_to_entry_map_; - - if (handle_to_entry_map.empty()) - return true; - - for (HandleTable::HandleToEntryMap::const_iterator it = - handle_to_entry_map.begin(); - it != handle_to_entry_map.end(); ++it) { - LOG(ERROR) << "Mojo embedder shutdown: Leaking handle " << (*it).first; - } - return false; -} - -} // namespace internal -} // namespace system - -namespace embedder { -namespace test { - -void InitWithSimplePlatformSupport() { - Init(make_scoped_ptr(new SimplePlatformSupport())); -} - -bool Shutdown() { - CHECK(internal::g_channel_manager); - delete internal::g_channel_manager; - internal::g_channel_manager = nullptr; - - CHECK(internal::g_core); - bool rv = system::internal::ShutdownCheckNoLeaks(internal::g_core); - delete internal::g_core; - internal::g_core = nullptr; - return rv; -} - -} // namespace test -} // namespace embedder - -} // namespace mojo diff --git a/mojo/edk/embedder/test_embedder.h b/mojo/edk/embedder/test_embedder.h deleted file mode 100644 index 76917ee..0000000 --- a/mojo/edk/embedder/test_embedder.h +++ /dev/null @@ -1,32 +0,0 @@ -// 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_EDK_EMBEDDER_TEST_EMBEDDER_H_ -#define MOJO_EDK_EMBEDDER_TEST_EMBEDDER_H_ - -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace embedder { -namespace test { - -// Calls |Init()| with a |SimplePlatformSupport| (use this in tests if, e.g., -// you don't care about sandboxing, etc.). -MOJO_SYSTEM_IMPL_EXPORT void InitWithSimplePlatformSupport(); - -// This shuts down the global, singleton instance. (Note: "Real" embedders are -// not expected to ever shut down this instance. This |Shutdown()| function will -// do more work to ensure that tests don't leak, etc.) Returns true if there -// were no problems, false if there were leaks -- i.e., handles still open -- or -// any other problems. -// -// Note: It is up to the caller to ensure that there are not outstanding -// callbacks from |CreateChannel()| before calling this. -MOJO_SYSTEM_IMPL_EXPORT bool Shutdown(); - -} // namespace test -} // namespace embedder -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_TEST_EMBEDDER_H_ diff --git a/mojo/edk/js/BUILD.gn b/mojo/edk/js/BUILD.gn deleted file mode 100644 index 9a066d3..0000000 --- a/mojo/edk/js/BUILD.gn +++ /dev/null @@ -1,56 +0,0 @@ -# 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. - -import("../mojo_edk.gni") - -mojo_edk_source_set("js") { - sources = [ - "core.cc", - "core.h", - "drain_data.cc", - "drain_data.h", - "handle.cc", - "handle.h", - "handle_close_observer.h", - "mojo_runner_delegate.cc", - "mojo_runner_delegate.h", - "support.cc", - "support.h", - "threading.cc", - "threading.h", - "waiting_callback.cc", - "waiting_callback.h", - ] - - public_deps = [ - "//base", - "//gin", - "//v8", - ] - - mojo_sdk_deps = [ - "mojo/public/cpp/environment", - "mojo/public/cpp/system", - ] -} - -mojo_edk_source_set("js_unittests") { - testonly = true - sources = [ - "handle_unittest.cc", - ] - - deps = [ - "//testing/gtest", - ] - - mojo_edk_deps = [ - "mojo/edk/js", - "mojo/edk/test:test_support", - ] - - mojo_sdk_deps = [ - "mojo/public/cpp/system", - ] -} diff --git a/mojo/edk/js/DEPS b/mojo/edk/js/DEPS deleted file mode 100644 index c350edf..0000000 --- a/mojo/edk/js/DEPS +++ /dev/null @@ -1,5 +0,0 @@ -include_rules = [ - "+base", - "+gin", - "+v8", -] diff --git a/mojo/edk/js/core.cc b/mojo/edk/js/core.cc deleted file mode 100644 index c06977d..0000000 --- a/mojo/edk/js/core.cc +++ /dev/null @@ -1,381 +0,0 @@ -// 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/edk/js/core.h" - -#include "base/bind.h" -#include "base/logging.h" -#include "gin/arguments.h" -#include "gin/array_buffer.h" -#include "gin/converter.h" -#include "gin/dictionary.h" -#include "gin/function_template.h" -#include "gin/handle.h" -#include "gin/object_template_builder.h" -#include "gin/per_isolate_data.h" -#include "gin/public/wrapper_info.h" -#include "gin/wrappable.h" -#include "mojo/edk/js/drain_data.h" -#include "mojo/edk/js/handle.h" - -namespace mojo { -namespace js { - -namespace { - -MojoResult CloseHandle(gin::Handle<HandleWrapper> handle) { - if (!handle->get().is_valid()) - return MOJO_RESULT_INVALID_ARGUMENT; - handle->Close(); - return MOJO_RESULT_OK; -} - -gin::Dictionary WaitHandle(const gin::Arguments& args, - mojo::Handle handle, - MojoHandleSignals signals, - MojoDeadline deadline) { - v8::Isolate* isolate = args.isolate(); - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(isolate); - - MojoHandleSignalsState signals_state; - MojoResult result = mojo::Wait(handle, signals, deadline, &signals_state); - dictionary.Set("result", result); - - mojo::WaitManyResult wmv(result, 0); - if (!wmv.AreSignalsStatesValid()) { - dictionary.Set("signalsState", v8::Null(isolate).As<v8::Value>()); - } else { - gin::Dictionary signalsStateDict = gin::Dictionary::CreateEmpty(isolate); - signalsStateDict.Set("satisfiedSignals", signals_state.satisfied_signals); - signalsStateDict.Set("satisfiableSignals", - signals_state.satisfiable_signals); - dictionary.Set("signalsState", signalsStateDict); - } - - return dictionary; -} - -gin::Dictionary WaitMany(const gin::Arguments& args, - const std::vector<mojo::Handle>& handles, - const std::vector<MojoHandleSignals>& signals, - MojoDeadline deadline) { - v8::Isolate* isolate = args.isolate(); - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(isolate); - - std::vector<MojoHandleSignalsState> signals_states(signals.size()); - mojo::WaitManyResult wmv = - mojo::WaitMany(handles, signals, deadline, &signals_states); - dictionary.Set("result", wmv.result); - if (wmv.IsIndexValid()) { - dictionary.Set("index", wmv.index); - } else { - dictionary.Set("index", v8::Null(isolate).As<v8::Value>()); - } - if (wmv.AreSignalsStatesValid()) { - std::vector<gin::Dictionary> vec; - for (size_t i = 0; i < handles.size(); ++i) { - gin::Dictionary signalsStateDict = gin::Dictionary::CreateEmpty(isolate); - signalsStateDict.Set("satisfiedSignals", - signals_states[i].satisfied_signals); - signalsStateDict.Set("satisfiableSignals", - signals_states[i].satisfiable_signals); - vec.push_back(signalsStateDict); - } - dictionary.Set("signalsState", vec); - } else { - dictionary.Set("signalsState", v8::Null(isolate).As<v8::Value>()); - } - - return dictionary; -} - -gin::Dictionary CreateMessagePipe(const gin::Arguments& args) { - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); - dictionary.Set("result", MOJO_RESULT_INVALID_ARGUMENT); - - MojoHandle handle0 = MOJO_HANDLE_INVALID; - MojoHandle handle1 = MOJO_HANDLE_INVALID; - MojoResult result = MOJO_RESULT_OK; - - v8::Handle<v8::Value> options_value = args.PeekNext(); - if (options_value.IsEmpty() || options_value->IsNull() || - options_value->IsUndefined()) { - result = MojoCreateMessagePipe(NULL, &handle0, &handle1); - } else if (options_value->IsObject()) { - gin::Dictionary options_dict(args.isolate(), options_value->ToObject()); - MojoCreateMessagePipeOptions options; - // For future struct_size, we can probably infer that from the presence of - // properties in options_dict. For now, it's always 8. - options.struct_size = 8; - // Ideally these would be optional. But the interface makes it hard to - // typecheck them then. - if (!options_dict.Get("flags", &options.flags)) { - return dictionary; - } - - result = MojoCreateMessagePipe(&options, &handle0, &handle1); - } else { - return dictionary; - } - - CHECK_EQ(MOJO_RESULT_OK, result); - - dictionary.Set("result", result); - dictionary.Set("handle0", mojo::Handle(handle0)); - dictionary.Set("handle1", mojo::Handle(handle1)); - return dictionary; -} - -MojoResult WriteMessage( - mojo::Handle handle, - const gin::ArrayBufferView& buffer, - const std::vector<gin::Handle<HandleWrapper> >& handles, - MojoWriteMessageFlags flags) { - std::vector<MojoHandle> raw_handles(handles.size()); - for (size_t i = 0; i < handles.size(); ++i) - raw_handles[i] = handles[i]->get().value(); - MojoResult rv = MojoWriteMessage(handle.value(), - buffer.bytes(), - static_cast<uint32_t>(buffer.num_bytes()), - raw_handles.empty() ? NULL : &raw_handles[0], - static_cast<uint32_t>(raw_handles.size()), - flags); - // MojoWriteMessage takes ownership of the handles upon success, so - // release them here. - if (rv == MOJO_RESULT_OK) { - for (size_t i = 0; i < handles.size(); ++i) - ignore_result(handles[i]->release()); - } - return rv; -} - -gin::Dictionary ReadMessage(const gin::Arguments& args, - mojo::Handle handle, - MojoReadMessageFlags flags) { - uint32_t num_bytes = 0; - uint32_t num_handles = 0; - MojoResult result = MojoReadMessage( - handle.value(), NULL, &num_bytes, NULL, &num_handles, flags); - if (result != MOJO_RESULT_RESOURCE_EXHAUSTED) { - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); - dictionary.Set("result", result); - return dictionary; - } - - v8::Handle<v8::ArrayBuffer> array_buffer = - v8::ArrayBuffer::New(args.isolate(), num_bytes); - std::vector<mojo::Handle> handles(num_handles); - - gin::ArrayBuffer buffer; - ConvertFromV8(args.isolate(), array_buffer, &buffer); - CHECK(buffer.num_bytes() == num_bytes); - - result = MojoReadMessage(handle.value(), - buffer.bytes(), - &num_bytes, - handles.empty() ? NULL : - reinterpret_cast<MojoHandle*>(&handles[0]), - &num_handles, - flags); - - CHECK(buffer.num_bytes() == num_bytes); - CHECK(handles.size() == num_handles); - - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); - dictionary.Set("result", result); - dictionary.Set("buffer", array_buffer); - dictionary.Set("handles", handles); - return dictionary; -} - -gin::Dictionary CreateDataPipe(const gin::Arguments& args) { - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); - dictionary.Set("result", MOJO_RESULT_INVALID_ARGUMENT); - - MojoHandle producer_handle = MOJO_HANDLE_INVALID; - MojoHandle consumer_handle = MOJO_HANDLE_INVALID; - MojoResult result = MOJO_RESULT_OK; - - v8::Handle<v8::Value> options_value = args.PeekNext(); - if (options_value.IsEmpty() || options_value->IsNull() || - options_value->IsUndefined()) { - result = MojoCreateDataPipe(NULL, &producer_handle, &consumer_handle); - } else if (options_value->IsObject()) { - gin::Dictionary options_dict(args.isolate(), options_value->ToObject()); - MojoCreateDataPipeOptions options; - // For future struct_size, we can probably infer that from the presence of - // properties in options_dict. For now, it's always 16. - options.struct_size = 16; - // Ideally these would be optional. But the interface makes it hard to - // typecheck them then. - if (!options_dict.Get("flags", &options.flags) || - !options_dict.Get("elementNumBytes", &options.element_num_bytes) || - !options_dict.Get("capacityNumBytes", &options.capacity_num_bytes)) { - return dictionary; - } - - result = MojoCreateDataPipe(&options, &producer_handle, &consumer_handle); - } else { - return dictionary; - } - - CHECK_EQ(MOJO_RESULT_OK, result); - - dictionary.Set("result", result); - dictionary.Set("producerHandle", mojo::Handle(producer_handle)); - dictionary.Set("consumerHandle", mojo::Handle(consumer_handle)); - return dictionary; -} - -gin::Dictionary WriteData(const gin::Arguments& args, - mojo::Handle handle, - const gin::ArrayBufferView& buffer, - MojoWriteDataFlags flags) { - uint32_t num_bytes = static_cast<uint32_t>(buffer.num_bytes()); - MojoResult result = - MojoWriteData(handle.value(), buffer.bytes(), &num_bytes, flags); - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); - dictionary.Set("result", result); - dictionary.Set("numBytes", num_bytes); - return dictionary; -} - -gin::Dictionary ReadData(const gin::Arguments& args, - mojo::Handle handle, - MojoReadDataFlags flags) { - uint32_t num_bytes = 0; - MojoResult result = MojoReadData( - handle.value(), NULL, &num_bytes, MOJO_READ_DATA_FLAG_QUERY); - if (result != MOJO_RESULT_OK) { - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); - dictionary.Set("result", result); - return dictionary; - } - - v8::Handle<v8::ArrayBuffer> array_buffer = - v8::ArrayBuffer::New(args.isolate(), num_bytes); - gin::ArrayBuffer buffer; - ConvertFromV8(args.isolate(), array_buffer, &buffer); - CHECK_EQ(num_bytes, buffer.num_bytes()); - - result = MojoReadData(handle.value(), buffer.bytes(), &num_bytes, flags); - CHECK_EQ(num_bytes, buffer.num_bytes()); - - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); - dictionary.Set("result", result); - dictionary.Set("buffer", array_buffer); - return dictionary; -} - -// Asynchronously read all of the data available for the specified data pipe -// consumer handle until the remote handle is closed or an error occurs. A -// Promise is returned whose settled value is an object like this: -// {result: core.RESULT_OK, buffer: dataArrayBuffer}. If the read failed, -// then the Promise is rejected, the result will be the actual error code, -// and the buffer will contain whatever was read before the error occurred. -// The drainData data pipe handle argument is closed automatically. - -v8::Handle<v8::Value> DoDrainData(gin::Arguments* args, - gin::Handle<HandleWrapper> handle) { - return (new DrainData(args->isolate(), handle->release()))->GetPromise(); -} - -bool IsHandle(gin::Arguments* args, v8::Handle<v8::Value> val) { - gin::Handle<mojo::js::HandleWrapper> ignore_handle; - return gin::Converter<gin::Handle<mojo::js::HandleWrapper>>::FromV8( - args->isolate(), val, &ignore_handle); -} - - -gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin }; - -} // namespace - -const char Core::kModuleName[] = "mojo/public/js/core"; - -v8::Local<v8::Value> Core::GetModule(v8::Isolate* isolate) { - gin::PerIsolateData* data = gin::PerIsolateData::From(isolate); - v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate( - &g_wrapper_info); - - if (templ.IsEmpty()) { - templ = - gin::ObjectTemplateBuilder(isolate) - // TODO(mpcomplete): Should these just be methods on the JS Handle - // object? - .SetMethod("close", CloseHandle) - .SetMethod("wait", WaitHandle) - .SetMethod("waitMany", WaitMany) - .SetMethod("createMessagePipe", CreateMessagePipe) - .SetMethod("writeMessage", WriteMessage) - .SetMethod("readMessage", ReadMessage) - .SetMethod("createDataPipe", CreateDataPipe) - .SetMethod("writeData", WriteData) - .SetMethod("readData", ReadData) - .SetMethod("drainData", DoDrainData) - .SetMethod("isHandle", IsHandle) - - .SetValue("RESULT_OK", MOJO_RESULT_OK) - .SetValue("RESULT_CANCELLED", MOJO_RESULT_CANCELLED) - .SetValue("RESULT_UNKNOWN", MOJO_RESULT_UNKNOWN) - .SetValue("RESULT_INVALID_ARGUMENT", MOJO_RESULT_INVALID_ARGUMENT) - .SetValue("RESULT_DEADLINE_EXCEEDED", MOJO_RESULT_DEADLINE_EXCEEDED) - .SetValue("RESULT_NOT_FOUND", MOJO_RESULT_NOT_FOUND) - .SetValue("RESULT_ALREADY_EXISTS", MOJO_RESULT_ALREADY_EXISTS) - .SetValue("RESULT_PERMISSION_DENIED", MOJO_RESULT_PERMISSION_DENIED) - .SetValue("RESULT_RESOURCE_EXHAUSTED", - MOJO_RESULT_RESOURCE_EXHAUSTED) - .SetValue("RESULT_FAILED_PRECONDITION", - MOJO_RESULT_FAILED_PRECONDITION) - .SetValue("RESULT_ABORTED", MOJO_RESULT_ABORTED) - .SetValue("RESULT_OUT_OF_RANGE", MOJO_RESULT_OUT_OF_RANGE) - .SetValue("RESULT_UNIMPLEMENTED", MOJO_RESULT_UNIMPLEMENTED) - .SetValue("RESULT_INTERNAL", MOJO_RESULT_INTERNAL) - .SetValue("RESULT_UNAVAILABLE", MOJO_RESULT_UNAVAILABLE) - .SetValue("RESULT_DATA_LOSS", MOJO_RESULT_DATA_LOSS) - .SetValue("RESULT_BUSY", MOJO_RESULT_BUSY) - .SetValue("RESULT_SHOULD_WAIT", MOJO_RESULT_SHOULD_WAIT) - - .SetValue("DEADLINE_INDEFINITE", MOJO_DEADLINE_INDEFINITE) - - .SetValue("HANDLE_SIGNAL_NONE", MOJO_HANDLE_SIGNAL_NONE) - .SetValue("HANDLE_SIGNAL_READABLE", MOJO_HANDLE_SIGNAL_READABLE) - .SetValue("HANDLE_SIGNAL_WRITABLE", MOJO_HANDLE_SIGNAL_WRITABLE) - .SetValue("HANDLE_SIGNAL_PEER_CLOSED", - MOJO_HANDLE_SIGNAL_PEER_CLOSED) - - .SetValue("CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE", - MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE) - - .SetValue("WRITE_MESSAGE_FLAG_NONE", MOJO_WRITE_MESSAGE_FLAG_NONE) - - .SetValue("READ_MESSAGE_FLAG_NONE", MOJO_READ_MESSAGE_FLAG_NONE) - .SetValue("READ_MESSAGE_FLAG_MAY_DISCARD", - MOJO_READ_MESSAGE_FLAG_MAY_DISCARD) - - .SetValue("CREATE_DATA_PIPE_OPTIONS_FLAG_NONE", - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE) - .SetValue("CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD", - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD) - - .SetValue("WRITE_DATA_FLAG_NONE", MOJO_WRITE_DATA_FLAG_NONE) - .SetValue("WRITE_DATA_FLAG_ALL_OR_NONE", - MOJO_WRITE_DATA_FLAG_ALL_OR_NONE) - - .SetValue("READ_DATA_FLAG_NONE", MOJO_READ_DATA_FLAG_NONE) - .SetValue("READ_DATA_FLAG_ALL_OR_NONE", - MOJO_READ_DATA_FLAG_ALL_OR_NONE) - .SetValue("READ_DATA_FLAG_DISCARD", MOJO_READ_DATA_FLAG_DISCARD) - .SetValue("READ_DATA_FLAG_QUERY", MOJO_READ_DATA_FLAG_QUERY) - .SetValue("READ_DATA_FLAG_PEEK", MOJO_READ_DATA_FLAG_PEEK) - .Build(); - - data->SetObjectTemplate(&g_wrapper_info, templ); - } - - return templ->NewInstance(); -} - -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/core.h b/mojo/edk/js/core.h deleted file mode 100644 index 445fb00..0000000 --- a/mojo/edk/js/core.h +++ /dev/null @@ -1,22 +0,0 @@ -// 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_EDK_JS_CORE_H_ -#define MOJO_EDK_JS_CORE_H_ - -#include "v8/include/v8.h" - -namespace mojo { -namespace js { - -class Core { - public: - static const char kModuleName[]; - static v8::Local<v8::Value> GetModule(v8::Isolate* isolate); -}; - -} // namespace js -} // namespace mojo - -#endif // MOJO_EDK_JS_CORE_H_ diff --git a/mojo/edk/js/drain_data.cc b/mojo/edk/js/drain_data.cc deleted file mode 100644 index 0667c64..0000000 --- a/mojo/edk/js/drain_data.cc +++ /dev/null @@ -1,131 +0,0 @@ -// 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/edk/js/drain_data.h" - -#include "gin/array_buffer.h" -#include "gin/converter.h" -#include "gin/dictionary.h" -#include "gin/per_context_data.h" -#include "gin/per_isolate_data.h" -#include "mojo/public/cpp/environment/environment.h" -#include "mojo/public/cpp/system/core.h" - -namespace mojo { -namespace js { - -DrainData::DrainData(v8::Isolate* isolate, mojo::Handle handle) - : isolate_(isolate), - handle_(DataPipeConsumerHandle(handle.value())), - wait_id_(0) { - - v8::Handle<v8::Context> context(isolate_->GetCurrentContext()); - runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr(); - - WaitForData(); -} - -v8::Handle<v8::Value> DrainData::GetPromise() { - CHECK(resolver_.IsEmpty()); - v8::Handle<v8::Promise::Resolver> resolver( - v8::Promise::Resolver::New(isolate_)); - resolver_.Reset(isolate_, resolver); - return resolver->GetPromise(); -} - -DrainData::~DrainData() { - if (wait_id_) - Environment::GetDefaultAsyncWaiter()->CancelWait(wait_id_); - resolver_.Reset(); -} - -void DrainData::WaitForData() { - wait_id_ = Environment::GetDefaultAsyncWaiter()->AsyncWait( - handle_.get().value(), - MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, - &DrainData::WaitCompleted, - this); -} - -void DrainData::DataReady(MojoResult result) { - wait_id_ = 0; - if (result != MOJO_RESULT_OK) { - DeliverData(result); - return; - } - while (result == MOJO_RESULT_OK) { - result = ReadData(); - if (result == MOJO_RESULT_SHOULD_WAIT) - WaitForData(); - else if (result != MOJO_RESULT_OK) - DeliverData(result); - } -} - -MojoResult DrainData::ReadData() { - const void* buffer; - uint32_t num_bytes = 0; - MojoResult result = BeginReadDataRaw( - handle_.get(), &buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE); - if (result != MOJO_RESULT_OK) - return result; - const char* p = static_cast<const char*>(buffer); - DataBuffer* data_buffer = new DataBuffer(p, p + num_bytes); - data_buffers_.push_back(data_buffer); - return EndReadDataRaw(handle_.get(), num_bytes); -} - -void DrainData::DeliverData(MojoResult result) { - if (!runner_) { - delete this; - return; - } - - size_t total_bytes = 0; - for (unsigned i = 0; i < data_buffers_.size(); i++) - total_bytes += data_buffers_[i]->size(); - - // Create a total_bytes length ArrayBuffer return value. - gin::Runner::Scope scope(runner_.get()); - v8::Handle<v8::ArrayBuffer> array_buffer = - v8::ArrayBuffer::New(isolate_, total_bytes); - gin::ArrayBuffer buffer; - ConvertFromV8(isolate_, array_buffer, &buffer); - CHECK_EQ(total_bytes, buffer.num_bytes()); - - // Copy the data_buffers into the ArrayBuffer. - char* array_buffer_ptr = static_cast<char*>(buffer.bytes()); - size_t offset = 0; - for (size_t i = 0; i < data_buffers_.size(); i++) { - size_t num_bytes = data_buffers_[i]->size(); - if (num_bytes == 0) - continue; - const char* data_buffer_ptr = &((*data_buffers_[i])[0]); - memcpy(array_buffer_ptr + offset, data_buffer_ptr, num_bytes); - offset += num_bytes; - } - - // The "settled" value of the promise always includes all of the data - // that was read before either an error occurred or the remote pipe handle - // was closed. The latter is indicated by MOJO_RESULT_FAILED_PRECONDITION. - - v8::Handle<v8::Promise::Resolver> resolver( - v8::Local<v8::Promise::Resolver>::New(isolate_, resolver_)); - - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(isolate_); - dictionary.Set("result", result); - dictionary.Set("buffer", array_buffer); - v8::Handle<v8::Value> settled_value(ConvertToV8(isolate_, dictionary)); - - if (result == MOJO_RESULT_FAILED_PRECONDITION) - resolver->Resolve(settled_value); - else - resolver->Reject(settled_value); - - delete this; -} - -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/drain_data.h b/mojo/edk/js/drain_data.h deleted file mode 100644 index 48f1de2..0000000 --- a/mojo/edk/js/drain_data.h +++ /dev/null @@ -1,64 +0,0 @@ -// 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_EDK_JS_DRAIN_DATA_H_ -#define MOJO_EDK_JS_DRAIN_DATA_H_ - -#include "base/memory/scoped_vector.h" -#include "gin/runner.h" -#include "mojo/public/c/environment/async_waiter.h" -#include "mojo/public/cpp/system/core.h" -#include "v8/include/v8.h" - -namespace mojo { -namespace js { - -// This class is the implementation of the Mojo JavaScript core module's -// drainData() method. It is not intended to be used directly. The caller -// allocates a DrainData on the heap and returns GetPromise() to JS. The -// implementation deletes itself after reading as much data as possible -// and rejecting or resolving the Promise. - -class DrainData { - public: - // Starts waiting for data on the specified data pipe consumer handle. - // See WaitForData(). The constructor does not block. - DrainData(v8::Isolate* isolate, mojo::Handle handle); - - // Returns a Promise that will be settled when no more data can be read. - // Should be called just once on a newly allocated DrainData object. - v8::Handle<v8::Value> GetPromise(); - - private: - ~DrainData(); - - // Registers an "async waiter" that calls DataReady() via WaitCompleted(). - void WaitForData(); - static void WaitCompleted(void* self, MojoResult result) { - static_cast<DrainData*>(self)->DataReady(result); - } - - // Use ReadData() to read whatever is availble now on handle_ and save - // it in data_buffers_. - void DataReady(MojoResult result); - MojoResult ReadData(); - - // When the remote data pipe handle is closed, or an error occurs, deliver - // all of the buffered data to the JS Promise and then delete this. - void DeliverData(MojoResult result); - - typedef std::vector<char> DataBuffer; - - v8::Isolate* isolate_; - ScopedDataPipeConsumerHandle handle_; - MojoAsyncWaitID wait_id_; - base::WeakPtr<gin::Runner> runner_; - v8::UniquePersistent<v8::Promise::Resolver> resolver_; - ScopedVector<DataBuffer> data_buffers_; -}; - -} // namespace js -} // namespace mojo - -#endif // MOJO_EDK_JS_DRAIN_DATA_H_ diff --git a/mojo/edk/js/handle.cc b/mojo/edk/js/handle.cc deleted file mode 100644 index de8f338..0000000 --- a/mojo/edk/js/handle.cc +++ /dev/null @@ -1,83 +0,0 @@ -// 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/edk/js/handle.h" - -#include "mojo/edk/js/handle_close_observer.h" - -namespace mojo { -namespace js { - -gin::WrapperInfo HandleWrapper::kWrapperInfo = { gin::kEmbedderNativeGin }; - -HandleWrapper::HandleWrapper(MojoHandle handle) - : handle_(mojo::Handle(handle)) { -} - -HandleWrapper::~HandleWrapper() { - NotifyCloseObservers(); -} - -void HandleWrapper::Close() { - NotifyCloseObservers(); - handle_.reset(); -} - -void HandleWrapper::AddCloseObserver(HandleCloseObserver* observer) { - close_observers_.AddObserver(observer); -} - -void HandleWrapper::RemoveCloseObserver(HandleCloseObserver* observer) { - close_observers_.RemoveObserver(observer); -} - -void HandleWrapper::NotifyCloseObservers() { - if (!handle_.is_valid()) - return; - - FOR_EACH_OBSERVER(HandleCloseObserver, close_observers_, OnWillCloseHandle()); -} - -} // namespace js -} // namespace mojo - -namespace gin { - -v8::Handle<v8::Value> Converter<mojo::Handle>::ToV8(v8::Isolate* isolate, - const mojo::Handle& val) { - if (!val.is_valid()) - return v8::Null(isolate); - return mojo::js::HandleWrapper::Create(isolate, val.value()).ToV8(); -} - -bool Converter<mojo::Handle>::FromV8(v8::Isolate* isolate, - v8::Handle<v8::Value> val, - mojo::Handle* out) { - if (val->IsNull()) { - *out = mojo::Handle(); - return true; - } - - gin::Handle<mojo::js::HandleWrapper> handle; - if (!Converter<gin::Handle<mojo::js::HandleWrapper> >::FromV8( - isolate, val, &handle)) - return false; - - *out = handle->get(); - return true; -} - -v8::Handle<v8::Value> Converter<mojo::MessagePipeHandle>::ToV8( - v8::Isolate* isolate, mojo::MessagePipeHandle val) { - return Converter<mojo::Handle>::ToV8(isolate, val); -} - -bool Converter<mojo::MessagePipeHandle>::FromV8(v8::Isolate* isolate, - v8::Handle<v8::Value> val, - mojo::MessagePipeHandle* out) { - return Converter<mojo::Handle>::FromV8(isolate, val, out); -} - - -} // namespace gin diff --git a/mojo/edk/js/handle.h b/mojo/edk/js/handle.h deleted file mode 100644 index 200b322..0000000 --- a/mojo/edk/js/handle.h +++ /dev/null @@ -1,98 +0,0 @@ -// 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_EDK_JS_HANDLE_H_ -#define MOJO_EDK_JS_HANDLE_H_ - -#include "base/observer_list.h" -#include "gin/converter.h" -#include "gin/handle.h" -#include "gin/wrappable.h" -#include "mojo/public/cpp/system/core.h" - -namespace mojo { -namespace js { -class HandleCloseObserver; - -// Wrapper for mojo Handles exposed to JavaScript. This ensures the Handle -// is Closed when its JS object is garbage collected. -class HandleWrapper : public gin::Wrappable<HandleWrapper> { - public: - static gin::WrapperInfo kWrapperInfo; - - static gin::Handle<HandleWrapper> Create(v8::Isolate* isolate, - MojoHandle handle) { - return gin::CreateHandle(isolate, new HandleWrapper(handle)); - } - - mojo::Handle get() const { return handle_.get(); } - mojo::Handle release() { return handle_.release(); } - void Close(); - - void AddCloseObserver(HandleCloseObserver* observer); - void RemoveCloseObserver(HandleCloseObserver* observer); - - protected: - HandleWrapper(MojoHandle handle); - ~HandleWrapper() override; - void NotifyCloseObservers(); - - mojo::ScopedHandle handle_; - ObserverList<HandleCloseObserver> close_observers_; -}; - -} // namespace js -} // namespace mojo - -namespace gin { - -// Note: It's important to use this converter rather than the one for -// MojoHandle, since that will do a simple int32 conversion. It's unfortunate -// there's no way to prevent against accidental use. -// TODO(mpcomplete): define converters for all Handle subtypes. -template<> -struct Converter<mojo::Handle> { - static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, - const mojo::Handle& val); - static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val, - mojo::Handle* out); -}; - -template<> -struct Converter<mojo::MessagePipeHandle> { - static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, - mojo::MessagePipeHandle val); - static bool FromV8(v8::Isolate* isolate, - v8::Handle<v8::Value> val, - mojo::MessagePipeHandle* out); -}; - -// We need to specialize the normal gin::Handle converter in order to handle -// converting |null| to a wrapper for an empty mojo::Handle. -template<> -struct Converter<gin::Handle<mojo::js::HandleWrapper> > { - static v8::Handle<v8::Value> ToV8( - v8::Isolate* isolate, const gin::Handle<mojo::js::HandleWrapper>& val) { - return val.ToV8(); - } - - static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val, - gin::Handle<mojo::js::HandleWrapper>* out) { - if (val->IsNull()) { - *out = mojo::js::HandleWrapper::Create(isolate, MOJO_HANDLE_INVALID); - return true; - } - - mojo::js::HandleWrapper* object = NULL; - if (!Converter<mojo::js::HandleWrapper*>::FromV8(isolate, val, &object)) { - return false; - } - *out = gin::Handle<mojo::js::HandleWrapper>(val, object); - return true; - } -}; - -} // namespace gin - -#endif // MOJO_EDK_JS_HANDLE_H_ diff --git a/mojo/edk/js/handle_close_observer.h b/mojo/edk/js/handle_close_observer.h deleted file mode 100644 index 3e537fd..0000000 --- a/mojo/edk/js/handle_close_observer.h +++ /dev/null @@ -1,22 +0,0 @@ -// 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_EDK_JS_HANDLE_CLOSE_OBSERVER_H_ -#define MOJO_EDK_JS_HANDLE_CLOSE_OBSERVER_H_ - -namespace mojo { -namespace js { - -class HandleCloseObserver { - public: - virtual void OnWillCloseHandle() = 0; - - protected: - virtual ~HandleCloseObserver() {} -}; - -} // namespace js -} // namespace mojo - -#endif // MOJO_EDK_JS_HANDLE_CLOSE_OBSERVER_H_ diff --git a/mojo/edk/js/handle_unittest.cc b/mojo/edk/js/handle_unittest.cc deleted file mode 100644 index 53f474e..0000000 --- a/mojo/edk/js/handle_unittest.cc +++ /dev/null @@ -1,90 +0,0 @@ -// 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 "base/macros.h" -#include "mojo/edk/js/handle.h" -#include "mojo/edk/js/handle_close_observer.h" -#include "mojo/public/cpp/system/core.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace js { - -class HandleWrapperTest : public testing::Test, - public HandleCloseObserver { - public: - HandleWrapperTest() : closes_observed_(0) {} - - void OnWillCloseHandle() override { closes_observed_++; } - - protected: - int closes_observed_; - - private: - DISALLOW_COPY_AND_ASSIGN(HandleWrapperTest); -}; - -class TestHandleWrapper : public HandleWrapper { - public: - explicit TestHandleWrapper(MojoHandle handle) : HandleWrapper(handle) {} - - private: - DISALLOW_COPY_AND_ASSIGN(TestHandleWrapper); -}; - -// Test that calling Close() on a HandleWrapper for an invalid handle does not -// notify observers. -TEST_F(HandleWrapperTest, CloseWithInvalidHandle) { - { - TestHandleWrapper wrapper(MOJO_HANDLE_INVALID); - wrapper.AddCloseObserver(this); - ASSERT_EQ(0, closes_observed_); - wrapper.Close(); - EXPECT_EQ(0, closes_observed_); - } - EXPECT_EQ(0, closes_observed_); -} - -// Test that destroying a HandleWrapper for an invalid handle does not notify -// observers. -TEST_F(HandleWrapperTest, DestroyWithInvalidHandle) { - { - TestHandleWrapper wrapper(MOJO_HANDLE_INVALID); - wrapper.AddCloseObserver(this); - ASSERT_EQ(0, closes_observed_); - } - EXPECT_EQ(0, closes_observed_); -} - -// Test that calling Close on a HandleWrapper for a valid handle notifies -// observers once. -TEST_F(HandleWrapperTest, CloseWithValidHandle) { - { - mojo::MessagePipe pipe; - TestHandleWrapper wrapper(pipe.handle0.release().value()); - wrapper.AddCloseObserver(this); - ASSERT_EQ(0, closes_observed_); - wrapper.Close(); - EXPECT_EQ(1, closes_observed_); - // Check that calling close again doesn't notify observers. - wrapper.Close(); - EXPECT_EQ(1, closes_observed_); - } - // Check that destroying a closed HandleWrapper doesn't notify observers. - EXPECT_EQ(1, closes_observed_); -} - -// Test that destroying a HandleWrapper for a valid handle notifies observers. -TEST_F(HandleWrapperTest, DestroyWithValidHandle) { - { - mojo::MessagePipe pipe; - TestHandleWrapper wrapper(pipe.handle0.release().value()); - wrapper.AddCloseObserver(this); - ASSERT_EQ(0, closes_observed_); - } - EXPECT_EQ(1, closes_observed_); -} - -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/mojo_runner_delegate.cc b/mojo/edk/js/mojo_runner_delegate.cc deleted file mode 100644 index 152b12c..0000000 --- a/mojo/edk/js/mojo_runner_delegate.cc +++ /dev/null @@ -1,78 +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/edk/js/mojo_runner_delegate.h" - -#include "base/bind.h" -#include "base/path_service.h" -#include "gin/converter.h" -#include "gin/modules/console.h" -#include "gin/modules/module_registry.h" -#include "gin/modules/timer.h" -#include "gin/try_catch.h" -#include "mojo/edk/js/core.h" -#include "mojo/edk/js/handle.h" -#include "mojo/edk/js/support.h" -#include "mojo/edk/js/threading.h" - -namespace mojo { -namespace js { - -namespace { - -// TODO(abarth): Rather than loading these modules from the file system, we -// should load them from the network via Mojo IPC. -std::vector<base::FilePath> GetModuleSearchPaths() { - std::vector<base::FilePath> search_paths(2); - PathService::Get(base::DIR_SOURCE_ROOT, &search_paths[0]); - PathService::Get(base::DIR_EXE, &search_paths[1]); - search_paths[1] = search_paths[1].AppendASCII("gen"); - return search_paths; -} - -void StartCallback(base::WeakPtr<gin::Runner> runner, - MojoHandle pipe, - v8::Handle<v8::Value> module) { - v8::Isolate* isolate = runner->GetContextHolder()->isolate(); - v8::Handle<v8::Function> start; - CHECK(gin::ConvertFromV8(isolate, module, &start)); - - v8::Handle<v8::Value> args[] = { - gin::ConvertToV8(isolate, Handle(pipe)) }; - runner->Call(start, runner->global(), 1, args); -} - -} // namespace - -MojoRunnerDelegate::MojoRunnerDelegate() - : ModuleRunnerDelegate(GetModuleSearchPaths()) { - AddBuiltinModule(gin::Console::kModuleName, gin::Console::GetModule); - AddBuiltinModule(gin::TimerModule::kName, gin::TimerModule::GetModule); - AddBuiltinModule(js::Core::kModuleName, js::Core::GetModule); - AddBuiltinModule(js::Support::kModuleName, js::Support::GetModule); - AddBuiltinModule(js::Threading::kModuleName, js::Threading::GetModule); -} - -MojoRunnerDelegate::~MojoRunnerDelegate() { -} - -void MojoRunnerDelegate::Start(gin::Runner* runner, - MojoHandle pipe, - const std::string& module) { - gin::Runner::Scope scope(runner); - gin::ModuleRegistry* registry = - gin::ModuleRegistry::From(runner->GetContextHolder()->context()); - registry->LoadModule(runner->GetContextHolder()->isolate(), module, - base::Bind(StartCallback, runner->GetWeakPtr(), pipe)); - AttemptToLoadMoreModules(runner); -} - -void MojoRunnerDelegate::UnhandledException(gin::ShellRunner* runner, - gin::TryCatch& try_catch) { - gin::ModuleRunnerDelegate::UnhandledException(runner, try_catch); - LOG(ERROR) << try_catch.GetStackTrace(); -} - -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/mojo_runner_delegate.h b/mojo/edk/js/mojo_runner_delegate.h deleted file mode 100644 index 423eefb..0000000 --- a/mojo/edk/js/mojo_runner_delegate.h +++ /dev/null @@ -1,33 +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_EDK_JS_MOJO_RUNNER_DELEGATE_H_ -#define MOJO_EDK_JS_MOJO_RUNNER_DELEGATE_H_ - -#include "base/macros.h" -#include "gin/modules/module_runner_delegate.h" -#include "mojo/public/c/system/core.h" - -namespace mojo { -namespace js { - -class MojoRunnerDelegate : public gin::ModuleRunnerDelegate { - public: - MojoRunnerDelegate(); - ~MojoRunnerDelegate() override; - - void Start(gin::Runner* runner, MojoHandle pipe, const std::string& module); - - private: - // From ModuleRunnerDelegate: - void UnhandledException(gin::ShellRunner* runner, - gin::TryCatch& try_catch) override; - - DISALLOW_COPY_AND_ASSIGN(MojoRunnerDelegate); -}; - -} // namespace js -} // namespace mojo - -#endif // MOJO_EDK_JS_MOJO_RUNNER_DELEGATE_H_ diff --git a/mojo/edk/js/support.cc b/mojo/edk/js/support.cc deleted file mode 100644 index 7d44024..0000000 --- a/mojo/edk/js/support.cc +++ /dev/null @@ -1,60 +0,0 @@ -// 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/edk/js/support.h" - -#include "base/bind.h" -#include "gin/arguments.h" -#include "gin/converter.h" -#include "gin/function_template.h" -#include "gin/object_template_builder.h" -#include "gin/per_isolate_data.h" -#include "gin/public/wrapper_info.h" -#include "gin/wrappable.h" -#include "mojo/edk/js/handle.h" -#include "mojo/edk/js/waiting_callback.h" -#include "mojo/public/cpp/system/core.h" - -namespace mojo { -namespace js { - -namespace { - -WaitingCallback* AsyncWait(const gin::Arguments& args, - gin::Handle<HandleWrapper> handle, - MojoHandleSignals signals, - v8::Handle<v8::Function> callback) { - return WaitingCallback::Create(args.isolate(), callback, handle, signals) - .get(); -} - -void CancelWait(WaitingCallback* waiting_callback) { - waiting_callback->Cancel(); -} - -gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin }; - -} // namespace - -const char Support::kModuleName[] = "mojo/public/js/support"; - -v8::Local<v8::Value> Support::GetModule(v8::Isolate* isolate) { - gin::PerIsolateData* data = gin::PerIsolateData::From(isolate); - v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate( - &g_wrapper_info); - - if (templ.IsEmpty()) { - templ = gin::ObjectTemplateBuilder(isolate) - .SetMethod("asyncWait", AsyncWait) - .SetMethod("cancelWait", CancelWait) - .Build(); - - data->SetObjectTemplate(&g_wrapper_info, templ); - } - - return templ->NewInstance(); -} - -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/support.h b/mojo/edk/js/support.h deleted file mode 100644 index b49dd23..0000000 --- a/mojo/edk/js/support.h +++ /dev/null @@ -1,22 +0,0 @@ -// 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_EDK_JS_SUPPORT_H_ -#define MOJO_EDK_JS_SUPPORT_H_ - -#include "v8/include/v8.h" - -namespace mojo { -namespace js { - -class Support { - public: - static const char kModuleName[]; - static v8::Local<v8::Value> GetModule(v8::Isolate* isolate); -}; - -} // namespace js -} // namespace mojo - -#endif // MOJO_EDK_JS_SUPPORT_H_ diff --git a/mojo/edk/js/test/BUILD.gn b/mojo/edk/js/test/BUILD.gn deleted file mode 100644 index badc3ad..0000000 --- a/mojo/edk/js/test/BUILD.gn +++ /dev/null @@ -1,41 +0,0 @@ -# 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. - -test("js_unittests") { - deps = [ - "../../js", - "../../js:js_unittests", - "../../test:run_all_unittests", - "../../test:test_support", - "../../../public/cpp/environment", - "../../../public/cpp/system", - "../../../public/cpp/utility", - "../../../public/interfaces/bindings/tests:test_interfaces", - "//base", - "//gin:gin_test", - "//mojo/environment:chromium", - ] - - sources = [ - "run_js_tests.cc", - ] -} - -test("js_integration_tests") { - deps = [ - "../../js", - "../../js/tests:js_to_cpp_tests", - "../../test:run_all_unittests", - "../../test:test_support", - "../../../public/cpp/bindings", - "../../../public/interfaces/bindings/tests:test_interfaces", - "//base", - "//gin:gin_test", - "//mojo/environment:chromium", - ] - - sources = [ - "run_js_integration_tests.cc", - ] -} diff --git a/mojo/edk/js/test/hexdump.js b/mojo/edk/js/test/hexdump.js deleted file mode 100644 index b36c47f..0000000 --- a/mojo/edk/js/test/hexdump.js +++ /dev/null @@ -1,34 +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. - -define(function() { - function hexify(value, length) { - var hex = value.toString(16); - while (hex.length < length) - hex = "0" + hex; - return hex; - } - - function dumpArray(bytes) { - var dumped = ""; - for (var i = 0; i < bytes.length; ++i) { - dumped += hexify(bytes[i], 2); - - if (i % 16 == 15) { - dumped += "\n"; - continue; - } - - if (i % 2 == 1) - dumped += " "; - if (i % 8 == 7) - dumped += " "; - } - return dumped; - } - - var exports = {}; - exports.dumpArray = dumpArray; - return exports; -}); diff --git a/mojo/edk/js/test/run_js_integration_tests.cc b/mojo/edk/js/test/run_js_integration_tests.cc deleted file mode 100644 index 1a6f1d6..0000000 --- a/mojo/edk/js/test/run_js_integration_tests.cc +++ /dev/null @@ -1,57 +0,0 @@ -// 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 "base/files/file_path.h" -#include "base/path_service.h" -#include "gin/modules/console.h" -#include "gin/modules/module_registry.h" -#include "gin/modules/timer.h" -#include "gin/test/file_runner.h" -#include "gin/test/gtest.h" -#include "mojo/edk/js/core.h" -#include "mojo/edk/js/support.h" -#include "mojo/edk/js/threading.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace js { -namespace { - -class TestRunnerDelegate : public gin::FileRunnerDelegate { - public: - TestRunnerDelegate() { - AddBuiltinModule(gin::Console::kModuleName, gin::Console::GetModule); - AddBuiltinModule(Core::kModuleName, Core::GetModule); - AddBuiltinModule(gin::TimerModule::kName, gin::TimerModule::GetModule); - AddBuiltinModule(Threading::kModuleName, Threading::GetModule); - } - private: - DISALLOW_COPY_AND_ASSIGN(TestRunnerDelegate); -}; - -void RunTest(std::string test, bool addSupportModule) { - base::FilePath path; - PathService::Get(base::DIR_SOURCE_ROOT, &path); - path = path.AppendASCII("mojo") - .AppendASCII("edk") - .AppendASCII("js") - .AppendASCII("tests") - .AppendASCII(test); - TestRunnerDelegate delegate; - if (addSupportModule) - delegate.AddBuiltinModule(Support::kModuleName, Support::GetModule); - gin::RunTestFromFile(path, &delegate, true); -} - -TEST(JSTest, connection) { - RunTest("connection_tests.js", false); -} - -TEST(JSTest, sample_service) { - RunTest("sample_service_tests.js", true); -} - -} // namespace -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/test/run_js_tests.cc b/mojo/edk/js/test/run_js_tests.cc deleted file mode 100644 index b574902..0000000 --- a/mojo/edk/js/test/run_js_tests.cc +++ /dev/null @@ -1,62 +0,0 @@ -// 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 "base/files/file_path.h" -#include "base/path_service.h" -#include "gin/modules/console.h" -#include "gin/modules/module_registry.h" -#include "gin/test/file_runner.h" -#include "gin/test/gtest.h" -#include "mojo/edk/js/core.h" -#include "mojo/edk/js/support.h" -#include "mojo/public/cpp/environment/environment.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace js { -namespace { - -class TestRunnerDelegate : public gin::FileRunnerDelegate { - public: - TestRunnerDelegate() { - AddBuiltinModule(gin::Console::kModuleName, gin::Console::GetModule); - AddBuiltinModule(Core::kModuleName, Core::GetModule); - AddBuiltinModule(Support::kModuleName, Support::GetModule); - } - - private: - DISALLOW_COPY_AND_ASSIGN(TestRunnerDelegate); -}; - -void RunTest(std::string test, bool run_until_idle) { - base::FilePath path; - PathService::Get(base::DIR_SOURCE_ROOT, &path); - path = path.AppendASCII("mojo") - .AppendASCII("public") - .AppendASCII("js") - .AppendASCII(test); - TestRunnerDelegate delegate; - gin::RunTestFromFile(path, &delegate, run_until_idle); -} - -// TODO(abarth): Should we autogenerate these stubs from GYP? -TEST(JSTest, core) { - RunTest("core_unittests.js", true); -} - -TEST(JSTest, codec) { - RunTest("codec_unittests.js", true); -} - -TEST(JSTest, struct) { - RunTest("struct_unittests.js", true); -} - -TEST(JSTest, validation) { - RunTest("validation_unittests.js", true); -} - -} // namespace -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/tests/BUILD.gn b/mojo/edk/js/tests/BUILD.gn deleted file mode 100644 index c9711c7..0000000 --- a/mojo/edk/js/tests/BUILD.gn +++ /dev/null @@ -1,36 +0,0 @@ -# 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. - -import("../../mojo_edk.gni") -import("../../../public/tools/bindings/mojom.gni") - -mojo_edk_source_set("js_to_cpp_tests") { - testonly = true - - deps = [ - ":js_to_cpp_bindings", - "//gin:gin_test", - ] - - mojo_edk_deps = [ - "mojo/edk/js", - "mojo/edk/test:test_support", - ] - - mojo_sdk_deps = [ - "mojo/public/cpp/bindings", - "mojo/public/cpp/system", - "mojo/public/interfaces/bindings/tests:test_interfaces", - ] - - sources = [ - "js_to_cpp_tests.cc", - ] -} - -mojom("js_to_cpp_bindings") { - sources = [ - "js_to_cpp.mojom", - ] -} diff --git a/mojo/edk/js/tests/connection_tests.js b/mojo/edk/js/tests/connection_tests.js deleted file mode 100644 index 17009d9..0000000 --- a/mojo/edk/js/tests/connection_tests.js +++ /dev/null @@ -1,264 +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. - -// Mock out the support module to avoid depending on the message loop. -define("mojo/public/js/support", ["timer"], function(timer) { - var waitingCallbacks = []; - - function WaitCookie(id) { - this.id = id; - } - - function asyncWait(handle, flags, callback) { - var id = waitingCallbacks.length; - waitingCallbacks.push(callback); - return new WaitCookie(id); - } - - function cancelWait(cookie) { - waitingCallbacks[cookie.id] = null; - } - - function numberOfWaitingCallbacks() { - var count = 0; - for (var i = 0; i < waitingCallbacks.length; ++i) { - if (waitingCallbacks[i]) - ++count; - } - return count; - } - - function pumpOnce(result) { - var callbacks = waitingCallbacks; - waitingCallbacks = []; - for (var i = 0; i < callbacks.length; ++i) { - if (callbacks[i]) - callbacks[i](result); - } - } - - // Queue up a pumpOnce call to execute after the stack unwinds. Use - // this to trigger a pump after all Promises are executed. - function queuePump(result) { - timer.createOneShot(0, pumpOnce.bind(undefined, result)); - } - - var exports = {}; - exports.asyncWait = asyncWait; - exports.cancelWait = cancelWait; - exports.numberOfWaitingCallbacks = numberOfWaitingCallbacks; - exports.pumpOnce = pumpOnce; - exports.queuePump = queuePump; - return exports; -}); - -define([ - "gin/test/expect", - "mojo/public/js/support", - "mojo/public/js/core", - "mojo/public/js/connection", - "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom", - "mojo/public/interfaces/bindings/tests/sample_service.mojom", - "mojo/public/js/threading", - "gc", -], function(expect, - mockSupport, - core, - connection, - sample_interfaces, - sample_service, - threading, - gc) { - testClientServer(); - testWriteToClosedPipe(); - testRequestResponse().then(function() { - this.result = "PASS"; - gc.collectGarbage(); // should not crash - threading.quit(); - }.bind(this)).catch(function(e) { - this.result = "FAIL: " + (e.stack || e); - threading.quit(); - }.bind(this)); - - function createPeerConnection(handle, stubClass, proxyClass) { - var c = new connection.Connection(handle, stubClass, proxyClass); - c.local.peer = c.remote; - c.remote.peer = c.local; - return c; - } - - function testClientServer() { - var receivedFrobinate = false; - var receivedDidFrobinate = false; - - // ServiceImpl ------------------------------------------------------------ - - function ServiceImpl() { - } - - ServiceImpl.prototype = Object.create( - sample_service.Service.stubClass.prototype); - - ServiceImpl.prototype.frobinate = function(foo, baz, port) { - receivedFrobinate = true; - - expect(foo.name).toBe("Example name"); - expect(baz).toBeTruthy(); - expect(core.close(port)).toBe(core.RESULT_OK); - - this.peer.didFrobinate(42); - }; - - // ServiceClientImpl ------------------------------------------------------ - - function ServiceClientImpl() { - } - - ServiceClientImpl.prototype = - Object.create(sample_service.ServiceClient.stubClass.prototype); - - ServiceClientImpl.prototype.didFrobinate = function(result) { - receivedDidFrobinate = true; - - expect(result).toBe(42); - }; - - var pipe = core.createMessagePipe(); - var anotherPipe = core.createMessagePipe(); - var sourcePipe = core.createMessagePipe(); - - var connection0 = createPeerConnection( - pipe.handle0, ServiceImpl, sample_service.ServiceClient.proxyClass); - - var connection1 = createPeerConnection( - pipe.handle1, ServiceClientImpl, sample_service.Service.proxyClass); - - var foo = new sample_service.Foo(); - foo.bar = new sample_service.Bar(); - foo.name = "Example name"; - foo.source = sourcePipe.handle0; - connection1.remote.frobinate(foo, true, anotherPipe.handle0); - - mockSupport.pumpOnce(core.RESULT_OK); - - expect(receivedFrobinate).toBeTruthy(); - expect(receivedDidFrobinate).toBeTruthy(); - - connection0.close(); - connection1.close(); - - expect(mockSupport.numberOfWaitingCallbacks()).toBe(0); - - // sourcePipe.handle0 was closed automatically when sent over IPC. - expect(core.close(sourcePipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT); - // sourcePipe.handle1 hasn't been closed yet. - expect(core.close(sourcePipe.handle1)).toBe(core.RESULT_OK); - - // anotherPipe.handle0 was closed automatically when sent over IPC. - expect(core.close(anotherPipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT); - // anotherPipe.handle1 hasn't been closed yet. - expect(core.close(anotherPipe.handle1)).toBe(core.RESULT_OK); - - // The Connection object is responsible for closing these handles. - expect(core.close(pipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT); - expect(core.close(pipe.handle1)).toBe(core.RESULT_INVALID_ARGUMENT); - } - - function testWriteToClosedPipe() { - var pipe = core.createMessagePipe(); - - var connection1 = createPeerConnection( - pipe.handle1, function() {}, sample_service.Service.proxyClass); - - // Close the other end of the pipe. - core.close(pipe.handle0); - - // Not observed yet because we haven't pumped events yet. - expect(connection1.encounteredError()).toBeFalsy(); - - var foo = new sample_service.Foo(); - foo.bar = new sample_service.Bar(); - // TODO(darin): crbug.com/357043: pass null in place of |foo| here. - connection1.remote.frobinate(foo, true, null); - - // Write failures are not reported. - expect(connection1.encounteredError()).toBeFalsy(); - - // Pump events, and then we should start observing the closed pipe. - mockSupport.pumpOnce(core.RESULT_OK); - - expect(connection1.encounteredError()).toBeTruthy(); - - connection1.close(); - } - - function testRequestResponse() { - - // ProviderImpl ------------------------------------------------------------ - - function ProviderImpl() { - } - - ProviderImpl.prototype = - Object.create(sample_interfaces.Provider.stubClass.prototype); - - ProviderImpl.prototype.echoString = function(a) { - mockSupport.queuePump(core.RESULT_OK); - return Promise.resolve({a: a}); - }; - - ProviderImpl.prototype.echoStrings = function(a, b) { - mockSupport.queuePump(core.RESULT_OK); - return Promise.resolve({a: a, b: b}); - }; - - // ProviderClientImpl ------------------------------------------------------ - - function ProviderClientImpl() { - } - - ProviderClientImpl.prototype = - Object.create(sample_interfaces.ProviderClient.stubClass.prototype); - - var pipe = core.createMessagePipe(); - - var connection0 = createPeerConnection( - pipe.handle0, - ProviderImpl, - sample_interfaces.ProviderClient.proxyClass); - - var connection1 = createPeerConnection( - pipe.handle1, - ProviderClientImpl, - sample_interfaces.Provider.proxyClass); - - var origReadMessage = core.readMessage; - // echoString - mockSupport.queuePump(core.RESULT_OK); - return connection1.remote.echoString("hello").then(function(response) { - expect(response.a).toBe("hello"); - }).then(function() { - // echoStrings - mockSupport.queuePump(core.RESULT_OK); - return connection1.remote.echoStrings("hello", "world"); - }).then(function(response) { - expect(response.a).toBe("hello"); - expect(response.b).toBe("world"); - }).then(function() { - // Mock a read failure, expect it to fail. - core.readMessage = function() { - return { result: core.RESULT_UNKNOWN }; - }; - mockSupport.queuePump(core.RESULT_OK); - return connection1.remote.echoString("goodbye"); - }).then(function() { - throw Error("Expected echoString to fail."); - }, function(error) { - expect(error.message).toBe("Connection error: " + core.RESULT_UNKNOWN); - - // Clean up. - core.readMessage = origReadMessage; - }); - } -}); diff --git a/mojo/edk/js/tests/js_to_cpp.mojom b/mojo/edk/js/tests/js_to_cpp.mojom deleted file mode 100644 index 69f67b6..0000000 --- a/mojo/edk/js/tests/js_to_cpp.mojom +++ /dev/null @@ -1,53 +0,0 @@ -module js_to_cpp; - -// This struct encompasses all of the basic types, so that they -// may be sent from C++ to JS and back for validation. -struct EchoArgs { - int64 si64; - int32 si32; - int16 si16; - int8 si8; - uint64 ui64; - uint32 ui32; - uint16 ui16; - uint8 ui8; - float float_val; - float float_inf; - float float_nan; - double double_val; - double double_inf; - double double_nan; - string? name; - array<string>? string_array; - handle<message_pipe>? message_handle; - handle<data_pipe_consumer>? data_handle; -}; - -struct EchoArgsList { - EchoArgsList? next; - EchoArgs? item; -}; - -// Note: For messages which control test flow, pick numbers that are unlikely -// to be hit as a result of our deliberate corruption of response messages. -interface CppSide { - // Sent for all tests to notify that the JS side is now ready. - StartTest@88888888(); - - // Indicates end for echo, bit-flip, and back-pointer tests. - TestFinished@99999999(); - - // Responses from specific tests. - PingResponse(); - EchoResponse(EchoArgsList list); - BitFlipResponse(EchoArgsList arg); - BackPointerResponse(EchoArgsList arg); -}; - -[Client=CppSide] -interface JsSide { - Ping(); - Echo(int32 numIterations, EchoArgs arg); - BitFlip(EchoArgs arg); - BackPointer(EchoArgs arg); -}; diff --git a/mojo/edk/js/tests/js_to_cpp_tests.cc b/mojo/edk/js/tests/js_to_cpp_tests.cc deleted file mode 100644 index 1da70c2..0000000 --- a/mojo/edk/js/tests/js_to_cpp_tests.cc +++ /dev/null @@ -1,418 +0,0 @@ -// 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 "base/at_exit.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "base/strings/utf_string_conversions.h" -#include "gin/array_buffer.h" -#include "gin/public/isolate_holder.h" -#include "mojo/edk/js/mojo_runner_delegate.h" -#include "mojo/edk/js/tests/js_to_cpp.mojom.h" -#include "mojo/edk/test/test_utils.h" -#include "mojo/public/cpp/system/core.h" -#include "mojo/public/cpp/system/macros.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace js { - -// Global value updated by some checks to prevent compilers from optimizing -// reads out of existence. -uint32 g_waste_accumulator = 0; - -namespace { - -// Negative numbers with different values in each byte, the last of -// which can survive promotion to double and back. -const int8 kExpectedInt8Value = -65; -const int16 kExpectedInt16Value = -16961; -const int32 kExpectedInt32Value = -1145258561; -const int64 kExpectedInt64Value = -77263311946305LL; - -// Positive numbers with different values in each byte, the last of -// which can survive promotion to double and back. -const uint8 kExpectedUInt8Value = 65; -const uint16 kExpectedUInt16Value = 16961; -const uint32 kExpectedUInt32Value = 1145258561; -const uint64 kExpectedUInt64Value = 77263311946305LL; - -// Double/float values, including special case constants. -const double kExpectedDoubleVal = 3.14159265358979323846; -const double kExpectedDoubleInf = std::numeric_limits<double>::infinity(); -const double kExpectedDoubleNan = std::numeric_limits<double>::quiet_NaN(); -const float kExpectedFloatVal = static_cast<float>(kExpectedDoubleVal); -const float kExpectedFloatInf = std::numeric_limits<float>::infinity(); -const float kExpectedFloatNan = std::numeric_limits<float>::quiet_NaN(); - -// NaN has the property that it is not equal to itself. -#define EXPECT_NAN(x) EXPECT_NE(x, x) - -void CheckDataPipe(MojoHandle data_pipe_handle) { - unsigned char buffer[100]; - uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer)); - MojoResult result = MojoReadData( - data_pipe_handle, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE); - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(64u, buffer_size); - for (int i = 0; i < 64; ++i) { - EXPECT_EQ(i, buffer[i]); - } -} - -void CheckMessagePipe(MojoHandle message_pipe_handle) { - unsigned char buffer[100]; - uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer)); - MojoResult result = MojoReadMessage( - message_pipe_handle, buffer, &buffer_size, 0, 0, 0); - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(64u, buffer_size); - for (int i = 0; i < 64; ++i) { - EXPECT_EQ(255 - i, buffer[i]); - } -} - -js_to_cpp::EchoArgsPtr BuildSampleEchoArgs() { - js_to_cpp::EchoArgsPtr args(js_to_cpp::EchoArgs::New()); - args->si64 = kExpectedInt64Value; - args->si32 = kExpectedInt32Value; - args->si16 = kExpectedInt16Value; - args->si8 = kExpectedInt8Value; - args->ui64 = kExpectedUInt64Value; - args->ui32 = kExpectedUInt32Value; - args->ui16 = kExpectedUInt16Value; - args->ui8 = kExpectedUInt8Value; - args->float_val = kExpectedFloatVal; - args->float_inf = kExpectedFloatInf; - args->float_nan = kExpectedFloatNan; - args->double_val = kExpectedDoubleVal; - args->double_inf = kExpectedDoubleInf; - args->double_nan = kExpectedDoubleNan; - args->name = "coming"; - Array<String> string_array(3); - string_array[0] = "one"; - string_array[1] = "two"; - string_array[2] = "three"; - args->string_array = string_array.Pass(); - return args.Pass(); -} - -void CheckSampleEchoArgs(const js_to_cpp::EchoArgs& arg) { - EXPECT_EQ(kExpectedInt64Value, arg.si64); - EXPECT_EQ(kExpectedInt32Value, arg.si32); - EXPECT_EQ(kExpectedInt16Value, arg.si16); - EXPECT_EQ(kExpectedInt8Value, arg.si8); - EXPECT_EQ(kExpectedUInt64Value, arg.ui64); - EXPECT_EQ(kExpectedUInt32Value, arg.ui32); - EXPECT_EQ(kExpectedUInt16Value, arg.ui16); - EXPECT_EQ(kExpectedUInt8Value, arg.ui8); - EXPECT_EQ(kExpectedFloatVal, arg.float_val); - EXPECT_EQ(kExpectedFloatInf, arg.float_inf); - EXPECT_NAN(arg.float_nan); - EXPECT_EQ(kExpectedDoubleVal, arg.double_val); - EXPECT_EQ(kExpectedDoubleInf, arg.double_inf); - EXPECT_NAN(arg.double_nan); - EXPECT_EQ(std::string("coming"), arg.name.get()); - EXPECT_EQ(std::string("one"), arg.string_array[0].get()); - EXPECT_EQ(std::string("two"), arg.string_array[1].get()); - EXPECT_EQ(std::string("three"), arg.string_array[2].get()); - CheckDataPipe(arg.data_handle.get().value()); - CheckMessagePipe(arg.message_handle.get().value()); -} - -void CheckSampleEchoArgsList(const js_to_cpp::EchoArgsListPtr& list) { - if (list.is_null()) - return; - CheckSampleEchoArgs(*list->item); - CheckSampleEchoArgsList(list->next); -} - -// More forgiving checks are needed in the face of potentially corrupt -// messages. The values don't matter so long as all accesses are within -// bounds. -void CheckCorruptedString(const String& arg) { - if (arg.is_null()) - return; - for (size_t i = 0; i < arg.size(); ++i) - g_waste_accumulator += arg[i]; -} - -void CheckCorruptedStringArray(const Array<String>& string_array) { - if (string_array.is_null()) - return; - for (size_t i = 0; i < string_array.size(); ++i) - CheckCorruptedString(string_array[i]); -} - -void CheckCorruptedDataPipe(MojoHandle data_pipe_handle) { - unsigned char buffer[100]; - uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer)); - MojoResult result = MojoReadData( - data_pipe_handle, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE); - if (result != MOJO_RESULT_OK) - return; - for (uint32_t i = 0; i < buffer_size; ++i) - g_waste_accumulator += buffer[i]; -} - -void CheckCorruptedMessagePipe(MojoHandle message_pipe_handle) { - unsigned char buffer[100]; - uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer)); - MojoResult result = MojoReadMessage( - message_pipe_handle, buffer, &buffer_size, 0, 0, 0); - if (result != MOJO_RESULT_OK) - return; - for (uint32_t i = 0; i < buffer_size; ++i) - g_waste_accumulator += buffer[i]; -} - -void CheckCorruptedEchoArgs(const js_to_cpp::EchoArgsPtr& arg) { - if (arg.is_null()) - return; - CheckCorruptedString(arg->name); - CheckCorruptedStringArray(arg->string_array); - if (arg->data_handle.is_valid()) - CheckCorruptedDataPipe(arg->data_handle.get().value()); - if (arg->message_handle.is_valid()) - CheckCorruptedMessagePipe(arg->message_handle.get().value()); -} - -void CheckCorruptedEchoArgsList(const js_to_cpp::EchoArgsListPtr& list) { - if (list.is_null()) - return; - CheckCorruptedEchoArgs(list->item); - CheckCorruptedEchoArgsList(list->next); -} - -// Base Provider implementation class. It's expected that tests subclass and -// override the appropriate Provider functions. When test is done quit the -// run_loop(). -class CppSideConnection : public js_to_cpp::CppSide { - public: - CppSideConnection() : - run_loop_(NULL), - js_side_(NULL), - mishandled_messages_(0) { - } - ~CppSideConnection() override {} - - void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; } - base::RunLoop* run_loop() { return run_loop_; } - - void set_js_side(js_to_cpp::JsSide* js_side) { js_side_ = js_side; } - js_to_cpp::JsSide* js_side() { return js_side_; } - - // js_to_cpp::CppSide: - void StartTest() override { NOTREACHED(); } - - void TestFinished() override { NOTREACHED(); } - - void PingResponse() override { mishandled_messages_ += 1; } - - void EchoResponse(js_to_cpp::EchoArgsListPtr list) override { - mishandled_messages_ += 1; - } - - void BitFlipResponse(js_to_cpp::EchoArgsListPtr list) override { - mishandled_messages_ += 1; - } - - void BackPointerResponse(js_to_cpp::EchoArgsListPtr list) override { - mishandled_messages_ += 1; - } - - protected: - base::RunLoop* run_loop_; - js_to_cpp::JsSide* js_side_; - int mishandled_messages_; - - private: - DISALLOW_COPY_AND_ASSIGN(CppSideConnection); -}; - -// Trivial test to verify a message sent from JS is received. -class PingCppSideConnection : public CppSideConnection { - public: - PingCppSideConnection() : got_message_(false) {} - ~PingCppSideConnection() override {} - - // js_to_cpp::CppSide: - void StartTest() override { js_side_->Ping(); } - - void PingResponse() override { - got_message_ = true; - run_loop()->Quit(); - } - - bool DidSucceed() { - return got_message_ && !mishandled_messages_; - } - - private: - bool got_message_; - DISALLOW_COPY_AND_ASSIGN(PingCppSideConnection); -}; - -// Test that parameters are passed with correct values. -class EchoCppSideConnection : public CppSideConnection { - public: - EchoCppSideConnection() : - message_count_(0), - termination_seen_(false) { - } - ~EchoCppSideConnection() override {} - - // js_to_cpp::CppSide: - void StartTest() override { - js_side_->Echo(kExpectedMessageCount, BuildSampleEchoArgs()); - } - - void EchoResponse(js_to_cpp::EchoArgsListPtr list) override { - const js_to_cpp::EchoArgsPtr& special_arg = list->item; - message_count_ += 1; - EXPECT_EQ(-1, special_arg->si64); - EXPECT_EQ(-1, special_arg->si32); - EXPECT_EQ(-1, special_arg->si16); - EXPECT_EQ(-1, special_arg->si8); - EXPECT_EQ(std::string("going"), special_arg->name.To<std::string>()); - CheckSampleEchoArgsList(list->next); - } - - void TestFinished() override { - termination_seen_ = true; - run_loop()->Quit(); - } - - bool DidSucceed() { - return termination_seen_ && - !mishandled_messages_ && - message_count_ == kExpectedMessageCount; - } - - private: - static const int kExpectedMessageCount = 10; - int message_count_; - bool termination_seen_; - DISALLOW_COPY_AND_ASSIGN(EchoCppSideConnection); -}; - -// Test that corrupted messages don't wreak havoc. -class BitFlipCppSideConnection : public CppSideConnection { - public: - BitFlipCppSideConnection() : termination_seen_(false) {} - ~BitFlipCppSideConnection() override {} - - // js_to_cpp::CppSide: - void StartTest() override { js_side_->BitFlip(BuildSampleEchoArgs()); } - - void BitFlipResponse(js_to_cpp::EchoArgsListPtr list) override { - CheckCorruptedEchoArgsList(list); - } - - void TestFinished() override { - termination_seen_ = true; - run_loop()->Quit(); - } - - bool DidSucceed() { - return termination_seen_; - } - - private: - bool termination_seen_; - DISALLOW_COPY_AND_ASSIGN(BitFlipCppSideConnection); -}; - -// Test that severely random messages don't wreak havoc. -class BackPointerCppSideConnection : public CppSideConnection { - public: - BackPointerCppSideConnection() : termination_seen_(false) {} - ~BackPointerCppSideConnection() override {} - - // js_to_cpp::CppSide: - void StartTest() override { js_side_->BackPointer(BuildSampleEchoArgs()); } - - void BackPointerResponse(js_to_cpp::EchoArgsListPtr list) override { - CheckCorruptedEchoArgsList(list); - } - - void TestFinished() override { - termination_seen_ = true; - run_loop()->Quit(); - } - - bool DidSucceed() { - return termination_seen_; - } - - private: - bool termination_seen_; - DISALLOW_COPY_AND_ASSIGN(BackPointerCppSideConnection); -}; - -} // namespace - -class JsToCppTest : public testing::Test { - public: - JsToCppTest() {} - - void RunTest(const std::string& test, CppSideConnection* cpp_side) { - cpp_side->set_run_loop(&run_loop_); - - MessagePipe pipe; - js_to_cpp::JsSidePtr js_side = - MakeProxy<js_to_cpp::JsSide>(pipe.handle0.Pass()); - js_side.set_client(cpp_side); - - js_side.internal_state()->router_for_testing()->EnableTestingMode(); - - cpp_side->set_js_side(js_side.get()); - - gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode, - gin::ArrayBufferAllocator::SharedInstance()); - gin::IsolateHolder instance; - MojoRunnerDelegate delegate; - gin::ShellRunner runner(&delegate, instance.isolate()); - delegate.Start(&runner, pipe.handle1.release().value(), test); - - run_loop_.Run(); - } - - private: - base::ShadowingAtExitManager at_exit_; - base::MessageLoop loop; - base::RunLoop run_loop_; - - DISALLOW_COPY_AND_ASSIGN(JsToCppTest); -}; - -TEST_F(JsToCppTest, Ping) { - PingCppSideConnection cpp_side_connection; - RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection); - EXPECT_TRUE(cpp_side_connection.DidSucceed()); -} - -TEST_F(JsToCppTest, Echo) { - EchoCppSideConnection cpp_side_connection; - RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection); - EXPECT_TRUE(cpp_side_connection.DidSucceed()); -} - -TEST_F(JsToCppTest, BitFlip) { - BitFlipCppSideConnection cpp_side_connection; - RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection); - EXPECT_TRUE(cpp_side_connection.DidSucceed()); -} - -TEST_F(JsToCppTest, BackPointer) { - BackPointerCppSideConnection cpp_side_connection; - RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection); - EXPECT_TRUE(cpp_side_connection.DidSucceed()); -} - -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/tests/js_to_cpp_tests.js b/mojo/edk/js/tests/js_to_cpp_tests.js deleted file mode 100644 index 140ad4c..0000000 --- a/mojo/edk/js/tests/js_to_cpp_tests.js +++ /dev/null @@ -1,226 +0,0 @@ -// 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. - -define('mojo/edk/js/tests/js_to_cpp_tests', [ - 'console', - 'mojo/edk/js/tests/js_to_cpp.mojom', - 'mojo/public/js/connection', - 'mojo/public/js/connector', - 'mojo/public/js/core', -], function (console, jsToCpp, connection, connector, core) { - var retainedConnection; - var sampleData; - var sampleMessage; - var BAD_VALUE = 13; - var DATA_PIPE_PARAMS = { - flags: core.CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, - elementNumBytes: 1, - capacityNumBytes: 64 - }; - - function JsSideConnection() { - } - - JsSideConnection.prototype = - Object.create(jsToCpp.JsSide.stubClass.prototype); - - JsSideConnection.prototype.ping = function (arg) { - this.cppSide_.pingResponse(); - }; - - JsSideConnection.prototype.echo = function (numIterations, arg) { - var dataPipe1; - var dataPipe2; - var i; - var messagePipe1; - var messagePipe2; - var specialArg; - - // Ensure expected negative values are negative. - if (arg.si64 > 0) - arg.si64 = BAD_VALUE; - - if (arg.si32 > 0) - arg.si32 = BAD_VALUE; - - if (arg.si16 > 0) - arg.si16 = BAD_VALUE; - - if (arg.si8 > 0) - arg.si8 = BAD_VALUE; - - for (i = 0; i < numIterations; ++i) { - dataPipe1 = core.createDataPipe(DATA_PIPE_PARAMS); - dataPipe2 = core.createDataPipe(DATA_PIPE_PARAMS); - messagePipe1 = core.createMessagePipe(); - messagePipe2 = core.createMessagePipe(); - - arg.data_handle = dataPipe1.consumerHandle; - arg.message_handle = messagePipe1.handle1; - - specialArg = new jsToCpp.EchoArgs(); - specialArg.si64 = -1; - specialArg.si32 = -1; - specialArg.si16 = -1; - specialArg.si8 = -1; - specialArg.name = 'going'; - specialArg.data_handle = dataPipe2.consumerHandle; - specialArg.message_handle = messagePipe2.handle1; - - writeDataPipe(dataPipe1, sampleData); - writeDataPipe(dataPipe2, sampleData); - writeMessagePipe(messagePipe1, sampleMessage); - writeMessagePipe(messagePipe2, sampleMessage); - - this.cppSide_.echoResponse(createEchoArgsList(specialArg, arg)); - - core.close(dataPipe1.producerHandle); - core.close(dataPipe2.producerHandle); - core.close(messagePipe1.handle0); - core.close(messagePipe2.handle0); - } - this.cppSide_.testFinished(); - }; - - JsSideConnection.prototype.bitFlip = function (arg) { - var iteration = 0; - var dataPipe; - var messagePipe; - var proto = connector.Connector.prototype; - var stopSignalled = false; - - proto.realAccept = proto.accept; - proto.accept = function (message) { - var offset = iteration / 8; - var mask; - var value; - if (offset < message.buffer.arrayBuffer.byteLength) { - mask = 1 << (iteration % 8); - value = message.buffer.getUint8(offset) ^ mask; - message.buffer.setUint8(offset, value); - return this.realAccept(message); - } - stopSignalled = true; - return false; - }; - - while (!stopSignalled) { - dataPipe = core.createDataPipe(DATA_PIPE_PARAMS); - messagePipe = core.createMessagePipe(); - writeDataPipe(dataPipe, sampleData); - writeMessagePipe(messagePipe, sampleMessage); - arg.data_handle = dataPipe.consumerHandle; - arg.message_handle = messagePipe.handle1; - - this.cppSide_.bitFlipResponse(createEchoArgsList(arg)); - - core.close(dataPipe.producerHandle); - core.close(messagePipe.handle0); - iteration += 1; - } - - proto.accept = proto.realAccept; - proto.realAccept = null; - this.cppSide_.testFinished(); - }; - - JsSideConnection.prototype.backPointer = function (arg) { - var iteration = 0; - var dataPipe; - var messagePipe; - var proto = connector.Connector.prototype; - var stopSignalled = false; - - proto.realAccept = proto.accept; - proto.accept = function (message) { - var delta = 8 * (1 + iteration % 32); - var offset = 8 * ((iteration / 32) | 0); - if (offset < message.buffer.arrayBuffer.byteLength - 4) { - message.buffer.dataView.setUint32(offset, 0x100000000 - delta, true); - message.buffer.dataView.setUint32(offset + 4, 0xffffffff, true); - return this.realAccept(message); - } - stopSignalled = true; - return false; - }; - - while (!stopSignalled) { - dataPipe = core.createDataPipe(DATA_PIPE_PARAMS); - messagePipe = core.createMessagePipe(); - writeDataPipe(dataPipe, sampleData); - writeMessagePipe(messagePipe, sampleMessage); - arg.data_handle = dataPipe.consumerHandle; - arg.message_handle = messagePipe.handle1; - - this.cppSide_.backPointerResponse(createEchoArgsList(arg)); - - core.close(dataPipe.producerHandle); - core.close(messagePipe.handle0); - iteration += 1; - } - - proto.accept = proto.realAccept; - proto.realAccept = null; - this.cppSide_.testFinished(); - }; - - function writeDataPipe(pipe, data) { - var writeResult = core.writeData( - pipe.producerHandle, data, core.WRITE_DATA_FLAG_ALL_OR_NONE); - - if (writeResult.result != core.RESULT_OK) { - console.log('ERROR: Data pipe write result was ' + writeResult.result); - return false; - } - if (writeResult.numBytes != data.length) { - console.log('ERROR: Data pipe write length was ' + writeResult.numBytes); - return false; - } - return true; - } - - function writeMessagePipe(pipe, arrayBuffer) { - var result = core.writeMessage(pipe.handle0, arrayBuffer, [], 0); - if (result != core.RESULT_OK) { - console.log('ERROR: Message pipe write result was ' + result); - return false; - } - return true; - } - - function createEchoArgsListElement(item, next) { - var list = new jsToCpp.EchoArgsList(); - list.item = item; - list.next = next; - return list; - } - - function createEchoArgsList() { - var genuineArray = Array.prototype.slice.call(arguments); - return genuineArray.reduceRight(function (previous, current) { - return createEchoArgsListElement(current, previous); - }, null); - } - - function createCppSideConnection(handle, stubClass, proxyClass) { - var c = new connection.Connection(handle, stubClass, proxyClass); - c.local.cppSide_ = c.remote; - return c; - } - - return function(handle) { - var i; - sampleData = new Uint8Array(DATA_PIPE_PARAMS.capacityNumBytes); - for (i = 0; i < sampleData.length; ++i) { - sampleData[i] = i; - } - sampleMessage = new Uint8Array(DATA_PIPE_PARAMS.capacityNumBytes); - for (i = 0; i < sampleMessage.length; ++i) { - sampleMessage[i] = 255 - i; - } - retainedConnection = createCppSideConnection( - handle, JsSideConnection,jsToCpp.CppSide.proxyClass); - retainedConnection.remote.startTest(); - }; -}); diff --git a/mojo/edk/js/tests/sample_service_tests.js b/mojo/edk/js/tests/sample_service_tests.js deleted file mode 100644 index 2afdf3e..0000000 --- a/mojo/edk/js/tests/sample_service_tests.js +++ /dev/null @@ -1,171 +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. - -define([ - "console", - "mojo/edk/js/test/hexdump", - "gin/test/expect", - "mojo/public/interfaces/bindings/tests/sample_service.mojom", - "mojo/public/interfaces/bindings/tests/sample_import.mojom", - "mojo/public/interfaces/bindings/tests/sample_import2.mojom", - "mojo/public/js/core", - ], function(console, hexdump, expect, sample, imported, imported2, core) { - - var global = this; - - // Set this variable to true to print the binary message in hex. - var dumpMessageAsHex = false; - - function makeFoo() { - var bar = new sample.Bar(); - bar.alpha = 20; - bar.beta = 40; - bar.gamma = 60; - bar.type = sample.Bar.Type.VERTICAL; - - var extra_bars = new Array(3); - for (var i = 0; i < extra_bars.length; ++i) { - var base = i * 100; - var type = i % 2 ? - sample.Bar.Type.VERTICAL : sample.Bar.Type.HORIZONTAL; - extra_bars[i] = new sample.Bar(); - extra_bars[i].alpha = base; - extra_bars[i].beta = base + 20; - extra_bars[i].gamma = base + 40; - extra_bars[i].type = type; - } - - var data = new Array(10); - for (var i = 0; i < data.length; ++i) { - data[i] = data.length - i; - } - - var source = 0xFFFF; // Invent a dummy handle. - - var foo = new sample.Foo(); - foo.name = "foopy"; - foo.x = 1; - foo.y = 2; - foo.a = false; - foo.b = true; - foo.c = false; - foo.bar = bar; - foo.extra_bars = extra_bars; - foo.data = data; - foo.source = source; - return foo; - } - - // Check that the given |Foo| is identical to the one made by |MakeFoo()|. - function checkFoo(foo) { - expect(foo.name).toBe("foopy"); - expect(foo.x).toBe(1); - expect(foo.y).toBe(2); - expect(foo.a).toBeFalsy(); - expect(foo.b).toBeTruthy(); - expect(foo.c).toBeFalsy(); - expect(foo.bar.alpha).toBe(20); - expect(foo.bar.beta).toBe(40); - expect(foo.bar.gamma).toBe(60); - expect(foo.bar.type).toBe(sample.Bar.Type.VERTICAL); - - expect(foo.extra_bars.length).toBe(3); - for (var i = 0; i < foo.extra_bars.length; ++i) { - var base = i * 100; - var type = i % 2 ? - sample.Bar.Type.VERTICAL : sample.Bar.Type.HORIZONTAL; - expect(foo.extra_bars[i].alpha).toBe(base); - expect(foo.extra_bars[i].beta).toBe(base + 20); - expect(foo.extra_bars[i].gamma).toBe(base + 40); - expect(foo.extra_bars[i].type).toBe(type); - } - - expect(foo.data.length).toBe(10); - for (var i = 0; i < foo.data.length; ++i) - expect(foo.data[i]).toBe(foo.data.length - i); - - expect(foo.source).toBe(0xFFFF); - } - - // Check that values are set to the defaults if we don't override them. - function checkDefaultValues() { - var bar = new sample.Bar(); - expect(bar.alpha).toBe(255); - expect(bar.type).toBe(sample.Bar.Type.VERTICAL); - - var foo = new sample.Foo(); - expect(foo.name).toBe("Fooby"); - expect(foo.a).toBeTruthy(); - expect(foo.data).toBeNull(); - - var defaults = new sample.DefaultsTest(); - expect(defaults.a0).toBe(-12); - expect(defaults.a1).toBe(sample.kTwelve); - expect(defaults.a2).toBe(1234); - expect(defaults.a3).toBe(34567); - expect(defaults.a4).toBe(123456); - expect(defaults.a5).toBe(3456789012); - expect(defaults.a6).toBe(-111111111111); - // JS doesn't have a 64 bit integer type so this is just checking that the - // expected and actual values have the same closest double value. - expect(defaults.a7).toBe(9999999999999999999); - expect(defaults.a8).toBe(0x12345); - expect(defaults.a9).toBe(-0x12345); - expect(defaults.a10).toBe(1234); - expect(defaults.a11).toBe(true); - expect(defaults.a12).toBe(false); - expect(defaults.a13).toBe(123.25); - expect(defaults.a14).toBe(1234567890.123); - expect(defaults.a15).toBe(1E10); - expect(defaults.a16).toBe(-1.2E+20); - expect(defaults.a17).toBe(1.23E-20); - expect(defaults.a20).toBe(sample.Bar.Type.BOTH); - expect(defaults.a21).toBeNull(); - expect(defaults.a22).toBeTruthy(); - expect(defaults.a22.shape).toBe(imported.Shape.RECTANGLE); - expect(defaults.a22.color).toBe(imported2.Color.BLACK); - expect(defaults.a21).toBeNull(); - expect(defaults.a23).toBe(0xFFFFFFFFFFFFFFFF); - expect(defaults.a24).toBe(0x123456789); - expect(defaults.a25).toBe(-0x123456789); - } - - function ServiceImpl() { - } - - ServiceImpl.prototype = Object.create(sample.Service.stubClass.prototype); - - ServiceImpl.prototype.frobinate = function(foo, baz, port) { - checkFoo(foo); - expect(baz).toBe(sample.Service.BazOptions.EXTRA); - expect(core.isHandle(port)).toBeTruthy(); - global.result = "PASS"; - }; - - function SimpleMessageReceiver() { - } - - SimpleMessageReceiver.prototype.accept = function(message) { - if (dumpMessageAsHex) { - var uint8Array = new Uint8Array(message.buffer.arrayBuffer); - console.log(hexdump.dumpArray(uint8Array)); - } - // Imagine some IPC happened here. - var serviceImpl = new ServiceImpl(); - serviceImpl.accept(message); - }; - - var serviceProxy = new sample.Service.proxyClass; - serviceProxy.receiver_ = new SimpleMessageReceiver; - - checkDefaultValues(); - - var foo = makeFoo(); - checkFoo(foo); - - var pipe = core.createMessagePipe(); - serviceProxy.frobinate(foo, sample.Service.BazOptions.EXTRA, pipe.handle0); - expect(core.close(pipe.handle0)).toBe(core.RESULT_OK); - expect(core.close(pipe.handle1)).toBe(core.RESULT_OK); -}); diff --git a/mojo/edk/js/threading.cc b/mojo/edk/js/threading.cc deleted file mode 100644 index b571e3e..0000000 --- a/mojo/edk/js/threading.cc +++ /dev/null @@ -1,47 +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/edk/js/threading.h" - -#include "base/message_loop/message_loop.h" -#include "gin/object_template_builder.h" -#include "gin/per_isolate_data.h" -#include "mojo/edk/js/handle.h" - -namespace mojo { -namespace js { - -namespace { - -void Quit() { - base::MessageLoop::current()->QuitNow(); -} - -gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin }; - -} // namespace - -const char Threading::kModuleName[] = "mojo/public/js/threading"; - -v8::Local<v8::Value> Threading::GetModule(v8::Isolate* isolate) { - gin::PerIsolateData* data = gin::PerIsolateData::From(isolate); - v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate( - &g_wrapper_info); - - if (templ.IsEmpty()) { - templ = gin::ObjectTemplateBuilder(isolate) - .SetMethod("quit", Quit) - .Build(); - - data->SetObjectTemplate(&g_wrapper_info, templ); - } - - return templ->NewInstance(); -} - -Threading::Threading() { -} - -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/threading.h b/mojo/edk/js/threading.h deleted file mode 100644 index 7cf0d53..0000000 --- a/mojo/edk/js/threading.h +++ /dev/null @@ -1,25 +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_EDK_JS_THREADING_H_ -#define MOJO_EDK_JS_THREADING_H_ - -#include "gin/public/wrapper_info.h" -#include "v8/include/v8.h" - -namespace mojo { -namespace js { - -class Threading { - public: - static const char kModuleName[]; - static v8::Local<v8::Value> GetModule(v8::Isolate* isolate); - private: - Threading(); -}; - -} // namespace js -} // namespace mojo - -#endif // MOJO_EDK_JS_THREADING_H_ diff --git a/mojo/edk/js/waiting_callback.cc b/mojo/edk/js/waiting_callback.cc deleted file mode 100644 index d5c48c5..0000000 --- a/mojo/edk/js/waiting_callback.cc +++ /dev/null @@ -1,115 +0,0 @@ -// 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/edk/js/waiting_callback.h" - -#include "base/bind.h" -#include "base/message_loop/message_loop.h" -#include "gin/per_context_data.h" -#include "mojo/public/cpp/environment/environment.h" - -namespace mojo { -namespace js { - -namespace { - -v8::Handle<v8::String> GetHiddenPropertyName(v8::Isolate* isolate) { - return gin::StringToSymbol(isolate, "::mojo::js::WaitingCallback"); -} - -} // namespace - -gin::WrapperInfo WaitingCallback::kWrapperInfo = { gin::kEmbedderNativeGin }; - -// static -gin::Handle<WaitingCallback> WaitingCallback::Create( - v8::Isolate* isolate, - v8::Handle<v8::Function> callback, - gin::Handle<HandleWrapper> handle_wrapper, - MojoHandleSignals signals) { - gin::Handle<WaitingCallback> waiting_callback = gin::CreateHandle( - isolate, new WaitingCallback(isolate, callback, handle_wrapper)); - waiting_callback->wait_id_ = Environment::GetDefaultAsyncWaiter()->AsyncWait( - handle_wrapper->get().value(), - signals, - MOJO_DEADLINE_INDEFINITE, - &WaitingCallback::CallOnHandleReady, - waiting_callback.get()); - return waiting_callback; -} - -void WaitingCallback::Cancel() { - if (!wait_id_) - return; - - handle_wrapper_->RemoveCloseObserver(this); - handle_wrapper_ = NULL; - Environment::GetDefaultAsyncWaiter()->CancelWait(wait_id_); - wait_id_ = 0; -} - -WaitingCallback::WaitingCallback(v8::Isolate* isolate, - v8::Handle<v8::Function> callback, - gin::Handle<HandleWrapper> handle_wrapper) - : wait_id_(0), handle_wrapper_(handle_wrapper.get()), weak_factory_(this) { - handle_wrapper_->AddCloseObserver(this); - v8::Handle<v8::Context> context = isolate->GetCurrentContext(); - runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr(); - GetWrapper(isolate)->SetHiddenValue(GetHiddenPropertyName(isolate), callback); -} - -WaitingCallback::~WaitingCallback() { - Cancel(); -} - -// static -void WaitingCallback::CallOnHandleReady(void* closure, MojoResult result) { - static_cast<WaitingCallback*>(closure)->OnHandleReady(result); -} - -void WaitingCallback::ClearWaitId() { - wait_id_ = 0; - handle_wrapper_->RemoveCloseObserver(this); - handle_wrapper_ = nullptr; -} - -void WaitingCallback::OnHandleReady(MojoResult result) { - ClearWaitId(); - CallCallback(result); -} - -void WaitingCallback::CallCallback(MojoResult result) { - // ClearWaitId must already have been called. - DCHECK(!wait_id_); - DCHECK(!handle_wrapper_); - - if (!runner_) - return; - - gin::Runner::Scope scope(runner_.get()); - v8::Isolate* isolate = runner_->GetContextHolder()->isolate(); - - v8::Handle<v8::Value> hidden_value = - GetWrapper(isolate)->GetHiddenValue(GetHiddenPropertyName(isolate)); - v8::Handle<v8::Function> callback; - CHECK(gin::ConvertFromV8(isolate, hidden_value, &callback)); - - v8::Handle<v8::Value> args[] = { gin::ConvertToV8(isolate, result) }; - runner_->Call(callback, runner_->global(), 1, args); -} - -void WaitingCallback::OnWillCloseHandle() { - Environment::GetDefaultAsyncWaiter()->CancelWait(wait_id_); - - // This may be called from GC, so we can't execute Javascript now, call - // ClearWaitId explicitly, and CallCallback asynchronously. - ClearWaitId(); - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&WaitingCallback::CallCallback, weak_factory_.GetWeakPtr(), - MOJO_RESULT_INVALID_ARGUMENT)); -} - -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/waiting_callback.h b/mojo/edk/js/waiting_callback.h deleted file mode 100644 index 5a9dfaf..0000000 --- a/mojo/edk/js/waiting_callback.h +++ /dev/null @@ -1,68 +0,0 @@ -// 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_EDK_JS_WAITING_CALLBACK_H_ -#define MOJO_EDK_JS_WAITING_CALLBACK_H_ - -#include "base/memory/weak_ptr.h" -#include "gin/handle.h" -#include "gin/runner.h" -#include "gin/wrappable.h" -#include "mojo/edk/js/handle.h" -#include "mojo/edk/js/handle_close_observer.h" -#include "mojo/public/c/environment/async_waiter.h" -#include "mojo/public/cpp/system/core.h" - -namespace mojo { -namespace js { - -class WaitingCallback : public gin::Wrappable<WaitingCallback>, - public HandleCloseObserver { - public: - static gin::WrapperInfo kWrapperInfo; - - // Creates a new WaitingCallback. - static gin::Handle<WaitingCallback> Create( - v8::Isolate* isolate, - v8::Handle<v8::Function> callback, - gin::Handle<HandleWrapper> handle_wrapper, - MojoHandleSignals signals); - - // Cancels the callback. Does nothing if a callback is not pending. This is - // implicitly invoked from the destructor but can be explicitly invoked as - // necessary. - void Cancel(); - - private: - WaitingCallback(v8::Isolate* isolate, - v8::Handle<v8::Function> callback, - gin::Handle<HandleWrapper> handle_wrapper); - ~WaitingCallback() override; - - // Callback from MojoAsyncWaiter. |closure| is the WaitingCallback. - static void CallOnHandleReady(void* closure, MojoResult result); - - // Invoked from CallOnHandleReady() (CallOnHandleReady() must be static). - void OnHandleReady(MojoResult result); - - // Invoked by the HandleWrapper if the handle is closed while this wait is - // still in progress. - void OnWillCloseHandle() override; - - void ClearWaitId(); - void CallCallback(MojoResult result); - - base::WeakPtr<gin::Runner> runner_; - MojoAsyncWaitID wait_id_; - - HandleWrapper* handle_wrapper_; - base::WeakPtrFactory<WaitingCallback> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(WaitingCallback); -}; - -} // namespace js -} // namespace mojo - -#endif // MOJO_EDK_JS_WAITING_CALLBACK_H_ diff --git a/mojo/edk/mojo_edk.gni b/mojo/edk/mojo_edk.gni deleted file mode 100644 index 1d1d992..0000000 --- a/mojo/edk/mojo_edk.gni +++ /dev/null @@ -1,95 +0,0 @@ -# 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. - -import("../public/mojo_sdk.gni") - -# A mojo_edk_source_set is a mojo_sdk_source_set that does not restrict -# external dependencies and understands the following additional variables, all -# of which admit a list of the relevant elements specified relative to the -# location of the Mojo EDK: -# mojo_edk_configs -# allow_circular_mojo_edk_includes_from -# mojo_edk_public_deps -# mojo_edk_deps - -# Note that it is assumed that the Mojo EDK is a sibling of the Mojo SDK in a -# client repo; the distinctions made above are for the sake of clarity in -# writing targets. -template("mojo_edk_source_set") { - mojo_sdk_source_set(target_name) { - restrict_external_deps = false - - if (defined(invoker.visibility)) { - visibility = invoker.visibility - } - if (defined(invoker.mojo_edk_visibility)) { - mojo_sdk_visibility = invoker.mojo_edk_visibility - } - if (defined(invoker.testonly)) { - testonly = invoker.testonly - } - if (defined(invoker.sources)) { - sources = invoker.sources - } - if (defined(invoker.defines)) { - defines = invoker.defines - } - if (defined(invoker.public_configs)) { - public_configs = invoker.public_configs - } - - configs = [] - if (defined(invoker.configs)) { - configs = invoker.configs - } - if (defined(invoker.mojo_edk_configs)) { - foreach(edk_config, invoker.mojo_edk_configs) { - # Check that the EDK config was not mistakenly given as an absolute - # path. - assert(get_path_info(edk_config, "abspath") != edk_config) - configs += [ rebase_path(edk_config, ".", mojo_root) ] - } - } - - allow_circular_includes_from = [] - if (defined(invoker.allow_circular_includes_from)) { - allow_circular_includes_from += invoker.allow_circular_includes_from - } - if (defined(invoker.allow_circular_mojo_edk_includes_from)) { - foreach(edk_target, invoker.allow_circular_mojo_edk_includes_from) { - # Check that the EDK target was not mistakenly given as an absolute - # path. - assert(get_path_info(edk_target, "abspath") != edk_target) - allow_circular_includes_from += - [ rebase_path(edk_target, ".", mojo_root) ] - } - } - - if (defined(invoker.public_deps)) { - public_deps = invoker.public_deps - } - mojo_sdk_public_deps = [] - if (defined(invoker.mojo_edk_public_deps)) { - # The EDK is required to be a sibling of the SDK, so the relative - # dependencies are rewritten in the same way. - mojo_sdk_public_deps = invoker.mojo_edk_public_deps - } - if (defined(invoker.mojo_sdk_public_deps)) { - mojo_sdk_public_deps += invoker.mojo_sdk_public_deps - } - - if (defined(invoker.deps)) { - deps = invoker.deps - } - mojo_sdk_deps = [] - if (defined(invoker.mojo_edk_deps)) { - # The EDK is required to be a sibling of the SDK, so the relative - # dependencies are rewritten in the same way. - mojo_sdk_deps = invoker.mojo_edk_deps - } - if (defined(invoker.mojo_sdk_deps)) { - mojo_sdk_deps += invoker.mojo_sdk_deps - } - } -} diff --git a/mojo/edk/system/BUILD.gn b/mojo/edk/system/BUILD.gn deleted file mode 100644 index 2f8abdd..0000000 --- a/mojo/edk/system/BUILD.gn +++ /dev/null @@ -1,209 +0,0 @@ -# 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. - -import("../mojo_edk.gni") - -if (is_android) { - import("//build/config/android/config.gni") - import("//build/config/android/rules.gni") -} - -config("system_config") { - defines = [ - # Ensures that dependent projects import the core functions on Windows. - "MOJO_USE_SYSTEM_IMPL", - ] -} - -component("system") { - output_name = "mojo_system_impl" - - sources = [ - "async_waiter.cc", - "async_waiter.h", - "awakable.h", - "awakable_list.cc", - "awakable_list.h", - "channel.cc", - "channel.h", - "channel_endpoint.cc", - "channel_endpoint.h", - "channel_endpoint_client.h", - "channel_endpoint_id.cc", - "channel_endpoint_id.h", - "channel_info.cc", - "channel_info.h", - "channel_manager.cc", - "channel_manager.h", - "configuration.cc", - "configuration.h", - "core.cc", - "core.h", - "data_pipe.cc", - "data_pipe.h", - "data_pipe_consumer_dispatcher.cc", - "data_pipe_consumer_dispatcher.h", - "data_pipe_producer_dispatcher.cc", - "data_pipe_producer_dispatcher.h", - "dispatcher.cc", - "dispatcher.h", - "endpoint_relayer.cc", - "endpoint_relayer.h", - "handle_signals_state.h", - "handle_table.cc", - "handle_table.h", - "incoming_endpoint.cc", - "incoming_endpoint.h", - "local_data_pipe.cc", - "local_data_pipe.h", - "local_message_pipe_endpoint.cc", - "local_message_pipe_endpoint.h", - "mapping_table.cc", - "mapping_table.h", - "memory.cc", - "memory.h", - "message_in_transit.cc", - "message_in_transit.h", - "message_in_transit_queue.cc", - "message_in_transit_queue.h", - "message_pipe.cc", - "message_pipe.h", - "message_pipe_dispatcher.cc", - "message_pipe_dispatcher.h", - "message_pipe_endpoint.cc", - "message_pipe_endpoint.h", - "options_validation.h", - "platform_handle_dispatcher.cc", - "platform_handle_dispatcher.h", - "proxy_message_pipe_endpoint.cc", - "proxy_message_pipe_endpoint.h", - "raw_channel.cc", - "raw_channel.h", - "raw_channel_posix.cc", - "raw_channel_win.cc", - "shared_buffer_dispatcher.cc", - "shared_buffer_dispatcher.h", - "simple_dispatcher.cc", - "simple_dispatcher.h", - "transport_data.cc", - "transport_data.h", - "unique_identifier.cc", - "unique_identifier.h", - "waiter.cc", - "waiter.h", - ] - - defines = [ - "MOJO_SYSTEM_IMPL_IMPLEMENTATION", - "MOJO_SYSTEM_IMPLEMENTATION", - ] - - all_dependent_configs = [ ":system_config" ] - - public_deps = [ - "../embedder", - "../embedder:platform", - "../../public/c/system", - ] - - deps = [ - "//base", - "//base/third_party/dynamic_annotations", - "//crypto", - ] - - allow_circular_includes_from = [ "../embedder" ] -} - -mojo_edk_source_set("test_utils") { - testonly = true - - sources = [ - "test_utils.cc", - "test_utils.h", - ] - - deps = [ - "//base", - "//base/test:test_support", - ] -} - -# GYP version: mojo/edk/mojo_edk.gyp:mojo_system_unittests -test("mojo_system_unittests") { - sources = [ - "../test/multiprocess_test_helper_unittest.cc", - "awakable_list_unittest.cc", - "channel_endpoint_id_unittest.cc", - "channel_manager_unittest.cc", - "channel_unittest.cc", - "core_test_base.cc", - "core_test_base.h", - "core_unittest.cc", - "data_pipe_unittest.cc", - "dispatcher_unittest.cc", - "local_data_pipe_unittest.cc", - "memory_unittest.cc", - "message_pipe_dispatcher_unittest.cc", - "message_pipe_test_utils.cc", - "message_pipe_test_utils.h", - "message_pipe_unittest.cc", - "multiprocess_message_pipe_unittest.cc", - "options_validation_unittest.cc", - "platform_handle_dispatcher_unittest.cc", - "raw_channel_unittest.cc", - "remote_message_pipe_unittest.cc", - "run_all_unittests.cc", - "shared_buffer_dispatcher_unittest.cc", - "simple_dispatcher_unittest.cc", - "unique_identifier_unittest.cc", - "waiter_test_utils.cc", - "waiter_test_utils.h", - "waiter_unittest.cc", - ] - - deps = [ - ":system", - ":test_utils", - "../embedder:embedder_unittests", - "../test:test_support", - "//base", - "//base/test:test_support", - "//testing/gtest", - ] - - if (is_android) { - deps += [ "//testing/android:native_test_native_code" ] - } - - allow_circular_includes_from = [ "../embedder:embedder_unittests" ] -} - -# GYP version: mojo/edk/mojo_edk.gyp:mojo_message_pipe_perftests -test("mojo_message_pipe_perftests") { - sources = [ - "message_pipe_perftest.cc", - "message_pipe_test_utils.h", - "message_pipe_test_utils.cc", - ] - - deps = [ - ":system", - ":test_utils", - "../test:test_support", - "//base", - "//base/test:test_support", - "//base/test:test_support_perf", - "//testing/gtest", - ] -} - -if (is_android) { - unittest_apk("mojo_system_unittests_apk") { - deps = [ - ":mojo_system_unittests", - ] - unittests_dep = ":mojo_system_unittests" - } -} diff --git a/mojo/edk/system/DEPS b/mojo/edk/system/DEPS deleted file mode 100644 index 4ef4138..0000000 --- a/mojo/edk/system/DEPS +++ /dev/null @@ -1,3 +0,0 @@ -include_rules = [ - "+crypto", -] diff --git a/mojo/edk/system/async_waiter.cc b/mojo/edk/system/async_waiter.cc deleted file mode 100644 index 071eb01..0000000 --- a/mojo/edk/system/async_waiter.cc +++ /dev/null @@ -1,23 +0,0 @@ -// 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/edk/system/async_waiter.h" - -namespace mojo { -namespace system { - -AsyncWaiter::AsyncWaiter(AwakeCallback callback) : callback_(callback) { -} - -AsyncWaiter::~AsyncWaiter() { -} - -bool AsyncWaiter::Awake(MojoResult result, uintptr_t context) { - callback_.Run(result); - delete this; - return false; -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/async_waiter.h b/mojo/edk/system/async_waiter.h deleted file mode 100644 index da412c0..0000000 --- a/mojo/edk/system/async_waiter.h +++ /dev/null @@ -1,38 +0,0 @@ -// 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_EDK_SYSTEM_ASYNC_WAITER_H_ -#define MOJO_EDK_SYSTEM_ASYNC_WAITER_H_ - -#include "base/callback.h" -#include "base/macros.h" -#include "mojo/edk/system/awakable.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { -namespace system { - -// An |Awakable| implementation that just calls a given callback object. -class MOJO_SYSTEM_IMPL_EXPORT AsyncWaiter final : public Awakable { - public: - typedef base::Callback<void(MojoResult)> AwakeCallback; - - // |callback| must satisfy the same contract as |Awakable::Awake()|. - explicit AsyncWaiter(AwakeCallback callback); - virtual ~AsyncWaiter(); - - private: - // |Awakable| implementation: - bool Awake(MojoResult result, uintptr_t context) override; - - AwakeCallback callback_; - - DISALLOW_COPY_AND_ASSIGN(AsyncWaiter); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_ASYNC_WAITER_H_ diff --git a/mojo/edk/system/awakable.h b/mojo/edk/system/awakable.h deleted file mode 100644 index 057d3eb..0000000 --- a/mojo/edk/system/awakable.h +++ /dev/null @@ -1,34 +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_EDK_SYSTEM_AWAKABLE_H_ -#define MOJO_EDK_SYSTEM_AWAKABLE_H_ - -#include <stdint.h> - -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { -namespace system { - -// An interface that may be waited on |AwakableList|. -class MOJO_SYSTEM_IMPL_EXPORT Awakable { - public: - // |Awake()| must satisfy the following contract: - // * As this is called from any thread, this must be thread-safe. - // * As this is called inside a lock, this must not call anything that takes - // "non-terminal" locks, i.e., those which are always safe to take. - // This should return false if this must not be called again for the same - // reason (e.g., for the same call to |AwakableList::Add()|). - virtual bool Awake(MojoResult result, uintptr_t context) = 0; - - protected: - Awakable() {} -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_AWAKABLE_H_ diff --git a/mojo/edk/system/awakable_list.cc b/mojo/edk/system/awakable_list.cc deleted file mode 100644 index e74e2c0..0000000 --- a/mojo/edk/system/awakable_list.cc +++ /dev/null @@ -1,64 +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/edk/system/awakable_list.h" - -#include "base/logging.h" -#include "mojo/edk/system/awakable.h" -#include "mojo/edk/system/handle_signals_state.h" - -namespace mojo { -namespace system { - -AwakableList::AwakableList() { -} - -AwakableList::~AwakableList() { - DCHECK(awakables_.empty()); -} - -void AwakableList::AwakeForStateChange(const HandleSignalsState& state) { - for (AwakeInfoList::iterator it = awakables_.begin(); - it != awakables_.end();) { - bool keep = true; - if (state.satisfies(it->signals)) - keep = it->awakable->Awake(MOJO_RESULT_OK, it->context); - else if (!state.can_satisfy(it->signals)) - keep = it->awakable->Awake(MOJO_RESULT_FAILED_PRECONDITION, it->context); - AwakeInfoList::iterator maybe_delete = it; - ++it; - - if (!keep) - awakables_.erase(maybe_delete); - } -} - -void AwakableList::CancelAll() { - for (AwakeInfoList::iterator it = awakables_.begin(); it != awakables_.end(); - ++it) { - it->awakable->Awake(MOJO_RESULT_CANCELLED, it->context); - } - awakables_.clear(); -} - -void AwakableList::Add(Awakable* awakable, - MojoHandleSignals signals, - uint32_t context) { - awakables_.push_back(AwakeInfo(awakable, signals, context)); -} - -void AwakableList::Remove(Awakable* awakable) { - // We allow a thread to wait on the same handle multiple times simultaneously, - // so we need to scan the entire list and remove all occurrences of |waiter|. - for (AwakeInfoList::iterator it = awakables_.begin(); - it != awakables_.end();) { - AwakeInfoList::iterator maybe_delete = it; - ++it; - if (maybe_delete->awakable == awakable) - awakables_.erase(maybe_delete); - } -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/awakable_list.h b/mojo/edk/system/awakable_list.h deleted file mode 100644 index 19c03c8..0000000 --- a/mojo/edk/system/awakable_list.h +++ /dev/null @@ -1,58 +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_EDK_SYSTEM_AWAKABLE_LIST_H_ -#define MOJO_EDK_SYSTEM_AWAKABLE_LIST_H_ - -#include <stdint.h> - -#include <list> - -#include "base/macros.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { -namespace system { - -class Awakable; -struct HandleSignalsState; - -// |AwakableList| tracks all the |Waiter|s that are waiting on a given -// handle/|Dispatcher|. There should be a |AwakableList| for each handle that -// can be waited on (in any way). In the simple case, the |AwakableList| 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.cc). This class is thread-unsafe (all concurrent access must be -// protected by some lock). -class MOJO_SYSTEM_IMPL_EXPORT AwakableList { - public: - AwakableList(); - ~AwakableList(); - - void AwakeForStateChange(const HandleSignalsState& state); - void CancelAll(); - void Add(Awakable* awakable, MojoHandleSignals signals, uint32_t context); - void Remove(Awakable* awakable); - - private: - struct AwakeInfo { - AwakeInfo(Awakable* awakable, MojoHandleSignals signals, uint32_t context) - : awakable(awakable), signals(signals), context(context) {} - - Awakable* awakable; - MojoHandleSignals signals; - uint32_t context; - }; - typedef std::list<AwakeInfo> AwakeInfoList; - - AwakeInfoList awakables_; - - DISALLOW_COPY_AND_ASSIGN(AwakableList); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_AWAKABLE_LIST_H_ diff --git a/mojo/edk/system/awakable_list_unittest.cc b/mojo/edk/system/awakable_list_unittest.cc deleted file mode 100644 index 0ef6a7a..0000000 --- a/mojo/edk/system/awakable_list_unittest.cc +++ /dev/null @@ -1,358 +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. - -// NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a -// heavily-loaded system). Sorry. |test::EpsilonTimeout()| may be increased to -// increase tolerance and reduce observed flakiness (though doing so reduces the -// meaningfulness of the test). - -#include "mojo/edk/system/awakable_list.h" - -#include "base/threading/platform_thread.h" // For |Sleep()|. -#include "base/time/time.h" -#include "mojo/edk/system/handle_signals_state.h" -#include "mojo/edk/system/test_utils.h" -#include "mojo/edk/system/waiter.h" -#include "mojo/edk/system/waiter_test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -TEST(AwakableListTest, BasicCancel) { - MojoResult result; - uint32_t context; - - // Cancel immediately after thread start. - { - AwakableList awakable_list; - test::SimpleWaiterThread thread(&result, &context); - awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1); - thread.Start(); - awakable_list.CancelAll(); - // Double-remove okay: - awakable_list.Remove(thread.waiter()); - } // Join |thread|. - EXPECT_EQ(MOJO_RESULT_CANCELLED, result); - EXPECT_EQ(1u, context); - - // Cancel before after thread start. - { - AwakableList awakable_list; - test::SimpleWaiterThread thread(&result, &context); - awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2); - awakable_list.CancelAll(); - thread.Start(); - } // Join |thread|. - EXPECT_EQ(MOJO_RESULT_CANCELLED, result); - EXPECT_EQ(2u, context); - - // Cancel some time after thread start. - { - AwakableList awakable_list; - test::SimpleWaiterThread thread(&result, &context); - awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3); - thread.Start(); - base::PlatformThread::Sleep(2 * test::EpsilonTimeout()); - awakable_list.CancelAll(); - } // Join |thread|. - EXPECT_EQ(MOJO_RESULT_CANCELLED, result); - EXPECT_EQ(3u, context); -} - -TEST(AwakableListTest, BasicAwakeSatisfied) { - MojoResult result; - uint32_t context; - - // Awake immediately after thread start. - { - AwakableList awakable_list; - test::SimpleWaiterThread thread(&result, &context); - awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1); - thread.Start(); - awakable_list.AwakeForStateChange(HandleSignalsState( - MOJO_HANDLE_SIGNAL_READABLE, - MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE)); - awakable_list.Remove(thread.waiter()); - } // Join |thread|. - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(1u, context); - - // Awake before after thread start. - { - AwakableList awakable_list; - test::SimpleWaiterThread thread(&result, &context); - awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2); - awakable_list.AwakeForStateChange(HandleSignalsState( - MOJO_HANDLE_SIGNAL_WRITABLE, - MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE)); - awakable_list.Remove(thread.waiter()); - // Double-remove okay: - awakable_list.Remove(thread.waiter()); - thread.Start(); - } // Join |thread|. - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(2u, context); - - // Awake some time after thread start. - { - AwakableList awakable_list; - test::SimpleWaiterThread thread(&result, &context); - awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3); - thread.Start(); - base::PlatformThread::Sleep(2 * test::EpsilonTimeout()); - awakable_list.AwakeForStateChange(HandleSignalsState( - MOJO_HANDLE_SIGNAL_READABLE, - MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE)); - awakable_list.Remove(thread.waiter()); - } // Join |thread|. - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(3u, context); -} - -TEST(AwakableListTest, BasicAwakeUnsatisfiable) { - MojoResult result; - uint32_t context; - - // Awake (for unsatisfiability) immediately after thread start. - { - AwakableList awakable_list; - test::SimpleWaiterThread thread(&result, &context); - awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1); - thread.Start(); - awakable_list.AwakeForStateChange(HandleSignalsState( - MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_WRITABLE)); - awakable_list.Remove(thread.waiter()); - } // Join |thread|. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); - EXPECT_EQ(1u, context); - - // Awake (for unsatisfiability) before after thread start. - { - AwakableList awakable_list; - test::SimpleWaiterThread thread(&result, &context); - awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2); - awakable_list.AwakeForStateChange(HandleSignalsState( - MOJO_HANDLE_SIGNAL_READABLE, MOJO_HANDLE_SIGNAL_READABLE)); - awakable_list.Remove(thread.waiter()); - thread.Start(); - } // Join |thread|. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); - EXPECT_EQ(2u, context); - - // Awake (for unsatisfiability) some time after thread start. - { - AwakableList awakable_list; - test::SimpleWaiterThread thread(&result, &context); - awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3); - thread.Start(); - base::PlatformThread::Sleep(2 * test::EpsilonTimeout()); - awakable_list.AwakeForStateChange(HandleSignalsState( - MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_WRITABLE)); - awakable_list.Remove(thread.waiter()); - // Double-remove okay: - awakable_list.Remove(thread.waiter()); - } // Join |thread|. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); - EXPECT_EQ(3u, context); -} - -TEST(AwakableListTest, MultipleAwakables) { - MojoResult result1; - MojoResult result2; - MojoResult result3; - MojoResult result4; - uint32_t context1; - uint32_t context2; - uint32_t context3; - uint32_t context4; - - // Cancel two awakables. - { - AwakableList awakable_list; - test::SimpleWaiterThread thread1(&result1, &context1); - awakable_list.Add(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1); - thread1.Start(); - test::SimpleWaiterThread thread2(&result2, &context2); - awakable_list.Add(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2); - thread2.Start(); - base::PlatformThread::Sleep(2 * test::EpsilonTimeout()); - awakable_list.CancelAll(); - } // Join threads. - EXPECT_EQ(MOJO_RESULT_CANCELLED, result1); - EXPECT_EQ(1u, context1); - EXPECT_EQ(MOJO_RESULT_CANCELLED, result2); - EXPECT_EQ(2u, context2); - - // Awake one awakable, cancel other. - { - AwakableList awakable_list; - test::SimpleWaiterThread thread1(&result1, &context1); - awakable_list.Add(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3); - thread1.Start(); - test::SimpleWaiterThread thread2(&result2, &context2); - awakable_list.Add(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 4); - thread2.Start(); - base::PlatformThread::Sleep(2 * test::EpsilonTimeout()); - awakable_list.AwakeForStateChange(HandleSignalsState( - MOJO_HANDLE_SIGNAL_READABLE, - MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE)); - awakable_list.Remove(thread1.waiter()); - awakable_list.CancelAll(); - } // Join threads. - EXPECT_EQ(MOJO_RESULT_OK, result1); - EXPECT_EQ(3u, context1); - EXPECT_EQ(MOJO_RESULT_CANCELLED, result2); - EXPECT_EQ(4u, context2); - - // Cancel one awakable, awake other for unsatisfiability. - { - AwakableList awakable_list; - test::SimpleWaiterThread thread1(&result1, &context1); - awakable_list.Add(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 5); - thread1.Start(); - test::SimpleWaiterThread thread2(&result2, &context2); - awakable_list.Add(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 6); - thread2.Start(); - base::PlatformThread::Sleep(2 * test::EpsilonTimeout()); - awakable_list.AwakeForStateChange(HandleSignalsState( - MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_READABLE)); - awakable_list.Remove(thread2.waiter()); - awakable_list.CancelAll(); - } // Join threads. - EXPECT_EQ(MOJO_RESULT_CANCELLED, result1); - EXPECT_EQ(5u, context1); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result2); - EXPECT_EQ(6u, context2); - - // Cancel one awakable, awake other for unsatisfiability. - { - AwakableList awakable_list; - test::SimpleWaiterThread thread1(&result1, &context1); - awakable_list.Add(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 7); - thread1.Start(); - - base::PlatformThread::Sleep(1 * test::EpsilonTimeout()); - - // Should do nothing. - awakable_list.AwakeForStateChange(HandleSignalsState( - MOJO_HANDLE_SIGNAL_NONE, - MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE)); - - test::SimpleWaiterThread thread2(&result2, &context2); - awakable_list.Add(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 8); - thread2.Start(); - - base::PlatformThread::Sleep(1 * test::EpsilonTimeout()); - - // Awake #1. - awakable_list.AwakeForStateChange(HandleSignalsState( - MOJO_HANDLE_SIGNAL_READABLE, - MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE)); - awakable_list.Remove(thread1.waiter()); - - base::PlatformThread::Sleep(1 * test::EpsilonTimeout()); - - test::SimpleWaiterThread thread3(&result3, &context3); - awakable_list.Add(thread3.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 9); - thread3.Start(); - - test::SimpleWaiterThread thread4(&result4, &context4); - awakable_list.Add(thread4.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 10); - thread4.Start(); - - base::PlatformThread::Sleep(1 * test::EpsilonTimeout()); - - // Awake #2 and #3 for unsatisfiability. - awakable_list.AwakeForStateChange(HandleSignalsState( - MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_READABLE)); - awakable_list.Remove(thread2.waiter()); - awakable_list.Remove(thread3.waiter()); - - // Cancel #4. - awakable_list.CancelAll(); - } // Join threads. - EXPECT_EQ(MOJO_RESULT_OK, result1); - EXPECT_EQ(7u, context1); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result2); - EXPECT_EQ(8u, context2); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result3); - EXPECT_EQ(9u, context3); - EXPECT_EQ(MOJO_RESULT_CANCELLED, result4); - EXPECT_EQ(10u, context4); -} - -class KeepAwakable : public Awakable { - public: - KeepAwakable() : awake_count(0) {} - - bool Awake(MojoResult result, uintptr_t context) override { - awake_count++; - return true; - } - - int awake_count; - - DISALLOW_COPY_AND_ASSIGN(KeepAwakable); -}; - -class RemoveAwakable : public Awakable { - public: - RemoveAwakable() : awake_count(0) {} - - bool Awake(MojoResult result, uintptr_t context) override { - awake_count++; - return false; - } - - int awake_count; - - DISALLOW_COPY_AND_ASSIGN(RemoveAwakable); -}; - -TEST(AwakableListTest, KeepAwakablesReturningTrue) { - KeepAwakable keep0; - KeepAwakable keep1; - RemoveAwakable remove0; - RemoveAwakable remove1; - RemoveAwakable remove2; - - HandleSignalsState hss(MOJO_HANDLE_SIGNAL_WRITABLE, - MOJO_HANDLE_SIGNAL_WRITABLE); - - AwakableList remove_all; - remove_all.Add(&remove0, MOJO_HANDLE_SIGNAL_WRITABLE, 0); - remove_all.Add(&remove1, MOJO_HANDLE_SIGNAL_WRITABLE, 0); - - remove_all.AwakeForStateChange(hss); - EXPECT_EQ(remove0.awake_count, 1); - EXPECT_EQ(remove1.awake_count, 1); - - remove_all.AwakeForStateChange(hss); - EXPECT_EQ(remove0.awake_count, 1); - EXPECT_EQ(remove1.awake_count, 1); - - AwakableList remove_first; - remove_first.Add(&remove2, MOJO_HANDLE_SIGNAL_WRITABLE, 0); - remove_first.Add(&keep0, MOJO_HANDLE_SIGNAL_WRITABLE, 0); - remove_first.Add(&keep1, MOJO_HANDLE_SIGNAL_WRITABLE, 0); - - remove_first.AwakeForStateChange(hss); - EXPECT_EQ(keep0.awake_count, 1); - EXPECT_EQ(keep1.awake_count, 1); - EXPECT_EQ(remove2.awake_count, 1); - - remove_first.AwakeForStateChange(hss); - EXPECT_EQ(keep0.awake_count, 2); - EXPECT_EQ(keep1.awake_count, 2); - EXPECT_EQ(remove2.awake_count, 1); - - remove_first.Remove(&keep0); - remove_first.Remove(&keep1); -} - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/channel.cc b/mojo/edk/system/channel.cc deleted file mode 100644 index 71d7208..0000000 --- a/mojo/edk/system/channel.cc +++ /dev/null @@ -1,604 +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/edk/system/channel.h" - -#include <algorithm> - -#include "base/bind.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/strings/stringprintf.h" -#include "mojo/edk/embedder/platform_handle_vector.h" -#include "mojo/edk/system/endpoint_relayer.h" -#include "mojo/edk/system/transport_data.h" - -namespace mojo { -namespace system { - -namespace { - -struct SerializedEndpoint { - // This is the endpoint ID on the receiving side, and should be a "remote ID". - // (The receiving side should already have had an endpoint attached and been - // run via the |Channel|s. This endpoint will have both IDs assigned, so this - // ID is only needed to associate that endpoint with a particular dispatcher.) - ChannelEndpointId receiver_endpoint_id; -}; - -} // namespace - -Channel::Channel(embedder::PlatformSupport* platform_support) - : platform_support_(platform_support), - is_running_(false), - is_shutting_down_(false), - channel_manager_(nullptr) { -} - -void Channel::Init(scoped_ptr<RawChannel> raw_channel) { - DCHECK(creation_thread_checker_.CalledOnValidThread()); - DCHECK(raw_channel); - - // No need to take |lock_|, since this must be called before this object - // becomes thread-safe. - DCHECK(!is_running_); - raw_channel_ = raw_channel.Pass(); - raw_channel_->Init(this); - is_running_ = true; -} - -void Channel::SetChannelManager(ChannelManager* channel_manager) { - DCHECK(channel_manager); - - base::AutoLock locker(lock_); - DCHECK(!is_shutting_down_); - DCHECK(!channel_manager_); - channel_manager_ = channel_manager; -} - -void Channel::Shutdown() { - DCHECK(creation_thread_checker_.CalledOnValidThread()); - - IdToEndpointMap to_destroy; - { - base::AutoLock locker(lock_); - if (!is_running_) - return; - - // Note: Don't reset |raw_channel_|, in case we're being called from within - // |OnReadMessage()| or |OnError()|. - raw_channel_->Shutdown(); - is_running_ = false; - - // We need to deal with it outside the lock. - std::swap(to_destroy, local_id_to_endpoint_map_); - } - - size_t num_live = 0; - size_t num_zombies = 0; - for (IdToEndpointMap::iterator it = to_destroy.begin(); - it != to_destroy.end(); ++it) { - if (it->second) { - num_live++; - it->second->DetachFromChannel(); - } else { - num_zombies++; - } - } - DVLOG_IF(2, num_live || num_zombies) << "Shut down Channel with " << num_live - << " live endpoints and " << num_zombies - << " zombies"; -} - -void Channel::WillShutdownSoon() { - base::AutoLock locker(lock_); - is_shutting_down_ = true; - channel_manager_ = nullptr; -} - -void Channel::SetBootstrapEndpoint(scoped_refptr<ChannelEndpoint> endpoint) { - DCHECK(endpoint); - - // Used for both local and remote IDs. - ChannelEndpointId bootstrap_id = ChannelEndpointId::GetBootstrap(); - - { - base::AutoLock locker(lock_); - - DLOG_IF(WARNING, is_shutting_down_) - << "SetBootstrapEndpoint() while shutting down"; - - // Bootstrap endpoint should be the first. - DCHECK(local_id_to_endpoint_map_.empty()); - - local_id_to_endpoint_map_[bootstrap_id] = endpoint; - } - - endpoint->AttachAndRun(this, bootstrap_id, bootstrap_id); -} - -bool Channel::WriteMessage(scoped_ptr<MessageInTransit> message) { - base::AutoLock locker(lock_); - if (!is_running_) { - // TODO(vtl): I think this is probably not an error condition, but I should - // think about it (and the shutdown sequence) more carefully. - LOG(WARNING) << "WriteMessage() after shutdown"; - return false; - } - - DLOG_IF(WARNING, is_shutting_down_) << "WriteMessage() while shutting down"; - return raw_channel_->WriteMessage(message.Pass()); -} - -bool Channel::IsWriteBufferEmpty() { - base::AutoLock locker(lock_); - if (!is_running_) - return true; - return raw_channel_->IsWriteBufferEmpty(); -} - -void Channel::DetachEndpoint(ChannelEndpoint* endpoint, - ChannelEndpointId local_id, - ChannelEndpointId remote_id) { - DCHECK(endpoint); - DCHECK(local_id.is_valid()); - - if (!remote_id.is_valid()) - return; // Nothing to do. - - { - base::AutoLock locker_(lock_); - if (!is_running_) - return; - - IdToEndpointMap::iterator it = local_id_to_endpoint_map_.find(local_id); - // We detach immediately if we receive a remove message, so it's possible - // that the local ID is no longer in |local_id_to_endpoint_map_|, or even - // that it's since been reused for another endpoint. In both cases, there's - // nothing more to do. - if (it == local_id_to_endpoint_map_.end() || it->second.get() != endpoint) - return; - - DCHECK(it->second); - it->second = nullptr; - - // Send a remove message outside the lock. - } - - if (!SendControlMessage(MessageInTransit::kSubtypeChannelRemoveEndpoint, - local_id, remote_id)) { - HandleLocalError(base::StringPrintf( - "Failed to send message to remove remote endpoint (local ID %u, remote " - "ID %u)", - static_cast<unsigned>(local_id.value()), - static_cast<unsigned>(remote_id.value()))); - } -} - -size_t Channel::GetSerializedEndpointSize() const { - return sizeof(SerializedEndpoint); -} - -void Channel::SerializeEndpointWithClosedPeer( - void* destination, - MessageInTransitQueue* message_queue) { - // We can actually just pass no client to |SerializeEndpointWithLocalPeer()|. - SerializeEndpointWithLocalPeer(destination, message_queue, nullptr, 0); -} - -scoped_refptr<ChannelEndpoint> Channel::SerializeEndpointWithLocalPeer( - void* destination, - MessageInTransitQueue* message_queue, - ChannelEndpointClient* endpoint_client, - unsigned endpoint_client_port) { - DCHECK(destination); - // Allow |endpoint_client| to be null, for use by - // |SerializeEndpointWithClosedPeer()|. - - scoped_refptr<ChannelEndpoint> endpoint(new ChannelEndpoint( - endpoint_client, endpoint_client_port, message_queue)); - - SerializedEndpoint* s = static_cast<SerializedEndpoint*>(destination); - s->receiver_endpoint_id = AttachAndRunEndpoint(endpoint); - DVLOG(2) << "Serializing endpoint with local or closed peer (remote ID = " - << s->receiver_endpoint_id << ")"; - - return endpoint; -} - -void Channel::SerializeEndpointWithRemotePeer( - void* destination, - MessageInTransitQueue* message_queue, - scoped_refptr<ChannelEndpoint> peer_endpoint) { - DCHECK(destination); - DCHECK(peer_endpoint); - - DLOG(WARNING) << "Direct message pipe passing across multiple channels not " - "yet implemented; will proxy"; - // Create and set up an |EndpointRelayer| to proxy. - // TODO(vtl): If we were to own/track the relayer directly (rather than owning - // it via its |ChannelEndpoint|s), then we might be able to make - // |ChannelEndpoint|'s |client_| pointer a raw pointer. - scoped_refptr<EndpointRelayer> relayer(new EndpointRelayer()); - scoped_refptr<ChannelEndpoint> endpoint( - new ChannelEndpoint(relayer.get(), 0, message_queue)); - relayer->Init(endpoint.get(), peer_endpoint.get()); - peer_endpoint->ReplaceClient(relayer.get(), 1); - - SerializedEndpoint* s = static_cast<SerializedEndpoint*>(destination); - s->receiver_endpoint_id = AttachAndRunEndpoint(endpoint); - DVLOG(2) << "Serializing endpoint with remote peer (remote ID = " - << s->receiver_endpoint_id << ")"; -} - -scoped_refptr<IncomingEndpoint> Channel::DeserializeEndpoint( - const void* source) { - const SerializedEndpoint* s = static_cast<const SerializedEndpoint*>(source); - ChannelEndpointId local_id = s->receiver_endpoint_id; - // No need to check the validity of |local_id| -- if it's not valid, it simply - // won't be in |incoming_endpoints_|. - DVLOG_IF(2, !local_id.is_valid() || !local_id.is_remote()) - << "Attempt to get incoming endpoint for invalid ID " << local_id; - - base::AutoLock locker(lock_); - - auto it = incoming_endpoints_.find(local_id); - if (it == incoming_endpoints_.end()) { - LOG(ERROR) << "Failed to deserialize endpoint (ID = " << local_id << ")"; - return nullptr; - } - - DVLOG(2) << "Deserializing endpoint (new local ID = " << local_id << ")"; - - scoped_refptr<IncomingEndpoint> rv; - rv.swap(it->second); - incoming_endpoints_.erase(it); - return rv; -} - -size_t Channel::GetSerializedPlatformHandleSize() const { - return raw_channel_->GetSerializedPlatformHandleSize(); -} - -Channel::~Channel() { - // The channel should have been shut down first. - DCHECK(!is_running_); -} - -void Channel::OnReadMessage( - const MessageInTransit::View& message_view, - embedder::ScopedPlatformHandleVectorPtr platform_handles) { - DCHECK(creation_thread_checker_.CalledOnValidThread()); - - switch (message_view.type()) { - case MessageInTransit::kTypeEndpoint: - OnReadMessageForEndpoint(message_view, platform_handles.Pass()); - break; - case MessageInTransit::kTypeChannel: - OnReadMessageForChannel(message_view, platform_handles.Pass()); - break; - default: - HandleRemoteError( - base::StringPrintf("Received message of invalid type %u", - static_cast<unsigned>(message_view.type()))); - break; - } -} - -void Channel::OnError(Error error) { - DCHECK(creation_thread_checker_.CalledOnValidThread()); - - switch (error) { - case ERROR_READ_SHUTDOWN: - // The other side was cleanly closed, so this isn't actually an error. - DVLOG(1) << "RawChannel read error (shutdown)"; - break; - case ERROR_READ_BROKEN: { - base::AutoLock locker(lock_); - LOG_IF(ERROR, !is_shutting_down_) - << "RawChannel read error (connection broken)"; - break; - } - case ERROR_READ_BAD_MESSAGE: - // Receiving a bad message means either a bug, data corruption, or - // malicious attack (probably due to some other bug). - LOG(ERROR) << "RawChannel read error (received bad message)"; - break; - case ERROR_READ_UNKNOWN: - LOG(ERROR) << "RawChannel read error (unknown)"; - break; - case ERROR_WRITE: - // Write errors are slightly notable: they probably shouldn't happen under - // normal operation (but maybe the other side crashed). - LOG(WARNING) << "RawChannel write error"; - break; - } - Shutdown(); -} - -void Channel::OnReadMessageForEndpoint( - const MessageInTransit::View& message_view, - embedder::ScopedPlatformHandleVectorPtr platform_handles) { - DCHECK(creation_thread_checker_.CalledOnValidThread()); - DCHECK(message_view.type() == MessageInTransit::kTypeEndpoint); - - ChannelEndpointId local_id = message_view.destination_id(); - if (!local_id.is_valid()) { - HandleRemoteError("Received message with no destination ID"); - return; - } - - scoped_refptr<ChannelEndpoint> endpoint; - { - base::AutoLock locker(lock_); - - // Since we own |raw_channel_|, and this method and |Shutdown()| should only - // be called from the creation thread, |raw_channel_| should never be null - // here. - DCHECK(is_running_); - - IdToEndpointMap::const_iterator it = - local_id_to_endpoint_map_.find(local_id); - if (it != local_id_to_endpoint_map_.end()) { - // Ignore messages for zombie endpoints (not an error). - if (!it->second) { - DVLOG(2) << "Ignoring downstream message for zombie endpoint (local ID " - "= " << local_id - << ", remote ID = " << message_view.source_id() << ")"; - return; - } - - endpoint = it->second; - } - } - if (!endpoint) { - HandleRemoteError(base::StringPrintf( - "Received a message for nonexistent local destination ID %u", - static_cast<unsigned>(local_id.value()))); - // This is strongly indicative of some problem. However, it's not a fatal - // error, since it may indicate a buggy (or hostile) remote process. Don't - // die even for Debug builds, since handling this properly needs to be - // tested (TODO(vtl)). - DLOG(ERROR) << "This should not happen under normal operation."; - return; - } - - scoped_ptr<MessageInTransit> message(new MessageInTransit(message_view)); - if (message_view.transport_data_buffer_size() > 0) { - DCHECK(message_view.transport_data_buffer()); - message->SetDispatchers(TransportData::DeserializeDispatchers( - message_view.transport_data_buffer(), - message_view.transport_data_buffer_size(), platform_handles.Pass(), - this)); - } - - endpoint->OnReadMessage(message.Pass()); -} - -void Channel::OnReadMessageForChannel( - const MessageInTransit::View& message_view, - embedder::ScopedPlatformHandleVectorPtr platform_handles) { - DCHECK(creation_thread_checker_.CalledOnValidThread()); - DCHECK_EQ(message_view.type(), MessageInTransit::kTypeChannel); - - // Currently, no channel messages take platform handles. - if (platform_handles) { - HandleRemoteError( - "Received invalid channel message (has platform handles)"); - NOTREACHED(); - return; - } - - switch (message_view.subtype()) { - case MessageInTransit::kSubtypeChannelAttachAndRunEndpoint: - DVLOG(2) << "Handling channel message to attach and run endpoint (local " - "ID " << message_view.destination_id() << ", remote ID " - << message_view.source_id() << ")"; - if (!OnAttachAndRunEndpoint(message_view.destination_id(), - message_view.source_id())) { - HandleRemoteError( - "Received invalid channel message to attach and run endpoint"); - } - break; - case MessageInTransit::kSubtypeChannelRemoveEndpoint: - DVLOG(2) << "Handling channel message to remove endpoint (local ID " - << message_view.destination_id() << ", remote ID " - << message_view.source_id() << ")"; - if (!OnRemoveEndpoint(message_view.destination_id(), - message_view.source_id())) { - HandleRemoteError( - "Received invalid channel message to remove endpoint"); - } - break; - case MessageInTransit::kSubtypeChannelRemoveEndpointAck: - DVLOG(2) << "Handling channel message to ack remove endpoint (local ID " - << message_view.destination_id() << ", remote ID " - << message_view.source_id() << ")"; - if (!OnRemoveEndpointAck(message_view.destination_id())) { - HandleRemoteError( - "Received invalid channel message to ack remove endpoint"); - } - break; - default: - HandleRemoteError("Received invalid channel message"); - NOTREACHED(); - break; - } -} - -bool Channel::OnAttachAndRunEndpoint(ChannelEndpointId local_id, - ChannelEndpointId remote_id) { - // We should only get this for remotely-created local endpoints, so our local - // ID should be "remote". - if (!local_id.is_valid() || !local_id.is_remote()) { - DVLOG(2) << "Received attach and run endpoint with invalid local ID"; - return false; - } - - // Conversely, the remote end should be "local". - if (!remote_id.is_valid() || remote_id.is_remote()) { - DVLOG(2) << "Received attach and run endpoint with invalid remote ID"; - return false; - } - - // Create/initialize an |IncomingEndpoint| and thus an endpoint (outside the - // lock). - scoped_refptr<IncomingEndpoint> incoming_endpoint(new IncomingEndpoint()); - scoped_refptr<ChannelEndpoint> endpoint = incoming_endpoint->Init(); - - bool success = true; - { - base::AutoLock locker(lock_); - - if (local_id_to_endpoint_map_.find(local_id) == - local_id_to_endpoint_map_.end()) { - DCHECK(incoming_endpoints_.find(local_id) == incoming_endpoints_.end()); - - // TODO(vtl): Use emplace when we move to C++11 unordered_maps. (It'll - // avoid some refcount churn.) - local_id_to_endpoint_map_[local_id] = endpoint; - incoming_endpoints_[local_id] = incoming_endpoint; - } else { - // We need to call |Close()| outside the lock. - success = false; - } - } - if (!success) { - DVLOG(2) << "Received attach and run endpoint for existing local ID"; - incoming_endpoint->Close(); - return false; - } - - endpoint->AttachAndRun(this, local_id, remote_id); - return true; -} - -bool Channel::OnRemoveEndpoint(ChannelEndpointId local_id, - ChannelEndpointId remote_id) { - DCHECK(creation_thread_checker_.CalledOnValidThread()); - - scoped_refptr<ChannelEndpoint> endpoint; - { - base::AutoLock locker(lock_); - - IdToEndpointMap::iterator it = local_id_to_endpoint_map_.find(local_id); - if (it == local_id_to_endpoint_map_.end()) { - DVLOG(2) << "Remove endpoint error: not found"; - return false; - } - - if (!it->second) { - // Remove messages "crossed"; we have to wait for the ack. - return true; - } - - endpoint = it->second; - local_id_to_endpoint_map_.erase(it); - // Detach and send the remove ack message outside the lock. - } - - endpoint->DetachFromChannel(); - - if (!SendControlMessage(MessageInTransit::kSubtypeChannelRemoveEndpointAck, - local_id, remote_id)) { - HandleLocalError(base::StringPrintf( - "Failed to send message to ack remove remote endpoint (local ID %u, " - "remote ID %u)", - static_cast<unsigned>(local_id.value()), - static_cast<unsigned>(remote_id.value()))); - } - - return true; -} - -bool Channel::OnRemoveEndpointAck(ChannelEndpointId local_id) { - DCHECK(creation_thread_checker_.CalledOnValidThread()); - - base::AutoLock locker(lock_); - - IdToEndpointMap::iterator it = local_id_to_endpoint_map_.find(local_id); - if (it == local_id_to_endpoint_map_.end()) { - DVLOG(2) << "Remove endpoint ack error: not found"; - return false; - } - - if (it->second) { - DVLOG(2) << "Remove endpoint ack error: wrong state"; - return false; - } - - local_id_to_endpoint_map_.erase(it); - return true; -} - -void Channel::HandleRemoteError(const base::StringPiece& error_message) { - // TODO(vtl): Is this how we really want to handle this? Probably we want to - // terminate the connection, since it's spewing invalid stuff. - LOG(WARNING) << error_message; -} - -void Channel::HandleLocalError(const base::StringPiece& error_message) { - // TODO(vtl): Is this how we really want to handle this? - // Sometimes we'll want to propagate the error back to the message pipe - // (endpoint), and notify it that the remote is (effectively) closed. - // Sometimes we'll want to kill the channel (and notify all the endpoints that - // their remotes are dead. - LOG(WARNING) << error_message; -} - -// Note: |endpoint| being a |scoped_refptr| makes this function safe, since it -// keeps the endpoint alive even after the lock is released. Otherwise, there's -// the temptation to simply pass the result of |new ChannelEndpoint(...)| -// directly to this function, which wouldn't be sufficient for safety. -ChannelEndpointId Channel::AttachAndRunEndpoint( - scoped_refptr<ChannelEndpoint> endpoint) { - DCHECK(endpoint); - - ChannelEndpointId local_id; - ChannelEndpointId remote_id; - { - base::AutoLock locker(lock_); - - DLOG_IF(WARNING, is_shutting_down_) - << "AttachAndRunEndpoint() while shutting down"; - - do { - local_id = local_id_generator_.GetNext(); - } while (local_id_to_endpoint_map_.find(local_id) != - local_id_to_endpoint_map_.end()); - - // TODO(vtl): We also need to check for collisions of remote IDs here. - remote_id = remote_id_generator_.GetNext(); - - local_id_to_endpoint_map_[local_id] = endpoint; - } - - if (!SendControlMessage(MessageInTransit::kSubtypeChannelAttachAndRunEndpoint, - local_id, remote_id)) { - HandleLocalError(base::StringPrintf( - "Failed to send message to run remote endpoint (local ID %u, remote ID " - "%u)", - static_cast<unsigned>(local_id.value()), - static_cast<unsigned>(remote_id.value()))); - // TODO(vtl): Should we continue on to |AttachAndRun()|? - } - - endpoint->AttachAndRun(this, local_id, remote_id); - return remote_id; -} - -bool Channel::SendControlMessage(MessageInTransit::Subtype subtype, - ChannelEndpointId local_id, - ChannelEndpointId remote_id) { - DVLOG(2) << "Sending channel control message: subtype " << subtype - << ", local ID " << local_id << ", remote ID " << remote_id; - scoped_ptr<MessageInTransit> message(new MessageInTransit( - MessageInTransit::kTypeChannel, subtype, 0, nullptr)); - message->set_source_id(local_id); - message->set_destination_id(remote_id); - return WriteMessage(message.Pass()); -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/channel.h b/mojo/edk/system/channel.h deleted file mode 100644 index 3961859..0000000 --- a/mojo/edk/system/channel.h +++ /dev/null @@ -1,260 +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_EDK_SYSTEM_CHANNEL_H_ -#define MOJO_EDK_SYSTEM_CHANNEL_H_ - -#include <stdint.h> - -#include "base/containers/hash_tables.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/strings/string_piece.h" -#include "base/synchronization/lock.h" -#include "base/threading/thread_checker.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/channel_endpoint.h" -#include "mojo/edk/system/channel_endpoint_id.h" -#include "mojo/edk/system/incoming_endpoint.h" -#include "mojo/edk/system/message_in_transit.h" -#include "mojo/edk/system/raw_channel.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { - -namespace embedder { -class PlatformSupport; -} - -namespace system { - -class ChannelEndpointClient; -class ChannelManager; -class MessageInTransitQueue; - -// This class is mostly thread-safe. It must be created on an I/O thread. -// |Init()| must be called on that same thread before it becomes thread-safe (in -// particular, before references are given to any other thread) and |Shutdown()| -// must be called on that same thread before destruction. Its public methods are -// otherwise thread-safe. (Many private methods are restricted to the creation -// thread.) It may be destroyed on any thread, in the sense that the last -// reference to it may be released on any thread, with the proviso that -// |Shutdown()| must have been called first (so the pattern is that a "main" -// reference is kept on its creation thread and is released after |Shutdown()| -// is called, but other threads may have temporarily "dangling" references). -// -// Note the lock order (in order of allowable acquisition): -// |ChannelEndpointClient| (e.g., |MessagePipe|), |ChannelEndpoint|, |Channel|. -// Thus |Channel| may not call into |ChannelEndpoint| with |Channel|'s lock -// held. -class MOJO_SYSTEM_IMPL_EXPORT Channel - : public base::RefCountedThreadSafe<Channel>, - public RawChannel::Delegate { - public: - // |platform_support| (typically owned by |Core|) must remain alive until - // after |Shutdown()| is called. - explicit Channel(embedder::PlatformSupport* platform_support); - - // This must be called on the creation thread before any other methods are - // called, and before references to this object are given to any other - // threads. |raw_channel| should be uninitialized. - void Init(scoped_ptr<RawChannel> raw_channel); - - // Sets the channel manager associated with this channel. This should be set - // at most once and only called before |WillShutdownSoon()| (and - // |Shutdown()|). - void SetChannelManager(ChannelManager* channel_manager); - - // This must be called on the creation thread before destruction (which can - // happen on any thread). - void Shutdown(); - - // Signals that |Shutdown()| will be called soon (this may be called from any - // thread, unlike |Shutdown()|). Warnings will be issued if, e.g., messages - // are written after this is called; other warnings may be suppressed. (This - // may be called multiple times, or not at all.) - // - // If set, the channel manager associated with this channel will be reset. - void WillShutdownSoon(); - - // Called to set (i.e., attach and run) the bootstrap (first) endpoint on the - // channel. Both the local and remote IDs are the bootstrap ID (given by - // |ChannelEndpointId::GetBootstrap()|). - // - // (Bootstrapping is symmetric: Both sides call this, which will establish the - // first connection across a channel.) - void SetBootstrapEndpoint(scoped_refptr<ChannelEndpoint> endpoint); - - // This forwards |message| verbatim to |raw_channel_|. - bool WriteMessage(scoped_ptr<MessageInTransit> message); - - // See |RawChannel::IsWriteBufferEmpty()|. - // TODO(vtl): Maybe we shouldn't expose this, and instead have a - // |FlushWriteBufferAndShutdown()| or something like that. - bool IsWriteBufferEmpty(); - - // Removes the given endpoint from this channel (|local_id| and |remote_id| - // are specified as an optimization; the latter should be an invalid - // |ChannelEndpointId| if the endpoint is not yet running). Note: If this is - // called, the |Channel| will *not* call - // |ChannelEndpoint::DetachFromChannel()|. - void DetachEndpoint(ChannelEndpoint* endpoint, - ChannelEndpointId local_id, - ChannelEndpointId remote_id); - - // Returns the size of a serialized endpoint (see |SerializeEndpoint...()| and - // |DeserializeEndpoint()| below). This value will remain constant for a given - // instance of |Channel|. - size_t GetSerializedEndpointSize() const; - - // Endpoint serialization methods: From the |Channel|'s point of view, there - // are three cases (discussed further below) and thus three methods. - // - // All three methods have a |destination| argument, which should be a buffer - // to which auxiliary information will be written and which should be - // transmitted to the peer |Channel| by some other means, but using this - // |Channel|. It should be a buffer of (at least) the size returned by - // |GetSerializedEndpointSize()| (exactly that much data will be written). - // - // All three also have a |message_queue| argument, which if non-null is the - // queue of messages already received by the endpoint to be serialized. - // - // Note that "serialize" really means "send" -- the |endpoint| will be sent - // "immediately". The contents of the |destination| buffer can then be used to - // claim the rematerialized endpoint from the peer |Channel|. (|destination| - // must be sent using this |Channel|, since otherwise it may be received - // before it is valid to the peer |Channel|.) - // - // Case 1: The endpoint's peer is already closed. - // - // Case 2: The endpoint's peer is local (i.e., it has a - // |ChannelEndpointClient| but no peer |ChannelEndpoint|). - // - // Case 3: The endpoint's peer is remote (i.e., it has a peer - // |ChannelEndpoint|). (This has two subcases: the peer endpoint may be on - // this |Channel| or another |Channel|.) - void SerializeEndpointWithClosedPeer(void* destination, - MessageInTransitQueue* message_queue); - // This one returns the |ChannelEndpoint| for the serialized endpoint (which - // can be used by, e.g., a |ProxyMessagePipeEndpoint|. - scoped_refptr<ChannelEndpoint> SerializeEndpointWithLocalPeer( - void* destination, - MessageInTransitQueue* message_queue, - ChannelEndpointClient* endpoint_client, - unsigned endpoint_client_port); - void SerializeEndpointWithRemotePeer( - void* destination, - MessageInTransitQueue* message_queue, - scoped_refptr<ChannelEndpoint> peer_endpoint); - - // Deserializes an endpoint that was sent from the peer |Channel| (using - // |SerializeEndpoint...()|. |source| should be (a copy of) the data that - // |SerializeEndpoint...()| wrote, and must be (at least) - // |GetSerializedEndpointSize()| bytes. This returns the deserialized - // |IncomingEndpoint| (which can be converted into a |MessagePipe|) or null on - // error. - scoped_refptr<IncomingEndpoint> DeserializeEndpoint(const void* source); - - // See |RawChannel::GetSerializedPlatformHandleSize()|. - size_t GetSerializedPlatformHandleSize() const; - - embedder::PlatformSupport* platform_support() const { - return platform_support_; - } - - private: - friend class base::RefCountedThreadSafe<Channel>; - ~Channel() override; - - // |RawChannel::Delegate| implementation (only called on the creation thread): - void OnReadMessage( - const MessageInTransit::View& message_view, - embedder::ScopedPlatformHandleVectorPtr platform_handles) override; - void OnError(Error error) override; - - // Helpers for |OnReadMessage| (only called on the creation thread): - void OnReadMessageForEndpoint( - const MessageInTransit::View& message_view, - embedder::ScopedPlatformHandleVectorPtr platform_handles); - void OnReadMessageForChannel( - const MessageInTransit::View& message_view, - embedder::ScopedPlatformHandleVectorPtr platform_handles); - - // Handles "attach and run endpoint" messages. - bool OnAttachAndRunEndpoint(ChannelEndpointId local_id, - ChannelEndpointId remote_id); - // Handles "remove endpoint" messages. - bool OnRemoveEndpoint(ChannelEndpointId local_id, - ChannelEndpointId remote_id); - // Handles "remove endpoint ack" messages. - bool OnRemoveEndpointAck(ChannelEndpointId local_id); - - // Handles errors (e.g., invalid messages) from the remote side. Callable from - // any thread. - void HandleRemoteError(const base::StringPiece& error_message); - // Handles internal errors/failures from the local side. Callable from any - // thread. - void HandleLocalError(const base::StringPiece& error_message); - - // Helper for |SerializeEndpoint...()|: Attaches the given (non-bootstrap) - // endpoint to this channel and runs it. This assigns the endpoint both local - // and remote IDs. This will also send a |kSubtypeChannelAttachAndRunEndpoint| - // message to the remote side to tell it to create an endpoint as well. This - // returns the *remote* ID (one for which |is_remote()| returns true). - // - // TODO(vtl): Maybe limit the number of attached message pipes. - ChannelEndpointId AttachAndRunEndpoint( - scoped_refptr<ChannelEndpoint> endpoint); - - // Helper to send channel control messages. Returns true on success. Should be - // called *without* |lock_| held. Callable from any thread. - bool SendControlMessage(MessageInTransit::Subtype subtype, - ChannelEndpointId source_id, - ChannelEndpointId destination_id); - - base::ThreadChecker creation_thread_checker_; - - embedder::PlatformSupport* const platform_support_; - - // Note: |ChannelEndpointClient|s (in particular, |MessagePipe|s) MUST NOT be - // used under |lock_|. E.g., |lock_| can only be acquired after - // |MessagePipe::lock_|, never before. Thus to call into a - // |ChannelEndpointClient|, a reference should be acquired from - // |local_id_to_endpoint_map_| under |lock_| and then the lock released. - base::Lock lock_; // Protects the members below. - - scoped_ptr<RawChannel> raw_channel_; - bool is_running_; - // Set when |WillShutdownSoon()| is called. - bool is_shutting_down_; - - // Has a reference to us. - ChannelManager* channel_manager_; - - typedef base::hash_map<ChannelEndpointId, scoped_refptr<ChannelEndpoint>> - IdToEndpointMap; - // Map from local IDs to endpoints. If the endpoint is null, this means that - // we're just waiting for the remove ack before removing the entry. - IdToEndpointMap local_id_to_endpoint_map_; - // Note: The IDs generated by this should be checked for existence before use. - LocalChannelEndpointIdGenerator local_id_generator_; - - typedef base::hash_map<ChannelEndpointId, scoped_refptr<IncomingEndpoint>> - IdToIncomingEndpointMap; - // Map from local IDs to incoming endpoints (i.e., those received inside other - // messages, but not yet claimed via |DeserializeEndpoint()|). - IdToIncomingEndpointMap incoming_endpoints_; - // TODO(vtl): We need to keep track of remote IDs (so that we don't collide - // if/when we wrap). - RemoteChannelEndpointIdGenerator remote_id_generator_; - - DISALLOW_COPY_AND_ASSIGN(Channel); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_CHANNEL_H_ diff --git a/mojo/edk/system/channel_endpoint.cc b/mojo/edk/system/channel_endpoint.cc deleted file mode 100644 index 8ef1f40..0000000 --- a/mojo/edk/system/channel_endpoint.cc +++ /dev/null @@ -1,202 +0,0 @@ -// 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/edk/system/channel_endpoint.h" - -#include "base/logging.h" -#include "base/threading/platform_thread.h" -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/channel_endpoint_client.h" - -namespace mojo { -namespace system { - -ChannelEndpoint::ChannelEndpoint(ChannelEndpointClient* client, - unsigned client_port, - MessageInTransitQueue* message_queue) - : client_(client), - client_port_(client_port), - channel_(nullptr), - is_detached_from_channel_(false) { - DCHECK(client_ || message_queue); - - if (message_queue) - channel_message_queue_.Swap(message_queue); -} - -bool ChannelEndpoint::EnqueueMessage(scoped_ptr<MessageInTransit> message) { - DCHECK(message); - - base::AutoLock locker(lock_); - - if (!channel_) { - // We may reach here if we haven't been attached/run yet. - // TODO(vtl): We may also reach here if the channel is shut down early for - // some reason (with live message pipes on it). Ideally, we'd return false - // (and not enqueue the message), but we currently don't have a way to check - // this. - channel_message_queue_.AddMessage(message.Pass()); - return true; - } - - return WriteMessageNoLock(message.Pass()); -} - -bool ChannelEndpoint::ReplaceClient(ChannelEndpointClient* client, - unsigned client_port) { - DCHECK(client); - - base::AutoLock locker(lock_); - DCHECK(client_); - DCHECK(client != client_.get() || client_port != client_port_); - client_ = client; - client_port_ = client_port; - return !is_detached_from_channel_; -} - -void ChannelEndpoint::DetachFromClient() { - base::AutoLock locker(lock_); - DCHECK(client_); - client_ = nullptr; - - if (!channel_) - return; - channel_->DetachEndpoint(this, local_id_, remote_id_); - ResetChannelNoLock(); -} - -void ChannelEndpoint::AttachAndRun(Channel* channel, - ChannelEndpointId local_id, - ChannelEndpointId remote_id) { - DCHECK(channel); - DCHECK(local_id.is_valid()); - DCHECK(remote_id.is_valid()); - - base::AutoLock locker(lock_); - DCHECK(!channel_); - DCHECK(!local_id_.is_valid()); - DCHECK(!remote_id_.is_valid()); - channel_ = channel; - local_id_ = local_id; - remote_id_ = remote_id; - - while (!channel_message_queue_.IsEmpty()) { - LOG_IF(WARNING, !WriteMessageNoLock(channel_message_queue_.GetMessage())) - << "Failed to write enqueue message to channel"; - } - - if (!client_) { - channel_->DetachEndpoint(this, local_id_, remote_id_); - ResetChannelNoLock(); - } -} - -void ChannelEndpoint::OnReadMessage(scoped_ptr<MessageInTransit> message) { - scoped_refptr<ChannelEndpointClient> client; - unsigned client_port = 0; - - // This loop is to make |ReplaceClient()| work. We can't call the client's - // |OnReadMessage()| under our lock, so by the time we do that, |client| may - // no longer be our client. - // - // In that case, |client| must return false. We'll then yield, and retry with - // the new client. (Theoretically, the client could be replaced again.) - // - // This solution isn't terribly elegant, but it's the least costly way of - // handling/avoiding this (very unlikely) race. (Other solutions -- e.g., - // adding a client message queue, which the client only fetches messages from - // -- impose significant cost in the common case.) - for (;;) { - { - base::AutoLock locker(lock_); - if (!channel_ || !client_) { - // This isn't a failure per se. (It just means that, e.g., the other end - // of the message point closed first.) - return; - } - - // If we get here in a second (third, etc.) iteration of the loop, it's - // because |ReplaceClient()| was called. - DCHECK(client_ != client || client_port_ != client_port); - - // Take a ref, and call |OnReadMessage()| outside the lock. - client = client_; - client_port = client_port_; - } - - if (client->OnReadMessage(client_port, message.get())) { - ignore_result(message.release()); - break; - } - - base::PlatformThread::YieldCurrentThread(); - } -} - -void ChannelEndpoint::DetachFromChannel() { - scoped_refptr<ChannelEndpointClient> client; - unsigned client_port = 0; - { - base::AutoLock locker(lock_); - - if (client_) { - // Take a ref, and call |OnDetachFromChannel()| outside the lock. - client = client_; - client_port = client_port_; - } - - // |channel_| may already be null if we already detached from the channel in - // |DetachFromClient()| by calling |Channel::DetachEndpoint()| (and there - // are racing detaches). - if (channel_) - ResetChannelNoLock(); - else - DCHECK(is_detached_from_channel_); - } - - // If |ReplaceClient()| is called (from another thread) after the above locked - // section but before we call |OnDetachFromChannel()|, |ReplaceClient()| - // return false to notify the caller that the channel was already detached. - // (The old client has to accept the arguably-spurious call to - // |OnDetachFromChannel()|.) - if (client) - client->OnDetachFromChannel(client_port); -} - -ChannelEndpoint::~ChannelEndpoint() { - DCHECK(!client_); - DCHECK(!channel_); - DCHECK(!local_id_.is_valid()); - DCHECK(!remote_id_.is_valid()); -} - -bool ChannelEndpoint::WriteMessageNoLock(scoped_ptr<MessageInTransit> message) { - DCHECK(message); - - lock_.AssertAcquired(); - - DCHECK(channel_); - DCHECK(local_id_.is_valid()); - DCHECK(remote_id_.is_valid()); - - message->SerializeAndCloseDispatchers(channel_); - message->set_source_id(local_id_); - message->set_destination_id(remote_id_); - return channel_->WriteMessage(message.Pass()); -} - -void ChannelEndpoint::ResetChannelNoLock() { - DCHECK(channel_); - DCHECK(local_id_.is_valid()); - DCHECK(remote_id_.is_valid()); - DCHECK(!is_detached_from_channel_); - - channel_ = nullptr; - local_id_ = ChannelEndpointId(); - remote_id_ = ChannelEndpointId(); - is_detached_from_channel_ = true; -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/channel_endpoint.h b/mojo/edk/system/channel_endpoint.h deleted file mode 100644 index 90ee86b..0000000 --- a/mojo/edk/system/channel_endpoint.h +++ /dev/null @@ -1,210 +0,0 @@ -// 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_EDK_SYSTEM_CHANNEL_ENDPOINT_H_ -#define MOJO_EDK_SYSTEM_CHANNEL_ENDPOINT_H_ - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/system/channel_endpoint_id.h" -#include "mojo/edk/system/message_in_transit_queue.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace system { - -class Channel; -class ChannelEndpointClient; -class MessageInTransit; - -// TODO(vtl): The plan: -// - (Done.) Move |Channel::Endpoint| to |ChannelEndpoint|. Make it -// refcounted, and not copyable. Make |Channel| a friend. Make things work. -// - (Done.) Give |ChannelEndpoint| a lock. The lock order (in order of -// allowable acquisition) is: |MessagePipe|, |ChannelEndpoint|, |Channel|. -// - (Done) Stop having |Channel| as a friend. -// - (Done) Move logic from |ProxyMessagePipeEndpoint| into |ChannelEndpoint|. -// Right now, we have to go through lots of contortions to manipulate state -// owned by |ProxyMessagePipeEndpoint| (in particular, |Channel::Endpoint| -// doesn't know about the remote ID; the local ID is duplicated in two -// places). Hollow out |ProxyMessagePipeEndpoint|, and have it just own a -// reference to |ChannelEndpoint| (hence the refcounting). -// - In essence, |ChannelEndpoint| becomes the thing that knows about -// channel-specific aspects of an endpoint (notably local and remote IDs, -// and knowledge about handshaking), and mediates between the |Channel| and -// the |MessagePipe|. -// - In the end state, |Channel| should no longer need to know about -// |MessagePipe| and ports (but only |ChannelEndpoint|) and -// |ProxyMessagePipeEndpoint| should no longer need to know about |Channel| -// (ditto). -// -// Things as they are now, before I change everything (TODO(vtl): update this -// comment appropriately): -// -// Terminology: -// - "Message pipe endpoint": In the implementation, a |MessagePipe| owns -// two |MessagePipeEndpoint| objects, one for each port. The -// |MessagePipeEndpoint| objects are only accessed via the |MessagePipe| -// (which has the lock), with the additional information of the port -// number. So as far as the channel is concerned, a message pipe endpoint -// is a pointer to a |MessagePipe| together with the port number. -// - The value of |port| in |EndpointInfo| refers to the -// |ProxyMessagePipeEndpoint| (i.e., the endpoint that is logically on -// the other side). Messages received by a channel for a message pipe -// are thus written to the *peer* of this port. -// - "Attached"/"detached": A message pipe endpoint is attached to a channel -// if it has a pointer to it. It must be detached before the channel gives -// up its pointer to it in order to break a reference cycle. (This cycle -// is needed to allow a channel to be shut down cleanly, without shutting -// down everything else first.) -// - "Running" (message pipe endpoint): A message pipe endpoint is running -// if messages written to it (via some |MessagePipeDispatcher|, to which -// some |MojoHandle| is assigned) are being transmitted through the -// channel. -// - Before a message pipe endpoint is run, it will queue messages. -// - When a message pipe endpoint is detached from a channel, it is also -// taken out of the running state. After that point, messages should -// no longer be written to it. -// - "Normal" message pipe endpoint (state): The channel itself does not -// have knowledge of whether a message pipe endpoint has started running -// yet. It will *receive* messages for a message pipe in either state (but -// the message pipe endpoint won't *send* messages to the channel if it -// has not started running). -// - "Zombie" message pipe endpoint (state): A message pipe endpoint is a -// zombie if it is still in |local_id_to_endpoint_info_map_|, but the -// channel is no longer forwarding messages to it (even if it may still be -// receiving messages for it). -// - There are various types of zombies, depending on the reason the -// message pipe endpoint cannot yet be removed. -// - If the remote side is closed, it will send a "remove" control -// message. After the channel receives that message (to which it -// responds with a "remove ack" control message), it knows that it -// shouldn't receive any more messages for that message pipe endpoint -// (local ID), but it must wait for the endpoint to detach. (It can't -// do so without a race, since it can't call into the message pipe -// under |lock_|.) [TODO(vtl): When I add remotely-allocated IDs, -// we'll have to remove the |EndpointInfo| from -// |local_id_to_endpoint_info_map_| -- i.e., remove the local ID, -// since it's no longer valid and may be reused by the remote side -- -// and keep the |EndpointInfo| alive in some other way.] -// - If the local side is closed and the message pipe endpoint was -// already running (so there are no queued messages left to send), it -// will detach the endpoint, and send a "remove" control message. -// However, the channel may still receive messages for that endpoint -// until it receives a "remove ack" control message. -// - If the local side is closed but the message pipe endpoint was not -// yet running , the detaching is delayed until after it is run and -// all the queued messages are sent to the channel. On being detached, -// things proceed as in one of the above cases. The endpoint is *not* -// a zombie until it is detached (or a "remove" message is received). -// [TODO(vtl): Maybe we can get rid of this case? It'd only not yet be -// running since under the current scheme it wouldn't have a remote ID -// yet.] -// - Note that even if the local side is closed, it may still receive a -// "remove" message from the other side (if the other side is closed -// simultaneously, and both sides send "remove" messages). In that -// case, it must still remain alive until it receives the "remove -// ack" (and it must ack the "remove" message that it received). -class MOJO_SYSTEM_IMPL_EXPORT ChannelEndpoint - : public base::RefCountedThreadSafe<ChannelEndpoint> { - public: - // Constructor for a |ChannelEndpoint| with the given client (specified by - // |client| and |client_port|). Optionally takes messages from - // |*message_queue| if |message_queue| is non-null. - // - // |client| may be null if this endpoint will never need to receive messages, - // in which case |message_queue| should not be null. In that case, this - // endpoint will simply send queued messages upon being attached to a - // |Channel| and immediately detach itself. - ChannelEndpoint(ChannelEndpointClient* client, - unsigned client_port, - MessageInTransitQueue* message_queue = nullptr); - - // Methods called by |ChannelEndpointClient|: - - // Called to enqueue an outbound message. (If |AttachAndRun()| has not yet - // been called, the message will be enqueued and sent when |AttachAndRun()| is - // called.) - bool EnqueueMessage(scoped_ptr<MessageInTransit> message); - - // Called to *replace* current client with a new client (which must differ - // from the existing client). This must not be called after - // |DetachFromClient()| has been called. - // - // This returns true in the typical case, and false if this endpoint has been - // detached from the channel, in which case the caller should probably call - // its (new) client's |OnDetachFromChannel()|. - bool ReplaceClient(ChannelEndpointClient* client, unsigned client_port); - - // Called before the |ChannelEndpointClient| gives up its reference to this - // object. - void DetachFromClient(); - - // Methods called by |Channel|: - - // Called when the |Channel| takes a reference to this object. This will send - // all queue messages (in |channel_message_queue_|). - // TODO(vtl): Maybe rename this "OnAttach"? - void AttachAndRun(Channel* channel, - ChannelEndpointId local_id, - ChannelEndpointId remote_id); - - // Called when the |Channel| receives a message for the |ChannelEndpoint|. - void OnReadMessage(scoped_ptr<MessageInTransit> message); - - // Called before the |Channel| gives up its reference to this object. - void DetachFromChannel(); - - private: - friend class base::RefCountedThreadSafe<ChannelEndpoint>; - ~ChannelEndpoint(); - - // Must be called with |lock_| held. - bool WriteMessageNoLock(scoped_ptr<MessageInTransit> message); - - // Resets |channel_| to null (and sets |is_detached_from_channel_|). This may - // only be called if |channel_| is non-null. Must be called with |lock_| held. - void ResetChannelNoLock(); - - // Protects the members below. - base::Lock lock_; - - // |client_| must be valid whenever it is non-null. Before |*client_| gives up - // its reference to this object, it must call |DetachFromClient()|. - // NOTE: This is a |scoped_refptr<>|, rather than a raw pointer, since the - // |Channel| needs to keep the |MessagePipe| alive for the "proxy-proxy" case. - // Possibly we'll be able to eliminate that case when we have full - // multiprocess support. - // WARNING: |ChannelEndpointClient| methods must not be called under |lock_|. - // Thus to make such a call, a reference must first be taken under |lock_| and - // the lock released. - // WARNING: Beware of interactions with |ReplaceClient()|. By the time the - // call is made, the client may have changed. This must be detected and dealt - // with. - scoped_refptr<ChannelEndpointClient> client_; - unsigned client_port_; - - // |channel_| must be valid whenever it is non-null. Before |*channel_| gives - // up its reference to this object, it must call |DetachFromChannel()|. - // |local_id_| and |remote_id_| are valid if and only |channel_| is non-null. - Channel* channel_; - ChannelEndpointId local_id_; - ChannelEndpointId remote_id_; - // This distinguishes the two cases of |channel| being null: not yet attached - // versus detached. - bool is_detached_from_channel_; - - // This queue is used before we're running on a channel and ready to send - // messages to the channel. - MessageInTransitQueue channel_message_queue_; - - DISALLOW_COPY_AND_ASSIGN(ChannelEndpoint); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_CHANNEL_ENDPOINT_H_ diff --git a/mojo/edk/system/channel_endpoint_client.h b/mojo/edk/system/channel_endpoint_client.h deleted file mode 100644 index c758e9c..0000000 --- a/mojo/edk/system/channel_endpoint_client.h +++ /dev/null @@ -1,63 +0,0 @@ -// 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_EDK_SYSTEM_CHANNEL_ENDPOINT_CLIENT_H_ -#define MOJO_EDK_SYSTEM_CHANNEL_ENDPOINT_CLIENT_H_ - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace system { - -class MessageInTransit; - -// Interface for receivers of messages from |ChannelEndpoint| (hence from -// |Channel|). |port| is simply the value passed to |ChannelEndpoint| on -// construction, and provides a lightweight way for an object to be the client -// of multiple |ChannelEndpoint|s. (|MessagePipe| implements this interface, in -// which case |port| is the port number for the |ProxyMessagePipeEndpoint| -// corresdponding to the |ChannelEndpoint|.) -// -// Implementations of this class should be thread-safe. |ChannelEndpointClient| -// *precedes* |ChannelEndpoint| in the lock order, so |ChannelEndpoint| should -// never call into this class with its lock held. (Instead, it should take a -// reference under its lock, release its lock, and make any needed call(s).) -// -// Note: As a consequence of this, all the client methods may be called even -// after |ChannelEndpoint::DetachFromClient()| has been called (so the -// |ChannelEndpoint| has apparently relinquished its pointer to the -// |ChannelEndpointClient|). -class MOJO_SYSTEM_IMPL_EXPORT ChannelEndpointClient - : public base::RefCountedThreadSafe<ChannelEndpointClient> { - public: - // Called by |ChannelEndpoint| in response to its |OnReadMessage()|, which is - // called by |Channel| when it receives a message for the |ChannelEndpoint|. - // (|port| is the value passed to |ChannelEndpoint|'s constructor as - // |client_port|.) - // - // This should return true if it accepted (and took ownership of) |message|. - virtual bool OnReadMessage(unsigned port, MessageInTransit* message) = 0; - - // Called by |ChannelEndpoint| when the |Channel| is relinquishing its pointer - // to the |ChannelEndpoint| (and vice versa). After this is called, - // |OnReadMessage()| will no longer be called. - virtual void OnDetachFromChannel(unsigned port) = 0; - - protected: - ChannelEndpointClient() {} - - virtual ~ChannelEndpointClient() {} - friend class base::RefCountedThreadSafe<ChannelEndpointClient>; - - private: - DISALLOW_COPY_AND_ASSIGN(ChannelEndpointClient); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_CHANNEL_ENDPOINT_CLIENT_H_ diff --git a/mojo/edk/system/channel_endpoint_id.cc b/mojo/edk/system/channel_endpoint_id.cc deleted file mode 100644 index ed00f6a..0000000 --- a/mojo/edk/system/channel_endpoint_id.cc +++ /dev/null @@ -1,30 +0,0 @@ -// 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/edk/system/channel_endpoint_id.h" - -#include "base/compiler_specific.h" - -namespace mojo { -namespace system { - -STATIC_CONST_MEMBER_DEFINITION const uint32_t ChannelEndpointId::kRemoteFlag; - -ChannelEndpointId LocalChannelEndpointIdGenerator::GetNext() { - ChannelEndpointId rv = next_; - next_.value_ = (next_.value_ + 1) & ~ChannelEndpointId::kRemoteFlag; - // Skip over the invalid value, in case we wrap. - if (!next_.is_valid()) - next_.value_++; - return rv; -} - -ChannelEndpointId RemoteChannelEndpointIdGenerator::GetNext() { - ChannelEndpointId rv = next_; - next_.value_ = (next_.value_ + 1) | ChannelEndpointId::kRemoteFlag; - return rv; -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/channel_endpoint_id.h b/mojo/edk/system/channel_endpoint_id.h deleted file mode 100644 index c673258..0000000 --- a/mojo/edk/system/channel_endpoint_id.h +++ /dev/null @@ -1,147 +0,0 @@ -// 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_EDK_SYSTEM_CHANNEL_ENDPOINT_ID_H_ -#define MOJO_EDK_SYSTEM_CHANNEL_ENDPOINT_ID_H_ - -#include <stddef.h> -#include <stdint.h> - -#include <ostream> - -#include "base/containers/hash_tables.h" -#include "base/gtest_prod_util.h" -#include "base/macros.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace system { - -// ChannelEndpointId ----------------------------------------------------------- - -class LocalChannelEndpointIdGenerator; -FORWARD_DECLARE_TEST(LocalChannelEndpointIdGeneratorTest, WrapAround); -FORWARD_DECLARE_TEST(RemoteChannelEndpointIdGeneratorTest, WrapAround); - -// Represents an ID for an endpoint (i.e., one side of a message pipe) on a -// |Channel|. This class must be POD. -// -// Note: The terminology "remote" for a |ChannelEndpointId| means a destination -// ID that was actually allocated by the sender, or similarly a source ID that -// was allocated by the receiver. -// -// From the standpoint of the |Channel| with such a remote ID in its endpoint -// table, such an ID is a "remotely-allocated local ID". From the standpoint of -// the |Channel| allocating such a remote ID (for its peer |Channel|), it's a -// "locally-allocated remote ID". -class MOJO_SYSTEM_IMPL_EXPORT ChannelEndpointId { - public: - ChannelEndpointId() : value_(0) {} - ChannelEndpointId(const ChannelEndpointId& other) : value_(other.value_) {} - - // Returns the local ID to use for the first message pipe endpoint on a - // channel. - static ChannelEndpointId GetBootstrap() { return ChannelEndpointId(1); } - - bool operator==(const ChannelEndpointId& other) const { - return value_ == other.value_; - } - bool operator!=(const ChannelEndpointId& other) const { - return !operator==(other); - } - // So that we can be used in |std::map|, etc. - bool operator<(const ChannelEndpointId& other) const { - return value_ < other.value_; - } - - bool is_valid() const { return !!value_; } - bool is_remote() const { return !!(value_ & kRemoteFlag); } - const uint32_t& value() const { return value_; } - - // Flag set in |value()| if this is a remote ID. - static const uint32_t kRemoteFlag = 0x80000000u; - - private: - friend class LocalChannelEndpointIdGenerator; - FRIEND_TEST_ALL_PREFIXES(LocalChannelEndpointIdGeneratorTest, WrapAround); - friend class RemoteChannelEndpointIdGenerator; - FRIEND_TEST_ALL_PREFIXES(RemoteChannelEndpointIdGeneratorTest, WrapAround); - - explicit ChannelEndpointId(uint32_t value) : value_(value) {} - - uint32_t value_; - - // Copying and assignment allowed. -}; -// This wrapper should add no overhead. -// TODO(vtl): Rewrite |sizeof(uint32_t)| as |sizeof(ChannelEndpointId::value)| -// once we have sufficient C++11 support. -static_assert(sizeof(ChannelEndpointId) == sizeof(uint32_t), - "ChannelEndpointId has incorrect size"); - -// So logging macros and |DCHECK_EQ()|, etc. work. -inline std::ostream& operator<<(std::ostream& out, - const ChannelEndpointId& channel_endpoint_id) { - return out << channel_endpoint_id.value(); -} - -// LocalChannelEndpointIdGenerator --------------------------------------------- - -// A generator for "new" local |ChannelEndpointId|s. It does not track -// used/existing IDs; that must be done separately. (This class is not -// thread-safe.) -class MOJO_SYSTEM_IMPL_EXPORT LocalChannelEndpointIdGenerator { - public: - LocalChannelEndpointIdGenerator() - : next_(ChannelEndpointId::GetBootstrap()) {} - - ChannelEndpointId GetNext(); - - private: - FRIEND_TEST_ALL_PREFIXES(LocalChannelEndpointIdGeneratorTest, WrapAround); - - ChannelEndpointId next_; - - DISALLOW_COPY_AND_ASSIGN(LocalChannelEndpointIdGenerator); -}; - -// RemoteChannelEndpointIdGenerator -------------------------------------------- - -// A generator for "new" remote |ChannelEndpointId|s, for |Channel|s to -// locally allocate remote IDs. (See the comment above |ChannelEndpointId| for -// an explanatory note.) It does not track used/existing IDs; that must be done -// separately. (This class is not thread-safe.) -class MOJO_SYSTEM_IMPL_EXPORT RemoteChannelEndpointIdGenerator { - public: - RemoteChannelEndpointIdGenerator() : next_(ChannelEndpointId::kRemoteFlag) {} - - ChannelEndpointId GetNext(); - - private: - FRIEND_TEST_ALL_PREFIXES(RemoteChannelEndpointIdGeneratorTest, WrapAround); - - ChannelEndpointId next_; - - DISALLOW_COPY_AND_ASSIGN(RemoteChannelEndpointIdGenerator); -}; - -} // namespace system -} // namespace mojo - -// Define "hash" functions for |ChannelEndpointId|s, so they can be used in hash -// tables. -// TODO(vtl): Once we can use |std::unordered_{map,set}|, update this (and -// remove the base/containers/hash_tables.h include). -namespace BASE_HASH_NAMESPACE { - -template <> -struct hash<mojo::system::ChannelEndpointId> { - size_t operator()(mojo::system::ChannelEndpointId channel_endpoint_id) const { - return static_cast<size_t>(channel_endpoint_id.value()); - } -}; - -} // namespace BASE_HASH_NAMESPACE - -#endif // MOJO_EDK_SYSTEM_CHANNEL_ENDPOINT_ID_H_ diff --git a/mojo/edk/system/channel_endpoint_id_unittest.cc b/mojo/edk/system/channel_endpoint_id_unittest.cc deleted file mode 100644 index a5bd14f..0000000 --- a/mojo/edk/system/channel_endpoint_id_unittest.cc +++ /dev/null @@ -1,155 +0,0 @@ -// 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/edk/system/channel_endpoint_id.h" - -#include <sstream> - -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -TEST(ChannelEndpointIdTest, Basic) { - ChannelEndpointId invalid; - ChannelEndpointId bootstrap(ChannelEndpointId::GetBootstrap()); - - EXPECT_EQ(invalid, invalid); - EXPECT_EQ(bootstrap, bootstrap); - EXPECT_FALSE(invalid == bootstrap); - - EXPECT_FALSE(invalid != invalid); - EXPECT_FALSE(bootstrap != bootstrap); - EXPECT_NE(invalid, bootstrap); - - EXPECT_FALSE(invalid < invalid); - EXPECT_LT(invalid, bootstrap); - - EXPECT_FALSE(invalid.is_valid()); - EXPECT_TRUE(bootstrap.is_valid()); - - EXPECT_FALSE(invalid.is_remote()); - EXPECT_FALSE(bootstrap.is_remote()); - - // Test assignment. - ChannelEndpointId copy; - copy = bootstrap; - EXPECT_EQ(copy, bootstrap); - copy = invalid; - EXPECT_EQ(copy, invalid); -} - -// Tests values of invalid and bootstrap IDs. (This tests implementation -// details.) -TEST(ChannelEndpointIdTest, Value) { - EXPECT_EQ(0u, ChannelEndpointId().value()); - EXPECT_EQ(1u, ChannelEndpointId::GetBootstrap().value()); -} - -// Tests ostream output. (This tests implementation details.) -TEST(ChannelEndpointIdTest, Ostream) { - { - std::ostringstream stream; - stream << ChannelEndpointId(); - EXPECT_EQ("0", stream.str()); - } - { - std::ostringstream stream; - stream << ChannelEndpointId::GetBootstrap(); - EXPECT_EQ("1", stream.str()); - } -} - -TEST(LocalChannelEndpointIdGeneratorTest, Basic) { - LocalChannelEndpointIdGenerator gen; - - ChannelEndpointId id1; - EXPECT_FALSE(id1.is_valid()); // Check sanity. - - id1 = gen.GetNext(); - EXPECT_TRUE(id1.is_valid()); - EXPECT_FALSE(id1.is_remote()); - - EXPECT_EQ(ChannelEndpointId::GetBootstrap().value(), id1.value()); - - ChannelEndpointId id2 = gen.GetNext(); - EXPECT_TRUE(id2.is_valid()); - EXPECT_FALSE(id2.is_remote()); - // Technically, nonequality here is an implementation detail, since, e.g., - // random generation of IDs would be a valid implementation. - EXPECT_NE(id2, id1); - // ... but right now we just increment to generate IDs. - EXPECT_EQ(2u, id2.value()); -} - -// Note: LocalChannelEndpointIdGeneratorTest.WrapAround is defined further -// below, outside the anonymous namespace. - -TEST(RemoteChannelEndpointIdGeneratorTest, Basic) { - RemoteChannelEndpointIdGenerator gen; - - ChannelEndpointId id1; - EXPECT_FALSE(id1.is_valid()); // Check sanity. - - id1 = gen.GetNext(); - EXPECT_TRUE(id1.is_valid()); - EXPECT_TRUE(id1.is_remote()); - - // This tests an implementation detail. - EXPECT_EQ(ChannelEndpointId::kRemoteFlag, id1.value()); - - ChannelEndpointId id2 = gen.GetNext(); - EXPECT_TRUE(id2.is_valid()); - EXPECT_TRUE(id2.is_remote()); - // Technically, nonequality here is an implementation detail, since, e.g., - // random generation of IDs would be a valid implementation. - EXPECT_NE(id2, id1); - // ... but right now we just increment to generate IDs. - EXPECT_EQ(ChannelEndpointId::kRemoteFlag + 1, id2.value()); -} - -// Note: RemoteChannelEndpointIdGeneratorTest.WrapAround is defined further -// below, outside the anonymous namespace. - -} // namespace - -// Tests that |LocalChannelEndpointIdGenerator| handles wrap-around correctly. -// (This tests implementation details.) This test isn't in an anonymous -// namespace, since it needs to be friended. -TEST(LocalChannelEndpointIdGeneratorTest, WrapAround) { - LocalChannelEndpointIdGenerator gen; - gen.next_ = ChannelEndpointId(ChannelEndpointId::kRemoteFlag - 1); - - ChannelEndpointId id = gen.GetNext(); - EXPECT_TRUE(id.is_valid()); - EXPECT_FALSE(id.is_remote()); - EXPECT_EQ(ChannelEndpointId::kRemoteFlag - 1, id.value()); - - id = gen.GetNext(); - EXPECT_TRUE(id.is_valid()); - EXPECT_FALSE(id.is_remote()); - EXPECT_EQ(1u, id.value()); -} - -// Tests that |RemoteChannelEndpointIdGenerator| handles wrap-around correctly. -// (This tests implementation details.) This test isn't in an anonymous -// namespace, since it needs to be friended. -TEST(RemoteChannelEndpointIdGeneratorTest, WrapAround) { - RemoteChannelEndpointIdGenerator gen; - gen.next_ = ChannelEndpointId(~0u); - - ChannelEndpointId id = gen.GetNext(); - EXPECT_TRUE(id.is_valid()); - EXPECT_TRUE(id.is_remote()); - EXPECT_EQ(~0u, id.value()); - - id = gen.GetNext(); - EXPECT_TRUE(id.is_valid()); - EXPECT_TRUE(id.is_remote()); - EXPECT_EQ(ChannelEndpointId::kRemoteFlag, id.value()); -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/channel_info.cc b/mojo/edk/system/channel_info.cc deleted file mode 100644 index 6d23215..0000000 --- a/mojo/edk/system/channel_info.cc +++ /dev/null @@ -1,31 +0,0 @@ -// 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/edk/system/channel_info.h" - -#include <algorithm> - -namespace mojo { -namespace system { - -ChannelInfo::ChannelInfo() { -} - -ChannelInfo::ChannelInfo( - scoped_refptr<Channel> channel, - scoped_refptr<base::TaskRunner> channel_thread_task_runner) - : channel(channel), channel_thread_task_runner(channel_thread_task_runner) { -} - -ChannelInfo::~ChannelInfo() { -} - -void ChannelInfo::Swap(ChannelInfo* other) { - // Note: Swapping avoids refcount churn. - std::swap(channel, other->channel); - std::swap(channel_thread_task_runner, other->channel_thread_task_runner); -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/channel_info.h b/mojo/edk/system/channel_info.h deleted file mode 100644 index 5167cca..0000000 --- a/mojo/edk/system/channel_info.h +++ /dev/null @@ -1,33 +0,0 @@ -// 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_EDK_SYSTEM_CHANNEL_INFO_H_ -#define MOJO_EDK_SYSTEM_CHANNEL_INFO_H_ - -#include "base/memory/ref_counted.h" -#include "base/task_runner.h" -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace system { - -struct MOJO_SYSTEM_IMPL_EXPORT ChannelInfo { - ChannelInfo(); - ChannelInfo(scoped_refptr<Channel> channel, - scoped_refptr<base::TaskRunner> channel_thread_task_runner); - ~ChannelInfo(); - - void Swap(ChannelInfo* other); - - scoped_refptr<Channel> channel; - // The task runner for |channel|'s creation thread (a.k.a. its I/O thread), on - // which it must, e.g., be shut down. - scoped_refptr<base::TaskRunner> channel_thread_task_runner; -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_CHANNEL_INFO_H_ diff --git a/mojo/edk/system/channel_manager.cc b/mojo/edk/system/channel_manager.cc deleted file mode 100644 index 4e58f89..0000000 --- a/mojo/edk/system/channel_manager.cc +++ /dev/null @@ -1,78 +0,0 @@ -// 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/edk/system/channel_manager.h" - -#include "base/bind.h" -#include "base/location.h" -#include "base/message_loop/message_loop_proxy.h" - -namespace mojo { -namespace system { - -namespace { - -void ShutdownChannelHelper(const ChannelInfo& channel_info) { - if (base::MessageLoopProxy::current() == - channel_info.channel_thread_task_runner) { - channel_info.channel->Shutdown(); - } else { - channel_info.channel->WillShutdownSoon(); - channel_info.channel_thread_task_runner->PostTask( - FROM_HERE, base::Bind(&Channel::Shutdown, channel_info.channel)); - } -} - -} // namespace - -ChannelManager::ChannelManager() { -} - -ChannelManager::~ChannelManager() { - // No need to take the lock. - for (const auto& map_elem : channel_infos_) - ShutdownChannelHelper(map_elem.second); -} - -ChannelId ChannelManager::AddChannel( - scoped_refptr<Channel> channel, - scoped_refptr<base::TaskRunner> channel_thread_task_runner) { - ChannelId channel_id = GetChannelId(channel.get()); - - { - base::AutoLock locker(lock_); - DCHECK(channel_infos_.find(channel_id) == channel_infos_.end()); - channel_infos_[channel_id] = - ChannelInfo(channel, channel_thread_task_runner); - } - channel->SetChannelManager(this); - - return channel_id; -} - -void ChannelManager::WillShutdownChannel(ChannelId channel_id) { - GetChannelInfo(channel_id).channel->WillShutdownSoon(); -} - -void ChannelManager::ShutdownChannel(ChannelId channel_id) { - ChannelInfo channel_info; - { - base::AutoLock locker(lock_); - auto it = channel_infos_.find(channel_id); - DCHECK(it != channel_infos_.end()); - channel_info.Swap(&it->second); - channel_infos_.erase(it); - } - ShutdownChannelHelper(channel_info); -} - -ChannelInfo ChannelManager::GetChannelInfo(ChannelId channel_id) { - base::AutoLock locker(lock_); - auto it = channel_infos_.find(channel_id); - DCHECK(it != channel_infos_.end()); - return it->second; -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/channel_manager.h b/mojo/edk/system/channel_manager.h deleted file mode 100644 index bb6371b..0000000 --- a/mojo/edk/system/channel_manager.h +++ /dev/null @@ -1,83 +0,0 @@ -// 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_EDK_SYSTEM_CHANNEL_MANAGER_H_ -#define MOJO_EDK_SYSTEM_CHANNEL_MANAGER_H_ - -#include <stdint.h> - -#include "base/containers/hash_tables.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "base/task_runner.h" -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/channel_info.h" - -namespace mojo { -namespace system { - -// IDs for |Channel|s managed by a |ChannelManager|. (IDs should be thought of -// as specific to a given |ChannelManager|.) 0 is never a valid ID. -// -// Note: We currently just use the pointer of the |Channel| casted to a -// |uintptr_t|, but we reserve the right to change this. -typedef uintptr_t ChannelId; - -// This class manages and "owns" |Channel|s (which typically connect to other -// processes) for a given process. This class is thread-safe. -class MOJO_SYSTEM_IMPL_EXPORT ChannelManager { - public: - ChannelManager(); - ~ChannelManager(); - - // Adds |channel| to the set of |Channel|s managed by this |ChannelManager|; - // |channel_thread_task_runner| should be the task runner for |channel|'s - // creation (a.k.a. I/O) thread. |channel| should either already be - // initialized. It should not be managed by any |ChannelManager| yet. Returns - // the ID for the added channel. - ChannelId AddChannel( - scoped_refptr<Channel> channel, - scoped_refptr<base::TaskRunner> channel_thread_task_runner); - - // Informs the channel manager (and thus channel) that it will be shutdown - // soon (by calling |ShutdownChannel()|). Calling this is optional (and may in - // fact be called multiple times) but it will suppress certain warnings (e.g., - // for the channel being broken) and enable others (if messages are written to - // the channel). - void WillShutdownChannel(ChannelId channel_id); - - // Shuts down the channel specified by the given ID. It is up to the caller to - // guarantee that this is only called once per channel (that was added using - // |AddChannel()|). If called from the chanel's creation thread (i.e., - // |base::MessageLoopProxy::current()| is the channel thread's |TaskRunner|), - // this will complete synchronously. - void ShutdownChannel(ChannelId channel_id); - - private: - // Gets the ID for a given channel. - // - // Note: This is currently a static method and thus may be called under - // |lock_|. If this is ever made non-static (i.e., made specific to a given - // |ChannelManager|), those call sites may have to changed. - static ChannelId GetChannelId(const Channel* channel) { - return reinterpret_cast<ChannelId>(channel); - } - - // Gets the |ChannelInfo| for the channel specified by the given ID. (This - // should *not* be called under lock.) - ChannelInfo GetChannelInfo(ChannelId channel_id); - - // Note: |Channel| methods should not be called under |lock_|. - base::Lock lock_; // Protects the members below. - - base::hash_map<ChannelId, ChannelInfo> channel_infos_; - - DISALLOW_COPY_AND_ASSIGN(ChannelManager); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_CHANNEL_MANAGER_H_ diff --git a/mojo/edk/system/channel_manager_unittest.cc b/mojo/edk/system/channel_manager_unittest.cc deleted file mode 100644 index 52f3f0e..0000000 --- a/mojo/edk/system/channel_manager_unittest.cc +++ /dev/null @@ -1,183 +0,0 @@ -// 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/edk/system/channel_manager.h" - -#include "base/callback.h" -#include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_proxy.h" -#include "base/run_loop.h" -#include "base/task_runner.h" -#include "base/test/test_timeouts.h" -#include "base/threading/platform_thread.h" -#include "base/threading/simple_thread.h" -#include "base/time/time.h" -#include "mojo/edk/embedder/platform_channel_pair.h" -#include "mojo/edk/embedder/simple_platform_support.h" -#include "mojo/edk/system/channel.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -class ChannelManagerTest : public testing::Test { - public: - ChannelManagerTest() : message_loop_(base::MessageLoop::TYPE_IO) {} - ~ChannelManagerTest() override {} - - protected: - embedder::SimplePlatformSupport* platform_support() { - return &platform_support_; - } - base::MessageLoop* message_loop() { return &message_loop_; } - - private: - embedder::SimplePlatformSupport platform_support_; - base::MessageLoop message_loop_; - - DISALLOW_COPY_AND_ASSIGN(ChannelManagerTest); -}; - -TEST_F(ChannelManagerTest, Basic) { - ChannelManager cm; - - // Hang on to a ref to the |Channel|, so that we can check that the - // |ChannelManager| takes/releases refs to it. - scoped_refptr<Channel> ch(new Channel(platform_support())); - ASSERT_TRUE(ch->HasOneRef()); - - embedder::PlatformChannelPair channel_pair; - ch->Init(RawChannel::Create(channel_pair.PassServerHandle())); - - ChannelId id = cm.AddChannel(ch, base::MessageLoopProxy::current()); - EXPECT_NE(id, 0u); - // |ChannelManager| should take a ref. - EXPECT_FALSE(ch->HasOneRef()); - - cm.WillShutdownChannel(id); - // |ChannelManager| should still have a ref. - EXPECT_FALSE(ch->HasOneRef()); - - cm.ShutdownChannel(id); - // On the "I/O" thread, so shutdown should happen synchronously. - // |ChannelManager| should have given up its ref. - EXPECT_TRUE(ch->HasOneRef()); -} - -TEST_F(ChannelManagerTest, TwoChannels) { - ChannelManager cm; - - // Hang on to a ref to each |Channel|, so that we can check that the - // |ChannelManager| takes/releases refs to them. - scoped_refptr<Channel> ch1(new Channel(platform_support())); - ASSERT_TRUE(ch1->HasOneRef()); - scoped_refptr<Channel> ch2(new Channel(platform_support())); - ASSERT_TRUE(ch2->HasOneRef()); - - embedder::PlatformChannelPair channel_pair; - ch1->Init(RawChannel::Create(channel_pair.PassServerHandle())); - ch2->Init(RawChannel::Create(channel_pair.PassClientHandle())); - - ChannelId id1 = cm.AddChannel(ch1, base::MessageLoopProxy::current()); - EXPECT_NE(id1, 0u); - EXPECT_FALSE(ch1->HasOneRef()); - - ChannelId id2 = cm.AddChannel(ch2, base::MessageLoopProxy::current()); - EXPECT_NE(id2, 0u); - EXPECT_NE(id2, id1); - EXPECT_FALSE(ch2->HasOneRef()); - - // Calling |WillShutdownChannel()| multiple times (on |id1|) is okay. - cm.WillShutdownChannel(id1); - cm.WillShutdownChannel(id1); - EXPECT_FALSE(ch1->HasOneRef()); - // Not calling |WillShutdownChannel()| (on |id2|) is okay too. - - cm.ShutdownChannel(id1); - EXPECT_TRUE(ch1->HasOneRef()); - cm.ShutdownChannel(id2); - EXPECT_TRUE(ch2->HasOneRef()); -} - -class OtherThread : public base::SimpleThread { - public: - // Note: We rely on the main thread keeping *exactly one* reference to - // |channel|. - OtherThread(scoped_refptr<base::TaskRunner> task_runner, - ChannelManager* channel_manager, - Channel* channel, - base::Closure quit_closure) - : base::SimpleThread("other_thread"), - task_runner_(task_runner), - channel_manager_(channel_manager), - channel_(channel), - quit_closure_(quit_closure) {} - ~OtherThread() override {} - - private: - void Run() override { - // See comment above constructor. - ASSERT_TRUE(channel_->HasOneRef()); - - ChannelId id = channel_manager_->AddChannel(make_scoped_refptr(channel_), - task_runner_); - EXPECT_NE(id, 0u); - // |ChannelManager| should take a ref. - EXPECT_FALSE(channel_->HasOneRef()); - - channel_manager_->WillShutdownChannel(id); - // |ChannelManager| should still have a ref. - EXPECT_FALSE(channel_->HasOneRef()); - - channel_manager_->ShutdownChannel(id); - // This doesn't happen synchronously, so we "wait" until it does. - // TODO(vtl): Possibly |Channel| should provide some notification of being - // shut down. - base::TimeTicks start_time(base::TimeTicks::Now()); - for (;;) { - if (channel_->HasOneRef()) - break; - - // Check, instead of assert, since if things go wrong, dying is more - // reliable than tearing down. - CHECK_LT(base::TimeTicks::Now() - start_time, - TestTimeouts::action_timeout()); - base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20)); - } - - CHECK(task_runner_->PostTask(FROM_HERE, quit_closure_)); - } - - scoped_refptr<base::TaskRunner> task_runner_; - ChannelManager* channel_manager_; - Channel* channel_; - base::Closure quit_closure_; - - DISALLOW_COPY_AND_ASSIGN(OtherThread); -}; - -TEST_F(ChannelManagerTest, CallsFromOtherThread) { - ChannelManager cm; - - // Hang on to a ref to the |Channel|, so that we can check that the - // |ChannelManager| takes/releases refs to it. - scoped_refptr<Channel> ch(new Channel(platform_support())); - ASSERT_TRUE(ch->HasOneRef()); - - embedder::PlatformChannelPair channel_pair; - ch->Init(RawChannel::Create(channel_pair.PassServerHandle())); - - base::RunLoop run_loop; - OtherThread thread(base::MessageLoopProxy::current(), &cm, ch.get(), - run_loop.QuitClosure()); - thread.Start(); - run_loop.Run(); - thread.Join(); -} - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/channel_unittest.cc b/mojo/edk/system/channel_unittest.cc deleted file mode 100644 index e82b321..0000000 --- a/mojo/edk/system/channel_unittest.cc +++ /dev/null @@ -1,210 +0,0 @@ -// 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/edk/system/channel.h" - -#include "base/bind.h" -#include "base/location.h" -#include "base/message_loop/message_loop.h" -#include "base/test/test_io_thread.h" -#include "mojo/edk/embedder/platform_channel_pair.h" -#include "mojo/edk/embedder/simple_platform_support.h" -#include "mojo/edk/system/channel_endpoint.h" -#include "mojo/edk/system/channel_endpoint_id.h" -#include "mojo/edk/system/message_pipe.h" -#include "mojo/edk/system/raw_channel.h" -#include "mojo/edk/system/test_utils.h" -#include "mojo/edk/system/waiter.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -class ChannelTest : public testing::Test { - public: - ChannelTest() : io_thread_(base::TestIOThread::kAutoStart) {} - ~ChannelTest() override {} - - void SetUp() override { - io_thread_.PostTaskAndWait( - FROM_HERE, - base::Bind(&ChannelTest::SetUpOnIOThread, base::Unretained(this))); - } - - void CreateChannelOnIOThread() { - CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop()); - channel_ = new Channel(&platform_support_); - } - - void InitChannelOnIOThread() { - CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop()); - - CHECK(raw_channel_); - CHECK(channel_); - channel_->Init(raw_channel_.Pass()); - } - - void ShutdownChannelOnIOThread() { - CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop()); - - CHECK(channel_); - channel_->Shutdown(); - } - - base::TestIOThread* io_thread() { return &io_thread_; } - RawChannel* raw_channel() { return raw_channel_.get(); } - scoped_ptr<RawChannel>* mutable_raw_channel() { return &raw_channel_; } - Channel* channel() { return channel_.get(); } - scoped_refptr<Channel>* mutable_channel() { return &channel_; } - - private: - void SetUpOnIOThread() { - CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop()); - - embedder::PlatformChannelPair channel_pair; - raw_channel_ = RawChannel::Create(channel_pair.PassServerHandle()).Pass(); - other_platform_handle_ = channel_pair.PassClientHandle(); - } - - embedder::SimplePlatformSupport platform_support_; - base::TestIOThread io_thread_; - scoped_ptr<RawChannel> raw_channel_; - embedder::ScopedPlatformHandle other_platform_handle_; - scoped_refptr<Channel> channel_; - - DISALLOW_COPY_AND_ASSIGN(ChannelTest); -}; - -// ChannelTest.InitShutdown ---------------------------------------------------- - -TEST_F(ChannelTest, InitShutdown) { - io_thread()->PostTaskAndWait(FROM_HERE, - base::Bind(&ChannelTest::CreateChannelOnIOThread, - base::Unretained(this))); - ASSERT_TRUE(channel()); - - io_thread()->PostTaskAndWait( - FROM_HERE, - base::Bind(&ChannelTest::InitChannelOnIOThread, base::Unretained(this))); - - io_thread()->PostTaskAndWait( - FROM_HERE, base::Bind(&ChannelTest::ShutdownChannelOnIOThread, - base::Unretained(this))); - - // Okay to destroy |Channel| on not-the-I/O-thread. - EXPECT_TRUE(channel()->HasOneRef()); - *mutable_channel() = nullptr; -} - -// ChannelTest.CloseBeforeAttachAndRun ----------------------------------------- - -TEST_F(ChannelTest, CloseBeforeRun) { - io_thread()->PostTaskAndWait(FROM_HERE, - base::Bind(&ChannelTest::CreateChannelOnIOThread, - base::Unretained(this))); - ASSERT_TRUE(channel()); - - io_thread()->PostTaskAndWait( - FROM_HERE, - base::Bind(&ChannelTest::InitChannelOnIOThread, base::Unretained(this))); - - scoped_refptr<ChannelEndpoint> channel_endpoint; - scoped_refptr<MessagePipe> mp( - MessagePipe::CreateLocalProxy(&channel_endpoint)); - - mp->Close(0); - - channel()->SetBootstrapEndpoint(channel_endpoint); - - io_thread()->PostTaskAndWait( - FROM_HERE, base::Bind(&ChannelTest::ShutdownChannelOnIOThread, - base::Unretained(this))); - - EXPECT_TRUE(channel()->HasOneRef()); -} - -// ChannelTest.ShutdownAfterAttachAndRun --------------------------------------- - -TEST_F(ChannelTest, ShutdownAfterAttach) { - io_thread()->PostTaskAndWait(FROM_HERE, - base::Bind(&ChannelTest::CreateChannelOnIOThread, - base::Unretained(this))); - ASSERT_TRUE(channel()); - - io_thread()->PostTaskAndWait( - FROM_HERE, - base::Bind(&ChannelTest::InitChannelOnIOThread, base::Unretained(this))); - - scoped_refptr<ChannelEndpoint> channel_endpoint; - scoped_refptr<MessagePipe> mp( - MessagePipe::CreateLocalProxy(&channel_endpoint)); - - channel()->SetBootstrapEndpoint(channel_endpoint); - - Waiter waiter; - waiter.Init(); - ASSERT_EQ( - MOJO_RESULT_OK, - mp->AddAwakable(0, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, nullptr)); - - // Don't wait for the shutdown to run ... - io_thread()->PostTask(FROM_HERE, - base::Bind(&ChannelTest::ShutdownChannelOnIOThread, - base::Unretained(this))); - - // ... since this |Wait()| should fail once the channel is shut down. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - waiter.Wait(MOJO_DEADLINE_INDEFINITE, nullptr)); - HandleSignalsState hss; - mp->RemoveAwakable(0, &waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - mp->Close(0); - - EXPECT_TRUE(channel()->HasOneRef()); -} - -// ChannelTest.WaitAfterAttachRunAndShutdown ----------------------------------- - -TEST_F(ChannelTest, WaitAfterAttachRunAndShutdown) { - io_thread()->PostTaskAndWait(FROM_HERE, - base::Bind(&ChannelTest::CreateChannelOnIOThread, - base::Unretained(this))); - ASSERT_TRUE(channel()); - - io_thread()->PostTaskAndWait( - FROM_HERE, - base::Bind(&ChannelTest::InitChannelOnIOThread, base::Unretained(this))); - - scoped_refptr<ChannelEndpoint> channel_endpoint; - scoped_refptr<MessagePipe> mp( - MessagePipe::CreateLocalProxy(&channel_endpoint)); - - channel()->SetBootstrapEndpoint(channel_endpoint); - - io_thread()->PostTaskAndWait( - FROM_HERE, base::Bind(&ChannelTest::ShutdownChannelOnIOThread, - base::Unretained(this))); - - Waiter waiter; - waiter.Init(); - HandleSignalsState hss; - EXPECT_EQ( - MOJO_RESULT_FAILED_PRECONDITION, - mp->AddAwakable(0, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - mp->Close(0); - - EXPECT_TRUE(channel()->HasOneRef()); -} - -// TODO(vtl): More. ------------------------------------------------------------ - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/configuration.cc b/mojo/edk/system/configuration.cc deleted file mode 100644 index 4756c99..0000000 --- a/mojo/edk/system/configuration.cc +++ /dev/null @@ -1,26 +0,0 @@ -// 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/edk/system/configuration.h" - -namespace mojo { -namespace system { -namespace internal { - -// These default values should be synced with the documentation in -// mojo/edk/embedder/configuration.h. -embedder::Configuration g_configuration = { - 1000000, // max_handle_table_size - 1000000, // max_mapping_table_sze - 1000000, // max_wait_many_num_handles - 4 * 1024 * 1024, // max_message_num_bytes - 10000, // max_message_num_handles - 256 * 1024 * 1024, // max_data_pipe_capacity_bytes - 1024 * 1024, // default_data_pipe_capacity_bytes - 16, // data_pipe_buffer_alignment_bytes - 1024 * 1024 * 1024}; // max_shared_memory_num_bytes - -} // namespace internal -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/configuration.h b/mojo/edk/system/configuration.h deleted file mode 100644 index 007277a..0000000 --- a/mojo/edk/system/configuration.h +++ /dev/null @@ -1,31 +0,0 @@ -// 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_EDK_SYSTEM_CONFIGURATION_H_ -#define MOJO_EDK_SYSTEM_CONFIGURATION_H_ - -#include "mojo/edk/embedder/configuration.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace system { - -namespace internal { -MOJO_SYSTEM_IMPL_EXPORT extern embedder::Configuration g_configuration; -} // namespace internal - -MOJO_SYSTEM_IMPL_EXPORT inline const embedder::Configuration& -GetConfiguration() { - return internal::g_configuration; -} - -MOJO_SYSTEM_IMPL_EXPORT inline embedder::Configuration* -GetMutableConfiguration() { - return &internal::g_configuration; -} - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_CONFIGURATION_H_ diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc deleted file mode 100644 index 6189e50..0000000 --- a/mojo/edk/system/core.cc +++ /dev/null @@ -1,609 +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/edk/system/core.h" - -#include <vector> - -#include "base/logging.h" -#include "base/time/time.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/embedder/platform_support.h" -#include "mojo/edk/system/async_waiter.h" -#include "mojo/edk/system/configuration.h" -#include "mojo/edk/system/data_pipe.h" -#include "mojo/edk/system/data_pipe_consumer_dispatcher.h" -#include "mojo/edk/system/data_pipe_producer_dispatcher.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/handle_signals_state.h" -#include "mojo/edk/system/local_data_pipe.h" -#include "mojo/edk/system/memory.h" -#include "mojo/edk/system/message_pipe.h" -#include "mojo/edk/system/message_pipe_dispatcher.h" -#include "mojo/edk/system/shared_buffer_dispatcher.h" -#include "mojo/edk/system/waiter.h" -#include "mojo/public/c/system/macros.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 an |AwakableList| 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 -// |AwakableList|. - -// 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. - -// TODO(vtl): This should take a |scoped_ptr<PlatformSupport>| as a parameter. -Core::Core(scoped_ptr<embedder::PlatformSupport> platform_support) - : platform_support_(platform_support.Pass()) { -} - -Core::~Core() { -} - -MojoHandle Core::AddDispatcher(const scoped_refptr<Dispatcher>& dispatcher) { - base::AutoLock locker(handle_table_lock_); - return handle_table_.AddDispatcher(dispatcher); -} - -scoped_refptr<Dispatcher> Core::GetDispatcher(MojoHandle handle) { - if (handle == MOJO_HANDLE_INVALID) - return nullptr; - - base::AutoLock locker(handle_table_lock_); - return handle_table_.GetDispatcher(handle); -} - -MojoResult Core::AsyncWait(MojoHandle handle, - MojoHandleSignals signals, - base::Callback<void(MojoResult)> callback) { - scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handle); - DCHECK(dispatcher); - - scoped_ptr<AsyncWaiter> waiter = make_scoped_ptr(new AsyncWaiter(callback)); - MojoResult rv = dispatcher->AddAwakable(waiter.get(), signals, 0, nullptr); - if (rv == MOJO_RESULT_OK) - ignore_result(waiter.release()); - return rv; -} - -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> 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, - MojoHandleSignals signals, - MojoDeadline deadline, - UserPointer<MojoHandleSignalsState> signals_state) { - uint32_t unused = static_cast<uint32_t>(-1); - HandleSignalsState hss; - MojoResult rv = WaitManyInternal(&handle, &signals, 1, deadline, &unused, - signals_state.IsNull() ? nullptr : &hss); - if (rv != MOJO_RESULT_INVALID_ARGUMENT && !signals_state.IsNull()) - signals_state.Put(hss); - return rv; -} - -MojoResult Core::WaitMany(UserPointer<const MojoHandle> handles, - UserPointer<const MojoHandleSignals> signals, - uint32_t num_handles, - MojoDeadline deadline, - UserPointer<uint32_t> result_index, - UserPointer<MojoHandleSignalsState> signals_states) { - if (num_handles < 1) - return MOJO_RESULT_INVALID_ARGUMENT; - if (num_handles > GetConfiguration().max_wait_many_num_handles) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - - UserPointer<const MojoHandle>::Reader handles_reader(handles, num_handles); - UserPointer<const MojoHandleSignals>::Reader signals_reader(signals, - num_handles); - uint32_t index = static_cast<uint32_t>(-1); - MojoResult rv; - if (signals_states.IsNull()) { - rv = WaitManyInternal(handles_reader.GetPointer(), - signals_reader.GetPointer(), num_handles, deadline, - &index, nullptr); - } else { - UserPointer<MojoHandleSignalsState>::Writer signals_states_writer( - signals_states, num_handles); - // Note: The |reinterpret_cast| is safe, since |HandleSignalsState| is a - // subclass of |MojoHandleSignalsState| that doesn't add any data members. - rv = WaitManyInternal(handles_reader.GetPointer(), - signals_reader.GetPointer(), num_handles, deadline, - &index, reinterpret_cast<HandleSignalsState*>( - signals_states_writer.GetPointer())); - if (rv != MOJO_RESULT_INVALID_ARGUMENT) - signals_states_writer.Commit(); - } - if (index != static_cast<uint32_t>(-1) && !result_index.IsNull()) - result_index.Put(index); - return rv; -} - -MojoResult Core::CreateMessagePipe( - UserPointer<const MojoCreateMessagePipeOptions> options, - UserPointer<MojoHandle> message_pipe_handle0, - UserPointer<MojoHandle> message_pipe_handle1) { - MojoCreateMessagePipeOptions validated_options = {}; - MojoResult result = - MessagePipeDispatcher::ValidateCreateOptions(options, &validated_options); - if (result != MOJO_RESULT_OK) - return result; - - scoped_refptr<MessagePipeDispatcher> dispatcher0( - new MessagePipeDispatcher(validated_options)); - scoped_refptr<MessagePipeDispatcher> dispatcher1( - new MessagePipeDispatcher(validated_options)); - - std::pair<MojoHandle, MojoHandle> 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<MessagePipe> message_pipe(MessagePipe::CreateLocalLocal()); - dispatcher0->Init(message_pipe, 0); - dispatcher1->Init(message_pipe, 1); - - message_pipe_handle0.Put(handle_pair.first); - message_pipe_handle1.Put(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, - UserPointer<const void> bytes, - uint32_t num_bytes, - UserPointer<const MojoHandle> handles, - uint32_t num_handles, - MojoWriteMessageFlags flags) { - scoped_refptr<Dispatcher> dispatcher(GetDispatcher(message_pipe_handle)); - if (!dispatcher) - return MOJO_RESULT_INVALID_ARGUMENT; - - // Easy case: not sending any handles. - if (num_handles == 0) - return dispatcher->WriteMessage(bytes, num_bytes, nullptr, 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 (num_handles > GetConfiguration().max_message_num_handles) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - - UserPointer<const MojoHandle>::Reader handles_reader(handles, num_handles); - - // 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<DispatcherTransport> 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_reader.GetPointer(), 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_reader.GetPointer(), num_handles); - } else { - handle_table_.RestoreBusyHandles(handles_reader.GetPointer(), - num_handles); - } - } - - return rv; -} - -MojoResult Core::ReadMessage(MojoHandle message_pipe_handle, - UserPointer<void> bytes, - UserPointer<uint32_t> num_bytes, - UserPointer<MojoHandle> handles, - UserPointer<uint32_t> num_handles, - MojoReadMessageFlags flags) { - scoped_refptr<Dispatcher> dispatcher(GetDispatcher(message_pipe_handle)); - if (!dispatcher) - return MOJO_RESULT_INVALID_ARGUMENT; - - uint32_t num_handles_value = num_handles.IsNull() ? 0 : num_handles.Get(); - - MojoResult rv; - if (num_handles_value == 0) { - // Easy case: won't receive any handles. - rv = dispatcher->ReadMessage(bytes, num_bytes, nullptr, &num_handles_value, - flags); - } else { - DispatcherVector dispatchers; - rv = dispatcher->ReadMessage(bytes, num_bytes, &dispatchers, - &num_handles_value, flags); - if (!dispatchers.empty()) { - DCHECK_EQ(rv, MOJO_RESULT_OK); - DCHECK(!num_handles.IsNull()); - DCHECK_LE(dispatchers.size(), static_cast<size_t>(num_handles_value)); - - bool success; - UserPointer<MojoHandle>::Writer handles_writer(handles, - dispatchers.size()); - { - base::AutoLock locker(handle_table_lock_); - success = handle_table_.AddDispatcherVector( - dispatchers, handles_writer.GetPointer()); - } - if (success) { - handles_writer.Commit(); - } else { - 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(); - } - if (rv == MOJO_RESULT_OK) - rv = MOJO_RESULT_RESOURCE_EXHAUSTED; - } - } - } - - if (!num_handles.IsNull()) - num_handles.Put(num_handles_value); - return rv; -} - -MojoResult Core::CreateDataPipe( - UserPointer<const MojoCreateDataPipeOptions> options, - UserPointer<MojoHandle> data_pipe_producer_handle, - UserPointer<MojoHandle> data_pipe_consumer_handle) { - MojoCreateDataPipeOptions validated_options = {}; - MojoResult result = - DataPipe::ValidateCreateOptions(options, &validated_options); - if (result != MOJO_RESULT_OK) - return result; - - scoped_refptr<DataPipeProducerDispatcher> producer_dispatcher( - new DataPipeProducerDispatcher()); - scoped_refptr<DataPipeConsumerDispatcher> consumer_dispatcher( - new DataPipeConsumerDispatcher()); - - std::pair<MojoHandle, MojoHandle> 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<DataPipe> data_pipe(new LocalDataPipe(validated_options)); - producer_dispatcher->Init(data_pipe); - consumer_dispatcher->Init(data_pipe); - - data_pipe_producer_handle.Put(handle_pair.first); - data_pipe_consumer_handle.Put(handle_pair.second); - return MOJO_RESULT_OK; -} - -MojoResult Core::WriteData(MojoHandle data_pipe_producer_handle, - UserPointer<const void> elements, - UserPointer<uint32_t> num_bytes, - MojoWriteDataFlags flags) { - scoped_refptr<Dispatcher> dispatcher( - GetDispatcher(data_pipe_producer_handle)); - if (!dispatcher) - return MOJO_RESULT_INVALID_ARGUMENT; - - return dispatcher->WriteData(elements, num_bytes, flags); -} - -MojoResult Core::BeginWriteData(MojoHandle data_pipe_producer_handle, - UserPointer<void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - MojoWriteDataFlags flags) { - scoped_refptr<Dispatcher> dispatcher( - GetDispatcher(data_pipe_producer_handle)); - if (!dispatcher) - 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> dispatcher( - GetDispatcher(data_pipe_producer_handle)); - if (!dispatcher) - return MOJO_RESULT_INVALID_ARGUMENT; - - return dispatcher->EndWriteData(num_bytes_written); -} - -MojoResult Core::ReadData(MojoHandle data_pipe_consumer_handle, - UserPointer<void> elements, - UserPointer<uint32_t> num_bytes, - MojoReadDataFlags flags) { - scoped_refptr<Dispatcher> dispatcher( - GetDispatcher(data_pipe_consumer_handle)); - if (!dispatcher) - return MOJO_RESULT_INVALID_ARGUMENT; - - return dispatcher->ReadData(elements, num_bytes, flags); -} - -MojoResult Core::BeginReadData(MojoHandle data_pipe_consumer_handle, - UserPointer<const void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - MojoReadDataFlags flags) { - scoped_refptr<Dispatcher> dispatcher( - GetDispatcher(data_pipe_consumer_handle)); - if (!dispatcher) - 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> dispatcher( - GetDispatcher(data_pipe_consumer_handle)); - if (!dispatcher) - return MOJO_RESULT_INVALID_ARGUMENT; - - return dispatcher->EndReadData(num_bytes_read); -} - -MojoResult Core::CreateSharedBuffer( - UserPointer<const MojoCreateSharedBufferOptions> options, - uint64_t num_bytes, - UserPointer<MojoHandle> shared_buffer_handle) { - MojoCreateSharedBufferOptions validated_options = {}; - MojoResult result = SharedBufferDispatcher::ValidateCreateOptions( - options, &validated_options); - if (result != MOJO_RESULT_OK) - return result; - - scoped_refptr<SharedBufferDispatcher> dispatcher; - result = SharedBufferDispatcher::Create(platform_support(), 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.Put(h); - return MOJO_RESULT_OK; -} - -MojoResult Core::DuplicateBufferHandle( - MojoHandle buffer_handle, - UserPointer<const MojoDuplicateBufferHandleOptions> options, - UserPointer<MojoHandle> new_buffer_handle) { - scoped_refptr<Dispatcher> dispatcher(GetDispatcher(buffer_handle)); - if (!dispatcher) - return MOJO_RESULT_INVALID_ARGUMENT; - - // Don't verify |options| here; that's the dispatcher's job. - scoped_refptr<Dispatcher> 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.Put(new_handle); - return MOJO_RESULT_OK; -} - -MojoResult Core::MapBuffer(MojoHandle buffer_handle, - uint64_t offset, - uint64_t num_bytes, - UserPointer<void*> buffer, - MojoMapBufferFlags flags) { - scoped_refptr<Dispatcher> dispatcher(GetDispatcher(buffer_handle)); - if (!dispatcher) - return MOJO_RESULT_INVALID_ARGUMENT; - - scoped_ptr<embedder::PlatformSharedBufferMapping> mapping; - MojoResult result = dispatcher->MapBuffer(offset, num_bytes, flags, &mapping); - if (result != MOJO_RESULT_OK) - return result; - - DCHECK(mapping); - void* address = mapping->GetBase(); - { - base::AutoLock locker(mapping_table_lock_); - result = mapping_table_.AddMapping(mapping.Pass()); - } - if (result != MOJO_RESULT_OK) - return result; - - buffer.Put(address); - return MOJO_RESULT_OK; -} - -MojoResult Core::UnmapBuffer(UserPointer<void> buffer) { - base::AutoLock locker(mapping_table_lock_); - return mapping_table_.RemoveMapping(buffer.GetPointerValue()); -} - -// 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 |Remove()|. Analyze this -// more carefully and address it if necessary. -MojoResult Core::WaitManyInternal(const MojoHandle* handles, - const MojoHandleSignals* signals, - uint32_t num_handles, - MojoDeadline deadline, - uint32_t* result_index, - HandleSignalsState* signals_states) { - DCHECK_GT(num_handles, 0u); - DCHECK_EQ(*result_index, static_cast<uint32_t>(-1)); - - DispatcherVector dispatchers; - dispatchers.reserve(num_handles); - for (uint32_t i = 0; i < num_handles; i++) { - scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handles[i]); - if (!dispatcher) { - *result_index = i; - 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]->AddAwakable( - &waiter, signals[i], i, signals_states ? &signals_states[i] : nullptr); - if (rv != MOJO_RESULT_OK) { - *result_index = i; - break; - } - } - uint32_t num_added = i; - - if (rv == MOJO_RESULT_ALREADY_EXISTS) - rv = MOJO_RESULT_OK; // The i-th one is already "triggered". - else if (rv == MOJO_RESULT_OK) - rv = waiter.Wait(deadline, result_index); - - // 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]->RemoveAwakable( - &waiter, signals_states ? &signals_states[i] : nullptr); - } - if (signals_states) { - for (; i < num_handles; i++) - signals_states[i] = dispatchers[i]->GetHandleSignalsState(); - } - - return rv; -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h deleted file mode 100644 index 0ff9c01..0000000 --- a/mojo/edk/system/core.h +++ /dev/null @@ -1,165 +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_EDK_SYSTEM_CORE_H_ -#define MOJO_EDK_SYSTEM_CORE_H_ - -#include <stdint.h> - -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/system/handle_table.h" -#include "mojo/edk/system/mapping_table.h" -#include "mojo/edk/system/memory.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/buffer.h" -#include "mojo/public/c/system/data_pipe.h" -#include "mojo/public/c/system/message_pipe.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { - -namespace embedder { -class PlatformSupport; -} - -namespace system { - -class Dispatcher; -struct HandleSignalsState; - -// |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 (and internally): - explicit Core(scoped_ptr<embedder::PlatformSupport> platform_support); - virtual ~Core(); - - // Adds |dispatcher| to the handle table, returning the handle for it. Returns - // |MOJO_HANDLE_INVALID| on failure, namely if the handle table is full. - MojoHandle AddDispatcher(const scoped_refptr<Dispatcher>& dispatcher); - - // Looks up the dispatcher for the given handle. Returns null if the handle is - // invalid. - scoped_refptr<Dispatcher> GetDispatcher(MojoHandle handle); - - // Watches on the given handle for the given signals, calling |callback| when - // a signal is satisfied or when all signals become unsatisfiable. |callback| - // must satisfy stringent requirements -- see |Awakable::Awake()| in - // awakable.h. In particular, it must not call any Mojo system functions. - MojoResult AsyncWait(MojoHandle handle, - MojoHandleSignals signals, - base::Callback<void(MojoResult)> callback); - - embedder::PlatformSupport* platform_support() const { - return platform_support_.get(); - } - - // --------------------------------------------------------------------------- - - // System calls implementation: - MojoTimeTicks GetTimeTicksNow(); - MojoResult Close(MojoHandle handle); - MojoResult Wait(MojoHandle handle, - MojoHandleSignals signals, - MojoDeadline deadline, - UserPointer<MojoHandleSignalsState> signals_state); - MojoResult WaitMany(UserPointer<const MojoHandle> handles, - UserPointer<const MojoHandleSignals> signals, - uint32_t num_handles, - MojoDeadline deadline, - UserPointer<uint32_t> result_index, - UserPointer<MojoHandleSignalsState> signals_states); - MojoResult CreateMessagePipe( - UserPointer<const MojoCreateMessagePipeOptions> options, - UserPointer<MojoHandle> message_pipe_handle0, - UserPointer<MojoHandle> message_pipe_handle1); - MojoResult WriteMessage(MojoHandle message_pipe_handle, - UserPointer<const void> bytes, - uint32_t num_bytes, - UserPointer<const MojoHandle> handles, - uint32_t num_handles, - MojoWriteMessageFlags flags); - MojoResult ReadMessage(MojoHandle message_pipe_handle, - UserPointer<void> bytes, - UserPointer<uint32_t> num_bytes, - UserPointer<MojoHandle> handles, - UserPointer<uint32_t> num_handles, - MojoReadMessageFlags flags); - MojoResult CreateDataPipe( - UserPointer<const MojoCreateDataPipeOptions> options, - UserPointer<MojoHandle> data_pipe_producer_handle, - UserPointer<MojoHandle> data_pipe_consumer_handle); - MojoResult WriteData(MojoHandle data_pipe_producer_handle, - UserPointer<const void> elements, - UserPointer<uint32_t> num_bytes, - MojoWriteDataFlags flags); - MojoResult BeginWriteData(MojoHandle data_pipe_producer_handle, - UserPointer<void*> buffer, - UserPointer<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, - UserPointer<void> elements, - UserPointer<uint32_t> num_bytes, - MojoReadDataFlags flags); - MojoResult BeginReadData(MojoHandle data_pipe_consumer_handle, - UserPointer<const void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - MojoReadDataFlags flags); - MojoResult EndReadData(MojoHandle data_pipe_consumer_handle, - uint32_t num_bytes_read); - MojoResult CreateSharedBuffer( - UserPointer<const MojoCreateSharedBufferOptions> options, - uint64_t num_bytes, - UserPointer<MojoHandle> shared_buffer_handle); - MojoResult DuplicateBufferHandle( - MojoHandle buffer_handle, - UserPointer<const MojoDuplicateBufferHandleOptions> options, - UserPointer<MojoHandle> new_buffer_handle); - MojoResult MapBuffer(MojoHandle buffer_handle, - uint64_t offset, - uint64_t num_bytes, - UserPointer<void*> buffer, - MojoMapBufferFlags flags); - MojoResult UnmapBuffer(UserPointer<void> buffer); - - private: - friend bool internal::ShutdownCheckNoLeaks(Core*); - - // Internal implementation of |Wait()| and |WaitMany()|; doesn't do basic - // validation of arguments. |*result_index| is only set if the result (whether - // success or failure) applies to a specific handle, so its value should be - // preinitialized to |static_cast<uint32_t>(-1)|. - MojoResult WaitManyInternal(const MojoHandle* handles, - const MojoHandleSignals* signals, - uint32_t num_handles, - MojoDeadline deadline, - uint32_t* result_index, - HandleSignalsState* signals_states); - - const scoped_ptr<embedder::PlatformSupport> platform_support_; - - // 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_EDK_SYSTEM_CORE_H_ diff --git a/mojo/edk/system/core_test_base.cc b/mojo/edk/system/core_test_base.cc deleted file mode 100644 index b81a051..0000000 --- a/mojo/edk/system/core_test_base.cc +++ /dev/null @@ -1,380 +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/edk/system/core_test_base.h" - -#include <vector> - -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "mojo/edk/embedder/simple_platform_support.h" -#include "mojo/edk/system/configuration.h" -#include "mojo/edk/system/core.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/memory.h" - -namespace mojo { -namespace system { -namespace test { - -namespace { - -// MockDispatcher -------------------------------------------------------------- - -class MockDispatcher : public Dispatcher { - public: - explicit MockDispatcher(CoreTestBase::MockHandleInfo* info) : info_(info) { - CHECK(info_); - info_->IncrementCtorCallCount(); - } - - // |Dispatcher| private methods: - Type GetType() const override { return kTypeUnknown; } - - private: - ~MockDispatcher() override { info_->IncrementDtorCallCount(); } - - // |Dispatcher| protected methods: - void CloseImplNoLock() override { - info_->IncrementCloseCallCount(); - lock().AssertAcquired(); - } - - MojoResult WriteMessageImplNoLock( - UserPointer<const void> bytes, - uint32_t num_bytes, - std::vector<DispatcherTransport>* transports, - MojoWriteMessageFlags /*flags*/) override { - info_->IncrementWriteMessageCallCount(); - lock().AssertAcquired(); - - if (num_bytes > GetConfiguration().max_message_num_bytes) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - - if (transports) - return MOJO_RESULT_UNIMPLEMENTED; - - return MOJO_RESULT_OK; - } - - MojoResult ReadMessageImplNoLock(UserPointer<void> bytes, - UserPointer<uint32_t> num_bytes, - DispatcherVector* dispatchers, - uint32_t* num_dispatchers, - MojoReadMessageFlags /*flags*/) override { - info_->IncrementReadMessageCallCount(); - lock().AssertAcquired(); - - if (num_dispatchers) { - *num_dispatchers = 1; - if (dispatchers) { - // Okay to leave an invalid dispatcher. - dispatchers->resize(1); - } - } - - return MOJO_RESULT_OK; - } - - MojoResult WriteDataImplNoLock(UserPointer<const void> /*elements*/, - UserPointer<uint32_t> /*num_bytes*/, - MojoWriteDataFlags /*flags*/) override { - info_->IncrementWriteDataCallCount(); - lock().AssertAcquired(); - return MOJO_RESULT_UNIMPLEMENTED; - } - - MojoResult BeginWriteDataImplNoLock( - UserPointer<void*> /*buffer*/, - UserPointer<uint32_t> /*buffer_num_bytes*/, - MojoWriteDataFlags /*flags*/) override { - info_->IncrementBeginWriteDataCallCount(); - lock().AssertAcquired(); - return MOJO_RESULT_UNIMPLEMENTED; - } - - MojoResult EndWriteDataImplNoLock(uint32_t /*num_bytes_written*/) override { - info_->IncrementEndWriteDataCallCount(); - lock().AssertAcquired(); - return MOJO_RESULT_UNIMPLEMENTED; - } - - MojoResult ReadDataImplNoLock(UserPointer<void> /*elements*/, - UserPointer<uint32_t> /*num_bytes*/, - MojoReadDataFlags /*flags*/) override { - info_->IncrementReadDataCallCount(); - lock().AssertAcquired(); - return MOJO_RESULT_UNIMPLEMENTED; - } - - MojoResult BeginReadDataImplNoLock(UserPointer<const void*> /*buffer*/, - UserPointer<uint32_t> /*buffer_num_bytes*/, - MojoReadDataFlags /*flags*/) override { - info_->IncrementBeginReadDataCallCount(); - lock().AssertAcquired(); - return MOJO_RESULT_UNIMPLEMENTED; - } - - MojoResult EndReadDataImplNoLock(uint32_t /*num_bytes_read*/) override { - info_->IncrementEndReadDataCallCount(); - lock().AssertAcquired(); - return MOJO_RESULT_UNIMPLEMENTED; - } - - MojoResult AddAwakableImplNoLock(Awakable* awakable, - MojoHandleSignals /*signals*/, - uint32_t /*context*/, - HandleSignalsState* signals_state) override { - info_->IncrementAddAwakableCallCount(); - lock().AssertAcquired(); - if (signals_state) - *signals_state = HandleSignalsState(); - if (info_->IsAddAwakableAllowed()) { - info_->AwakableWasAdded(awakable); - return MOJO_RESULT_OK; - } - - return MOJO_RESULT_FAILED_PRECONDITION; - } - - void RemoveAwakableImplNoLock(Awakable* /*awakable*/, - HandleSignalsState* signals_state) override { - info_->IncrementRemoveAwakableCallCount(); - lock().AssertAcquired(); - if (signals_state) - *signals_state = HandleSignalsState(); - } - - void CancelAllAwakablesNoLock() override { - info_->IncrementCancelAllAwakablesCallCount(); - lock().AssertAcquired(); - } - - scoped_refptr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock() - override { - return scoped_refptr<Dispatcher>(new MockDispatcher(info_)); - } - - CoreTestBase::MockHandleInfo* const info_; - - DISALLOW_COPY_AND_ASSIGN(MockDispatcher); -}; - -} // namespace - -// CoreTestBase ---------------------------------------------------------------- - -CoreTestBase::CoreTestBase() { -} - -CoreTestBase::~CoreTestBase() { -} - -void CoreTestBase::SetUp() { - core_ = new Core(make_scoped_ptr(new embedder::SimplePlatformSupport())); -} - -void CoreTestBase::TearDown() { - delete core_; - core_ = nullptr; -} - -MojoHandle CoreTestBase::CreateMockHandle(CoreTestBase::MockHandleInfo* info) { - CHECK(core_); - scoped_refptr<MockDispatcher> dispatcher(new MockDispatcher(info)); - return core_->AddDispatcher(dispatcher); -} - -// CoreTestBase_MockHandleInfo ------------------------------------------------- - -CoreTestBase_MockHandleInfo::CoreTestBase_MockHandleInfo() - : ctor_call_count_(0), - dtor_call_count_(0), - close_call_count_(0), - write_message_call_count_(0), - read_message_call_count_(0), - write_data_call_count_(0), - begin_write_data_call_count_(0), - end_write_data_call_count_(0), - read_data_call_count_(0), - begin_read_data_call_count_(0), - end_read_data_call_count_(0), - add_awakable_call_count_(0), - remove_awakable_call_count_(0), - cancel_all_awakables_call_count_(0), - add_awakable_allowed_(false) { -} - -CoreTestBase_MockHandleInfo::~CoreTestBase_MockHandleInfo() { -} - -unsigned CoreTestBase_MockHandleInfo::GetCtorCallCount() const { - base::AutoLock locker(lock_); - return ctor_call_count_; -} - -unsigned CoreTestBase_MockHandleInfo::GetDtorCallCount() const { - base::AutoLock locker(lock_); - return dtor_call_count_; -} - -unsigned CoreTestBase_MockHandleInfo::GetCloseCallCount() const { - base::AutoLock locker(lock_); - return close_call_count_; -} - -unsigned CoreTestBase_MockHandleInfo::GetWriteMessageCallCount() const { - base::AutoLock locker(lock_); - return write_message_call_count_; -} - -unsigned CoreTestBase_MockHandleInfo::GetReadMessageCallCount() const { - base::AutoLock locker(lock_); - return read_message_call_count_; -} - -unsigned CoreTestBase_MockHandleInfo::GetWriteDataCallCount() const { - base::AutoLock locker(lock_); - return write_data_call_count_; -} - -unsigned CoreTestBase_MockHandleInfo::GetBeginWriteDataCallCount() const { - base::AutoLock locker(lock_); - return begin_write_data_call_count_; -} - -unsigned CoreTestBase_MockHandleInfo::GetEndWriteDataCallCount() const { - base::AutoLock locker(lock_); - return end_write_data_call_count_; -} - -unsigned CoreTestBase_MockHandleInfo::GetReadDataCallCount() const { - base::AutoLock locker(lock_); - return read_data_call_count_; -} - -unsigned CoreTestBase_MockHandleInfo::GetBeginReadDataCallCount() const { - base::AutoLock locker(lock_); - return begin_read_data_call_count_; -} - -unsigned CoreTestBase_MockHandleInfo::GetEndReadDataCallCount() const { - base::AutoLock locker(lock_); - return end_read_data_call_count_; -} - -unsigned CoreTestBase_MockHandleInfo::GetAddAwakableCallCount() const { - base::AutoLock locker(lock_); - return add_awakable_call_count_; -} - -unsigned CoreTestBase_MockHandleInfo::GetRemoveAwakableCallCount() const { - base::AutoLock locker(lock_); - return remove_awakable_call_count_; -} - -unsigned CoreTestBase_MockHandleInfo::GetCancelAllAwakablesCallCount() const { - base::AutoLock locker(lock_); - return cancel_all_awakables_call_count_; -} - -size_t CoreTestBase_MockHandleInfo::GetAddedAwakableSize() const { - base::AutoLock locker(lock_); - return added_awakables_.size(); -} - -Awakable* CoreTestBase_MockHandleInfo::GetAddedAwakableAt(unsigned i) const { - base::AutoLock locker(lock_); - return added_awakables_[i]; -} - -void CoreTestBase_MockHandleInfo::IncrementCtorCallCount() { - base::AutoLock locker(lock_); - ctor_call_count_++; -} - -void CoreTestBase_MockHandleInfo::IncrementDtorCallCount() { - base::AutoLock locker(lock_); - dtor_call_count_++; -} - -void CoreTestBase_MockHandleInfo::IncrementCloseCallCount() { - base::AutoLock locker(lock_); - close_call_count_++; -} - -void CoreTestBase_MockHandleInfo::IncrementWriteMessageCallCount() { - base::AutoLock locker(lock_); - write_message_call_count_++; -} - -void CoreTestBase_MockHandleInfo::IncrementReadMessageCallCount() { - base::AutoLock locker(lock_); - read_message_call_count_++; -} - -void CoreTestBase_MockHandleInfo::IncrementWriteDataCallCount() { - base::AutoLock locker(lock_); - write_data_call_count_++; -} - -void CoreTestBase_MockHandleInfo::IncrementBeginWriteDataCallCount() { - base::AutoLock locker(lock_); - begin_write_data_call_count_++; -} - -void CoreTestBase_MockHandleInfo::IncrementEndWriteDataCallCount() { - base::AutoLock locker(lock_); - end_write_data_call_count_++; -} - -void CoreTestBase_MockHandleInfo::IncrementReadDataCallCount() { - base::AutoLock locker(lock_); - read_data_call_count_++; -} - -void CoreTestBase_MockHandleInfo::IncrementBeginReadDataCallCount() { - base::AutoLock locker(lock_); - begin_read_data_call_count_++; -} - -void CoreTestBase_MockHandleInfo::IncrementEndReadDataCallCount() { - base::AutoLock locker(lock_); - end_read_data_call_count_++; -} - -void CoreTestBase_MockHandleInfo::IncrementAddAwakableCallCount() { - base::AutoLock locker(lock_); - add_awakable_call_count_++; -} - -void CoreTestBase_MockHandleInfo::IncrementRemoveAwakableCallCount() { - base::AutoLock locker(lock_); - remove_awakable_call_count_++; -} - -void CoreTestBase_MockHandleInfo::IncrementCancelAllAwakablesCallCount() { - base::AutoLock locker(lock_); - cancel_all_awakables_call_count_++; -} - -void CoreTestBase_MockHandleInfo::AllowAddAwakable(bool alllow) { - base::AutoLock locker(lock_); - add_awakable_allowed_ = alllow; -} - -bool CoreTestBase_MockHandleInfo::IsAddAwakableAllowed() const { - base::AutoLock locker(lock_); - return add_awakable_allowed_; -} - -void CoreTestBase_MockHandleInfo::AwakableWasAdded(Awakable* awakable) { - base::AutoLock locker(lock_); - added_awakables_.push_back(awakable); -} - -} // namespace test -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/core_test_base.h b/mojo/edk/system/core_test_base.h deleted file mode 100644 index c2b5ee2..0000000 --- a/mojo/edk/system/core_test_base.h +++ /dev/null @@ -1,116 +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_EDK_SYSTEM_CORE_TEST_BASE_H_ -#define MOJO_EDK_SYSTEM_CORE_TEST_BASE_H_ - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/synchronization/lock.h" -#include "mojo/public/c/system/types.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { - -class Core; -class Awakable; - -namespace test { - -class CoreTestBase_MockHandleInfo; - -class CoreTestBase : public testing::Test { - public: - typedef CoreTestBase_MockHandleInfo MockHandleInfo; - - CoreTestBase(); - ~CoreTestBase() override; - - void SetUp() override; - void TearDown() override; - - protected: - // |info| must remain alive until the returned handle is closed. - MojoHandle CreateMockHandle(MockHandleInfo* info); - - Core* core() { return core_; } - - private: - Core* core_; - - DISALLOW_COPY_AND_ASSIGN(CoreTestBase); -}; - -class CoreTestBase_MockHandleInfo { - public: - CoreTestBase_MockHandleInfo(); - ~CoreTestBase_MockHandleInfo(); - - unsigned GetCtorCallCount() const; - unsigned GetDtorCallCount() const; - unsigned GetCloseCallCount() const; - unsigned GetWriteMessageCallCount() const; - unsigned GetReadMessageCallCount() const; - unsigned GetWriteDataCallCount() const; - unsigned GetBeginWriteDataCallCount() const; - unsigned GetEndWriteDataCallCount() const; - unsigned GetReadDataCallCount() const; - unsigned GetBeginReadDataCallCount() const; - unsigned GetEndReadDataCallCount() const; - unsigned GetAddAwakableCallCount() const; - unsigned GetRemoveAwakableCallCount() const; - unsigned GetCancelAllAwakablesCallCount() const; - - size_t GetAddedAwakableSize() const; - Awakable* GetAddedAwakableAt(unsigned i) const; - - // For use by |MockDispatcher|: - void IncrementCtorCallCount(); - void IncrementDtorCallCount(); - void IncrementCloseCallCount(); - void IncrementWriteMessageCallCount(); - void IncrementReadMessageCallCount(); - void IncrementWriteDataCallCount(); - void IncrementBeginWriteDataCallCount(); - void IncrementEndWriteDataCallCount(); - void IncrementReadDataCallCount(); - void IncrementBeginReadDataCallCount(); - void IncrementEndReadDataCallCount(); - void IncrementAddAwakableCallCount(); - void IncrementRemoveAwakableCallCount(); - void IncrementCancelAllAwakablesCallCount(); - - void AllowAddAwakable(bool alllow); - bool IsAddAwakableAllowed() const; - void AwakableWasAdded(Awakable*); - - private: - mutable base::Lock lock_; // Protects the following members. - unsigned ctor_call_count_; - unsigned dtor_call_count_; - unsigned close_call_count_; - unsigned write_message_call_count_; - unsigned read_message_call_count_; - unsigned write_data_call_count_; - unsigned begin_write_data_call_count_; - unsigned end_write_data_call_count_; - unsigned read_data_call_count_; - unsigned begin_read_data_call_count_; - unsigned end_read_data_call_count_; - unsigned add_awakable_call_count_; - unsigned remove_awakable_call_count_; - unsigned cancel_all_awakables_call_count_; - - bool add_awakable_allowed_; - std::vector<Awakable*> added_awakables_; - - DISALLOW_COPY_AND_ASSIGN(CoreTestBase_MockHandleInfo); -}; - -} // namespace test -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_CORE_TEST_BASE_H_ diff --git a/mojo/edk/system/core_unittest.cc b/mojo/edk/system/core_unittest.cc deleted file mode 100644 index b789301..0000000 --- a/mojo/edk/system/core_unittest.cc +++ /dev/null @@ -1,1317 +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/edk/system/core.h" - -#include <stdint.h> - -#include <limits> - -#include "base/bind.h" -#include "base/threading/platform_thread.h" -#include "base/time/time.h" -#include "mojo/edk/system/awakable.h" -#include "mojo/edk/system/core_test_base.h" - -namespace mojo { -namespace system { -namespace { - -const MojoHandleSignalsState kEmptyMojoHandleSignalsState = {0u, 0u}; -const MojoHandleSignalsState kFullMojoHandleSignalsState = {~0u, ~0u}; -const MojoHandleSignals kAllSignals = MOJO_HANDLE_SIGNAL_READABLE | - MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED; - -typedef test::CoreTestBase CoreTest; - -TEST_F(CoreTest, GetTimeTicksNow) { - const MojoTimeTicks start = core()->GetTimeTicksNow(); - EXPECT_NE(static_cast<MojoTimeTicks>(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<MojoTimeTicks>(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, NullUserPointer(), 0, NullUserPointer(), 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(1u, info.GetWriteMessageCallCount()); - - EXPECT_EQ(0u, info.GetReadMessageCallCount()); - uint32_t num_bytes = 0; - EXPECT_EQ( - MOJO_RESULT_OK, - core()->ReadMessage(h, NullUserPointer(), MakeUserPointer(&num_bytes), - NullUserPointer(), NullUserPointer(), - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(1u, info.GetReadMessageCallCount()); - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage(h, NullUserPointer(), NullUserPointer(), - NullUserPointer(), NullUserPointer(), - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(2u, info.GetReadMessageCallCount()); - - EXPECT_EQ(0u, info.GetWriteDataCallCount()); - EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, - core()->WriteData(h, NullUserPointer(), NullUserPointer(), - MOJO_WRITE_DATA_FLAG_NONE)); - EXPECT_EQ(1u, info.GetWriteDataCallCount()); - - EXPECT_EQ(0u, info.GetBeginWriteDataCallCount()); - EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, - core()->BeginWriteData(h, NullUserPointer(), NullUserPointer(), - 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, NullUserPointer(), NullUserPointer(), - MOJO_READ_DATA_FLAG_NONE)); - EXPECT_EQ(1u, info.GetReadDataCallCount()); - - EXPECT_EQ(0u, info.GetBeginReadDataCallCount()); - EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, - core()->BeginReadData(h, NullUserPointer(), NullUserPointer(), - 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.GetAddAwakableCallCount()); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE, MOJO_DEADLINE_INDEFINITE, - NullUserPointer())); - EXPECT_EQ(1u, info.GetAddAwakableCallCount()); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE, 0, NullUserPointer())); - EXPECT_EQ(2u, info.GetAddAwakableCallCount()); - MojoHandleSignalsState hss = kFullMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE, MOJO_DEADLINE_INDEFINITE, - MakeUserPointer(&hss))); - EXPECT_EQ(3u, info.GetAddAwakableCallCount()); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(0u, hss.satisfiable_signals); - EXPECT_EQ( - MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE, 10 * 1000, NullUserPointer())); - EXPECT_EQ(4u, info.GetAddAwakableCallCount()); - hss = kFullMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE, 10 * 1000, - MakeUserPointer(&hss))); - EXPECT_EQ(5u, info.GetAddAwakableCallCount()); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(0u, hss.satisfiable_signals); - - MojoHandleSignals handle_signals = ~MOJO_HANDLE_SIGNAL_NONE; - EXPECT_EQ( - MOJO_RESULT_FAILED_PRECONDITION, - core()->WaitMany(MakeUserPointer(&h), MakeUserPointer(&handle_signals), 1, - MOJO_DEADLINE_INDEFINITE, NullUserPointer(), - NullUserPointer())); - EXPECT_EQ(6u, info.GetAddAwakableCallCount()); - uint32_t result_index = static_cast<uint32_t>(-1); - EXPECT_EQ( - MOJO_RESULT_FAILED_PRECONDITION, - core()->WaitMany(MakeUserPointer(&h), MakeUserPointer(&handle_signals), 1, - MOJO_DEADLINE_INDEFINITE, MakeUserPointer(&result_index), - NullUserPointer())); - EXPECT_EQ(7u, info.GetAddAwakableCallCount()); - EXPECT_EQ(0u, result_index); - hss = kFullMojoHandleSignalsState; - EXPECT_EQ( - MOJO_RESULT_FAILED_PRECONDITION, - core()->WaitMany(MakeUserPointer(&h), MakeUserPointer(&handle_signals), 1, - MOJO_DEADLINE_INDEFINITE, NullUserPointer(), - MakeUserPointer(&hss))); - EXPECT_EQ(8u, info.GetAddAwakableCallCount()); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(0u, hss.satisfiable_signals); - result_index = static_cast<uint32_t>(-1); - hss = kFullMojoHandleSignalsState; - EXPECT_EQ( - MOJO_RESULT_FAILED_PRECONDITION, - core()->WaitMany(MakeUserPointer(&h), MakeUserPointer(&handle_signals), 1, - MOJO_DEADLINE_INDEFINITE, MakeUserPointer(&result_index), - MakeUserPointer(&hss))); - EXPECT_EQ(9u, info.GetAddAwakableCallCount()); - EXPECT_EQ(0u, result_index); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(0u, hss.satisfiable_signals); - - EXPECT_EQ(0u, info.GetDtorCallCount()); - EXPECT_EQ(0u, info.GetCloseCallCount()); - EXPECT_EQ(0u, info.GetCancelAllAwakablesCallCount()); - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h)); - EXPECT_EQ(1u, info.GetCancelAllAwakablesCallCount()); - EXPECT_EQ(1u, info.GetCloseCallCount()); - EXPECT_EQ(1u, info.GetDtorCallCount()); - - // No awakables should ever have ever been added. - EXPECT_EQ(0u, info.GetRemoveAwakableCallCount()); -} - -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_HANDLE_SIGNAL_NONE, - MOJO_DEADLINE_INDEFINITE, NullUserPointer())); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->Wait(10, ~MOJO_HANDLE_SIGNAL_NONE, - MOJO_DEADLINE_INDEFINITE, NullUserPointer())); - - MojoHandleSignalsState hss = kFullMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->Wait(MOJO_HANDLE_INVALID, ~MOJO_HANDLE_SIGNAL_NONE, - MOJO_DEADLINE_INDEFINITE, MakeUserPointer(&hss))); - // On invalid argument, it shouldn't modify the handle signals state. - EXPECT_EQ(kFullMojoHandleSignalsState.satisfied_signals, - hss.satisfied_signals); - EXPECT_EQ(kFullMojoHandleSignalsState.satisfiable_signals, - hss.satisfiable_signals); - hss = kFullMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->Wait(10, ~MOJO_HANDLE_SIGNAL_NONE, - MOJO_DEADLINE_INDEFINITE, MakeUserPointer(&hss))); - // On invalid argument, it shouldn't modify the handle signals state. - EXPECT_EQ(kFullMojoHandleSignalsState.satisfied_signals, - hss.satisfied_signals); - EXPECT_EQ(kFullMojoHandleSignalsState.satisfiable_signals, - hss.satisfiable_signals); - } - - // |WaitMany()|: - { - MojoHandle handles[2] = {MOJO_HANDLE_INVALID, MOJO_HANDLE_INVALID}; - MojoHandleSignals signals[2] = {~MOJO_HANDLE_SIGNAL_NONE, - ~MOJO_HANDLE_SIGNAL_NONE}; - EXPECT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - core()->WaitMany(MakeUserPointer(handles), MakeUserPointer(signals), 0, - MOJO_DEADLINE_INDEFINITE, NullUserPointer(), - NullUserPointer())); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WaitMany(NullUserPointer(), MakeUserPointer(signals), 0, - MOJO_DEADLINE_INDEFINITE, NullUserPointer(), - NullUserPointer())); - // If |num_handles| is invalid, it should leave |result_index| and - // |signals_states| alone. - // (We use -1 internally; make sure that doesn't leak.) - uint32_t result_index = 123; - MojoHandleSignalsState hss = kFullMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WaitMany(NullUserPointer(), MakeUserPointer(signals), 0, - MOJO_DEADLINE_INDEFINITE, - MakeUserPointer(&result_index), - MakeUserPointer(&hss))); - EXPECT_EQ(123u, result_index); - EXPECT_EQ(kFullMojoHandleSignalsState.satisfied_signals, - hss.satisfied_signals); - EXPECT_EQ(kFullMojoHandleSignalsState.satisfiable_signals, - hss.satisfiable_signals); - - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WaitMany(MakeUserPointer(handles), NullUserPointer(), 0, - MOJO_DEADLINE_INDEFINITE, NullUserPointer(), - NullUserPointer())); - EXPECT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - core()->WaitMany(MakeUserPointer(handles), MakeUserPointer(signals), 1, - MOJO_DEADLINE_INDEFINITE, NullUserPointer(), - NullUserPointer())); - // But if a handle is bad, then it should set |result_index| but still leave - // |signals_states| alone. - result_index = static_cast<uint32_t>(-1); - hss = kFullMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WaitMany( - MakeUserPointer(handles), MakeUserPointer(signals), 1, - MOJO_DEADLINE_INDEFINITE, MakeUserPointer(&result_index), - MakeUserPointer(&hss))); - EXPECT_EQ(0u, result_index); - EXPECT_EQ(kFullMojoHandleSignalsState.satisfied_signals, - hss.satisfied_signals); - EXPECT_EQ(kFullMojoHandleSignalsState.satisfiable_signals, - hss.satisfiable_signals); - - MockHandleInfo info[2]; - handles[0] = CreateMockHandle(&info[0]); - - result_index = static_cast<uint32_t>(-1); - hss = kFullMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->WaitMany( - MakeUserPointer(handles), MakeUserPointer(signals), 1, - MOJO_DEADLINE_INDEFINITE, MakeUserPointer(&result_index), - MakeUserPointer(&hss))); - EXPECT_EQ(0u, result_index); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(0u, hss.satisfiable_signals); - - // On invalid argument, it'll leave |signals_states| alone. - result_index = static_cast<uint32_t>(-1); - hss = kFullMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WaitMany( - MakeUserPointer(handles), MakeUserPointer(signals), 2, - MOJO_DEADLINE_INDEFINITE, MakeUserPointer(&result_index), - MakeUserPointer(&hss))); - EXPECT_EQ(1u, result_index); - EXPECT_EQ(kFullMojoHandleSignalsState.satisfied_signals, - hss.satisfied_signals); - EXPECT_EQ(kFullMojoHandleSignalsState.satisfiable_signals, - hss.satisfiable_signals); - handles[1] = handles[0] + 1; // Invalid handle. - EXPECT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - core()->WaitMany(MakeUserPointer(handles), MakeUserPointer(signals), 2, - MOJO_DEADLINE_INDEFINITE, NullUserPointer(), - NullUserPointer())); - handles[1] = CreateMockHandle(&info[1]); - EXPECT_EQ( - MOJO_RESULT_FAILED_PRECONDITION, - core()->WaitMany(MakeUserPointer(handles), MakeUserPointer(signals), 2, - MOJO_DEADLINE_INDEFINITE, NullUserPointer(), - NullUserPointer())); - - // TODO(vtl): Test one where we get "failed precondition" only for the - // second handle (and the first one is valid to wait on). - - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(handles[0])); - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(handles[1])); - } - - // |CreateMessagePipe()|: Nothing to check (apart from things that cause - // death). - - // |WriteMessage()|: - // Only check arguments checked by |Core|, namely |handle|, |handles|, and - // |num_handles|. - { - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WriteMessage(MOJO_HANDLE_INVALID, NullUserPointer(), 0, - NullUserPointer(), 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - MockHandleInfo info; - MojoHandle h = CreateMockHandle(&info); - MojoHandle handles[2] = {MOJO_HANDLE_INVALID, MOJO_HANDLE_INVALID}; - - // 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, NullUserPointer(), 0, MakeUserPointer(handles), - std::numeric_limits<uint32_t>::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, NullUserPointer(), 0, MakeUserPointer(handles), - std::numeric_limits<uint32_t>::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, NullUserPointer(), 0, MakeUserPointer(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, NullUserPointer(), 0, MakeUserPointer(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, NullUserPointer(), 0, MakeUserPointer(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, NullUserPointer(), 0, MakeUserPointer(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, NullUserPointer(), 0, MakeUserPointer(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, NullUserPointer(), 0, MakeUserPointer(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, NullUserPointer(), 0, MakeUserPointer(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, NullUserPointer(), - NullUserPointer(), NullUserPointer(), - NullUserPointer(), MOJO_READ_MESSAGE_FLAG_NONE)); - - MockHandleInfo info; - MojoHandle h = CreateMockHandle(&info); - - // Okay. - uint32_t handle_count = 0; - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage( - h, NullUserPointer(), NullUserPointer(), NullUserPointer(), - MakeUserPointer(&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)); - } -} - -// These test invalid arguments that should cause death if we're being paranoid -// about checking arguments (which we would want to do if, e.g., we were in a -// true "kernel" situation, but we might not want to do otherwise for -// performance reasons). Probably blatant errors like passing in null pointers -// (for required pointer arguments) will still cause death, but perhaps not -// predictably. -TEST_F(CoreTest, InvalidArgumentsDeath) { - const char kMemoryCheckFailedRegex[] = "Check failed"; - - // |WaitMany()|: - { - MojoHandle handle = MOJO_HANDLE_INVALID; - MojoHandleSignals signals = ~MOJO_HANDLE_SIGNAL_NONE; - EXPECT_DEATH_IF_SUPPORTED( - core()->WaitMany(NullUserPointer(), MakeUserPointer(&signals), 1, - MOJO_DEADLINE_INDEFINITE, NullUserPointer(), - NullUserPointer()), - kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED( - core()->WaitMany(MakeUserPointer(&handle), NullUserPointer(), 1, - MOJO_DEADLINE_INDEFINITE, NullUserPointer(), - NullUserPointer()), - kMemoryCheckFailedRegex); - // TODO(vtl): |result_index| and |signals_states| are optional. Test them - // with non-null invalid pointers? - } - - // |CreateMessagePipe()|: - { - MojoHandle h; - EXPECT_DEATH_IF_SUPPORTED( - core()->CreateMessagePipe(NullUserPointer(), NullUserPointer(), - NullUserPointer()), - kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED( - core()->CreateMessagePipe(NullUserPointer(), MakeUserPointer(&h), - NullUserPointer()), - kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED( - core()->CreateMessagePipe(NullUserPointer(), NullUserPointer(), - MakeUserPointer(&h)), - kMemoryCheckFailedRegex); - } - - // |WriteMessage()|: - // Only check arguments checked by |Core|, namely |handle|, |handles|, and - // |num_handles|. - { - MockHandleInfo info; - MojoHandle h = CreateMockHandle(&info); - - // Null |handles| with nonzero |num_handles|. - EXPECT_DEATH_IF_SUPPORTED( - core()->WriteMessage(h, NullUserPointer(), 0, NullUserPointer(), 1, - MOJO_WRITE_MESSAGE_FLAG_NONE), - kMemoryCheckFailedRegex); - - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h)); - } - - // |ReadMessage()|: - // Only check arguments checked by |Core|, namely |handle|, |handles|, and - // |num_handles|. - { - MockHandleInfo info; - MojoHandle h = CreateMockHandle(&info); - - uint32_t handle_count = 1; - EXPECT_DEATH_IF_SUPPORTED( - core()->ReadMessage(h, NullUserPointer(), NullUserPointer(), - NullUserPointer(), MakeUserPointer(&handle_count), - MOJO_READ_MESSAGE_FLAG_NONE), - kMemoryCheckFailedRegex); - - 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 signals) - -TEST_F(CoreTest, MessagePipe) { - MojoHandle h[2]; - MojoHandleSignalsState hss[2]; - uint32_t result_index; - - EXPECT_EQ(MOJO_RESULT_OK, - core()->CreateMessagePipe(NullUserPointer(), MakeUserPointer(&h[0]), - MakeUserPointer(&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. - MojoHandleSignals signals[2] = {MOJO_HANDLE_SIGNAL_READABLE, - MOJO_HANDLE_SIGNAL_READABLE}; - result_index = static_cast<uint32_t>(-1); - hss[0] = kEmptyMojoHandleSignalsState; - hss[1] = kEmptyMojoHandleSignalsState; - EXPECT_EQ( - MOJO_RESULT_DEADLINE_EXCEEDED, - core()->WaitMany(MakeUserPointer(h), MakeUserPointer(signals), 2, 0, - MakeUserPointer(&result_index), MakeUserPointer(hss))); - EXPECT_EQ(static_cast<uint32_t>(-1), result_index); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals); - EXPECT_EQ(kAllSignals, hss[0].satisfiable_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[1].satisfied_signals); - EXPECT_EQ(kAllSignals, hss[1].satisfiable_signals); - - // Try to read anyway. - char buffer[1] = {'a'}; - uint32_t buffer_size = 1; - EXPECT_EQ( - MOJO_RESULT_SHOULD_WAIT, - core()->ReadMessage(h[0], UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), NullUserPointer(), - NullUserPointer(), 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. - hss[0] = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, core()->Wait(h[0], MOJO_HANDLE_SIGNAL_WRITABLE, - 1000000000, MakeUserPointer(&hss[0]))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals); - EXPECT_EQ(kAllSignals, hss[0].satisfiable_signals); - hss[0] = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, core()->Wait(h[1], MOJO_HANDLE_SIGNAL_WRITABLE, - 1000000000, MakeUserPointer(&hss[0]))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals); - EXPECT_EQ(kAllSignals, hss[0].satisfiable_signals); - - // Also check that |h[1]| is writable using |WaitMany()|. - signals[0] = MOJO_HANDLE_SIGNAL_READABLE; - signals[1] = MOJO_HANDLE_SIGNAL_WRITABLE; - result_index = static_cast<uint32_t>(-1); - hss[0] = kEmptyMojoHandleSignalsState; - hss[1] = kEmptyMojoHandleSignalsState; - EXPECT_EQ( - MOJO_RESULT_OK, - core()->WaitMany(MakeUserPointer(h), MakeUserPointer(signals), 2, - MOJO_DEADLINE_INDEFINITE, MakeUserPointer(&result_index), - MakeUserPointer(hss))); - EXPECT_EQ(1u, result_index); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals); - EXPECT_EQ(kAllSignals, hss[0].satisfiable_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[1].satisfied_signals); - EXPECT_EQ(kAllSignals, hss[1].satisfiable_signals); - - // Write to |h[1]|. - buffer[0] = 'b'; - EXPECT_EQ( - MOJO_RESULT_OK, - core()->WriteMessage(h[1], UserPointer<const void>(buffer), 1, - NullUserPointer(), 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Check that |h[0]| is now readable. - signals[0] = MOJO_HANDLE_SIGNAL_READABLE; - signals[1] = MOJO_HANDLE_SIGNAL_READABLE; - result_index = static_cast<uint32_t>(-1); - hss[0] = kEmptyMojoHandleSignalsState; - hss[1] = kEmptyMojoHandleSignalsState; - EXPECT_EQ( - MOJO_RESULT_OK, - core()->WaitMany(MakeUserPointer(h), MakeUserPointer(signals), 2, - MOJO_DEADLINE_INDEFINITE, MakeUserPointer(&result_index), - MakeUserPointer(hss))); - EXPECT_EQ(0u, result_index); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss[0].satisfied_signals); - EXPECT_EQ(kAllSignals, hss[0].satisfiable_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[1].satisfied_signals); - EXPECT_EQ(kAllSignals, hss[1].satisfiable_signals); - - // Read from |h[0]|. - // First, get only the size. - buffer_size = 0; - EXPECT_EQ( - MOJO_RESULT_RESOURCE_EXHAUSTED, - core()->ReadMessage(h[0], NullUserPointer(), - MakeUserPointer(&buffer_size), NullUserPointer(), - NullUserPointer(), 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], UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), NullUserPointer(), - NullUserPointer(), MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ('b', buffer[0]); - EXPECT_EQ(1u, buffer_size); - - // |h[0]| should no longer be readable. - hss[0] = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - core()->Wait(h[0], MOJO_HANDLE_SIGNAL_READABLE, 0, - MakeUserPointer(&hss[0]))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals); - EXPECT_EQ(kAllSignals, hss[0].satisfiable_signals); - - // Write to |h[0]|. - buffer[0] = 'd'; - EXPECT_EQ( - MOJO_RESULT_OK, - core()->WriteMessage(h[0], UserPointer<const void>(buffer), 1, - NullUserPointer(), 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). - hss[0] = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(h[1], MOJO_HANDLE_SIGNAL_WRITABLE, 1000000000, - MakeUserPointer(&hss[0]))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss[0].satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss[0].satisfiable_signals); - - // Check that |h[1]| is still readable (for the moment). - hss[0] = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, core()->Wait(h[1], MOJO_HANDLE_SIGNAL_READABLE, - 1000000000, MakeUserPointer(&hss[0]))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss[0].satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss[0].satisfiable_signals); - - // Discard a message from |h[1]|. - EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, - core()->ReadMessage(h[1], NullUserPointer(), NullUserPointer(), - NullUserPointer(), NullUserPointer(), - MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)); - - // |h[1]| is no longer readable (and will never be). - hss[0] = kFullMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(h[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000, - MakeUserPointer(&hss[0]))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss[0].satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss[0].satisfiable_signals); - - // Try writing to |h[1]|. - buffer[0] = 'e'; - EXPECT_EQ( - MOJO_RESULT_FAILED_PRECONDITION, - core()->WriteMessage(h[1], UserPointer<const void>(buffer), 1, - NullUserPointer(), 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<uint32_t>(sizeof(kHello)); - const char kWorld[] = "world!!!"; - const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld)); - char buffer[100]; - const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer)); - uint32_t num_bytes; - MojoHandle handles[10]; - uint32_t num_handles; - MojoHandleSignalsState hss; - MojoHandle h_received; - - MojoHandle h_passing[2]; - EXPECT_EQ(MOJO_RESULT_OK, - core()->CreateMessagePipe(NullUserPointer(), - MakeUserPointer(&h_passing[0]), - MakeUserPointer(&h_passing[1]))); - - // Make sure that |h_passing[]| work properly. - EXPECT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h_passing[0], UserPointer<const void>(kHello), - kHelloSize, NullUserPointer(), 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000, - MakeUserPointer(&hss))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage( - h_passing[1], UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), MakeUserPointer(handles), - MakeUserPointer(&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], UserPointer<const void>(kHello), - kHelloSize, MakeUserPointer(&h_passing[0]), 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WriteMessage(h_passing[0], UserPointer<const void>(kHello), - kHelloSize, MakeUserPointer(&h_passing[1]), 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - MojoHandle h_passed[2]; - EXPECT_EQ(MOJO_RESULT_OK, - core()->CreateMessagePipe(NullUserPointer(), - MakeUserPointer(&h_passed[0]), - MakeUserPointer(&h_passed[1]))); - - // Make sure that |h_passed[]| work properly. - EXPECT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h_passed[0], UserPointer<const void>(kHello), - kHelloSize, NullUserPointer(), 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h_passed[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000, - MakeUserPointer(&hss))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage( - h_passed[1], UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), MakeUserPointer(handles), - MakeUserPointer(&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], UserPointer<const void>(kWorld), - kWorldSize, MakeUserPointer(&h_passed[1]), 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000, - MakeUserPointer(&hss))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage( - h_passing[1], UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), MakeUserPointer(handles), - MakeUserPointer(&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], UserPointer<const void>(kHello), - kHelloSize, NullUserPointer(), 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h_received, MOJO_HANDLE_SIGNAL_READABLE, 1000000000, - MakeUserPointer(&hss))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage( - h_received, UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), MakeUserPointer(handles), - MakeUserPointer(&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. - MojoHandleSignalsState hss; - - EXPECT_EQ(MOJO_RESULT_OK, - core()->CreateDataPipe(NullUserPointer(), MakeUserPointer(&ph), - MakeUserPointer(&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. - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ( - MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(ph, MOJO_HANDLE_SIGNAL_READABLE, 0, MakeUserPointer(&hss))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, core()->Wait(ph, MOJO_HANDLE_SIGNAL_WRITABLE, 0, - MakeUserPointer(&hss))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Consumer should be never-writable, and not yet readable. - hss = kFullMojoHandleSignalsState; - EXPECT_EQ( - MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(ch, MOJO_HANDLE_SIGNAL_WRITABLE, 0, MakeUserPointer(&hss))); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - hss = kFullMojoHandleSignalsState; - EXPECT_EQ( - MOJO_RESULT_DEADLINE_EXCEEDED, - core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, MakeUserPointer(&hss))); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Write. - signed char elements[2] = {'A', 'B'}; - uint32_t num_bytes = 2u; - EXPECT_EQ(MOJO_RESULT_OK, - core()->WriteData(ph, UserPointer<const void>(elements), - MakeUserPointer(&num_bytes), - MOJO_WRITE_DATA_FLAG_NONE)); - EXPECT_EQ(2u, num_bytes); - - // Consumer should now be readable. - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, - MakeUserPointer(&hss))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Peek one character. - elements[0] = -1; - elements[1] = -1; - num_bytes = 1u; - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadData( - ch, UserPointer<void>(elements), MakeUserPointer(&num_bytes), - MOJO_READ_DATA_FLAG_NONE | MOJO_READ_DATA_FLAG_PEEK)); - EXPECT_EQ('A', elements[0]); - EXPECT_EQ(-1, elements[1]); - - // Read one character. - elements[0] = -1; - elements[1] = -1; - num_bytes = 1u; - EXPECT_EQ(MOJO_RESULT_OK, core()->ReadData(ch, UserPointer<void>(elements), - MakeUserPointer(&num_bytes), - MOJO_READ_DATA_FLAG_NONE)); - EXPECT_EQ('A', elements[0]); - EXPECT_EQ(-1, elements[1]); - - // Two-phase write. - void* write_ptr = nullptr; - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, - core()->BeginWriteData(ph, MakeUserPointer(&write_ptr), - MakeUserPointer(&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, UserPointer<const void>(elements), - MakeUserPointer(&num_bytes), - MOJO_WRITE_DATA_FLAG_NONE)); - - // Actually write the data, and complete it now. - static_cast<char*>(write_ptr)[0] = 'C'; - static_cast<char*>(write_ptr)[1] = 'D'; - static_cast<char*>(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, NullUserPointer(), MakeUserPointer(&num_bytes), - MOJO_READ_DATA_FLAG_QUERY)); - EXPECT_EQ(4u, num_bytes); - - // Try to query with peek. Should fail. - num_bytes = 0; - EXPECT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - core()->ReadData(ch, NullUserPointer(), MakeUserPointer(&num_bytes), - MOJO_READ_DATA_FLAG_QUERY | MOJO_READ_DATA_FLAG_PEEK)); - EXPECT_EQ(0u, 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, NullUserPointer(), MakeUserPointer(&num_bytes), - MOJO_READ_DATA_FLAG_DISCARD | MOJO_READ_DATA_FLAG_ALL_OR_NONE)); - - // Try to discard two characters, in peek mode. Should fail. - num_bytes = 2; - EXPECT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - core()->ReadData(ch, NullUserPointer(), MakeUserPointer(&num_bytes), - MOJO_READ_DATA_FLAG_DISCARD | MOJO_READ_DATA_FLAG_PEEK)); - - // Discard two characters. - num_bytes = 2; - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadData( - ch, NullUserPointer(), MakeUserPointer(&num_bytes), - MOJO_READ_DATA_FLAG_DISCARD | MOJO_READ_DATA_FLAG_ALL_OR_NONE)); - - // Try a two-phase read of the remaining two bytes with peek. Should fail. - const void* read_ptr = nullptr; - num_bytes = 2; - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->BeginReadData(ch, MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), - MOJO_READ_DATA_FLAG_PEEK)); - - // Read the remaining two characters, in two-phase mode (all-or-none). - num_bytes = 2; - ASSERT_EQ(MOJO_RESULT_OK, - core()->BeginReadData(ch, MakeUserPointer(&read_ptr), - MakeUserPointer(&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, NullUserPointer(), MakeUserPointer(&num_bytes), - MOJO_READ_DATA_FLAG_DISCARD)); - - // Actually check our data and end the two-phase read. - EXPECT_EQ('D', static_cast<const char*>(read_ptr)[0]); - EXPECT_EQ('E', static_cast<const char*>(read_ptr)[1]); - EXPECT_EQ(MOJO_RESULT_OK, core()->EndReadData(ch, 2u)); - - // Consumer should now be no longer readable. - hss = kFullMojoHandleSignalsState; - EXPECT_EQ( - MOJO_RESULT_DEADLINE_EXCEEDED, - core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, MakeUserPointer(&hss))); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // TODO(vtl): More. - - // Close the producer. - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ph)); - - // The consumer should now be never-readable. - hss = kFullMojoHandleSignalsState; - EXPECT_EQ( - MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, MakeUserPointer(&hss))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - 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<uint32_t>(sizeof(kHello)); - const char kWorld[] = "world!!!"; - const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld)); - char buffer[100]; - const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer)); - uint32_t num_bytes; - MojoHandle handles[10]; - uint32_t num_handles; - MojoHandleSignalsState hss; - - MojoHandle h_passing[2]; - EXPECT_EQ(MOJO_RESULT_OK, - core()->CreateMessagePipe(NullUserPointer(), - MakeUserPointer(&h_passing[0]), - MakeUserPointer(&h_passing[1]))); - - MojoHandle ph, ch; - EXPECT_EQ(MOJO_RESULT_OK, - core()->CreateDataPipe(NullUserPointer(), MakeUserPointer(&ph), - MakeUserPointer(&ch))); - - // Send |ch| from |h_passing[0]| to |h_passing[1]|. - EXPECT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h_passing[0], UserPointer<const void>(kHello), - kHelloSize, MakeUserPointer(&ch), 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000, - MakeUserPointer(&hss))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage( - h_passing[1], UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), MakeUserPointer(handles), - MakeUserPointer(&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, UserPointer<const void>(kWorld), - MakeUserPointer(&num_bytes), - MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)); - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(ch_received, MOJO_HANDLE_SIGNAL_READABLE, 1000000000, - MakeUserPointer(&hss))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - num_bytes = kBufferSize; - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadData(ch_received, UserPointer<void>(buffer), - MakeUserPointer(&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], UserPointer<const void>(kWorld), - kWorldSize, MakeUserPointer(&ph), 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000, - MakeUserPointer(&hss))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage( - h_passing[1], UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), MakeUserPointer(handles), - MakeUserPointer(&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, UserPointer<const void>(kHello), - MakeUserPointer(&num_bytes), - MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)); - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(ch_received, MOJO_HANDLE_SIGNAL_READABLE, 1000000000, - MakeUserPointer(&hss))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - num_bytes = kBufferSize; - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadData(ch_received, UserPointer<void>(buffer), - MakeUserPointer(&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 = nullptr; - num_bytes = 0; - ASSERT_EQ(MOJO_RESULT_OK, - core()->BeginWriteData(ph, MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), - MOJO_WRITE_DATA_FLAG_NONE)); - ASSERT_GE(num_bytes, 1u); - EXPECT_EQ(MOJO_RESULT_BUSY, - core()->WriteMessage(h_passing[0], UserPointer<const void>(kHello), - kHelloSize, MakeUserPointer(&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], UserPointer<const void>(kHello), - kHelloSize, MakeUserPointer(&ch), 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ch = MOJO_HANDLE_INVALID; - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000, - NullUserPointer())); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage( - h_passing[1], UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), MakeUserPointer(handles), - MakeUserPointer(&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<char*>(write_ptr)[0] = 'x'; - EXPECT_EQ(MOJO_RESULT_OK, core()->EndWriteData(ph, 1)); - - // Wait for |ch| to be readable. - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, - 1000000000, MakeUserPointer(&hss))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Make sure that |ch| can't be sent if it's in a two-phase read. - const void* read_ptr = nullptr; - num_bytes = 1; - ASSERT_EQ(MOJO_RESULT_OK, - core()->BeginReadData(ch, MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), - MOJO_READ_DATA_FLAG_ALL_OR_NONE)); - EXPECT_EQ(MOJO_RESULT_BUSY, - core()->WriteMessage(h_passing[0], UserPointer<const void>(kHello), - kHelloSize, MakeUserPointer(&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], UserPointer<const void>(kWorld), - kWorldSize, MakeUserPointer(&ph), 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ph = MOJO_HANDLE_INVALID; - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000, - MakeUserPointer(&hss))); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - EXPECT_EQ(MOJO_RESULT_OK, - core()->ReadMessage( - h_passing[1], UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), MakeUserPointer(handles), - MakeUserPointer(&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<const char*>(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)); -} - -struct TestAsyncWaiter { - TestAsyncWaiter() : result(MOJO_RESULT_UNKNOWN) {} - - void Awake(MojoResult r) { result = r; } - - MojoResult result; -}; - -TEST_F(CoreTest, AsyncWait) { - TestAsyncWaiter waiter; - MockHandleInfo info; - MojoHandle h = CreateMockHandle(&info); - - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->AsyncWait(h, MOJO_HANDLE_SIGNAL_READABLE, - base::Bind(&TestAsyncWaiter::Awake, - base::Unretained(&waiter)))); - EXPECT_EQ(0u, info.GetAddedAwakableSize()); - - info.AllowAddAwakable(true); - EXPECT_EQ(MOJO_RESULT_OK, - core()->AsyncWait(h, MOJO_HANDLE_SIGNAL_READABLE, - base::Bind(&TestAsyncWaiter::Awake, - base::Unretained(&waiter)))); - EXPECT_EQ(1u, info.GetAddedAwakableSize()); - - EXPECT_FALSE(info.GetAddedAwakableAt(0)->Awake(MOJO_RESULT_BUSY, 0)); - EXPECT_EQ(MOJO_RESULT_BUSY, waiter.result); - - EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h)); -} - -// TODO(vtl): Test |DuplicateBufferHandle()| and |MapBuffer()|. - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/data_pipe.cc b/mojo/edk/system/data_pipe.cc deleted file mode 100644 index 2f433bd..0000000 --- a/mojo/edk/system/data_pipe.cc +++ /dev/null @@ -1,483 +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/edk/system/data_pipe.h" - -#include <string.h> - -#include <algorithm> -#include <limits> - -#include "base/logging.h" -#include "mojo/edk/system/awakable_list.h" -#include "mojo/edk/system/configuration.h" -#include "mojo/edk/system/memory.h" -#include "mojo/edk/system/options_validation.h" - -namespace mojo { -namespace system { - -// static -MojoCreateDataPipeOptions DataPipe::GetDefaultCreateOptions() { - MojoCreateDataPipeOptions result = { - static_cast<uint32_t>(sizeof(MojoCreateDataPipeOptions)), - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, - 1u, - static_cast<uint32_t>( - GetConfiguration().default_data_pipe_capacity_bytes)}; - return result; -} - -// static -MojoResult DataPipe::ValidateCreateOptions( - UserPointer<const MojoCreateDataPipeOptions> in_options, - MojoCreateDataPipeOptions* out_options) { - const MojoCreateDataPipeOptionsFlags kKnownFlags = - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD; - - *out_options = GetDefaultCreateOptions(); - if (in_options.IsNull()) - return MOJO_RESULT_OK; - - UserOptionsReader<MojoCreateDataPipeOptions> reader(in_options); - if (!reader.is_valid()) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (!OPTIONS_STRUCT_HAS_MEMBER(MojoCreateDataPipeOptions, flags, reader)) - return MOJO_RESULT_OK; - if ((reader.options().flags & ~kKnownFlags)) - return MOJO_RESULT_UNIMPLEMENTED; - out_options->flags = reader.options().flags; - - // Checks for fields beyond |flags|: - - if (!OPTIONS_STRUCT_HAS_MEMBER(MojoCreateDataPipeOptions, element_num_bytes, - reader)) - return MOJO_RESULT_OK; - if (reader.options().element_num_bytes == 0) - return MOJO_RESULT_INVALID_ARGUMENT; - out_options->element_num_bytes = reader.options().element_num_bytes; - - if (!OPTIONS_STRUCT_HAS_MEMBER(MojoCreateDataPipeOptions, capacity_num_bytes, - reader) || - reader.options().capacity_num_bytes == 0) { - // Round the default capacity down to a multiple of the element size (but at - // least one element). - size_t default_data_pipe_capacity_bytes = - GetConfiguration().default_data_pipe_capacity_bytes; - out_options->capacity_num_bytes = - std::max(static_cast<uint32_t>(default_data_pipe_capacity_bytes - - (default_data_pipe_capacity_bytes % - out_options->element_num_bytes)), - out_options->element_num_bytes); - return MOJO_RESULT_OK; - } - if (reader.options().capacity_num_bytes % out_options->element_num_bytes != 0) - return MOJO_RESULT_INVALID_ARGUMENT; - if (reader.options().capacity_num_bytes > - GetConfiguration().max_data_pipe_capacity_bytes) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - out_options->capacity_num_bytes = reader.options().capacity_num_bytes; - - return MOJO_RESULT_OK; -} - -void DataPipe::ProducerCancelAllAwakables() { - base::AutoLock locker(lock_); - DCHECK(has_local_producer_no_lock()); - producer_awakable_list_->CancelAll(); -} - -void DataPipe::ProducerClose() { - base::AutoLock locker(lock_); - DCHECK(producer_open_); - producer_open_ = false; - DCHECK(has_local_producer_no_lock()); - producer_awakable_list_.reset(); - // Not a bug, except possibly in "user" code. - DVLOG_IF(2, producer_in_two_phase_write_no_lock()) - << "Producer closed with active two-phase write"; - producer_two_phase_max_num_bytes_written_ = 0; - ProducerCloseImplNoLock(); - AwakeConsumerAwakablesForStateChangeNoLock( - ConsumerGetHandleSignalsStateImplNoLock()); -} - -MojoResult DataPipe::ProducerWriteData(UserPointer<const void> elements, - UserPointer<uint32_t> num_bytes, - bool all_or_none) { - base::AutoLock locker(lock_); - DCHECK(has_local_producer_no_lock()); - - if (producer_in_two_phase_write_no_lock()) - return MOJO_RESULT_BUSY; - if (!consumer_open_no_lock()) - return MOJO_RESULT_FAILED_PRECONDITION; - - // Returning "busy" takes priority over "invalid argument". - uint32_t max_num_bytes_to_write = num_bytes.Get(); - if (max_num_bytes_to_write % element_num_bytes_ != 0) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (max_num_bytes_to_write == 0) - return MOJO_RESULT_OK; // Nothing to do. - - uint32_t min_num_bytes_to_write = all_or_none ? max_num_bytes_to_write : 0; - - HandleSignalsState old_consumer_state = - ConsumerGetHandleSignalsStateImplNoLock(); - MojoResult rv = ProducerWriteDataImplNoLock( - elements, num_bytes, max_num_bytes_to_write, min_num_bytes_to_write); - HandleSignalsState new_consumer_state = - ConsumerGetHandleSignalsStateImplNoLock(); - if (!new_consumer_state.equals(old_consumer_state)) - AwakeConsumerAwakablesForStateChangeNoLock(new_consumer_state); - return rv; -} - -MojoResult DataPipe::ProducerBeginWriteData( - UserPointer<void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - bool all_or_none) { - base::AutoLock locker(lock_); - DCHECK(has_local_producer_no_lock()); - - if (producer_in_two_phase_write_no_lock()) - return MOJO_RESULT_BUSY; - if (!consumer_open_no_lock()) - return MOJO_RESULT_FAILED_PRECONDITION; - - uint32_t min_num_bytes_to_write = 0; - if (all_or_none) { - min_num_bytes_to_write = buffer_num_bytes.Get(); - if (min_num_bytes_to_write % element_num_bytes_ != 0) - return MOJO_RESULT_INVALID_ARGUMENT; - } - - MojoResult rv = ProducerBeginWriteDataImplNoLock(buffer, buffer_num_bytes, - min_num_bytes_to_write); - if (rv != MOJO_RESULT_OK) - return rv; - // Note: No need to awake producer awakables, even though we're going from - // writable to non-writable (since you can't wait on non-writability). - // Similarly, though this may have discarded data (in "may discard" mode), - // making it non-readable, there's still no need to awake consumer awakables. - DCHECK(producer_in_two_phase_write_no_lock()); - return MOJO_RESULT_OK; -} - -MojoResult DataPipe::ProducerEndWriteData(uint32_t num_bytes_written) { - base::AutoLock locker(lock_); - DCHECK(has_local_producer_no_lock()); - - if (!producer_in_two_phase_write_no_lock()) - return MOJO_RESULT_FAILED_PRECONDITION; - // Note: Allow successful completion of the two-phase write even if the - // consumer has been closed. - - HandleSignalsState old_consumer_state = - ConsumerGetHandleSignalsStateImplNoLock(); - MojoResult rv; - if (num_bytes_written > producer_two_phase_max_num_bytes_written_ || - num_bytes_written % element_num_bytes_ != 0) { - rv = MOJO_RESULT_INVALID_ARGUMENT; - producer_two_phase_max_num_bytes_written_ = 0; - } else { - rv = ProducerEndWriteDataImplNoLock(num_bytes_written); - } - // Two-phase write ended even on failure. - DCHECK(!producer_in_two_phase_write_no_lock()); - // If we're now writable, we *became* writable (since we weren't writable - // during the two-phase write), so awake producer awakables. - HandleSignalsState new_producer_state = - ProducerGetHandleSignalsStateImplNoLock(); - if (new_producer_state.satisfies(MOJO_HANDLE_SIGNAL_WRITABLE)) - AwakeProducerAwakablesForStateChangeNoLock(new_producer_state); - HandleSignalsState new_consumer_state = - ConsumerGetHandleSignalsStateImplNoLock(); - if (!new_consumer_state.equals(old_consumer_state)) - AwakeConsumerAwakablesForStateChangeNoLock(new_consumer_state); - return rv; -} - -HandleSignalsState DataPipe::ProducerGetHandleSignalsState() { - base::AutoLock locker(lock_); - DCHECK(has_local_producer_no_lock()); - return ProducerGetHandleSignalsStateImplNoLock(); -} - -MojoResult DataPipe::ProducerAddAwakable(Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state) { - base::AutoLock locker(lock_); - DCHECK(has_local_producer_no_lock()); - - HandleSignalsState producer_state = ProducerGetHandleSignalsStateImplNoLock(); - if (producer_state.satisfies(signals)) { - if (signals_state) - *signals_state = producer_state; - return MOJO_RESULT_ALREADY_EXISTS; - } - if (!producer_state.can_satisfy(signals)) { - if (signals_state) - *signals_state = producer_state; - return MOJO_RESULT_FAILED_PRECONDITION; - } - - producer_awakable_list_->Add(awakable, signals, context); - return MOJO_RESULT_OK; -} - -void DataPipe::ProducerRemoveAwakable(Awakable* awakable, - HandleSignalsState* signals_state) { - base::AutoLock locker(lock_); - DCHECK(has_local_producer_no_lock()); - producer_awakable_list_->Remove(awakable); - if (signals_state) - *signals_state = ProducerGetHandleSignalsStateImplNoLock(); -} - -bool DataPipe::ProducerIsBusy() const { - base::AutoLock locker(lock_); - return producer_in_two_phase_write_no_lock(); -} - -void DataPipe::ConsumerCancelAllAwakables() { - base::AutoLock locker(lock_); - DCHECK(has_local_consumer_no_lock()); - consumer_awakable_list_->CancelAll(); -} - -void DataPipe::ConsumerClose() { - base::AutoLock locker(lock_); - DCHECK(consumer_open_); - consumer_open_ = false; - DCHECK(has_local_consumer_no_lock()); - consumer_awakable_list_.reset(); - // Not a bug, except possibly in "user" code. - DVLOG_IF(2, consumer_in_two_phase_read_no_lock()) - << "Consumer closed with active two-phase read"; - consumer_two_phase_max_num_bytes_read_ = 0; - ConsumerCloseImplNoLock(); - AwakeProducerAwakablesForStateChangeNoLock( - ProducerGetHandleSignalsStateImplNoLock()); -} - -MojoResult DataPipe::ConsumerReadData(UserPointer<void> elements, - UserPointer<uint32_t> num_bytes, - bool all_or_none, - bool peek) { - base::AutoLock locker(lock_); - DCHECK(has_local_consumer_no_lock()); - - if (consumer_in_two_phase_read_no_lock()) - return MOJO_RESULT_BUSY; - - uint32_t max_num_bytes_to_read = num_bytes.Get(); - if (max_num_bytes_to_read % element_num_bytes_ != 0) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (max_num_bytes_to_read == 0) - return MOJO_RESULT_OK; // Nothing to do. - - uint32_t min_num_bytes_to_read = all_or_none ? max_num_bytes_to_read : 0; - - HandleSignalsState old_producer_state = - ProducerGetHandleSignalsStateImplNoLock(); - MojoResult rv = ConsumerReadDataImplNoLock( - elements, num_bytes, max_num_bytes_to_read, min_num_bytes_to_read, peek); - HandleSignalsState new_producer_state = - ProducerGetHandleSignalsStateImplNoLock(); - if (!new_producer_state.equals(old_producer_state)) - AwakeProducerAwakablesForStateChangeNoLock(new_producer_state); - return rv; -} - -MojoResult DataPipe::ConsumerDiscardData(UserPointer<uint32_t> num_bytes, - bool all_or_none) { - base::AutoLock locker(lock_); - DCHECK(has_local_consumer_no_lock()); - - if (consumer_in_two_phase_read_no_lock()) - return MOJO_RESULT_BUSY; - - uint32_t max_num_bytes_to_discard = num_bytes.Get(); - if (max_num_bytes_to_discard % element_num_bytes_ != 0) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (max_num_bytes_to_discard == 0) - return MOJO_RESULT_OK; // Nothing to do. - - uint32_t min_num_bytes_to_discard = - all_or_none ? max_num_bytes_to_discard : 0; - - HandleSignalsState old_producer_state = - ProducerGetHandleSignalsStateImplNoLock(); - MojoResult rv = ConsumerDiscardDataImplNoLock( - num_bytes, max_num_bytes_to_discard, min_num_bytes_to_discard); - HandleSignalsState new_producer_state = - ProducerGetHandleSignalsStateImplNoLock(); - if (!new_producer_state.equals(old_producer_state)) - AwakeProducerAwakablesForStateChangeNoLock(new_producer_state); - return rv; -} - -MojoResult DataPipe::ConsumerQueryData(UserPointer<uint32_t> num_bytes) { - base::AutoLock locker(lock_); - DCHECK(has_local_consumer_no_lock()); - - if (consumer_in_two_phase_read_no_lock()) - return MOJO_RESULT_BUSY; - - // Note: Don't need to validate |*num_bytes| for query. - return ConsumerQueryDataImplNoLock(num_bytes); -} - -MojoResult DataPipe::ConsumerBeginReadData( - UserPointer<const void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - bool all_or_none) { - base::AutoLock locker(lock_); - DCHECK(has_local_consumer_no_lock()); - - if (consumer_in_two_phase_read_no_lock()) - return MOJO_RESULT_BUSY; - - uint32_t min_num_bytes_to_read = 0; - if (all_or_none) { - min_num_bytes_to_read = buffer_num_bytes.Get(); - if (min_num_bytes_to_read % element_num_bytes_ != 0) - return MOJO_RESULT_INVALID_ARGUMENT; - } - - MojoResult rv = ConsumerBeginReadDataImplNoLock(buffer, buffer_num_bytes, - min_num_bytes_to_read); - if (rv != MOJO_RESULT_OK) - return rv; - DCHECK(consumer_in_two_phase_read_no_lock()); - return MOJO_RESULT_OK; -} - -MojoResult DataPipe::ConsumerEndReadData(uint32_t num_bytes_read) { - base::AutoLock locker(lock_); - DCHECK(has_local_consumer_no_lock()); - - if (!consumer_in_two_phase_read_no_lock()) - return MOJO_RESULT_FAILED_PRECONDITION; - - HandleSignalsState old_producer_state = - ProducerGetHandleSignalsStateImplNoLock(); - MojoResult rv; - if (num_bytes_read > consumer_two_phase_max_num_bytes_read_ || - num_bytes_read % element_num_bytes_ != 0) { - rv = MOJO_RESULT_INVALID_ARGUMENT; - consumer_two_phase_max_num_bytes_read_ = 0; - } else { - rv = ConsumerEndReadDataImplNoLock(num_bytes_read); - } - // Two-phase read ended even on failure. - DCHECK(!consumer_in_two_phase_read_no_lock()); - // If we're now readable, we *became* readable (since we weren't readable - // during the two-phase read), so awake consumer awakables. - HandleSignalsState new_consumer_state = - ConsumerGetHandleSignalsStateImplNoLock(); - if (new_consumer_state.satisfies(MOJO_HANDLE_SIGNAL_READABLE)) - AwakeConsumerAwakablesForStateChangeNoLock(new_consumer_state); - HandleSignalsState new_producer_state = - ProducerGetHandleSignalsStateImplNoLock(); - if (!new_producer_state.equals(old_producer_state)) - AwakeProducerAwakablesForStateChangeNoLock(new_producer_state); - return rv; -} - -HandleSignalsState DataPipe::ConsumerGetHandleSignalsState() { - base::AutoLock locker(lock_); - DCHECK(has_local_consumer_no_lock()); - return ConsumerGetHandleSignalsStateImplNoLock(); -} - -MojoResult DataPipe::ConsumerAddAwakable(Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state) { - base::AutoLock locker(lock_); - DCHECK(has_local_consumer_no_lock()); - - HandleSignalsState consumer_state = ConsumerGetHandleSignalsStateImplNoLock(); - if (consumer_state.satisfies(signals)) { - if (signals_state) - *signals_state = consumer_state; - return MOJO_RESULT_ALREADY_EXISTS; - } - if (!consumer_state.can_satisfy(signals)) { - if (signals_state) - *signals_state = consumer_state; - return MOJO_RESULT_FAILED_PRECONDITION; - } - - consumer_awakable_list_->Add(awakable, signals, context); - return MOJO_RESULT_OK; -} - -void DataPipe::ConsumerRemoveAwakable(Awakable* awakable, - HandleSignalsState* signals_state) { - base::AutoLock locker(lock_); - DCHECK(has_local_consumer_no_lock()); - consumer_awakable_list_->Remove(awakable); - if (signals_state) - *signals_state = ConsumerGetHandleSignalsStateImplNoLock(); -} - -bool DataPipe::ConsumerIsBusy() const { - base::AutoLock locker(lock_); - return consumer_in_two_phase_read_no_lock(); -} - -DataPipe::DataPipe(bool has_local_producer, - bool has_local_consumer, - const MojoCreateDataPipeOptions& validated_options) - : may_discard_((validated_options.flags & - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD)), - element_num_bytes_(validated_options.element_num_bytes), - capacity_num_bytes_(validated_options.capacity_num_bytes), - producer_open_(true), - consumer_open_(true), - producer_awakable_list_(has_local_producer ? new AwakableList() - : nullptr), - consumer_awakable_list_(has_local_consumer ? new AwakableList() - : nullptr), - producer_two_phase_max_num_bytes_written_(0), - consumer_two_phase_max_num_bytes_read_(0) { - // Check that the passed in options actually are validated. - MojoCreateDataPipeOptions unused = {0}; - DCHECK_EQ(ValidateCreateOptions(MakeUserPointer(&validated_options), &unused), - MOJO_RESULT_OK); -} - -DataPipe::~DataPipe() { - DCHECK(!producer_open_); - DCHECK(!consumer_open_); - DCHECK(!producer_awakable_list_); - DCHECK(!consumer_awakable_list_); -} - -void DataPipe::AwakeProducerAwakablesForStateChangeNoLock( - const HandleSignalsState& new_producer_state) { - lock_.AssertAcquired(); - if (!has_local_producer_no_lock()) - return; - producer_awakable_list_->AwakeForStateChange(new_producer_state); -} - -void DataPipe::AwakeConsumerAwakablesForStateChangeNoLock( - const HandleSignalsState& new_consumer_state) { - lock_.AssertAcquired(); - if (!has_local_consumer_no_lock()) - return; - consumer_awakable_list_->AwakeForStateChange(new_consumer_state); -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/data_pipe.h b/mojo/edk/system/data_pipe.h deleted file mode 100644 index d893465..0000000 --- a/mojo/edk/system/data_pipe.h +++ /dev/null @@ -1,219 +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_EDK_SYSTEM_DATA_PIPE_H_ -#define MOJO_EDK_SYSTEM_DATA_PIPE_H_ - -#include <stdint.h> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/system/handle_signals_state.h" -#include "mojo/edk/system/memory.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/data_pipe.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { -namespace system { - -class Awakable; -class AwakableList; - -// |DataPipe| is a base class for secondary objects implementing data pipes, -// 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 -// class is thread-safe. -class MOJO_SYSTEM_IMPL_EXPORT DataPipe - : public base::RefCountedThreadSafe<DataPipe> { - public: - // The default options for |MojoCreateDataPipe()|. (Real uses should obtain - // this via |ValidateCreateOptions()| with a null |in_options|; this is - // exposed directly for testing convenience.) - static MojoCreateDataPipeOptions GetDefaultCreateOptions(); - - // Validates and/or sets default options for |MojoCreateDataPipeOptions|. If - // non-null, |in_options| must point to a struct of at least - // |in_options->struct_size| bytes. |out_options| must point to a (current) - // |MojoCreateDataPipeOptions| and will be entirely overwritten on success (it - // may be partly overwritten on failure). - static MojoResult ValidateCreateOptions( - UserPointer<const MojoCreateDataPipeOptions> in_options, - MojoCreateDataPipeOptions* out_options); - - // These are called by the producer dispatcher to implement its methods of - // corresponding names. - void ProducerCancelAllAwakables(); - void ProducerClose(); - MojoResult ProducerWriteData(UserPointer<const void> elements, - UserPointer<uint32_t> num_bytes, - bool all_or_none); - MojoResult ProducerBeginWriteData(UserPointer<void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - bool all_or_none); - MojoResult ProducerEndWriteData(uint32_t num_bytes_written); - HandleSignalsState ProducerGetHandleSignalsState(); - MojoResult ProducerAddAwakable(Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state); - void ProducerRemoveAwakable(Awakable* awakable, - HandleSignalsState* signals_state); - bool ProducerIsBusy() const; - - // These are called by the consumer dispatcher to implement its methods of - // corresponding names. - void ConsumerCancelAllAwakables(); - void ConsumerClose(); - // This does not validate its arguments, except to check that |*num_bytes| is - // a multiple of |element_num_bytes_|. - MojoResult ConsumerReadData(UserPointer<void> elements, - UserPointer<uint32_t> num_bytes, - bool all_or_none, - bool peek); - MojoResult ConsumerDiscardData(UserPointer<uint32_t> num_bytes, - bool all_or_none); - MojoResult ConsumerQueryData(UserPointer<uint32_t> num_bytes); - MojoResult ConsumerBeginReadData(UserPointer<const void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - bool all_or_none); - MojoResult ConsumerEndReadData(uint32_t num_bytes_read); - HandleSignalsState ConsumerGetHandleSignalsState(); - MojoResult ConsumerAddAwakable(Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state); - void ConsumerRemoveAwakable(Awakable* awakable, - HandleSignalsState* signals_state); - bool ConsumerIsBusy() const; - - protected: - DataPipe(bool has_local_producer, - bool has_local_consumer, - const MojoCreateDataPipeOptions& validated_options); - - friend class base::RefCountedThreadSafe<DataPipe>; - virtual ~DataPipe(); - - virtual void ProducerCloseImplNoLock() = 0; - // |num_bytes.Get()| will be a nonzero multiple of |element_num_bytes_|. - virtual MojoResult ProducerWriteDataImplNoLock( - UserPointer<const void> elements, - UserPointer<uint32_t> num_bytes, - uint32_t max_num_bytes_to_write, - uint32_t min_num_bytes_to_write) = 0; - virtual MojoResult ProducerBeginWriteDataImplNoLock( - UserPointer<void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - uint32_t min_num_bytes_to_write) = 0; - virtual MojoResult ProducerEndWriteDataImplNoLock( - uint32_t num_bytes_written) = 0; - // Note: A producer should not be writable during a two-phase write. - virtual HandleSignalsState ProducerGetHandleSignalsStateImplNoLock() - const = 0; - - virtual void ConsumerCloseImplNoLock() = 0; - // |*num_bytes| will be a nonzero multiple of |element_num_bytes_|. - virtual MojoResult ConsumerReadDataImplNoLock(UserPointer<void> elements, - UserPointer<uint32_t> num_bytes, - uint32_t max_num_bytes_to_read, - uint32_t min_num_bytes_to_read, - bool peek) = 0; - virtual MojoResult ConsumerDiscardDataImplNoLock( - UserPointer<uint32_t> num_bytes, - uint32_t max_num_bytes_to_discard, - uint32_t min_num_bytes_to_discard) = 0; - // |*num_bytes| will be a nonzero multiple of |element_num_bytes_|. - virtual MojoResult ConsumerQueryDataImplNoLock( - UserPointer<uint32_t> num_bytes) = 0; - virtual MojoResult ConsumerBeginReadDataImplNoLock( - UserPointer<const void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - uint32_t min_num_bytes_to_read) = 0; - virtual MojoResult ConsumerEndReadDataImplNoLock(uint32_t num_bytes_read) = 0; - // Note: A consumer should not be writable during a two-phase read. - virtual HandleSignalsState ConsumerGetHandleSignalsStateImplNoLock() - const = 0; - - // Thread-safe and fast (they don't take the lock): - bool may_discard() const { return may_discard_; } - size_t element_num_bytes() const { return element_num_bytes_; } - size_t capacity_num_bytes() const { return capacity_num_bytes_; } - - // Must be called under lock. - bool producer_open_no_lock() const { - lock_.AssertAcquired(); - return producer_open_; - } - bool consumer_open_no_lock() const { - lock_.AssertAcquired(); - return consumer_open_; - } - uint32_t producer_two_phase_max_num_bytes_written_no_lock() const { - lock_.AssertAcquired(); - return producer_two_phase_max_num_bytes_written_; - } - uint32_t consumer_two_phase_max_num_bytes_read_no_lock() const { - lock_.AssertAcquired(); - return consumer_two_phase_max_num_bytes_read_; - } - void set_producer_two_phase_max_num_bytes_written_no_lock( - uint32_t num_bytes) { - lock_.AssertAcquired(); - producer_two_phase_max_num_bytes_written_ = num_bytes; - } - void set_consumer_two_phase_max_num_bytes_read_no_lock(uint32_t num_bytes) { - lock_.AssertAcquired(); - consumer_two_phase_max_num_bytes_read_ = num_bytes; - } - bool producer_in_two_phase_write_no_lock() const { - lock_.AssertAcquired(); - return producer_two_phase_max_num_bytes_written_ > 0; - } - bool consumer_in_two_phase_read_no_lock() const { - lock_.AssertAcquired(); - return consumer_two_phase_max_num_bytes_read_ > 0; - } - - private: - void AwakeProducerAwakablesForStateChangeNoLock( - const HandleSignalsState& new_producer_state); - void AwakeConsumerAwakablesForStateChangeNoLock( - const HandleSignalsState& new_consumer_state); - - bool has_local_producer_no_lock() const { - lock_.AssertAcquired(); - return !!producer_awakable_list_; - } - bool has_local_consumer_no_lock() const { - lock_.AssertAcquired(); - return !!consumer_awakable_list_; - } - - const bool may_discard_; - const size_t element_num_bytes_; - const size_t capacity_num_bytes_; - - mutable base::Lock lock_; // Protects the following members. - // *Known* state of producer or consumer. - bool producer_open_; - bool consumer_open_; - // Non-null only if the producer or consumer, respectively, is local. - scoped_ptr<AwakableList> producer_awakable_list_; - scoped_ptr<AwakableList> consumer_awakable_list_; - // These are nonzero if and only if a two-phase write/read is in progress. - uint32_t producer_two_phase_max_num_bytes_written_; - uint32_t consumer_two_phase_max_num_bytes_read_; - - DISALLOW_COPY_AND_ASSIGN(DataPipe); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_DATA_PIPE_H_ diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.cc b/mojo/edk/system/data_pipe_consumer_dispatcher.cc deleted file mode 100644 index 21127c6..0000000 --- a/mojo/edk/system/data_pipe_consumer_dispatcher.cc +++ /dev/null @@ -1,135 +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/edk/system/data_pipe_consumer_dispatcher.h" - -#include "base/logging.h" -#include "mojo/edk/system/data_pipe.h" -#include "mojo/edk/system/memory.h" - -namespace mojo { -namespace system { - -DataPipeConsumerDispatcher::DataPipeConsumerDispatcher() { -} - -void DataPipeConsumerDispatcher::Init(scoped_refptr<DataPipe> data_pipe) { - DCHECK(data_pipe); - data_pipe_ = data_pipe; -} - -Dispatcher::Type DataPipeConsumerDispatcher::GetType() const { - return kTypeDataPipeConsumer; -} - -DataPipeConsumerDispatcher::~DataPipeConsumerDispatcher() { - // |Close()|/|CloseImplNoLock()| should have taken care of the pipe. - DCHECK(!data_pipe_); -} - -void DataPipeConsumerDispatcher::CancelAllAwakablesNoLock() { - lock().AssertAcquired(); - data_pipe_->ConsumerCancelAllAwakables(); -} - -void DataPipeConsumerDispatcher::CloseImplNoLock() { - lock().AssertAcquired(); - data_pipe_->ConsumerClose(); - data_pipe_ = nullptr; -} - -scoped_refptr<Dispatcher> -DataPipeConsumerDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() { - lock().AssertAcquired(); - - scoped_refptr<DataPipeConsumerDispatcher> rv = - new DataPipeConsumerDispatcher(); - rv->Init(data_pipe_); - data_pipe_ = nullptr; - return scoped_refptr<Dispatcher>(rv.get()); -} - -MojoResult DataPipeConsumerDispatcher::ReadDataImplNoLock( - UserPointer<void> elements, - UserPointer<uint32_t> num_bytes, - MojoReadDataFlags flags) { - lock().AssertAcquired(); - - if ((flags & MOJO_READ_DATA_FLAG_DISCARD)) { - // These flags are mutally exclusive. - if ((flags & MOJO_READ_DATA_FLAG_QUERY) || - (flags & MOJO_READ_DATA_FLAG_PEEK)) - return MOJO_RESULT_INVALID_ARGUMENT; - DVLOG_IF(2, !elements.IsNull()) - << "Discard mode: ignoring non-null |elements|"; - return data_pipe_->ConsumerDiscardData( - num_bytes, (flags & MOJO_READ_DATA_FLAG_ALL_OR_NONE)); - } - - if ((flags & MOJO_READ_DATA_FLAG_QUERY)) { - if ((flags & MOJO_READ_DATA_FLAG_PEEK)) - return MOJO_RESULT_INVALID_ARGUMENT; - DCHECK(!(flags & MOJO_READ_DATA_FLAG_DISCARD)); // Handled above. - DVLOG_IF(2, !elements.IsNull()) - << "Query mode: ignoring non-null |elements|"; - return data_pipe_->ConsumerQueryData(num_bytes); - } - - return data_pipe_->ConsumerReadData( - elements, num_bytes, !!(flags & MOJO_READ_DATA_FLAG_ALL_OR_NONE), - !!(flags & MOJO_READ_DATA_FLAG_PEEK)); -} - -MojoResult DataPipeConsumerDispatcher::BeginReadDataImplNoLock( - UserPointer<const void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - MojoReadDataFlags flags) { - lock().AssertAcquired(); - - // These flags may not be used in two-phase mode. - if ((flags & MOJO_READ_DATA_FLAG_DISCARD) || - (flags & MOJO_READ_DATA_FLAG_QUERY) || (flags & MOJO_READ_DATA_FLAG_PEEK)) - return MOJO_RESULT_INVALID_ARGUMENT; - - return data_pipe_->ConsumerBeginReadData( - buffer, buffer_num_bytes, (flags & MOJO_READ_DATA_FLAG_ALL_OR_NONE)); -} - -MojoResult DataPipeConsumerDispatcher::EndReadDataImplNoLock( - uint32_t num_bytes_read) { - lock().AssertAcquired(); - - return data_pipe_->ConsumerEndReadData(num_bytes_read); -} - -HandleSignalsState DataPipeConsumerDispatcher::GetHandleSignalsStateImplNoLock() - const { - lock().AssertAcquired(); - return data_pipe_->ConsumerGetHandleSignalsState(); -} - -MojoResult DataPipeConsumerDispatcher::AddAwakableImplNoLock( - Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state) { - lock().AssertAcquired(); - return data_pipe_->ConsumerAddAwakable(awakable, signals, context, - signals_state); -} - -void DataPipeConsumerDispatcher::RemoveAwakableImplNoLock( - Awakable* awakable, - HandleSignalsState* signals_state) { - lock().AssertAcquired(); - data_pipe_->ConsumerRemoveAwakable(awakable, signals_state); -} - -bool DataPipeConsumerDispatcher::IsBusyNoLock() const { - lock().AssertAcquired(); - return data_pipe_->ConsumerIsBusy(); -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.h b/mojo/edk/system/data_pipe_consumer_dispatcher.h deleted file mode 100644 index 10a3d94..0000000 --- a/mojo/edk/system/data_pipe_consumer_dispatcher.h +++ /dev/null @@ -1,64 +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_EDK_SYSTEM_DATA_PIPE_CONSUMER_DISPATCHER_H_ -#define MOJO_EDK_SYSTEM_DATA_PIPE_CONSUMER_DISPATCHER_H_ - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace system { - -class DataPipe; - -// This is the |Dispatcher| implementation for the consumer handle for data -// pipes (created by the Mojo primitive |MojoCreateDataPipe()|). This class is -// thread-safe. -class MOJO_SYSTEM_IMPL_EXPORT DataPipeConsumerDispatcher : public Dispatcher { - public: - DataPipeConsumerDispatcher(); - - // Must be called before any other methods. - void Init(scoped_refptr<DataPipe> data_pipe); - - // |Dispatcher| public methods: - Type GetType() const override; - - private: - ~DataPipeConsumerDispatcher() override; - - // |Dispatcher| protected methods: - void CancelAllAwakablesNoLock() override; - void CloseImplNoLock() override; - scoped_refptr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock() - override; - MojoResult ReadDataImplNoLock(UserPointer<void> elements, - UserPointer<uint32_t> num_bytes, - MojoReadDataFlags flags) override; - MojoResult BeginReadDataImplNoLock(UserPointer<const void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - MojoReadDataFlags flags) override; - MojoResult EndReadDataImplNoLock(uint32_t num_bytes_read) override; - HandleSignalsState GetHandleSignalsStateImplNoLock() const override; - MojoResult AddAwakableImplNoLock(Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state) override; - void RemoveAwakableImplNoLock(Awakable* awakable, - HandleSignalsState* signals_state) override; - bool IsBusyNoLock() const override; - - // Protected by |lock()|: - scoped_refptr<DataPipe> data_pipe_; // This will be null if closed. - - DISALLOW_COPY_AND_ASSIGN(DataPipeConsumerDispatcher); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_DATA_PIPE_CONSUMER_DISPATCHER_H_ diff --git a/mojo/edk/system/data_pipe_producer_dispatcher.cc b/mojo/edk/system/data_pipe_producer_dispatcher.cc deleted file mode 100644 index 0531126..0000000 --- a/mojo/edk/system/data_pipe_producer_dispatcher.cc +++ /dev/null @@ -1,108 +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/edk/system/data_pipe_producer_dispatcher.h" - -#include "base/logging.h" -#include "mojo/edk/system/data_pipe.h" -#include "mojo/edk/system/memory.h" - -namespace mojo { -namespace system { - -DataPipeProducerDispatcher::DataPipeProducerDispatcher() { -} - -void DataPipeProducerDispatcher::Init(scoped_refptr<DataPipe> data_pipe) { - DCHECK(data_pipe); - data_pipe_ = data_pipe; -} - -Dispatcher::Type DataPipeProducerDispatcher::GetType() const { - return kTypeDataPipeProducer; -} - -DataPipeProducerDispatcher::~DataPipeProducerDispatcher() { - // |Close()|/|CloseImplNoLock()| should have taken care of the pipe. - DCHECK(!data_pipe_); -} - -void DataPipeProducerDispatcher::CancelAllAwakablesNoLock() { - lock().AssertAcquired(); - data_pipe_->ProducerCancelAllAwakables(); -} - -void DataPipeProducerDispatcher::CloseImplNoLock() { - lock().AssertAcquired(); - data_pipe_->ProducerClose(); - data_pipe_ = nullptr; -} - -scoped_refptr<Dispatcher> -DataPipeProducerDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() { - lock().AssertAcquired(); - - scoped_refptr<DataPipeProducerDispatcher> rv = - new DataPipeProducerDispatcher(); - rv->Init(data_pipe_); - data_pipe_ = nullptr; - return scoped_refptr<Dispatcher>(rv.get()); -} - -MojoResult DataPipeProducerDispatcher::WriteDataImplNoLock( - UserPointer<const void> elements, - UserPointer<uint32_t> num_bytes, - MojoWriteDataFlags flags) { - lock().AssertAcquired(); - return data_pipe_->ProducerWriteData( - elements, num_bytes, (flags & MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)); -} - -MojoResult DataPipeProducerDispatcher::BeginWriteDataImplNoLock( - UserPointer<void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - MojoWriteDataFlags flags) { - lock().AssertAcquired(); - - return data_pipe_->ProducerBeginWriteData( - buffer, buffer_num_bytes, (flags & MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)); -} - -MojoResult DataPipeProducerDispatcher::EndWriteDataImplNoLock( - uint32_t num_bytes_written) { - lock().AssertAcquired(); - - return data_pipe_->ProducerEndWriteData(num_bytes_written); -} - -HandleSignalsState DataPipeProducerDispatcher::GetHandleSignalsStateImplNoLock() - const { - lock().AssertAcquired(); - return data_pipe_->ProducerGetHandleSignalsState(); -} - -MojoResult DataPipeProducerDispatcher::AddAwakableImplNoLock( - Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state) { - lock().AssertAcquired(); - return data_pipe_->ProducerAddAwakable(awakable, signals, context, - signals_state); -} - -void DataPipeProducerDispatcher::RemoveAwakableImplNoLock( - Awakable* awakable, - HandleSignalsState* signals_state) { - lock().AssertAcquired(); - data_pipe_->ProducerRemoveAwakable(awakable, signals_state); -} - -bool DataPipeProducerDispatcher::IsBusyNoLock() const { - lock().AssertAcquired(); - return data_pipe_->ProducerIsBusy(); -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/data_pipe_producer_dispatcher.h b/mojo/edk/system/data_pipe_producer_dispatcher.h deleted file mode 100644 index 39c070c..0000000 --- a/mojo/edk/system/data_pipe_producer_dispatcher.h +++ /dev/null @@ -1,64 +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_EDK_SYSTEM_DATA_PIPE_PRODUCER_DISPATCHER_H_ -#define MOJO_EDK_SYSTEM_DATA_PIPE_PRODUCER_DISPATCHER_H_ - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace system { - -class DataPipe; - -// This is the |Dispatcher| implementation for the producer handle for data -// pipes (created by the Mojo primitive |MojoCreateDataPipe()|). This class is -// thread-safe. -class MOJO_SYSTEM_IMPL_EXPORT DataPipeProducerDispatcher : public Dispatcher { - public: - DataPipeProducerDispatcher(); - - // Must be called before any other methods. - void Init(scoped_refptr<DataPipe> data_pipe); - - // |Dispatcher| public methods: - Type GetType() const override; - - private: - ~DataPipeProducerDispatcher() override; - - // |Dispatcher| protected methods: - void CancelAllAwakablesNoLock() override; - void CloseImplNoLock() override; - scoped_refptr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock() - override; - MojoResult WriteDataImplNoLock(UserPointer<const void> elements, - UserPointer<uint32_t> num_bytes, - MojoWriteDataFlags flags) override; - MojoResult BeginWriteDataImplNoLock(UserPointer<void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - MojoWriteDataFlags flags) override; - MojoResult EndWriteDataImplNoLock(uint32_t num_bytes_written) override; - HandleSignalsState GetHandleSignalsStateImplNoLock() const override; - MojoResult AddAwakableImplNoLock(Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state) override; - void RemoveAwakableImplNoLock(Awakable* awakable, - HandleSignalsState* signals_state) override; - bool IsBusyNoLock() const override; - - // Protected by |lock()|: - scoped_refptr<DataPipe> data_pipe_; // This will be null if closed. - - DISALLOW_COPY_AND_ASSIGN(DataPipeProducerDispatcher); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_DATA_PIPE_PRODUCER_DISPATCHER_H_ diff --git a/mojo/edk/system/data_pipe_unittest.cc b/mojo/edk/system/data_pipe_unittest.cc deleted file mode 100644 index ad02222..0000000 --- a/mojo/edk/system/data_pipe_unittest.cc +++ /dev/null @@ -1,375 +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/edk/system/data_pipe.h" - -#include <stddef.h> -#include <stdint.h> - -#include <limits> - -#include "mojo/edk/system/configuration.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -const uint32_t kSizeOfCreateOptions = - static_cast<uint32_t>(sizeof(MojoCreateDataPipeOptions)); - -// Does a cursory sanity check of |validated_options|. Calls -// |ValidateCreateOptions()| on already-validated options. The validated options -// should be valid, and the revalidated copy should be the same. -void RevalidateCreateOptions( - const MojoCreateDataPipeOptions& validated_options) { - EXPECT_EQ(kSizeOfCreateOptions, validated_options.struct_size); - // Nothing to check for flags. - EXPECT_GT(validated_options.element_num_bytes, 0u); - EXPECT_GT(validated_options.capacity_num_bytes, 0u); - EXPECT_EQ(0u, validated_options.capacity_num_bytes % - validated_options.element_num_bytes); - - MojoCreateDataPipeOptions revalidated_options = {}; - EXPECT_EQ(MOJO_RESULT_OK, - DataPipe::ValidateCreateOptions(MakeUserPointer(&validated_options), - &revalidated_options)); - EXPECT_EQ(validated_options.struct_size, revalidated_options.struct_size); - EXPECT_EQ(validated_options.element_num_bytes, - revalidated_options.element_num_bytes); - EXPECT_EQ(validated_options.capacity_num_bytes, - revalidated_options.capacity_num_bytes); - EXPECT_EQ(validated_options.flags, revalidated_options.flags); -} - -// Checks that a default-computed capacity is correct. (Does not duplicate the -// checks done by |RevalidateCreateOptions()|.) -void CheckDefaultCapacity(const MojoCreateDataPipeOptions& validated_options) { - EXPECT_LE(validated_options.capacity_num_bytes, - GetConfiguration().default_data_pipe_capacity_bytes); - EXPECT_GT(validated_options.capacity_num_bytes + - validated_options.element_num_bytes, - GetConfiguration().default_data_pipe_capacity_bytes); -} - -// Tests valid inputs to |ValidateCreateOptions()|. -TEST(DataPipeTest, ValidateCreateOptionsValid) { - // Default options. - { - MojoCreateDataPipeOptions validated_options = {}; - EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( - NullUserPointer(), &validated_options)); - RevalidateCreateOptions(validated_options); - CheckDefaultCapacity(validated_options); - } - - // Size member, but nothing beyond. - { - MojoCreateDataPipeOptions options = { - offsetof(MojoCreateDataPipeOptions, flags) // |struct_size|. - }; - MojoCreateDataPipeOptions validated_options = {}; - EXPECT_EQ(MOJO_RESULT_OK, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), - &validated_options)); - RevalidateCreateOptions(validated_options); - CheckDefaultCapacity(validated_options); - } - - // Different flags. - MojoCreateDataPipeOptionsFlags flags_values[] = { - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD}; - for (size_t i = 0; i < arraysize(flags_values); i++) { - const MojoCreateDataPipeOptionsFlags flags = flags_values[i]; - - // Flags member, but nothing beyond. - { - MojoCreateDataPipeOptions options = { - // |struct_size|. - offsetof(MojoCreateDataPipeOptions, element_num_bytes), - flags // |flags|. - }; - MojoCreateDataPipeOptions validated_options = {}; - EXPECT_EQ(MOJO_RESULT_OK, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), - &validated_options)); - RevalidateCreateOptions(validated_options); - EXPECT_EQ(options.flags, validated_options.flags); - CheckDefaultCapacity(validated_options); - } - - // Different capacities (size 1). - for (uint32_t capacity = 1; capacity <= 100 * 1000 * 1000; capacity *= 10) { - MojoCreateDataPipeOptions options = { - kSizeOfCreateOptions, // |struct_size|. - flags, // |flags|. - 1, // |element_num_bytes|. - capacity // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {}; - EXPECT_EQ(MOJO_RESULT_OK, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), - &validated_options)) - << capacity; - RevalidateCreateOptions(validated_options); - EXPECT_EQ(options.flags, validated_options.flags); - EXPECT_EQ(options.element_num_bytes, validated_options.element_num_bytes); - EXPECT_EQ(options.capacity_num_bytes, - validated_options.capacity_num_bytes); - } - - // Small sizes. - for (uint32_t size = 1; size < 100; size++) { - // Different capacities. - for (uint32_t elements = 1; elements <= 1000 * 1000; elements *= 10) { - MojoCreateDataPipeOptions options = { - kSizeOfCreateOptions, // |struct_size|. - flags, // |flags|. - size, // |element_num_bytes|. - size * elements // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {}; - EXPECT_EQ(MOJO_RESULT_OK, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), - &validated_options)) - << size << ", " << elements; - RevalidateCreateOptions(validated_options); - EXPECT_EQ(options.flags, validated_options.flags); - EXPECT_EQ(options.element_num_bytes, - validated_options.element_num_bytes); - EXPECT_EQ(options.capacity_num_bytes, - validated_options.capacity_num_bytes); - } - - // Default capacity. - { - MojoCreateDataPipeOptions options = { - kSizeOfCreateOptions, // |struct_size|. - flags, // |flags|. - size, // |element_num_bytes|. - 0 // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {}; - EXPECT_EQ(MOJO_RESULT_OK, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), - &validated_options)) - << size; - RevalidateCreateOptions(validated_options); - EXPECT_EQ(options.flags, validated_options.flags); - EXPECT_EQ(options.element_num_bytes, - validated_options.element_num_bytes); - CheckDefaultCapacity(validated_options); - } - - // No capacity field. - { - MojoCreateDataPipeOptions options = { - // |struct_size|. - offsetof(MojoCreateDataPipeOptions, capacity_num_bytes), - flags, // |flags|. - size // |element_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {}; - EXPECT_EQ(MOJO_RESULT_OK, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), - &validated_options)) - << size; - RevalidateCreateOptions(validated_options); - EXPECT_EQ(options.flags, validated_options.flags); - EXPECT_EQ(options.element_num_bytes, - validated_options.element_num_bytes); - CheckDefaultCapacity(validated_options); - } - } - - // Larger sizes. - for (uint32_t size = 100; size <= 100 * 1000; size *= 10) { - // Capacity of 1000 elements. - { - MojoCreateDataPipeOptions options = { - kSizeOfCreateOptions, // |struct_size|. - flags, // |flags|. - size, // |element_num_bytes|. - 1000 * size // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {}; - EXPECT_EQ(MOJO_RESULT_OK, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), - &validated_options)) - << size; - RevalidateCreateOptions(validated_options); - EXPECT_EQ(options.flags, validated_options.flags); - EXPECT_EQ(options.element_num_bytes, - validated_options.element_num_bytes); - EXPECT_EQ(options.capacity_num_bytes, - validated_options.capacity_num_bytes); - } - - // Default capacity. - { - MojoCreateDataPipeOptions options = { - kSizeOfCreateOptions, // |struct_size|. - flags, // |flags|. - size, // |element_num_bytes|. - 0 // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {}; - EXPECT_EQ(MOJO_RESULT_OK, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), - &validated_options)) - << size; - RevalidateCreateOptions(validated_options); - EXPECT_EQ(options.flags, validated_options.flags); - EXPECT_EQ(options.element_num_bytes, - validated_options.element_num_bytes); - CheckDefaultCapacity(validated_options); - } - - // No capacity field. - { - MojoCreateDataPipeOptions options = { - // |struct_size|. - offsetof(MojoCreateDataPipeOptions, capacity_num_bytes), - flags, // |flags|. - size // |element_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {}; - EXPECT_EQ(MOJO_RESULT_OK, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), - &validated_options)) - << size; - RevalidateCreateOptions(validated_options); - EXPECT_EQ(options.flags, validated_options.flags); - EXPECT_EQ(options.element_num_bytes, - validated_options.element_num_bytes); - CheckDefaultCapacity(validated_options); - } - } - } -} - -TEST(DataPipeTest, ValidateCreateOptionsInvalid) { - // Invalid |struct_size|. - { - MojoCreateDataPipeOptions options = { - 1, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - 1, // |element_num_bytes|. - 0 // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions unused; - EXPECT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &unused)); - } - - // Unknown |flags|. - { - MojoCreateDataPipeOptions options = { - kSizeOfCreateOptions, // |struct_size|. - ~0u, // |flags|. - 1, // |element_num_bytes|. - 0 // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions unused; - EXPECT_EQ( - MOJO_RESULT_UNIMPLEMENTED, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &unused)); - } - - // Invalid |element_num_bytes|. - { - MojoCreateDataPipeOptions options = { - kSizeOfCreateOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - 0, // |element_num_bytes|. - 1000 // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions unused; - EXPECT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &unused)); - } - // |element_num_bytes| too big. - { - MojoCreateDataPipeOptions options = { - kSizeOfCreateOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - std::numeric_limits<uint32_t>::max(), // |element_num_bytes|. - std::numeric_limits<uint32_t>::max() // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions unused; - EXPECT_EQ( - MOJO_RESULT_RESOURCE_EXHAUSTED, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &unused)); - } - { - MojoCreateDataPipeOptions options = { - kSizeOfCreateOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - std::numeric_limits<uint32_t>::max() - 1000, // |element_num_bytes|. - std::numeric_limits<uint32_t>::max() - 1000 // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions unused; - EXPECT_EQ( - MOJO_RESULT_RESOURCE_EXHAUSTED, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &unused)); - } - - // Invalid |capacity_num_bytes|. - { - MojoCreateDataPipeOptions options = { - kSizeOfCreateOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - 2, // |element_num_bytes|. - 1 // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions unused; - EXPECT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &unused)); - } - { - MojoCreateDataPipeOptions options = { - kSizeOfCreateOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - 2, // |element_num_bytes|. - 111 // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions unused; - EXPECT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &unused)); - } - { - MojoCreateDataPipeOptions options = { - kSizeOfCreateOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - 5, // |element_num_bytes|. - 104 // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions unused; - EXPECT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &unused)); - } - // |capacity_num_bytes| too big. - { - MojoCreateDataPipeOptions options = { - kSizeOfCreateOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - 8, // |element_num_bytes|. - 0xffff0000 // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions unused; - EXPECT_EQ( - MOJO_RESULT_RESOURCE_EXHAUSTED, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), &unused)); - } -} - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/dispatcher.cc b/mojo/edk/system/dispatcher.cc deleted file mode 100644 index e2f2b87..0000000 --- a/mojo/edk/system/dispatcher.cc +++ /dev/null @@ -1,492 +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/edk/system/dispatcher.h" - -#include "base/logging.h" -#include "mojo/edk/system/configuration.h" -#include "mojo/edk/system/message_pipe_dispatcher.h" -#include "mojo/edk/system/platform_handle_dispatcher.h" -#include "mojo/edk/system/shared_buffer_dispatcher.h" - -namespace mojo { -namespace system { - -namespace test { - -// TODO(vtl): Maybe this should be defined in a test-only file instead. -DispatcherTransport DispatcherTryStartTransport(Dispatcher* dispatcher) { - return Dispatcher::HandleTableAccess::TryStartTransport(dispatcher); -} - -} // namespace test - -// Dispatcher ------------------------------------------------------------------ - -// static -DispatcherTransport Dispatcher::HandleTableAccess::TryStartTransport( - Dispatcher* dispatcher) { - DCHECK(dispatcher); - - if (!dispatcher->lock_.Try()) - return DispatcherTransport(); - - // We shouldn't race with things that close dispatchers, since closing can - // only take place either under |handle_table_lock_| or when the handle is - // marked as busy. - DCHECK(!dispatcher->is_closed_); - - return DispatcherTransport(dispatcher); -} - -// static -void Dispatcher::TransportDataAccess::StartSerialize( - Dispatcher* dispatcher, - Channel* channel, - size_t* max_size, - size_t* max_platform_handles) { - DCHECK(dispatcher); - dispatcher->StartSerialize(channel, max_size, max_platform_handles); -} - -// static -bool Dispatcher::TransportDataAccess::EndSerializeAndClose( - Dispatcher* dispatcher, - Channel* channel, - void* destination, - size_t* actual_size, - embedder::PlatformHandleVector* platform_handles) { - DCHECK(dispatcher); - return dispatcher->EndSerializeAndClose(channel, destination, actual_size, - platform_handles); -} - -// static -scoped_refptr<Dispatcher> Dispatcher::TransportDataAccess::Deserialize( - Channel* channel, - int32_t type, - const void* source, - size_t size, - embedder::PlatformHandleVector* platform_handles) { - switch (static_cast<int32_t>(type)) { - case kTypeUnknown: - DVLOG(2) << "Deserializing invalid handle"; - return nullptr; - case kTypeMessagePipe: - return scoped_refptr<Dispatcher>( - MessagePipeDispatcher::Deserialize(channel, source, size)); - case kTypeDataPipeProducer: - case kTypeDataPipeConsumer: - // TODO(vtl): Implement. - LOG(WARNING) << "Deserialization of dispatcher type " << type - << " not supported"; - return nullptr; - case kTypeSharedBuffer: - return scoped_refptr<Dispatcher>(SharedBufferDispatcher::Deserialize( - channel, source, size, platform_handles)); - case kTypePlatformHandle: - return scoped_refptr<Dispatcher>(PlatformHandleDispatcher::Deserialize( - channel, source, size, platform_handles)); - } - LOG(WARNING) << "Unknown dispatcher type " << type; - return nullptr; -} - -MojoResult Dispatcher::Close() { - base::AutoLock locker(lock_); - if (is_closed_) - return MOJO_RESULT_INVALID_ARGUMENT; - - CloseNoLock(); - return MOJO_RESULT_OK; -} - -MojoResult Dispatcher::WriteMessage( - UserPointer<const void> bytes, - uint32_t num_bytes, - std::vector<DispatcherTransport>* transports, - MojoWriteMessageFlags flags) { - DCHECK(!transports || - (transports->size() > 0 && - transports->size() < GetConfiguration().max_message_num_handles)); - - base::AutoLock locker(lock_); - if (is_closed_) - return MOJO_RESULT_INVALID_ARGUMENT; - - return WriteMessageImplNoLock(bytes, num_bytes, transports, flags); -} - -MojoResult Dispatcher::ReadMessage(UserPointer<void> bytes, - UserPointer<uint32_t> num_bytes, - DispatcherVector* dispatchers, - uint32_t* num_dispatchers, - MojoReadMessageFlags flags) { - DCHECK(!num_dispatchers || *num_dispatchers == 0 || - (dispatchers && dispatchers->empty())); - - base::AutoLock locker(lock_); - if (is_closed_) - return MOJO_RESULT_INVALID_ARGUMENT; - - return ReadMessageImplNoLock(bytes, num_bytes, dispatchers, num_dispatchers, - flags); -} - -MojoResult Dispatcher::WriteData(UserPointer<const void> elements, - UserPointer<uint32_t> num_bytes, - MojoWriteDataFlags flags) { - base::AutoLock locker(lock_); - if (is_closed_) - return MOJO_RESULT_INVALID_ARGUMENT; - - return WriteDataImplNoLock(elements, num_bytes, flags); -} - -MojoResult Dispatcher::BeginWriteData(UserPointer<void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - MojoWriteDataFlags flags) { - base::AutoLock locker(lock_); - if (is_closed_) - return MOJO_RESULT_INVALID_ARGUMENT; - - return BeginWriteDataImplNoLock(buffer, buffer_num_bytes, flags); -} - -MojoResult Dispatcher::EndWriteData(uint32_t num_bytes_written) { - base::AutoLock locker(lock_); - if (is_closed_) - return MOJO_RESULT_INVALID_ARGUMENT; - - return EndWriteDataImplNoLock(num_bytes_written); -} - -MojoResult Dispatcher::ReadData(UserPointer<void> elements, - UserPointer<uint32_t> num_bytes, - MojoReadDataFlags flags) { - base::AutoLock locker(lock_); - if (is_closed_) - return MOJO_RESULT_INVALID_ARGUMENT; - - return ReadDataImplNoLock(elements, num_bytes, flags); -} - -MojoResult Dispatcher::BeginReadData(UserPointer<const void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - MojoReadDataFlags flags) { - base::AutoLock locker(lock_); - if (is_closed_) - return MOJO_RESULT_INVALID_ARGUMENT; - - return BeginReadDataImplNoLock(buffer, buffer_num_bytes, flags); -} - -MojoResult Dispatcher::EndReadData(uint32_t num_bytes_read) { - base::AutoLock locker(lock_); - if (is_closed_) - return MOJO_RESULT_INVALID_ARGUMENT; - - return EndReadDataImplNoLock(num_bytes_read); -} - -MojoResult Dispatcher::DuplicateBufferHandle( - UserPointer<const MojoDuplicateBufferHandleOptions> options, - scoped_refptr<Dispatcher>* new_dispatcher) { - base::AutoLock locker(lock_); - if (is_closed_) - return MOJO_RESULT_INVALID_ARGUMENT; - - return DuplicateBufferHandleImplNoLock(options, new_dispatcher); -} - -MojoResult Dispatcher::MapBuffer( - uint64_t offset, - uint64_t num_bytes, - MojoMapBufferFlags flags, - scoped_ptr<embedder::PlatformSharedBufferMapping>* mapping) { - base::AutoLock locker(lock_); - if (is_closed_) - return MOJO_RESULT_INVALID_ARGUMENT; - - return MapBufferImplNoLock(offset, num_bytes, flags, mapping); -} - -HandleSignalsState Dispatcher::GetHandleSignalsState() const { - base::AutoLock locker(lock_); - if (is_closed_) - return HandleSignalsState(); - - return GetHandleSignalsStateImplNoLock(); -} - -MojoResult Dispatcher::AddAwakable(Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state) { - base::AutoLock locker(lock_); - if (is_closed_) { - if (signals_state) - *signals_state = HandleSignalsState(); - return MOJO_RESULT_INVALID_ARGUMENT; - } - - return AddAwakableImplNoLock(awakable, signals, context, signals_state); -} - -void Dispatcher::RemoveAwakable(Awakable* awakable, - HandleSignalsState* handle_signals_state) { - base::AutoLock locker(lock_); - if (is_closed_) { - if (handle_signals_state) - *handle_signals_state = HandleSignalsState(); - return; - } - - RemoveAwakableImplNoLock(awakable, handle_signals_state); -} - -Dispatcher::Dispatcher() : is_closed_(false) { -} - -Dispatcher::~Dispatcher() { - // Make sure that |Close()| was called. - DCHECK(is_closed_); -} - -void Dispatcher::CancelAllAwakablesNoLock() { - lock_.AssertAcquired(); - DCHECK(is_closed_); - // By default, waiting isn't supported. Only dispatchers that can be waited on - // will do something nontrivial. -} - -void Dispatcher::CloseImplNoLock() { - lock_.AssertAcquired(); - DCHECK(is_closed_); - // This may not need to do anything. Dispatchers should override this to do - // any actual close-time cleanup necessary. -} - -MojoResult Dispatcher::WriteMessageImplNoLock( - UserPointer<const void> /*bytes*/, - uint32_t /*num_bytes*/, - std::vector<DispatcherTransport>* /*transports*/, - MojoWriteMessageFlags /*flags*/) { - lock_.AssertAcquired(); - DCHECK(!is_closed_); - // By default, not supported. Only needed for message pipe dispatchers. - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::ReadMessageImplNoLock( - UserPointer<void> /*bytes*/, - UserPointer<uint32_t> /*num_bytes*/, - DispatcherVector* /*dispatchers*/, - uint32_t* /*num_dispatchers*/, - MojoReadMessageFlags /*flags*/) { - lock_.AssertAcquired(); - DCHECK(!is_closed_); - // By default, not supported. Only needed for message pipe dispatchers. - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::WriteDataImplNoLock(UserPointer<const void> /*elements*/, - UserPointer<uint32_t> /*num_bytes*/, - MojoWriteDataFlags /*flags*/) { - lock_.AssertAcquired(); - DCHECK(!is_closed_); - // By default, not supported. Only needed for data pipe dispatchers. - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::BeginWriteDataImplNoLock( - UserPointer<void*> /*buffer*/, - UserPointer<uint32_t> /*buffer_num_bytes*/, - MojoWriteDataFlags /*flags*/) { - lock_.AssertAcquired(); - DCHECK(!is_closed_); - // By default, not supported. Only needed for data pipe dispatchers. - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::EndWriteDataImplNoLock(uint32_t /*num_bytes_written*/) { - lock_.AssertAcquired(); - DCHECK(!is_closed_); - // By default, not supported. Only needed for data pipe dispatchers. - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::ReadDataImplNoLock(UserPointer<void> /*elements*/, - UserPointer<uint32_t> /*num_bytes*/, - MojoReadDataFlags /*flags*/) { - lock_.AssertAcquired(); - DCHECK(!is_closed_); - // By default, not supported. Only needed for data pipe dispatchers. - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::BeginReadDataImplNoLock( - UserPointer<const void*> /*buffer*/, - UserPointer<uint32_t> /*buffer_num_bytes*/, - MojoReadDataFlags /*flags*/) { - lock_.AssertAcquired(); - DCHECK(!is_closed_); - // By default, not supported. Only needed for data pipe dispatchers. - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::EndReadDataImplNoLock(uint32_t /*num_bytes_read*/) { - lock_.AssertAcquired(); - DCHECK(!is_closed_); - // By default, not supported. Only needed for data pipe dispatchers. - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::DuplicateBufferHandleImplNoLock( - UserPointer<const MojoDuplicateBufferHandleOptions> /*options*/, - scoped_refptr<Dispatcher>* /*new_dispatcher*/) { - lock_.AssertAcquired(); - DCHECK(!is_closed_); - // By default, not supported. Only needed for buffer dispatchers. - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::MapBufferImplNoLock( - uint64_t /*offset*/, - uint64_t /*num_bytes*/, - MojoMapBufferFlags /*flags*/, - scoped_ptr<embedder::PlatformSharedBufferMapping>* /*mapping*/) { - lock_.AssertAcquired(); - DCHECK(!is_closed_); - // By default, not supported. Only needed for buffer dispatchers. - return MOJO_RESULT_INVALID_ARGUMENT; -} - -HandleSignalsState Dispatcher::GetHandleSignalsStateImplNoLock() const { - lock_.AssertAcquired(); - DCHECK(!is_closed_); - // By default, waiting isn't supported. Only dispatchers that can be waited on - // will do something nontrivial. - return HandleSignalsState(); -} - -MojoResult Dispatcher::AddAwakableImplNoLock( - Awakable* /*awakable*/, - MojoHandleSignals /*signals*/, - uint32_t /*context*/, - HandleSignalsState* signals_state) { - lock_.AssertAcquired(); - DCHECK(!is_closed_); - // By default, waiting isn't supported. Only dispatchers that can be waited on - // will do something nontrivial. - if (signals_state) - *signals_state = HandleSignalsState(); - return MOJO_RESULT_FAILED_PRECONDITION; -} - -void Dispatcher::RemoveAwakableImplNoLock(Awakable* /*awakable*/, - HandleSignalsState* signals_state) { - lock_.AssertAcquired(); - DCHECK(!is_closed_); - // By default, waiting isn't supported. Only dispatchers that can be waited on - // will do something nontrivial. - if (signals_state) - *signals_state = HandleSignalsState(); -} - -void Dispatcher::StartSerializeImplNoLock(Channel* /*channel*/, - size_t* max_size, - size_t* max_platform_handles) { - DCHECK(HasOneRef()); // Only one ref => no need to take the lock. - DCHECK(!is_closed_); - *max_size = 0; - *max_platform_handles = 0; -} - -bool Dispatcher::EndSerializeAndCloseImplNoLock( - Channel* /*channel*/, - void* /*destination*/, - size_t* /*actual_size*/, - embedder::PlatformHandleVector* /*platform_handles*/) { - DCHECK(HasOneRef()); // Only one ref => no need to take the lock. - DCHECK(is_closed_); - // By default, serializing isn't supported, so just close. - CloseImplNoLock(); - return false; -} - -bool Dispatcher::IsBusyNoLock() const { - lock_.AssertAcquired(); - DCHECK(!is_closed_); - // Most dispatchers support only "atomic" operations, so they are never busy - // (in this sense). - return false; -} - -void Dispatcher::CloseNoLock() { - lock_.AssertAcquired(); - DCHECK(!is_closed_); - - is_closed_ = true; - CancelAllAwakablesNoLock(); - CloseImplNoLock(); -} - -scoped_refptr<Dispatcher> -Dispatcher::CreateEquivalentDispatcherAndCloseNoLock() { - lock_.AssertAcquired(); - DCHECK(!is_closed_); - - is_closed_ = true; - CancelAllAwakablesNoLock(); - return CreateEquivalentDispatcherAndCloseImplNoLock(); -} - -void Dispatcher::StartSerialize(Channel* channel, - size_t* max_size, - size_t* max_platform_handles) { - DCHECK(channel); - DCHECK(max_size); - DCHECK(max_platform_handles); - DCHECK(HasOneRef()); // Only one ref => no need to take the lock. - DCHECK(!is_closed_); - StartSerializeImplNoLock(channel, max_size, max_platform_handles); -} - -bool Dispatcher::EndSerializeAndClose( - Channel* channel, - void* destination, - size_t* actual_size, - embedder::PlatformHandleVector* platform_handles) { - DCHECK(channel); - DCHECK(actual_size); - DCHECK(HasOneRef()); // Only one ref => no need to take the lock. - DCHECK(!is_closed_); - - // Like other |...Close()| methods, we mark ourselves as closed before calling - // the impl. But there's no need to cancel waiters: we shouldn't have any (and - // shouldn't be in |Core|'s handle table. - is_closed_ = true; - -#if !defined(NDEBUG) - // See the comment above |EndSerializeAndCloseImplNoLock()|. In brief: Locking - // isn't actually needed, but we need to satisfy assertions (which we don't - // want to remove or weaken). - base::AutoLock locker(lock_); -#endif - - return EndSerializeAndCloseImplNoLock(channel, destination, actual_size, - platform_handles); -} - -// DispatcherTransport --------------------------------------------------------- - -void DispatcherTransport::End() { - DCHECK(dispatcher_); - dispatcher_->lock_.Release(); - dispatcher_ = nullptr; -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/dispatcher.h b/mojo/edk/system/dispatcher.h deleted file mode 100644 index c069269..0000000 --- a/mojo/edk/system/dispatcher.h +++ /dev/null @@ -1,405 +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_EDK_SYSTEM_DISPATCHER_H_ -#define MOJO_EDK_SYSTEM_DISPATCHER_H_ - -#include <stddef.h> -#include <stdint.h> - -#include <vector> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/embedder/platform_handle_vector.h" -#include "mojo/edk/system/handle_signals_state.h" -#include "mojo/edk/system/memory.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/buffer.h" -#include "mojo/public/c/system/data_pipe.h" -#include "mojo/public/c/system/message_pipe.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { - -namespace embedder { -class PlatformSharedBufferMapping; -} - -namespace system { - -class Channel; -class Core; -class Dispatcher; -class DispatcherTransport; -class HandleTable; -class LocalMessagePipeEndpoint; -class ProxyMessagePipeEndpoint; -class TransportData; -class Awakable; - -typedef std::vector<scoped_refptr<Dispatcher>> DispatcherVector; - -namespace test { - -// Test helper. We need to declare it here so we can friend it. -MOJO_SYSTEM_IMPL_EXPORT DispatcherTransport -DispatcherTryStartTransport(Dispatcher* dispatcher); - -} // namespace test - -// A |Dispatcher| implements Mojo primitives that are "attached" to a particular -// handle. This includes most (all?) primitives except for |MojoWait...()|. This -// object is thread-safe, with its state being protected by a single lock -// |lock_|, which is also made available to implementation subclasses (via the -// |lock()| method). -class MOJO_SYSTEM_IMPL_EXPORT Dispatcher - : public base::RefCountedThreadSafe<Dispatcher> { - public: - enum Type { - kTypeUnknown = 0, - kTypeMessagePipe, - kTypeDataPipeProducer, - kTypeDataPipeConsumer, - kTypeSharedBuffer, - - // "Private" types (not exposed via the public interface): - kTypePlatformHandle = -1 - }; - virtual Type GetType() const = 0; - - // These methods implement the various primitives named |Mojo...()|. These - // take |lock_| and handle races with |Close()|. Then they call out to - // subclasses' |...ImplNoLock()| methods (still under |lock_|), which actually - // implement the primitives. - // NOTE(vtl): This puts a big lock around each dispatcher (i.e., handle), and - // prevents the various |...ImplNoLock()|s from releasing the lock as soon as - // possible. If this becomes an issue, we can rethink this. - MojoResult Close(); - - // |transports| may be non-null if and only if there are handles to be - // written; not that |this| must not be in |transports|. On success, all the - // dispatchers in |transports| must have been moved to a closed state; on - // failure, they should remain in their original state. - MojoResult WriteMessage(UserPointer<const void> bytes, - uint32_t num_bytes, - std::vector<DispatcherTransport>* transports, - MojoWriteMessageFlags flags); - // |dispatchers| must be non-null but empty, if |num_dispatchers| is non-null - // and nonzero. On success, it will be set to the dispatchers to be received - // (and assigned handles) as part of the message. - MojoResult ReadMessage(UserPointer<void> bytes, - UserPointer<uint32_t> num_bytes, - DispatcherVector* dispatchers, - uint32_t* num_dispatchers, - MojoReadMessageFlags flags); - MojoResult WriteData(UserPointer<const void> elements, - UserPointer<uint32_t> elements_num_bytes, - MojoWriteDataFlags flags); - MojoResult BeginWriteData(UserPointer<void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - MojoWriteDataFlags flags); - MojoResult EndWriteData(uint32_t num_bytes_written); - MojoResult ReadData(UserPointer<void> elements, - UserPointer<uint32_t> num_bytes, - MojoReadDataFlags flags); - MojoResult BeginReadData(UserPointer<const void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - MojoReadDataFlags flags); - MojoResult EndReadData(uint32_t num_bytes_read); - // |options| may be null. |new_dispatcher| must not be null, but - // |*new_dispatcher| should be null (and will contain the dispatcher for the - // new handle on success). - MojoResult DuplicateBufferHandle( - UserPointer<const MojoDuplicateBufferHandleOptions> options, - scoped_refptr<Dispatcher>* new_dispatcher); - MojoResult MapBuffer( - uint64_t offset, - uint64_t num_bytes, - MojoMapBufferFlags flags, - scoped_ptr<embedder::PlatformSharedBufferMapping>* mapping); - - // Gets the current handle signals state. (The default implementation simply - // returns a default-constructed |HandleSignalsState|, i.e., no signals - // satisfied or satisfiable.) Note: The state is subject to change from other - // threads. - HandleSignalsState GetHandleSignalsState() const; - - // Adds an awakable to this dispatcher, which will be woken up when this - // object changes state to satisfy |signals| with context |context|. It will - // also be woken up when it becomes impossible for the object to ever satisfy - // |signals| with a suitable error status. - // - // If |signals_state| is non-null, on *failure* |*signals_state| will be set - // to the current handle signals state (on success, it is left untouched). - // - // Returns: - // - |MOJO_RESULT_OK| if the awakable was added; - // - |MOJO_RESULT_ALREADY_EXISTS| if |signals| is already satisfied; - // - |MOJO_RESULT_INVALID_ARGUMENT| if the dispatcher has been closed; and - // - |MOJO_RESULT_FAILED_PRECONDITION| if it is not (or no longer) possible - // that |signals| will ever be satisfied. - MojoResult AddAwakable(Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state); - // Removes an awakable from this dispatcher. (It is valid to call this - // multiple times for the same |awakable| on the same object, so long as - // |AddAwakable()| was called at most once.) If |signals_state| is non-null, - // |*signals_state| will be set to the current handle signals state. - void RemoveAwakable(Awakable* awakable, HandleSignalsState* signals_state); - - // A dispatcher must be put into a special state in order to be sent across a - // message pipe. Outside of tests, only |HandleTableAccess| is allowed to do - // this, since there are requirements on the handle table (see below). - // - // In this special state, only a restricted set of operations is allowed. - // These are the ones available as |DispatcherTransport| methods. Other - // |Dispatcher| methods must not be called until |DispatcherTransport::End()| - // has been called. - class HandleTableAccess { - private: - friend class Core; - friend class HandleTable; - // 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 - // table entry is not marked busy. The caller must maintain a reference to - // |dispatcher| until |DispatcherTransport::End()| is called. - static DispatcherTransport TryStartTransport(Dispatcher* dispatcher); - }; - - // A |TransportData| may serialize dispatchers that are given to it (and which - // were previously attached to the |MessageInTransit| that is creating it) to - // a given |Channel| and then (probably in a different process) deserialize. - // Note that the |MessageInTransit| "owns" (i.e., has the only ref to) these - // dispatchers, so there are no locking issues. (There's no lock ordering - // issue, and in fact no need to take dispatcher locks at all.) - // TODO(vtl): Consider making another wrapper similar to |DispatcherTransport| - // (but with an owning, unique reference), and having - // |CreateEquivalentDispatcherAndCloseImplNoLock()| return that wrapper (and - // |MessageInTransit|, etc. only holding on to such wrappers). - class TransportDataAccess { - private: - friend class TransportData; - - // Serialization API. These functions may only be called on such - // dispatchers. (|channel| is the |Channel| to which the dispatcher is to be - // serialized.) See the |Dispatcher| methods of the same names for more - // details. - static void StartSerialize(Dispatcher* dispatcher, - Channel* channel, - size_t* max_size, - size_t* max_platform_handles); - static bool EndSerializeAndClose( - Dispatcher* dispatcher, - Channel* channel, - void* destination, - size_t* actual_size, - embedder::PlatformHandleVector* platform_handles); - - // Deserialization API. - // Note: This "clears" (i.e., reset to the invalid handle) any platform - // handles that it takes ownership of. - static scoped_refptr<Dispatcher> Deserialize( - Channel* channel, - int32_t type, - const void* source, - size_t size, - embedder::PlatformHandleVector* platform_handles); - }; - - protected: - friend class base::RefCountedThreadSafe<Dispatcher>; - - Dispatcher(); - virtual ~Dispatcher(); - - // These are to be overridden by subclasses (if necessary). They are called - // exactly once -- first |CancelAllAwakablesNoLock()|, then - // |CloseImplNoLock()|, - // when the dispatcher is being closed. They are called under |lock_|. - virtual void CancelAllAwakablesNoLock(); - virtual void CloseImplNoLock(); - virtual scoped_refptr<Dispatcher> - CreateEquivalentDispatcherAndCloseImplNoLock() = 0; - - // These are to be overridden by subclasses (if necessary). They are never - // called after the dispatcher has been closed. They are called under |lock_|. - // See the descriptions of the methods without the "ImplNoLock" for more - // information. - virtual MojoResult WriteMessageImplNoLock( - UserPointer<const void> bytes, - uint32_t num_bytes, - std::vector<DispatcherTransport>* transports, - MojoWriteMessageFlags flags); - virtual MojoResult ReadMessageImplNoLock(UserPointer<void> bytes, - UserPointer<uint32_t> num_bytes, - DispatcherVector* dispatchers, - uint32_t* num_dispatchers, - MojoReadMessageFlags flags); - virtual MojoResult WriteDataImplNoLock(UserPointer<const void> elements, - UserPointer<uint32_t> num_bytes, - MojoWriteDataFlags flags); - virtual MojoResult BeginWriteDataImplNoLock( - UserPointer<void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - MojoWriteDataFlags flags); - virtual MojoResult EndWriteDataImplNoLock(uint32_t num_bytes_written); - virtual MojoResult ReadDataImplNoLock(UserPointer<void> elements, - UserPointer<uint32_t> num_bytes, - MojoReadDataFlags flags); - virtual MojoResult BeginReadDataImplNoLock( - UserPointer<const void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - MojoReadDataFlags flags); - virtual MojoResult EndReadDataImplNoLock(uint32_t num_bytes_read); - virtual MojoResult DuplicateBufferHandleImplNoLock( - UserPointer<const MojoDuplicateBufferHandleOptions> options, - scoped_refptr<Dispatcher>* new_dispatcher); - virtual MojoResult MapBufferImplNoLock( - uint64_t offset, - uint64_t num_bytes, - MojoMapBufferFlags flags, - scoped_ptr<embedder::PlatformSharedBufferMapping>* mapping); - virtual HandleSignalsState GetHandleSignalsStateImplNoLock() const; - virtual MojoResult AddAwakableImplNoLock(Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state); - virtual void RemoveAwakableImplNoLock(Awakable* awakable, - HandleSignalsState* signals_state); - - // These implement the API used to serialize dispatchers to a |Channel| - // (described below). They will only be called on a dispatcher that's attached - // to and "owned" by a |MessageInTransit|. See the non-"impl" versions for - // more information. - // - // Note: |StartSerializeImplNoLock()| is actually called with |lock_| NOT - // held, since the dispatcher should only be accessible to the calling thread. - // On Debug builds, |EndSerializeAndCloseImplNoLock()| is called with |lock_| - // held, to satisfy any |lock_.AssertAcquired()| (e.g., in |CloseImplNoLock()| - // -- and anything it calls); disentangling those assertions is - // difficult/fragile, and would weaken our general checking of invariants. - // - // TODO(vtl): Consider making these pure virtual once most things support - // being passed over a message pipe. - virtual void StartSerializeImplNoLock(Channel* channel, - size_t* max_size, - size_t* max_platform_handles); - virtual bool EndSerializeAndCloseImplNoLock( - Channel* channel, - void* destination, - size_t* actual_size, - embedder::PlatformHandleVector* platform_handles); - - // Available to subclasses. (Note: Returns a non-const reference, just like - // |base::AutoLock|'s constructor takes a non-const reference.) - base::Lock& lock() const { return lock_; } - - private: - friend class DispatcherTransport; - - // This should be overridden to return true if/when there's an ongoing - // operation (e.g., two-phase read/writes on data pipes) that should prevent a - // handle from being sent over a message pipe (with status "busy"). - virtual bool IsBusyNoLock() const; - - // Closes the dispatcher. This must be done under lock, and unlike |Close()|, - // the dispatcher must not be closed already. (This is the "equivalent" of - // |CreateEquivalentDispatcherAndCloseNoLock()|, for situations where the - // dispatcher must be disposed of instead of "transferred".) - void CloseNoLock(); - - // Creates an equivalent dispatcher -- representing the same resource as this - // dispatcher -- and close (i.e., disable) this dispatcher. I.e., this - // dispatcher will look as though it was closed, but the resource it - // represents will be assigned to the new dispatcher. This must be called - // under the dispatcher's lock. - scoped_refptr<Dispatcher> CreateEquivalentDispatcherAndCloseNoLock(); - - // API to serialize dispatchers to a |Channel|, exposed to only - // |TransportData| (via |TransportData|). They may only be called on a - // dispatcher attached to a |MessageInTransit| (and in particular not in - // |CoreImpl|'s handle table). - // - // Starts the serialization. Returns (via the two "out" parameters) the - // maximum amount of space that may be needed to serialize this dispatcher to - // the given |Channel| (no more than - // |TransportData::kMaxSerializedDispatcherSize|) and the maximum number of - // |PlatformHandle|s that may need to be attached (no more than - // |TransportData::kMaxSerializedDispatcherPlatformHandles|). If this - // dispatcher cannot be serialized to the given |Channel|, |*max_size| and - // |*max_platform_handles| should be set to zero. A call to this method will - // ALWAYS be followed by a call to |EndSerializeAndClose()| (even if this - // dispatcher cannot be serialized to the given |Channel|). - void StartSerialize(Channel* channel, - size_t* max_size, - size_t* max_platform_handles); - // Completes the serialization of this dispatcher to the given |Channel| and - // closes it. (This call will always follow an earlier call to - // |StartSerialize()|, with the same |Channel|.) This does so by writing to - // |destination| and appending any |PlatformHandle|s needed to - // |platform_handles| (which may be null if no platform handles were indicated - // to be required to |StartSerialize()|). This may write no more than the - // amount indicated by |StartSerialize()|. (WARNING: Beware of races, e.g., if - // something can be mutated between the two calls!) Returns true on success, - // in which case |*actual_size| is set to the amount it actually wrote to - // |destination|. On failure, |*actual_size| should not be modified; however, - // the dispatcher will still be closed. - bool EndSerializeAndClose(Channel* channel, - void* destination, - size_t* actual_size, - embedder::PlatformHandleVector* platform_handles); - - // This protects the following members as well as any state added by - // subclasses. - mutable base::Lock lock_; - bool is_closed_; - - DISALLOW_COPY_AND_ASSIGN(Dispatcher); -}; - -// Wrapper around a |Dispatcher| pointer, while it's being processed to be -// passed in a message pipe. See the comment about -// |Dispatcher::HandleTableAccess| for more details. -// -// Note: This class is deliberately "thin" -- no more expensive than a -// |Dispatcher*|. -class MOJO_SYSTEM_IMPL_EXPORT DispatcherTransport { - public: - DispatcherTransport() : dispatcher_(nullptr) {} - - void End(); - - Dispatcher::Type GetType() const { return dispatcher_->GetType(); } - bool IsBusy() const { return dispatcher_->IsBusyNoLock(); } - void Close() { dispatcher_->CloseNoLock(); } - scoped_refptr<Dispatcher> CreateEquivalentDispatcherAndClose() { - return dispatcher_->CreateEquivalentDispatcherAndCloseNoLock(); - } - - bool is_valid() const { return !!dispatcher_; } - - protected: - Dispatcher* dispatcher() { return dispatcher_; } - - private: - friend class Dispatcher::HandleTableAccess; - - explicit DispatcherTransport(Dispatcher* dispatcher) - : dispatcher_(dispatcher) {} - - Dispatcher* dispatcher_; - - // Copy and assign allowed. -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_DISPATCHER_H_ diff --git a/mojo/edk/system/dispatcher_unittest.cc b/mojo/edk/system/dispatcher_unittest.cc deleted file mode 100644 index 97e2eed..0000000 --- a/mojo/edk/system/dispatcher_unittest.cc +++ /dev/null @@ -1,307 +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/edk/system/dispatcher.h" - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_vector.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/simple_thread.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/system/memory.h" -#include "mojo/edk/system/waiter.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -// Trivial subclass that makes the constructor public. -class TrivialDispatcher : public Dispatcher { - public: - TrivialDispatcher() {} - - Type GetType() const override { return kTypeUnknown; } - - private: - friend class base::RefCountedThreadSafe<TrivialDispatcher>; - ~TrivialDispatcher() override {} - - scoped_refptr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock() - override { - lock().AssertAcquired(); - return scoped_refptr<Dispatcher>(new TrivialDispatcher()); - } - - DISALLOW_COPY_AND_ASSIGN(TrivialDispatcher); -}; - -TEST(DispatcherTest, Basic) { - scoped_refptr<Dispatcher> d(new TrivialDispatcher()); - - EXPECT_EQ(Dispatcher::kTypeUnknown, d->GetType()); - - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - d->WriteMessage(NullUserPointer(), 0, nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - d->ReadMessage(NullUserPointer(), NullUserPointer(), nullptr, - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - d->WriteData(NullUserPointer(), NullUserPointer(), - MOJO_WRITE_DATA_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - d->BeginWriteData(NullUserPointer(), NullUserPointer(), - MOJO_WRITE_DATA_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, d->EndWriteData(0)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - d->ReadData(NullUserPointer(), NullUserPointer(), - MOJO_READ_DATA_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - d->BeginReadData(NullUserPointer(), NullUserPointer(), - MOJO_READ_DATA_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, d->EndReadData(0)); - Waiter w; - w.Init(); - HandleSignalsState hss; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - d->AddAwakable(&w, ~MOJO_HANDLE_SIGNAL_NONE, 0, &hss)); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(0u, hss.satisfiable_signals); - // Okay to remove even if it wasn't added (or was already removed). - hss = HandleSignalsState(); - d->RemoveAwakable(&w, &hss); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(0u, hss.satisfiable_signals); - hss = HandleSignalsState(); - d->RemoveAwakable(&w, &hss); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(0u, hss.satisfiable_signals); - - EXPECT_EQ(MOJO_RESULT_OK, d->Close()); - - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - d->WriteMessage(NullUserPointer(), 0, nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - d->ReadMessage(NullUserPointer(), NullUserPointer(), nullptr, - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - d->WriteData(NullUserPointer(), NullUserPointer(), - MOJO_WRITE_DATA_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - d->BeginWriteData(NullUserPointer(), NullUserPointer(), - MOJO_WRITE_DATA_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, d->EndWriteData(0)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - d->ReadData(NullUserPointer(), NullUserPointer(), - MOJO_READ_DATA_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - d->BeginReadData(NullUserPointer(), NullUserPointer(), - MOJO_READ_DATA_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, d->EndReadData(0)); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - d->AddAwakable(&w, ~MOJO_HANDLE_SIGNAL_NONE, 0, &hss)); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(0u, hss.satisfiable_signals); - hss = HandleSignalsState(); - d->RemoveAwakable(&w, &hss); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(0u, hss.satisfiable_signals); -} - -class ThreadSafetyStressThread : public base::SimpleThread { - public: - enum DispatcherOp { - CLOSE = 0, - WRITE_MESSAGE, - READ_MESSAGE, - WRITE_DATA, - BEGIN_WRITE_DATA, - END_WRITE_DATA, - READ_DATA, - BEGIN_READ_DATA, - END_READ_DATA, - DUPLICATE_BUFFER_HANDLE, - MAP_BUFFER, - ADD_WAITER, - REMOVE_WAITER, - DISPATCHER_OP_COUNT - }; - - ThreadSafetyStressThread(base::WaitableEvent* event, - scoped_refptr<Dispatcher> dispatcher, - DispatcherOp op) - : base::SimpleThread("thread_safety_stress_thread"), - event_(event), - dispatcher_(dispatcher), - op_(op) { - CHECK_LE(0, op_); - CHECK_LT(op_, DISPATCHER_OP_COUNT); - } - - ~ThreadSafetyStressThread() override { Join(); } - - private: - void Run() override { - event_->Wait(); - - waiter_.Init(); - switch (op_) { - case CLOSE: { - MojoResult r = dispatcher_->Close(); - EXPECT_TRUE(r == MOJO_RESULT_OK || r == MOJO_RESULT_INVALID_ARGUMENT) - << "Result: " << r; - break; - } - case WRITE_MESSAGE: - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - dispatcher_->WriteMessage(NullUserPointer(), 0, nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - break; - case READ_MESSAGE: - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - dispatcher_->ReadMessage(NullUserPointer(), NullUserPointer(), - nullptr, nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - break; - case WRITE_DATA: - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - dispatcher_->WriteData(NullUserPointer(), NullUserPointer(), - MOJO_WRITE_DATA_FLAG_NONE)); - break; - case BEGIN_WRITE_DATA: - EXPECT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - dispatcher_->BeginWriteData(NullUserPointer(), NullUserPointer(), - MOJO_WRITE_DATA_FLAG_NONE)); - break; - case END_WRITE_DATA: - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, dispatcher_->EndWriteData(0)); - break; - case READ_DATA: - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - dispatcher_->ReadData(NullUserPointer(), NullUserPointer(), - MOJO_READ_DATA_FLAG_NONE)); - break; - case BEGIN_READ_DATA: - EXPECT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - dispatcher_->BeginReadData(NullUserPointer(), NullUserPointer(), - MOJO_READ_DATA_FLAG_NONE)); - break; - case END_READ_DATA: - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, dispatcher_->EndReadData(0)); - break; - case DUPLICATE_BUFFER_HANDLE: { - scoped_refptr<Dispatcher> unused; - EXPECT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - dispatcher_->DuplicateBufferHandle(NullUserPointer(), &unused)); - break; - } - case MAP_BUFFER: { - scoped_ptr<embedder::PlatformSharedBufferMapping> unused; - EXPECT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - dispatcher_->MapBuffer(0u, 0u, MOJO_MAP_BUFFER_FLAG_NONE, &unused)); - break; - } - case ADD_WAITER: { - HandleSignalsState hss; - MojoResult r = dispatcher_->AddAwakable( - &waiter_, ~MOJO_HANDLE_SIGNAL_NONE, 0, &hss); - EXPECT_TRUE(r == MOJO_RESULT_FAILED_PRECONDITION || - r == MOJO_RESULT_INVALID_ARGUMENT); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(0u, hss.satisfiable_signals); - break; - } - case REMOVE_WAITER: { - HandleSignalsState hss; - dispatcher_->RemoveAwakable(&waiter_, &hss); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(0u, hss.satisfiable_signals); - break; - } - default: - NOTREACHED(); - break; - } - - // Always try to remove the waiter, in case we added it. - HandleSignalsState hss; - dispatcher_->RemoveAwakable(&waiter_, &hss); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(0u, hss.satisfiable_signals); - } - - base::WaitableEvent* const event_; - const scoped_refptr<Dispatcher> dispatcher_; - const DispatcherOp op_; - - Waiter waiter_; - - DISALLOW_COPY_AND_ASSIGN(ThreadSafetyStressThread); -}; - -TEST(DispatcherTest, ThreadSafetyStress) { - static const size_t kRepeatCount = 20; - static const size_t kNumThreads = 100; - - for (size_t i = 0; i < kRepeatCount; i++) { - // Manual reset, not initially signalled. - base::WaitableEvent event(true, false); - scoped_refptr<Dispatcher> d(new TrivialDispatcher()); - - { - ScopedVector<ThreadSafetyStressThread> threads; - for (size_t j = 0; j < kNumThreads; j++) { - ThreadSafetyStressThread::DispatcherOp op = - static_cast<ThreadSafetyStressThread::DispatcherOp>( - (i + j) % ThreadSafetyStressThread::DISPATCHER_OP_COUNT); - threads.push_back(new ThreadSafetyStressThread(&event, d, op)); - threads.back()->Start(); - } - // Kicks off real work on the threads: - event.Signal(); - } // Joins all the threads. - - // One of the threads should already have closed the dispatcher. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, d->Close()); - } -} - -TEST(DispatcherTest, ThreadSafetyStressNoClose) { - static const size_t kRepeatCount = 20; - static const size_t kNumThreads = 100; - - for (size_t i = 0; i < kRepeatCount; i++) { - // Manual reset, not initially signalled. - base::WaitableEvent event(true, false); - scoped_refptr<Dispatcher> d(new TrivialDispatcher()); - - { - ScopedVector<ThreadSafetyStressThread> threads; - for (size_t j = 0; j < kNumThreads; j++) { - ThreadSafetyStressThread::DispatcherOp op = - static_cast<ThreadSafetyStressThread::DispatcherOp>( - (i + j) % (ThreadSafetyStressThread::DISPATCHER_OP_COUNT - 1) + - 1); - threads.push_back(new ThreadSafetyStressThread(&event, d, op)); - threads.back()->Start(); - } - // Kicks off real work on the threads: - event.Signal(); - } // Joins all the threads. - - EXPECT_EQ(MOJO_RESULT_OK, d->Close()); - } -} - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/endpoint_relayer.cc b/mojo/edk/system/endpoint_relayer.cc deleted file mode 100644 index 52faefa..0000000 --- a/mojo/edk/system/endpoint_relayer.cc +++ /dev/null @@ -1,70 +0,0 @@ -// 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/edk/system/endpoint_relayer.h" - -#include "base/logging.h" -#include "mojo/edk/system/channel_endpoint.h" -#include "mojo/edk/system/message_in_transit.h" - -namespace mojo { -namespace system { - -EndpointRelayer::EndpointRelayer() { -} - -// static -unsigned EndpointRelayer::GetPeerPort(unsigned port) { - DCHECK(port == 0 || port == 1); - return port ^ 1; -} - -void EndpointRelayer::Init(ChannelEndpoint* endpoint0, - ChannelEndpoint* endpoint1) { - DCHECK(endpoint0); - DCHECK(endpoint1); - DCHECK(!endpoints_[0]); - DCHECK(!endpoints_[1]); - endpoints_[0] = endpoint0; - endpoints_[1] = endpoint1; -} - -bool EndpointRelayer::OnReadMessage(unsigned port, MessageInTransit* message) { - DCHECK(message); - - base::AutoLock locker(lock_); - - // If we're no longer the client, then reject the message. - if (!endpoints_[port]) - return false; - - // Otherwise, consume it even if the peer port is closed. - unsigned peer_port = GetPeerPort(port); - if (endpoints_[peer_port]) - endpoints_[peer_port]->EnqueueMessage(make_scoped_ptr(message)); - return true; -} - -void EndpointRelayer::OnDetachFromChannel(unsigned port) { - base::AutoLock locker(lock_); - - if (endpoints_[port]) { - endpoints_[port]->DetachFromClient(); - endpoints_[port] = nullptr; - } - - unsigned peer_port = GetPeerPort(port); - if (endpoints_[peer_port]) { - endpoints_[peer_port]->DetachFromClient(); - endpoints_[peer_port] = nullptr; - } -} - -EndpointRelayer::~EndpointRelayer() { - DCHECK(!endpoints_[0]); - DCHECK(!endpoints_[1]); -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/endpoint_relayer.h b/mojo/edk/system/endpoint_relayer.h deleted file mode 100644 index a0a93a4..0000000 --- a/mojo/edk/system/endpoint_relayer.h +++ /dev/null @@ -1,49 +0,0 @@ -// 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_EDK_SYSTEM_ENDPOINT_RELAYER_H_ -#define MOJO_EDK_SYSTEM_ENDPOINT_RELAYER_H_ - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/system/channel_endpoint_client.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace system { - -class ChannelEndpoint; - -// This is a simple |ChannelEndpointClient| that just relays messages between -// two |ChannelEndpoint|s (without the overhead of |MessagePipe|). -class MOJO_SYSTEM_IMPL_EXPORT EndpointRelayer : public ChannelEndpointClient { - public: - EndpointRelayer(); - - // Gets the other port number (i.e., 0 -> 1, 1 -> 0). - static unsigned GetPeerPort(unsigned port); - - // Initialize this object. This must be called before any other method. - void Init(ChannelEndpoint* endpoint0, ChannelEndpoint* endpoint1); - - // |ChannelEndpointClient| methods: - bool OnReadMessage(unsigned port, MessageInTransit* message) override; - void OnDetachFromChannel(unsigned port) override; - - private: - ~EndpointRelayer() override; - - // TODO(vtl): We could probably get away without the lock if we had a - // thread-safe |scoped_refptr|. - base::Lock lock_; // Protects the following members. - scoped_refptr<ChannelEndpoint> endpoints_[2]; - - DISALLOW_COPY_AND_ASSIGN(EndpointRelayer); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_ENDPOINT_RELAYER_H_ diff --git a/mojo/edk/system/handle_signals_state.h b/mojo/edk/system/handle_signals_state.h deleted file mode 100644 index 3391ea0..0000000 --- a/mojo/edk/system/handle_signals_state.h +++ /dev/null @@ -1,50 +0,0 @@ -// 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_EDK_SYSTEM_HANDLE_SIGNALS_STATE_H_ -#define MOJO_EDK_SYSTEM_HANDLE_SIGNALS_STATE_H_ - -#include "base/macros.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { -namespace system { - -// Just "add" some constructors and methods to the C struct -// |MojoHandleSignalsState| (for convenience). This should add no overhead. -struct MOJO_SYSTEM_IMPL_EXPORT HandleSignalsState - : public MojoHandleSignalsState { - HandleSignalsState() { - satisfied_signals = MOJO_HANDLE_SIGNAL_NONE; - satisfiable_signals = MOJO_HANDLE_SIGNAL_NONE; - } - HandleSignalsState(MojoHandleSignals satisfied, - MojoHandleSignals satisfiable) { - satisfied_signals = satisfied; - satisfiable_signals = satisfiable; - } - - bool equals(const HandleSignalsState& other) const { - return satisfied_signals == other.satisfied_signals && - satisfiable_signals == other.satisfiable_signals; - } - - bool satisfies(MojoHandleSignals signals) const { - return !!(satisfied_signals & signals); - } - - bool can_satisfy(MojoHandleSignals signals) const { - return !!(satisfiable_signals & signals); - } - - // (Copy and assignment allowed.) -}; -static_assert(sizeof(HandleSignalsState) == sizeof(MojoHandleSignalsState), - "HandleSignalsState should add no overhead"); - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_HANDLE_SIGNALS_STATE_H_ diff --git a/mojo/edk/system/handle_table.cc b/mojo/edk/system/handle_table.cc deleted file mode 100644 index e054919..0000000 --- a/mojo/edk/system/handle_table.cc +++ /dev/null @@ -1,243 +0,0 @@ -// 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/edk/system/handle_table.h" - -#include <limits> -#include "base/logging.h" -#include "base/macros.h" -#include "mojo/edk/system/configuration.h" -#include "mojo/edk/system/dispatcher.h" - -namespace mojo { -namespace system { - -HandleTable::Entry::Entry() : busy(false) { -} - -HandleTable::Entry::Entry(const scoped_refptr<Dispatcher>& dispatcher) - : dispatcher(dispatcher), busy(false) { -} - -HandleTable::Entry::~Entry() { - DCHECK(!busy); -} - -HandleTable::HandleTable() : next_handle_(MOJO_HANDLE_INVALID + 1) { -} - -HandleTable::~HandleTable() { - // This should usually not be reached (the only instance should be owned by - // the singleton |Core|, which lives forever), except in tests. -} - -Dispatcher* HandleTable::GetDispatcher(MojoHandle handle) { - DCHECK_NE(handle, MOJO_HANDLE_INVALID); - - HandleToEntryMap::iterator it = handle_to_entry_map_.find(handle); - if (it == handle_to_entry_map_.end()) - return nullptr; - return it->second.dispatcher.get(); -} - -MojoResult HandleTable::GetAndRemoveDispatcher( - MojoHandle handle, - scoped_refptr<Dispatcher>* dispatcher) { - DCHECK_NE(handle, MOJO_HANDLE_INVALID); - DCHECK(dispatcher); - - HandleToEntryMap::iterator it = handle_to_entry_map_.find(handle); - if (it == handle_to_entry_map_.end()) - return MOJO_RESULT_INVALID_ARGUMENT; - if (it->second.busy) - return MOJO_RESULT_BUSY; - *dispatcher = it->second.dispatcher; - handle_to_entry_map_.erase(it); - - return MOJO_RESULT_OK; -} - -MojoHandle HandleTable::AddDispatcher( - const scoped_refptr<Dispatcher>& dispatcher) { - if (handle_to_entry_map_.size() >= GetConfiguration().max_handle_table_size) - return MOJO_HANDLE_INVALID; - return AddDispatcherNoSizeCheck(dispatcher); -} - -std::pair<MojoHandle, MojoHandle> HandleTable::AddDispatcherPair( - const scoped_refptr<Dispatcher>& dispatcher0, - const scoped_refptr<Dispatcher>& dispatcher1) { - if (handle_to_entry_map_.size() + 1 >= - GetConfiguration().max_handle_table_size) - return std::make_pair(MOJO_HANDLE_INVALID, MOJO_HANDLE_INVALID); - return std::make_pair(AddDispatcherNoSizeCheck(dispatcher0), - AddDispatcherNoSizeCheck(dispatcher1)); -} - -bool HandleTable::AddDispatcherVector(const DispatcherVector& dispatchers, - MojoHandle* handles) { - size_t max_message_num_handles = GetConfiguration().max_message_num_handles; - size_t max_handle_table_size = GetConfiguration().max_handle_table_size; - - DCHECK_LE(dispatchers.size(), max_message_num_handles); - DCHECK(handles); - DCHECK_LT( - static_cast<uint64_t>(max_handle_table_size) + max_message_num_handles, - std::numeric_limits<size_t>::max()) - << "Addition may overflow"; - - if (handle_to_entry_map_.size() + dispatchers.size() > max_handle_table_size) - return false; - - for (size_t i = 0; i < dispatchers.size(); i++) { - if (dispatchers[i]) { - handles[i] = AddDispatcherNoSizeCheck(dispatchers[i]); - } else { - LOG(WARNING) << "Invalid dispatcher at index " << i; - handles[i] = MOJO_HANDLE_INVALID; - } - } - return true; -} - -MojoResult HandleTable::MarkBusyAndStartTransport( - MojoHandle disallowed_handle, - const MojoHandle* handles, - uint32_t num_handles, - std::vector<DispatcherTransport>* transports) { - DCHECK_NE(disallowed_handle, MOJO_HANDLE_INVALID); - DCHECK(handles); - DCHECK_LE(num_handles, GetConfiguration().max_message_num_handles); - DCHECK(transports); - DCHECK_EQ(transports->size(), num_handles); - - std::vector<Entry*> entries(num_handles); - - // First verify all the handles and get their dispatchers. - uint32_t i; - MojoResult error_result = MOJO_RESULT_INTERNAL; - for (i = 0; i < num_handles; i++) { - // Sending your own handle is not allowed (and, for consistency, returns - // "busy"). - if (handles[i] == disallowed_handle) { - error_result = MOJO_RESULT_BUSY; - break; - } - - HandleToEntryMap::iterator it = handle_to_entry_map_.find(handles[i]); - if (it == handle_to_entry_map_.end()) { - error_result = MOJO_RESULT_INVALID_ARGUMENT; - break; - } - - entries[i] = &it->second; - if (entries[i]->busy) { - error_result = MOJO_RESULT_BUSY; - break; - } - // Note: By marking the handle as busy here, we're also preventing the - // same handle from being sent multiple times in the same message. - entries[i]->busy = true; - - // Try to start the transport. - DispatcherTransport transport = - Dispatcher::HandleTableAccess::TryStartTransport( - entries[i]->dispatcher.get()); - if (!transport.is_valid()) { - // Only log for Debug builds, since this is not a problem with the system - // code, but with user code. - DLOG(WARNING) << "Likely race condition in user code detected: attempt " - "to transfer handle " << handles[i] - << " while it is in use on a different thread"; - - // Unset the busy flag (since it won't be unset below). - entries[i]->busy = false; - error_result = MOJO_RESULT_BUSY; - break; - } - - // Check if the dispatcher is busy (e.g., in a two-phase read/write). - // (Note that this must be done after the dispatcher's lock is acquired.) - if (transport.IsBusy()) { - // Unset the busy flag and end the transport (since it won't be done - // below). - entries[i]->busy = false; - transport.End(); - error_result = MOJO_RESULT_BUSY; - break; - } - - // Hang on to the transport (which we'll need to end the transport). - (*transports)[i] = transport; - } - if (i < num_handles) { - DCHECK_NE(error_result, MOJO_RESULT_INTERNAL); - - // Unset the busy flags and release the locks. - for (uint32_t j = 0; j < i; j++) { - DCHECK(entries[j]->busy); - entries[j]->busy = false; - (*transports)[j].End(); - } - return error_result; - } - - return MOJO_RESULT_OK; -} - -MojoHandle HandleTable::AddDispatcherNoSizeCheck( - const scoped_refptr<Dispatcher>& dispatcher) { - DCHECK(dispatcher); - DCHECK_LT(handle_to_entry_map_.size(), - GetConfiguration().max_handle_table_size); - DCHECK_NE(next_handle_, MOJO_HANDLE_INVALID); - - // TODO(vtl): Maybe we want to do something different/smarter. (Or maybe try - // assigning randomly?) - while (handle_to_entry_map_.find(next_handle_) != - handle_to_entry_map_.end()) { - next_handle_++; - if (next_handle_ == MOJO_HANDLE_INVALID) - next_handle_++; - } - - MojoHandle new_handle = next_handle_; - handle_to_entry_map_[new_handle] = Entry(dispatcher); - - next_handle_++; - if (next_handle_ == MOJO_HANDLE_INVALID) - next_handle_++; - - return new_handle; -} - -void HandleTable::RemoveBusyHandles(const MojoHandle* handles, - uint32_t num_handles) { - DCHECK(handles); - DCHECK_LE(num_handles, GetConfiguration().max_message_num_handles); - - for (uint32_t i = 0; i < num_handles; i++) { - HandleToEntryMap::iterator it = handle_to_entry_map_.find(handles[i]); - DCHECK(it != handle_to_entry_map_.end()); - DCHECK(it->second.busy); - it->second.busy = false; // For the sake of a |DCHECK()|. - handle_to_entry_map_.erase(it); - } -} - -void HandleTable::RestoreBusyHandles(const MojoHandle* handles, - uint32_t num_handles) { - DCHECK(handles); - DCHECK_LE(num_handles, GetConfiguration().max_message_num_handles); - - for (uint32_t i = 0; i < num_handles; i++) { - HandleToEntryMap::iterator it = handle_to_entry_map_.find(handles[i]); - DCHECK(it != handle_to_entry_map_.end()); - DCHECK(it->second.busy); - it->second.busy = false; - } -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/handle_table.h b/mojo/edk/system/handle_table.h deleted file mode 100644 index 9385643..0000000 --- a/mojo/edk/system/handle_table.h +++ /dev/null @@ -1,144 +0,0 @@ -// 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_EDK_SYSTEM_HANDLE_TABLE_H_ -#define MOJO_EDK_SYSTEM_HANDLE_TABLE_H_ - -#include <utility> -#include <vector> - -#include "base/containers/hash_tables.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { -namespace system { - -class Core; -class Dispatcher; -class DispatcherTransport; - -typedef std::vector<scoped_refptr<Dispatcher>> DispatcherVector; - -// Test-only function (defined/used in embedder/test_embedder.cc). Declared here -// so it can be friended. -namespace internal { -bool ShutdownCheckNoLeaks(Core*); -} - -// 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 |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: - HandleTable(); - ~HandleTable(); - - // Gets the dispatcher for a given handle (which should not be - // |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 |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 - // |MOJO_HANDLE_INVALID|) and removes it. (On failure, returns an appropriate - // result (and leaves |dispatcher| alone), namely - // |MOJO_RESULT_INVALID_ARGUMENT| if there's no dispatcher for the given - // handle or |MOJO_RESULT_BUSY| if the handle is marked as busy.) - MojoResult GetAndRemoveDispatcher(MojoHandle handle, - scoped_refptr<Dispatcher>* dispatcher); - - // Adds a dispatcher (which must be valid), returning the handle for it. - // Returns |MOJO_HANDLE_INVALID| on failure (if the handle table is full). - MojoHandle AddDispatcher(const scoped_refptr<Dispatcher>& dispatcher); - - // Adds a pair of dispatchers (which must be valid), return a pair of handles - // for them. On failure (if the handle table is full), the first (and second) - // handles will be |MOJO_HANDLE_INVALID|, and neither dispatcher will be - // added. - std::pair<MojoHandle, MojoHandle> AddDispatcherPair( - const scoped_refptr<Dispatcher>& dispatcher0, - const scoped_refptr<Dispatcher>& dispatcher1); - - // Adds the given vector of dispatchers (of size at most - // |kMaxMessageNumHandles|). |handles| must point to an array of size at least - // |dispatchers.size()|. Unlike the other |AddDispatcher...()| functions, some - // of the dispatchers may be invalid (null). Returns true on success and false - // on failure (if the handle table is full), in which case it leaves - // |handles[...]| untouched (and all dispatchers unadded). - bool AddDispatcherVector(const DispatcherVector& dispatchers, - MojoHandle* handles); - - // Tries to mark the given handles as busy and start transport on them (i.e., - // take their dispatcher locks); |transports| must be sized to contain - // |num_handles| elements. On failure, returns them to their original - // (non-busy, unlocked state). - MojoResult MarkBusyAndStartTransport( - MojoHandle disallowed_handle, - const MojoHandle* handles, - uint32_t num_handles, - std::vector<DispatcherTransport>* transports); - - // Remove the given handles, which must all be present and which should have - // previously been marked busy by |MarkBusyAndStartTransport()|. - void RemoveBusyHandles(const MojoHandle* handles, uint32_t num_handles); - - // Restores the given handles, which must all be present and which should have - // previously been marked busy by |MarkBusyAndStartTransport()|, to a non-busy - // state. - void RestoreBusyHandles(const MojoHandle* handles, uint32_t num_handles); - - private: - friend bool internal::ShutdownCheckNoLeaks(Core*); - - // The |busy| member is used only to deal with functions (in particular - // |Core::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 |Core::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 |Core::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 |Core::WriteMessage()| from sending a handle that has been - // closed (or learning about this too late). - struct Entry { - Entry(); - explicit Entry(const scoped_refptr<Dispatcher>& dispatcher); - ~Entry(); - - scoped_refptr<Dispatcher> dispatcher; - bool busy; - }; - typedef base::hash_map<MojoHandle, Entry> HandleToEntryMap; - - // Adds the given dispatcher to the handle table, not doing any size checks. - MojoHandle AddDispatcherNoSizeCheck( - const scoped_refptr<Dispatcher>& dispatcher); - - HandleToEntryMap handle_to_entry_map_; - MojoHandle next_handle_; // Invariant: never |MOJO_HANDLE_INVALID|. - - DISALLOW_COPY_AND_ASSIGN(HandleTable); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_HANDLE_TABLE_H_ diff --git a/mojo/edk/system/incoming_endpoint.cc b/mojo/edk/system/incoming_endpoint.cc deleted file mode 100644 index 14f1a71..0000000 --- a/mojo/edk/system/incoming_endpoint.cc +++ /dev/null @@ -1,59 +0,0 @@ -// 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/edk/system/incoming_endpoint.h" - -#include "base/logging.h" -#include "mojo/edk/system/channel_endpoint.h" -#include "mojo/edk/system/message_in_transit.h" -#include "mojo/edk/system/message_pipe.h" - -namespace mojo { -namespace system { - -IncomingEndpoint::IncomingEndpoint() { -} - -scoped_refptr<ChannelEndpoint> IncomingEndpoint::Init() { - endpoint_ = new ChannelEndpoint(this, 0); - return endpoint_; -} - -scoped_refptr<MessagePipe> IncomingEndpoint::ConvertToMessagePipe() { - base::AutoLock locker(lock_); - scoped_refptr<MessagePipe> message_pipe( - MessagePipe::CreateLocalProxyFromExisting(&message_queue_, - endpoint_.get())); - DCHECK(message_queue_.IsEmpty()); - endpoint_ = nullptr; - return message_pipe; -} - -void IncomingEndpoint::Close() { - base::AutoLock locker(lock_); - if (endpoint_) { - endpoint_->DetachFromClient(); - endpoint_ = nullptr; - } -} - -bool IncomingEndpoint::OnReadMessage(unsigned /*port*/, - MessageInTransit* message) { - base::AutoLock locker(lock_); - if (!endpoint_) - return false; - - message_queue_.AddMessage(make_scoped_ptr(message)); - return true; -} - -void IncomingEndpoint::OnDetachFromChannel(unsigned /*port*/) { - Close(); -} - -IncomingEndpoint::~IncomingEndpoint() { -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/incoming_endpoint.h b/mojo/edk/system/incoming_endpoint.h deleted file mode 100644 index 4db7d5a..0000000 --- a/mojo/edk/system/incoming_endpoint.h +++ /dev/null @@ -1,54 +0,0 @@ -// 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_EDK_SYSTEM_INCOMING_ENDPOINT_H_ -#define MOJO_EDK_SYSTEM_INCOMING_ENDPOINT_H_ - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/system/channel_endpoint_client.h" -#include "mojo/edk/system/message_in_transit_queue.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace system { - -class ChannelEndpoint; -class MessagePipe; - -// This is a simple |ChannelEndpointClient| that only receives messages. It's -// used for endpoints that are "received" by |Channel|, but not yet turned into -// |MessagePipe|s. -class MOJO_SYSTEM_IMPL_EXPORT IncomingEndpoint : public ChannelEndpointClient { - public: - IncomingEndpoint(); - - // Must be called before any other method. - scoped_refptr<ChannelEndpoint> Init(); - - scoped_refptr<MessagePipe> ConvertToMessagePipe(); - - // Must be called before destroying this object if |ConvertToMessagePipe()| - // wasn't called (but |Init()| was). - void Close(); - - // |ChannelEndpointClient| methods: - bool OnReadMessage(unsigned port, MessageInTransit* message) override; - void OnDetachFromChannel(unsigned port) override; - - private: - ~IncomingEndpoint() override; - - base::Lock lock_; // Protects the following members. - scoped_refptr<ChannelEndpoint> endpoint_; - MessageInTransitQueue message_queue_; - - DISALLOW_COPY_AND_ASSIGN(IncomingEndpoint); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_INCOMING_ENDPOINT_H_ diff --git a/mojo/edk/system/local_data_pipe.cc b/mojo/edk/system/local_data_pipe.cc deleted file mode 100644 index 8b9f673..0000000 --- a/mojo/edk/system/local_data_pipe.cc +++ /dev/null @@ -1,350 +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. - -// TODO(vtl): I currently potentially overflow in doing index calculations. -// E.g., |start_index_| and |current_num_bytes_| fit into a |uint32_t|, but -// their sum may not. This is bad and poses a security risk. (We're currently -// saved by the limit on capacity -- the maximum size of the buffer, checked in -// |DataPipe::ValidateOptions()|, is currently sufficiently small.) - -#include "mojo/edk/system/local_data_pipe.h" - -#include <string.h> - -#include <algorithm> - -#include "base/logging.h" -#include "mojo/edk/system/configuration.h" - -namespace mojo { -namespace system { - -LocalDataPipe::LocalDataPipe(const MojoCreateDataPipeOptions& options) - : DataPipe(true, true, options), start_index_(0), current_num_bytes_(0) { - // Note: |buffer_| is lazily allocated, since a common case will be that one - // of the handles is immediately passed off to another process. -} - -LocalDataPipe::~LocalDataPipe() { -} - -void LocalDataPipe::ProducerCloseImplNoLock() { - // If the consumer is still open and we still have data, we have to keep the - // buffer around. Currently, we won't free it even if it empties later. (We - // could do this -- requiring a check on every read -- but that seems to be - // optimizing for the uncommon case.) - if (!consumer_open_no_lock() || !current_num_bytes_) { - // Note: There can only be a two-phase *read* (by the consumer) if we still - // have data. - DCHECK(!consumer_in_two_phase_read_no_lock()); - DestroyBufferNoLock(); - } -} - -MojoResult LocalDataPipe::ProducerWriteDataImplNoLock( - UserPointer<const void> elements, - UserPointer<uint32_t> num_bytes, - uint32_t max_num_bytes_to_write, - uint32_t min_num_bytes_to_write) { - DCHECK_EQ(max_num_bytes_to_write % element_num_bytes(), 0u); - DCHECK_EQ(min_num_bytes_to_write % element_num_bytes(), 0u); - DCHECK_GT(max_num_bytes_to_write, 0u); - DCHECK(consumer_open_no_lock()); - - size_t num_bytes_to_write = 0; - if (may_discard()) { - if (min_num_bytes_to_write > capacity_num_bytes()) - return MOJO_RESULT_OUT_OF_RANGE; - - num_bytes_to_write = std::min(static_cast<size_t>(max_num_bytes_to_write), - capacity_num_bytes()); - if (num_bytes_to_write > capacity_num_bytes() - current_num_bytes_) { - // Discard as much as needed (discard oldest first). - MarkDataAsConsumedNoLock(num_bytes_to_write - - (capacity_num_bytes() - current_num_bytes_)); - // No need to wake up write waiters, since we're definitely going to leave - // the buffer full. - } - } else { - if (min_num_bytes_to_write > capacity_num_bytes() - current_num_bytes_) { - // Don't return "should wait" since you can't wait for a specified amount - // of data. - return MOJO_RESULT_OUT_OF_RANGE; - } - - num_bytes_to_write = std::min(static_cast<size_t>(max_num_bytes_to_write), - capacity_num_bytes() - current_num_bytes_); - } - if (num_bytes_to_write == 0) - return MOJO_RESULT_SHOULD_WAIT; - - // The amount we can write in our first |memcpy()|. - size_t num_bytes_to_write_first = - std::min(num_bytes_to_write, GetMaxNumBytesToWriteNoLock()); - // Do the first (and possibly only) |memcpy()|. - size_t first_write_index = - (start_index_ + current_num_bytes_) % capacity_num_bytes(); - EnsureBufferNoLock(); - elements.GetArray(buffer_.get() + first_write_index, - num_bytes_to_write_first); - - if (num_bytes_to_write_first < num_bytes_to_write) { - // The "second write index" is zero. - elements.At(num_bytes_to_write_first) - .GetArray(buffer_.get(), num_bytes_to_write - num_bytes_to_write_first); - } - - current_num_bytes_ += num_bytes_to_write; - DCHECK_LE(current_num_bytes_, capacity_num_bytes()); - num_bytes.Put(static_cast<uint32_t>(num_bytes_to_write)); - return MOJO_RESULT_OK; -} - -MojoResult LocalDataPipe::ProducerBeginWriteDataImplNoLock( - UserPointer<void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - uint32_t min_num_bytes_to_write) { - DCHECK(consumer_open_no_lock()); - - // The index we need to start writing at. - size_t write_index = - (start_index_ + current_num_bytes_) % capacity_num_bytes(); - - size_t max_num_bytes_to_write = GetMaxNumBytesToWriteNoLock(); - if (min_num_bytes_to_write > max_num_bytes_to_write) { - // In "may discard" mode, we can always write from the write index to the - // end of the buffer. - if (may_discard() && - min_num_bytes_to_write <= capacity_num_bytes() - write_index) { - // To do so, we need to discard an appropriate amount of data. - // We should only reach here if the start index is after the write index! - DCHECK_GE(start_index_, write_index); - DCHECK_GT(min_num_bytes_to_write - max_num_bytes_to_write, 0u); - MarkDataAsConsumedNoLock(min_num_bytes_to_write - max_num_bytes_to_write); - max_num_bytes_to_write = min_num_bytes_to_write; - } else { - // Don't return "should wait" since you can't wait for a specified amount - // of data. - return MOJO_RESULT_OUT_OF_RANGE; - } - } - - // Don't go into a two-phase write if there's no room. - if (max_num_bytes_to_write == 0) - return MOJO_RESULT_SHOULD_WAIT; - - EnsureBufferNoLock(); - buffer.Put(buffer_.get() + write_index); - buffer_num_bytes.Put(static_cast<uint32_t>(max_num_bytes_to_write)); - set_producer_two_phase_max_num_bytes_written_no_lock( - static_cast<uint32_t>(max_num_bytes_to_write)); - return MOJO_RESULT_OK; -} - -MojoResult LocalDataPipe::ProducerEndWriteDataImplNoLock( - uint32_t num_bytes_written) { - DCHECK_LE(num_bytes_written, - producer_two_phase_max_num_bytes_written_no_lock()); - current_num_bytes_ += num_bytes_written; - DCHECK_LE(current_num_bytes_, capacity_num_bytes()); - set_producer_two_phase_max_num_bytes_written_no_lock(0); - return MOJO_RESULT_OK; -} - -HandleSignalsState LocalDataPipe::ProducerGetHandleSignalsStateImplNoLock() - const { - HandleSignalsState rv; - if (consumer_open_no_lock()) { - if ((may_discard() || current_num_bytes_ < capacity_num_bytes()) && - !producer_in_two_phase_write_no_lock()) - rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_WRITABLE; - rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_WRITABLE; - } else { - rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED; - } - rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED; - return rv; -} - -void LocalDataPipe::ConsumerCloseImplNoLock() { - // If the producer is around and in a two-phase write, we have to keep the - // buffer around. (We then don't free it until the producer is closed. This - // could be rectified, but again seems like optimizing for the uncommon case.) - if (!producer_open_no_lock() || !producer_in_two_phase_write_no_lock()) - DestroyBufferNoLock(); - current_num_bytes_ = 0; -} - -MojoResult LocalDataPipe::ConsumerReadDataImplNoLock( - UserPointer<void> elements, - UserPointer<uint32_t> num_bytes, - uint32_t max_num_bytes_to_read, - uint32_t min_num_bytes_to_read, - bool peek) { - DCHECK_EQ(max_num_bytes_to_read % element_num_bytes(), 0u); - DCHECK_EQ(min_num_bytes_to_read % element_num_bytes(), 0u); - DCHECK_GT(max_num_bytes_to_read, 0u); - - if (min_num_bytes_to_read > current_num_bytes_) { - // Don't return "should wait" since you can't wait for a specified amount of - // data. - return producer_open_no_lock() ? MOJO_RESULT_OUT_OF_RANGE - : MOJO_RESULT_FAILED_PRECONDITION; - } - - size_t num_bytes_to_read = - std::min(static_cast<size_t>(max_num_bytes_to_read), current_num_bytes_); - if (num_bytes_to_read == 0) { - return producer_open_no_lock() ? MOJO_RESULT_SHOULD_WAIT - : MOJO_RESULT_FAILED_PRECONDITION; - } - - // The amount we can read in our first |memcpy()|. - size_t num_bytes_to_read_first = - std::min(num_bytes_to_read, GetMaxNumBytesToReadNoLock()); - elements.PutArray(buffer_.get() + start_index_, num_bytes_to_read_first); - - if (num_bytes_to_read_first < num_bytes_to_read) { - // The "second read index" is zero. - elements.At(num_bytes_to_read_first) - .PutArray(buffer_.get(), num_bytes_to_read - num_bytes_to_read_first); - } - - if (!peek) - MarkDataAsConsumedNoLock(num_bytes_to_read); - num_bytes.Put(static_cast<uint32_t>(num_bytes_to_read)); - return MOJO_RESULT_OK; -} - -MojoResult LocalDataPipe::ConsumerDiscardDataImplNoLock( - UserPointer<uint32_t> num_bytes, - uint32_t max_num_bytes_to_discard, - uint32_t min_num_bytes_to_discard) { - DCHECK_EQ(max_num_bytes_to_discard % element_num_bytes(), 0u); - DCHECK_EQ(min_num_bytes_to_discard % element_num_bytes(), 0u); - DCHECK_GT(max_num_bytes_to_discard, 0u); - - if (min_num_bytes_to_discard > current_num_bytes_) { - // Don't return "should wait" since you can't wait for a specified amount of - // data. - return producer_open_no_lock() ? MOJO_RESULT_OUT_OF_RANGE - : MOJO_RESULT_FAILED_PRECONDITION; - } - - // Be consistent with other operations; error if no data available. - if (current_num_bytes_ == 0) { - return producer_open_no_lock() ? MOJO_RESULT_SHOULD_WAIT - : MOJO_RESULT_FAILED_PRECONDITION; - } - - size_t num_bytes_to_discard = std::min( - static_cast<size_t>(max_num_bytes_to_discard), current_num_bytes_); - MarkDataAsConsumedNoLock(num_bytes_to_discard); - num_bytes.Put(static_cast<uint32_t>(num_bytes_to_discard)); - return MOJO_RESULT_OK; -} - -MojoResult LocalDataPipe::ConsumerQueryDataImplNoLock( - UserPointer<uint32_t> num_bytes) { - // Note: This cast is safe, since the capacity fits into a |uint32_t|. - num_bytes.Put(static_cast<uint32_t>(current_num_bytes_)); - return MOJO_RESULT_OK; -} - -MojoResult LocalDataPipe::ConsumerBeginReadDataImplNoLock( - UserPointer<const void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - uint32_t min_num_bytes_to_read) { - size_t max_num_bytes_to_read = GetMaxNumBytesToReadNoLock(); - if (min_num_bytes_to_read > max_num_bytes_to_read) { - // Don't return "should wait" since you can't wait for a specified amount of - // data. - return producer_open_no_lock() ? MOJO_RESULT_OUT_OF_RANGE - : MOJO_RESULT_FAILED_PRECONDITION; - } - - // Don't go into a two-phase read if there's no data. - if (max_num_bytes_to_read == 0) { - return producer_open_no_lock() ? MOJO_RESULT_SHOULD_WAIT - : MOJO_RESULT_FAILED_PRECONDITION; - } - - buffer.Put(buffer_.get() + start_index_); - buffer_num_bytes.Put(static_cast<uint32_t>(max_num_bytes_to_read)); - set_consumer_two_phase_max_num_bytes_read_no_lock( - static_cast<uint32_t>(max_num_bytes_to_read)); - return MOJO_RESULT_OK; -} - -MojoResult LocalDataPipe::ConsumerEndReadDataImplNoLock( - uint32_t num_bytes_read) { - DCHECK_LE(num_bytes_read, consumer_two_phase_max_num_bytes_read_no_lock()); - DCHECK_LE(start_index_ + num_bytes_read, capacity_num_bytes()); - MarkDataAsConsumedNoLock(num_bytes_read); - set_consumer_two_phase_max_num_bytes_read_no_lock(0); - return MOJO_RESULT_OK; -} - -HandleSignalsState LocalDataPipe::ConsumerGetHandleSignalsStateImplNoLock() - const { - HandleSignalsState rv; - if (current_num_bytes_ > 0) { - if (!consumer_in_two_phase_read_no_lock()) - rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_READABLE; - rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE; - } else if (producer_open_no_lock()) { - rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE; - } - if (!producer_open_no_lock()) - rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED; - rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED; - return rv; -} - -void LocalDataPipe::EnsureBufferNoLock() { - DCHECK(producer_open_no_lock()); - if (buffer_) - return; - buffer_.reset(static_cast<char*>( - base::AlignedAlloc(capacity_num_bytes(), - GetConfiguration().data_pipe_buffer_alignment_bytes))); -} - -void LocalDataPipe::DestroyBufferNoLock() { -#ifndef NDEBUG - // Scribble on the buffer to help detect use-after-frees. (This also helps the - // unit test detect certain bugs without needing ASAN or similar.) - if (buffer_) - memset(buffer_.get(), 0xcd, capacity_num_bytes()); -#endif - buffer_.reset(); -} - -size_t LocalDataPipe::GetMaxNumBytesToWriteNoLock() { - size_t next_index = start_index_ + current_num_bytes_; - if (next_index >= capacity_num_bytes()) { - next_index %= capacity_num_bytes(); - DCHECK_GE(start_index_, next_index); - DCHECK_EQ(start_index_ - next_index, - capacity_num_bytes() - current_num_bytes_); - return start_index_ - next_index; - } - return capacity_num_bytes() - next_index; -} - -size_t LocalDataPipe::GetMaxNumBytesToReadNoLock() { - if (start_index_ + current_num_bytes_ > capacity_num_bytes()) - return capacity_num_bytes() - start_index_; - return current_num_bytes_; -} - -void LocalDataPipe::MarkDataAsConsumedNoLock(size_t num_bytes) { - DCHECK_LE(num_bytes, current_num_bytes_); - start_index_ += num_bytes; - start_index_ %= capacity_num_bytes(); - current_num_bytes_ -= num_bytes; -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/local_data_pipe.h b/mojo/edk/system/local_data_pipe.h deleted file mode 100644 index abe3a46..0000000 --- a/mojo/edk/system/local_data_pipe.h +++ /dev/null @@ -1,89 +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_EDK_SYSTEM_LOCAL_DATA_PIPE_H_ -#define MOJO_EDK_SYSTEM_LOCAL_DATA_PIPE_H_ - -#include "base/macros.h" -#include "base/memory/aligned_memory.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "mojo/edk/system/data_pipe.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace system { - -// |LocalDataPipe| is a subclass that "implements" |DataPipe| for data pipes -// whose producer and consumer are both local. This class is thread-safe (with -// protection provided by |DataPipe|'s |lock_|. -class MOJO_SYSTEM_IMPL_EXPORT LocalDataPipe : public DataPipe { - public: - // |validated_options| should be the output of |DataPipe::ValidateOptions()|. - // In particular: |struct_size| is ignored (so |validated_options| must be the - // current version of the struct) and |capacity_num_bytes| must be nonzero. - explicit LocalDataPipe(const MojoCreateDataPipeOptions& validated_options); - - private: - friend class base::RefCountedThreadSafe<LocalDataPipe>; - ~LocalDataPipe() override; - - // |DataPipe| implementation: - void ProducerCloseImplNoLock() override; - MojoResult ProducerWriteDataImplNoLock( - UserPointer<const void> elements, - UserPointer<uint32_t> num_bytes, - uint32_t max_num_bytes_to_write, - uint32_t min_num_bytes_to_write) override; - MojoResult ProducerBeginWriteDataImplNoLock( - UserPointer<void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - uint32_t min_num_bytes_to_write) override; - MojoResult ProducerEndWriteDataImplNoLock( - uint32_t num_bytes_written) override; - HandleSignalsState ProducerGetHandleSignalsStateImplNoLock() const override; - void ConsumerCloseImplNoLock() override; - MojoResult ConsumerReadDataImplNoLock(UserPointer<void> elements, - UserPointer<uint32_t> num_bytes, - uint32_t max_num_bytes_to_read, - uint32_t min_num_bytes_to_read, - bool peek) override; - MojoResult ConsumerDiscardDataImplNoLock( - UserPointer<uint32_t> num_bytes, - uint32_t max_num_bytes_to_discard, - uint32_t min_num_bytes_to_discard) override; - MojoResult ConsumerQueryDataImplNoLock( - UserPointer<uint32_t> num_bytes) override; - MojoResult ConsumerBeginReadDataImplNoLock( - UserPointer<const void*> buffer, - UserPointer<uint32_t> buffer_num_bytes, - uint32_t min_num_bytes_to_read) override; - MojoResult ConsumerEndReadDataImplNoLock(uint32_t num_bytes_read) override; - HandleSignalsState ConsumerGetHandleSignalsStateImplNoLock() const override; - - void EnsureBufferNoLock(); - void DestroyBufferNoLock(); - - // Get the maximum (single) write/read size right now (in number of elements); - // result fits in a |uint32_t|. - size_t GetMaxNumBytesToWriteNoLock(); - size_t GetMaxNumBytesToReadNoLock(); - - // Marks the given number of bytes as consumed/discarded. |num_bytes| must be - // greater than |current_num_bytes_|. - void MarkDataAsConsumedNoLock(size_t num_bytes); - - // The members below are protected by |DataPipe|'s |lock_|: - scoped_ptr<char, base::AlignedFreeDeleter> buffer_; - // Circular buffer. - size_t start_index_; - size_t current_num_bytes_; - - DISALLOW_COPY_AND_ASSIGN(LocalDataPipe); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_LOCAL_DATA_PIPE_H_ diff --git a/mojo/edk/system/local_data_pipe_unittest.cc b/mojo/edk/system/local_data_pipe_unittest.cc deleted file mode 100644 index 1223a2ba..0000000 --- a/mojo/edk/system/local_data_pipe_unittest.cc +++ /dev/null @@ -1,2071 +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/edk/system/local_data_pipe.h" - -#include <string.h> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "mojo/edk/system/data_pipe.h" -#include "mojo/edk/system/waiter.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -const uint32_t kSizeOfOptions = - static_cast<uint32_t>(sizeof(MojoCreateDataPipeOptions)); - -// Validate options. -TEST(LocalDataPipeTest, Creation) { - // Create using default options. - { - // Get default options. - MojoCreateDataPipeOptions default_options = {0}; - EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( - NullUserPointer(), &default_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(default_options)); - dp->ProducerClose(); - dp->ConsumerClose(); - } - - // Create using non-default options. - { - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - 1, // |element_num_bytes|. - 1000 // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {0}; - EXPECT_EQ(MOJO_RESULT_OK, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), - &validated_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - dp->ProducerClose(); - dp->ConsumerClose(); - } - { - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - 4, // |element_num_bytes|. - 4000 // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {0}; - EXPECT_EQ(MOJO_RESULT_OK, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), - &validated_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - dp->ProducerClose(); - dp->ConsumerClose(); - } - { - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD, // |flags|. - 7, // |element_num_bytes|. - 7000000 // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {0}; - EXPECT_EQ(MOJO_RESULT_OK, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), - &validated_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - dp->ProducerClose(); - dp->ConsumerClose(); - } - // Default capacity. - { - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD, // |flags|. - 100, // |element_num_bytes|. - 0 // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {0}; - EXPECT_EQ(MOJO_RESULT_OK, - DataPipe::ValidateCreateOptions(MakeUserPointer(&options), - &validated_options)); - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - dp->ProducerClose(); - dp->ConsumerClose(); - } -} - -TEST(LocalDataPipeTest, SimpleReadWrite) { - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|. - 1000 * sizeof(int32_t) // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {0}; - EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( - MakeUserPointer(&options), &validated_options)); - - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - - int32_t elements[10] = {0}; - uint32_t num_bytes = 0; - - // Try reading; nothing there yet. - num_bytes = static_cast<uint32_t>(arraysize(elements) * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - dp->ConsumerReadData(UserPointer<void>(elements), - MakeUserPointer(&num_bytes), false, false)); - - // Query; nothing there yet. - num_bytes = 0; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(0u, num_bytes); - - // Discard; nothing there yet. - num_bytes = static_cast<uint32_t>(5u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), false)); - - // Read with invalid |num_bytes|. - num_bytes = sizeof(elements[0]) + 1; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - dp->ConsumerReadData(UserPointer<void>(elements), - MakeUserPointer(&num_bytes), false, false)); - - // Write two elements. - elements[0] = 123; - elements[1] = 456; - num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(elements), - MakeUserPointer(&num_bytes), false)); - // It should have written everything (even without "all or none"). - EXPECT_EQ(2u * sizeof(elements[0]), num_bytes); - - // Query. - num_bytes = 0; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(2 * sizeof(elements[0]), num_bytes); - - // Read one element. - elements[0] = -1; - elements[1] = -1; - num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(elements), - MakeUserPointer(&num_bytes), false, false)); - EXPECT_EQ(1u * sizeof(elements[0]), num_bytes); - EXPECT_EQ(123, elements[0]); - EXPECT_EQ(-1, elements[1]); - - // Query. - num_bytes = 0; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(1 * sizeof(elements[0]), num_bytes); - - // Peek one element. - elements[0] = -1; - elements[1] = -1; - num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(elements), - MakeUserPointer(&num_bytes), false, true)); - EXPECT_EQ(1u * sizeof(elements[0]), num_bytes); - EXPECT_EQ(456, elements[0]); - EXPECT_EQ(-1, elements[1]); - - // Query. Still has 1 element remaining. - num_bytes = 0; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(1 * sizeof(elements[0]), num_bytes); - - // Try to read two elements, with "all or none". - elements[0] = -1; - elements[1] = -1; - num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - dp->ConsumerReadData(UserPointer<void>(elements), - MakeUserPointer(&num_bytes), true, false)); - EXPECT_EQ(-1, elements[0]); - EXPECT_EQ(-1, elements[1]); - - // Try to read two elements, without "all or none". - elements[0] = -1; - elements[1] = -1; - num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(elements), - MakeUserPointer(&num_bytes), false, false)); - EXPECT_EQ(456, elements[0]); - EXPECT_EQ(-1, elements[1]); - - // Query. - num_bytes = 0; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(0u, num_bytes); - - dp->ProducerClose(); - dp->ConsumerClose(); -} - -// Note: The "basic" waiting tests test that the "wait states" are correct in -// various situations; they don't test that waiters are properly awoken on state -// changes. (For that, we need to use multiple threads.) -TEST(LocalDataPipeTest, BasicProducerWaiting) { - // Note: We take advantage of the fact that for |LocalDataPipe|, capacities - // are strict maximums. This is not guaranteed by the API. - - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|. - 2 * sizeof(int32_t) // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {0}; - EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( - MakeUserPointer(&options), &validated_options)); - - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - Waiter waiter; - uint32_t context = 0; - HandleSignalsState hss; - - // Never readable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ( - MOJO_RESULT_FAILED_PRECONDITION, - dp->ProducerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 12, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Already writable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ( - MOJO_RESULT_ALREADY_EXISTS, - dp->ProducerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 34, &hss)); - - // Write two elements. - int32_t elements[2] = {123, 456}; - uint32_t num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(elements), - MakeUserPointer(&num_bytes), true)); - EXPECT_EQ(static_cast<uint32_t>(2u * sizeof(elements[0])), num_bytes); - - // Adding a waiter should now succeed. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - dp->ProducerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 56, - nullptr)); - // And it shouldn't be writable yet. - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, nullptr)); - hss = HandleSignalsState(); - dp->ProducerRemoveAwakable(&waiter, &hss); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Peek one element. - elements[0] = -1; - elements[1] = -1; - num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(elements), - MakeUserPointer(&num_bytes), true, true)); - EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes); - EXPECT_EQ(123, elements[0]); - EXPECT_EQ(-1, elements[1]); - - // Add a waiter. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - dp->ProducerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 56, - nullptr)); - // And it still shouldn't be writable yet. - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, nullptr)); - hss = HandleSignalsState(); - dp->ProducerRemoveAwakable(&waiter, &hss); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Do it again. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - dp->ProducerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 78, - nullptr)); - - // Read one element. - elements[0] = -1; - elements[1] = -1; - num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(elements), - MakeUserPointer(&num_bytes), true, false)); - EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes); - EXPECT_EQ(123, elements[0]); - EXPECT_EQ(-1, elements[1]); - - // Waiting should now succeed. - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(1000, &context)); - EXPECT_EQ(78u, context); - hss = HandleSignalsState(); - dp->ProducerRemoveAwakable(&waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Try writing, using a two-phase write. - void* buffer = nullptr; - num_bytes = static_cast<uint32_t>(3u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerBeginWriteData(MakeUserPointer(&buffer), - MakeUserPointer(&num_bytes), false)); - EXPECT_TRUE(buffer); - EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes); - - static_cast<int32_t*>(buffer)[0] = 789; - EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(static_cast<uint32_t>( - 1u * sizeof(elements[0])))); - - // Add a waiter. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - dp->ProducerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 90, - nullptr)); - - // Read one element, using a two-phase read. - const void* read_buffer = nullptr; - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerBeginReadData(MakeUserPointer(&read_buffer), - MakeUserPointer(&num_bytes), false)); - EXPECT_TRUE(read_buffer); - // Since we only read one element (after having written three in all), the - // two-phase read should only allow us to read one. This checks an - // implementation detail! - EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes); - EXPECT_EQ(456, static_cast<const int32_t*>(read_buffer)[0]); - EXPECT_EQ( - MOJO_RESULT_OK, - dp->ConsumerEndReadData(static_cast<uint32_t>(1u * sizeof(elements[0])))); - - // Waiting should succeed. - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(1000, &context)); - EXPECT_EQ(90u, context); - hss = HandleSignalsState(); - dp->ProducerRemoveAwakable(&waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Write one element. - elements[0] = 123; - num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(elements), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes); - - // Add a waiter. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - dp->ProducerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 12, - nullptr)); - - // Close the consumer. - dp->ConsumerClose(); - - // It should now be never-writable. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, waiter.Wait(1000, &context)); - EXPECT_EQ(12u, context); - hss = HandleSignalsState(); - dp->ProducerRemoveAwakable(&waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - dp->ProducerClose(); -} - -TEST(LocalDataPipeTest, PeerClosedWaiting) { - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|. - 2 * sizeof(int32_t) // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {0}; - EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( - MakeUserPointer(&options), &validated_options)); - - Waiter waiter; - HandleSignalsState hss; - - // Check MOJO_HANDLE_SIGNAL_PEER_CLOSED on producer. - { - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - // Add a waiter. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - dp->ProducerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - 12, nullptr)); - - // Close the consumer. - dp->ConsumerClose(); - - // It should be signaled. - uint32_t context = 0; - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(1000, &context)); - EXPECT_EQ(12u, context); - hss = HandleSignalsState(); - dp->ProducerRemoveAwakable(&waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - dp->ProducerClose(); - } - - // Check MOJO_HANDLE_SIGNAL_PEER_CLOSED on consumer. - { - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - // Add a waiter. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - 12, nullptr)); - - // Close the producer. - dp->ProducerClose(); - - // It should be signaled. - uint32_t context = 0; - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(1000, &context)); - EXPECT_EQ(12u, context); - hss = HandleSignalsState(); - dp->ConsumerRemoveAwakable(&waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - dp->ConsumerClose(); - } -} - -TEST(LocalDataPipeTest, BasicConsumerWaiting) { - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|. - 1000 * sizeof(int32_t) // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {0}; - EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( - MakeUserPointer(&options), &validated_options)); - - { - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - Waiter waiter; - uint32_t context = 0; - HandleSignalsState hss; - - // Never writable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 12, - &hss)); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Not yet readable. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 34, - nullptr)); - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, nullptr)); - hss = HandleSignalsState(); - dp->ConsumerRemoveAwakable(&waiter, &hss); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Write two elements. - int32_t elements[2] = {123, 456}; - uint32_t num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(elements), - MakeUserPointer(&num_bytes), true)); - - // Should already be readable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 56, - &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Discard one element. - num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), true)); - EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes); - - // Should still be readable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 78, - &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Peek one element. - elements[0] = -1; - elements[1] = -1; - num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(elements), - MakeUserPointer(&num_bytes), true, true)); - EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes); - EXPECT_EQ(456, elements[0]); - EXPECT_EQ(-1, elements[1]); - - // Should still be readable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 78, - &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Read one element. - elements[0] = -1; - elements[1] = -1; - num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(elements), - MakeUserPointer(&num_bytes), true, false)); - EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes); - EXPECT_EQ(456, elements[0]); - EXPECT_EQ(-1, elements[1]); - - // Adding a waiter should now succeed. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 90, - nullptr)); - - // Write one element. - elements[0] = 789; - elements[1] = -1; - num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(elements), - MakeUserPointer(&num_bytes), true)); - - // Waiting should now succeed. - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(1000, &context)); - EXPECT_EQ(90u, context); - hss = HandleSignalsState(); - dp->ConsumerRemoveAwakable(&waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Close the producer. - dp->ProducerClose(); - - // Should still be readable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 12, - &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Read one element. - elements[0] = -1; - elements[1] = -1; - num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(elements), - MakeUserPointer(&num_bytes), true, false)); - EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes); - EXPECT_EQ(789, elements[0]); - EXPECT_EQ(-1, elements[1]); - - // Should be never-readable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 34, - &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - dp->ConsumerClose(); - } - - // Test with two-phase APIs and closing the producer with an active consumer - // waiter. - { - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - Waiter waiter; - uint32_t context = 0; - HandleSignalsState hss; - - // Write two elements. - int32_t* elements = nullptr; - void* buffer = nullptr; - // Request room for three (but we'll only write two). - uint32_t num_bytes = static_cast<uint32_t>(3u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerBeginWriteData(MakeUserPointer(&buffer), - MakeUserPointer(&num_bytes), true)); - EXPECT_TRUE(buffer); - EXPECT_GE(num_bytes, static_cast<uint32_t>(3u * sizeof(elements[0]))); - elements = static_cast<int32_t*>(buffer); - elements[0] = 123; - elements[1] = 456; - EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(static_cast<uint32_t>( - 2u * sizeof(elements[0])))); - - // Should already be readable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 12, - &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Read one element. - // Request two in all-or-none mode, but only read one. - const void* read_buffer = nullptr; - num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerBeginReadData(MakeUserPointer(&read_buffer), - MakeUserPointer(&num_bytes), true)); - EXPECT_TRUE(read_buffer); - EXPECT_EQ(static_cast<uint32_t>(2u * sizeof(elements[0])), num_bytes); - const int32_t* read_elements = static_cast<const int32_t*>(read_buffer); - EXPECT_EQ(123, read_elements[0]); - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(static_cast<uint32_t>( - 1u * sizeof(elements[0])))); - - // Should still be readable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 34, - &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Read one element. - // Request three, but not in all-or-none mode. - read_buffer = nullptr; - num_bytes = static_cast<uint32_t>(3u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerBeginReadData(MakeUserPointer(&read_buffer), - MakeUserPointer(&num_bytes), false)); - EXPECT_TRUE(read_buffer); - EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes); - read_elements = static_cast<const int32_t*>(read_buffer); - EXPECT_EQ(456, read_elements[0]); - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(static_cast<uint32_t>( - 1u * sizeof(elements[0])))); - - // Adding a waiter should now succeed. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 56, - nullptr)); - - // Close the producer. - dp->ProducerClose(); - - // Should be never-readable. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, waiter.Wait(1000, &context)); - EXPECT_EQ(56u, context); - hss = HandleSignalsState(); - dp->ConsumerRemoveAwakable(&waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - dp->ConsumerClose(); - } -} - -// Tests that data pipes aren't writable/readable during two-phase writes/reads. -TEST(LocalDataPipeTest, BasicTwoPhaseWaiting) { - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|. - 1000 * sizeof(int32_t) // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {0}; - EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( - MakeUserPointer(&options), &validated_options)); - - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - Waiter waiter; - HandleSignalsState hss; - - // It should be writable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ( - MOJO_RESULT_ALREADY_EXISTS, - dp->ProducerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - uint32_t num_bytes = static_cast<uint32_t>(1u * sizeof(int32_t)); - void* write_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), false)); - EXPECT_TRUE(write_ptr); - EXPECT_GE(num_bytes, static_cast<uint32_t>(1u * sizeof(int32_t))); - - // At this point, it shouldn't be writable. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - dp->ProducerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 1, - nullptr)); - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, nullptr)); - hss = HandleSignalsState(); - dp->ProducerRemoveAwakable(&waiter, &hss); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // It shouldn't be readable yet either. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 2, - nullptr)); - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, nullptr)); - hss = HandleSignalsState(); - dp->ConsumerRemoveAwakable(&waiter, &hss); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - static_cast<int32_t*>(write_ptr)[0] = 123; - EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData( - static_cast<uint32_t>(1u * sizeof(int32_t)))); - - // It should be writable again. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ( - MOJO_RESULT_ALREADY_EXISTS, - dp->ProducerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 3, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // And readable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ( - MOJO_RESULT_ALREADY_EXISTS, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 4, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Start another two-phase write and check that it's readable even in the - // middle of it. - num_bytes = static_cast<uint32_t>(1u * sizeof(int32_t)); - write_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), false)); - EXPECT_TRUE(write_ptr); - EXPECT_GE(num_bytes, static_cast<uint32_t>(1u * sizeof(int32_t))); - - // It should be readable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ( - MOJO_RESULT_ALREADY_EXISTS, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 5, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // End the two-phase write without writing anything. - EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(0u)); - - // Start a two-phase read. - num_bytes = static_cast<uint32_t>(1u * sizeof(int32_t)); - const void* read_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerBeginReadData(MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), false)); - EXPECT_TRUE(read_ptr); - EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(int32_t)), num_bytes); - - // At this point, it should still be writable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ( - MOJO_RESULT_ALREADY_EXISTS, - dp->ProducerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 6, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // But not readable. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 7, - nullptr)); - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, nullptr)); - hss = HandleSignalsState(); - dp->ConsumerRemoveAwakable(&waiter, &hss); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // End the two-phase read without reading anything. - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(0u)); - - // It should be readable again. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ( - MOJO_RESULT_ALREADY_EXISTS, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 8, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - dp->ProducerClose(); - dp->ConsumerClose(); -} - -// Test that a "may discard" data pipe is writable even when it's full. -TEST(LocalDataPipeTest, BasicMayDiscardWaiting) { - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD, // |flags|. - static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|. - 1 * sizeof(int32_t) // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {0}; - EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( - MakeUserPointer(&options), &validated_options)); - - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - Waiter waiter; - HandleSignalsState hss; - - // Writable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ( - MOJO_RESULT_ALREADY_EXISTS, - dp->ProducerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Not readable. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 1, - nullptr)); - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, nullptr)); - hss = HandleSignalsState(); - dp->ConsumerRemoveAwakable(&waiter, &hss); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - uint32_t num_bytes = static_cast<uint32_t>(sizeof(int32_t)); - int32_t element = 123; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(&element), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(static_cast<uint32_t>(sizeof(int32_t)), num_bytes); - - // Still writable (even though it's full). - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ( - MOJO_RESULT_ALREADY_EXISTS, - dp->ProducerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 2, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Now readable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ( - MOJO_RESULT_ALREADY_EXISTS, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 3, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Overwrite that element. - num_bytes = static_cast<uint32_t>(sizeof(int32_t)); - element = 456; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(&element), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(static_cast<uint32_t>(sizeof(int32_t)), num_bytes); - - // Still writable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ( - MOJO_RESULT_ALREADY_EXISTS, - dp->ProducerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 4, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // And still readable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ( - MOJO_RESULT_ALREADY_EXISTS, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 5, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Read that element. - num_bytes = static_cast<uint32_t>(sizeof(int32_t)); - element = 0; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(&element), - MakeUserPointer(&num_bytes), false, false)); - EXPECT_EQ(static_cast<uint32_t>(sizeof(int32_t)), num_bytes); - EXPECT_EQ(456, element); - - // Still writable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ( - MOJO_RESULT_ALREADY_EXISTS, - dp->ProducerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 6, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // No longer readable. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - dp->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 7, - nullptr)); - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, nullptr)); - hss = HandleSignalsState(); - dp->ConsumerRemoveAwakable(&waiter, &hss); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - dp->ProducerClose(); - dp->ConsumerClose(); -} - -void Seq(int32_t start, size_t count, int32_t* out) { - for (size_t i = 0; i < count; i++) - out[i] = start + static_cast<int32_t>(i); -} - -TEST(LocalDataPipeTest, MayDiscard) { - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD, // |flags|. - static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|. - 10 * sizeof(int32_t) // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {0}; - EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( - MakeUserPointer(&options), &validated_options)); - - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - - int32_t buffer[100] = {0}; - uint32_t num_bytes = 0; - - num_bytes = 20u * sizeof(int32_t); - Seq(0, arraysize(buffer), buffer); - // Try writing more than capacity. (This test relies on the implementation - // enforcing the capacity strictly.) - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(buffer), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(10u * sizeof(int32_t), num_bytes); - - // Read half of what we wrote. - num_bytes = 5u * sizeof(int32_t); - memset(buffer, 0xab, sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), false, false)); - EXPECT_EQ(5u * sizeof(int32_t), num_bytes); - int32_t expected_buffer[100]; - memset(expected_buffer, 0xab, sizeof(expected_buffer)); - Seq(0, 5u, expected_buffer); - EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer))); - // Internally, a circular buffer would now look like: - // -, -, -, -, -, 5, 6, 7, 8, 9 - - // Write a bit more than the space that's available. - num_bytes = 8u * sizeof(int32_t); - Seq(100, arraysize(buffer), buffer); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(buffer), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(8u * sizeof(int32_t), num_bytes); - // Internally, a circular buffer would now look like: - // 100, 101, 102, 103, 104, 105, 106, 107, 8, 9 - - // Read half of what's available. - num_bytes = 5u * sizeof(int32_t); - memset(buffer, 0xab, sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), false, false)); - EXPECT_EQ(5u * sizeof(int32_t), num_bytes); - memset(expected_buffer, 0xab, sizeof(expected_buffer)); - expected_buffer[0] = 8; - expected_buffer[1] = 9; - expected_buffer[2] = 100; - expected_buffer[3] = 101; - expected_buffer[4] = 102; - EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer))); - // Internally, a circular buffer would now look like: - // -, -, -, 103, 104, 105, 106, 107, -, - - - // Write one integer. - num_bytes = 1u * sizeof(int32_t); - Seq(200, arraysize(buffer), buffer); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(buffer), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(1u * sizeof(int32_t), num_bytes); - // Internally, a circular buffer would now look like: - // -, -, -, 103, 104, 105, 106, 107, 200, - - - // Write five more. - num_bytes = 5u * sizeof(int32_t); - Seq(300, arraysize(buffer), buffer); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(buffer), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(5u * sizeof(int32_t), num_bytes); - // Internally, a circular buffer would now look like: - // 301, 302, 303, 304, 104, 105, 106, 107, 200, 300 - - // Read it all. - num_bytes = sizeof(buffer); - memset(buffer, 0xab, sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), false, false)); - EXPECT_EQ(10u * sizeof(int32_t), num_bytes); - memset(expected_buffer, 0xab, sizeof(expected_buffer)); - expected_buffer[0] = 104; - expected_buffer[1] = 105; - expected_buffer[2] = 106; - expected_buffer[3] = 107; - expected_buffer[4] = 200; - expected_buffer[5] = 300; - expected_buffer[6] = 301; - expected_buffer[7] = 302; - expected_buffer[8] = 303; - expected_buffer[9] = 304; - EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer))); - - // Test two-phase writes, including in all-or-none mode. - // Note: Again, the following depends on an implementation detail -- namely - // that the write pointer will point at the 5th element of the buffer (and the - // buffer has exactly the capacity requested). - - num_bytes = 0u; - void* write_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), false)); - EXPECT_TRUE(write_ptr); - EXPECT_EQ(6u * sizeof(int32_t), num_bytes); - Seq(400, 6, static_cast<int32_t*>(write_ptr)); - EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(6u * sizeof(int32_t))); - // Internally, a circular buffer would now look like: - // -, -, -, -, 400, 401, 402, 403, 404, 405 - - // |ProducerBeginWriteData()| ignores |*num_bytes| except in "all-or-none" - // mode. - num_bytes = 6u * sizeof(int32_t); - write_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(4u * sizeof(int32_t), num_bytes); - static_cast<int32_t*>(write_ptr)[0] = 500; - EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(1u * sizeof(int32_t))); - // Internally, a circular buffer would now look like: - // 500, -, -, -, 400, 401, 402, 403, 404, 405 - - // Requesting a 10-element buffer in all-or-none mode fails at this point. - num_bytes = 10u * sizeof(int32_t); - write_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - dp->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), true)); - - // But requesting, say, a 5-element (up to 9, really) buffer should be okay. - // It will discard two elements. - num_bytes = 5u * sizeof(int32_t); - write_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), true)); - EXPECT_EQ(5u * sizeof(int32_t), num_bytes); - // Only write 4 elements though. - Seq(600, 4, static_cast<int32_t*>(write_ptr)); - EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(4u * sizeof(int32_t))); - // Internally, a circular buffer would now look like: - // 500, 600, 601, 602, 603, -, 402, 403, 404, 405 - - // Do this again. Make sure we can get a buffer all the way out to the end of - // the internal buffer. - num_bytes = 5u * sizeof(int32_t); - write_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), true)); - EXPECT_EQ(5u * sizeof(int32_t), num_bytes); - // Only write 3 elements though. - Seq(700, 3, static_cast<int32_t*>(write_ptr)); - EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(3u * sizeof(int32_t))); - // Internally, a circular buffer would now look like: - // 500, 600, 601, 602, 603, 700, 701, 702, -, - - - // Read everything. - num_bytes = sizeof(buffer); - memset(buffer, 0xab, sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), false, false)); - EXPECT_EQ(8u * sizeof(int32_t), num_bytes); - memset(expected_buffer, 0xab, sizeof(expected_buffer)); - expected_buffer[0] = 500; - expected_buffer[1] = 600; - expected_buffer[2] = 601; - expected_buffer[3] = 602; - expected_buffer[4] = 603; - expected_buffer[5] = 700; - expected_buffer[6] = 701; - expected_buffer[7] = 702; - EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer))); - - dp->ProducerClose(); - dp->ConsumerClose(); -} - -TEST(LocalDataPipeTest, AllOrNone) { - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|. - 10 * sizeof(int32_t) // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {0}; - EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( - MakeUserPointer(&options), &validated_options)); - - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - - // Try writing way too much. - uint32_t num_bytes = 20u * sizeof(int32_t); - int32_t buffer[100]; - Seq(0, arraysize(buffer), buffer); - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - dp->ProducerWriteData(UserPointer<const void>(buffer), - MakeUserPointer(&num_bytes), true)); - - // Should still be empty. - num_bytes = ~0u; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(0u, num_bytes); - - // Write some data. - num_bytes = 5u * sizeof(int32_t); - Seq(100, arraysize(buffer), buffer); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(buffer), - MakeUserPointer(&num_bytes), true)); - EXPECT_EQ(5u * sizeof(int32_t), num_bytes); - - // Half full. - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(5u * sizeof(int32_t), num_bytes); - - // Too much. - num_bytes = 6u * sizeof(int32_t); - Seq(200, arraysize(buffer), buffer); - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - dp->ProducerWriteData(UserPointer<const void>(buffer), - MakeUserPointer(&num_bytes), true)); - - // Try reading too much. - num_bytes = 11u * sizeof(int32_t); - memset(buffer, 0xab, sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - dp->ConsumerReadData(UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), true, false)); - int32_t expected_buffer[100]; - memset(expected_buffer, 0xab, sizeof(expected_buffer)); - EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer))); - - // Try discarding too much. - num_bytes = 11u * sizeof(int32_t); - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), true)); - - // Just a little. - num_bytes = 2u * sizeof(int32_t); - Seq(300, arraysize(buffer), buffer); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(buffer), - MakeUserPointer(&num_bytes), true)); - EXPECT_EQ(2u * sizeof(int32_t), num_bytes); - - // Just right. - num_bytes = 3u * sizeof(int32_t); - Seq(400, arraysize(buffer), buffer); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(buffer), - MakeUserPointer(&num_bytes), true)); - EXPECT_EQ(3u * sizeof(int32_t), num_bytes); - - // Exactly full. - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(10u * sizeof(int32_t), num_bytes); - - // Read half. - num_bytes = 5u * sizeof(int32_t); - memset(buffer, 0xab, sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), true, false)); - EXPECT_EQ(5u * sizeof(int32_t), num_bytes); - memset(expected_buffer, 0xab, sizeof(expected_buffer)); - Seq(100, 5, expected_buffer); - EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer))); - - // Try reading too much again. - num_bytes = 6u * sizeof(int32_t); - memset(buffer, 0xab, sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - dp->ConsumerReadData(UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), true, false)); - memset(expected_buffer, 0xab, sizeof(expected_buffer)); - EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer))); - - // Try discarding too much again. - num_bytes = 6u * sizeof(int32_t); - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), true)); - - // Discard a little. - num_bytes = 2u * sizeof(int32_t); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), true)); - EXPECT_EQ(2u * sizeof(int32_t), num_bytes); - - // Three left. - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(3u * sizeof(int32_t), num_bytes); - - // Close the producer, then test producer-closed cases. - dp->ProducerClose(); - - // Try reading too much; "failed precondition" since the producer is closed. - num_bytes = 4u * sizeof(int32_t); - memset(buffer, 0xab, sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - dp->ConsumerReadData(UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), true, false)); - memset(expected_buffer, 0xab, sizeof(expected_buffer)); - EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer))); - - // Try discarding too much; "failed precondition" again. - num_bytes = 4u * sizeof(int32_t); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), true)); - - // Read a little. - num_bytes = 2u * sizeof(int32_t); - memset(buffer, 0xab, sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), true, false)); - EXPECT_EQ(2u * sizeof(int32_t), num_bytes); - memset(expected_buffer, 0xab, sizeof(expected_buffer)); - Seq(400, 2, expected_buffer); - EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer))); - - // Discard the remaining element. - num_bytes = 1u * sizeof(int32_t); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), true)); - EXPECT_EQ(1u * sizeof(int32_t), num_bytes); - - // Empty again. - num_bytes = ~0u; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(0u, num_bytes); - - dp->ConsumerClose(); -} - -TEST(LocalDataPipeTest, AllOrNoneMayDiscard) { - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD, // |flags|. - static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|. - 10 * sizeof(int32_t) // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {0}; - EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( - MakeUserPointer(&options), &validated_options)); - - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - - // Try writing way too much. - uint32_t num_bytes = 20u * sizeof(int32_t); - int32_t buffer[100]; - Seq(0, arraysize(buffer), buffer); - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - dp->ProducerWriteData(UserPointer<const void>(buffer), - MakeUserPointer(&num_bytes), true)); - - // Write some stuff. - num_bytes = 5u * sizeof(int32_t); - Seq(100, arraysize(buffer), buffer); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(buffer), - MakeUserPointer(&num_bytes), true)); - EXPECT_EQ(5u * sizeof(int32_t), num_bytes); - - // Write lots of stuff (discarding all but "104"). - num_bytes = 9u * sizeof(int32_t); - Seq(200, arraysize(buffer), buffer); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(buffer), - MakeUserPointer(&num_bytes), true)); - EXPECT_EQ(9u * sizeof(int32_t), num_bytes); - - // Read one. - num_bytes = 1u * sizeof(int32_t); - memset(buffer, 0xab, sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), true, false)); - EXPECT_EQ(1u * sizeof(int32_t), num_bytes); - int32_t expected_buffer[100]; - memset(expected_buffer, 0xab, sizeof(expected_buffer)); - expected_buffer[0] = 104; - EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer))); - - // Try reading too many. - num_bytes = 10u * sizeof(int32_t); - memset(buffer, 0xab, sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - dp->ConsumerReadData(UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), true, false)); - memset(expected_buffer, 0xab, sizeof(expected_buffer)); - EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer))); - - // Try discarding too many. - num_bytes = 10u * sizeof(int32_t); - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), true)); - - // Discard a bunch. - num_bytes = 4u * sizeof(int32_t); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), true)); - - // Half full. - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(5u * sizeof(int32_t), num_bytes); - - // Write as much as possible. - num_bytes = 10u * sizeof(int32_t); - Seq(300, arraysize(buffer), buffer); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(buffer), - MakeUserPointer(&num_bytes), true)); - EXPECT_EQ(10u * sizeof(int32_t), num_bytes); - - // Read everything. - num_bytes = 10u * sizeof(int32_t); - memset(buffer, 0xab, sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), true, false)); - memset(expected_buffer, 0xab, sizeof(expected_buffer)); - EXPECT_EQ(10u * sizeof(int32_t), num_bytes); - Seq(300, 10, expected_buffer); - EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer))); - - // Note: All-or-none two-phase writes on a "may discard" data pipe are tested - // in LocalDataPipeTest.MayDiscard. - - dp->ProducerClose(); - dp->ConsumerClose(); -} - -TEST(LocalDataPipeTest, TwoPhaseAllOrNone) { - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|. - 10 * sizeof(int32_t) // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {0}; - EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( - MakeUserPointer(&options), &validated_options)); - - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - - // Try writing way too much (two-phase). - uint32_t num_bytes = 20u * sizeof(int32_t); - void* write_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - dp->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), true)); - - // Try writing an amount which isn't a multiple of the element size - // (two-phase). - static_assert(sizeof(int32_t) > 1u, "Wow! int32_t's have size 1"); - num_bytes = 1u; - write_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - dp->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), true)); - - // Try reading way too much (two-phase). - num_bytes = 20u * sizeof(int32_t); - const void* read_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - dp->ConsumerBeginReadData(MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), true)); - - // Write half (two-phase). - num_bytes = 5u * sizeof(int32_t); - write_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), true)); - // May provide more space than requested. - EXPECT_GE(num_bytes, 5u * sizeof(int32_t)); - EXPECT_TRUE(write_ptr); - Seq(0, 5, static_cast<int32_t*>(write_ptr)); - EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(5u * sizeof(int32_t))); - - // Try reading an amount which isn't a multiple of the element size - // (two-phase). - num_bytes = 1u; - read_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - dp->ConsumerBeginReadData(MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), true)); - - // Read one (two-phase). - num_bytes = 1u * sizeof(int32_t); - read_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerBeginReadData(MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), true)); - EXPECT_GE(num_bytes, 1u * sizeof(int32_t)); - EXPECT_EQ(0, static_cast<const int32_t*>(read_ptr)[0]); - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(1u * sizeof(int32_t))); - - // We should have four left, leaving room for six. - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(4u * sizeof(int32_t), num_bytes); - - // Assuming a tight circular buffer of the specified capacity, we can't do a - // two-phase write of six now. - num_bytes = 6u * sizeof(int32_t); - write_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - dp->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), true)); - - // Write six elements (simple), filling the buffer. - num_bytes = 6u * sizeof(int32_t); - int32_t buffer[100]; - Seq(100, 6, buffer); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(buffer), - MakeUserPointer(&num_bytes), true)); - EXPECT_EQ(6u * sizeof(int32_t), num_bytes); - - // We have ten. - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(10u * sizeof(int32_t), num_bytes); - - // But a two-phase read of ten should fail. - num_bytes = 10u * sizeof(int32_t); - read_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - dp->ConsumerBeginReadData(MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), true)); - - // Close the producer. - dp->ProducerClose(); - - // A two-phase read of nine should work. - num_bytes = 9u * sizeof(int32_t); - read_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerBeginReadData(MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), true)); - EXPECT_GE(num_bytes, 9u * sizeof(int32_t)); - EXPECT_EQ(1, static_cast<const int32_t*>(read_ptr)[0]); - EXPECT_EQ(2, static_cast<const int32_t*>(read_ptr)[1]); - EXPECT_EQ(3, static_cast<const int32_t*>(read_ptr)[2]); - EXPECT_EQ(4, static_cast<const int32_t*>(read_ptr)[3]); - EXPECT_EQ(100, static_cast<const int32_t*>(read_ptr)[4]); - EXPECT_EQ(101, static_cast<const int32_t*>(read_ptr)[5]); - EXPECT_EQ(102, static_cast<const int32_t*>(read_ptr)[6]); - EXPECT_EQ(103, static_cast<const int32_t*>(read_ptr)[7]); - EXPECT_EQ(104, static_cast<const int32_t*>(read_ptr)[8]); - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(9u * sizeof(int32_t))); - - // A two-phase read of two should fail, with "failed precondition". - num_bytes = 2u * sizeof(int32_t); - read_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - dp->ConsumerBeginReadData(MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), true)); - - dp->ConsumerClose(); -} - -// Tests that |ProducerWriteData()| and |ConsumerReadData()| writes and reads, -// respectively, as much as possible, even if it has to "wrap around" the -// internal circular buffer. (Note that the two-phase write and read do not do -// this.) -TEST(LocalDataPipeTest, WrapAround) { - unsigned char test_data[1000]; - for (size_t i = 0; i < arraysize(test_data); i++) - test_data[i] = static_cast<unsigned char>(i); - - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - 1u, // |element_num_bytes|. - 100u // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {0}; - EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( - MakeUserPointer(&options), &validated_options)); - // This test won't be valid if |ValidateCreateOptions()| decides to give the - // pipe more space. - ASSERT_EQ(100u, validated_options.capacity_num_bytes); - - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - - // Write 20 bytes. - uint32_t num_bytes = 20u; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(&test_data[0]), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(20u, num_bytes); - - // Read 10 bytes. - unsigned char read_buffer[1000] = {0}; - num_bytes = 10u; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(read_buffer), - MakeUserPointer(&num_bytes), false, false)); - EXPECT_EQ(10u, num_bytes); - EXPECT_EQ(0, memcmp(read_buffer, &test_data[0], 10u)); - - // Check that a two-phase write can now only write (at most) 80 bytes. (This - // checks an implementation detail; this behavior is not guaranteed, but we - // need it for this test.) - void* write_buffer_ptr = nullptr; - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerBeginWriteData(MakeUserPointer(&write_buffer_ptr), - MakeUserPointer(&num_bytes), false)); - EXPECT_TRUE(write_buffer_ptr); - EXPECT_EQ(80u, num_bytes); - EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(0u)); - - // Write as much data as we can (using |ProducerWriteData()|). We should write - // 90 bytes. - num_bytes = 200u; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(&test_data[20]), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(90u, num_bytes); - - // Check that a two-phase read can now only read (at most) 90 bytes. (This - // checks an implementation detail; this behavior is not guaranteed, but we - // need it for this test.) - const void* read_buffer_ptr = nullptr; - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerBeginReadData(MakeUserPointer(&read_buffer_ptr), - MakeUserPointer(&num_bytes), false)); - EXPECT_TRUE(read_buffer_ptr); - EXPECT_EQ(90u, num_bytes); - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(0u)); - - // Read as much as possible (using |ConsumerReadData()|). We should read 100 - // bytes. - num_bytes = - static_cast<uint32_t>(arraysize(read_buffer) * sizeof(read_buffer[0])); - memset(read_buffer, 0, num_bytes); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(read_buffer), - MakeUserPointer(&num_bytes), false, false)); - EXPECT_EQ(100u, num_bytes); - EXPECT_EQ(0, memcmp(read_buffer, &test_data[10], 100u)); - - dp->ProducerClose(); - dp->ConsumerClose(); -} - -// Tests the behavior of closing the producer or consumer with respect to -// writes and reads (simple and two-phase). -TEST(LocalDataPipeTest, CloseWriteRead) { - const char kTestData[] = "hello world"; - const uint32_t kTestDataSize = static_cast<uint32_t>(sizeof(kTestData)); - - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - 1u, // |element_num_bytes|. - 1000u // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {0}; - EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( - MakeUserPointer(&options), &validated_options)); - - // Close producer first, then consumer. - { - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - - // Write some data, so we'll have something to read. - uint32_t num_bytes = kTestDataSize; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(kTestData), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(kTestDataSize, num_bytes); - - // Write it again, so we'll have something left over. - num_bytes = kTestDataSize; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(kTestData), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(kTestDataSize, num_bytes); - - // Start two-phase write. - void* write_buffer_ptr = nullptr; - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerBeginWriteData(MakeUserPointer(&write_buffer_ptr), - MakeUserPointer(&num_bytes), false)); - EXPECT_TRUE(write_buffer_ptr); - EXPECT_GT(num_bytes, 0u); - - // Start two-phase read. - const void* read_buffer_ptr = nullptr; - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerBeginReadData(MakeUserPointer(&read_buffer_ptr), - MakeUserPointer(&num_bytes), false)); - EXPECT_TRUE(read_buffer_ptr); - EXPECT_EQ(2u * kTestDataSize, num_bytes); - - // Close the producer. - dp->ProducerClose(); - - // The consumer can finish its two-phase read. - EXPECT_EQ(0, memcmp(read_buffer_ptr, kTestData, kTestDataSize)); - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(kTestDataSize)); - - // And start another. - read_buffer_ptr = nullptr; - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerBeginReadData(MakeUserPointer(&read_buffer_ptr), - MakeUserPointer(&num_bytes), false)); - EXPECT_TRUE(read_buffer_ptr); - EXPECT_EQ(kTestDataSize, num_bytes); - - // Close the consumer, which cancels the two-phase read. - dp->ConsumerClose(); - } - - // Close consumer first, then producer. - { - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - - // Write some data, so we'll have something to read. - uint32_t num_bytes = kTestDataSize; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(kTestData), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(kTestDataSize, num_bytes); - - // Start two-phase write. - void* write_buffer_ptr = nullptr; - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerBeginWriteData(MakeUserPointer(&write_buffer_ptr), - MakeUserPointer(&num_bytes), false)); - EXPECT_TRUE(write_buffer_ptr); - ASSERT_GT(num_bytes, kTestDataSize); - - // Start two-phase read. - const void* read_buffer_ptr = nullptr; - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerBeginReadData(MakeUserPointer(&read_buffer_ptr), - MakeUserPointer(&num_bytes), false)); - EXPECT_TRUE(read_buffer_ptr); - EXPECT_EQ(kTestDataSize, num_bytes); - - // Close the consumer. - dp->ConsumerClose(); - - // Actually write some data. (Note: Premature freeing of the buffer would - // probably only be detected under ASAN or similar.) - memcpy(write_buffer_ptr, kTestData, kTestDataSize); - // Note: Even though the consumer has been closed, ending the two-phase - // write will report success. - EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(kTestDataSize)); - - // But trying to write should result in failure. - num_bytes = kTestDataSize; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - dp->ProducerWriteData(UserPointer<const void>(kTestData), - MakeUserPointer(&num_bytes), false)); - - // As will trying to start another two-phase write. - write_buffer_ptr = nullptr; - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - dp->ProducerBeginWriteData(MakeUserPointer(&write_buffer_ptr), - MakeUserPointer(&num_bytes), false)); - - dp->ProducerClose(); - } - - // Test closing the consumer first, then the producer, with an active - // two-phase write. - { - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - - // Start two-phase write. - void* write_buffer_ptr = nullptr; - uint32_t num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerBeginWriteData(MakeUserPointer(&write_buffer_ptr), - MakeUserPointer(&num_bytes), false)); - EXPECT_TRUE(write_buffer_ptr); - ASSERT_GT(num_bytes, kTestDataSize); - - dp->ConsumerClose(); - dp->ProducerClose(); - } - - // Test closing the producer and then trying to read (with no data). - { - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - - // Write some data, so we'll have something to read. - uint32_t num_bytes = kTestDataSize; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(kTestData), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(kTestDataSize, num_bytes); - - // Close the producer. - dp->ProducerClose(); - - // Peek that data. - char buffer[1000]; - num_bytes = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), false, true)); - EXPECT_EQ(kTestDataSize, num_bytes); - EXPECT_EQ(0, memcmp(buffer, kTestData, kTestDataSize)); - - // Read that data. - memset(buffer, 0, 1000); - num_bytes = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerReadData(UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), false, false)); - EXPECT_EQ(kTestDataSize, num_bytes); - EXPECT_EQ(0, memcmp(buffer, kTestData, kTestDataSize)); - - // A second read should fail. - num_bytes = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - dp->ConsumerReadData(UserPointer<void>(buffer), - MakeUserPointer(&num_bytes), false, false)); - - // A two-phase read should also fail. - const void* read_buffer_ptr = nullptr; - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - dp->ConsumerBeginReadData(MakeUserPointer(&read_buffer_ptr), - MakeUserPointer(&num_bytes), false)); - - // Ditto for discard. - num_bytes = 10u; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - dp->ConsumerDiscardData(MakeUserPointer(&num_bytes), false)); - - dp->ConsumerClose(); - } -} - -TEST(LocalDataPipeTest, TwoPhaseMoreInvalidArguments) { - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|. - 10 * sizeof(int32_t) // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {0}; - EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( - MakeUserPointer(&options), &validated_options)); - - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - - // No data. - uint32_t num_bytes = 1000u; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(0u, num_bytes); - - // Try "ending" a two-phase write when one isn't active. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - dp->ProducerEndWriteData(1u * sizeof(int32_t))); - - // Still no data. - num_bytes = 1000u; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(0u, num_bytes); - - // Try ending a two-phase write with an invalid amount (too much). - num_bytes = 0u; - void* write_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - dp->ProducerEndWriteData(num_bytes + - static_cast<uint32_t>(sizeof(int32_t)))); - - // But the two-phase write still ended. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, dp->ProducerEndWriteData(0u)); - - // Still no data. - num_bytes = 1000u; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(0u, num_bytes); - - // Try ending a two-phase write with an invalid amount (not a multiple of the - // element size). - num_bytes = 0u; - write_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), false)); - EXPECT_GE(num_bytes, 1u); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, dp->ProducerEndWriteData(1u)); - - // But the two-phase write still ended. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, dp->ProducerEndWriteData(0u)); - - // Still no data. - num_bytes = 1000u; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(0u, num_bytes); - - // Now write some data, so we'll be able to try reading. - int32_t element = 123; - num_bytes = 1u * sizeof(int32_t); - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(&element), - MakeUserPointer(&num_bytes), false)); - - // One element available. - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(1u * sizeof(int32_t), num_bytes); - - // Try "ending" a two-phase read when one isn't active. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - dp->ConsumerEndReadData(1u * sizeof(int32_t))); - - // Still one element available. - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(1u * sizeof(int32_t), num_bytes); - - // Try ending a two-phase read with an invalid amount (too much). - num_bytes = 0u; - const void* read_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerBeginReadData(MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - dp->ConsumerEndReadData(num_bytes + - static_cast<uint32_t>(sizeof(int32_t)))); - - // Still one element available. - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(1u * sizeof(int32_t), num_bytes); - - // Try ending a two-phase read with an invalid amount (not a multiple of the - // element size). - num_bytes = 0u; - read_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerBeginReadData(MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(1u * sizeof(int32_t), num_bytes); - EXPECT_EQ(123, static_cast<const int32_t*>(read_ptr)[0]); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, dp->ConsumerEndReadData(1u)); - - // Still one element available. - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(1u * sizeof(int32_t), num_bytes); - - dp->ProducerClose(); - dp->ConsumerClose(); -} - -// Tests that even with "may discard", the data won't change under a two-phase -// read. -// TODO(vtl): crbug.com/348644: We currently don't pass this. (There are two -// related issues: First, we don't recognize that the data given to -// |ConsumerBeginReadData()| isn't discardable until |ConsumerEndReadData()|, -// and thus we erroneously allow |ProducerWriteData()| to succeed. Second, the -// |ProducerWriteData()| then changes the data underneath the two-phase read.) -TEST(LocalDataPipeTest, DISABLED_MayDiscardTwoPhaseConsistent) { - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD, // |flags|. - 1, // |element_num_bytes|. - 2 // |capacity_num_bytes|. - }; - MojoCreateDataPipeOptions validated_options = {0}; - EXPECT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions( - MakeUserPointer(&options), &validated_options)); - - scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options)); - - // Write some elements. - char elements[2] = {'a', 'b'}; - uint32_t num_bytes = 2u; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(elements), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(2u, num_bytes); - - // Begin reading. - const void* read_ptr = nullptr; - num_bytes = 2u; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerBeginReadData(MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(2u, num_bytes); - EXPECT_EQ('a', static_cast<const char*>(read_ptr)[0]); - EXPECT_EQ('b', static_cast<const char*>(read_ptr)[1]); - - // Try to write some more. But nothing should be discardable right now. - elements[0] = 'x'; - elements[1] = 'y'; - num_bytes = 2u; - // TODO(vtl): This should be: - // EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - // dp->ProducerWriteData(elements, &num_bytes, false)); - // but we incorrectly think that the bytes being read are discardable. Letting - // this through reveals the significant consequence. - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(elements), - MakeUserPointer(&num_bytes), false)); - - // Check that our read buffer hasn't changed underneath us. - EXPECT_EQ('a', static_cast<const char*>(read_ptr)[0]); - EXPECT_EQ('b', static_cast<const char*>(read_ptr)[1]); - - // End reading. - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(2u)); - - // Now writing should succeed. - EXPECT_EQ(MOJO_RESULT_OK, - dp->ProducerWriteData(UserPointer<const void>(elements), - MakeUserPointer(&num_bytes), false)); - - // And if we read, we should get the new values. - read_ptr = nullptr; - num_bytes = 2u; - EXPECT_EQ(MOJO_RESULT_OK, - dp->ConsumerBeginReadData(MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), false)); - EXPECT_EQ(2u, num_bytes); - EXPECT_EQ('x', static_cast<const char*>(read_ptr)[0]); - EXPECT_EQ('y', static_cast<const char*>(read_ptr)[1]); - - // End reading. - EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(2u)); - - dp->ProducerClose(); - dp->ConsumerClose(); -} - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/local_message_pipe_endpoint.cc b/mojo/edk/system/local_message_pipe_endpoint.cc deleted file mode 100644 index 1800aa4..0000000 --- a/mojo/edk/system/local_message_pipe_endpoint.cc +++ /dev/null @@ -1,183 +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/edk/system/local_message_pipe_endpoint.h" - -#include <string.h> - -#include "base/logging.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/message_in_transit.h" - -namespace mojo { -namespace system { - -LocalMessagePipeEndpoint::LocalMessagePipeEndpoint( - MessageInTransitQueue* message_queue) - : is_open_(true), is_peer_open_(true) { - if (message_queue) - message_queue_.Swap(message_queue); -} - -LocalMessagePipeEndpoint::~LocalMessagePipeEndpoint() { - DCHECK(!is_open_); - DCHECK(message_queue_.IsEmpty()); // Should be implied by not being open. -} - -MessagePipeEndpoint::Type LocalMessagePipeEndpoint::GetType() const { - return kTypeLocal; -} - -bool LocalMessagePipeEndpoint::OnPeerClose() { - DCHECK(is_open_); - DCHECK(is_peer_open_); - - HandleSignalsState old_state = GetHandleSignalsState(); - is_peer_open_ = false; - HandleSignalsState new_state = GetHandleSignalsState(); - - if (!new_state.equals(old_state)) - awakable_list_.AwakeForStateChange(new_state); - - return true; -} - -void LocalMessagePipeEndpoint::EnqueueMessage( - scoped_ptr<MessageInTransit> message) { - DCHECK(is_open_); - DCHECK(is_peer_open_); - - bool was_empty = message_queue_.IsEmpty(); - message_queue_.AddMessage(message.Pass()); - if (was_empty) - awakable_list_.AwakeForStateChange(GetHandleSignalsState()); -} - -void LocalMessagePipeEndpoint::Close() { - DCHECK(is_open_); - is_open_ = false; - message_queue_.Clear(); -} - -void LocalMessagePipeEndpoint::CancelAllAwakables() { - DCHECK(is_open_); - awakable_list_.CancelAll(); -} - -MojoResult LocalMessagePipeEndpoint::ReadMessage( - UserPointer<void> bytes, - UserPointer<uint32_t> num_bytes, - DispatcherVector* dispatchers, - uint32_t* num_dispatchers, - MojoReadMessageFlags flags) { - DCHECK(is_open_); - DCHECK(!dispatchers || dispatchers->empty()); - - const uint32_t max_bytes = num_bytes.IsNull() ? 0 : num_bytes.Get(); - const uint32_t max_num_dispatchers = num_dispatchers ? *num_dispatchers : 0; - - if (message_queue_.IsEmpty()) { - return is_peer_open_ ? MOJO_RESULT_SHOULD_WAIT - : MOJO_RESULT_FAILED_PRECONDITION; - } - - // TODO(vtl): If |flags & MOJO_READ_MESSAGE_FLAG_MAY_DISCARD|, we could pop - // and release the lock immediately. - bool enough_space = true; - MessageInTransit* message = message_queue_.PeekMessage(); - if (!num_bytes.IsNull()) - num_bytes.Put(message->num_bytes()); - if (message->num_bytes() <= max_bytes) - bytes.PutArray(message->bytes(), message->num_bytes()); - else - enough_space = false; - - if (DispatcherVector* queued_dispatchers = message->dispatchers()) { - if (num_dispatchers) - *num_dispatchers = static_cast<uint32_t>(queued_dispatchers->size()); - if (enough_space) { - if (queued_dispatchers->empty()) { - // Nothing to do. - } else if (queued_dispatchers->size() <= max_num_dispatchers) { - DCHECK(dispatchers); - dispatchers->swap(*queued_dispatchers); - } else { - enough_space = false; - } - } - } else { - if (num_dispatchers) - *num_dispatchers = 0; - } - - message = nullptr; - - if (enough_space || (flags & MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)) { - message_queue_.DiscardMessage(); - - // Now it's empty, thus no longer readable. - if (message_queue_.IsEmpty()) { - // It's currently not possible to wait for non-readability, but we should - // do the state change anyway. - awakable_list_.AwakeForStateChange(GetHandleSignalsState()); - } - } - - if (!enough_space) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - - return MOJO_RESULT_OK; -} - -HandleSignalsState LocalMessagePipeEndpoint::GetHandleSignalsState() const { - HandleSignalsState rv; - if (!message_queue_.IsEmpty()) { - rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_READABLE; - rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE; - } - if (is_peer_open_) { - rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_WRITABLE; - rv.satisfiable_signals |= - MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE; - } else { - rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED; - } - rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED; - return rv; -} - -MojoResult LocalMessagePipeEndpoint::AddAwakable( - Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state) { - DCHECK(is_open_); - - HandleSignalsState state = GetHandleSignalsState(); - if (state.satisfies(signals)) { - if (signals_state) - *signals_state = state; - return MOJO_RESULT_ALREADY_EXISTS; - } - if (!state.can_satisfy(signals)) { - if (signals_state) - *signals_state = state; - return MOJO_RESULT_FAILED_PRECONDITION; - } - - awakable_list_.Add(awakable, signals, context); - return MOJO_RESULT_OK; -} - -void LocalMessagePipeEndpoint::RemoveAwakable( - Awakable* awakable, - HandleSignalsState* signals_state) { - DCHECK(is_open_); - awakable_list_.Remove(awakable); - if (signals_state) - *signals_state = GetHandleSignalsState(); -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/local_message_pipe_endpoint.h b/mojo/edk/system/local_message_pipe_endpoint.h deleted file mode 100644 index f792d90..0000000 --- a/mojo/edk/system/local_message_pipe_endpoint.h +++ /dev/null @@ -1,67 +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_EDK_SYSTEM_LOCAL_MESSAGE_PIPE_ENDPOINT_H_ -#define MOJO_EDK_SYSTEM_LOCAL_MESSAGE_PIPE_ENDPOINT_H_ - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "mojo/edk/system/awakable_list.h" -#include "mojo/edk/system/handle_signals_state.h" -#include "mojo/edk/system/message_in_transit_queue.h" -#include "mojo/edk/system/message_pipe_endpoint.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace system { - -class MOJO_SYSTEM_IMPL_EXPORT LocalMessagePipeEndpoint - : public MessagePipeEndpoint { - public: - // If |message_queue| is non-null, its contents will be taken as the queue of - // (already-received) messages. - explicit LocalMessagePipeEndpoint( - MessageInTransitQueue* message_queue = nullptr); - ~LocalMessagePipeEndpoint() override; - - // |MessagePipeEndpoint| implementation: - Type GetType() const override; - bool OnPeerClose() override; - void EnqueueMessage(scoped_ptr<MessageInTransit> message) override; - - // There's a dispatcher for |LocalMessagePipeEndpoint|s, so we have to - // implement/override these: - void Close() override; - void CancelAllAwakables() override; - MojoResult ReadMessage(UserPointer<void> bytes, - UserPointer<uint32_t> num_bytes, - DispatcherVector* dispatchers, - uint32_t* num_dispatchers, - MojoReadMessageFlags flags) override; - HandleSignalsState GetHandleSignalsState() const override; - MojoResult AddAwakable(Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state) override; - void RemoveAwakable(Awakable* awakable, - HandleSignalsState* signals_state) override; - - // This is only to be used by |MessagePipe|: - MessageInTransitQueue* message_queue() { return &message_queue_; } - - private: - bool is_open_; - bool is_peer_open_; - - // Queue of incoming messages. - MessageInTransitQueue message_queue_; - AwakableList awakable_list_; - - DISALLOW_COPY_AND_ASSIGN(LocalMessagePipeEndpoint); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_LOCAL_MESSAGE_PIPE_ENDPOINT_H_ diff --git a/mojo/edk/system/mapping_table.cc b/mojo/edk/system/mapping_table.cc deleted file mode 100644 index 693e8d7..0000000 --- a/mojo/edk/system/mapping_table.cc +++ /dev/null @@ -1,48 +0,0 @@ -// 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/edk/system/mapping_table.h" - -#include "base/logging.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/system/configuration.h" - -namespace mojo { -namespace system { - -MappingTable::MappingTable() { -} - -MappingTable::~MappingTable() { - // This should usually not be reached (the only instance should be owned by - // the singleton |Core|, which lives forever), except in tests. -} - -MojoResult MappingTable::AddMapping( - scoped_ptr<embedder::PlatformSharedBufferMapping> mapping) { - DCHECK(mapping); - - if (address_to_mapping_map_.size() >= - GetConfiguration().max_mapping_table_sze) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - - uintptr_t address = reinterpret_cast<uintptr_t>(mapping->GetBase()); - DCHECK(address_to_mapping_map_.find(address) == - address_to_mapping_map_.end()); - address_to_mapping_map_[address] = mapping.release(); - return MOJO_RESULT_OK; -} - -MojoResult MappingTable::RemoveMapping(uintptr_t address) { - AddressToMappingMap::iterator it = address_to_mapping_map_.find(address); - if (it == address_to_mapping_map_.end()) - return MOJO_RESULT_INVALID_ARGUMENT; - embedder::PlatformSharedBufferMapping* mapping_to_delete = it->second; - address_to_mapping_map_.erase(it); - delete mapping_to_delete; - return MOJO_RESULT_OK; -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/mapping_table.h b/mojo/edk/system/mapping_table.h deleted file mode 100644 index d2812d8..0000000 --- a/mojo/edk/system/mapping_table.h +++ /dev/null @@ -1,62 +0,0 @@ -// 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_EDK_SYSTEM_MAPPING_TABLE_H_ -#define MOJO_EDK_SYSTEM_MAPPING_TABLE_H_ - -#include <stdint.h> - -#include <vector> - -#include "base/containers/hash_tables.h" -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { - -namespace embedder { -class PlatformSharedBufferMapping; -} - -namespace system { - -class Core; - -// Test-only function (defined/used in embedder/test_embedder.cc). Declared here -// so it can be friended. -namespace internal { -bool ShutdownCheckNoLeaks(Core*); -} - -// This class provides the (global) table of memory mappings (owned by |Core|), -// which maps mapping base addresses to |PlatformSharedBufferMapping|s. -// -// This class is NOT thread-safe; locking is left to |Core|. -class MOJO_SYSTEM_IMPL_EXPORT MappingTable { - public: - MappingTable(); - ~MappingTable(); - - // Tries to add a mapping. (Takes ownership of the mapping in all cases; on - // failure, it will be destroyed.) - MojoResult AddMapping( - scoped_ptr<embedder::PlatformSharedBufferMapping> mapping); - MojoResult RemoveMapping(uintptr_t address); - - private: - friend bool internal::ShutdownCheckNoLeaks(Core*); - - typedef base::hash_map<uintptr_t, embedder::PlatformSharedBufferMapping*> - AddressToMappingMap; - AddressToMappingMap address_to_mapping_map_; - - DISALLOW_COPY_AND_ASSIGN(MappingTable); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_MAPPING_TABLE_H_ diff --git a/mojo/edk/system/memory.cc b/mojo/edk/system/memory.cc deleted file mode 100644 index 0e3d9a9..0000000 --- a/mojo/edk/system/memory.cc +++ /dev/null @@ -1,89 +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/edk/system/memory.h" - -#include <limits> - -#include "base/logging.h" -#include "build/build_config.h" - -namespace mojo { -namespace system { -namespace internal { - -template <size_t alignment> -bool IsAligned(const void* pointer) { - return reinterpret_cast<uintptr_t>(pointer) % alignment == 0; -} - -// MSVS (2010, 2013) sometimes (on the stack) aligns, e.g., |int64_t|s (for -// which |__alignof(int64_t)| is 8) to 4-byte boundaries. http://goo.gl/Y2n56T -#if defined(COMPILER_MSVC) && defined(ARCH_CPU_32_BITS) -template <> -bool IsAligned<8>(const void* pointer) { - return reinterpret_cast<uintptr_t>(pointer) % 4 == 0; -} -#endif - -template <size_t size, size_t alignment> -void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointer(const void* pointer) { - CHECK(pointer && IsAligned<alignment>(pointer)); -} - -// Explicitly instantiate the sizes we need. Add instantiations as needed. -template void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointer<1, 1>(const void*); -template void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointer<4, 4>(const void*); -template void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointer<8, 4>(const void*); -template void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointer<8, 8>(const void*); - -template <size_t size, size_t alignment> -void MOJO_SYSTEM_IMPL_EXPORT -CheckUserPointerWithCount(const void* pointer, size_t count) { - CHECK_LE(count, std::numeric_limits<size_t>::max() / size); - CHECK(count == 0 || (pointer && IsAligned<alignment>(pointer))); -} - -// Explicitly instantiate the sizes we need. Add instantiations as needed. -template void MOJO_SYSTEM_IMPL_EXPORT -CheckUserPointerWithCount<1, 1>(const void*, size_t); -template void MOJO_SYSTEM_IMPL_EXPORT -CheckUserPointerWithCount<4, 4>(const void*, size_t); -template void MOJO_SYSTEM_IMPL_EXPORT -CheckUserPointerWithCount<8, 4>(const void*, size_t); -template void MOJO_SYSTEM_IMPL_EXPORT -CheckUserPointerWithCount<8, 8>(const void*, size_t); - -template <size_t alignment> -void CheckUserPointerWithSize(const void* pointer, size_t size) { - // TODO(vtl): If running in kernel mode, do a full verification. For now, just - // check that it's non-null and aligned. (A faster user mode implementation is - // also possible if this check is skipped.) - CHECK(size == 0 || (!!pointer && internal::IsAligned<alignment>(pointer))); -} - -// Explicitly instantiate the sizes we need. Add instantiations as needed. -template void MOJO_SYSTEM_IMPL_EXPORT -CheckUserPointerWithSize<1>(const void*, size_t); -template void MOJO_SYSTEM_IMPL_EXPORT -CheckUserPointerWithSize<4>(const void*, size_t); -// Whereas the other |Check...()| functions are usually used with integral typs -// or arrays of integral types, this one is used with Options structs for which -// alignment has been explicitly been specified (using |MOJO_ALIGNAS()|), which -// MSVS *does* respect. -#if defined(COMPILER_MSVC) && defined(ARCH_CPU_32_BITS) -template <> -void MOJO_SYSTEM_IMPL_EXPORT -CheckUserPointerWithSize<8>(const void* pointer, size_t size) { - CHECK(size == 0 || - (!!pointer && reinterpret_cast<uintptr_t>(pointer) % 8 == 0)); -} -#else -template void MOJO_SYSTEM_IMPL_EXPORT -CheckUserPointerWithSize<8>(const void*, size_t); -#endif - -} // namespace internal -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/memory.h b/mojo/edk/system/memory.h deleted file mode 100644 index 96cf219..0000000 --- a/mojo/edk/system/memory.h +++ /dev/null @@ -1,377 +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_EDK_SYSTEM_MEMORY_H_ -#define MOJO_EDK_SYSTEM_MEMORY_H_ - -#include <stddef.h> -#include <stdint.h> -#include <string.h> // For |memcpy()|. - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/macros.h" - -namespace mojo { -namespace system { - -namespace internal { - -// Removes |const| from |T| (available as |remove_const<T>::type|): -// TODO(vtl): Remove these once we have the C++11 |remove_const|. -template <typename T> -struct remove_const { - typedef T type; -}; -template <typename T> -struct remove_const<const T> { - typedef T type; -}; - -// Yields |(const) char| if |T| is |(const) void|, else |T|: -template <typename T> -struct VoidToChar { - typedef T type; -}; -template <> -struct VoidToChar<void> { - typedef char type; -}; -template <> -struct VoidToChar<const void> { - typedef const char type; -}; - -// Checks (insofar as appropriate/possible) that |pointer| is a valid pointer to -// a buffer of the given size and alignment (both in bytes). -template <size_t size, size_t alignment> -void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointer(const void* pointer); - -// Checks (insofar as appropriate/possible) that |pointer| is a valid pointer to -// a buffer of |count| elements of the given size and alignment (both in bytes). -template <size_t size, size_t alignment> -void MOJO_SYSTEM_IMPL_EXPORT -CheckUserPointerWithCount(const void* pointer, size_t count); - -// Checks (insofar as appropriate/possible) that |pointer| is a valid pointer to -// a buffer of the given size and alignment (both in bytes). -template <size_t alignment> -void MOJO_SYSTEM_IMPL_EXPORT -CheckUserPointerWithSize(const void* pointer, size_t size); - -} // namespace internal - -// Forward declarations so that they can be friended. -template <typename Type> -class UserPointerReader; -template <typename Type> -class UserPointerWriter; -template <typename Type> -class UserPointerReaderWriter; -template <class Options> -class UserOptionsReader; - -// Provides a convenient way to implicitly get null |UserPointer<Type>|s. -struct NullUserPointer {}; - -// Represents a user pointer to a single |Type| (which must be POD), for Mojo -// primitive parameters. -// -// Use a const |Type| for in parameters, and non-const |Type|s for out and -// in-out parameters (in which case the |Put()| method is available). -template <typename Type> -class UserPointer { - private: - typedef typename internal::VoidToChar<Type>::type NonVoidType; - - public: - // Instead of explicitly using these constructors, you can often use - // |MakeUserPointer()| (or |NullUserPointer()| for null pointers). (The common - // exception is when you have, e.g., a |char*| and want to get a - // |UserPointer<void>|.) - UserPointer() : pointer_(nullptr) {} - explicit UserPointer(Type* pointer) : pointer_(pointer) {} - // Allow implicit conversion from the "null user pointer". - UserPointer(NullUserPointer) : pointer_(nullptr) {} - ~UserPointer() {} - - // Allow assignment from the "null user pointer". - UserPointer<Type>& operator=(NullUserPointer) { - pointer_ = nullptr; - return *this; - } - - // Allow conversion to a "non-const" |UserPointer|. - operator UserPointer<const Type>() const { - return UserPointer<const Type>(pointer_); - } - - bool IsNull() const { return !pointer_; } - - // "Reinterpret casts" to a |UserPointer<ToType>|. - template <typename ToType> - UserPointer<ToType> ReinterpretCast() const { - return UserPointer<ToType>(reinterpret_cast<ToType*>(pointer_)); - } - - // Checks that this pointer points to a valid |Type| in the same way as - // |Get()| and |Put()|. - // TODO(vtl): Logically, there should be separate read checks and write - // checks. - void Check() const { - internal::CheckUserPointer<sizeof(NonVoidType), MOJO_ALIGNOF(NonVoidType)>( - pointer_); - } - - // Checks that this pointer points to a valid array (of type |Type|, or just a - // buffer if |Type| is |void| or |const void|) of |count| elements (or bytes - // if |Type| is |void| or |const void|) in the same way as |GetArray()| and - // |PutArray()|. - // TODO(vtl): Logically, there should be separate read checks and write - // checks. - // TODO(vtl): Switch more things to use this. - void CheckArray(size_t count) const { - internal::CheckUserPointerWithCount<sizeof(NonVoidType), - MOJO_ALIGNOF(NonVoidType)>(pointer_, - count); - } - - // Gets the value (of type |Type|, or a |char| if |Type| is |void|) pointed to - // by this user pointer. Use this when you'd use the rvalue |*user_pointer|, - // but be aware that this may be costly -- so if the value will be used - // multiple times, you should save it. - // - // (We want to force a copy here, so return |Type| not |const Type&|.) - NonVoidType Get() const { - Check(); - internal::CheckUserPointer<sizeof(NonVoidType), MOJO_ALIGNOF(NonVoidType)>( - pointer_); - return *pointer_; - } - - // Gets an array (of type |Type|, or just a buffer if |Type| is |void| or - // |const void|) of |count| elements (or bytes if |Type| is |void| or |const - // void|) from the location pointed to by this user pointer. Use this when - // you'd do something like |memcpy(destination, user_pointer, count * - // sizeof(Type)|. - void GetArray(typename internal::remove_const<Type>::type* destination, - size_t count) const { - CheckArray(count); - memcpy(destination, pointer_, count * sizeof(NonVoidType)); - } - - // Puts a value (of type |Type|, or of type |char| if |Type| is |void|) to the - // location pointed to by this user pointer. Use this when you'd use the - // lvalue |*user_pointer|. Since this may be costly, you should avoid using - // this (for the same user pointer) more than once. - // - // Note: This |Put()| method is not valid when |T| is const, e.g., |const - // uint32_t|, but it's okay to include them so long as this template is only - // implicitly instantiated (see 14.7.1 of the C++11 standard) and not - // explicitly instantiated. (On implicit instantiation, only the declarations - // need be valid, not the definitions.) - // - // In C++11, we could do something like: - // template <typename _Type = Type> - // typename enable_if<!is_const<_Type>::value && - // !is_void<_Type>::value>::type Put( - // const _Type& value) { ... } - // (which obviously be correct), but C++03 doesn't allow default function - // template arguments. - void Put(const NonVoidType& value) { - Check(); - *pointer_ = value; - } - - // Puts an array (of type |Type|, or just a buffer if |Type| is |void|) with - // |count| elements (or bytes |Type| is |void|) to the location pointed to by - // this user pointer. Use this when you'd do something like - // |memcpy(user_pointer, source, count * sizeof(Type))|. - // - // Note: The same comments about the validity of |Put()| (except for the part - // about |void|) apply here. - void PutArray(const Type* source, size_t count) { - CheckArray(count); - memcpy(pointer_, source, count * sizeof(NonVoidType)); - } - - // Gets a |UserPointer| at offset |i| (in |Type|s) relative to this. - UserPointer At(size_t i) const { - return UserPointer( - static_cast<Type*>(static_cast<NonVoidType*>(pointer_) + i)); - } - - // Gets the value of the |UserPointer| as a |uintptr_t|. This should not be - // casted back to a pointer (and dereferenced), but may be used as a key for - // lookup or passed back to the user. - uintptr_t GetPointerValue() const { - return reinterpret_cast<uintptr_t>(pointer_); - } - - // These provides safe (read-only/write-only/read-and-write) access to a - // |UserPointer<Type>| (probably pointing to an array) using just an ordinary - // pointer (obtained via |GetPointer()|). - // - // The memory returned by |GetPointer()| may be a copy of the original user - // memory, but should be modified only if the user is intended to eventually - // see the change.) If any changes are made, |Commit()| should be called to - // guarantee that the changes are written back to user memory (it may be - // called multiple times). - // - // Note: These classes are designed to allow fast, unsafe implementations (in - // which |GetPointer()| just returns the user pointer) if desired. Thus if - // |Commit()| is *not* called, changes may or may not be made visible to the - // user. - // - // Use these classes in the following way: - // - // MojoResult Core::PutFoos(UserPointer<const uint32_t> foos, - // uint32_t num_foos) { - // UserPointer<const uint32_t>::Reader foos_reader(foos, num_foos); - // return PutFoosImpl(foos_reader.GetPointer(), num_foos); - // } - // - // MojoResult Core::GetFoos(UserPointer<uint32_t> foos, - // uint32_t num_foos) { - // UserPointer<uint32_t>::Writer foos_writer(foos, num_foos); - // MojoResult rv = GetFoosImpl(foos.GetPointer(), num_foos); - // foos_writer.Commit(); - // return rv; - // } - // - // TODO(vtl): Possibly, since we're not really being safe, we should just not - // copy for Release builds. - typedef UserPointerReader<Type> Reader; - typedef UserPointerWriter<Type> Writer; - typedef UserPointerReaderWriter<Type> ReaderWriter; - - private: - friend class UserPointerReader<Type>; - friend class UserPointerReader<const Type>; - friend class UserPointerWriter<Type>; - friend class UserPointerReaderWriter<Type>; - template <class Options> - friend class UserOptionsReader; - - Type* pointer_; - // Allow copy and assignment. -}; - -// Provides a convenient way to make a |UserPointer<Type>|. -template <typename Type> -inline UserPointer<Type> MakeUserPointer(Type* pointer) { - return UserPointer<Type>(pointer); -} - -// Implementation of |UserPointer<Type>::Reader|. -template <typename Type> -class UserPointerReader { - private: - typedef typename internal::remove_const<Type>::type TypeNoConst; - - public: - // Note: If |count| is zero, |GetPointer()| will always return null. - UserPointerReader(UserPointer<const Type> user_pointer, size_t count) { - Init(user_pointer.pointer_, count, true); - } - UserPointerReader(UserPointer<TypeNoConst> user_pointer, size_t count) { - Init(user_pointer.pointer_, count, true); - } - - const Type* GetPointer() const { return buffer_.get(); } - - private: - template <class Options> - friend class UserOptionsReader; - - struct NoCheck {}; - UserPointerReader(NoCheck, - UserPointer<const Type> user_pointer, - size_t count) { - Init(user_pointer.pointer_, count, false); - } - - void Init(const Type* user_pointer, size_t count, bool check) { - if (count == 0) - return; - - if (check) { - internal::CheckUserPointerWithCount<sizeof(Type), MOJO_ALIGNOF(Type)>( - user_pointer, count); - } - buffer_.reset(new TypeNoConst[count]); - memcpy(buffer_.get(), user_pointer, count * sizeof(Type)); - } - - scoped_ptr<TypeNoConst[]> buffer_; - - DISALLOW_COPY_AND_ASSIGN(UserPointerReader); -}; - -// Implementation of |UserPointer<Type>::Writer|. -template <typename Type> -class UserPointerWriter { - public: - // Note: If |count| is zero, |GetPointer()| will always return null. - UserPointerWriter(UserPointer<Type> user_pointer, size_t count) - : user_pointer_(user_pointer), count_(count) { - if (count_ > 0) { - buffer_.reset(new Type[count_]); - memset(buffer_.get(), 0, count_ * sizeof(Type)); - } - } - - Type* GetPointer() const { return buffer_.get(); } - - void Commit() { - internal::CheckUserPointerWithCount<sizeof(Type), MOJO_ALIGNOF(Type)>( - user_pointer_.pointer_, count_); - memcpy(user_pointer_.pointer_, buffer_.get(), count_ * sizeof(Type)); - } - - private: - UserPointer<Type> user_pointer_; - size_t count_; - scoped_ptr<Type[]> buffer_; - - DISALLOW_COPY_AND_ASSIGN(UserPointerWriter); -}; - -// Implementation of |UserPointer<Type>::ReaderWriter|. -template <typename Type> -class UserPointerReaderWriter { - public: - // Note: If |count| is zero, |GetPointer()| will always return null. - UserPointerReaderWriter(UserPointer<Type> user_pointer, size_t count) - : user_pointer_(user_pointer), count_(count) { - if (count_ > 0) { - internal::CheckUserPointerWithCount<sizeof(Type), MOJO_ALIGNOF(Type)>( - user_pointer_.pointer_, count_); - buffer_.reset(new Type[count]); - memcpy(buffer_.get(), user_pointer.pointer_, count * sizeof(Type)); - } - } - - Type* GetPointer() const { return buffer_.get(); } - size_t GetCount() const { return count_; } - - void Commit() { - internal::CheckUserPointerWithCount<sizeof(Type), MOJO_ALIGNOF(Type)>( - user_pointer_.pointer_, count_); - memcpy(user_pointer_.pointer_, buffer_.get(), count_ * sizeof(Type)); - } - - private: - UserPointer<Type> user_pointer_; - size_t count_; - scoped_ptr<Type[]> buffer_; - - DISALLOW_COPY_AND_ASSIGN(UserPointerReaderWriter); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_MEMORY_H_ diff --git a/mojo/edk/system/memory_unittest.cc b/mojo/edk/system/memory_unittest.cc deleted file mode 100644 index 46515c0..0000000 --- a/mojo/edk/system/memory_unittest.cc +++ /dev/null @@ -1,294 +0,0 @@ -// 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/edk/system/memory.h" - -#include <stddef.h> -#include <stdint.h> - -#include <limits> - -#include "mojo/public/c/system/macros.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -TEST(MemoryTest, Valid) { - char my_char; - int32_t my_int32; - int64_t my_int64_array[5] = {}; // Zero initialize. - - UserPointer<char> my_char_ptr(&my_char); - UserPointer<int32_t> my_int32_ptr(&my_int32); - UserPointer<int64_t> my_int64_array_ptr(my_int64_array); - - // |UserPointer<>::IsNull()|: - EXPECT_FALSE(my_char_ptr.IsNull()); - EXPECT_FALSE(my_int32_ptr.IsNull()); - EXPECT_FALSE(my_int64_array_ptr.IsNull()); - - // |UserPointer<>::Put()| and |UserPointer<>::Get()|: - my_char_ptr.Put('x'); - EXPECT_EQ('x', my_char); - EXPECT_EQ('x', my_char_ptr.Get()); - my_int32_ptr.Put(123); - EXPECT_EQ(123, my_int32); - EXPECT_EQ(123, my_int32_ptr.Get()); - my_int64_array_ptr.Put(456); - EXPECT_EQ(456, my_int64_array[0]); - EXPECT_EQ(456, my_int64_array_ptr.Get()); - - // |UserPointer<>::At()|, etc.: - my_int64_array_ptr.At(3).Put(789); - EXPECT_EQ(789, my_int64_array[3]); - { - // Copy construction: - UserPointer<int64_t> other(my_int64_array_ptr.At(3)); - EXPECT_FALSE(other.IsNull()); - EXPECT_EQ(789, other.Get()); - - // Assignment: - other = my_int64_array_ptr; - EXPECT_FALSE(other.IsNull()); - EXPECT_EQ(456, other.Get()); - - // Assignment to |NullUserPointer()|: - other = NullUserPointer(); - EXPECT_TRUE(other.IsNull()); - - // |MakeUserPointer()|: - other = MakeUserPointer(&my_int64_array[1]); - other.Put(-123); - EXPECT_EQ(-123, my_int64_array_ptr.At(1).Get()); - } - - // "const" |UserPointer<>|: - { - // Explicit constructor from |NullUserPointer()|: - UserPointer<const char> other((NullUserPointer())); - EXPECT_TRUE(other.IsNull()); - - // Conversion to "const": - other = my_char_ptr; - EXPECT_EQ('x', other.Get()); - } - - // Default constructor: - { - UserPointer<int32_t> other; - EXPECT_TRUE(other.IsNull()); - - other = my_int32_ptr; - other.Put(-456); - EXPECT_EQ(-456, my_int32_ptr.Get()); - } - - // |UserPointer<>::CheckArray()|: - my_int64_array_ptr.CheckArray(5); - - // |UserPointer<>::GetArray()|: - { - // From a "const" |UserPointer<>| (why not?): - UserPointer<const int64_t> other(my_int64_array_ptr); - int64_t array[3] = {1, 2, 3}; - other.At(1).GetArray(array, 3); - EXPECT_EQ(-123, array[0]); - EXPECT_EQ(0, array[1]); - EXPECT_EQ(789, array[2]); - } - - // |UserPointer<>::PutArray()|: - { - const int64_t array[2] = {654, 321}; - my_int64_array_ptr.At(3).PutArray(array, 2); - EXPECT_EQ(0, my_int64_array[2]); - EXPECT_EQ(654, my_int64_array[3]); - EXPECT_EQ(321, my_int64_array[4]); - } - - // |UserPointer<>::Reader|: - { - UserPointer<int64_t>::Reader reader(my_int64_array_ptr, 5); - EXPECT_EQ(456, reader.GetPointer()[0]); - EXPECT_EQ(321, reader.GetPointer()[4]); - } - - // Non-const to const: - { - UserPointer<const int64_t>::Reader reader(my_int64_array_ptr.At(3), 1); - const int64_t* ptr = reader.GetPointer(); - EXPECT_EQ(654, *ptr); - } - - // |UserPointer<>::Writer|: - { - UserPointer<int64_t>::Writer writer(my_int64_array_ptr.At(2), 1); - int64_t* ptr = writer.GetPointer(); - *ptr = 1234567890123LL; - writer.Commit(); - EXPECT_EQ(1234567890123LL, my_int64_array[2]); - } - - // |UserPointer<>::ReaderWriter|: - { - UserPointer<int32_t>::ReaderWriter reader_writer(my_int32_ptr, 1); - int32_t* ptr = reader_writer.GetPointer(); - EXPECT_EQ(-456, *ptr); - *ptr = 42; - reader_writer.Commit(); - EXPECT_EQ(42, my_int32); - } - - // |UserPointer<>::ReinterpretCast<>|: - // (This assumes little-endian, etc.) - { - UserPointer<const char> other(my_int32_ptr.ReinterpretCast<char>()); - EXPECT_EQ(42, other.Get()); - EXPECT_EQ(0, other.At(1).Get()); - EXPECT_EQ(0, other.At(2).Get()); - EXPECT_EQ(0, other.At(3).Get()); - } - - // |UserPointer<>::GetPointerValue()|: - { - UserPointer<int32_t> other; - EXPECT_EQ(0u, other.GetPointerValue()); - other = my_int32_ptr; - EXPECT_EQ(reinterpret_cast<uintptr_t>(&my_int32), other.GetPointerValue()); - } -} - -TEST(MemoryTest, InvalidDeath) { - const char kMemoryCheckFailedRegex[] = "Check failed"; - - // Note: |Check...()| are defined to be "best effort" checks (and may always - // return true). Thus these tests of invalid cases only reflect the current - // implementation. - - // These tests depend on |int32_t| and |int64_t| having nontrivial alignment. - static_assert(MOJO_ALIGNOF(int32_t) != 1, - "int32_t does not require nontrivial alignment"); - static_assert(MOJO_ALIGNOF(int64_t) != 1, - "int64_t does not require nontrivial alignment"); - - // Null: - { - UserPointer<char> ptr(nullptr); - char array[5] = {}; - EXPECT_DEATH_IF_SUPPORTED(ptr.Check(), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.Get(), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.Put('x'), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(5), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(array, 5), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.PutArray(array, 5), kMemoryCheckFailedRegex); - } - { - UserPointer<int32_t> ptr(nullptr); - int32_t array[5] = {}; - EXPECT_DEATH_IF_SUPPORTED(ptr.Check(), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.Get(), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.Put(123), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(5), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(array, 5), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.PutArray(array, 5), kMemoryCheckFailedRegex); - } - { - UserPointer<int64_t> ptr(nullptr); - int64_t array[5] = {}; - EXPECT_DEATH_IF_SUPPORTED(ptr.Check(), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.Get(), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.Put(123), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(5), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(array, 5), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.PutArray(array, 5), kMemoryCheckFailedRegex); - } - // Also check a const pointer: - { - UserPointer<const int32_t> ptr(nullptr); - int32_t array[5] = {}; - EXPECT_DEATH_IF_SUPPORTED(ptr.Check(), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.Get(), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(5), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(array, 5), kMemoryCheckFailedRegex); - } - - // Unaligned: - { - int32_t x[10]; - UserPointer<int32_t> ptr( - reinterpret_cast<int32_t*>(reinterpret_cast<uintptr_t>(x) + 1)); - int32_t array[5] = {}; - EXPECT_DEATH_IF_SUPPORTED(ptr.Check(), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.Get(), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.Put(123), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(5), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(array, 5), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.PutArray(array, 5), kMemoryCheckFailedRegex); - } - { - int64_t x[10]; - UserPointer<int64_t> ptr( - reinterpret_cast<int64_t*>(reinterpret_cast<uintptr_t>(x) + 1)); - int64_t array[5] = {}; - EXPECT_DEATH_IF_SUPPORTED(ptr.Check(), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.Get(), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.Put(123), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(5), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(array, 5), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.PutArray(array, 5), kMemoryCheckFailedRegex); - } - // Also check a const pointer: - { - int32_t x[10]; - UserPointer<const int32_t> ptr( - reinterpret_cast<const int32_t*>(reinterpret_cast<uintptr_t>(x) + 1)); - int32_t array[5] = {}; - EXPECT_DEATH_IF_SUPPORTED(ptr.Check(), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.Get(), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(5), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(array, 5), kMemoryCheckFailedRegex); - } - - // Count too big: - { - const size_t kTooBig = - std::numeric_limits<size_t>::max() / sizeof(int32_t) + 1; - int32_t x = 0; - UserPointer<int32_t> ptr(&x); - EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(kTooBig), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(&x, kTooBig), - kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.PutArray(&x, kTooBig), - kMemoryCheckFailedRegex); - } - { - const size_t kTooBig = - std::numeric_limits<size_t>::max() / sizeof(int64_t) + 1; - int64_t x = 0; - UserPointer<int64_t> ptr(&x); - EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(kTooBig), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(&x, kTooBig), - kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.PutArray(&x, kTooBig), - kMemoryCheckFailedRegex); - } - // Also check a const pointer: - { - const size_t kTooBig = - std::numeric_limits<size_t>::max() / sizeof(int32_t) + 1; - int32_t x = 0; - UserPointer<const int32_t> ptr(&x); - EXPECT_DEATH_IF_SUPPORTED(ptr.CheckArray(kTooBig), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(ptr.GetArray(&x, kTooBig), - kMemoryCheckFailedRegex); - } - - // TODO(vtl): Tests for |UserPointer{Reader,Writer,ReaderWriter}|. -} - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/message_in_transit.cc b/mojo/edk/system/message_in_transit.cc deleted file mode 100644 index 68919f2..0000000 --- a/mojo/edk/system/message_in_transit.cc +++ /dev/null @@ -1,223 +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/edk/system/message_in_transit.h" - -#include <string.h> - -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "mojo/edk/system/configuration.h" -#include "mojo/edk/system/transport_data.h" - -namespace mojo { -namespace system { - -STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Type - MessageInTransit::kTypeEndpoint; -STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Type - MessageInTransit::kTypeChannel; -STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Type - MessageInTransit::kTypeRawChannel; -STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype - MessageInTransit::kSubtypeEndpointData; -STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype - MessageInTransit::kSubtypeChannelAttachAndRunEndpoint; -STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype - MessageInTransit::kSubtypeChannelRemoveEndpoint; -STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype - MessageInTransit::kSubtypeChannelRemoveEndpointAck; -STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype - MessageInTransit::kSubtypeRawChannelPosixExtraPlatformHandles; -STATIC_CONST_MEMBER_DEFINITION const size_t MessageInTransit::kMessageAlignment; - -struct MessageInTransit::PrivateStructForCompileAsserts { - // The size of |Header| must be a multiple of the alignment. - static_assert(sizeof(Header) % kMessageAlignment == 0, - "sizeof(MessageInTransit::Header) invalid"); -}; - -MessageInTransit::View::View(size_t message_size, const void* buffer) - : buffer_(buffer) { - size_t next_message_size = 0; - DCHECK(MessageInTransit::GetNextMessageSize(buffer_, message_size, - &next_message_size)); - DCHECK_EQ(message_size, next_message_size); - // This should be equivalent. - DCHECK_EQ(message_size, total_size()); -} - -bool MessageInTransit::View::IsValid(size_t serialized_platform_handle_size, - const char** error_message) const { - size_t max_message_num_bytes = GetConfiguration().max_message_num_bytes; - // Avoid dangerous situations, but making sure that the size of the "header" + - // the size of the data fits into a 31-bit number. - DCHECK_LE(static_cast<uint64_t>(sizeof(Header)) + max_message_num_bytes, - 0x7fffffffULL) - << "GetConfiguration().max_message_num_bytes too big"; - - // We assume (to avoid extra rounding code) that the maximum message (data) - // size is a multiple of the alignment. - DCHECK_EQ(max_message_num_bytes % kMessageAlignment, 0U) - << "GetConfiguration().max_message_num_bytes not a multiple of alignment"; - - // Note: This also implies a check on the |main_buffer_size()|, which is just - // |RoundUpMessageAlignment(sizeof(Header) + num_bytes())|. - if (num_bytes() > max_message_num_bytes) { - *error_message = "Message data payload too large"; - return false; - } - - if (transport_data_buffer_size() > 0) { - const char* e = TransportData::ValidateBuffer( - serialized_platform_handle_size, transport_data_buffer(), - transport_data_buffer_size()); - if (e) { - *error_message = e; - return false; - } - } - - return true; -} - -MessageInTransit::MessageInTransit(Type type, - Subtype subtype, - uint32_t num_bytes, - const void* bytes) - : main_buffer_size_(RoundUpMessageAlignment(sizeof(Header) + num_bytes)), - main_buffer_(static_cast<char*>( - base::AlignedAlloc(main_buffer_size_, kMessageAlignment))) { - ConstructorHelper(type, subtype, num_bytes); - if (bytes) { - memcpy(MessageInTransit::bytes(), bytes, num_bytes); - memset(static_cast<char*>(MessageInTransit::bytes()) + num_bytes, 0, - main_buffer_size_ - sizeof(Header) - num_bytes); - } else { - memset(MessageInTransit::bytes(), 0, main_buffer_size_ - sizeof(Header)); - } -} - -MessageInTransit::MessageInTransit(Type type, - Subtype subtype, - uint32_t num_bytes, - UserPointer<const void> bytes) - : main_buffer_size_(RoundUpMessageAlignment(sizeof(Header) + num_bytes)), - main_buffer_(static_cast<char*>( - base::AlignedAlloc(main_buffer_size_, kMessageAlignment))) { - ConstructorHelper(type, subtype, num_bytes); - bytes.GetArray(MessageInTransit::bytes(), num_bytes); - memset(static_cast<char*>(MessageInTransit::bytes()) + num_bytes, 0, - main_buffer_size_ - sizeof(Header) - num_bytes); -} - -MessageInTransit::MessageInTransit(const View& message_view) - : main_buffer_size_(message_view.main_buffer_size()), - main_buffer_(static_cast<char*>( - base::AlignedAlloc(main_buffer_size_, kMessageAlignment))) { - DCHECK_GE(main_buffer_size_, sizeof(Header)); - DCHECK_EQ(main_buffer_size_ % kMessageAlignment, 0u); - - memcpy(main_buffer_.get(), message_view.main_buffer(), main_buffer_size_); - DCHECK_EQ(main_buffer_size_, - RoundUpMessageAlignment(sizeof(Header) + num_bytes())); -} - -MessageInTransit::~MessageInTransit() { - if (dispatchers_) { - for (size_t i = 0; i < dispatchers_->size(); i++) { - if (!(*dispatchers_)[i]) - continue; - - DCHECK((*dispatchers_)[i]->HasOneRef()); - (*dispatchers_)[i]->Close(); - } - } -} - -// static -bool MessageInTransit::GetNextMessageSize(const void* buffer, - size_t buffer_size, - size_t* next_message_size) { - DCHECK(next_message_size); - if (!buffer_size) - return false; - DCHECK(buffer); - DCHECK_EQ( - reinterpret_cast<uintptr_t>(buffer) % MessageInTransit::kMessageAlignment, - 0u); - - if (buffer_size < sizeof(Header)) - return false; - - const Header* header = static_cast<const Header*>(buffer); - *next_message_size = header->total_size; - DCHECK_EQ(*next_message_size % kMessageAlignment, 0u); - return true; -} - -void MessageInTransit::SetDispatchers( - scoped_ptr<DispatcherVector> dispatchers) { - DCHECK(dispatchers); - DCHECK(!dispatchers_); - DCHECK(!transport_data_); - - dispatchers_ = dispatchers.Pass(); -#ifndef NDEBUG - for (size_t i = 0; i < dispatchers_->size(); i++) - DCHECK(!(*dispatchers_)[i] || (*dispatchers_)[i]->HasOneRef()); -#endif -} - -void MessageInTransit::SetTransportData( - scoped_ptr<TransportData> transport_data) { - DCHECK(transport_data); - DCHECK(!transport_data_); - DCHECK(!dispatchers_); - - transport_data_ = transport_data.Pass(); - UpdateTotalSize(); -} - -void MessageInTransit::SerializeAndCloseDispatchers(Channel* channel) { - DCHECK(channel); - DCHECK(!transport_data_); - - if (!dispatchers_ || !dispatchers_->size()) - return; - - transport_data_.reset(new TransportData(dispatchers_.Pass(), channel)); - - // Update the sizes in the message header. - UpdateTotalSize(); -} - -void MessageInTransit::ConstructorHelper(Type type, - Subtype subtype, - uint32_t num_bytes) { - DCHECK_LE(num_bytes, GetConfiguration().max_message_num_bytes); - - // |total_size| is updated below, from the other values. - header()->type = type; - header()->subtype = subtype; - header()->source_id = ChannelEndpointId(); - header()->destination_id = ChannelEndpointId(); - header()->num_bytes = num_bytes; - header()->unused = 0; - // Note: If dispatchers are subsequently attached, then |total_size| will have - // to be adjusted. - UpdateTotalSize(); -} - -void MessageInTransit::UpdateTotalSize() { - DCHECK_EQ(main_buffer_size_ % kMessageAlignment, 0u); - header()->total_size = static_cast<uint32_t>(main_buffer_size_); - if (transport_data_) { - header()->total_size += - static_cast<uint32_t>(transport_data_->buffer_size()); - } -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/message_in_transit.h b/mojo/edk/system/message_in_transit.h deleted file mode 100644 index 023362a..0000000 --- a/mojo/edk/system/message_in_transit.h +++ /dev/null @@ -1,265 +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_EDK_SYSTEM_MESSAGE_IN_TRANSIT_H_ -#define MOJO_EDK_SYSTEM_MESSAGE_IN_TRANSIT_H_ - -#include <stddef.h> -#include <stdint.h> - -#include <vector> - -#include "base/macros.h" -#include "base/memory/aligned_memory.h" -#include "base/memory/scoped_ptr.h" -#include "mojo/edk/system/channel_endpoint_id.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/memory.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace system { - -class Channel; -class TransportData; - -// This class is used to represent data in transit. It is thread-unsafe. -// -// |MessageInTransit| buffers: -// -// A |MessageInTransit| can be serialized by writing the main buffer and then, -// if it has one, the transport data buffer. Both buffers are -// |kMessageAlignment|-byte aligned and a multiple of |kMessageAlignment| bytes -// in size. -// -// The main buffer consists of the header (of type |Header|, which is an -// internal detail of this class) followed immediately by the message data -// (accessed by |bytes()| and of size |num_bytes()|, and also -// |kMessageAlignment|-byte aligned), and then any padding needed to make the -// main buffer a multiple of |kMessageAlignment| bytes in size. -// -// See |TransportData| for a description of the (serialized) transport data -// buffer. -class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit { - public: - typedef uint16_t Type; - // Messages that are forwarded to endpoints. - static const Type kTypeEndpoint = 0; - // Messages that are consumed by the |Channel|. - static const Type kTypeChannel = 1; - // Messages that are consumed by the |RawChannel| (implementation). - static const Type kTypeRawChannel = 2; - - typedef uint16_t Subtype; - // Subtypes for type |kTypeEndpoint|: - static const Subtype kSubtypeEndpointData = 0; - // Subtypes for type |kTypeChannel|: - static const Subtype kSubtypeChannelAttachAndRunEndpoint = 0; - static const Subtype kSubtypeChannelRemoveEndpoint = 1; - static const Subtype kSubtypeChannelRemoveEndpointAck = 2; - - // Subtypes for type |kTypeRawChannel|: - static const Subtype kSubtypeRawChannelPosixExtraPlatformHandles = 0; - - // Messages (the header and data) must always be aligned to a multiple of this - // quantity (which must be a power of 2). - static const size_t kMessageAlignment = 8; - - // Forward-declare |Header| so that |View| can use it: - private: - struct Header; - - public: - // This represents a view of serialized message data in a raw buffer. - class MOJO_SYSTEM_IMPL_EXPORT View { - public: - // Constructs a view from the given buffer of the given size. (The size must - // be as provided by |MessageInTransit::GetNextMessageSize()|.) The buffer - // must remain alive/unmodified through the lifetime of this object. - // |buffer| should be |kMessageAlignment|-byte aligned. - View(size_t message_size, const void* buffer); - - // Checks that the given |View| appears to be for a valid message, within - // predetermined limits (e.g., |num_bytes()| and |main_buffer_size()|, that - // |transport_data_buffer()|/|transport_data_buffer_size()| is for valid - // transport data -- see |TransportData::ValidateBuffer()|). - // - // It returns true (and leaves |error_message| alone) if this object appears - // to be a valid message (according to the above) and false, pointing - // |*error_message| to a suitable error message, if not. - bool IsValid(size_t serialized_platform_handle_size, - const char** error_message) const; - - // API parallel to that for |MessageInTransit| itself (mostly getters for - // header data). - const void* main_buffer() const { return buffer_; } - size_t main_buffer_size() const { - return RoundUpMessageAlignment(sizeof(Header) + header()->num_bytes); - } - const void* transport_data_buffer() const { - return (total_size() > main_buffer_size()) - ? static_cast<const char*>(buffer_) + main_buffer_size() - : nullptr; - } - size_t transport_data_buffer_size() const { - return total_size() - main_buffer_size(); - } - size_t total_size() const { return header()->total_size; } - uint32_t num_bytes() const { return header()->num_bytes; } - const void* bytes() const { - return static_cast<const char*>(buffer_) + sizeof(Header); - } - Type type() const { return header()->type; } - Subtype subtype() const { return header()->subtype; } - ChannelEndpointId source_id() const { return header()->source_id; } - ChannelEndpointId destination_id() const { - return header()->destination_id; - } - - private: - const Header* header() const { return static_cast<const Header*>(buffer_); } - - const void* const buffer_; - - // Though this struct is trivial, disallow copy and assign, since it doesn't - // own its data. (If you're copying/assigning this, you're probably doing - // something wrong.) - DISALLOW_COPY_AND_ASSIGN(View); - }; - - // |bytes| is optional; if null, the message data will be zero-initialized. - MessageInTransit(Type type, - Subtype subtype, - uint32_t num_bytes, - const void* bytes); - // |bytes| should be valid (and non-null), unless |num_bytes| is zero. - MessageInTransit(Type type, - Subtype subtype, - uint32_t num_bytes, - UserPointer<const void> bytes); - // Constructs a |MessageInTransit| from a |View|. - explicit MessageInTransit(const View& message_view); - - ~MessageInTransit(); - - // Gets the size of the next message from |buffer|, which has |buffer_size| - // bytes currently available, returning true and setting |*next_message_size| - // on success. |buffer| should be aligned on a |kMessageAlignment| boundary - // (and on success, |*next_message_size| will be a multiple of - // |kMessageAlignment|). - // TODO(vtl): In |RawChannelPosix|, the alignment requirements are currently - // satisified on a faith-based basis. - static bool GetNextMessageSize(const void* buffer, - size_t buffer_size, - size_t* next_message_size); - - // Makes this message "own" the given set of dispatchers. The dispatchers must - // not be referenced from anywhere else (in particular, not from the handle - // table), i.e., each dispatcher must have a reference count of 1. This - // message must not already have dispatchers. - void SetDispatchers(scoped_ptr<DispatcherVector> dispatchers); - - // Sets the |TransportData| for this message. This should only be done when - // there are no dispatchers and no existing |TransportData|. - void SetTransportData(scoped_ptr<TransportData> transport_data); - - // Serializes any dispatchers to the secondary buffer. This message must not - // already have a secondary buffer (so this must only be called once). The - // caller must ensure (e.g., by holding on to a reference) that |channel| - // stays alive through the call. - void SerializeAndCloseDispatchers(Channel* channel); - - // Gets the main buffer and its size (in number of bytes), respectively. - const void* main_buffer() const { return main_buffer_.get(); } - size_t main_buffer_size() const { return main_buffer_size_; } - - // Gets the transport data buffer (if any). - const TransportData* transport_data() const { return transport_data_.get(); } - TransportData* transport_data() { return transport_data_.get(); } - - // Gets the total size of the message (see comment in |Header|, below). - size_t total_size() const { return header()->total_size; } - - // Gets the size of the message data. - uint32_t num_bytes() const { return header()->num_bytes; } - - // Gets the message data (of size |num_bytes()| bytes). - const void* bytes() const { return main_buffer_.get() + sizeof(Header); } - void* bytes() { return main_buffer_.get() + sizeof(Header); } - - Type type() const { return header()->type; } - Subtype subtype() const { return header()->subtype; } - ChannelEndpointId source_id() const { return header()->source_id; } - ChannelEndpointId destination_id() const { return header()->destination_id; } - - void set_source_id(ChannelEndpointId source_id) { - header()->source_id = source_id; - } - void set_destination_id(ChannelEndpointId destination_id) { - header()->destination_id = destination_id; - } - - // Gets the dispatchers attached to this message; this may return null if - // there are none. Note that the caller may mutate the set of dispatchers - // (e.g., take ownership of all the dispatchers, leaving the vector empty). - DispatcherVector* dispatchers() { return dispatchers_.get(); } - - // Returns true if this message has dispatchers attached. - bool has_dispatchers() const { - return dispatchers_ && !dispatchers_->empty(); - } - - // Rounds |n| up to a multiple of |kMessageAlignment|. - static inline size_t RoundUpMessageAlignment(size_t n) { - return (n + kMessageAlignment - 1) & ~(kMessageAlignment - 1); - } - - private: - // To allow us to make compile-assertions about |Header| in the .cc file. - struct PrivateStructForCompileAsserts; - - // Header for the data (main buffer). Must be a multiple of - // |kMessageAlignment| bytes in size. Must be POD. - struct Header { - // Total size of the message, including the header, the message data - // ("bytes") including padding (to make it a multiple of |kMessageAlignment| - // bytes), and serialized handle information. Note that this may not be the - // correct value if dispatchers are attached but - // |SerializeAndCloseDispatchers()| has not been called. - uint32_t total_size; - Type type; // 2 bytes. - Subtype subtype; // 2 bytes. - ChannelEndpointId source_id; // 4 bytes. - ChannelEndpointId destination_id; // 4 bytes. - // Size of actual message data. - uint32_t num_bytes; - uint32_t unused; - }; - - const Header* header() const { - return reinterpret_cast<const Header*>(main_buffer_.get()); - } - Header* header() { return reinterpret_cast<Header*>(main_buffer_.get()); } - - void ConstructorHelper(Type type, Subtype subtype, uint32_t num_bytes); - void UpdateTotalSize(); - - const size_t main_buffer_size_; - const scoped_ptr<char, base::AlignedFreeDeleter> main_buffer_; // Never null. - - scoped_ptr<TransportData> transport_data_; // May be null. - - // Any dispatchers that may be attached to this message. These dispatchers - // should be "owned" by this message, i.e., have a ref count of exactly 1. (We - // allow a dispatcher entry to be null, in case it couldn't be duplicated for - // some reason.) - scoped_ptr<DispatcherVector> dispatchers_; - - DISALLOW_COPY_AND_ASSIGN(MessageInTransit); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_MESSAGE_IN_TRANSIT_H_ diff --git a/mojo/edk/system/message_in_transit_queue.cc b/mojo/edk/system/message_in_transit_queue.cc deleted file mode 100644 index ab5195e..0000000 --- a/mojo/edk/system/message_in_transit_queue.cc +++ /dev/null @@ -1,32 +0,0 @@ -// 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/edk/system/message_in_transit_queue.h" - -#include "base/logging.h" -#include "base/stl_util.h" - -namespace mojo { -namespace system { - -MessageInTransitQueue::MessageInTransitQueue() { -} - -MessageInTransitQueue::~MessageInTransitQueue() { - if (!IsEmpty()) { - LOG(WARNING) << "Destroying nonempty message queue"; - Clear(); - } -} - -void MessageInTransitQueue::Clear() { - STLDeleteElements(&queue_); -} - -void MessageInTransitQueue::Swap(MessageInTransitQueue* other) { - queue_.swap(other->queue_); -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/message_in_transit_queue.h b/mojo/edk/system/message_in_transit_queue.h deleted file mode 100644 index d81bd2b..0000000 --- a/mojo/edk/system/message_in_transit_queue.h +++ /dev/null @@ -1,61 +0,0 @@ -// 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_EDK_SYSTEM_MESSAGE_IN_TRANSIT_QUEUE_H_ -#define MOJO_EDK_SYSTEM_MESSAGE_IN_TRANSIT_QUEUE_H_ - -#include <deque> - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "mojo/edk/system/message_in_transit.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace system { - -// A simple queue for |MessageInTransit|s (that owns its messages). -// This class is not thread-safe. -// TODO(vtl): Write tests. -class MOJO_SYSTEM_IMPL_EXPORT MessageInTransitQueue { - public: - MessageInTransitQueue(); - ~MessageInTransitQueue(); - - bool IsEmpty() const { return queue_.empty(); } - - void AddMessage(scoped_ptr<MessageInTransit> message) { - queue_.push_back(message.release()); - } - - scoped_ptr<MessageInTransit> GetMessage() { - MessageInTransit* rv = queue_.front(); - queue_.pop_front(); - return make_scoped_ptr(rv); - } - - MessageInTransit* PeekMessage() { return queue_.front(); } - - void DiscardMessage() { - delete queue_.front(); - queue_.pop_front(); - } - - void Clear(); - - // Efficiently swaps contents with |*other|. - void Swap(MessageInTransitQueue* other); - - private: - // TODO(vtl): When C++11 is available, switch this to a deque of - // |scoped_ptr|/|unique_ptr|s. - std::deque<MessageInTransit*> queue_; - - DISALLOW_COPY_AND_ASSIGN(MessageInTransitQueue); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_MESSAGE_IN_TRANSIT_QUEUE_H_ diff --git a/mojo/edk/system/message_pipe.cc b/mojo/edk/system/message_pipe.cc deleted file mode 100644 index 554401e..0000000 --- a/mojo/edk/system/message_pipe.cc +++ /dev/null @@ -1,384 +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/edk/system/message_pipe.h" - -#include "base/logging.h" -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/channel_endpoint.h" -#include "mojo/edk/system/channel_endpoint_id.h" -#include "mojo/edk/system/incoming_endpoint.h" -#include "mojo/edk/system/local_message_pipe_endpoint.h" -#include "mojo/edk/system/message_in_transit.h" -#include "mojo/edk/system/message_pipe_dispatcher.h" -#include "mojo/edk/system/message_pipe_endpoint.h" -#include "mojo/edk/system/proxy_message_pipe_endpoint.h" - -namespace mojo { -namespace system { - -// static -MessagePipe* MessagePipe::CreateLocalLocal() { - MessagePipe* message_pipe = new MessagePipe(); - message_pipe->endpoints_[0].reset(new LocalMessagePipeEndpoint()); - message_pipe->endpoints_[1].reset(new LocalMessagePipeEndpoint()); - return message_pipe; -} - -// static -MessagePipe* MessagePipe::CreateLocalProxy( - scoped_refptr<ChannelEndpoint>* channel_endpoint) { - DCHECK(!*channel_endpoint); // Not technically wrong, but unlikely. - MessagePipe* message_pipe = new MessagePipe(); - message_pipe->endpoints_[0].reset(new LocalMessagePipeEndpoint()); - *channel_endpoint = new ChannelEndpoint(message_pipe, 1); - message_pipe->endpoints_[1].reset( - new ProxyMessagePipeEndpoint(channel_endpoint->get())); - return message_pipe; -} - -// static -MessagePipe* MessagePipe::CreateLocalProxyFromExisting( - MessageInTransitQueue* message_queue, - ChannelEndpoint* channel_endpoint) { - DCHECK(message_queue); - MessagePipe* message_pipe = new MessagePipe(); - message_pipe->endpoints_[0].reset( - new LocalMessagePipeEndpoint(message_queue)); - if (channel_endpoint) { - bool attached_to_channel = channel_endpoint->ReplaceClient(message_pipe, 1); - message_pipe->endpoints_[1].reset( - new ProxyMessagePipeEndpoint(channel_endpoint)); - if (!attached_to_channel) - message_pipe->OnDetachFromChannel(1); - } else { - // This means that the proxy side was already closed; we only need to inform - // the local side of this. - // TODO(vtl): This is safe to do without locking (but perhaps slightly - // dubious), since no other thread has access to |message_pipe| yet. - message_pipe->endpoints_[0]->OnPeerClose(); - } - return message_pipe; -} - -// static -MessagePipe* MessagePipe::CreateProxyLocal( - scoped_refptr<ChannelEndpoint>* channel_endpoint) { - DCHECK(!*channel_endpoint); // Not technically wrong, but unlikely. - MessagePipe* message_pipe = new MessagePipe(); - *channel_endpoint = new ChannelEndpoint(message_pipe, 0); - message_pipe->endpoints_[0].reset( - new ProxyMessagePipeEndpoint(channel_endpoint->get())); - message_pipe->endpoints_[1].reset(new LocalMessagePipeEndpoint()); - return message_pipe; -} - -// static -unsigned MessagePipe::GetPeerPort(unsigned port) { - DCHECK(port == 0 || port == 1); - return port ^ 1; -} - -// static -bool MessagePipe::Deserialize(Channel* channel, - const void* source, - size_t size, - scoped_refptr<MessagePipe>* message_pipe, - unsigned* port) { - DCHECK(!*message_pipe); // Not technically wrong, but unlikely. - - if (size != channel->GetSerializedEndpointSize()) { - LOG(ERROR) << "Invalid serialized message pipe"; - return false; - } - - scoped_refptr<IncomingEndpoint> incoming_endpoint = - channel->DeserializeEndpoint(source); - if (!incoming_endpoint) - return false; - - *message_pipe = incoming_endpoint->ConvertToMessagePipe(); - DCHECK(*message_pipe); - *port = 0; - return true; -} - -MessagePipeEndpoint::Type MessagePipe::GetType(unsigned port) { - DCHECK(port == 0 || port == 1); - base::AutoLock locker(lock_); - DCHECK(endpoints_[port]); - - return endpoints_[port]->GetType(); -} - -void MessagePipe::CancelAllAwakables(unsigned port) { - DCHECK(port == 0 || port == 1); - - base::AutoLock locker(lock_); - DCHECK(endpoints_[port]); - endpoints_[port]->CancelAllAwakables(); -} - -void MessagePipe::Close(unsigned port) { - DCHECK(port == 0 || port == 1); - - unsigned peer_port = GetPeerPort(port); - - base::AutoLock locker(lock_); - // The endpoint's |OnPeerClose()| may have been called first and returned - // false, which would have resulted in its destruction. - if (!endpoints_[port]) - return; - - endpoints_[port]->Close(); - if (endpoints_[peer_port]) { - if (!endpoints_[peer_port]->OnPeerClose()) - endpoints_[peer_port].reset(); - } - endpoints_[port].reset(); -} - -// TODO(vtl): Handle flags. -MojoResult MessagePipe::WriteMessage( - unsigned port, - UserPointer<const void> bytes, - uint32_t num_bytes, - std::vector<DispatcherTransport>* transports, - MojoWriteMessageFlags flags) { - DCHECK(port == 0 || port == 1); - - base::AutoLock locker(lock_); - return EnqueueMessageNoLock( - GetPeerPort(port), - make_scoped_ptr(new MessageInTransit( - MessageInTransit::kTypeEndpoint, - MessageInTransit::kSubtypeEndpointData, num_bytes, bytes)), - transports); -} - -MojoResult MessagePipe::ReadMessage(unsigned port, - UserPointer<void> bytes, - UserPointer<uint32_t> num_bytes, - DispatcherVector* dispatchers, - uint32_t* num_dispatchers, - MojoReadMessageFlags flags) { - DCHECK(port == 0 || port == 1); - - base::AutoLock locker(lock_); - DCHECK(endpoints_[port]); - - return endpoints_[port]->ReadMessage(bytes, num_bytes, dispatchers, - num_dispatchers, flags); -} - -HandleSignalsState MessagePipe::GetHandleSignalsState(unsigned port) const { - DCHECK(port == 0 || port == 1); - - base::AutoLock locker(const_cast<base::Lock&>(lock_)); - DCHECK(endpoints_[port]); - - return endpoints_[port]->GetHandleSignalsState(); -} - -MojoResult MessagePipe::AddAwakable(unsigned port, - Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state) { - DCHECK(port == 0 || port == 1); - - base::AutoLock locker(lock_); - DCHECK(endpoints_[port]); - - return endpoints_[port]->AddAwakable(awakable, signals, context, - signals_state); -} - -void MessagePipe::RemoveAwakable(unsigned port, - Awakable* awakable, - HandleSignalsState* signals_state) { - DCHECK(port == 0 || port == 1); - - base::AutoLock locker(lock_); - DCHECK(endpoints_[port]); - - endpoints_[port]->RemoveAwakable(awakable, signals_state); -} - -void MessagePipe::StartSerialize(unsigned /*port*/, - Channel* channel, - size_t* max_size, - size_t* max_platform_handles) { - *max_size = channel->GetSerializedEndpointSize(); - *max_platform_handles = 0; -} - -bool MessagePipe::EndSerialize( - unsigned port, - Channel* channel, - void* destination, - size_t* actual_size, - embedder::PlatformHandleVector* /*platform_handles*/) { - DCHECK(port == 0 || port == 1); - - base::AutoLock locker(lock_); - DCHECK(endpoints_[port]); - - // The port being serialized must be local. - DCHECK_EQ(endpoints_[port]->GetType(), MessagePipeEndpoint::kTypeLocal); - - unsigned peer_port = GetPeerPort(port); - MessageInTransitQueue* message_queue = - static_cast<LocalMessagePipeEndpoint*>(endpoints_[port].get()) - ->message_queue(); - // The replacement for |endpoints_[port]|, if any. - MessagePipeEndpoint* replacement_endpoint = nullptr; - - // The three cases below correspond to the ones described above - // |Channel::SerializeEndpoint...()| (in channel.h). - if (!endpoints_[peer_port]) { - // Case 1: (known-)closed peer port. There's no reason for us to continue to - // exist afterwards. - channel->SerializeEndpointWithClosedPeer(destination, message_queue); - } else if (endpoints_[peer_port]->GetType() == - MessagePipeEndpoint::kTypeLocal) { - // Case 2: local peer port. We replace |port|'s |LocalMessagePipeEndpoint| - // with a |ProxyMessagePipeEndpoint| hooked up to the |ChannelEndpoint| that - // the |Channel| returns to us. - scoped_refptr<ChannelEndpoint> channel_endpoint = - channel->SerializeEndpointWithLocalPeer(destination, message_queue, - this, port); - replacement_endpoint = new ProxyMessagePipeEndpoint(channel_endpoint.get()); - } else { - // Case 3: remote peer port. We get the |peer_port|'s |ChannelEndpoint| and - // pass it to the |Channel|. There's no reason for us to continue to exist - // afterwards. - DCHECK_EQ(endpoints_[peer_port]->GetType(), - MessagePipeEndpoint::kTypeProxy); - ProxyMessagePipeEndpoint* peer_endpoint = - static_cast<ProxyMessagePipeEndpoint*>(endpoints_[peer_port].get()); - scoped_refptr<ChannelEndpoint> peer_channel_endpoint = - peer_endpoint->ReleaseChannelEndpoint(); - channel->SerializeEndpointWithRemotePeer(destination, message_queue, - peer_channel_endpoint); - // No need to call |Close()| after |ReleaseChannelEndpoint()|. - endpoints_[peer_port].reset(); - } - - endpoints_[port]->Close(); - endpoints_[port].reset(replacement_endpoint); - - *actual_size = channel->GetSerializedEndpointSize(); - return true; -} - -bool MessagePipe::OnReadMessage(unsigned port, MessageInTransit* message) { - base::AutoLock locker(lock_); - - if (!endpoints_[port]) { - // This will happen only on the rare occasion that the call to - // |OnReadMessage()| is racing with us calling - // |ChannelEndpoint::ReplaceClient()|, in which case we reject the message, - // and the |ChannelEndpoint| can retry (calling the new client's - // |OnReadMessage()|). - return false; - } - - // This is called when the |ChannelEndpoint| for the - // |ProxyMessagePipeEndpoint| |port| receives a message (from the |Channel|). - // We need to pass this message on to its peer port (typically a - // |LocalMessagePipeEndpoint|). - MojoResult result = EnqueueMessageNoLock(GetPeerPort(port), - make_scoped_ptr(message), nullptr); - DLOG_IF(WARNING, result != MOJO_RESULT_OK) - << "EnqueueMessageNoLock() failed (result = " << result << ")"; - return true; -} - -void MessagePipe::OnDetachFromChannel(unsigned port) { - Close(port); -} - -MessagePipe::MessagePipe() { -} - -MessagePipe::~MessagePipe() { - // Owned by the dispatchers. The owning dispatchers should only release us via - // their |Close()| method, which should inform us of being closed via our - // |Close()|. Thus these should already be null. - DCHECK(!endpoints_[0]); - DCHECK(!endpoints_[1]); -} - -MojoResult MessagePipe::EnqueueMessageNoLock( - unsigned port, - scoped_ptr<MessageInTransit> message, - std::vector<DispatcherTransport>* transports) { - DCHECK(port == 0 || port == 1); - DCHECK(message); - - DCHECK_EQ(message->type(), MessageInTransit::kTypeEndpoint); - DCHECK(endpoints_[GetPeerPort(port)]); - - // The destination port need not be open, unlike the source port. - if (!endpoints_[port]) - return MOJO_RESULT_FAILED_PRECONDITION; - - if (transports) { - MojoResult result = AttachTransportsNoLock(port, message.get(), transports); - if (result != MOJO_RESULT_OK) - return result; - } - - // The endpoint's |EnqueueMessage()| may not report failure. - endpoints_[port]->EnqueueMessage(message.Pass()); - return MOJO_RESULT_OK; -} - -MojoResult MessagePipe::AttachTransportsNoLock( - unsigned port, - MessageInTransit* message, - std::vector<DispatcherTransport>* transports) { - DCHECK(!message->has_dispatchers()); - - // 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 |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 will be - // available to read the message from.) - for (size_t i = 0; i < transports->size(); i++) { - if (!(*transports)[i].is_valid()) - continue; - if ((*transports)[i].GetType() == Dispatcher::kTypeMessagePipe) { - MessagePipeDispatcherTransport mp_transport((*transports)[i]); - if (mp_transport.GetMessagePipe() == this) { - // 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; - } - } - } - - // Clone the dispatchers and attach them to the message. (This must be done as - // a separate loop, since we want to leave the dispatchers alone on failure.) - scoped_ptr<DispatcherVector> dispatchers(new DispatcherVector()); - dispatchers->reserve(transports->size()); - for (size_t i = 0; i < transports->size(); i++) { - if ((*transports)[i].is_valid()) { - dispatchers->push_back( - (*transports)[i].CreateEquivalentDispatcherAndClose()); - } else { - LOG(WARNING) << "Enqueueing null dispatcher"; - dispatchers->push_back(nullptr); - } - } - message->SetDispatchers(dispatchers.Pass()); - return MOJO_RESULT_OK; -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/message_pipe.h b/mojo/edk/system/message_pipe.h deleted file mode 100644 index df05014..0000000 --- a/mojo/edk/system/message_pipe.h +++ /dev/null @@ -1,148 +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_EDK_SYSTEM_MESSAGE_PIPE_H_ -#define MOJO_EDK_SYSTEM_MESSAGE_PIPE_H_ - -#include <stddef.h> -#include <stdint.h> - -#include <vector> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/embedder/platform_handle_vector.h" -#include "mojo/edk/system/channel_endpoint_client.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/handle_signals_state.h" -#include "mojo/edk/system/memory.h" -#include "mojo/edk/system/message_in_transit.h" -#include "mojo/edk/system/message_pipe_endpoint.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/message_pipe.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { -namespace system { - -class Awakable; -class Channel; -class ChannelEndpoint; -class MessageInTransitQueue; - -// |MessagePipe| is the secondary object implementing a message pipe (see the -// 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 ChannelEndpointClient { - public: - // Creates a |MessagePipe| with two new |LocalMessagePipeEndpoint|s. - static MessagePipe* CreateLocalLocal(); - - // Creates a |MessagePipe| with a |LocalMessagePipeEndpoint| on port 0 and a - // |ProxyMessagePipeEndpoint| on port 1. |*channel_endpoint| is set to the - // (newly-created) |ChannelEndpoint| for the latter. - static MessagePipe* CreateLocalProxy( - scoped_refptr<ChannelEndpoint>* channel_endpoint); - - // Similar to |CreateLocalProxy()|, except that it'll do so from an existing - // |ChannelEndpoint| (whose |ReplaceClient()| it'll call) and take - // |message_queue|'s contents as already-received incoming messages. If - // |channel_endpoint| is null, this will create a "half-open" message pipe. - static MessagePipe* CreateLocalProxyFromExisting( - MessageInTransitQueue* message_queue, - ChannelEndpoint* channel_endpoint); - - // Creates a |MessagePipe| with a |ProxyMessagePipeEndpoint| on port 0 and a - // |LocalMessagePipeEndpoint| on port 1. |*channel_endpoint| is set to the - // (newly-created) |ChannelEndpoint| for the former. - // Note: This is really only needed in tests (outside of tests, this - // configuration arises from a local message pipe having its port 0 - // "converted" using |ConvertLocalToProxy()|). - static MessagePipe* CreateProxyLocal( - scoped_refptr<ChannelEndpoint>* channel_endpoint); - - // Gets the other port number (i.e., 0 -> 1, 1 -> 0). - static unsigned GetPeerPort(unsigned port); - - // Used by |MessagePipeDispatcher::Deserialize()|. Returns true on success (in - // which case, |*message_pipe|/|*port| are set appropriately) and false on - // failure (in which case |*message_pipe| may or may not be set to null). - static bool Deserialize(Channel* channel, - const void* source, - size_t size, - scoped_refptr<MessagePipe>* message_pipe, - unsigned* port); - - // Gets the type of the endpoint (used for assertions, etc.). - MessagePipeEndpoint::Type GetType(unsigned port); - - // These are called by the dispatcher to implement its methods of - // corresponding names. In all cases, the port |port| must be open. - void CancelAllAwakables(unsigned port); - void Close(unsigned port); - // Unlike |MessagePipeDispatcher::WriteMessage()|, this does not validate its - // arguments. - MojoResult WriteMessage(unsigned port, - UserPointer<const void> bytes, - uint32_t num_bytes, - std::vector<DispatcherTransport>* transports, - MojoWriteMessageFlags flags); - MojoResult ReadMessage(unsigned port, - UserPointer<void> bytes, - UserPointer<uint32_t> num_bytes, - DispatcherVector* dispatchers, - uint32_t* num_dispatchers, - MojoReadMessageFlags flags); - HandleSignalsState GetHandleSignalsState(unsigned port) const; - MojoResult AddAwakable(unsigned port, - Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state); - void RemoveAwakable(unsigned port, - Awakable* awakable, - HandleSignalsState* signals_state); - void StartSerialize(unsigned port, - Channel* channel, - size_t* max_size, - size_t* max_platform_handles); - bool EndSerialize(unsigned port, - Channel* channel, - void* destination, - size_t* actual_size, - embedder::PlatformHandleVector* platform_handles); - - // |ChannelEndpointClient| methods: - bool OnReadMessage(unsigned port, MessageInTransit* message) override; - void OnDetachFromChannel(unsigned port) override; - - private: - MessagePipe(); - ~MessagePipe() override; - - // This is used internally by |WriteMessage()| and by |OnReadMessage()|. - // |transports| may be non-null only if it's nonempty and |message| has no - // dispatchers attached. Must be called with |lock_| held. - MojoResult EnqueueMessageNoLock(unsigned port, - scoped_ptr<MessageInTransit> message, - std::vector<DispatcherTransport>* transports); - - // Helper for |EnqueueMessageNoLock()|. Must be called with |lock_| held. - MojoResult AttachTransportsNoLock( - unsigned port, - MessageInTransit* message, - std::vector<DispatcherTransport>* transports); - - base::Lock lock_; // Protects the following members. - scoped_ptr<MessagePipeEndpoint> endpoints_[2]; - - DISALLOW_COPY_AND_ASSIGN(MessagePipe); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_MESSAGE_PIPE_H_ diff --git a/mojo/edk/system/message_pipe_dispatcher.cc b/mojo/edk/system/message_pipe_dispatcher.cc deleted file mode 100644 index a6733dbe..0000000 --- a/mojo/edk/system/message_pipe_dispatcher.cc +++ /dev/null @@ -1,230 +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/edk/system/message_pipe_dispatcher.h" - -#include "base/logging.h" -#include "mojo/edk/system/configuration.h" -#include "mojo/edk/system/local_message_pipe_endpoint.h" -#include "mojo/edk/system/memory.h" -#include "mojo/edk/system/message_pipe.h" -#include "mojo/edk/system/options_validation.h" -#include "mojo/edk/system/proxy_message_pipe_endpoint.h" - -namespace mojo { -namespace system { - -const unsigned kInvalidPort = static_cast<unsigned>(-1); - -// MessagePipeDispatcher ------------------------------------------------------- - -// static -const MojoCreateMessagePipeOptions - MessagePipeDispatcher::kDefaultCreateOptions = { - static_cast<uint32_t>(sizeof(MojoCreateMessagePipeOptions)), - MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE}; - -MessagePipeDispatcher::MessagePipeDispatcher( - const MojoCreateMessagePipeOptions& /*validated_options*/) - : port_(kInvalidPort) { -} - -// static -MojoResult MessagePipeDispatcher::ValidateCreateOptions( - UserPointer<const MojoCreateMessagePipeOptions> in_options, - MojoCreateMessagePipeOptions* out_options) { - const MojoCreateMessagePipeOptionsFlags kKnownFlags = - MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE; - - *out_options = kDefaultCreateOptions; - if (in_options.IsNull()) - return MOJO_RESULT_OK; - - UserOptionsReader<MojoCreateMessagePipeOptions> reader(in_options); - if (!reader.is_valid()) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (!OPTIONS_STRUCT_HAS_MEMBER(MojoCreateMessagePipeOptions, flags, reader)) - return MOJO_RESULT_OK; - if ((reader.options().flags & ~kKnownFlags)) - return MOJO_RESULT_UNIMPLEMENTED; - out_options->flags = reader.options().flags; - - // Checks for fields beyond |flags|: - - // (Nothing here yet.) - - return MOJO_RESULT_OK; -} - -void MessagePipeDispatcher::Init(scoped_refptr<MessagePipe> message_pipe, - unsigned port) { - DCHECK(message_pipe); - DCHECK(port == 0 || port == 1); - - message_pipe_ = message_pipe; - port_ = port; -} - -Dispatcher::Type MessagePipeDispatcher::GetType() const { - return kTypeMessagePipe; -} - -// static -scoped_refptr<MessagePipeDispatcher> -MessagePipeDispatcher::CreateRemoteMessagePipe( - scoped_refptr<ChannelEndpoint>* channel_endpoint) { - scoped_refptr<MessagePipe> message_pipe( - MessagePipe::CreateLocalProxy(channel_endpoint)); - scoped_refptr<MessagePipeDispatcher> dispatcher( - new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions)); - dispatcher->Init(message_pipe, 0); - return dispatcher; -} - -// static -scoped_refptr<MessagePipeDispatcher> MessagePipeDispatcher::Deserialize( - Channel* channel, - const void* source, - size_t size) { - unsigned port = kInvalidPort; - scoped_refptr<MessagePipe> message_pipe; - if (!MessagePipe::Deserialize(channel, source, size, &message_pipe, &port)) - return nullptr; - DCHECK(message_pipe); - DCHECK(port == 0 || port == 1); - - scoped_refptr<MessagePipeDispatcher> dispatcher( - new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions)); - dispatcher->Init(message_pipe, port); - return dispatcher; -} - -MessagePipeDispatcher::~MessagePipeDispatcher() { - // |Close()|/|CloseImplNoLock()| should have taken care of the pipe. - DCHECK(!message_pipe_); -} - -MessagePipe* MessagePipeDispatcher::GetMessagePipeNoLock() const { - lock().AssertAcquired(); - return message_pipe_.get(); -} - -unsigned MessagePipeDispatcher::GetPortNoLock() const { - lock().AssertAcquired(); - return port_; -} - -void MessagePipeDispatcher::CancelAllAwakablesNoLock() { - lock().AssertAcquired(); - message_pipe_->CancelAllAwakables(port_); -} - -void MessagePipeDispatcher::CloseImplNoLock() { - lock().AssertAcquired(); - message_pipe_->Close(port_); - message_pipe_ = nullptr; - port_ = kInvalidPort; -} - -scoped_refptr<Dispatcher> -MessagePipeDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() { - lock().AssertAcquired(); - - // TODO(vtl): Currently, there are no options, so we just use - // |kDefaultCreateOptions|. Eventually, we'll have to duplicate the options - // too. - scoped_refptr<MessagePipeDispatcher> rv = - new MessagePipeDispatcher(kDefaultCreateOptions); - rv->Init(message_pipe_, port_); - message_pipe_ = nullptr; - port_ = kInvalidPort; - return scoped_refptr<Dispatcher>(rv.get()); -} - -MojoResult MessagePipeDispatcher::WriteMessageImplNoLock( - UserPointer<const void> bytes, - uint32_t num_bytes, - std::vector<DispatcherTransport>* transports, - MojoWriteMessageFlags flags) { - DCHECK(!transports || - (transports->size() > 0 && - transports->size() <= GetConfiguration().max_message_num_handles)); - - lock().AssertAcquired(); - - if (num_bytes > GetConfiguration().max_message_num_bytes) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - - return message_pipe_->WriteMessage(port_, bytes, num_bytes, transports, - flags); -} - -MojoResult MessagePipeDispatcher::ReadMessageImplNoLock( - UserPointer<void> bytes, - UserPointer<uint32_t> num_bytes, - DispatcherVector* dispatchers, - uint32_t* num_dispatchers, - MojoReadMessageFlags flags) { - lock().AssertAcquired(); - return message_pipe_->ReadMessage(port_, bytes, num_bytes, dispatchers, - num_dispatchers, flags); -} - -HandleSignalsState MessagePipeDispatcher::GetHandleSignalsStateImplNoLock() - const { - lock().AssertAcquired(); - return message_pipe_->GetHandleSignalsState(port_); -} - -MojoResult MessagePipeDispatcher::AddAwakableImplNoLock( - Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state) { - lock().AssertAcquired(); - return message_pipe_->AddAwakable(port_, awakable, signals, context, - signals_state); -} - -void MessagePipeDispatcher::RemoveAwakableImplNoLock( - Awakable* awakable, - HandleSignalsState* signals_state) { - lock().AssertAcquired(); - message_pipe_->RemoveAwakable(port_, awakable, signals_state); -} - -void MessagePipeDispatcher::StartSerializeImplNoLock( - Channel* channel, - size_t* max_size, - size_t* max_platform_handles) { - DCHECK(HasOneRef()); // Only one ref => no need to take the lock. - return message_pipe_->StartSerialize(port_, channel, max_size, - max_platform_handles); -} - -bool MessagePipeDispatcher::EndSerializeAndCloseImplNoLock( - Channel* channel, - void* destination, - size_t* actual_size, - embedder::PlatformHandleVector* platform_handles) { - DCHECK(HasOneRef()); // Only one ref => no need to take the lock. - - bool rv = message_pipe_->EndSerialize(port_, channel, destination, - actual_size, platform_handles); - message_pipe_ = nullptr; - port_ = kInvalidPort; - return rv; -} - -// MessagePipeDispatcherTransport ---------------------------------------------- - -MessagePipeDispatcherTransport::MessagePipeDispatcherTransport( - DispatcherTransport transport) - : DispatcherTransport(transport) { - DCHECK_EQ(message_pipe_dispatcher()->GetType(), Dispatcher::kTypeMessagePipe); -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/message_pipe_dispatcher.h b/mojo/edk/system/message_pipe_dispatcher.h deleted file mode 100644 index fb16dd3..0000000 --- a/mojo/edk/system/message_pipe_dispatcher.h +++ /dev/null @@ -1,133 +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_EDK_SYSTEM_MESSAGE_PIPE_DISPATCHER_H_ -#define MOJO_EDK_SYSTEM_MESSAGE_PIPE_DISPATCHER_H_ - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/memory.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace system { - -class ChannelEndpoint; -class MessagePipe; -class MessagePipeDispatcherTransport; - -// This is the |Dispatcher| implementation for message pipes (created by the -// Mojo primitive |MojoCreateMessagePipe()|). This class is thread-safe. -class MOJO_SYSTEM_IMPL_EXPORT MessagePipeDispatcher : public Dispatcher { - public: - // The default options to use for |MojoCreateMessagePipe()|. (Real uses - // should obtain this via |ValidateCreateOptions()| with a null |in_options|; - // this is exposed directly for testing convenience.) - static const MojoCreateMessagePipeOptions kDefaultCreateOptions; - - MessagePipeDispatcher( - const MojoCreateMessagePipeOptions& /*validated_options*/); - - // Validates and/or sets default options for |MojoCreateMessagePipeOptions|. - // If non-null, |in_options| must point to a struct of at least - // |in_options->struct_size| bytes. |out_options| must point to a (current) - // |MojoCreateMessagePipeOptions| and will be entirely overwritten on success - // (it may be partly overwritten on failure). - static MojoResult ValidateCreateOptions( - UserPointer<const MojoCreateMessagePipeOptions> in_options, - MojoCreateMessagePipeOptions* out_options); - - // Must be called before any other methods. (This method is not thread-safe.) - void Init(scoped_refptr<MessagePipe> message_pipe, unsigned port); - - // |Dispatcher| public methods: - Type GetType() const override; - - // Creates a |MessagePipe| with a local endpoint (at port 0) and a proxy - // endpoint, and creates/initializes a |MessagePipeDispatcher| (attached to - // the message pipe, port 0). - // TODO(vtl): This currently uses |kDefaultCreateOptions|, which is okay since - // there aren't any options, but eventually options should be plumbed through. - static scoped_refptr<MessagePipeDispatcher> CreateRemoteMessagePipe( - scoped_refptr<ChannelEndpoint>* channel_endpoint); - - // The "opposite" of |SerializeAndClose()|. (Typically this is called by - // |Dispatcher::Deserialize()|.) - static scoped_refptr<MessagePipeDispatcher> Deserialize(Channel* channel, - const void* source, - size_t size); - - private: - friend class MessagePipeDispatcherTransport; - - ~MessagePipeDispatcher() override; - - // Gets a dumb pointer to |message_pipe_|. This must be called under the - // |Dispatcher| lock (that it's a dumb pointer is okay since it's under lock). - // This is needed when sending handles across processes, where nontrivial, - // invasive work needs to be done. - MessagePipe* GetMessagePipeNoLock() const; - // Similarly for the port. - unsigned GetPortNoLock() const; - - // |Dispatcher| protected methods: - void CancelAllAwakablesNoLock() override; - void CloseImplNoLock() override; - scoped_refptr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock() - override; - MojoResult WriteMessageImplNoLock( - UserPointer<const void> bytes, - uint32_t num_bytes, - std::vector<DispatcherTransport>* transports, - MojoWriteMessageFlags flags) override; - MojoResult ReadMessageImplNoLock(UserPointer<void> bytes, - UserPointer<uint32_t> num_bytes, - DispatcherVector* dispatchers, - uint32_t* num_dispatchers, - MojoReadMessageFlags flags) override; - HandleSignalsState GetHandleSignalsStateImplNoLock() const override; - MojoResult AddAwakableImplNoLock(Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state) override; - void RemoveAwakableImplNoLock(Awakable* awakable, - HandleSignalsState* signals_state) override; - void StartSerializeImplNoLock(Channel* channel, - size_t* max_size, - size_t* max_platform_handles) override; - bool EndSerializeAndCloseImplNoLock( - Channel* channel, - void* destination, - size_t* actual_size, - embedder::PlatformHandleVector* platform_handles) override; - - // Protected by |lock()|: - scoped_refptr<MessagePipe> message_pipe_; // This will be null if closed. - unsigned port_; - - DISALLOW_COPY_AND_ASSIGN(MessagePipeDispatcher); -}; - -class MessagePipeDispatcherTransport : public DispatcherTransport { - public: - explicit MessagePipeDispatcherTransport(DispatcherTransport transport); - - MessagePipe* GetMessagePipe() { - return message_pipe_dispatcher()->GetMessagePipeNoLock(); - } - unsigned GetPort() { return message_pipe_dispatcher()->GetPortNoLock(); } - - private: - MessagePipeDispatcher* message_pipe_dispatcher() { - return static_cast<MessagePipeDispatcher*>(dispatcher()); - } - - // Copy and assign allowed. -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_MESSAGE_PIPE_DISPATCHER_H_ diff --git a/mojo/edk/system/message_pipe_dispatcher_unittest.cc b/mojo/edk/system/message_pipe_dispatcher_unittest.cc deleted file mode 100644 index b5562b0..0000000 --- a/mojo/edk/system/message_pipe_dispatcher_unittest.cc +++ /dev/null @@ -1,691 +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. - -// NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a -// heavily-loaded system). Sorry. |test::EpsilonTimeout()| may be increased to -// increase tolerance and reduce observed flakiness (though doing so reduces the -// meaningfulness of the test). - -#include "mojo/edk/system/message_pipe_dispatcher.h" - -#include <string.h> - -#include <limits> - -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_vector.h" -#include "base/rand_util.h" -#include "base/threading/platform_thread.h" // For |Sleep()|. -#include "base/threading/simple_thread.h" -#include "base/time/time.h" -#include "mojo/edk/system/message_pipe.h" -#include "mojo/edk/system/test_utils.h" -#include "mojo/edk/system/waiter.h" -#include "mojo/edk/system/waiter_test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -const MojoHandleSignals kAllSignals = MOJO_HANDLE_SIGNAL_READABLE | - MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED; - -TEST(MessagePipeDispatcherTest, Basic) { - test::Stopwatch stopwatch; - int32_t buffer[1]; - const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer)); - uint32_t buffer_size; - - // Run this test both with |d0| as port 0, |d1| as port 1 and vice versa. - for (unsigned i = 0; i < 2; i++) { - scoped_refptr<MessagePipeDispatcher> d0(new MessagePipeDispatcher( - MessagePipeDispatcher::kDefaultCreateOptions)); - EXPECT_EQ(Dispatcher::kTypeMessagePipe, d0->GetType()); - scoped_refptr<MessagePipeDispatcher> d1(new MessagePipeDispatcher( - MessagePipeDispatcher::kDefaultCreateOptions)); - { - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal()); - d0->Init(mp, i); // 0, 1. - d1->Init(mp, i ^ 1); // 1, 0. - } - Waiter w; - uint32_t context = 0; - HandleSignalsState hss; - - // Try adding a writable waiter when already writable. - w.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - d0->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - // Shouldn't need to remove the waiter (it was not added). - - // Add a readable waiter to |d0|, then make it readable (by writing to - // |d1|), then wait. - w.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - d0->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 1, nullptr)); - buffer[0] = 123456789; - EXPECT_EQ(MOJO_RESULT_OK, - d1->WriteMessage(UserPointer<const void>(buffer), kBufferSize, - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - stopwatch.Start(); - EXPECT_EQ(MOJO_RESULT_OK, w.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(1u, context); - EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout()); - hss = HandleSignalsState(); - d0->RemoveAwakable(&w, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Try adding a readable waiter when already readable (from above). - w.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - d0->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 2, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - // Shouldn't need to remove the waiter (it was not added). - - // Make |d0| no longer readable (by reading from it). - buffer[0] = 0; - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_OK, - d0->ReadMessage(UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(kBufferSize, buffer_size); - EXPECT_EQ(123456789, buffer[0]); - - // Wait for zero time for readability on |d0| (will time out). - w.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - d0->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 3, nullptr)); - stopwatch.Start(); - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, w.Wait(0, nullptr)); - EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout()); - hss = HandleSignalsState(); - d0->RemoveAwakable(&w, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Wait for non-zero, finite time for readability on |d0| (will time out). - w.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - d0->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 3, nullptr)); - stopwatch.Start(); - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - w.Wait(2 * test::EpsilonTimeout().InMicroseconds(), nullptr)); - base::TimeDelta elapsed = stopwatch.Elapsed(); - EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout()); - EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout()); - hss = HandleSignalsState(); - d0->RemoveAwakable(&w, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Check the peer closed signal. - w.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - d0->AddAwakable(&w, MOJO_HANDLE_SIGNAL_PEER_CLOSED, 12, nullptr)); - - // Close the peer. - EXPECT_EQ(MOJO_RESULT_OK, d1->Close()); - - // It should be signaled. - EXPECT_EQ(MOJO_RESULT_OK, w.Wait(1000, &context)); - EXPECT_EQ(12u, context); - hss = HandleSignalsState(); - d0->RemoveAwakable(&w, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - EXPECT_EQ(MOJO_RESULT_OK, d0->Close()); - } -} - -TEST(MessagePipeDispatcherTest, InvalidParams) { - char buffer[1]; - - scoped_refptr<MessagePipeDispatcher> d0( - new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions)); - scoped_refptr<MessagePipeDispatcher> d1( - new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions)); - { - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal()); - d0->Init(mp, 0); - d1->Init(mp, 1); - } - - // |WriteMessage|: - // Huge buffer size. - EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, - d0->WriteMessage(UserPointer<const void>(buffer), - std::numeric_limits<uint32_t>::max(), nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - EXPECT_EQ(MOJO_RESULT_OK, d0->Close()); - EXPECT_EQ(MOJO_RESULT_OK, d1->Close()); -} - -// These test invalid arguments that should cause death if we're being paranoid -// about checking arguments (which we would want to do if, e.g., we were in a -// true "kernel" situation, but we might not want to do otherwise for -// performance reasons). Probably blatant errors like passing in null pointers -// (for required pointer arguments) will still cause death, but perhaps not -// predictably. -TEST(MessagePipeDispatcherTest, InvalidParamsDeath) { - const char kMemoryCheckFailedRegex[] = "Check failed"; - - scoped_refptr<MessagePipeDispatcher> d0( - new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions)); - scoped_refptr<MessagePipeDispatcher> d1( - new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions)); - { - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal()); - d0->Init(mp, 0); - d1->Init(mp, 1); - } - - // |WriteMessage|: - // Null buffer with nonzero buffer size. - EXPECT_DEATH_IF_SUPPORTED(d0->WriteMessage(NullUserPointer(), 1, nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE), - kMemoryCheckFailedRegex); - - // |ReadMessage|: - // Null buffer with nonzero buffer size. - // First write something so that we actually have something to read. - EXPECT_EQ(MOJO_RESULT_OK, - d1->WriteMessage(UserPointer<const void>("x"), 1, nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - uint32_t buffer_size = 1; - EXPECT_DEATH_IF_SUPPORTED( - d0->ReadMessage(NullUserPointer(), MakeUserPointer(&buffer_size), 0, - nullptr, MOJO_READ_MESSAGE_FLAG_NONE), - kMemoryCheckFailedRegex); - - EXPECT_EQ(MOJO_RESULT_OK, d0->Close()); - EXPECT_EQ(MOJO_RESULT_OK, d1->Close()); -} - -// Test what happens when one end is closed (single-threaded test). -TEST(MessagePipeDispatcherTest, BasicClosed) { - int32_t buffer[1]; - const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer)); - uint32_t buffer_size; - - // Run this test both with |d0| as port 0, |d1| as port 1 and vice versa. - for (unsigned i = 0; i < 2; i++) { - scoped_refptr<MessagePipeDispatcher> d0(new MessagePipeDispatcher( - MessagePipeDispatcher::kDefaultCreateOptions)); - scoped_refptr<MessagePipeDispatcher> d1(new MessagePipeDispatcher( - MessagePipeDispatcher::kDefaultCreateOptions)); - { - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal()); - d0->Init(mp, i); // 0, 1. - d1->Init(mp, i ^ 1); // 1, 0. - } - Waiter w; - HandleSignalsState hss; - - // Write (twice) to |d1|. - buffer[0] = 123456789; - EXPECT_EQ(MOJO_RESULT_OK, - d1->WriteMessage(UserPointer<const void>(buffer), kBufferSize, - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - buffer[0] = 234567890; - EXPECT_EQ(MOJO_RESULT_OK, - d1->WriteMessage(UserPointer<const void>(buffer), kBufferSize, - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Try waiting for readable on |d0|; should fail (already satisfied). - w.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - d0->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Try reading from |d1|; should fail (nothing to read). - buffer[0] = 0; - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - d1->ReadMessage(UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - - // Close |d1|. - EXPECT_EQ(MOJO_RESULT_OK, d1->Close()); - - // Try waiting for readable on |d0|; should fail (already satisfied). - w.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - d0->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 1, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Read from |d0|. - buffer[0] = 0; - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_OK, - d0->ReadMessage(UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(kBufferSize, buffer_size); - EXPECT_EQ(123456789, buffer[0]); - - // Try waiting for readable on |d0|; should fail (already satisfied). - w.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - d0->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 2, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Read again from |d0|. - buffer[0] = 0; - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_OK, - d0->ReadMessage(UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(kBufferSize, buffer_size); - EXPECT_EQ(234567890, buffer[0]); - - // Try waiting for readable on |d0|; should fail (unsatisfiable). - w.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - d0->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 3, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - // Try waiting for writable on |d0|; should fail (unsatisfiable). - w.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - d0->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 4, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - // Try reading from |d0|; should fail (nothing to read and other end - // closed). - buffer[0] = 0; - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - d0->ReadMessage(UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - - // Try writing to |d0|; should fail (other end closed). - buffer[0] = 345678901; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - d0->WriteMessage(UserPointer<const void>(buffer), kBufferSize, - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - EXPECT_EQ(MOJO_RESULT_OK, d0->Close()); - } -} - -#if defined(OS_WIN) -// http://crbug.com/396386 -#define MAYBE_BasicThreaded DISABLED_BasicThreaded -#else -#define MAYBE_BasicThreaded BasicThreaded -#endif -TEST(MessagePipeDispatcherTest, MAYBE_BasicThreaded) { - test::Stopwatch stopwatch; - int32_t buffer[1]; - const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer)); - uint32_t buffer_size; - base::TimeDelta elapsed; - bool did_wait; - MojoResult result; - uint32_t context; - HandleSignalsState hss; - - // Run this test both with |d0| as port 0, |d1| as port 1 and vice versa. - for (unsigned i = 0; i < 2; i++) { - scoped_refptr<MessagePipeDispatcher> d0(new MessagePipeDispatcher( - MessagePipeDispatcher::kDefaultCreateOptions)); - scoped_refptr<MessagePipeDispatcher> d1(new MessagePipeDispatcher( - MessagePipeDispatcher::kDefaultCreateOptions)); - { - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal()); - d0->Init(mp, i); // 0, 1. - d1->Init(mp, i ^ 1); // 1, 0. - } - - // Wait for readable on |d1|, which will become readable after some time. - { - test::WaiterThread thread(d1, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, 1, &did_wait, &result, - &context, &hss); - stopwatch.Start(); - thread.Start(); - base::PlatformThread::Sleep(2 * test::EpsilonTimeout()); - // Wake it up by writing to |d0|. - buffer[0] = 123456789; - EXPECT_EQ(MOJO_RESULT_OK, - d0->WriteMessage(UserPointer<const void>(buffer), kBufferSize, - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - } // Joins the thread. - elapsed = stopwatch.Elapsed(); - EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout()); - EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout()); - EXPECT_TRUE(did_wait); - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(1u, context); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Now |d1| is already readable. Try waiting for it again. - { - test::WaiterThread thread(d1, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, 2, &did_wait, &result, - &context, &hss); - stopwatch.Start(); - thread.Start(); - } // Joins the thread. - EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout()); - EXPECT_FALSE(did_wait); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, result); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Consume what we wrote to |d0|. - buffer[0] = 0; - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_OK, - d1->ReadMessage(UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(kBufferSize, buffer_size); - EXPECT_EQ(123456789, buffer[0]); - - // Wait for readable on |d1| and close |d0| after some time, which should - // cancel that wait. - { - test::WaiterThread thread(d1, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, 3, &did_wait, &result, - &context, &hss); - stopwatch.Start(); - thread.Start(); - base::PlatformThread::Sleep(2 * test::EpsilonTimeout()); - EXPECT_EQ(MOJO_RESULT_OK, d0->Close()); - } // Joins the thread. - elapsed = stopwatch.Elapsed(); - EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout()); - EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout()); - EXPECT_TRUE(did_wait); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); - EXPECT_EQ(3u, context); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - EXPECT_EQ(MOJO_RESULT_OK, d1->Close()); - } - - for (unsigned i = 0; i < 2; i++) { - scoped_refptr<MessagePipeDispatcher> d0(new MessagePipeDispatcher( - MessagePipeDispatcher::kDefaultCreateOptions)); - scoped_refptr<MessagePipeDispatcher> d1(new MessagePipeDispatcher( - MessagePipeDispatcher::kDefaultCreateOptions)); - { - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal()); - d0->Init(mp, i); // 0, 1. - d1->Init(mp, i ^ 1); // 1, 0. - } - - // Wait for readable on |d1| and close |d1| after some time, which should - // cancel that wait. - { - test::WaiterThread thread(d1, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, 4, &did_wait, &result, - &context, &hss); - stopwatch.Start(); - thread.Start(); - base::PlatformThread::Sleep(2 * test::EpsilonTimeout()); - EXPECT_EQ(MOJO_RESULT_OK, d1->Close()); - } // Joins the thread. - elapsed = stopwatch.Elapsed(); - EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout()); - EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout()); - EXPECT_TRUE(did_wait); - EXPECT_EQ(MOJO_RESULT_CANCELLED, result); - EXPECT_EQ(4u, context); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(0u, hss.satisfiable_signals); - - EXPECT_EQ(MOJO_RESULT_OK, d0->Close()); - } -} - -// Stress test ----------------------------------------------------------------- - -const size_t kMaxMessageSize = 2000; - -class WriterThread : public base::SimpleThread { - public: - // |*messages_written| and |*bytes_written| belong to the thread while it's - // alive. - WriterThread(scoped_refptr<Dispatcher> write_dispatcher, - size_t* messages_written, - size_t* bytes_written) - : base::SimpleThread("writer_thread"), - write_dispatcher_(write_dispatcher), - messages_written_(messages_written), - bytes_written_(bytes_written) { - *messages_written_ = 0; - *bytes_written_ = 0; - } - - ~WriterThread() override { Join(); } - - private: - void Run() override { - // Make some data to write. - unsigned char buffer[kMaxMessageSize]; - for (size_t i = 0; i < kMaxMessageSize; i++) - buffer[i] = static_cast<unsigned char>(i); - - // Number of messages to write. - *messages_written_ = static_cast<size_t>(base::RandInt(1000, 6000)); - - // Write messages. - for (size_t i = 0; i < *messages_written_; i++) { - uint32_t bytes_to_write = static_cast<uint32_t>( - base::RandInt(1, static_cast<int>(kMaxMessageSize))); - EXPECT_EQ(MOJO_RESULT_OK, - write_dispatcher_->WriteMessage(UserPointer<const void>(buffer), - bytes_to_write, nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - *bytes_written_ += bytes_to_write; - } - - // Write one last "quit" message. - EXPECT_EQ(MOJO_RESULT_OK, write_dispatcher_->WriteMessage( - UserPointer<const void>("quit"), 4, nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - } - - const scoped_refptr<Dispatcher> write_dispatcher_; - size_t* const messages_written_; - size_t* const bytes_written_; - - DISALLOW_COPY_AND_ASSIGN(WriterThread); -}; - -class ReaderThread : public base::SimpleThread { - public: - // |*messages_read| and |*bytes_read| belong to the thread while it's alive. - ReaderThread(scoped_refptr<Dispatcher> read_dispatcher, - size_t* messages_read, - size_t* bytes_read) - : base::SimpleThread("reader_thread"), - read_dispatcher_(read_dispatcher), - messages_read_(messages_read), - bytes_read_(bytes_read) { - *messages_read_ = 0; - *bytes_read_ = 0; - } - - ~ReaderThread() override { Join(); } - - private: - void Run() override { - unsigned char buffer[kMaxMessageSize]; - Waiter w; - HandleSignalsState hss; - MojoResult result; - - // Read messages. - for (;;) { - // Wait for it to be readable. - w.Init(); - hss = HandleSignalsState(); - result = read_dispatcher_->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 0, - &hss); - EXPECT_TRUE(result == MOJO_RESULT_OK || - result == MOJO_RESULT_ALREADY_EXISTS) - << "result: " << result; - if (result == MOJO_RESULT_OK) { - // Actually need to wait. - EXPECT_EQ(MOJO_RESULT_OK, w.Wait(MOJO_DEADLINE_INDEFINITE, nullptr)); - read_dispatcher_->RemoveAwakable(&w, &hss); - } - // We may not actually be readable, since we're racing with other threads. - EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE)); - - // Now, try to do the read. - // Clear the buffer so that we can check the result. - memset(buffer, 0, sizeof(buffer)); - uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer)); - result = read_dispatcher_->ReadMessage( - UserPointer<void>(buffer), MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE); - EXPECT_TRUE(result == MOJO_RESULT_OK || result == MOJO_RESULT_SHOULD_WAIT) - << "result: " << result; - // We're racing with others to read, so maybe we failed. - if (result == MOJO_RESULT_SHOULD_WAIT) - continue; // In which case, try again. - // Check for quit. - if (buffer_size == 4 && memcmp("quit", buffer, 4) == 0) - return; - EXPECT_GE(buffer_size, 1u); - EXPECT_LE(buffer_size, kMaxMessageSize); - EXPECT_TRUE(IsValidMessage(buffer, buffer_size)); - - (*messages_read_)++; - *bytes_read_ += buffer_size; - } - } - - static bool IsValidMessage(const unsigned char* buffer, - uint32_t message_size) { - size_t i; - for (i = 0; i < message_size; i++) { - if (buffer[i] != static_cast<unsigned char>(i)) - return false; - } - // Check that the remaining bytes weren't stomped on. - for (; i < kMaxMessageSize; i++) { - if (buffer[i] != 0) - return false; - } - return true; - } - - const scoped_refptr<Dispatcher> read_dispatcher_; - size_t* const messages_read_; - size_t* const bytes_read_; - - DISALLOW_COPY_AND_ASSIGN(ReaderThread); -}; - -TEST(MessagePipeDispatcherTest, Stress) { - static const size_t kNumWriters = 30; - static const size_t kNumReaders = kNumWriters; - - scoped_refptr<MessagePipeDispatcher> d_write( - new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions)); - scoped_refptr<MessagePipeDispatcher> d_read( - new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions)); - { - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal()); - d_write->Init(mp, 0); - d_read->Init(mp, 1); - } - - size_t messages_written[kNumWriters]; - size_t bytes_written[kNumWriters]; - size_t messages_read[kNumReaders]; - size_t bytes_read[kNumReaders]; - { - // Make writers. - ScopedVector<WriterThread> writers; - for (size_t i = 0; i < kNumWriters; i++) { - writers.push_back( - new WriterThread(d_write, &messages_written[i], &bytes_written[i])); - } - - // Make readers. - ScopedVector<ReaderThread> readers; - for (size_t i = 0; i < kNumReaders; i++) { - readers.push_back( - new ReaderThread(d_read, &messages_read[i], &bytes_read[i])); - } - - // Start writers. - for (size_t i = 0; i < kNumWriters; i++) - writers[i]->Start(); - - // Start readers. - for (size_t i = 0; i < kNumReaders; i++) - readers[i]->Start(); - - // TODO(vtl): Maybe I should have an event that triggers all the threads to - // start doing stuff for real (so that the first ones created/started aren't - // advantaged). - } // Joins all the threads. - - size_t total_messages_written = 0; - size_t total_bytes_written = 0; - for (size_t i = 0; i < kNumWriters; i++) { - total_messages_written += messages_written[i]; - total_bytes_written += bytes_written[i]; - } - size_t total_messages_read = 0; - size_t total_bytes_read = 0; - for (size_t i = 0; i < kNumReaders; i++) { - total_messages_read += messages_read[i]; - total_bytes_read += bytes_read[i]; - // We'd have to be really unlucky to have read no messages on a thread. - EXPECT_GT(messages_read[i], 0u) << "reader: " << i; - EXPECT_GE(bytes_read[i], messages_read[i]) << "reader: " << i; - } - EXPECT_EQ(total_messages_written, total_messages_read); - EXPECT_EQ(total_bytes_written, total_bytes_read); - - EXPECT_EQ(MOJO_RESULT_OK, d_write->Close()); - EXPECT_EQ(MOJO_RESULT_OK, d_read->Close()); -} - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/message_pipe_endpoint.cc b/mojo/edk/system/message_pipe_endpoint.cc deleted file mode 100644 index 4b8bc5e..0000000 --- a/mojo/edk/system/message_pipe_endpoint.cc +++ /dev/null @@ -1,52 +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/edk/system/message_pipe_endpoint.h" - -#include "base/logging.h" - -namespace mojo { -namespace system { - -void MessagePipeEndpoint::CancelAllAwakables() { - NOTREACHED(); -} - -MojoResult MessagePipeEndpoint::ReadMessage(UserPointer<void> /*bytes*/, - UserPointer<uint32_t> /*num_bytes*/, - DispatcherVector* /*dispatchers*/, - uint32_t* /*num_dispatchers*/, - MojoReadMessageFlags /*flags*/) { - NOTREACHED(); - return MOJO_RESULT_INTERNAL; -} - -HandleSignalsState MessagePipeEndpoint::GetHandleSignalsState() const { - NOTREACHED(); - return HandleSignalsState(); -} - -MojoResult MessagePipeEndpoint::AddAwakable(Awakable* /*awakable*/, - MojoHandleSignals /*signals*/, - uint32_t /*context*/, - HandleSignalsState* signals_state) { - NOTREACHED(); - if (signals_state) - *signals_state = HandleSignalsState(); - return MOJO_RESULT_INTERNAL; -} - -void MessagePipeEndpoint::RemoveAwakable(Awakable* /*awakable*/, - HandleSignalsState* signals_state) { - NOTREACHED(); - if (signals_state) - *signals_state = HandleSignalsState(); -} - -void MessagePipeEndpoint::Attach(ChannelEndpoint* /*channel_endpoint*/) { - NOTREACHED(); -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/message_pipe_endpoint.h b/mojo/edk/system/message_pipe_endpoint.h deleted file mode 100644 index 0b5f12e..0000000 --- a/mojo/edk/system/message_pipe_endpoint.h +++ /dev/null @@ -1,90 +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_EDK_SYSTEM_MESSAGE_PIPE_ENDPOINT_H_ -#define MOJO_EDK_SYSTEM_MESSAGE_PIPE_ENDPOINT_H_ - -#include <stdint.h> - -#include <vector> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/memory.h" -#include "mojo/edk/system/message_in_transit.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/message_pipe.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { -namespace system { - -class ChannelEndpoint; -class Awakable; - -// This is an interface to one of the ends of a message pipe, and is used by -// |MessagePipe|. Its most important role is to provide a sink for messages -// (i.e., a place where messages can be sent). It has a secondary role: When the -// endpoint is local (i.e., in the current process), there'll be a dispatcher -// corresponding to the endpoint. In that case, the implementation of -// |MessagePipeEndpoint| also implements the functionality required by the -// dispatcher, e.g., to read messages and to wait. Implementations of this class -// are not thread-safe; instances are protected by |MesssagePipe|'s lock. -class MOJO_SYSTEM_IMPL_EXPORT MessagePipeEndpoint { - public: - virtual ~MessagePipeEndpoint() {} - - enum Type { kTypeLocal, kTypeProxy }; - virtual Type GetType() const = 0; - - // All implementations must implement these. - // Returns false if the endpoint should be closed and destroyed, else true. - virtual bool OnPeerClose() = 0; - // Implements |MessagePipe::EnqueueMessage()|. The major differences are that: - // a) Dispatchers have been vetted and cloned/attached to the message. - // b) At this point, we cannot report failure (if, e.g., a channel is torn - // down at this point, we should silently swallow the message). - virtual void EnqueueMessage(scoped_ptr<MessageInTransit> message) = 0; - virtual void Close() = 0; - - // Implementations must override these if they represent a local endpoint, - // i.e., one for which there's a |MessagePipeDispatcher| (and thus a handle). - // An implementation for a proxy endpoint (for which there's no dispatcher) - // needs not override these methods, since they should never be called. - // - // These methods implement the methods of the same name in |MessagePipe|, - // though |MessagePipe|'s implementation may have to do a little more if the - // operation involves both endpoints. - virtual void CancelAllAwakables(); - virtual MojoResult ReadMessage(UserPointer<void> bytes, - UserPointer<uint32_t> num_bytes, - DispatcherVector* dispatchers, - uint32_t* num_dispatchers, - MojoReadMessageFlags flags); - virtual HandleSignalsState GetHandleSignalsState() const; - virtual MojoResult AddAwakable(Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state); - virtual void RemoveAwakable(Awakable* awakable, - HandleSignalsState* signals_state); - - // Implementations must override these if they represent a proxy endpoint. An - // implementation for a local endpoint needs not override these methods, since - // they should never be called. - virtual void Attach(ChannelEndpoint* channel_endpoint); - - protected: - MessagePipeEndpoint() {} - - private: - DISALLOW_COPY_AND_ASSIGN(MessagePipeEndpoint); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_MESSAGE_PIPE_ENDPOINT_H_ diff --git a/mojo/edk/system/message_pipe_perftest.cc b/mojo/edk/system/message_pipe_perftest.cc deleted file mode 100644 index 2387782..0000000 --- a/mojo/edk/system/message_pipe_perftest.cc +++ /dev/null @@ -1,172 +0,0 @@ -// 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 <stdint.h> -#include <stdio.h> -#include <string.h> - -#include <string> -#include <vector> - -#include "base/bind.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/pickle.h" -#include "base/strings/stringprintf.h" -#include "base/test/perf_time_logger.h" -#include "base/time/time.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/local_message_pipe_endpoint.h" -#include "mojo/edk/system/message_pipe.h" -#include "mojo/edk/system/message_pipe_test_utils.h" -#include "mojo/edk/system/proxy_message_pipe_endpoint.h" -#include "mojo/edk/system/raw_channel.h" -#include "mojo/edk/system/test_utils.h" -#include "mojo/edk/test/test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -class MultiprocessMessagePipePerfTest - : public test::MultiprocessMessagePipeTestBase { - public: - MultiprocessMessagePipePerfTest() : message_count_(0), message_size_(0) {} - - void SetUpMeasurement(int message_count, size_t message_size) { - message_count_ = message_count; - message_size_ = message_size; - payload_ = Pickle(); - payload_.WriteString(std::string(message_size, '*')); - read_buffer_.resize(message_size * 2); - } - - protected: - void WriteWaitThenRead(scoped_refptr<MessagePipe> mp) { - CHECK_EQ(mp->WriteMessage(0, UserPointer<const void>(payload_.data()), - static_cast<uint32_t>(payload_.size()), nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - HandleSignalsState hss; - CHECK_EQ(test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss), - MOJO_RESULT_OK); - uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer_.size()); - CHECK_EQ(mp->ReadMessage(0, UserPointer<void>(&read_buffer_[0]), - MakeUserPointer(&read_buffer_size), nullptr, - nullptr, MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - CHECK_EQ(read_buffer_size, static_cast<uint32_t>(payload_.size())); - } - - void SendQuitMessage(scoped_refptr<MessagePipe> mp) { - CHECK_EQ(mp->WriteMessage(0, UserPointer<const void>(""), 0, nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - } - - void Measure(scoped_refptr<MessagePipe> mp) { - // Have one ping-pong to ensure channel being established. - WriteWaitThenRead(mp); - - std::string test_name = - base::StringPrintf("IPC_Perf_%dx_%u", message_count_, - static_cast<unsigned>(message_size_)); - base::PerfTimeLogger logger(test_name.c_str()); - - for (int i = 0; i < message_count_; ++i) - WriteWaitThenRead(mp); - - logger.Done(); - } - - private: - int message_count_; - size_t message_size_; - Pickle payload_; - std::string read_buffer_; - scoped_ptr<base::PerfTimeLogger> perf_logger_; -}; - -// For each message received, sends a reply message with the same contents -// repeated twice, until the other end is closed or it receives "quitquitquit" -// (which it doesn't reply to). It'll return the number of messages received, -// not including any "quitquitquit" message, modulo 100. -MOJO_MULTIPROCESS_TEST_CHILD_MAIN(PingPongClient) { - embedder::SimplePlatformSupport platform_support; - test::ChannelThread channel_thread(&platform_support); - embedder::ScopedPlatformHandle client_platform_handle = - mojo::test::MultiprocessTestHelper::client_platform_handle.Pass(); - CHECK(client_platform_handle.is_valid()); - scoped_refptr<ChannelEndpoint> ep; - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep)); - channel_thread.Start(client_platform_handle.Pass(), ep); - - std::string buffer(1000000, '\0'); - int rv = 0; - while (true) { - // Wait for our end of the message pipe to be readable. - HandleSignalsState hss; - MojoResult result = - test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss); - if (result != MOJO_RESULT_OK) { - rv = result; - break; - } - - uint32_t read_size = static_cast<uint32_t>(buffer.size()); - CHECK_EQ(mp->ReadMessage(0, UserPointer<void>(&buffer[0]), - MakeUserPointer(&read_size), nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - - // Empty message indicates quitting - if (0 == read_size) - break; - - CHECK_EQ(mp->WriteMessage(0, UserPointer<const void>(&buffer[0]), - static_cast<uint32_t>(read_size), nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - } - - mp->Close(0); - return rv; -} - -// Repeatedly sends messages as previous one got replied by the child. -// Waits for the child to close its end before quitting once specified -// number of messages has been sent. -#if defined(OS_ANDROID) -// Android multi-process tests are not executing the new process. This is flaky. -#define MAYBE_PingPong DISABLED_PingPong -#else -#define MAYBE_PingPong PingPong -#endif // defined(OS_ANDROID) -TEST_F(MultiprocessMessagePipePerfTest, MAYBE_PingPong) { - helper()->StartChild("PingPongClient"); - - scoped_refptr<ChannelEndpoint> ep; - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep)); - Init(ep); - - // This values are set to align with one at ipc_pertests.cc for comparison. - const size_t kMsgSize[5] = {12, 144, 1728, 20736, 248832}; - const int kMessageCount[5] = {50000, 50000, 50000, 12000, 1000}; - - for (size_t i = 0; i < 5; i++) { - SetUpMeasurement(kMessageCount[i], kMsgSize[i]); - Measure(mp); - } - - SendQuitMessage(mp); - mp->Close(0); - EXPECT_EQ(0, helper()->WaitForChildShutdown()); -} - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/message_pipe_test_utils.cc b/mojo/edk/system/message_pipe_test_utils.cc deleted file mode 100644 index f227543..0000000 --- a/mojo/edk/system/message_pipe_test_utils.cc +++ /dev/null @@ -1,109 +0,0 @@ -// 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/edk/system/message_pipe_test_utils.h" - -#include "base/bind.h" -#include "base/threading/platform_thread.h" // For |Sleep()|. -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/channel_endpoint.h" -#include "mojo/edk/system/message_pipe.h" -#include "mojo/edk/system/waiter.h" - -namespace mojo { -namespace system { -namespace test { - -MojoResult WaitIfNecessary(scoped_refptr<MessagePipe> mp, - MojoHandleSignals signals, - HandleSignalsState* signals_state) { - Waiter waiter; - waiter.Init(); - - MojoResult add_result = - mp->AddAwakable(0, &waiter, signals, 0, signals_state); - if (add_result != MOJO_RESULT_OK) { - return (add_result == MOJO_RESULT_ALREADY_EXISTS) ? MOJO_RESULT_OK - : add_result; - } - - MojoResult wait_result = waiter.Wait(MOJO_DEADLINE_INDEFINITE, nullptr); - mp->RemoveAwakable(0, &waiter, signals_state); - return wait_result; -} - -ChannelThread::ChannelThread(embedder::PlatformSupport* platform_support) - : platform_support_(platform_support), - test_io_thread_(base::TestIOThread::kManualStart) { -} - -ChannelThread::~ChannelThread() { - Stop(); -} - -void ChannelThread::Start(embedder::ScopedPlatformHandle platform_handle, - scoped_refptr<ChannelEndpoint> channel_endpoint) { - test_io_thread_.Start(); - test_io_thread_.PostTaskAndWait( - FROM_HERE, - base::Bind(&ChannelThread::InitChannelOnIOThread, base::Unretained(this), - base::Passed(&platform_handle), channel_endpoint)); -} - -void ChannelThread::Stop() { - if (channel_) { - // Hack to flush write buffers before quitting. - // TODO(vtl): Remove this once |Channel| has a - // |FlushWriteBufferAndShutdown()| (or whatever). - while (!channel_->IsWriteBufferEmpty()) - base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20)); - - test_io_thread_.PostTaskAndWait( - FROM_HERE, base::Bind(&ChannelThread::ShutdownChannelOnIOThread, - base::Unretained(this))); - } - test_io_thread_.Stop(); -} - -void ChannelThread::InitChannelOnIOThread( - embedder::ScopedPlatformHandle platform_handle, - scoped_refptr<ChannelEndpoint> channel_endpoint) { - CHECK_EQ(base::MessageLoop::current(), test_io_thread_.message_loop()); - CHECK(platform_handle.is_valid()); - - // Create and initialize |Channel|. - channel_ = new Channel(platform_support_); - channel_->Init(RawChannel::Create(platform_handle.Pass())); - - // Start the bootstrap endpoint. - // Note: On the "server" (parent process) side, we need not attach/run the - // endpoint immediately. However, on the "client" (child process) side, this - // *must* be done here -- otherwise, the |Channel| may receive/process - // messages (which it can do as soon as it's hooked up to the IO thread - // message loop, and that message loop runs) before the endpoint is attached. - channel_->SetBootstrapEndpoint(channel_endpoint); -} - -void ChannelThread::ShutdownChannelOnIOThread() { - CHECK(channel_); - channel_->Shutdown(); - channel_ = nullptr; -} - -#if !defined(OS_IOS) -MultiprocessMessagePipeTestBase::MultiprocessMessagePipeTestBase() - : channel_thread_(&platform_support_) { -} - -MultiprocessMessagePipeTestBase::~MultiprocessMessagePipeTestBase() { -} - -void MultiprocessMessagePipeTestBase::Init(scoped_refptr<ChannelEndpoint> ep) { - channel_thread_.Start(helper_.server_platform_handle.Pass(), ep); -} -#endif - -} // namespace test -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/message_pipe_test_utils.h b/mojo/edk/system/message_pipe_test_utils.h deleted file mode 100644 index c9d9c8b..0000000 --- a/mojo/edk/system/message_pipe_test_utils.h +++ /dev/null @@ -1,73 +0,0 @@ -// 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_EDK_SYSTEM_MESSAGE_PIPE_TEST_UTILS_H_ -#define MOJO_EDK_SYSTEM_MESSAGE_PIPE_TEST_UTILS_H_ - -#include "base/test/test_io_thread.h" -#include "mojo/edk/embedder/simple_platform_support.h" -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/test_utils.h" -#include "mojo/edk/test/multiprocess_test_helper.h" - -namespace mojo { -namespace system { - -class Channel; -class ChannelEndpoint; -class MessagePipe; - -namespace test { - -MojoResult WaitIfNecessary(scoped_refptr<MessagePipe> mp, - MojoHandleSignals signals, - HandleSignalsState* signals_state); - -class ChannelThread { - public: - explicit ChannelThread(embedder::PlatformSupport* platform_support); - ~ChannelThread(); - - void Start(embedder::ScopedPlatformHandle platform_handle, - scoped_refptr<ChannelEndpoint> channel_endpoint); - void Stop(); - - private: - void InitChannelOnIOThread(embedder::ScopedPlatformHandle platform_handle, - scoped_refptr<ChannelEndpoint> channel_endpoint); - void ShutdownChannelOnIOThread(); - - embedder::PlatformSupport* const platform_support_; - base::TestIOThread test_io_thread_; - scoped_refptr<Channel> channel_; - - DISALLOW_COPY_AND_ASSIGN(ChannelThread); -}; - -#if !defined(OS_IOS) -class MultiprocessMessagePipeTestBase : public testing::Test { - public: - MultiprocessMessagePipeTestBase(); - ~MultiprocessMessagePipeTestBase() override; - - protected: - void Init(scoped_refptr<ChannelEndpoint> ep); - - embedder::PlatformSupport* platform_support() { return &platform_support_; } - mojo::test::MultiprocessTestHelper* helper() { return &helper_; } - - private: - embedder::SimplePlatformSupport platform_support_; - ChannelThread channel_thread_; - mojo::test::MultiprocessTestHelper helper_; - - DISALLOW_COPY_AND_ASSIGN(MultiprocessMessagePipeTestBase); -}; -#endif - -} // namespace test -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_MESSAGE_PIPE_TEST_UTILS_H_ diff --git a/mojo/edk/system/message_pipe_unittest.cc b/mojo/edk/system/message_pipe_unittest.cc deleted file mode 100644 index a30b636..0000000 --- a/mojo/edk/system/message_pipe_unittest.cc +++ /dev/null @@ -1,574 +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/edk/system/message_pipe.h" - -#include "base/memory/ref_counted.h" -#include "base/threading/platform_thread.h" // For |Sleep()|. -#include "base/time/time.h" -#include "mojo/edk/system/waiter.h" -#include "mojo/edk/system/waiter_test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -const MojoHandleSignals kAllSignals = MOJO_HANDLE_SIGNAL_READABLE | - MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED; - -// Tests: -// - only default flags -// - reading messages from a port -// - when there are no/one/two messages available for that port -// - with buffer size 0 (and null buffer) -- should get size -// - with too-small buffer -- should get size -// - also verify that buffers aren't modified when/where they shouldn't be -// - writing messages to a port -// - in the obvious scenarios (as above) -// - to a port that's been closed -// - writing a message to a port, closing the other (would be the source) port, -// and reading it -TEST(MessagePipeTest, Basic) { - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal()); - - int32_t buffer[2]; - const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer)); - uint32_t buffer_size; - - // Nothing to read yet on port 0. - buffer[0] = 123; - buffer[1] = 456; - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - mp->ReadMessage(0, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(kBufferSize, buffer_size); - EXPECT_EQ(123, buffer[0]); - EXPECT_EQ(456, buffer[1]); - - // Ditto for port 1. - buffer[0] = 123; - buffer[1] = 456; - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - mp->ReadMessage(1, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - - // Write from port 1 (to port 0). - buffer[0] = 789012345; - buffer[1] = 0; - EXPECT_EQ(MOJO_RESULT_OK, - mp->WriteMessage(1, UserPointer<const void>(buffer), - static_cast<uint32_t>(sizeof(buffer[0])), nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Read from port 0. - buffer[0] = 123; - buffer[1] = 456; - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_OK, - mp->ReadMessage(0, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); - EXPECT_EQ(789012345, buffer[0]); - EXPECT_EQ(456, buffer[1]); - - // Read again from port 0 -- it should be empty. - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - mp->ReadMessage(0, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - - // Write two messages from port 0 (to port 1). - buffer[0] = 123456789; - buffer[1] = 0; - EXPECT_EQ(MOJO_RESULT_OK, - mp->WriteMessage(0, UserPointer<const void>(buffer), - static_cast<uint32_t>(sizeof(buffer[0])), nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - buffer[0] = 234567890; - buffer[1] = 0; - EXPECT_EQ(MOJO_RESULT_OK, - mp->WriteMessage(0, UserPointer<const void>(buffer), - static_cast<uint32_t>(sizeof(buffer[0])), nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Read from port 1 with buffer size 0 (should get the size of next message). - // Also test that giving a null buffer is okay when the buffer size is 0. - buffer_size = 0; - EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, - mp->ReadMessage(1, NullUserPointer(), MakeUserPointer(&buffer_size), - 0, nullptr, MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); - - // Read from port 1 with buffer size 1 (too small; should get the size of next - // message). - buffer[0] = 123; - buffer[1] = 456; - buffer_size = 1; - EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, - mp->ReadMessage(1, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); - EXPECT_EQ(123, buffer[0]); - EXPECT_EQ(456, buffer[1]); - - // Read from port 1. - buffer[0] = 123; - buffer[1] = 456; - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_OK, - mp->ReadMessage(1, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); - EXPECT_EQ(123456789, buffer[0]); - EXPECT_EQ(456, buffer[1]); - - // Read again from port 1. - buffer[0] = 123; - buffer[1] = 456; - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_OK, - mp->ReadMessage(1, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); - EXPECT_EQ(234567890, buffer[0]); - EXPECT_EQ(456, buffer[1]); - - // Read again from port 1 -- it should be empty. - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - mp->ReadMessage(1, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - - // Write from port 0 (to port 1). - buffer[0] = 345678901; - buffer[1] = 0; - EXPECT_EQ(MOJO_RESULT_OK, - mp->WriteMessage(0, UserPointer<const void>(buffer), - static_cast<uint32_t>(sizeof(buffer[0])), nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Close port 0. - mp->Close(0); - - // Try to write from port 1 (to port 0). - buffer[0] = 456789012; - buffer[1] = 0; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - mp->WriteMessage(1, UserPointer<const void>(buffer), - static_cast<uint32_t>(sizeof(buffer[0])), nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Read from port 1; should still get message (even though port 0 was closed). - buffer[0] = 123; - buffer[1] = 456; - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_OK, - mp->ReadMessage(1, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); - EXPECT_EQ(345678901, buffer[0]); - EXPECT_EQ(456, buffer[1]); - - // Read again from port 1 -- it should be empty (and port 0 is closed). - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - mp->ReadMessage(1, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - - mp->Close(1); -} - -TEST(MessagePipeTest, CloseWithQueuedIncomingMessages) { - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal()); - - int32_t buffer[1]; - const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer)); - uint32_t buffer_size; - - // Write some messages from port 1 (to port 0). - for (int32_t i = 0; i < 5; i++) { - buffer[0] = i; - EXPECT_EQ(MOJO_RESULT_OK, - mp->WriteMessage(1, UserPointer<const void>(buffer), kBufferSize, - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - } - - // Port 0 shouldn't be empty. - buffer_size = 0; - EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, - mp->ReadMessage(0, NullUserPointer(), MakeUserPointer(&buffer_size), - 0, nullptr, MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(kBufferSize, buffer_size); - - // Close port 0 first, which should have outstanding (incoming) messages. - mp->Close(0); - mp->Close(1); -} - -TEST(MessagePipeTest, DiscardMode) { - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal()); - - int32_t buffer[2]; - const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer)); - uint32_t buffer_size; - - // Write from port 1 (to port 0). - buffer[0] = 789012345; - buffer[1] = 0; - EXPECT_EQ(MOJO_RESULT_OK, - mp->WriteMessage(1, UserPointer<const void>(buffer), - static_cast<uint32_t>(sizeof(buffer[0])), nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Read/discard from port 0 (no buffer); get size. - buffer_size = 0; - EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, - mp->ReadMessage(0, NullUserPointer(), MakeUserPointer(&buffer_size), - 0, nullptr, MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)); - EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); - - // Read again from port 0 -- it should be empty. - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - mp->ReadMessage(0, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)); - - // Write from port 1 (to port 0). - buffer[0] = 890123456; - buffer[1] = 0; - EXPECT_EQ(MOJO_RESULT_OK, - mp->WriteMessage(1, UserPointer<const void>(buffer), - static_cast<uint32_t>(sizeof(buffer[0])), nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Read from port 0 (buffer big enough). - buffer[0] = 123; - buffer[1] = 456; - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_OK, - mp->ReadMessage(0, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)); - EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); - EXPECT_EQ(890123456, buffer[0]); - EXPECT_EQ(456, buffer[1]); - - // Read again from port 0 -- it should be empty. - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - mp->ReadMessage(0, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)); - - // Write from port 1 (to port 0). - buffer[0] = 901234567; - buffer[1] = 0; - EXPECT_EQ(MOJO_RESULT_OK, - mp->WriteMessage(1, UserPointer<const void>(buffer), - static_cast<uint32_t>(sizeof(buffer[0])), nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Read/discard from port 0 (buffer too small); get size. - buffer_size = 1; - EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, - mp->ReadMessage(0, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)); - EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); - - // Read again from port 0 -- it should be empty. - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - mp->ReadMessage(0, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)); - - // Write from port 1 (to port 0). - buffer[0] = 123456789; - buffer[1] = 0; - EXPECT_EQ(MOJO_RESULT_OK, - mp->WriteMessage(1, UserPointer<const void>(buffer), - static_cast<uint32_t>(sizeof(buffer[0])), nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Discard from port 0. - buffer_size = 1; - EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, - mp->ReadMessage(0, NullUserPointer(), NullUserPointer(), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)); - - // Read again from port 0 -- it should be empty. - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - mp->ReadMessage(0, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)); - - mp->Close(0); - mp->Close(1); -} - -TEST(MessagePipeTest, BasicWaiting) { - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal()); - Waiter waiter; - HandleSignalsState hss; - - int32_t buffer[1]; - const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer)); - uint32_t buffer_size; - - // Always writable (until the other port is closed). - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - mp->AddAwakable(0, &waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - mp->AddAwakable(0, &waiter, MOJO_HANDLE_SIGNAL_READABLE | - MOJO_HANDLE_SIGNAL_WRITABLE, - 0, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Not yet readable. - waiter.Init(); - ASSERT_EQ( - MOJO_RESULT_OK, - mp->AddAwakable(0, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 1, nullptr)); - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, nullptr)); - hss = HandleSignalsState(); - mp->RemoveAwakable(0, &waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // The peer is not closed. - waiter.Init(); - ASSERT_EQ( - MOJO_RESULT_OK, - mp->AddAwakable(0, &waiter, MOJO_HANDLE_SIGNAL_PEER_CLOSED, 2, nullptr)); - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, nullptr)); - hss = HandleSignalsState(); - mp->RemoveAwakable(0, &waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Write from port 0 (to port 1), to make port 1 readable. - buffer[0] = 123456789; - EXPECT_EQ(MOJO_RESULT_OK, - mp->WriteMessage(0, UserPointer<const void>(buffer), kBufferSize, - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Port 1 should already be readable now. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - mp->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 3, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - mp->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE | - MOJO_HANDLE_SIGNAL_WRITABLE, - 0, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - // ... and still writable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - mp->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 4, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Close port 0. - mp->Close(0); - - // Port 1 should be signaled with peer closed. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ( - MOJO_RESULT_ALREADY_EXISTS, - mp->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_PEER_CLOSED, 5, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Port 1 should not be writable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - mp->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 6, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // But it should still be readable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - mp->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 7, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Read from port 1. - buffer[0] = 0; - buffer_size = kBufferSize; - EXPECT_EQ(MOJO_RESULT_OK, - mp->ReadMessage(1, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), 0, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(123456789, buffer[0]); - - // Now port 1 should no longer be readable. - waiter.Init(); - hss = HandleSignalsState(); - EXPECT_EQ( - MOJO_RESULT_FAILED_PRECONDITION, - mp->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 8, nullptr)); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(0u, hss.satisfiable_signals); - - mp->Close(1); -} - -TEST(MessagePipeTest, ThreadedWaiting) { - int32_t buffer[1]; - const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer)); - - MojoResult result; - uint32_t context; - - // Write to wake up waiter waiting for read. - { - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal()); - test::SimpleWaiterThread thread(&result, &context); - - thread.waiter()->Init(); - ASSERT_EQ(MOJO_RESULT_OK, - mp->AddAwakable(1, thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, - 1, nullptr)); - thread.Start(); - - buffer[0] = 123456789; - // Write from port 0 (to port 1), which should wake up the waiter. - EXPECT_EQ(MOJO_RESULT_OK, - mp->WriteMessage(0, UserPointer<const void>(buffer), kBufferSize, - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - HandleSignalsState hss; - mp->RemoveAwakable(1, thread.waiter(), &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - mp->Close(0); - mp->Close(1); - } // Joins |thread|. - // The waiter should have woken up successfully. - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(1u, context); - - // Close to cancel waiter. - { - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal()); - test::SimpleWaiterThread thread(&result, &context); - - thread.waiter()->Init(); - ASSERT_EQ(MOJO_RESULT_OK, - mp->AddAwakable(1, thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, - 2, nullptr)); - thread.Start(); - - // Close port 1 first -- this should result in the waiter being cancelled. - mp->CancelAllAwakables(1); - mp->Close(1); - - // Port 1 is closed, so |Dispatcher::RemoveAwakable()| wouldn't call into - // the |MessagePipe| to remove any waiter. - - mp->Close(0); - } // Joins |thread|. - EXPECT_EQ(MOJO_RESULT_CANCELLED, result); - EXPECT_EQ(2u, context); - - // Close to cancel waiter using peer closed signal. - { - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal()); - test::SimpleWaiterThread thread(&result, &context); - - thread.waiter()->Init(); - ASSERT_EQ(MOJO_RESULT_OK, - mp->AddAwakable(1, thread.waiter(), - MOJO_HANDLE_SIGNAL_PEER_CLOSED, 3, nullptr)); - thread.Start(); - - // Close port 1 first -- this should result in the waiter being cancelled. - mp->CancelAllAwakables(1); - mp->Close(1); - - // Port 1 is closed, so |Dispatcher::RemoveAwakable()| wouldn't call into - // the |MessagePipe| to remove any waiter. - - mp->Close(0); - } // Joins |thread|. - EXPECT_EQ(MOJO_RESULT_CANCELLED, result); - EXPECT_EQ(3u, context); - - // Close to make waiter un-wake-up-able. - { - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalLocal()); - test::SimpleWaiterThread thread(&result, &context); - - thread.waiter()->Init(); - ASSERT_EQ(MOJO_RESULT_OK, - mp->AddAwakable(1, thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, - 4, nullptr)); - thread.Start(); - - // Close port 0 first -- this should wake the waiter up, since port 1 will - // never be readable. - mp->CancelAllAwakables(0); - mp->Close(0); - - HandleSignalsState hss; - mp->RemoveAwakable(1, thread.waiter(), &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - mp->CancelAllAwakables(1); - mp->Close(1); - } // Joins |thread|. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); - EXPECT_EQ(4u, context); -} - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/multiprocess_message_pipe_unittest.cc b/mojo/edk/system/multiprocess_message_pipe_unittest.cc deleted file mode 100644 index af3b3bb..0000000 --- a/mojo/edk/system/multiprocess_message_pipe_unittest.cc +++ /dev/null @@ -1,520 +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 <stdint.h> -#include <stdio.h> -#include <string.h> - -#include <string> -#include <vector> - -#include "base/bind.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/files/scoped_file.h" -#include "base/files/scoped_temp_dir.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/macros.h" -#include "build/build_config.h" // TODO(vtl): Remove this. -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/message_pipe.h" -#include "mojo/edk/system/message_pipe_test_utils.h" -#include "mojo/edk/system/platform_handle_dispatcher.h" -#include "mojo/edk/system/raw_channel.h" -#include "mojo/edk/system/shared_buffer_dispatcher.h" -#include "mojo/edk/system/test_utils.h" -#include "mojo/edk/test/test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -class MultiprocessMessagePipeTest - : public test::MultiprocessMessagePipeTestBase {}; - -// For each message received, sends a reply message with the same contents -// repeated twice, until the other end is closed or it receives "quitquitquit" -// (which it doesn't reply to). It'll return the number of messages received, -// not including any "quitquitquit" message, modulo 100. -MOJO_MULTIPROCESS_TEST_CHILD_MAIN(EchoEcho) { - embedder::SimplePlatformSupport platform_support; - test::ChannelThread channel_thread(&platform_support); - embedder::ScopedPlatformHandle client_platform_handle = - mojo::test::MultiprocessTestHelper::client_platform_handle.Pass(); - CHECK(client_platform_handle.is_valid()); - scoped_refptr<ChannelEndpoint> ep; - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep)); - channel_thread.Start(client_platform_handle.Pass(), ep); - - const std::string quitquitquit("quitquitquit"); - int rv = 0; - for (;; rv = (rv + 1) % 100) { - // Wait for our end of the message pipe to be readable. - HandleSignalsState hss; - MojoResult result = - test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss); - if (result != MOJO_RESULT_OK) { - // It was closed, probably. - CHECK_EQ(result, MOJO_RESULT_FAILED_PRECONDITION); - CHECK_EQ(hss.satisfied_signals, MOJO_HANDLE_SIGNAL_PEER_CLOSED); - CHECK_EQ(hss.satisfiable_signals, MOJO_HANDLE_SIGNAL_PEER_CLOSED); - break; - } else { - CHECK((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE)); - CHECK((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE)); - } - - std::string read_buffer(1000, '\0'); - uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer.size()); - CHECK_EQ(mp->ReadMessage(0, UserPointer<void>(&read_buffer[0]), - MakeUserPointer(&read_buffer_size), nullptr, - nullptr, MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - read_buffer.resize(read_buffer_size); - VLOG(2) << "Child got: " << read_buffer; - - if (read_buffer == quitquitquit) { - VLOG(2) << "Child quitting."; - break; - } - - std::string write_buffer = read_buffer + read_buffer; - CHECK_EQ(mp->WriteMessage(0, UserPointer<const void>(write_buffer.data()), - static_cast<uint32_t>(write_buffer.size()), - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - } - - mp->Close(0); - return rv; -} - -// Sends "hello" to child, and expects "hellohello" back. -#if defined(OS_ANDROID) -// Android multi-process tests are not executing the new process. This is flaky. -#define MAYBE_Basic DISABLED_Basic -#else -#define MAYBE_Basic Basic -#endif // defined(OS_ANDROID) -TEST_F(MultiprocessMessagePipeTest, MAYBE_Basic) { - helper()->StartChild("EchoEcho"); - - scoped_refptr<ChannelEndpoint> ep; - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep)); - Init(ep); - - std::string hello("hello"); - EXPECT_EQ(MOJO_RESULT_OK, - mp->WriteMessage(0, UserPointer<const void>(hello.data()), - static_cast<uint32_t>(hello.size()), nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - HandleSignalsState hss; - EXPECT_EQ(MOJO_RESULT_OK, - test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - // The child may or may not have closed its end of the message pipe and died - // (and we may or may not know it yet), so our end may or may not appear as - // writable. - EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE)); - EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE)); - - std::string read_buffer(1000, '\0'); - uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer.size()); - CHECK_EQ(mp->ReadMessage(0, UserPointer<void>(&read_buffer[0]), - MakeUserPointer(&read_buffer_size), nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - read_buffer.resize(read_buffer_size); - VLOG(2) << "Parent got: " << read_buffer; - EXPECT_EQ(hello + hello, read_buffer); - - mp->Close(0); - - // We sent one message. - EXPECT_EQ(1 % 100, helper()->WaitForChildShutdown()); -} - -// Sends a bunch of messages to the child. Expects them "repeated" back. Waits -// for the child to close its end before quitting. -#if defined(OS_ANDROID) -// Android multi-process tests are not executing the new process. This is flaky. -#define MAYBE_QueueMessages DISABLED_QueueMessages -#else -#define MAYBE_QueueMessages QueueMessages -#endif // defined(OS_ANDROID) -TEST_F(MultiprocessMessagePipeTest, DISABLED_QueueMessages) { - helper()->StartChild("EchoEcho"); - - scoped_refptr<ChannelEndpoint> ep; - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep)); - Init(ep); - - static const size_t kNumMessages = 1001; - for (size_t i = 0; i < kNumMessages; i++) { - std::string write_buffer(i, 'A' + (i % 26)); - EXPECT_EQ(MOJO_RESULT_OK, - mp->WriteMessage(0, UserPointer<const void>(write_buffer.data()), - static_cast<uint32_t>(write_buffer.size()), - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - } - - const std::string quitquitquit("quitquitquit"); - EXPECT_EQ(MOJO_RESULT_OK, - mp->WriteMessage(0, UserPointer<const void>(quitquitquit.data()), - static_cast<uint32_t>(quitquitquit.size()), - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - for (size_t i = 0; i < kNumMessages; i++) { - HandleSignalsState hss; - EXPECT_EQ(MOJO_RESULT_OK, - test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - // The child may or may not have closed its end of the message pipe and died - // (and we may or may not know it yet), so our end may or may not appear as - // writable. - EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE)); - EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE)); - - std::string read_buffer(kNumMessages * 2, '\0'); - uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer.size()); - CHECK_EQ(mp->ReadMessage(0, UserPointer<void>(&read_buffer[0]), - MakeUserPointer(&read_buffer_size), nullptr, - nullptr, MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - read_buffer.resize(read_buffer_size); - - EXPECT_EQ(std::string(i * 2, 'A' + (i % 26)), read_buffer); - } - - // Wait for it to become readable, which should fail (since we sent - // "quitquitquit"). - HandleSignalsState hss; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - mp->Close(0); - - EXPECT_EQ(static_cast<int>(kNumMessages % 100), - helper()->WaitForChildShutdown()); -} - -MOJO_MULTIPROCESS_TEST_CHILD_MAIN(CheckSharedBuffer) { - embedder::SimplePlatformSupport platform_support; - test::ChannelThread channel_thread(&platform_support); - embedder::ScopedPlatformHandle client_platform_handle = - mojo::test::MultiprocessTestHelper::client_platform_handle.Pass(); - CHECK(client_platform_handle.is_valid()); - scoped_refptr<ChannelEndpoint> ep; - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep)); - channel_thread.Start(client_platform_handle.Pass(), ep); - - // Wait for the first message from our parent. - HandleSignalsState hss; - CHECK_EQ(test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss), - MOJO_RESULT_OK); - // In this test, the parent definitely doesn't close its end of the message - // pipe before we do. - CHECK_EQ(hss.satisfied_signals, - MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE); - CHECK_EQ(hss.satisfiable_signals, MOJO_HANDLE_SIGNAL_READABLE | - MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED); - - // It should have a shared buffer. - std::string read_buffer(100, '\0'); - uint32_t num_bytes = static_cast<uint32_t>(read_buffer.size()); - DispatcherVector dispatchers; - uint32_t num_dispatchers = 10; // Maximum number to receive. - CHECK_EQ(mp->ReadMessage(0, UserPointer<void>(&read_buffer[0]), - MakeUserPointer(&num_bytes), &dispatchers, - &num_dispatchers, MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - read_buffer.resize(num_bytes); - CHECK_EQ(read_buffer, std::string("go 1")); - CHECK_EQ(num_dispatchers, 1u); - - CHECK_EQ(dispatchers[0]->GetType(), Dispatcher::kTypeSharedBuffer); - - scoped_refptr<SharedBufferDispatcher> dispatcher( - static_cast<SharedBufferDispatcher*>(dispatchers[0].get())); - - // Make a mapping. - scoped_ptr<embedder::PlatformSharedBufferMapping> mapping; - CHECK_EQ(dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping), - MOJO_RESULT_OK); - CHECK(mapping); - CHECK(mapping->GetBase()); - CHECK_EQ(mapping->GetLength(), 100u); - - // Write some stuff to the shared buffer. - static const char kHello[] = "hello"; - memcpy(mapping->GetBase(), kHello, sizeof(kHello)); - - // We should be able to close the dispatcher now. - dispatcher->Close(); - - // And send a message to signal that we've written stuff. - const std::string go2("go 2"); - CHECK_EQ(mp->WriteMessage(0, UserPointer<const void>(&go2[0]), - static_cast<uint32_t>(go2.size()), nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - - // Now wait for our parent to send us a message. - hss = HandleSignalsState(); - CHECK_EQ(test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss), - MOJO_RESULT_OK); - CHECK_EQ(hss.satisfied_signals, - MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE); - CHECK_EQ(hss.satisfiable_signals, MOJO_HANDLE_SIGNAL_READABLE | - MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED); - - read_buffer = std::string(100, '\0'); - num_bytes = static_cast<uint32_t>(read_buffer.size()); - CHECK_EQ(mp->ReadMessage(0, UserPointer<void>(&read_buffer[0]), - MakeUserPointer(&num_bytes), nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - read_buffer.resize(num_bytes); - CHECK_EQ(read_buffer, std::string("go 3")); - - // It should have written something to the shared buffer. - static const char kWorld[] = "world!!!"; - CHECK_EQ(memcmp(mapping->GetBase(), kWorld, sizeof(kWorld)), 0); - - // And we're done. - mp->Close(0); - - return 0; -} - -#if defined(OS_POSIX) && !defined(OS_ANDROID) -#define MAYBE_SharedBufferPassing SharedBufferPassing -#else -// Not yet implemented (on Windows). -// Android multi-process tests are not executing the new process. This is flaky. -#define MAYBE_SharedBufferPassing DISABLED_SharedBufferPassing -#endif -TEST_F(MultiprocessMessagePipeTest, MAYBE_SharedBufferPassing) { - helper()->StartChild("CheckSharedBuffer"); - - scoped_refptr<ChannelEndpoint> ep; - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep)); - Init(ep); - - // Make a shared buffer. - scoped_refptr<SharedBufferDispatcher> dispatcher; - EXPECT_EQ(MOJO_RESULT_OK, SharedBufferDispatcher::Create( - platform_support(), - SharedBufferDispatcher::kDefaultCreateOptions, - 100, &dispatcher)); - ASSERT_TRUE(dispatcher); - - // Make a mapping. - scoped_ptr<embedder::PlatformSharedBufferMapping> mapping; - EXPECT_EQ(MOJO_RESULT_OK, - dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping)); - ASSERT_TRUE(mapping); - ASSERT_TRUE(mapping->GetBase()); - ASSERT_EQ(100u, mapping->GetLength()); - - // Send the shared buffer. - const std::string go1("go 1"); - DispatcherTransport transport( - test::DispatcherTryStartTransport(dispatcher.get())); - ASSERT_TRUE(transport.is_valid()); - - std::vector<DispatcherTransport> transports; - transports.push_back(transport); - EXPECT_EQ(MOJO_RESULT_OK, - mp->WriteMessage(0, UserPointer<const void>(&go1[0]), - static_cast<uint32_t>(go1.size()), &transports, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - transport.End(); - - EXPECT_TRUE(dispatcher->HasOneRef()); - dispatcher = nullptr; - - // Wait for a message from the child. - HandleSignalsState hss; - EXPECT_EQ(MOJO_RESULT_OK, - test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE)); - EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE)); - - std::string read_buffer(100, '\0'); - uint32_t num_bytes = static_cast<uint32_t>(read_buffer.size()); - EXPECT_EQ(MOJO_RESULT_OK, - mp->ReadMessage(0, UserPointer<void>(&read_buffer[0]), - MakeUserPointer(&num_bytes), nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - read_buffer.resize(num_bytes); - EXPECT_EQ(std::string("go 2"), read_buffer); - - // After we get it, the child should have written something to the shared - // buffer. - static const char kHello[] = "hello"; - EXPECT_EQ(0, memcmp(mapping->GetBase(), kHello, sizeof(kHello))); - - // Now we'll write some stuff to the shared buffer. - static const char kWorld[] = "world!!!"; - memcpy(mapping->GetBase(), kWorld, sizeof(kWorld)); - - // And send a message to signal that we've written stuff. - const std::string go3("go 3"); - EXPECT_EQ(MOJO_RESULT_OK, - mp->WriteMessage(0, UserPointer<const void>(&go3[0]), - static_cast<uint32_t>(go3.size()), nullptr, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Wait for |mp| to become readable, which should fail. - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - mp->Close(0); - - EXPECT_EQ(0, helper()->WaitForChildShutdown()); -} - -MOJO_MULTIPROCESS_TEST_CHILD_MAIN(CheckPlatformHandleFile) { - embedder::SimplePlatformSupport platform_support; - test::ChannelThread channel_thread(&platform_support); - embedder::ScopedPlatformHandle client_platform_handle = - mojo::test::MultiprocessTestHelper::client_platform_handle.Pass(); - CHECK(client_platform_handle.is_valid()); - scoped_refptr<ChannelEndpoint> ep; - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep)); - channel_thread.Start(client_platform_handle.Pass(), ep); - - HandleSignalsState hss; - CHECK_EQ(test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss), - MOJO_RESULT_OK); - CHECK_EQ(hss.satisfied_signals, - MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE); - CHECK_EQ(hss.satisfiable_signals, MOJO_HANDLE_SIGNAL_READABLE | - MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED); - - std::string read_buffer(100, '\0'); - uint32_t num_bytes = static_cast<uint32_t>(read_buffer.size()); - DispatcherVector dispatchers; - uint32_t num_dispatchers = 30; // Maximum number to receive. - CHECK_EQ(mp->ReadMessage(0, UserPointer<void>(&read_buffer[0]), - MakeUserPointer(&num_bytes), &dispatchers, - &num_dispatchers, MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - mp->Close(0); - - read_buffer.resize(num_bytes); - char hello[32]; - int num_handles = 0; - sscanf(read_buffer.c_str(), "%s %d", hello, &num_handles); - CHECK_EQ(std::string("hello"), std::string(hello)); - CHECK_GT(num_handles, 0); - - for (int i = 0; i < num_handles; ++i) { - CHECK_EQ(dispatchers[i]->GetType(), Dispatcher::kTypePlatformHandle); - - scoped_refptr<PlatformHandleDispatcher> dispatcher( - static_cast<PlatformHandleDispatcher*>(dispatchers[i].get())); - embedder::ScopedPlatformHandle h = dispatcher->PassPlatformHandle().Pass(); - CHECK(h.is_valid()); - dispatcher->Close(); - - base::ScopedFILE fp(mojo::test::FILEFromPlatformHandle(h.Pass(), "r")); - CHECK(fp); - std::string fread_buffer(100, '\0'); - size_t bytes_read = - fread(&fread_buffer[0], 1, fread_buffer.size(), fp.get()); - fread_buffer.resize(bytes_read); - CHECK_EQ(fread_buffer, "world"); - } - - return 0; -} - -class MultiprocessMessagePipeTestWithPipeCount - : public test::MultiprocessMessagePipeTestBase, - public testing::WithParamInterface<size_t> {}; - -TEST_P(MultiprocessMessagePipeTestWithPipeCount, PlatformHandlePassing) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - helper()->StartChild("CheckPlatformHandleFile"); - - scoped_refptr<ChannelEndpoint> ep; - scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep)); - Init(ep); - - std::vector<scoped_refptr<PlatformHandleDispatcher>> dispatchers; - std::vector<DispatcherTransport> transports; - - size_t pipe_count = GetParam(); - for (size_t i = 0; i < pipe_count; ++i) { - base::FilePath unused; - base::ScopedFILE fp( - CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused)); - const std::string world("world"); - CHECK_EQ(fwrite(&world[0], 1, world.size(), fp.get()), world.size()); - fflush(fp.get()); - rewind(fp.get()); - - scoped_refptr<PlatformHandleDispatcher> dispatcher( - new PlatformHandleDispatcher(embedder::ScopedPlatformHandle( - mojo::test::PlatformHandleFromFILE(fp.Pass())))); - dispatchers.push_back(dispatcher); - DispatcherTransport transport( - test::DispatcherTryStartTransport(dispatcher.get())); - ASSERT_TRUE(transport.is_valid()); - transports.push_back(transport); - } - - char message[128]; - sprintf(message, "hello %d", static_cast<int>(pipe_count)); - EXPECT_EQ(MOJO_RESULT_OK, - mp->WriteMessage(0, UserPointer<const void>(message), - static_cast<uint32_t>(strlen(message)), - &transports, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - for (size_t i = 0; i < pipe_count; ++i) { - transports[i].End(); - EXPECT_TRUE(dispatchers[i]->HasOneRef()); - } - - dispatchers.clear(); - - // Wait for it to become readable, which should fail. - HandleSignalsState hss; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - mp->Close(0); - - EXPECT_EQ(0, helper()->WaitForChildShutdown()); -} - -// Not yet implemented (on Windows). -// Android multi-process tests are not executing the new process. This is flaky. -#if defined(OS_POSIX) && !defined(OS_ANDROID) -INSTANTIATE_TEST_CASE_P(PipeCount, - MultiprocessMessagePipeTestWithPipeCount, - testing::Values(1u, 10u, 25u)); -#endif - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/options_validation.h b/mojo/edk/system/options_validation.h deleted file mode 100644 index 9724e38..0000000 --- a/mojo/edk/system/options_validation.h +++ /dev/null @@ -1,102 +0,0 @@ -// 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. - -// Functions to help with verifying various |Mojo...Options| structs from the -// (public, C) API. These are "extensible" structs, which all have |struct_size| -// as their first member. All fields (other than |struct_size|) are optional, -// but any |flags| specified must be known to the system (otherwise, an error of -// |MOJO_RESULT_UNIMPLEMENTED| should be returned). - -#ifndef MOJO_EDK_SYSTEM_OPTIONS_VALIDATION_H_ -#define MOJO_EDK_SYSTEM_OPTIONS_VALIDATION_H_ - -#include <stddef.h> -#include <stdint.h> - -#include <algorithm> - -#include "base/logging.h" -#include "base/macros.h" -#include "mojo/edk/system/memory.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { -namespace system { - -template <class Options> -class UserOptionsReader { - public: - // Constructor from a |UserPointer<const Options>| (which it checks -- this - // constructor has side effects!). - // Note: We initialize |options_reader_| without checking, since we do a check - // in |GetSizeForReader()|. - explicit UserOptionsReader(UserPointer<const Options> options) - : options_reader_(UserPointer<const char>::Reader::NoCheck(), - options.template ReinterpretCast<const char>(), - GetSizeForReader(options)) { - static_assert(offsetof(Options, struct_size) == 0, - "struct_size not first member of Options"); - // TODO(vtl): Enable when MSVC supports this (C++11 extended sizeof): - // static_assert(sizeof(Options::struct_size) == sizeof(uint32_t), - // "Options::struct_size not a uint32_t"); - // (Or maybe assert that its type is uint32_t?) - } - - bool is_valid() const { return !!options_reader_.GetPointer(); } - - const Options& options() const { - DCHECK(is_valid()); - return *reinterpret_cast<const Options*>(options_reader_.GetPointer()); - } - - // Checks that the given (variable-size) |options| passed to the constructor - // (plausibly) has a member at the given offset with the given size. You - // probably want to use |OPTIONS_STRUCT_HAS_MEMBER()| instead. - bool HasMember(size_t offset, size_t size) const { - DCHECK(is_valid()); - // We assume that |offset| and |size| are reasonable, since they should come - // from |offsetof(Options, some_member)| and |sizeof(Options::some_member)|, - // respectively. - return options().struct_size >= offset + size; - } - - private: - static inline size_t GetSizeForReader(UserPointer<const Options> options) { - uint32_t struct_size = - options.template ReinterpretCast<const uint32_t>().Get(); - if (struct_size < sizeof(uint32_t)) - return 0; - - // Check the full requested size. - // Note: Use |MOJO_ALIGNOF()| here to match the exact macro used in the - // declaration of Options structs. - internal::CheckUserPointerWithSize<MOJO_ALIGNOF(Options)>(options.pointer_, - struct_size); - options.template ReinterpretCast<const char>().CheckArray(struct_size); - // But we'll never look at more than |sizeof(Options)| bytes. - return std::min(static_cast<size_t>(struct_size), sizeof(Options)); - } - - UserPointer<const char>::Reader options_reader_; - - DISALLOW_COPY_AND_ASSIGN(UserOptionsReader); -}; - -// Macro to invoke |UserOptionsReader<Options>::HasMember()| parametrized by -// member name instead of offset and size. -// -// (We can't just give |HasMember()| a member pointer template argument instead, -// since there's no good/strictly-correct way to get an offset from that.) -// -// TODO(vtl): With C++11, use |sizeof(Options::member)| instead of (the -// contortion below). We might also be able to pull out the type |Options| from -// |reader| (using |decltype|) instead of requiring a parameter. -#define OPTIONS_STRUCT_HAS_MEMBER(Options, member, reader) \ - reader.HasMember(offsetof(Options, member), sizeof(reader.options().member)) - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_OPTIONS_VALIDATION_H_ diff --git a/mojo/edk/system/options_validation_unittest.cc b/mojo/edk/system/options_validation_unittest.cc deleted file mode 100644 index 89c4e60..0000000 --- a/mojo/edk/system/options_validation_unittest.cc +++ /dev/null @@ -1,130 +0,0 @@ -// 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/edk/system/options_validation.h" - -#include <stddef.h> -#include <stdint.h> - -#include "mojo/public/c/system/macros.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -// Declare a test options struct just as we do in actual public headers. - -typedef uint32_t TestOptionsFlags; - -static_assert(MOJO_ALIGNOF(int64_t) == 8, "int64_t has weird alignment"); -struct MOJO_ALIGNAS(8) TestOptions { - uint32_t struct_size; - TestOptionsFlags flags; - uint32_t member1; - uint32_t member2; -}; -static_assert(sizeof(TestOptions) == 16, "TestOptions has wrong size"); - -const uint32_t kSizeOfTestOptions = static_cast<uint32_t>(sizeof(TestOptions)); - -TEST(OptionsValidationTest, Valid) { - { - const TestOptions kOptions = {kSizeOfTestOptions}; - UserOptionsReader<TestOptions> reader(MakeUserPointer(&kOptions)); - EXPECT_TRUE(reader.is_valid()); - EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, flags, reader)); - EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member1, reader)); - EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member2, reader)); - } - { - const TestOptions kOptions = {static_cast<uint32_t>( - offsetof(TestOptions, struct_size) + sizeof(uint32_t))}; - UserOptionsReader<TestOptions> reader(MakeUserPointer(&kOptions)); - EXPECT_TRUE(reader.is_valid()); - EXPECT_FALSE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, flags, reader)); - EXPECT_FALSE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member1, reader)); - EXPECT_FALSE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member2, reader)); - } - - { - const TestOptions kOptions = { - static_cast<uint32_t>(offsetof(TestOptions, flags) + sizeof(uint32_t))}; - UserOptionsReader<TestOptions> reader(MakeUserPointer(&kOptions)); - EXPECT_TRUE(reader.is_valid()); - EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, flags, reader)); - EXPECT_FALSE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member1, reader)); - EXPECT_FALSE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member2, reader)); - } - { - MOJO_ALIGNAS(8) char buf[sizeof(TestOptions) + 100] = {}; - TestOptions* options = reinterpret_cast<TestOptions*>(buf); - options->struct_size = kSizeOfTestOptions + 1; - UserOptionsReader<TestOptions> reader(MakeUserPointer(options)); - EXPECT_TRUE(reader.is_valid()); - EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, flags, reader)); - EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member1, reader)); - EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member2, reader)); - } - { - MOJO_ALIGNAS(8) char buf[sizeof(TestOptions) + 100] = {}; - TestOptions* options = reinterpret_cast<TestOptions*>(buf); - options->struct_size = kSizeOfTestOptions + 4; - UserOptionsReader<TestOptions> reader(MakeUserPointer(options)); - EXPECT_TRUE(reader.is_valid()); - EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, flags, reader)); - EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member1, reader)); - EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member2, reader)); - } -} - -TEST(OptionsValidationTest, Invalid) { - // Size too small: - for (size_t i = 0; i < sizeof(uint32_t); i++) { - TestOptions options = {static_cast<uint32_t>(i)}; - UserOptionsReader<TestOptions> reader(MakeUserPointer(&options)); - EXPECT_FALSE(reader.is_valid()) << i; - } -} - -// These test invalid arguments that should cause death if we're being paranoid -// about checking arguments (which we would want to do if, e.g., we were in a -// true "kernel" situation, but we might not want to do otherwise for -// performance reasons). Probably blatant errors like passing in null pointers -// (for required pointer arguments) will still cause death, but perhaps not -// predictably. -TEST(OptionsValidationTest, InvalidDeath) { - const char kMemoryCheckFailedRegex[] = "Check failed"; - - // Null: - EXPECT_DEATH_IF_SUPPORTED( - { UserOptionsReader<TestOptions> reader((NullUserPointer())); }, - kMemoryCheckFailedRegex); - - // Unaligned: - EXPECT_DEATH_IF_SUPPORTED( - { - UserOptionsReader<TestOptions> reader( - MakeUserPointer(reinterpret_cast<const TestOptions*>(1))); - }, - kMemoryCheckFailedRegex); - // Note: The current implementation checks the size only after checking the - // alignment versus that required for the |uint32_t| size, so it won't die in - // the expected way if you pass, e.g., 4. So we have to manufacture a valid - // pointer at an offset of alignment 4. - EXPECT_DEATH_IF_SUPPORTED( - { - uint32_t buffer[100] = {}; - TestOptions* options = (reinterpret_cast<uintptr_t>(buffer) % 8 == 0) - ? reinterpret_cast<TestOptions*>(&buffer[1]) - : reinterpret_cast<TestOptions*>(&buffer[0]); - options->struct_size = static_cast<uint32_t>(sizeof(TestOptions)); - UserOptionsReader<TestOptions> reader(MakeUserPointer(options)); - }, - kMemoryCheckFailedRegex); -} - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/platform_handle_dispatcher.cc b/mojo/edk/system/platform_handle_dispatcher.cc deleted file mode 100644 index f064c67..0000000 --- a/mojo/edk/system/platform_handle_dispatcher.cc +++ /dev/null @@ -1,118 +0,0 @@ -// 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/edk/system/platform_handle_dispatcher.h" - -#include <algorithm> - -#include "base/logging.h" - -namespace mojo { -namespace system { - -namespace { - -const size_t kInvalidPlatformHandleIndex = static_cast<size_t>(-1); - -struct SerializedPlatformHandleDispatcher { - size_t platform_handle_index; // (Or |kInvalidPlatformHandleIndex|.) -}; - -} // namespace - -PlatformHandleDispatcher::PlatformHandleDispatcher( - embedder::ScopedPlatformHandle platform_handle) - : platform_handle_(platform_handle.Pass()) { -} - -embedder::ScopedPlatformHandle PlatformHandleDispatcher::PassPlatformHandle() { - base::AutoLock locker(lock()); - return platform_handle_.Pass(); -} - -Dispatcher::Type PlatformHandleDispatcher::GetType() const { - return kTypePlatformHandle; -} - -// static -scoped_refptr<PlatformHandleDispatcher> PlatformHandleDispatcher::Deserialize( - Channel* channel, - const void* source, - size_t size, - embedder::PlatformHandleVector* platform_handles) { - if (size != sizeof(SerializedPlatformHandleDispatcher)) { - LOG(ERROR) << "Invalid serialized platform handle dispatcher (bad size)"; - return nullptr; - } - - const SerializedPlatformHandleDispatcher* serialization = - static_cast<const SerializedPlatformHandleDispatcher*>(source); - size_t platform_handle_index = serialization->platform_handle_index; - - // Starts off invalid, which is what we want. - embedder::PlatformHandle platform_handle; - - if (platform_handle_index != kInvalidPlatformHandleIndex) { - if (!platform_handles || - platform_handle_index >= platform_handles->size()) { - LOG(ERROR) - << "Invalid serialized platform handle dispatcher (missing handles)"; - return nullptr; - } - - // We take ownership of the handle, so we have to invalidate the one in - // |platform_handles|. - std::swap(platform_handle, (*platform_handles)[platform_handle_index]); - } - - return scoped_refptr<PlatformHandleDispatcher>(new PlatformHandleDispatcher( - embedder::ScopedPlatformHandle(platform_handle))); -} - -PlatformHandleDispatcher::~PlatformHandleDispatcher() { -} - -void PlatformHandleDispatcher::CloseImplNoLock() { - lock().AssertAcquired(); - platform_handle_.reset(); -} - -scoped_refptr<Dispatcher> -PlatformHandleDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() { - lock().AssertAcquired(); - return scoped_refptr<Dispatcher>( - new PlatformHandleDispatcher(platform_handle_.Pass())); -} - -void PlatformHandleDispatcher::StartSerializeImplNoLock( - Channel* /*channel*/, - size_t* max_size, - size_t* max_platform_handles) { - DCHECK(HasOneRef()); // Only one ref => no need to take the lock. - *max_size = sizeof(SerializedPlatformHandleDispatcher); - *max_platform_handles = 1; -} - -bool PlatformHandleDispatcher::EndSerializeAndCloseImplNoLock( - Channel* /*channel*/, - void* destination, - size_t* actual_size, - embedder::PlatformHandleVector* platform_handles) { - DCHECK(HasOneRef()); // Only one ref => no need to take the lock. - - SerializedPlatformHandleDispatcher* serialization = - static_cast<SerializedPlatformHandleDispatcher*>(destination); - if (platform_handle_.is_valid()) { - serialization->platform_handle_index = platform_handles->size(); - platform_handles->push_back(platform_handle_.release()); - } else { - serialization->platform_handle_index = kInvalidPlatformHandleIndex; - } - - *actual_size = sizeof(SerializedPlatformHandleDispatcher); - return true; -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/platform_handle_dispatcher.h b/mojo/edk/system/platform_handle_dispatcher.h deleted file mode 100644 index 6eb56b7..0000000 --- a/mojo/edk/system/platform_handle_dispatcher.h +++ /dev/null @@ -1,61 +0,0 @@ -// 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_EDK_SYSTEM_PLATFORM_HANDLE_DISPATCHER_H_ -#define MOJO_EDK_SYSTEM_PLATFORM_HANDLE_DISPATCHER_H_ - -#include "base/macros.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/simple_dispatcher.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace system { - -// A dispatcher that simply wraps/transports a |PlatformHandle| (only for use by -// the embedder). -class MOJO_SYSTEM_IMPL_EXPORT PlatformHandleDispatcher - : public SimpleDispatcher { - public: - explicit PlatformHandleDispatcher( - embedder::ScopedPlatformHandle platform_handle); - - embedder::ScopedPlatformHandle PassPlatformHandle(); - - // |Dispatcher| public methods: - Type GetType() const override; - - // The "opposite" of |SerializeAndClose()|. (Typically this is called by - // |Dispatcher::Deserialize()|.) - static scoped_refptr<PlatformHandleDispatcher> Deserialize( - Channel* channel, - const void* source, - size_t size, - embedder::PlatformHandleVector* platform_handles); - - private: - ~PlatformHandleDispatcher() override; - - // |Dispatcher| protected methods: - void CloseImplNoLock() override; - scoped_refptr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock() - override; - void StartSerializeImplNoLock(Channel* channel, - size_t* max_size, - size_t* max_platform_handles) override; - bool EndSerializeAndCloseImplNoLock( - Channel* channel, - void* destination, - size_t* actual_size, - embedder::PlatformHandleVector* platform_handles) override; - - embedder::ScopedPlatformHandle platform_handle_; - - DISALLOW_COPY_AND_ASSIGN(PlatformHandleDispatcher); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_PLATFORM_HANDLE_DISPATCHER_H_ diff --git a/mojo/edk/system/platform_handle_dispatcher_unittest.cc b/mojo/edk/system/platform_handle_dispatcher_unittest.cc deleted file mode 100644 index dae53b4..0000000 --- a/mojo/edk/system/platform_handle_dispatcher_unittest.cc +++ /dev/null @@ -1,111 +0,0 @@ -// 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/edk/system/platform_handle_dispatcher.h" - -#include <stdio.h> - -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/files/scoped_file.h" -#include "base/files/scoped_temp_dir.h" -#include "base/memory/ref_counted.h" -#include "mojo/edk/test/test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -TEST(PlatformHandleDispatcherTest, Basic) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - static const char kHelloWorld[] = "hello world"; - - base::FilePath unused; - base::ScopedFILE fp( - CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused)); - ASSERT_TRUE(fp); - EXPECT_EQ(sizeof(kHelloWorld), - fwrite(kHelloWorld, 1, sizeof(kHelloWorld), fp.get())); - - embedder::ScopedPlatformHandle h( - mojo::test::PlatformHandleFromFILE(fp.Pass())); - EXPECT_FALSE(fp); - ASSERT_TRUE(h.is_valid()); - - scoped_refptr<PlatformHandleDispatcher> dispatcher( - new PlatformHandleDispatcher(h.Pass())); - EXPECT_FALSE(h.is_valid()); - EXPECT_EQ(Dispatcher::kTypePlatformHandle, dispatcher->GetType()); - - h = dispatcher->PassPlatformHandle().Pass(); - EXPECT_TRUE(h.is_valid()); - - fp = mojo::test::FILEFromPlatformHandle(h.Pass(), "rb").Pass(); - EXPECT_FALSE(h.is_valid()); - EXPECT_TRUE(fp); - - rewind(fp.get()); - char read_buffer[1000] = {}; - EXPECT_EQ(sizeof(kHelloWorld), - fread(read_buffer, 1, sizeof(read_buffer), fp.get())); - EXPECT_STREQ(kHelloWorld, read_buffer); - - // Try getting the handle again. (It should fail cleanly.) - h = dispatcher->PassPlatformHandle().Pass(); - EXPECT_FALSE(h.is_valid()); - - EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close()); -} - -TEST(PlatformHandleDispatcherTest, CreateEquivalentDispatcherAndClose) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - static const char kFooBar[] = "foo bar"; - - base::FilePath unused; - base::ScopedFILE fp( - CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused)); - EXPECT_EQ(sizeof(kFooBar), fwrite(kFooBar, 1, sizeof(kFooBar), fp.get())); - - scoped_refptr<PlatformHandleDispatcher> dispatcher( - new PlatformHandleDispatcher( - mojo::test::PlatformHandleFromFILE(fp.Pass()))); - - DispatcherTransport transport( - test::DispatcherTryStartTransport(dispatcher.get())); - EXPECT_TRUE(transport.is_valid()); - EXPECT_EQ(Dispatcher::kTypePlatformHandle, transport.GetType()); - EXPECT_FALSE(transport.IsBusy()); - - scoped_refptr<Dispatcher> generic_dispatcher = - transport.CreateEquivalentDispatcherAndClose(); - ASSERT_TRUE(generic_dispatcher); - - transport.End(); - EXPECT_TRUE(dispatcher->HasOneRef()); - dispatcher = nullptr; - - ASSERT_EQ(Dispatcher::kTypePlatformHandle, generic_dispatcher->GetType()); - dispatcher = static_cast<PlatformHandleDispatcher*>(generic_dispatcher.get()); - - fp = mojo::test::FILEFromPlatformHandle(dispatcher->PassPlatformHandle(), - "rb").Pass(); - EXPECT_TRUE(fp); - - rewind(fp.get()); - char read_buffer[1000] = {}; - EXPECT_EQ(sizeof(kFooBar), - fread(read_buffer, 1, sizeof(read_buffer), fp.get())); - EXPECT_STREQ(kFooBar, read_buffer); - - EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close()); -} - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/proxy_message_pipe_endpoint.cc b/mojo/edk/system/proxy_message_pipe_endpoint.cc deleted file mode 100644 index 5f0f2e9..0000000 --- a/mojo/edk/system/proxy_message_pipe_endpoint.cc +++ /dev/null @@ -1,65 +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/edk/system/proxy_message_pipe_endpoint.h" - -#include <string.h> - -#include "base/logging.h" -#include "mojo/edk/system/channel_endpoint.h" -#include "mojo/edk/system/local_message_pipe_endpoint.h" -#include "mojo/edk/system/message_pipe_dispatcher.h" - -namespace mojo { -namespace system { - -ProxyMessagePipeEndpoint::ProxyMessagePipeEndpoint( - ChannelEndpoint* channel_endpoint) - : channel_endpoint_(channel_endpoint) { -} - -ProxyMessagePipeEndpoint::~ProxyMessagePipeEndpoint() { - DCHECK(!channel_endpoint_); -} - -scoped_refptr<ChannelEndpoint> -ProxyMessagePipeEndpoint::ReleaseChannelEndpoint() { - DCHECK(channel_endpoint_); - scoped_refptr<ChannelEndpoint> rv; - rv.swap(channel_endpoint_); - return rv; -} - -MessagePipeEndpoint::Type ProxyMessagePipeEndpoint::GetType() const { - return kTypeProxy; -} - -bool ProxyMessagePipeEndpoint::OnPeerClose() { - DetachIfNecessary(); - return false; -} - -// Note: We may have to enqueue messages even when our (local) peer isn't open -// -- it may have been written to and closed immediately, before we were ready. -// This case is handled in |Run()| (which will call us). -void ProxyMessagePipeEndpoint::EnqueueMessage( - scoped_ptr<MessageInTransit> message) { - DCHECK(channel_endpoint_); - LOG_IF(WARNING, !channel_endpoint_->EnqueueMessage(message.Pass())) - << "Failed to write enqueue message to channel"; -} - -void ProxyMessagePipeEndpoint::Close() { - DetachIfNecessary(); -} - -void ProxyMessagePipeEndpoint::DetachIfNecessary() { - if (channel_endpoint_) { - channel_endpoint_->DetachFromClient(); - channel_endpoint_ = nullptr; - } -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/proxy_message_pipe_endpoint.h b/mojo/edk/system/proxy_message_pipe_endpoint.h deleted file mode 100644 index 6e10699..0000000 --- a/mojo/edk/system/proxy_message_pipe_endpoint.h +++ /dev/null @@ -1,61 +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_EDK_SYSTEM_PROXY_MESSAGE_PIPE_ENDPOINT_H_ -#define MOJO_EDK_SYSTEM_PROXY_MESSAGE_PIPE_ENDPOINT_H_ - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "mojo/edk/system/message_in_transit.h" -#include "mojo/edk/system/message_pipe_endpoint.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace system { - -class ChannelEndpoint; -class LocalMessagePipeEndpoint; -class MessagePipe; - -// A |ProxyMessagePipeEndpoint| is an endpoint which delegates everything to a -// |ChannelEndpoint|, which may be co-owned by a |Channel|. Like any -// |MessagePipeEndpoint|, a |ProxyMessagePipeEndpoint| is owned by a -// |MessagePipe|. -// -// For example, a |MessagePipe| with one endpoint local and the other endpoint -// remote consists of a |LocalMessagePipeEndpoint| and a -// |ProxyMessagePipeEndpoint|, with only the local endpoint being accessible via -// a |MessagePipeDispatcher|. -class MOJO_SYSTEM_IMPL_EXPORT ProxyMessagePipeEndpoint - : public MessagePipeEndpoint { - public: - explicit ProxyMessagePipeEndpoint(ChannelEndpoint* channel_endpoint); - ~ProxyMessagePipeEndpoint() override; - - // Returns |channel_endpoint_| and resets |channel_endpoint_| to null. This - // may be called at most once, after which |Close()| need not be called. - // - // Note: The returned |ChannelEndpoint| must have its client changed while - // still under |MessagePipe|'s lock (which this must have also been called - // under). - scoped_refptr<ChannelEndpoint> ReleaseChannelEndpoint(); - - // |MessagePipeEndpoint| implementation: - Type GetType() const override; - bool OnPeerClose() override; - void EnqueueMessage(scoped_ptr<MessageInTransit> message) override; - void Close() override; - - private: - void DetachIfNecessary(); - - scoped_refptr<ChannelEndpoint> channel_endpoint_; - - DISALLOW_COPY_AND_ASSIGN(ProxyMessagePipeEndpoint); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_PROXY_MESSAGE_PIPE_ENDPOINT_H_ diff --git a/mojo/edk/system/raw_channel.cc b/mojo/edk/system/raw_channel.cc deleted file mode 100644 index aff1110..0000000 --- a/mojo/edk/system/raw_channel.cc +++ /dev/null @@ -1,514 +0,0 @@ -// 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/edk/system/raw_channel.h" - -#include <string.h> - -#include <algorithm> - -#include "base/bind.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/message_loop/message_loop.h" -#include "base/stl_util.h" -#include "mojo/edk/system/message_in_transit.h" -#include "mojo/edk/system/transport_data.h" - -namespace mojo { -namespace system { - -const size_t kReadSize = 4096; - -// RawChannel::ReadBuffer ------------------------------------------------------ - -RawChannel::ReadBuffer::ReadBuffer() : buffer_(kReadSize), num_valid_bytes_(0) { -} - -RawChannel::ReadBuffer::~ReadBuffer() { -} - -void RawChannel::ReadBuffer::GetBuffer(char** addr, size_t* size) { - DCHECK_GE(buffer_.size(), num_valid_bytes_ + kReadSize); - *addr = &buffer_[0] + num_valid_bytes_; - *size = kReadSize; -} - -// RawChannel::WriteBuffer ----------------------------------------------------- - -RawChannel::WriteBuffer::WriteBuffer(size_t serialized_platform_handle_size) - : serialized_platform_handle_size_(serialized_platform_handle_size), - platform_handles_offset_(0), - data_offset_(0) { -} - -RawChannel::WriteBuffer::~WriteBuffer() { - STLDeleteElements(&message_queue_); -} - -bool RawChannel::WriteBuffer::HavePlatformHandlesToSend() const { - if (message_queue_.empty()) - return false; - - const TransportData* transport_data = - message_queue_.front()->transport_data(); - if (!transport_data) - return false; - - const embedder::PlatformHandleVector* all_platform_handles = - transport_data->platform_handles(); - if (!all_platform_handles) { - DCHECK_EQ(platform_handles_offset_, 0u); - return false; - } - if (platform_handles_offset_ >= all_platform_handles->size()) { - DCHECK_EQ(platform_handles_offset_, all_platform_handles->size()); - return false; - } - - return true; -} - -void RawChannel::WriteBuffer::GetPlatformHandlesToSend( - size_t* num_platform_handles, - embedder::PlatformHandle** platform_handles, - void** serialization_data) { - DCHECK(HavePlatformHandlesToSend()); - - MessageInTransit* message = message_queue_.front(); - TransportData* transport_data = message->transport_data(); - embedder::PlatformHandleVector* all_platform_handles = - transport_data->platform_handles(); - *num_platform_handles = - all_platform_handles->size() - platform_handles_offset_; - *platform_handles = &(*all_platform_handles)[platform_handles_offset_]; - - if (serialized_platform_handle_size_ > 0) { - size_t serialization_data_offset = - transport_data->platform_handle_table_offset(); - DCHECK_GT(serialization_data_offset, 0u); - serialization_data_offset += - platform_handles_offset_ * serialized_platform_handle_size_; - *serialization_data = static_cast<char*>(transport_data->buffer()) + - serialization_data_offset; - } else { - *serialization_data = nullptr; - } -} - -void RawChannel::WriteBuffer::GetBuffers(std::vector<Buffer>* buffers) const { - buffers->clear(); - - if (message_queue_.empty()) - return; - - MessageInTransit* message = message_queue_.front(); - DCHECK_LT(data_offset_, message->total_size()); - size_t bytes_to_write = message->total_size() - data_offset_; - - size_t transport_data_buffer_size = - message->transport_data() ? message->transport_data()->buffer_size() : 0; - - if (!transport_data_buffer_size) { - // Only write from the main buffer. - DCHECK_LT(data_offset_, message->main_buffer_size()); - DCHECK_LE(bytes_to_write, message->main_buffer_size()); - Buffer buffer = { - static_cast<const char*>(message->main_buffer()) + data_offset_, - bytes_to_write}; - buffers->push_back(buffer); - return; - } - - if (data_offset_ >= message->main_buffer_size()) { - // Only write from the transport data buffer. - DCHECK_LT(data_offset_ - message->main_buffer_size(), - transport_data_buffer_size); - DCHECK_LE(bytes_to_write, transport_data_buffer_size); - Buffer buffer = { - static_cast<const char*>(message->transport_data()->buffer()) + - (data_offset_ - message->main_buffer_size()), - bytes_to_write}; - buffers->push_back(buffer); - return; - } - - // TODO(vtl): We could actually send out buffers from multiple messages, with - // the "stopping" condition being reaching a message with platform handles - // attached. - - // Write from both buffers. - DCHECK_EQ(bytes_to_write, message->main_buffer_size() - data_offset_ + - transport_data_buffer_size); - Buffer buffer1 = { - static_cast<const char*>(message->main_buffer()) + data_offset_, - message->main_buffer_size() - data_offset_}; - buffers->push_back(buffer1); - Buffer buffer2 = { - static_cast<const char*>(message->transport_data()->buffer()), - transport_data_buffer_size}; - buffers->push_back(buffer2); -} - -// RawChannel ------------------------------------------------------------------ - -RawChannel::RawChannel() - : message_loop_for_io_(nullptr), - delegate_(nullptr), - read_stopped_(false), - write_stopped_(false), - weak_ptr_factory_(this) { -} - -RawChannel::~RawChannel() { - DCHECK(!read_buffer_); - DCHECK(!write_buffer_); - - // No need to take the |write_lock_| here -- if there are still weak pointers - // outstanding, then we're hosed anyway (since we wouldn't be able to - // invalidate them cleanly, since we might not be on the I/O thread). - DCHECK(!weak_ptr_factory_.HasWeakPtrs()); -} - -void RawChannel::Init(Delegate* delegate) { - DCHECK(delegate); - - DCHECK(!delegate_); - delegate_ = delegate; - - CHECK_EQ(base::MessageLoop::current()->type(), base::MessageLoop::TYPE_IO); - DCHECK(!message_loop_for_io_); - message_loop_for_io_ = - static_cast<base::MessageLoopForIO*>(base::MessageLoop::current()); - - // No need to take the lock. No one should be using us yet. - DCHECK(!read_buffer_); - read_buffer_.reset(new ReadBuffer); - DCHECK(!write_buffer_); - write_buffer_.reset(new WriteBuffer(GetSerializedPlatformHandleSize())); - - OnInit(); - - IOResult io_result = ScheduleRead(); - if (io_result != IO_PENDING) { - // This will notify the delegate about the read failure. Although we're on - // the I/O thread, don't call it in the nested context. - message_loop_for_io_->PostTask( - FROM_HERE, base::Bind(&RawChannel::OnReadCompleted, - weak_ptr_factory_.GetWeakPtr(), io_result, 0)); - } - // Note: |ScheduleRead()| failure is treated as a read failure (by notifying - // the delegate), not an initialization failure. -} - -void RawChannel::Shutdown() { - DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io_); - - base::AutoLock locker(write_lock_); - - LOG_IF(WARNING, !write_buffer_->message_queue_.empty()) - << "Shutting down RawChannel with write buffer nonempty"; - - // Reset the delegate so that it won't receive further calls. - delegate_ = nullptr; - read_stopped_ = true; - write_stopped_ = true; - weak_ptr_factory_.InvalidateWeakPtrs(); - - OnShutdownNoLock(read_buffer_.Pass(), write_buffer_.Pass()); -} - -// Reminder: This must be thread-safe. -bool RawChannel::WriteMessage(scoped_ptr<MessageInTransit> message) { - DCHECK(message); - - base::AutoLock locker(write_lock_); - if (write_stopped_) - return false; - - if (!write_buffer_->message_queue_.empty()) { - EnqueueMessageNoLock(message.Pass()); - return true; - } - - EnqueueMessageNoLock(message.Pass()); - DCHECK_EQ(write_buffer_->data_offset_, 0u); - - size_t platform_handles_written = 0; - size_t bytes_written = 0; - IOResult io_result = WriteNoLock(&platform_handles_written, &bytes_written); - if (io_result == IO_PENDING) - return true; - - bool result = OnWriteCompletedNoLock(io_result, platform_handles_written, - bytes_written); - if (!result) { - // Even if we're on the I/O thread, don't call |OnError()| in the nested - // context. - message_loop_for_io_->PostTask( - FROM_HERE, - base::Bind(&RawChannel::CallOnError, weak_ptr_factory_.GetWeakPtr(), - Delegate::ERROR_WRITE)); - } - - return result; -} - -// Reminder: This must be thread-safe. -bool RawChannel::IsWriteBufferEmpty() { - base::AutoLock locker(write_lock_); - return write_buffer_->message_queue_.empty(); -} - -void RawChannel::OnReadCompleted(IOResult io_result, size_t bytes_read) { - DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io_); - - if (read_stopped_) { - NOTREACHED(); - return; - } - - // Keep reading data in a loop, and dispatch messages if enough data is - // received. Exit the loop if any of the following happens: - // - one or more messages were dispatched; - // - the last read failed, was a partial read or would block; - // - |Shutdown()| was called. - do { - switch (io_result) { - case IO_SUCCEEDED: - break; - case IO_FAILED_SHUTDOWN: - case IO_FAILED_BROKEN: - case IO_FAILED_UNKNOWN: - read_stopped_ = true; - CallOnError(ReadIOResultToError(io_result)); - return; - case IO_PENDING: - NOTREACHED(); - return; - } - - read_buffer_->num_valid_bytes_ += bytes_read; - - // Dispatch all the messages that we can. - bool did_dispatch_message = false; - // Tracks the offset of the first undispatched message in |read_buffer_|. - // Currently, we copy data to ensure that this is zero at the beginning. - size_t read_buffer_start = 0; - size_t remaining_bytes = read_buffer_->num_valid_bytes_; - size_t message_size; - // Note that we rely on short-circuit evaluation here: - // - |read_buffer_start| may be an invalid index into - // |read_buffer_->buffer_| if |remaining_bytes| is zero. - // - |message_size| is only valid if |GetNextMessageSize()| returns true. - // TODO(vtl): Use |message_size| more intelligently (e.g., to request the - // next read). - // TODO(vtl): Validate that |message_size| is sane. - while (remaining_bytes > 0 && MessageInTransit::GetNextMessageSize( - &read_buffer_->buffer_[read_buffer_start], - remaining_bytes, &message_size) && - remaining_bytes >= message_size) { - MessageInTransit::View message_view( - message_size, &read_buffer_->buffer_[read_buffer_start]); - DCHECK_EQ(message_view.total_size(), message_size); - - const char* error_message = nullptr; - if (!message_view.IsValid(GetSerializedPlatformHandleSize(), - &error_message)) { - DCHECK(error_message); - LOG(ERROR) << "Received invalid message: " << error_message; - read_stopped_ = true; - CallOnError(Delegate::ERROR_READ_BAD_MESSAGE); - return; - } - - if (message_view.type() == MessageInTransit::kTypeRawChannel) { - if (!OnReadMessageForRawChannel(message_view)) { - read_stopped_ = true; - CallOnError(Delegate::ERROR_READ_BAD_MESSAGE); - return; - } - } else { - embedder::ScopedPlatformHandleVectorPtr platform_handles; - if (message_view.transport_data_buffer()) { - size_t num_platform_handles; - const void* platform_handle_table; - TransportData::GetPlatformHandleTable( - message_view.transport_data_buffer(), &num_platform_handles, - &platform_handle_table); - - if (num_platform_handles > 0) { - platform_handles = - GetReadPlatformHandles(num_platform_handles, - platform_handle_table).Pass(); - if (!platform_handles) { - LOG(ERROR) << "Invalid number of platform handles received"; - read_stopped_ = true; - CallOnError(Delegate::ERROR_READ_BAD_MESSAGE); - return; - } - } - } - - // TODO(vtl): In the case that we aren't expecting any platform handles, - // for the POSIX implementation, we should confirm that none are stored. - - // Dispatch the message. - DCHECK(delegate_); - delegate_->OnReadMessage(message_view, platform_handles.Pass()); - if (read_stopped_) { - // |Shutdown()| was called in |OnReadMessage()|. - // TODO(vtl): Add test for this case. - return; - } - } - - did_dispatch_message = true; - - // Update our state. - read_buffer_start += message_size; - remaining_bytes -= message_size; - } - - if (read_buffer_start > 0) { - // Move data back to start. - read_buffer_->num_valid_bytes_ = remaining_bytes; - if (read_buffer_->num_valid_bytes_ > 0) { - memmove(&read_buffer_->buffer_[0], - &read_buffer_->buffer_[read_buffer_start], remaining_bytes); - } - read_buffer_start = 0; - } - - if (read_buffer_->buffer_.size() - read_buffer_->num_valid_bytes_ < - kReadSize) { - // Use power-of-2 buffer sizes. - // TODO(vtl): Make sure the buffer doesn't get too large (and enforce the - // maximum message size to whatever extent necessary). - // TODO(vtl): We may often be able to peek at the header and get the real - // required extra space (which may be much bigger than |kReadSize|). - size_t new_size = std::max(read_buffer_->buffer_.size(), kReadSize); - while (new_size < read_buffer_->num_valid_bytes_ + kReadSize) - new_size *= 2; - - // TODO(vtl): It's suboptimal to zero out the fresh memory. - read_buffer_->buffer_.resize(new_size, 0); - } - - // (1) If we dispatched any messages, stop reading for now (and let the - // message loop do its thing for another round). - // TODO(vtl): Is this the behavior we want? (Alternatives: i. Dispatch only - // a single message. Risks: slower, more complex if we want to avoid lots of - // copying. ii. Keep reading until there's no more data and dispatch all the - // messages we can. Risks: starvation of other users of the message loop.) - // (2) If we didn't max out |kReadSize|, stop reading for now. - bool schedule_for_later = did_dispatch_message || bytes_read < kReadSize; - bytes_read = 0; - io_result = schedule_for_later ? ScheduleRead() : Read(&bytes_read); - } while (io_result != IO_PENDING); -} - -void RawChannel::OnWriteCompleted(IOResult io_result, - size_t platform_handles_written, - size_t bytes_written) { - DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io_); - DCHECK_NE(io_result, IO_PENDING); - - bool did_fail = false; - { - base::AutoLock locker(write_lock_); - DCHECK_EQ(write_stopped_, write_buffer_->message_queue_.empty()); - - if (write_stopped_) { - NOTREACHED(); - return; - } - - did_fail = !OnWriteCompletedNoLock(io_result, platform_handles_written, - bytes_written); - } - - if (did_fail) - CallOnError(Delegate::ERROR_WRITE); -} - -void RawChannel::EnqueueMessageNoLock(scoped_ptr<MessageInTransit> message) { - write_lock_.AssertAcquired(); - write_buffer_->message_queue_.push_back(message.release()); -} - -bool RawChannel::OnReadMessageForRawChannel( - const MessageInTransit::View& message_view) { - // No non-implementation specific |RawChannel| control messages. - LOG(ERROR) << "Invalid control message (subtype " << message_view.subtype() - << ")"; - return false; -} - -// static -RawChannel::Delegate::Error RawChannel::ReadIOResultToError( - IOResult io_result) { - switch (io_result) { - case IO_FAILED_SHUTDOWN: - return Delegate::ERROR_READ_SHUTDOWN; - case IO_FAILED_BROKEN: - return Delegate::ERROR_READ_BROKEN; - case IO_FAILED_UNKNOWN: - return Delegate::ERROR_READ_UNKNOWN; - case IO_SUCCEEDED: - case IO_PENDING: - NOTREACHED(); - break; - } - return Delegate::ERROR_READ_UNKNOWN; -} - -void RawChannel::CallOnError(Delegate::Error error) { - DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io_); - // TODO(vtl): Add a "write_lock_.AssertNotAcquired()"? - if (delegate_) - delegate_->OnError(error); -} - -bool RawChannel::OnWriteCompletedNoLock(IOResult io_result, - size_t platform_handles_written, - size_t bytes_written) { - write_lock_.AssertAcquired(); - - DCHECK(!write_stopped_); - DCHECK(!write_buffer_->message_queue_.empty()); - - if (io_result == IO_SUCCEEDED) { - write_buffer_->platform_handles_offset_ += platform_handles_written; - write_buffer_->data_offset_ += bytes_written; - - MessageInTransit* message = write_buffer_->message_queue_.front(); - if (write_buffer_->data_offset_ >= message->total_size()) { - // Complete write. - CHECK_EQ(write_buffer_->data_offset_, message->total_size()); - write_buffer_->message_queue_.pop_front(); - delete message; - write_buffer_->platform_handles_offset_ = 0; - write_buffer_->data_offset_ = 0; - - if (write_buffer_->message_queue_.empty()) - return true; - } - - // Schedule the next write. - io_result = ScheduleWriteNoLock(); - if (io_result == IO_PENDING) - return true; - DCHECK_NE(io_result, IO_SUCCEEDED); - } - - write_stopped_ = true; - STLDeleteElements(&write_buffer_->message_queue_); - write_buffer_->platform_handles_offset_ = 0; - write_buffer_->data_offset_ = 0; - return false; -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/raw_channel.h b/mojo/edk/system/raw_channel.h deleted file mode 100644 index f567767..0000000 --- a/mojo/edk/system/raw_channel.h +++ /dev/null @@ -1,329 +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_EDK_SYSTEM_RAW_CHANNEL_H_ -#define MOJO_EDK_SYSTEM_RAW_CHANNEL_H_ - -#include <deque> -#include <vector> - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/embedder/platform_handle_vector.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/message_in_transit.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace base { -class MessageLoopForIO; -} - -namespace mojo { -namespace system { - -// |RawChannel| is an interface and base class for objects that wrap an OS -// "pipe". It presents the following interface to users: -// - Receives and dispatches messages on an I/O thread (running a -// |MessageLoopForIO|. -// - Provides a thread-safe way of writing messages (|WriteMessage()|); -// writing/queueing messages will not block and is atomic from the point of -// view of the caller. If necessary, messages are queued (to be written on -// the aforementioned thread). -// -// OS-specific implementation subclasses are to be instantiated using the -// |Create()| static factory method. -// -// With the exception of |WriteMessage()|, this class is thread-unsafe (and in -// general its methods should only be used on the I/O thread, i.e., the thread -// on which |Init()| is called). -class MOJO_SYSTEM_IMPL_EXPORT RawChannel { - public: - virtual ~RawChannel(); - - // The |Delegate| is only accessed on the same thread as the message loop - // (passed in on creation). - class MOJO_SYSTEM_IMPL_EXPORT Delegate { - public: - enum Error { - // Failed read due to raw channel shutdown (e.g., on the other side). - ERROR_READ_SHUTDOWN, - // Failed read due to raw channel being broken (e.g., if the other side - // died without shutting down). - ERROR_READ_BROKEN, - // Received a bad message. - ERROR_READ_BAD_MESSAGE, - // Unknown read error. - ERROR_READ_UNKNOWN, - // Generic write error. - ERROR_WRITE - }; - - // Called when a message is read. This may call |Shutdown()| (on the - // |RawChannel|), but must not destroy it. - virtual void OnReadMessage( - const MessageInTransit::View& message_view, - embedder::ScopedPlatformHandleVectorPtr platform_handles) = 0; - - // Called when there's a (fatal) error. This may call the raw channel's - // |Shutdown()|, but must not destroy it. - // - // For each raw channel, there'll be at most one |ERROR_READ_...| and at - // most one |ERROR_WRITE| notification. After |OnError(ERROR_READ_...)|, - // |OnReadMessage()| won't be called again. - virtual void OnError(Error error) = 0; - - protected: - virtual ~Delegate() {} - }; - - // Static factory method. |handle| should be a handle to a - // (platform-appropriate) bidirectional communication channel (e.g., a socket - // on POSIX, a named pipe on Windows). - static scoped_ptr<RawChannel> Create(embedder::ScopedPlatformHandle handle); - - // This must be called (on an I/O thread) before this object is used. Does - // *not* take ownership of |delegate|. Both the I/O thread and |delegate| must - // remain alive until |Shutdown()| is called (unless this fails); |delegate| - // will no longer be used after |Shutdown()|. - void Init(Delegate* delegate); - - // This must be called (on the I/O thread) before this object is destroyed. - void Shutdown(); - - // Writes the given message (or schedules it to be written). |message| must - // have no |Dispatcher|s still attached (i.e., - // |SerializeAndCloseDispatchers()| should have been called). This method is - // thread-safe and may be called from any thread. Returns true on success. - bool WriteMessage(scoped_ptr<MessageInTransit> message); - - // Returns true if the write buffer is empty (i.e., all messages written using - // |WriteMessage()| have actually been sent. - // TODO(vtl): We should really also notify our delegate when the write buffer - // becomes empty (or something like that). - bool IsWriteBufferEmpty(); - - // Returns the amount of space needed in the |MessageInTransit|'s - // |TransportData|'s "platform handle table" per platform handle (to be - // attached to a message). (This amount may be zero.) - virtual size_t GetSerializedPlatformHandleSize() const = 0; - - protected: - // Result of I/O operations. - enum IOResult { - IO_SUCCEEDED, - // Failed due to a (probably) clean shutdown (e.g., of the other end). - IO_FAILED_SHUTDOWN, - // Failed due to the connection being broken (e.g., the other end dying). - IO_FAILED_BROKEN, - // Failed due to some other (unexpected) reason. - IO_FAILED_UNKNOWN, - IO_PENDING - }; - - class MOJO_SYSTEM_IMPL_EXPORT ReadBuffer { - public: - ReadBuffer(); - ~ReadBuffer(); - - void GetBuffer(char** addr, size_t* size); - - private: - friend class RawChannel; - - // We store data from |[Schedule]Read()|s in |buffer_|. The start of - // |buffer_| is always aligned with a message boundary (we will copy memory - // to ensure this), but |buffer_| may be larger than the actual number of - // bytes we have. - std::vector<char> buffer_; - size_t num_valid_bytes_; - - DISALLOW_COPY_AND_ASSIGN(ReadBuffer); - }; - - class MOJO_SYSTEM_IMPL_EXPORT WriteBuffer { - public: - struct Buffer { - const char* addr; - size_t size; - }; - - explicit WriteBuffer(size_t serialized_platform_handle_size); - ~WriteBuffer(); - - // Returns true if there are (more) platform handles to be sent (from the - // front of |message_queue_|). - bool HavePlatformHandlesToSend() const; - // Gets platform handles to be sent (from the front of |message_queue_|). - // This should only be called if |HavePlatformHandlesToSend()| returned - // true. There are two components to this: the actual |PlatformHandle|s - // (which should be closed once sent) and any additional serialization - // information (which will be embedded in the message's data; there are - // |GetSerializedPlatformHandleSize()| bytes per handle). Once all platform - // handles have been sent, the message data should be written next (see - // |GetBuffers()|). - void GetPlatformHandlesToSend(size_t* num_platform_handles, - embedder::PlatformHandle** platform_handles, - void** serialization_data); - - // Gets buffers to be written. These buffers will always come from the front - // of |message_queue_|. Once they are completely written, the front - // |MessageInTransit| should be popped (and destroyed); this is done in - // |OnWriteCompletedNoLock()|. - void GetBuffers(std::vector<Buffer>* buffers) const; - - private: - friend class RawChannel; - - const size_t serialized_platform_handle_size_; - - // TODO(vtl): When C++11 is available, switch this to a deque of - // |scoped_ptr|/|unique_ptr|s. - std::deque<MessageInTransit*> message_queue_; - // Platform handles are sent before the message data, but doing so may - // require several passes. |platform_handles_offset_| indicates the position - // in the first message's vector of platform handles to send next. - size_t platform_handles_offset_; - // The first message's data may have been partially sent. |data_offset_| - // indicates the position in the first message's data to start the next - // write. - size_t data_offset_; - - DISALLOW_COPY_AND_ASSIGN(WriteBuffer); - }; - - RawChannel(); - - // |result| must not be |IO_PENDING|. Must be called on the I/O thread WITHOUT - // |write_lock_| held. - void OnReadCompleted(IOResult io_result, size_t bytes_read); - // |result| must not be |IO_PENDING|. Must be called on the I/O thread WITHOUT - // |write_lock_| held. - void OnWriteCompleted(IOResult io_result, - size_t platform_handles_written, - size_t bytes_written); - - base::MessageLoopForIO* message_loop_for_io() { return message_loop_for_io_; } - base::Lock& write_lock() { return write_lock_; } - - // Should only be called on the I/O thread. - ReadBuffer* read_buffer() { return read_buffer_.get(); } - - // Only called under |write_lock_|. - WriteBuffer* write_buffer_no_lock() { - write_lock_.AssertAcquired(); - return write_buffer_.get(); - } - - // Adds |message| to the write message queue. Implementation subclasses may - // override this to add any additional "control" messages needed. This is - // called (on any thread) with |write_lock_| held. - virtual void EnqueueMessageNoLock(scoped_ptr<MessageInTransit> message); - - // Handles any control messages targeted to the |RawChannel| (or - // implementation subclass). Implementation subclasses may override this to - // handle any implementation-specific control messages, but should call - // |RawChannel::OnReadMessageForRawChannel()| for any remaining messages. - // Returns true on success and false on error (e.g., invalid control message). - // This is only called on the I/O thread. - virtual bool OnReadMessageForRawChannel( - const MessageInTransit::View& message_view); - - // Reads into |read_buffer()|. - // This class guarantees that: - // - the area indicated by |GetBuffer()| will stay valid until read completion - // (but please also see the comments for |OnShutdownNoLock()|); - // - a second read is not started if there is a pending read; - // - the method is called on the I/O thread WITHOUT |write_lock_| held. - // - // The implementing subclass must guarantee that: - // - |bytes_read| is untouched unless |Read()| returns |IO_SUCCEEDED|; - // - if the method returns |IO_PENDING|, |OnReadCompleted()| will be called on - // the I/O thread to report the result, unless |Shutdown()| is called. - virtual IOResult Read(size_t* bytes_read) = 0; - // Similar to |Read()|, except that the implementing subclass must also - // guarantee that the method doesn't succeed synchronously, i.e., it only - // returns |IO_FAILED_...| or |IO_PENDING|. - virtual IOResult ScheduleRead() = 0; - - // Called by |OnReadCompleted()| to get the platform handles associated with - // the given platform handle table (from a message). This should only be - // called when |num_platform_handles| is nonzero. Returns null if the - // |num_platform_handles| handles are not available. Only called on the I/O - // thread (without |write_lock_| held). - virtual embedder::ScopedPlatformHandleVectorPtr GetReadPlatformHandles( - size_t num_platform_handles, - const void* platform_handle_table) = 0; - - // Writes contents in |write_buffer_no_lock()|. - // This class guarantees that: - // - the |PlatformHandle|s given by |GetPlatformHandlesToSend()| and the - // buffer(s) given by |GetBuffers()| will remain valid until write - // completion (see also the comments for |OnShutdownNoLock()|); - // - a second write is not started if there is a pending write; - // - the method is called under |write_lock_|. - // - // The implementing subclass must guarantee that: - // - |platform_handles_written| and |bytes_written| are untouched unless - // |WriteNoLock()| returns |IO_SUCCEEDED|; - // - if the method returns |IO_PENDING|, |OnWriteCompleted()| will be called - // on the I/O thread to report the result, unless |Shutdown()| is called. - virtual IOResult WriteNoLock(size_t* platform_handles_written, - size_t* bytes_written) = 0; - // Similar to |WriteNoLock()|, except that the implementing subclass must also - // guarantee that the method doesn't succeed synchronously, i.e., it only - // returns |IO_FAILED_...| or |IO_PENDING|. - virtual IOResult ScheduleWriteNoLock() = 0; - - // Must be called on the I/O thread WITHOUT |write_lock_| held. - virtual void OnInit() = 0; - // On shutdown, passes the ownership of the buffers to subclasses, which may - // want to preserve them if there are pending read/write. Must be called on - // the I/O thread under |write_lock_|. - virtual void OnShutdownNoLock(scoped_ptr<ReadBuffer> read_buffer, - scoped_ptr<WriteBuffer> write_buffer) = 0; - - private: - // Converts an |IO_FAILED_...| for a read to a |Delegate::Error|. - static Delegate::Error ReadIOResultToError(IOResult io_result); - - // Calls |delegate_->OnError(error)|. Must be called on the I/O thread WITHOUT - // |write_lock_| held. - void CallOnError(Delegate::Error error); - - // If |io_result| is |IO_SUCCESS|, updates the write buffer and schedules a - // write operation to run later if there is more to write. If |io_result| is - // failure or any other error occurs, cancels pending writes and returns - // false. Must be called under |write_lock_| and only if |write_stopped_| is - // false. - bool OnWriteCompletedNoLock(IOResult io_result, - size_t platform_handles_written, - size_t bytes_written); - - // Set in |Init()| and never changed (hence usable on any thread without - // locking): - base::MessageLoopForIO* message_loop_for_io_; - - // Only used on the I/O thread: - Delegate* delegate_; - bool read_stopped_; - scoped_ptr<ReadBuffer> read_buffer_; - - base::Lock write_lock_; // Protects the following members. - bool write_stopped_; - scoped_ptr<WriteBuffer> write_buffer_; - - // This is used for posting tasks from write threads to the I/O thread. It - // must only be accessed under |write_lock_|. The weak pointers it produces - // are only used/invalidated on the I/O thread. - base::WeakPtrFactory<RawChannel> weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(RawChannel); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_RAW_CHANNEL_H_ diff --git a/mojo/edk/system/raw_channel_posix.cc b/mojo/edk/system/raw_channel_posix.cc deleted file mode 100644 index 078f8cb..0000000 --- a/mojo/edk/system/raw_channel_posix.cc +++ /dev/null @@ -1,469 +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/edk/system/raw_channel.h" - -#include <errno.h> -#include <sys/uio.h> -#include <unistd.h> - -#include <algorithm> -#include <deque> - -#include "base/bind.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/embedder/platform_channel_utils_posix.h" -#include "mojo/edk/embedder/platform_handle.h" -#include "mojo/edk/embedder/platform_handle_vector.h" -#include "mojo/edk/system/transport_data.h" - -namespace mojo { -namespace system { - -namespace { - -class RawChannelPosix : public RawChannel, - public base::MessageLoopForIO::Watcher { - public: - explicit RawChannelPosix(embedder::ScopedPlatformHandle handle); - ~RawChannelPosix() override; - - // |RawChannel| public methods: - size_t GetSerializedPlatformHandleSize() const override; - - private: - // |RawChannel| protected methods: - // Actually override this so that we can send multiple messages with (only) - // FDs if necessary. - void EnqueueMessageNoLock(scoped_ptr<MessageInTransit> message) override; - // Override this to handle those extra FD-only messages. - bool OnReadMessageForRawChannel( - const MessageInTransit::View& message_view) override; - IOResult Read(size_t* bytes_read) override; - IOResult ScheduleRead() override; - embedder::ScopedPlatformHandleVectorPtr GetReadPlatformHandles( - size_t num_platform_handles, - const void* platform_handle_table) override; - IOResult WriteNoLock(size_t* platform_handles_written, - size_t* bytes_written) override; - IOResult ScheduleWriteNoLock() override; - void OnInit() override; - void OnShutdownNoLock(scoped_ptr<ReadBuffer> read_buffer, - scoped_ptr<WriteBuffer> write_buffer) override; - - // |base::MessageLoopForIO::Watcher| implementation: - void OnFileCanReadWithoutBlocking(int fd) override; - void OnFileCanWriteWithoutBlocking(int fd) override; - - // Implements most of |Read()| (except for a bit of clean-up): - IOResult ReadImpl(size_t* bytes_read); - - // Watches for |fd_| to become writable. Must be called on the I/O thread. - void WaitToWrite(); - - embedder::ScopedPlatformHandle fd_; - - // The following members are only used on the I/O thread: - scoped_ptr<base::MessageLoopForIO::FileDescriptorWatcher> read_watcher_; - scoped_ptr<base::MessageLoopForIO::FileDescriptorWatcher> write_watcher_; - - bool pending_read_; - - std::deque<embedder::PlatformHandle> read_platform_handles_; - - // The following members are used on multiple threads and protected by - // |write_lock()|: - bool pending_write_; - - // This is used for posting tasks from write threads to the I/O thread. It - // must only be accessed under |write_lock_|. The weak pointers it produces - // are only used/invalidated on the I/O thread. - base::WeakPtrFactory<RawChannelPosix> weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(RawChannelPosix); -}; - -RawChannelPosix::RawChannelPosix(embedder::ScopedPlatformHandle handle) - : fd_(handle.Pass()), - pending_read_(false), - pending_write_(false), - weak_ptr_factory_(this) { - DCHECK(fd_.is_valid()); -} - -RawChannelPosix::~RawChannelPosix() { - DCHECK(!pending_read_); - DCHECK(!pending_write_); - - // No need to take the |write_lock()| here -- if there are still weak pointers - // outstanding, then we're hosed anyway (since we wouldn't be able to - // invalidate them cleanly, since we might not be on the I/O thread). - DCHECK(!weak_ptr_factory_.HasWeakPtrs()); - - // These must have been shut down/destroyed on the I/O thread. - DCHECK(!read_watcher_); - DCHECK(!write_watcher_); - - embedder::CloseAllPlatformHandles(&read_platform_handles_); -} - -size_t RawChannelPosix::GetSerializedPlatformHandleSize() const { - // We don't actually need any space on POSIX (since we just send FDs). - return 0; -} - -void RawChannelPosix::EnqueueMessageNoLock( - scoped_ptr<MessageInTransit> message) { - if (message->transport_data()) { - embedder::PlatformHandleVector* const platform_handles = - message->transport_data()->platform_handles(); - if (platform_handles && - platform_handles->size() > embedder::kPlatformChannelMaxNumHandles) { - // We can't attach all the FDs to a single message, so we have to "split" - // the message. Send as many control messages as needed first with FDs - // attached (and no data). - size_t i = 0; - for (; platform_handles->size() - i > - embedder::kPlatformChannelMaxNumHandles; - i += embedder::kPlatformChannelMaxNumHandles) { - scoped_ptr<MessageInTransit> fd_message(new MessageInTransit( - MessageInTransit::kTypeRawChannel, - MessageInTransit::kSubtypeRawChannelPosixExtraPlatformHandles, 0, - nullptr)); - embedder::ScopedPlatformHandleVectorPtr fds( - new embedder::PlatformHandleVector( - platform_handles->begin() + i, - platform_handles->begin() + i + - embedder::kPlatformChannelMaxNumHandles)); - fd_message->SetTransportData( - make_scoped_ptr(new TransportData(fds.Pass()))); - RawChannel::EnqueueMessageNoLock(fd_message.Pass()); - } - - // Remove the handles that we "moved" into the other messages. - platform_handles->erase(platform_handles->begin(), - platform_handles->begin() + i); - } - } - - RawChannel::EnqueueMessageNoLock(message.Pass()); -} - -bool RawChannelPosix::OnReadMessageForRawChannel( - const MessageInTransit::View& message_view) { - DCHECK_EQ(message_view.type(), MessageInTransit::kTypeRawChannel); - - if (message_view.subtype() == - MessageInTransit::kSubtypeRawChannelPosixExtraPlatformHandles) { - // We don't need to do anything. |RawChannel| won't extract the platform - // handles, and they'll be accumulated in |Read()|. - return true; - } - - return RawChannel::OnReadMessageForRawChannel(message_view); -} - -RawChannel::IOResult RawChannelPosix::Read(size_t* bytes_read) { - DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io()); - DCHECK(!pending_read_); - - IOResult rv = ReadImpl(bytes_read); - if (rv != IO_SUCCEEDED && rv != IO_PENDING) { - // Make sure that |OnFileCanReadWithoutBlocking()| won't be called again. - read_watcher_.reset(); - } - return rv; -} - -RawChannel::IOResult RawChannelPosix::ScheduleRead() { - DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io()); - DCHECK(!pending_read_); - - pending_read_ = true; - - return IO_PENDING; -} - -embedder::ScopedPlatformHandleVectorPtr RawChannelPosix::GetReadPlatformHandles( - size_t num_platform_handles, - const void* /*platform_handle_table*/) { - DCHECK_GT(num_platform_handles, 0u); - - if (read_platform_handles_.size() < num_platform_handles) { - embedder::CloseAllPlatformHandles(&read_platform_handles_); - read_platform_handles_.clear(); - return embedder::ScopedPlatformHandleVectorPtr(); - } - - embedder::ScopedPlatformHandleVectorPtr rv( - new embedder::PlatformHandleVector(num_platform_handles)); - rv->assign(read_platform_handles_.begin(), - read_platform_handles_.begin() + num_platform_handles); - read_platform_handles_.erase( - read_platform_handles_.begin(), - read_platform_handles_.begin() + num_platform_handles); - return rv.Pass(); -} - -RawChannel::IOResult RawChannelPosix::WriteNoLock( - size_t* platform_handles_written, - size_t* bytes_written) { - write_lock().AssertAcquired(); - - DCHECK(!pending_write_); - - size_t num_platform_handles = 0; - ssize_t write_result; - if (write_buffer_no_lock()->HavePlatformHandlesToSend()) { - embedder::PlatformHandle* platform_handles; - void* serialization_data; // Actually unused. - write_buffer_no_lock()->GetPlatformHandlesToSend( - &num_platform_handles, &platform_handles, &serialization_data); - DCHECK_GT(num_platform_handles, 0u); - DCHECK_LE(num_platform_handles, embedder::kPlatformChannelMaxNumHandles); - DCHECK(platform_handles); - - // TODO(vtl): Reduce code duplication. (This is duplicated from below.) - std::vector<WriteBuffer::Buffer> buffers; - write_buffer_no_lock()->GetBuffers(&buffers); - DCHECK(!buffers.empty()); - const size_t kMaxBufferCount = 10; - iovec iov[kMaxBufferCount]; - size_t buffer_count = std::min(buffers.size(), kMaxBufferCount); - for (size_t i = 0; i < buffer_count; ++i) { - iov[i].iov_base = const_cast<char*>(buffers[i].addr); - iov[i].iov_len = buffers[i].size; - } - - write_result = embedder::PlatformChannelSendmsgWithHandles( - fd_.get(), iov, buffer_count, platform_handles, num_platform_handles); - for (size_t i = 0; i < num_platform_handles; i++) - platform_handles[i].CloseIfNecessary(); - } else { - std::vector<WriteBuffer::Buffer> buffers; - write_buffer_no_lock()->GetBuffers(&buffers); - DCHECK(!buffers.empty()); - - if (buffers.size() == 1) { - write_result = embedder::PlatformChannelWrite(fd_.get(), buffers[0].addr, - buffers[0].size); - } else { - const size_t kMaxBufferCount = 10; - iovec iov[kMaxBufferCount]; - size_t buffer_count = std::min(buffers.size(), kMaxBufferCount); - for (size_t i = 0; i < buffer_count; ++i) { - iov[i].iov_base = const_cast<char*>(buffers[i].addr); - iov[i].iov_len = buffers[i].size; - } - - write_result = - embedder::PlatformChannelWritev(fd_.get(), iov, buffer_count); - } - } - - if (write_result >= 0) { - *platform_handles_written = num_platform_handles; - *bytes_written = static_cast<size_t>(write_result); - return IO_SUCCEEDED; - } - - if (errno == EPIPE) - return IO_FAILED_SHUTDOWN; - - if (errno != EAGAIN && errno != EWOULDBLOCK) { - PLOG(WARNING) << "sendmsg/write/writev"; - return IO_FAILED_UNKNOWN; - } - - return ScheduleWriteNoLock(); -} - -RawChannel::IOResult RawChannelPosix::ScheduleWriteNoLock() { - write_lock().AssertAcquired(); - - DCHECK(!pending_write_); - - // Set up to wait for the FD to become writable. - // If we're not on the I/O thread, we have to post a task to do this. - if (base::MessageLoop::current() != message_loop_for_io()) { - message_loop_for_io()->PostTask(FROM_HERE, - base::Bind(&RawChannelPosix::WaitToWrite, - weak_ptr_factory_.GetWeakPtr())); - pending_write_ = true; - return IO_PENDING; - } - - if (message_loop_for_io()->WatchFileDescriptor( - fd_.get().fd, false, base::MessageLoopForIO::WATCH_WRITE, - write_watcher_.get(), this)) { - pending_write_ = true; - return IO_PENDING; - } - - return IO_FAILED_UNKNOWN; -} - -void RawChannelPosix::OnInit() { - DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io()); - - DCHECK(!read_watcher_); - read_watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher()); - DCHECK(!write_watcher_); - write_watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher()); - - // I don't know how this can fail (unless |fd_| is bad, in which case it's a - // bug in our code). I also don't know if |WatchFileDescriptor()| actually - // fails cleanly. - CHECK(message_loop_for_io()->WatchFileDescriptor( - fd_.get().fd, true, base::MessageLoopForIO::WATCH_READ, - read_watcher_.get(), this)); -} - -void RawChannelPosix::OnShutdownNoLock( - scoped_ptr<ReadBuffer> /*read_buffer*/, - scoped_ptr<WriteBuffer> /*write_buffer*/) { - DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io()); - write_lock().AssertAcquired(); - - read_watcher_.reset(); // This will stop watching (if necessary). - write_watcher_.reset(); // This will stop watching (if necessary). - - pending_read_ = false; - pending_write_ = false; - - DCHECK(fd_.is_valid()); - fd_.reset(); - - weak_ptr_factory_.InvalidateWeakPtrs(); -} - -void RawChannelPosix::OnFileCanReadWithoutBlocking(int fd) { - DCHECK_EQ(fd, fd_.get().fd); - DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io()); - - if (!pending_read_) { - NOTREACHED(); - return; - } - - pending_read_ = false; - size_t bytes_read = 0; - IOResult io_result = Read(&bytes_read); - if (io_result != IO_PENDING) - OnReadCompleted(io_result, bytes_read); - - // On failure, |read_watcher_| must have been reset; on success, - // we assume that |OnReadCompleted()| always schedules another read. - // Otherwise, we could end up spinning -- getting - // |OnFileCanReadWithoutBlocking()| again and again but not doing any actual - // read. - // TODO(yzshen): An alternative is to stop watching if RawChannel doesn't - // schedule a new read. But that code won't be reached under the current - // RawChannel implementation. - DCHECK(!read_watcher_ || pending_read_); -} - -void RawChannelPosix::OnFileCanWriteWithoutBlocking(int fd) { - DCHECK_EQ(fd, fd_.get().fd); - DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io()); - - IOResult io_result; - size_t platform_handles_written = 0; - size_t bytes_written = 0; - { - base::AutoLock locker(write_lock()); - - DCHECK(pending_write_); - - pending_write_ = false; - io_result = WriteNoLock(&platform_handles_written, &bytes_written); - } - - if (io_result != IO_PENDING) - OnWriteCompleted(io_result, platform_handles_written, bytes_written); -} - -RawChannel::IOResult RawChannelPosix::ReadImpl(size_t* bytes_read) { - char* buffer = nullptr; - size_t bytes_to_read = 0; - read_buffer()->GetBuffer(&buffer, &bytes_to_read); - - size_t old_num_platform_handles = read_platform_handles_.size(); - ssize_t read_result = embedder::PlatformChannelRecvmsg( - fd_.get(), buffer, bytes_to_read, &read_platform_handles_); - if (read_platform_handles_.size() > old_num_platform_handles) { - DCHECK_LE(read_platform_handles_.size() - old_num_platform_handles, - embedder::kPlatformChannelMaxNumHandles); - - // We should never accumulate more than |TransportData::kMaxPlatformHandles - // + embedder::kPlatformChannelMaxNumHandles| handles. (The latter part is - // possible because we could have accumulated all the handles for a message, - // then received the message data plus the first set of handles for the next - // message in the subsequent |recvmsg()|.) - if (read_platform_handles_.size() > - (TransportData::GetMaxPlatformHandles() + - embedder::kPlatformChannelMaxNumHandles)) { - LOG(ERROR) << "Received too many platform handles"; - embedder::CloseAllPlatformHandles(&read_platform_handles_); - read_platform_handles_.clear(); - return IO_FAILED_UNKNOWN; - } - } - - if (read_result > 0) { - *bytes_read = static_cast<size_t>(read_result); - return IO_SUCCEEDED; - } - - // |read_result == 0| means "end of file". - if (read_result == 0) - return IO_FAILED_SHUTDOWN; - - if (errno == EAGAIN || errno == EWOULDBLOCK) - return ScheduleRead(); - - if (errno == ECONNRESET) - return IO_FAILED_BROKEN; - - PLOG(WARNING) << "recvmsg"; - return IO_FAILED_UNKNOWN; -} - -void RawChannelPosix::WaitToWrite() { - DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io()); - - DCHECK(write_watcher_); - - if (!message_loop_for_io()->WatchFileDescriptor( - fd_.get().fd, false, base::MessageLoopForIO::WATCH_WRITE, - write_watcher_.get(), this)) { - { - base::AutoLock locker(write_lock()); - - DCHECK(pending_write_); - pending_write_ = false; - } - OnWriteCompleted(IO_FAILED_UNKNOWN, 0, 0); - } -} - -} // namespace - -// ----------------------------------------------------------------------------- - -// Static factory method declared in raw_channel.h. -// static -scoped_ptr<RawChannel> RawChannel::Create( - embedder::ScopedPlatformHandle handle) { - return make_scoped_ptr(new RawChannelPosix(handle.Pass())); -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/raw_channel_unittest.cc b/mojo/edk/system/raw_channel_unittest.cc deleted file mode 100644 index a8cbcba..0000000 --- a/mojo/edk/system/raw_channel_unittest.cc +++ /dev/null @@ -1,805 +0,0 @@ -// 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/edk/system/raw_channel.h" - -#include <stdint.h> -#include <stdio.h> - -#include <vector> - -#include "base/bind.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/files/scoped_file.h" -#include "base/files/scoped_temp_dir.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/scoped_vector.h" -#include "base/rand_util.h" -#include "base/synchronization/lock.h" -#include "base/synchronization/waitable_event.h" -#include "base/test/test_io_thread.h" -#include "base/threading/platform_thread.h" // For |Sleep()|. -#include "base/threading/simple_thread.h" -#include "base/time/time.h" -#include "build/build_config.h" // TODO(vtl): Remove this. -#include "mojo/edk/embedder/platform_channel_pair.h" -#include "mojo/edk/embedder/platform_handle.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/message_in_transit.h" -#include "mojo/edk/system/test_utils.h" -#include "mojo/edk/system/transport_data.h" -#include "mojo/edk/test/test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -scoped_ptr<MessageInTransit> MakeTestMessage(uint32_t num_bytes) { - std::vector<unsigned char> bytes(num_bytes, 0); - for (size_t i = 0; i < num_bytes; i++) - bytes[i] = static_cast<unsigned char>(i + num_bytes); - return make_scoped_ptr(new MessageInTransit( - MessageInTransit::kTypeEndpoint, MessageInTransit::kSubtypeEndpointData, - num_bytes, bytes.empty() ? nullptr : &bytes[0])); -} - -bool CheckMessageData(const void* bytes, uint32_t num_bytes) { - const unsigned char* b = static_cast<const unsigned char*>(bytes); - for (uint32_t i = 0; i < num_bytes; i++) { - if (b[i] != static_cast<unsigned char>(i + num_bytes)) - return false; - } - return true; -} - -void InitOnIOThread(RawChannel* raw_channel, RawChannel::Delegate* delegate) { - raw_channel->Init(delegate); -} - -bool WriteTestMessageToHandle(const embedder::PlatformHandle& handle, - uint32_t num_bytes) { - scoped_ptr<MessageInTransit> message(MakeTestMessage(num_bytes)); - - size_t write_size = 0; - mojo::test::BlockingWrite(handle, message->main_buffer(), - message->main_buffer_size(), &write_size); - return write_size == message->main_buffer_size(); -} - -// ----------------------------------------------------------------------------- - -class RawChannelTest : public testing::Test { - public: - RawChannelTest() : io_thread_(base::TestIOThread::kManualStart) {} - ~RawChannelTest() override {} - - void SetUp() override { - embedder::PlatformChannelPair channel_pair; - handles[0] = channel_pair.PassServerHandle(); - handles[1] = channel_pair.PassClientHandle(); - io_thread_.Start(); - } - - void TearDown() override { - io_thread_.Stop(); - handles[0].reset(); - handles[1].reset(); - } - - protected: - base::TestIOThread* io_thread() { return &io_thread_; } - - embedder::ScopedPlatformHandle handles[2]; - - private: - base::TestIOThread io_thread_; - - DISALLOW_COPY_AND_ASSIGN(RawChannelTest); -}; - -// RawChannelTest.WriteMessage ------------------------------------------------- - -class WriteOnlyRawChannelDelegate : public RawChannel::Delegate { - public: - WriteOnlyRawChannelDelegate() {} - ~WriteOnlyRawChannelDelegate() override {} - - // |RawChannel::Delegate| implementation: - void OnReadMessage( - const MessageInTransit::View& /*message_view*/, - embedder::ScopedPlatformHandleVectorPtr /*platform_handles*/) override { - CHECK(false); // Should not get called. - } - void OnError(Error error) override { - // We'll get a read (shutdown) error when the connection is closed. - CHECK_EQ(error, ERROR_READ_SHUTDOWN); - } - - private: - DISALLOW_COPY_AND_ASSIGN(WriteOnlyRawChannelDelegate); -}; - -static const int64_t kMessageReaderSleepMs = 1; -static const size_t kMessageReaderMaxPollIterations = 3000; - -class TestMessageReaderAndChecker { - public: - explicit TestMessageReaderAndChecker(embedder::PlatformHandle handle) - : handle_(handle) {} - ~TestMessageReaderAndChecker() { CHECK(bytes_.empty()); } - - bool ReadAndCheckNextMessage(uint32_t expected_size) { - unsigned char buffer[4096]; - - for (size_t i = 0; i < kMessageReaderMaxPollIterations;) { - size_t read_size = 0; - CHECK(mojo::test::NonBlockingRead(handle_, buffer, sizeof(buffer), - &read_size)); - - // Append newly-read data to |bytes_|. - bytes_.insert(bytes_.end(), buffer, buffer + read_size); - - // If we have the header.... - size_t message_size; - if (MessageInTransit::GetNextMessageSize( - bytes_.empty() ? nullptr : &bytes_[0], bytes_.size(), - &message_size)) { - // If we've read the whole message.... - if (bytes_.size() >= message_size) { - bool rv = true; - MessageInTransit::View message_view(message_size, &bytes_[0]); - CHECK_EQ(message_view.main_buffer_size(), message_size); - - if (message_view.num_bytes() != expected_size) { - LOG(ERROR) << "Wrong size: " << message_size << " instead of " - << expected_size << " bytes."; - rv = false; - } else if (!CheckMessageData(message_view.bytes(), - message_view.num_bytes())) { - LOG(ERROR) << "Incorrect message bytes."; - rv = false; - } - - // Erase message data. - bytes_.erase(bytes_.begin(), - bytes_.begin() + message_view.main_buffer_size()); - return rv; - } - } - - if (static_cast<size_t>(read_size) < sizeof(buffer)) { - i++; - base::PlatformThread::Sleep( - base::TimeDelta::FromMilliseconds(kMessageReaderSleepMs)); - } - } - - LOG(ERROR) << "Too many iterations."; - return false; - } - - private: - const embedder::PlatformHandle handle_; - - // The start of the received data should always be on a message boundary. - std::vector<unsigned char> bytes_; - - DISALLOW_COPY_AND_ASSIGN(TestMessageReaderAndChecker); -}; - -// Tests writing (and verifies reading using our own custom reader). -TEST_F(RawChannelTest, WriteMessage) { - WriteOnlyRawChannelDelegate delegate; - scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass())); - TestMessageReaderAndChecker checker(handles[1].get()); - io_thread()->PostTaskAndWait( - FROM_HERE, - base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate))); - - // Write and read, for a variety of sizes. - for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1) { - EXPECT_TRUE(rc->WriteMessage(MakeTestMessage(size))); - EXPECT_TRUE(checker.ReadAndCheckNextMessage(size)) << size; - } - - // Write/queue and read afterwards, for a variety of sizes. - for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1) - EXPECT_TRUE(rc->WriteMessage(MakeTestMessage(size))); - for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1) - EXPECT_TRUE(checker.ReadAndCheckNextMessage(size)) << size; - - io_thread()->PostTaskAndWait( - FROM_HERE, base::Bind(&RawChannel::Shutdown, base::Unretained(rc.get()))); -} - -// RawChannelTest.OnReadMessage ------------------------------------------------ - -class ReadCheckerRawChannelDelegate : public RawChannel::Delegate { - public: - ReadCheckerRawChannelDelegate() : done_event_(false, false), position_(0) {} - ~ReadCheckerRawChannelDelegate() override {} - - // |RawChannel::Delegate| implementation (called on the I/O thread): - void OnReadMessage( - const MessageInTransit::View& message_view, - embedder::ScopedPlatformHandleVectorPtr platform_handles) override { - EXPECT_FALSE(platform_handles); - - size_t position; - size_t expected_size; - bool should_signal = false; - { - base::AutoLock locker(lock_); - CHECK_LT(position_, expected_sizes_.size()); - position = position_; - expected_size = expected_sizes_[position]; - position_++; - if (position_ >= expected_sizes_.size()) - should_signal = true; - } - - EXPECT_EQ(expected_size, message_view.num_bytes()) << position; - if (message_view.num_bytes() == expected_size) { - EXPECT_TRUE( - CheckMessageData(message_view.bytes(), message_view.num_bytes())) - << position; - } - - if (should_signal) - done_event_.Signal(); - } - void OnError(Error error) override { - // We'll get a read (shutdown) error when the connection is closed. - CHECK_EQ(error, ERROR_READ_SHUTDOWN); - } - - // Waits for all the messages (of sizes |expected_sizes_|) to be seen. - void Wait() { done_event_.Wait(); } - - void SetExpectedSizes(const std::vector<uint32_t>& expected_sizes) { - base::AutoLock locker(lock_); - CHECK_EQ(position_, expected_sizes_.size()); - expected_sizes_ = expected_sizes; - position_ = 0; - } - - private: - base::WaitableEvent done_event_; - - base::Lock lock_; // Protects the following members. - std::vector<uint32_t> expected_sizes_; - size_t position_; - - DISALLOW_COPY_AND_ASSIGN(ReadCheckerRawChannelDelegate); -}; - -// Tests reading (writing using our own custom writer). -TEST_F(RawChannelTest, OnReadMessage) { - ReadCheckerRawChannelDelegate delegate; - scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass())); - io_thread()->PostTaskAndWait( - FROM_HERE, - base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate))); - - // Write and read, for a variety of sizes. - for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1) { - delegate.SetExpectedSizes(std::vector<uint32_t>(1, size)); - - EXPECT_TRUE(WriteTestMessageToHandle(handles[1].get(), size)); - - delegate.Wait(); - } - - // Set up reader and write as fast as we can. - // Write/queue and read afterwards, for a variety of sizes. - std::vector<uint32_t> expected_sizes; - for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1) - expected_sizes.push_back(size); - delegate.SetExpectedSizes(expected_sizes); - for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1) - EXPECT_TRUE(WriteTestMessageToHandle(handles[1].get(), size)); - delegate.Wait(); - - io_thread()->PostTaskAndWait( - FROM_HERE, base::Bind(&RawChannel::Shutdown, base::Unretained(rc.get()))); -} - -// RawChannelTest.WriteMessageAndOnReadMessage --------------------------------- - -class RawChannelWriterThread : public base::SimpleThread { - public: - RawChannelWriterThread(RawChannel* raw_channel, size_t write_count) - : base::SimpleThread("raw_channel_writer_thread"), - raw_channel_(raw_channel), - left_to_write_(write_count) {} - - ~RawChannelWriterThread() override { Join(); } - - private: - void Run() override { - static const int kMaxRandomMessageSize = 25000; - - while (left_to_write_-- > 0) { - EXPECT_TRUE(raw_channel_->WriteMessage(MakeTestMessage( - static_cast<uint32_t>(base::RandInt(1, kMaxRandomMessageSize))))); - } - } - - RawChannel* const raw_channel_; - size_t left_to_write_; - - DISALLOW_COPY_AND_ASSIGN(RawChannelWriterThread); -}; - -class ReadCountdownRawChannelDelegate : public RawChannel::Delegate { - public: - explicit ReadCountdownRawChannelDelegate(size_t expected_count) - : done_event_(false, false), expected_count_(expected_count), count_(0) {} - ~ReadCountdownRawChannelDelegate() override {} - - // |RawChannel::Delegate| implementation (called on the I/O thread): - void OnReadMessage( - const MessageInTransit::View& message_view, - embedder::ScopedPlatformHandleVectorPtr platform_handles) override { - EXPECT_FALSE(platform_handles); - - EXPECT_LT(count_, expected_count_); - count_++; - - EXPECT_TRUE( - CheckMessageData(message_view.bytes(), message_view.num_bytes())); - - if (count_ >= expected_count_) - done_event_.Signal(); - } - void OnError(Error error) override { - // We'll get a read (shutdown) error when the connection is closed. - CHECK_EQ(error, ERROR_READ_SHUTDOWN); - } - - // Waits for all the messages to have been seen. - void Wait() { done_event_.Wait(); } - - private: - base::WaitableEvent done_event_; - size_t expected_count_; - size_t count_; - - DISALLOW_COPY_AND_ASSIGN(ReadCountdownRawChannelDelegate); -}; - -TEST_F(RawChannelTest, WriteMessageAndOnReadMessage) { - static const size_t kNumWriterThreads = 10; - static const size_t kNumWriteMessagesPerThread = 4000; - - WriteOnlyRawChannelDelegate writer_delegate; - scoped_ptr<RawChannel> writer_rc(RawChannel::Create(handles[0].Pass())); - io_thread()->PostTaskAndWait(FROM_HERE, - base::Bind(&InitOnIOThread, writer_rc.get(), - base::Unretained(&writer_delegate))); - - ReadCountdownRawChannelDelegate reader_delegate(kNumWriterThreads * - kNumWriteMessagesPerThread); - scoped_ptr<RawChannel> reader_rc(RawChannel::Create(handles[1].Pass())); - io_thread()->PostTaskAndWait(FROM_HERE, - base::Bind(&InitOnIOThread, reader_rc.get(), - base::Unretained(&reader_delegate))); - - { - ScopedVector<RawChannelWriterThread> writer_threads; - for (size_t i = 0; i < kNumWriterThreads; i++) { - writer_threads.push_back(new RawChannelWriterThread( - writer_rc.get(), kNumWriteMessagesPerThread)); - } - for (size_t i = 0; i < writer_threads.size(); i++) - writer_threads[i]->Start(); - } // Joins all the writer threads. - - // Sleep a bit, to let any extraneous reads be processed. (There shouldn't be - // any, but we want to know about them.) - base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); - - // Wait for reading to finish. - reader_delegate.Wait(); - - io_thread()->PostTaskAndWait( - FROM_HERE, - base::Bind(&RawChannel::Shutdown, base::Unretained(reader_rc.get()))); - - io_thread()->PostTaskAndWait( - FROM_HERE, - base::Bind(&RawChannel::Shutdown, base::Unretained(writer_rc.get()))); -} - -// RawChannelTest.OnError ------------------------------------------------------ - -class ErrorRecordingRawChannelDelegate - : public ReadCountdownRawChannelDelegate { - public: - ErrorRecordingRawChannelDelegate(size_t expected_read_count, - bool expect_read_error, - bool expect_write_error) - : ReadCountdownRawChannelDelegate(expected_read_count), - got_read_error_event_(false, false), - got_write_error_event_(false, false), - expecting_read_error_(expect_read_error), - expecting_write_error_(expect_write_error) {} - - ~ErrorRecordingRawChannelDelegate() override {} - - void OnError(Error error) override { - switch (error) { - case ERROR_READ_SHUTDOWN: - ASSERT_TRUE(expecting_read_error_); - expecting_read_error_ = false; - got_read_error_event_.Signal(); - break; - case ERROR_READ_BROKEN: - // TODO(vtl): Test broken connections. - CHECK(false); - break; - case ERROR_READ_BAD_MESSAGE: - // TODO(vtl): Test reception/detection of bad messages. - CHECK(false); - break; - case ERROR_READ_UNKNOWN: - // TODO(vtl): Test however it is we might get here. - CHECK(false); - break; - case ERROR_WRITE: - ASSERT_TRUE(expecting_write_error_); - expecting_write_error_ = false; - got_write_error_event_.Signal(); - break; - } - } - - void WaitForReadError() { got_read_error_event_.Wait(); } - void WaitForWriteError() { got_write_error_event_.Wait(); } - - private: - base::WaitableEvent got_read_error_event_; - base::WaitableEvent got_write_error_event_; - - bool expecting_read_error_; - bool expecting_write_error_; - - DISALLOW_COPY_AND_ASSIGN(ErrorRecordingRawChannelDelegate); -}; - -// Tests (fatal) errors. -TEST_F(RawChannelTest, OnError) { - ErrorRecordingRawChannelDelegate delegate(0, true, true); - scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass())); - io_thread()->PostTaskAndWait( - FROM_HERE, - base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate))); - - // Close the handle of the other end, which should make writing fail. - handles[1].reset(); - - EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(1))); - - // We should get a write error. - delegate.WaitForWriteError(); - - // We should also get a read error. - delegate.WaitForReadError(); - - EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(2))); - - // Sleep a bit, to make sure we don't get another |OnError()| - // notification. (If we actually get another one, |OnError()| crashes.) - base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20)); - - io_thread()->PostTaskAndWait( - FROM_HERE, base::Bind(&RawChannel::Shutdown, base::Unretained(rc.get()))); -} - -// RawChannelTest.ReadUnaffectedByWriteError ----------------------------------- - -TEST_F(RawChannelTest, ReadUnaffectedByWriteError) { - const size_t kMessageCount = 5; - - // Write a few messages into the other end. - uint32_t message_size = 1; - for (size_t i = 0; i < kMessageCount; - i++, message_size += message_size / 2 + 1) - EXPECT_TRUE(WriteTestMessageToHandle(handles[1].get(), message_size)); - - // Close the other end, which should make writing fail. - handles[1].reset(); - - // Only start up reading here. The system buffer should still contain the - // messages that were written. - ErrorRecordingRawChannelDelegate delegate(kMessageCount, true, true); - scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass())); - io_thread()->PostTaskAndWait( - FROM_HERE, - base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate))); - - EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(1))); - - // We should definitely get a write error. - delegate.WaitForWriteError(); - - // Wait for reading to finish. A writing failure shouldn't affect reading. - delegate.Wait(); - - // And then we should get a read error. - delegate.WaitForReadError(); - - io_thread()->PostTaskAndWait( - FROM_HERE, base::Bind(&RawChannel::Shutdown, base::Unretained(rc.get()))); -} - -// RawChannelTest.WriteMessageAfterShutdown ------------------------------------ - -// Makes sure that calling |WriteMessage()| after |Shutdown()| behaves -// correctly. -TEST_F(RawChannelTest, WriteMessageAfterShutdown) { - WriteOnlyRawChannelDelegate delegate; - scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass())); - io_thread()->PostTaskAndWait( - FROM_HERE, - base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate))); - io_thread()->PostTaskAndWait( - FROM_HERE, base::Bind(&RawChannel::Shutdown, base::Unretained(rc.get()))); - - EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(1))); -} - -// RawChannelTest.ShutdownOnReadMessage ---------------------------------------- - -class ShutdownOnReadMessageRawChannelDelegate : public RawChannel::Delegate { - public: - explicit ShutdownOnReadMessageRawChannelDelegate(RawChannel* raw_channel) - : raw_channel_(raw_channel), - done_event_(false, false), - did_shutdown_(false) {} - ~ShutdownOnReadMessageRawChannelDelegate() override {} - - // |RawChannel::Delegate| implementation (called on the I/O thread): - void OnReadMessage( - const MessageInTransit::View& message_view, - embedder::ScopedPlatformHandleVectorPtr platform_handles) override { - EXPECT_FALSE(platform_handles); - EXPECT_FALSE(did_shutdown_); - EXPECT_TRUE( - CheckMessageData(message_view.bytes(), message_view.num_bytes())); - raw_channel_->Shutdown(); - did_shutdown_ = true; - done_event_.Signal(); - } - void OnError(Error /*error*/) override { - CHECK(false); // Should not get called. - } - - // Waits for shutdown. - void Wait() { - done_event_.Wait(); - EXPECT_TRUE(did_shutdown_); - } - - private: - RawChannel* const raw_channel_; - base::WaitableEvent done_event_; - bool did_shutdown_; - - DISALLOW_COPY_AND_ASSIGN(ShutdownOnReadMessageRawChannelDelegate); -}; - -TEST_F(RawChannelTest, ShutdownOnReadMessage) { - // Write a few messages into the other end. - for (size_t count = 0; count < 5; count++) - EXPECT_TRUE(WriteTestMessageToHandle(handles[1].get(), 10)); - - scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass())); - ShutdownOnReadMessageRawChannelDelegate delegate(rc.get()); - io_thread()->PostTaskAndWait( - FROM_HERE, - base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate))); - - // Wait for the delegate, which will shut the |RawChannel| down. - delegate.Wait(); -} - -// RawChannelTest.ShutdownOnError{Read, Write} --------------------------------- - -class ShutdownOnErrorRawChannelDelegate : public RawChannel::Delegate { - public: - ShutdownOnErrorRawChannelDelegate(RawChannel* raw_channel, - Error shutdown_on_error_type) - : raw_channel_(raw_channel), - shutdown_on_error_type_(shutdown_on_error_type), - done_event_(false, false), - did_shutdown_(false) {} - ~ShutdownOnErrorRawChannelDelegate() override {} - - // |RawChannel::Delegate| implementation (called on the I/O thread): - void OnReadMessage( - const MessageInTransit::View& /*message_view*/, - embedder::ScopedPlatformHandleVectorPtr /*platform_handles*/) override { - CHECK(false); // Should not get called. - } - void OnError(Error error) override { - EXPECT_FALSE(did_shutdown_); - if (error != shutdown_on_error_type_) - return; - raw_channel_->Shutdown(); - did_shutdown_ = true; - done_event_.Signal(); - } - - // Waits for shutdown. - void Wait() { - done_event_.Wait(); - EXPECT_TRUE(did_shutdown_); - } - - private: - RawChannel* const raw_channel_; - const Error shutdown_on_error_type_; - base::WaitableEvent done_event_; - bool did_shutdown_; - - DISALLOW_COPY_AND_ASSIGN(ShutdownOnErrorRawChannelDelegate); -}; - -TEST_F(RawChannelTest, ShutdownOnErrorRead) { - scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass())); - ShutdownOnErrorRawChannelDelegate delegate( - rc.get(), RawChannel::Delegate::ERROR_READ_SHUTDOWN); - io_thread()->PostTaskAndWait( - FROM_HERE, - base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate))); - - // Close the handle of the other end, which should stuff fail. - handles[1].reset(); - - // Wait for the delegate, which will shut the |RawChannel| down. - delegate.Wait(); -} - -TEST_F(RawChannelTest, ShutdownOnErrorWrite) { - scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass())); - ShutdownOnErrorRawChannelDelegate delegate(rc.get(), - RawChannel::Delegate::ERROR_WRITE); - io_thread()->PostTaskAndWait( - FROM_HERE, - base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate))); - - // Close the handle of the other end, which should stuff fail. - handles[1].reset(); - - EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(1))); - - // Wait for the delegate, which will shut the |RawChannel| down. - delegate.Wait(); -} - -// RawChannelTest.ReadWritePlatformHandles ------------------------------------- - -class ReadPlatformHandlesCheckerRawChannelDelegate - : public RawChannel::Delegate { - public: - ReadPlatformHandlesCheckerRawChannelDelegate() : done_event_(false, false) {} - ~ReadPlatformHandlesCheckerRawChannelDelegate() override {} - - // |RawChannel::Delegate| implementation (called on the I/O thread): - void OnReadMessage( - const MessageInTransit::View& message_view, - embedder::ScopedPlatformHandleVectorPtr platform_handles) override { - const char kHello[] = "hello"; - - EXPECT_EQ(sizeof(kHello), message_view.num_bytes()); - EXPECT_STREQ(kHello, static_cast<const char*>(message_view.bytes())); - - ASSERT_TRUE(platform_handles); - ASSERT_EQ(2u, platform_handles->size()); - embedder::ScopedPlatformHandle h1(platform_handles->at(0)); - EXPECT_TRUE(h1.is_valid()); - embedder::ScopedPlatformHandle h2(platform_handles->at(1)); - EXPECT_TRUE(h2.is_valid()); - platform_handles->clear(); - - { - char buffer[100] = {}; - - base::ScopedFILE fp(mojo::test::FILEFromPlatformHandle(h1.Pass(), "rb")); - EXPECT_TRUE(fp); - rewind(fp.get()); - EXPECT_EQ(1u, fread(buffer, 1, sizeof(buffer), fp.get())); - EXPECT_EQ('1', buffer[0]); - } - - { - char buffer[100] = {}; - base::ScopedFILE fp(mojo::test::FILEFromPlatformHandle(h2.Pass(), "rb")); - EXPECT_TRUE(fp); - rewind(fp.get()); - EXPECT_EQ(1u, fread(buffer, 1, sizeof(buffer), fp.get())); - EXPECT_EQ('2', buffer[0]); - } - - done_event_.Signal(); - } - void OnError(Error error) override { - // We'll get a read (shutdown) error when the connection is closed. - CHECK_EQ(error, ERROR_READ_SHUTDOWN); - } - - void Wait() { done_event_.Wait(); } - - private: - base::WaitableEvent done_event_; - - DISALLOW_COPY_AND_ASSIGN(ReadPlatformHandlesCheckerRawChannelDelegate); -}; - -#if defined(OS_POSIX) -#define MAYBE_ReadWritePlatformHandles ReadWritePlatformHandles -#else -// Not yet implemented (on Windows). -#define MAYBE_ReadWritePlatformHandles DISABLED_ReadWritePlatformHandles -#endif -TEST_F(RawChannelTest, MAYBE_ReadWritePlatformHandles) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - WriteOnlyRawChannelDelegate write_delegate; - scoped_ptr<RawChannel> rc_write(RawChannel::Create(handles[0].Pass())); - io_thread()->PostTaskAndWait(FROM_HERE, - base::Bind(&InitOnIOThread, rc_write.get(), - base::Unretained(&write_delegate))); - - ReadPlatformHandlesCheckerRawChannelDelegate read_delegate; - scoped_ptr<RawChannel> rc_read(RawChannel::Create(handles[1].Pass())); - io_thread()->PostTaskAndWait(FROM_HERE, - base::Bind(&InitOnIOThread, rc_read.get(), - base::Unretained(&read_delegate))); - - base::FilePath unused; - base::ScopedFILE fp1( - base::CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused)); - EXPECT_EQ(1u, fwrite("1", 1, 1, fp1.get())); - base::ScopedFILE fp2( - base::CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused)); - EXPECT_EQ(1u, fwrite("2", 1, 1, fp2.get())); - - { - const char kHello[] = "hello"; - embedder::ScopedPlatformHandleVectorPtr platform_handles( - new embedder::PlatformHandleVector()); - platform_handles->push_back( - mojo::test::PlatformHandleFromFILE(fp1.Pass()).release()); - platform_handles->push_back( - mojo::test::PlatformHandleFromFILE(fp2.Pass()).release()); - - scoped_ptr<MessageInTransit> message(new MessageInTransit( - MessageInTransit::kTypeEndpoint, MessageInTransit::kSubtypeEndpointData, - sizeof(kHello), kHello)); - message->SetTransportData( - make_scoped_ptr(new TransportData(platform_handles.Pass()))); - EXPECT_TRUE(rc_write->WriteMessage(message.Pass())); - } - - read_delegate.Wait(); - - io_thread()->PostTaskAndWait( - FROM_HERE, - base::Bind(&RawChannel::Shutdown, base::Unretained(rc_read.get()))); - io_thread()->PostTaskAndWait( - FROM_HERE, - base::Bind(&RawChannel::Shutdown, base::Unretained(rc_write.get()))); -} - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/raw_channel_win.cc b/mojo/edk/system/raw_channel_win.cc deleted file mode 100644 index 7ec7ad7..0000000 --- a/mojo/edk/system/raw_channel_win.cc +++ /dev/null @@ -1,576 +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/edk/system/raw_channel.h" - -#include <windows.h> - -#include "base/auto_reset.h" -#include "base/bind.h" -#include "base/compiler_specific.h" -#include "base/lazy_instance.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/synchronization/lock.h" -#include "base/win/windows_version.h" -#include "mojo/edk/embedder/platform_handle.h" - -namespace mojo { -namespace system { - -namespace { - -class VistaOrHigherFunctions { - public: - VistaOrHigherFunctions(); - - bool is_vista_or_higher() const { return is_vista_or_higher_; } - - BOOL SetFileCompletionNotificationModes(HANDLE handle, UCHAR flags) { - return set_file_completion_notification_modes_(handle, flags); - } - - BOOL CancelIoEx(HANDLE handle, LPOVERLAPPED overlapped) { - return cancel_io_ex_(handle, overlapped); - } - - private: - typedef BOOL(WINAPI* SetFileCompletionNotificationModesFunc)(HANDLE, UCHAR); - typedef BOOL(WINAPI* CancelIoExFunc)(HANDLE, LPOVERLAPPED); - - bool is_vista_or_higher_; - SetFileCompletionNotificationModesFunc - set_file_completion_notification_modes_; - CancelIoExFunc cancel_io_ex_; -}; - -VistaOrHigherFunctions::VistaOrHigherFunctions() - : is_vista_or_higher_(base::win::GetVersion() >= base::win::VERSION_VISTA), - set_file_completion_notification_modes_(nullptr), - cancel_io_ex_(nullptr) { - if (!is_vista_or_higher_) - return; - - HMODULE module = GetModuleHandleW(L"kernel32.dll"); - set_file_completion_notification_modes_ = - reinterpret_cast<SetFileCompletionNotificationModesFunc>( - GetProcAddress(module, "SetFileCompletionNotificationModes")); - DCHECK(set_file_completion_notification_modes_); - - cancel_io_ex_ = - reinterpret_cast<CancelIoExFunc>(GetProcAddress(module, "CancelIoEx")); - DCHECK(cancel_io_ex_); -} - -base::LazyInstance<VistaOrHigherFunctions> g_vista_or_higher_functions = - LAZY_INSTANCE_INITIALIZER; - -class RawChannelWin : public RawChannel { - public: - RawChannelWin(embedder::ScopedPlatformHandle handle); - ~RawChannelWin() override; - - // |RawChannel| public methods: - size_t GetSerializedPlatformHandleSize() const override; - - private: - // RawChannelIOHandler receives OS notifications for I/O completion. It must - // be created on the I/O thread. - // - // It manages its own destruction. Destruction happens on the I/O thread when - // all the following conditions are satisfied: - // - |DetachFromOwnerNoLock()| has been called; - // - there is no pending read; - // - there is no pending write. - class RawChannelIOHandler : public base::MessageLoopForIO::IOHandler { - public: - RawChannelIOHandler(RawChannelWin* owner, - embedder::ScopedPlatformHandle handle); - - HANDLE handle() const { return handle_.get().handle; } - - // The following methods are only called by the owner on the I/O thread. - bool pending_read() const; - base::MessageLoopForIO::IOContext* read_context(); - // Instructs the object to wait for an |OnIOCompleted()| notification. - void OnPendingReadStarted(); - - // The following methods are only called by the owner under - // |owner_->write_lock()|. - bool pending_write_no_lock() const; - base::MessageLoopForIO::IOContext* write_context_no_lock(); - // Instructs the object to wait for an |OnIOCompleted()| notification. - void OnPendingWriteStartedNoLock(); - - // |base::MessageLoopForIO::IOHandler| implementation: - // Must be called on the I/O thread. It could be called before or after - // detached from the owner. - void OnIOCompleted(base::MessageLoopForIO::IOContext* context, - DWORD bytes_transferred, - DWORD error) override; - - // Must be called on the I/O thread under |owner_->write_lock()|. - // After this call, the owner must not make any further calls on this - // object, and therefore the object is used on the I/O thread exclusively - // (if it stays alive). - void DetachFromOwnerNoLock(scoped_ptr<ReadBuffer> read_buffer, - scoped_ptr<WriteBuffer> write_buffer); - - private: - ~RawChannelIOHandler() override; - - // Returns true if |owner_| has been reset and there is not pending read or - // write. - // Must be called on the I/O thread. - bool ShouldSelfDestruct() const; - - // Must be called on the I/O thread. It may be called before or after - // detaching from the owner. - void OnReadCompleted(DWORD bytes_read, DWORD error); - // Must be called on the I/O thread. It may be called before or after - // detaching from the owner. - void OnWriteCompleted(DWORD bytes_written, DWORD error); - - embedder::ScopedPlatformHandle handle_; - - // |owner_| is reset on the I/O thread under |owner_->write_lock()|. - // Therefore, it may be used on any thread under lock; or on the I/O thread - // without locking. - RawChannelWin* owner_; - - // The following members must be used on the I/O thread. - scoped_ptr<ReadBuffer> preserved_read_buffer_after_detach_; - scoped_ptr<WriteBuffer> preserved_write_buffer_after_detach_; - bool suppress_self_destruct_; - - bool pending_read_; - base::MessageLoopForIO::IOContext read_context_; - - // The following members must be used under |owner_->write_lock()| while the - // object is still attached to the owner, and only on the I/O thread - // afterwards. - bool pending_write_; - base::MessageLoopForIO::IOContext write_context_; - - DISALLOW_COPY_AND_ASSIGN(RawChannelIOHandler); - }; - - // |RawChannel| private methods: - IOResult Read(size_t* bytes_read) override; - IOResult ScheduleRead() override; - embedder::ScopedPlatformHandleVectorPtr GetReadPlatformHandles( - size_t num_platform_handles, - const void* platform_handle_table) override; - IOResult WriteNoLock(size_t* platform_handles_written, - size_t* bytes_written) override; - IOResult ScheduleWriteNoLock() override; - void OnInit() override; - void OnShutdownNoLock(scoped_ptr<ReadBuffer> read_buffer, - scoped_ptr<WriteBuffer> write_buffer) override; - - // Passed to |io_handler_| during initialization. - embedder::ScopedPlatformHandle handle_; - - RawChannelIOHandler* io_handler_; - - const bool skip_completion_port_on_success_; - - DISALLOW_COPY_AND_ASSIGN(RawChannelWin); -}; - -RawChannelWin::RawChannelIOHandler::RawChannelIOHandler( - RawChannelWin* owner, - embedder::ScopedPlatformHandle handle) - : handle_(handle.Pass()), - owner_(owner), - suppress_self_destruct_(false), - pending_read_(false), - pending_write_(false) { - memset(&read_context_.overlapped, 0, sizeof(read_context_.overlapped)); - read_context_.handler = this; - memset(&write_context_.overlapped, 0, sizeof(write_context_.overlapped)); - write_context_.handler = this; - - owner_->message_loop_for_io()->RegisterIOHandler(handle_.get().handle, this); -} - -RawChannelWin::RawChannelIOHandler::~RawChannelIOHandler() { - DCHECK(ShouldSelfDestruct()); -} - -bool RawChannelWin::RawChannelIOHandler::pending_read() const { - DCHECK(owner_); - DCHECK_EQ(base::MessageLoop::current(), owner_->message_loop_for_io()); - return pending_read_; -} - -base::MessageLoopForIO::IOContext* -RawChannelWin::RawChannelIOHandler::read_context() { - DCHECK(owner_); - DCHECK_EQ(base::MessageLoop::current(), owner_->message_loop_for_io()); - return &read_context_; -} - -void RawChannelWin::RawChannelIOHandler::OnPendingReadStarted() { - DCHECK(owner_); - DCHECK_EQ(base::MessageLoop::current(), owner_->message_loop_for_io()); - DCHECK(!pending_read_); - pending_read_ = true; -} - -bool RawChannelWin::RawChannelIOHandler::pending_write_no_lock() const { - DCHECK(owner_); - owner_->write_lock().AssertAcquired(); - return pending_write_; -} - -base::MessageLoopForIO::IOContext* -RawChannelWin::RawChannelIOHandler::write_context_no_lock() { - DCHECK(owner_); - owner_->write_lock().AssertAcquired(); - return &write_context_; -} - -void RawChannelWin::RawChannelIOHandler::OnPendingWriteStartedNoLock() { - DCHECK(owner_); - owner_->write_lock().AssertAcquired(); - DCHECK(!pending_write_); - pending_write_ = true; -} - -void RawChannelWin::RawChannelIOHandler::OnIOCompleted( - base::MessageLoopForIO::IOContext* context, - DWORD bytes_transferred, - DWORD error) { - DCHECK(!owner_ || - base::MessageLoop::current() == owner_->message_loop_for_io()); - - { - // Suppress self-destruction inside |OnReadCompleted()|, etc. (in case they - // result in a call to |Shutdown()|). - base::AutoReset<bool> resetter(&suppress_self_destruct_, true); - - if (context == &read_context_) - OnReadCompleted(bytes_transferred, error); - else if (context == &write_context_) - OnWriteCompleted(bytes_transferred, error); - else - NOTREACHED(); - } - - if (ShouldSelfDestruct()) - delete this; -} - -void RawChannelWin::RawChannelIOHandler::DetachFromOwnerNoLock( - scoped_ptr<ReadBuffer> read_buffer, - scoped_ptr<WriteBuffer> write_buffer) { - DCHECK(owner_); - DCHECK_EQ(base::MessageLoop::current(), owner_->message_loop_for_io()); - owner_->write_lock().AssertAcquired(); - - // If read/write is pending, we have to retain the corresponding buffer. - if (pending_read_) - preserved_read_buffer_after_detach_ = read_buffer.Pass(); - if (pending_write_) - preserved_write_buffer_after_detach_ = write_buffer.Pass(); - - owner_ = nullptr; - if (ShouldSelfDestruct()) - delete this; -} - -bool RawChannelWin::RawChannelIOHandler::ShouldSelfDestruct() const { - if (owner_ || suppress_self_destruct_) - return false; - - // Note: Detached, hence no lock needed for |pending_write_|. - return !pending_read_ && !pending_write_; -} - -void RawChannelWin::RawChannelIOHandler::OnReadCompleted(DWORD bytes_read, - DWORD error) { - DCHECK(!owner_ || - base::MessageLoop::current() == owner_->message_loop_for_io()); - DCHECK(suppress_self_destruct_); - - CHECK(pending_read_); - pending_read_ = false; - if (!owner_) - return; - - if (error == ERROR_SUCCESS) { - DCHECK_GT(bytes_read, 0u); - owner_->OnReadCompleted(IO_SUCCEEDED, bytes_read); - } else if (error == ERROR_BROKEN_PIPE) { - DCHECK_EQ(bytes_read, 0u); - owner_->OnReadCompleted(IO_FAILED_SHUTDOWN, 0); - } else { - DCHECK_EQ(bytes_read, 0u); - LOG(WARNING) << "ReadFile: " << logging::SystemErrorCodeToString(error); - owner_->OnReadCompleted(IO_FAILED_UNKNOWN, 0); - } -} - -void RawChannelWin::RawChannelIOHandler::OnWriteCompleted(DWORD bytes_written, - DWORD error) { - DCHECK(!owner_ || - base::MessageLoop::current() == owner_->message_loop_for_io()); - DCHECK(suppress_self_destruct_); - - if (!owner_) { - // No lock needed. - CHECK(pending_write_); - pending_write_ = false; - return; - } - - { - base::AutoLock locker(owner_->write_lock()); - CHECK(pending_write_); - pending_write_ = false; - } - - if (error == ERROR_SUCCESS) { - owner_->OnWriteCompleted(IO_SUCCEEDED, 0, bytes_written); - } else if (error == ERROR_BROKEN_PIPE) { - owner_->OnWriteCompleted(IO_FAILED_SHUTDOWN, 0, 0); - } else { - LOG(WARNING) << "WriteFile: " << logging::SystemErrorCodeToString(error); - owner_->OnWriteCompleted(IO_FAILED_UNKNOWN, 0, 0); - } -} - -RawChannelWin::RawChannelWin(embedder::ScopedPlatformHandle handle) - : handle_(handle.Pass()), - io_handler_(nullptr), - skip_completion_port_on_success_( - g_vista_or_higher_functions.Get().is_vista_or_higher()) { - DCHECK(handle_.is_valid()); -} - -RawChannelWin::~RawChannelWin() { - DCHECK(!io_handler_); -} - -size_t RawChannelWin::GetSerializedPlatformHandleSize() const { - // TODO(vtl): Implement. - return 0; -} - -RawChannel::IOResult RawChannelWin::Read(size_t* bytes_read) { - DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io()); - DCHECK(io_handler_); - DCHECK(!io_handler_->pending_read()); - - char* buffer = nullptr; - size_t bytes_to_read = 0; - read_buffer()->GetBuffer(&buffer, &bytes_to_read); - - BOOL result = - ReadFile(io_handler_->handle(), buffer, static_cast<DWORD>(bytes_to_read), - nullptr, &io_handler_->read_context()->overlapped); - if (!result) { - DWORD error = GetLastError(); - if (error == ERROR_BROKEN_PIPE) - return IO_FAILED_SHUTDOWN; - if (error != ERROR_IO_PENDING) { - LOG(WARNING) << "ReadFile: " << logging::SystemErrorCodeToString(error); - return IO_FAILED_UNKNOWN; - } - } - - if (result && skip_completion_port_on_success_) { - DWORD bytes_read_dword = 0; - BOOL get_size_result = GetOverlappedResult( - io_handler_->handle(), &io_handler_->read_context()->overlapped, - &bytes_read_dword, FALSE); - DPCHECK(get_size_result); - *bytes_read = bytes_read_dword; - return IO_SUCCEEDED; - } - - // If the read is pending or the read has succeeded but we don't skip - // completion port on success, instruct |io_handler_| to wait for the - // completion packet. - // - // TODO(yzshen): It seems there isn't document saying that all error cases - // (other than ERROR_IO_PENDING) are guaranteed to *not* queue a completion - // packet. If we do get one for errors, |RawChannelIOHandler::OnIOCompleted()| - // will crash so we will learn about it. - - io_handler_->OnPendingReadStarted(); - return IO_PENDING; -} - -RawChannel::IOResult RawChannelWin::ScheduleRead() { - DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io()); - DCHECK(io_handler_); - DCHECK(!io_handler_->pending_read()); - - size_t bytes_read = 0; - IOResult io_result = Read(&bytes_read); - if (io_result == IO_SUCCEEDED) { - DCHECK(skip_completion_port_on_success_); - - // We have finished reading successfully. Queue a notification manually. - io_handler_->OnPendingReadStarted(); - // |io_handler_| won't go away before the task is run, so it is safe to use - // |base::Unretained()|. - message_loop_for_io()->PostTask( - FROM_HERE, base::Bind(&RawChannelIOHandler::OnIOCompleted, - base::Unretained(io_handler_), - base::Unretained(io_handler_->read_context()), - static_cast<DWORD>(bytes_read), ERROR_SUCCESS)); - return IO_PENDING; - } - - return io_result; -} - -embedder::ScopedPlatformHandleVectorPtr RawChannelWin::GetReadPlatformHandles( - size_t num_platform_handles, - const void* platform_handle_table) { - // TODO(vtl): Implement. - NOTIMPLEMENTED(); - return embedder::ScopedPlatformHandleVectorPtr(); -} - -RawChannel::IOResult RawChannelWin::WriteNoLock( - size_t* platform_handles_written, - size_t* bytes_written) { - write_lock().AssertAcquired(); - - DCHECK(io_handler_); - DCHECK(!io_handler_->pending_write_no_lock()); - - if (write_buffer_no_lock()->HavePlatformHandlesToSend()) { - // TODO(vtl): Implement. - NOTIMPLEMENTED(); - } - - std::vector<WriteBuffer::Buffer> buffers; - write_buffer_no_lock()->GetBuffers(&buffers); - DCHECK(!buffers.empty()); - - // TODO(yzshen): Handle multi-segment writes more efficiently. - DWORD bytes_written_dword = 0; - BOOL result = - WriteFile(io_handler_->handle(), buffers[0].addr, - static_cast<DWORD>(buffers[0].size), &bytes_written_dword, - &io_handler_->write_context_no_lock()->overlapped); - if (!result) { - DWORD error = GetLastError(); - if (error == ERROR_BROKEN_PIPE) - return IO_FAILED_SHUTDOWN; - if (error != ERROR_IO_PENDING) { - LOG(WARNING) << "WriteFile: " << logging::SystemErrorCodeToString(error); - return IO_FAILED_UNKNOWN; - } - } - - if (result && skip_completion_port_on_success_) { - *platform_handles_written = 0; - *bytes_written = bytes_written_dword; - return IO_SUCCEEDED; - } - - // If the write is pending or the write has succeeded but we don't skip - // completion port on success, instruct |io_handler_| to wait for the - // completion packet. - // - // TODO(yzshen): it seems there isn't document saying that all error cases - // (other than ERROR_IO_PENDING) are guaranteed to *not* queue a completion - // packet. If we do get one for errors, |RawChannelIOHandler::OnIOCompleted()| - // will crash so we will learn about it. - - io_handler_->OnPendingWriteStartedNoLock(); - return IO_PENDING; -} - -RawChannel::IOResult RawChannelWin::ScheduleWriteNoLock() { - write_lock().AssertAcquired(); - - DCHECK(io_handler_); - DCHECK(!io_handler_->pending_write_no_lock()); - - // TODO(vtl): Do something with |platform_handles_written|. - size_t platform_handles_written = 0; - size_t bytes_written = 0; - IOResult io_result = WriteNoLock(&platform_handles_written, &bytes_written); - if (io_result == IO_SUCCEEDED) { - DCHECK(skip_completion_port_on_success_); - - // We have finished writing successfully. Queue a notification manually. - io_handler_->OnPendingWriteStartedNoLock(); - // |io_handler_| won't go away before that task is run, so it is safe to use - // |base::Unretained()|. - message_loop_for_io()->PostTask( - FROM_HERE, - base::Bind(&RawChannelIOHandler::OnIOCompleted, - base::Unretained(io_handler_), - base::Unretained(io_handler_->write_context_no_lock()), - static_cast<DWORD>(bytes_written), ERROR_SUCCESS)); - return IO_PENDING; - } - - return io_result; -} - -void RawChannelWin::OnInit() { - DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io()); - - DCHECK(handle_.is_valid()); - if (skip_completion_port_on_success_) { - // I don't know how this can fail (unless |handle_| is bad, in which case - // it's a bug in our code). - CHECK(g_vista_or_higher_functions.Get().SetFileCompletionNotificationModes( - handle_.get().handle, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)); - } - - DCHECK(!io_handler_); - io_handler_ = new RawChannelIOHandler(this, handle_.Pass()); -} - -void RawChannelWin::OnShutdownNoLock(scoped_ptr<ReadBuffer> read_buffer, - scoped_ptr<WriteBuffer> write_buffer) { - DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io()); - DCHECK(io_handler_); - - write_lock().AssertAcquired(); - - if (io_handler_->pending_read() || io_handler_->pending_write_no_lock()) { - // |io_handler_| will be alive until pending read/write (if any) completes. - // Call |CancelIoEx()| or |CancelIo()| so that resources can be freed up as - // soon as possible. - // Note: |CancelIo()| only cancels read/write requests started from this - // thread. - if (g_vista_or_higher_functions.Get().is_vista_or_higher()) { - g_vista_or_higher_functions.Get().CancelIoEx(io_handler_->handle(), - nullptr); - } else { - CancelIo(io_handler_->handle()); - } - } - - io_handler_->DetachFromOwnerNoLock(read_buffer.Pass(), write_buffer.Pass()); - io_handler_ = nullptr; -} - -} // namespace - -// ----------------------------------------------------------------------------- - -// Static factory method declared in raw_channel.h. -// static -scoped_ptr<RawChannel> RawChannel::Create( - embedder::ScopedPlatformHandle handle) { - return make_scoped_ptr(new RawChannelWin(handle.Pass())); -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/remote_message_pipe_unittest.cc b/mojo/edk/system/remote_message_pipe_unittest.cc deleted file mode 100644 index 1860c23..0000000 --- a/mojo/edk/system/remote_message_pipe_unittest.cc +++ /dev/null @@ -1,1376 +0,0 @@ -// 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 <stdint.h> -#include <stdio.h> -#include <string.h> - -#include <vector> - -#include "base/bind.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/files/scoped_file.h" -#include "base/files/scoped_temp_dir.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "base/test/test_io_thread.h" -#include "base/threading/platform_thread.h" // For |Sleep()|. -#include "build/build_config.h" // TODO(vtl): Remove this. -#include "mojo/edk/embedder/platform_channel_pair.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/embedder/simple_platform_support.h" -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/channel_endpoint.h" -#include "mojo/edk/system/channel_endpoint_id.h" -#include "mojo/edk/system/incoming_endpoint.h" -#include "mojo/edk/system/message_pipe.h" -#include "mojo/edk/system/message_pipe_dispatcher.h" -#include "mojo/edk/system/platform_handle_dispatcher.h" -#include "mojo/edk/system/raw_channel.h" -#include "mojo/edk/system/shared_buffer_dispatcher.h" -#include "mojo/edk/system/test_utils.h" -#include "mojo/edk/system/waiter.h" -#include "mojo/edk/test/test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -const MojoHandleSignals kAllSignals = MOJO_HANDLE_SIGNAL_READABLE | - MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED; - -class RemoteMessagePipeTest : public testing::Test { - public: - RemoteMessagePipeTest() : io_thread_(base::TestIOThread::kAutoStart) {} - ~RemoteMessagePipeTest() override {} - - void SetUp() override { - io_thread_.PostTaskAndWait( - FROM_HERE, base::Bind(&RemoteMessagePipeTest::SetUpOnIOThread, - base::Unretained(this))); - } - - void TearDown() override { - io_thread_.PostTaskAndWait( - FROM_HERE, base::Bind(&RemoteMessagePipeTest::TearDownOnIOThread, - base::Unretained(this))); - } - - protected: - // This connects the two given |ChannelEndpoint|s. It assumes/requires that - // this is the bootstrap case (i.e., no other message pipes have ever been - // hosted on the channel). - void BootstrapChannelEndpoints(scoped_refptr<ChannelEndpoint> ep0, - scoped_refptr<ChannelEndpoint> ep1) { - io_thread_.PostTaskAndWait( - FROM_HERE, - base::Bind(&RemoteMessagePipeTest::BootstrapChannelEndpointsOnIOThread, - base::Unretained(this), ep0, ep1)); - } - - // This bootstraps |ep| on |channels_[channel_index]|. It assumes/requires - // that this is the bootstrap case (i.e., no message pipes have ever been - // hosted on the channel). This returns *without* waiting. - void BootstrapChannelEndpointNoWait(unsigned channel_index, - scoped_refptr<ChannelEndpoint> ep) { - io_thread_.PostTask( - FROM_HERE, - base::Bind(&RemoteMessagePipeTest::BootstrapChannelEndpointOnIOThread, - base::Unretained(this), channel_index, ep)); - } - - void RestoreInitialState() { - io_thread_.PostTaskAndWait( - FROM_HERE, - base::Bind(&RemoteMessagePipeTest::RestoreInitialStateOnIOThread, - base::Unretained(this))); - } - - embedder::PlatformSupport* platform_support() { return &platform_support_; } - base::TestIOThread* io_thread() { return &io_thread_; } - // Warning: It's up to the caller to ensure that the returned channel - // is/remains valid. - Channel* channels(size_t i) { return channels_[i].get(); } - - private: - void SetUpOnIOThread() { - CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop()); - - embedder::PlatformChannelPair channel_pair; - platform_handles_[0] = channel_pair.PassServerHandle(); - platform_handles_[1] = channel_pair.PassClientHandle(); - } - - void TearDownOnIOThread() { - CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop()); - - if (channels_[0]) { - channels_[0]->Shutdown(); - channels_[0] = nullptr; - } - if (channels_[1]) { - channels_[1]->Shutdown(); - channels_[1] = nullptr; - } - } - - void CreateAndInitChannel(unsigned channel_index) { - CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop()); - CHECK(channel_index == 0 || channel_index == 1); - CHECK(!channels_[channel_index]); - - channels_[channel_index] = new Channel(&platform_support_); - channels_[channel_index]->Init( - RawChannel::Create(platform_handles_[channel_index].Pass())); - } - - void BootstrapChannelEndpointsOnIOThread(scoped_refptr<ChannelEndpoint> ep0, - scoped_refptr<ChannelEndpoint> ep1) { - CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop()); - - if (!channels_[0]) - CreateAndInitChannel(0); - if (!channels_[1]) - CreateAndInitChannel(1); - - channels_[0]->SetBootstrapEndpoint(ep0); - channels_[1]->SetBootstrapEndpoint(ep1); - } - - void BootstrapChannelEndpointOnIOThread(unsigned channel_index, - scoped_refptr<ChannelEndpoint> ep) { - CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop()); - CHECK(channel_index == 0 || channel_index == 1); - - CreateAndInitChannel(channel_index); - channels_[channel_index]->SetBootstrapEndpoint(ep); - } - - void RestoreInitialStateOnIOThread() { - CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop()); - - TearDownOnIOThread(); - SetUpOnIOThread(); - } - - embedder::SimplePlatformSupport platform_support_; - base::TestIOThread io_thread_; - embedder::ScopedPlatformHandle platform_handles_[2]; - scoped_refptr<Channel> channels_[2]; - - DISALLOW_COPY_AND_ASSIGN(RemoteMessagePipeTest); -}; - -TEST_F(RemoteMessagePipeTest, Basic) { - static const char kHello[] = "hello"; - static const char kWorld[] = "world!!!1!!!1!"; - char buffer[100] = {0}; - uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer)); - Waiter waiter; - HandleSignalsState hss; - uint32_t context = 0; - - // Connect message pipes. MP 0, port 1 will be attached to channel 0 and - // connected to MP 1, port 0, which will be attached to channel 1. This leaves - // MP 0, port 0 and MP 1, port 1 as the "user-facing" endpoints. - - scoped_refptr<ChannelEndpoint> ep0; - scoped_refptr<MessagePipe> mp0(MessagePipe::CreateLocalProxy(&ep0)); - scoped_refptr<ChannelEndpoint> ep1; - scoped_refptr<MessagePipe> mp1(MessagePipe::CreateProxyLocal(&ep1)); - BootstrapChannelEndpoints(ep0, ep1); - - // Write in one direction: MP 0, port 0 -> ... -> MP 1, port 1. - - // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do - // it later, it might already be readable.) - waiter.Init(); - ASSERT_EQ( - MOJO_RESULT_OK, - mp1->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, nullptr)); - - // Write to MP 0, port 0. - EXPECT_EQ( - MOJO_RESULT_OK, - mp0->WriteMessage(0, UserPointer<const void>(kHello), sizeof(kHello), - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Wait. - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(123u, context); - hss = HandleSignalsState(); - mp1->RemoveAwakable(1, &waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Read from MP 1, port 1. - EXPECT_EQ(MOJO_RESULT_OK, - mp1->ReadMessage(1, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kHello), static_cast<size_t>(buffer_size)); - EXPECT_STREQ(kHello, buffer); - - // Write in the other direction: MP 1, port 1 -> ... -> MP 0, port 0. - - waiter.Init(); - ASSERT_EQ( - MOJO_RESULT_OK, - mp0->AddAwakable(0, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 456, nullptr)); - - EXPECT_EQ( - MOJO_RESULT_OK, - mp1->WriteMessage(1, UserPointer<const void>(kWorld), sizeof(kWorld), - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(456u, context); - hss = HandleSignalsState(); - mp0->RemoveAwakable(0, &waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - buffer_size = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - mp0->ReadMessage(0, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kWorld), static_cast<size_t>(buffer_size)); - EXPECT_STREQ(kWorld, buffer); - - // Close MP 0, port 0. - mp0->Close(0); - - // Try to wait for MP 1, port 1 to become readable. This will eventually fail - // when it realizes that MP 0, port 0 has been closed. (It may also fail - // immediately.) - waiter.Init(); - hss = HandleSignalsState(); - MojoResult result = - mp1->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 789, &hss); - if (result == MOJO_RESULT_OK) { - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(789u, context); - hss = HandleSignalsState(); - mp1->RemoveAwakable(1, &waiter, &hss); - } - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - // And MP 1, port 1. - mp1->Close(1); -} - -TEST_F(RemoteMessagePipeTest, PeerClosed) { - Waiter waiter; - HandleSignalsState hss; - uint32_t context = 0; - - // Connect message pipes. MP 0, port 1 will be attached to channel 0 and - // connected to MP 1, port 0, which will be attached to channel 1. This leaves - // MP 0, port 0 and MP 1, port 1 as the "user-facing" endpoints. - - scoped_refptr<ChannelEndpoint> ep0; - scoped_refptr<MessagePipe> mp0(MessagePipe::CreateLocalProxy(&ep0)); - scoped_refptr<ChannelEndpoint> ep1; - scoped_refptr<MessagePipe> mp1(MessagePipe::CreateProxyLocal(&ep1)); - BootstrapChannelEndpoints(ep0, ep1); - - // Close MP 0, port 0. - mp0->Close(0); - - // Try to wait for MP 1, port 1 to be signaled with peer closed. - waiter.Init(); - hss = HandleSignalsState(); - MojoResult result = - mp1->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_PEER_CLOSED, 101, &hss); - if (result == MOJO_RESULT_OK) { - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(101u, context); - hss = HandleSignalsState(); - mp1->RemoveAwakable(1, &waiter, &hss); - } - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - // And MP 1, port 1. - mp1->Close(1); -} - -TEST_F(RemoteMessagePipeTest, Multiplex) { - static const char kHello[] = "hello"; - static const char kWorld[] = "world!!!1!!!1!"; - char buffer[100] = {0}; - uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer)); - Waiter waiter; - HandleSignalsState hss; - uint32_t context = 0; - - // Connect message pipes as in the |Basic| test. - - scoped_refptr<ChannelEndpoint> ep0; - scoped_refptr<MessagePipe> mp0(MessagePipe::CreateLocalProxy(&ep0)); - scoped_refptr<ChannelEndpoint> ep1; - scoped_refptr<MessagePipe> mp1(MessagePipe::CreateProxyLocal(&ep1)); - BootstrapChannelEndpoints(ep0, ep1); - - // Now put another message pipe on the channel. - - // Do this by creating a message pipe (for the |channels(0)| side) and - // attaching and running it, yielding the remote ID. A message is then sent - // via |ep0| (i.e., sent using |mp0|, port 0) with this remote ID. Upon - // receiving this message, |PassIncomingMessagePipe()| is used to obtain the - // message pipe on the other side. - scoped_refptr<MessagePipe> mp2(MessagePipe::CreateLocalLocal()); - ASSERT_TRUE(channels(0)); - size_t max_endpoint_info_size; - size_t max_platform_handle_count; - mp2->StartSerialize(1, channels(0), &max_endpoint_info_size, - &max_platform_handle_count); - EXPECT_GT(max_endpoint_info_size, 0u); - ASSERT_EQ(0u, max_platform_handle_count); - scoped_ptr<char[]> endpoint_info(new char[max_endpoint_info_size]); - size_t endpoint_info_size; - mp2->EndSerialize(1, channels(0), endpoint_info.get(), &endpoint_info_size, - nullptr); - EXPECT_EQ(max_endpoint_info_size, endpoint_info_size); - - waiter.Init(); - ASSERT_EQ( - MOJO_RESULT_OK, - mp1->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, nullptr)); - - EXPECT_EQ(MOJO_RESULT_OK, - mp0->WriteMessage(0, UserPointer<const void>(endpoint_info.get()), - static_cast<uint32_t>(endpoint_info_size), - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(123u, context); - hss = HandleSignalsState(); - mp1->RemoveAwakable(1, &waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - EXPECT_EQ(endpoint_info_size, channels(1)->GetSerializedEndpointSize()); - scoped_ptr<char[]> received_endpoint_info(new char[endpoint_info_size]); - buffer_size = static_cast<uint32_t>(endpoint_info_size); - EXPECT_EQ(MOJO_RESULT_OK, - mp1->ReadMessage(1, UserPointer<void>(received_endpoint_info.get()), - MakeUserPointer(&buffer_size), nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(endpoint_info_size, static_cast<size_t>(buffer_size)); - EXPECT_EQ(0, memcmp(received_endpoint_info.get(), endpoint_info.get(), - endpoint_info_size)); - - // Warning: The local side of mp3 is port 0, not port 1. - scoped_refptr<IncomingEndpoint> incoming_endpoint = - channels(1)->DeserializeEndpoint(received_endpoint_info.get()); - ASSERT_TRUE(incoming_endpoint); - scoped_refptr<MessagePipe> mp3 = incoming_endpoint->ConvertToMessagePipe(); - ASSERT_TRUE(mp3); - - // Write: MP 2, port 0 -> MP 3, port 1. - - waiter.Init(); - ASSERT_EQ( - MOJO_RESULT_OK, - mp3->AddAwakable(0, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 789, nullptr)); - - EXPECT_EQ( - MOJO_RESULT_OK, - mp2->WriteMessage(0, UserPointer<const void>(kHello), sizeof(kHello), - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(789u, context); - hss = HandleSignalsState(); - mp3->RemoveAwakable(0, &waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Make sure there's nothing on MP 0, port 0 or MP 1, port 1 or MP 2, port 0. - buffer_size = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - mp0->ReadMessage(0, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - buffer_size = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - mp1->ReadMessage(1, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - buffer_size = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - mp2->ReadMessage(0, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - - // Read from MP 3, port 1. - buffer_size = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - mp3->ReadMessage(0, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kHello), static_cast<size_t>(buffer_size)); - EXPECT_STREQ(kHello, buffer); - - // Write: MP 0, port 0 -> MP 1, port 1 again. - - waiter.Init(); - ASSERT_EQ( - MOJO_RESULT_OK, - mp1->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, nullptr)); - - EXPECT_EQ( - MOJO_RESULT_OK, - mp0->WriteMessage(0, UserPointer<const void>(kWorld), sizeof(kWorld), - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(123u, context); - hss = HandleSignalsState(); - mp1->RemoveAwakable(1, &waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Make sure there's nothing on the other ports. - buffer_size = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - mp0->ReadMessage(0, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - buffer_size = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - mp2->ReadMessage(0, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - buffer_size = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - mp3->ReadMessage(0, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - - buffer_size = static_cast<uint32_t>(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - mp1->ReadMessage(1, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kWorld), static_cast<size_t>(buffer_size)); - EXPECT_STREQ(kWorld, buffer); - - mp0->Close(0); - mp1->Close(1); - mp2->Close(0); - mp3->Close(0); -} - -TEST_F(RemoteMessagePipeTest, CloseBeforeAttachAndRun) { - static const char kHello[] = "hello"; - char buffer[100] = {0}; - uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer)); - Waiter waiter; - HandleSignalsState hss; - uint32_t context = 0; - - // Connect message pipes. MP 0, port 1 will be attached to channel 0 and - // connected to MP 1, port 0, which will be attached to channel 1. This leaves - // MP 0, port 0 and MP 1, port 1 as the "user-facing" endpoints. - - scoped_refptr<ChannelEndpoint> ep0; - scoped_refptr<MessagePipe> mp0(MessagePipe::CreateLocalProxy(&ep0)); - - // Write to MP 0, port 0. - EXPECT_EQ( - MOJO_RESULT_OK, - mp0->WriteMessage(0, UserPointer<const void>(kHello), sizeof(kHello), - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Close MP 0, port 0 before it's even been attached to the channel and run. - mp0->Close(0); - - BootstrapChannelEndpointNoWait(0, ep0); - - scoped_refptr<ChannelEndpoint> ep1; - scoped_refptr<MessagePipe> mp1(MessagePipe::CreateProxyLocal(&ep1)); - - // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do - // it later, it might already be readable.) - waiter.Init(); - ASSERT_EQ( - MOJO_RESULT_OK, - mp1->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, nullptr)); - - BootstrapChannelEndpointNoWait(1, ep1); - - // Wait. - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(123u, context); - hss = HandleSignalsState(); - // Note: MP 1, port 1 should definitely should be readable, but it may or may - // not appear as writable (there's a race, and it may not have noticed that - // the other side was closed yet -- e.g., inserting a sleep here would make it - // much more likely to notice that it's no longer writable). - mp1->RemoveAwakable(1, &waiter, &hss); - EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE)); - EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE)); - - // Read from MP 1, port 1. - EXPECT_EQ(MOJO_RESULT_OK, - mp1->ReadMessage(1, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kHello), static_cast<size_t>(buffer_size)); - EXPECT_STREQ(kHello, buffer); - - // And MP 1, port 1. - mp1->Close(1); -} - -TEST_F(RemoteMessagePipeTest, CloseBeforeConnect) { - static const char kHello[] = "hello"; - char buffer[100] = {0}; - uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer)); - Waiter waiter; - HandleSignalsState hss; - uint32_t context = 0; - - // Connect message pipes. MP 0, port 1 will be attached to channel 0 and - // connected to MP 1, port 0, which will be attached to channel 1. This leaves - // MP 0, port 0 and MP 1, port 1 as the "user-facing" endpoints. - - scoped_refptr<ChannelEndpoint> ep0; - scoped_refptr<MessagePipe> mp0(MessagePipe::CreateLocalProxy(&ep0)); - - // Write to MP 0, port 0. - EXPECT_EQ( - MOJO_RESULT_OK, - mp0->WriteMessage(0, UserPointer<const void>(kHello), sizeof(kHello), - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - BootstrapChannelEndpointNoWait(0, ep0); - - // Close MP 0, port 0 before channel 1 is even connected. - mp0->Close(0); - - scoped_refptr<ChannelEndpoint> ep1; - scoped_refptr<MessagePipe> mp1(MessagePipe::CreateProxyLocal(&ep1)); - - // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do - // it later, it might already be readable.) - waiter.Init(); - ASSERT_EQ( - MOJO_RESULT_OK, - mp1->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, nullptr)); - - BootstrapChannelEndpointNoWait(1, ep1); - - // Wait. - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(123u, context); - hss = HandleSignalsState(); - // Note: MP 1, port 1 should definitely should be readable, but it may or may - // not appear as writable (there's a race, and it may not have noticed that - // the other side was closed yet -- e.g., inserting a sleep here would make it - // much more likely to notice that it's no longer writable). - mp1->RemoveAwakable(1, &waiter, &hss); - EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE)); - EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE)); - - // Read from MP 1, port 1. - EXPECT_EQ(MOJO_RESULT_OK, - mp1->ReadMessage(1, UserPointer<void>(buffer), - MakeUserPointer(&buffer_size), nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kHello), static_cast<size_t>(buffer_size)); - EXPECT_STREQ(kHello, buffer); - - // And MP 1, port 1. - mp1->Close(1); -} - -TEST_F(RemoteMessagePipeTest, HandlePassing) { - static const char kHello[] = "hello"; - Waiter waiter; - HandleSignalsState hss; - uint32_t context = 0; - - scoped_refptr<ChannelEndpoint> ep0; - scoped_refptr<MessagePipe> mp0(MessagePipe::CreateLocalProxy(&ep0)); - scoped_refptr<ChannelEndpoint> ep1; - scoped_refptr<MessagePipe> mp1(MessagePipe::CreateProxyLocal(&ep1)); - BootstrapChannelEndpoints(ep0, ep1); - - // We'll try to pass this dispatcher. - scoped_refptr<MessagePipeDispatcher> dispatcher( - new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions)); - scoped_refptr<MessagePipe> local_mp(MessagePipe::CreateLocalLocal()); - dispatcher->Init(local_mp, 0); - - // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do - // it later, it might already be readable.) - waiter.Init(); - ASSERT_EQ( - MOJO_RESULT_OK, - mp1->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, nullptr)); - - // Write to MP 0, port 0. - { - DispatcherTransport transport( - test::DispatcherTryStartTransport(dispatcher.get())); - EXPECT_TRUE(transport.is_valid()); - - std::vector<DispatcherTransport> transports; - transports.push_back(transport); - EXPECT_EQ( - MOJO_RESULT_OK, - mp0->WriteMessage(0, UserPointer<const void>(kHello), sizeof(kHello), - &transports, MOJO_WRITE_MESSAGE_FLAG_NONE)); - transport.End(); - - // |dispatcher| should have been closed. This is |DCHECK()|ed when the - // |dispatcher| is destroyed. - EXPECT_TRUE(dispatcher->HasOneRef()); - dispatcher = nullptr; - } - - // Wait. - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(123u, context); - hss = HandleSignalsState(); - mp1->RemoveAwakable(1, &waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Read from MP 1, port 1. - char read_buffer[100] = {0}; - uint32_t read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer)); - DispatcherVector read_dispatchers; - uint32_t read_num_dispatchers = 10; // Maximum to get. - EXPECT_EQ( - MOJO_RESULT_OK, - mp1->ReadMessage(1, UserPointer<void>(read_buffer), - MakeUserPointer(&read_buffer_size), &read_dispatchers, - &read_num_dispatchers, MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size)); - EXPECT_STREQ(kHello, read_buffer); - EXPECT_EQ(1u, read_dispatchers.size()); - EXPECT_EQ(1u, read_num_dispatchers); - ASSERT_TRUE(read_dispatchers[0]); - EXPECT_TRUE(read_dispatchers[0]->HasOneRef()); - - EXPECT_EQ(Dispatcher::kTypeMessagePipe, read_dispatchers[0]->GetType()); - dispatcher = static_cast<MessagePipeDispatcher*>(read_dispatchers[0].get()); - - // Add the waiter now, before it becomes readable to avoid a race. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - dispatcher->AddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 456, - nullptr)); - - // Write to "local_mp", port 1. - EXPECT_EQ( - MOJO_RESULT_OK, - local_mp->WriteMessage(1, UserPointer<const void>(kHello), sizeof(kHello), - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // TODO(vtl): FIXME -- We (racily) crash if I close |dispatcher| immediately - // here. (We don't crash if I sleep and then close.) - - // Wait for the dispatcher to become readable. - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(456u, context); - hss = HandleSignalsState(); - dispatcher->RemoveAwakable(&waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Read from the dispatcher. - memset(read_buffer, 0, sizeof(read_buffer)); - read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - dispatcher->ReadMessage(UserPointer<void>(read_buffer), - MakeUserPointer(&read_buffer_size), 0, - nullptr, MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size)); - EXPECT_STREQ(kHello, read_buffer); - - // Prepare to wait on "local_mp", port 1. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - local_mp->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 789, - nullptr)); - - // Write to the dispatcher. - EXPECT_EQ(MOJO_RESULT_OK, dispatcher->WriteMessage( - UserPointer<const void>(kHello), sizeof(kHello), - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Wait. - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(789u, context); - hss = HandleSignalsState(); - local_mp->RemoveAwakable(1, &waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Read from "local_mp", port 1. - memset(read_buffer, 0, sizeof(read_buffer)); - read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - local_mp->ReadMessage(1, UserPointer<void>(read_buffer), - MakeUserPointer(&read_buffer_size), nullptr, - nullptr, MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size)); - EXPECT_STREQ(kHello, read_buffer); - - // TODO(vtl): Also test that messages queued up before the handle was sent are - // delivered properly. - - // Close everything that belongs to us. - mp0->Close(0); - mp1->Close(1); - EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close()); - // Note that |local_mp|'s port 0 belong to |dispatcher|, which was closed. - local_mp->Close(1); -} - -TEST_F(RemoteMessagePipeTest, HandlePassingHalfClosed) { - static const char kHello[] = "hello"; - static const char kWorld[] = "world!"; - Waiter waiter; - HandleSignalsState hss; - uint32_t context = 0; - - // We'll try to pass this dispatcher. - scoped_refptr<MessagePipeDispatcher> dispatcher( - new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions)); - scoped_refptr<MessagePipe> local_mp(MessagePipe::CreateLocalLocal()); - dispatcher->Init(local_mp, 0); - - hss = local_mp->GetHandleSignalsState(0); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - // Write to the other end (|local_mp|, port 1), and then close it. - EXPECT_EQ( - MOJO_RESULT_OK, - local_mp->WriteMessage(1, UserPointer<const void>(kHello), sizeof(kHello), - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - hss = local_mp->GetHandleSignalsState(0); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - // Then the second message.... - EXPECT_EQ( - MOJO_RESULT_OK, - local_mp->WriteMessage(1, UserPointer<const void>(kWorld), sizeof(kWorld), - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - hss = local_mp->GetHandleSignalsState(0); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - // Then close it. - local_mp->Close(1); - - scoped_refptr<ChannelEndpoint> ep0; - scoped_refptr<MessagePipe> mp0(MessagePipe::CreateLocalProxy(&ep0)); - scoped_refptr<ChannelEndpoint> ep1; - scoped_refptr<MessagePipe> mp1(MessagePipe::CreateProxyLocal(&ep1)); - BootstrapChannelEndpoints(ep0, ep1); - - // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do - // it later, it might already be readable.) - waiter.Init(); - ASSERT_EQ( - MOJO_RESULT_OK, - mp1->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, nullptr)); - - // Write to MP 0, port 0. - { - DispatcherTransport transport( - test::DispatcherTryStartTransport(dispatcher.get())); - EXPECT_TRUE(transport.is_valid()); - - std::vector<DispatcherTransport> transports; - transports.push_back(transport); - EXPECT_EQ( - MOJO_RESULT_OK, - mp0->WriteMessage(0, UserPointer<const void>(kHello), sizeof(kHello), - &transports, MOJO_WRITE_MESSAGE_FLAG_NONE)); - transport.End(); - - // |dispatcher| should have been closed. This is |DCHECK()|ed when the - // |dispatcher| is destroyed. - EXPECT_TRUE(dispatcher->HasOneRef()); - dispatcher = nullptr; - } - - // Wait. - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(123u, context); - hss = HandleSignalsState(); - mp1->RemoveAwakable(1, &waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Read from MP 1, port 1. - char read_buffer[100] = {0}; - uint32_t read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer)); - DispatcherVector read_dispatchers; - uint32_t read_num_dispatchers = 10; // Maximum to get. - EXPECT_EQ( - MOJO_RESULT_OK, - mp1->ReadMessage(1, UserPointer<void>(read_buffer), - MakeUserPointer(&read_buffer_size), &read_dispatchers, - &read_num_dispatchers, MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size)); - EXPECT_STREQ(kHello, read_buffer); - EXPECT_EQ(1u, read_dispatchers.size()); - EXPECT_EQ(1u, read_num_dispatchers); - ASSERT_TRUE(read_dispatchers[0]); - EXPECT_TRUE(read_dispatchers[0]->HasOneRef()); - - EXPECT_EQ(Dispatcher::kTypeMessagePipe, read_dispatchers[0]->GetType()); - dispatcher = static_cast<MessagePipeDispatcher*>(read_dispatchers[0].get()); - - // |dispatcher| should already be readable and not writable. - hss = dispatcher->GetHandleSignalsState(); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - // So read from it. - memset(read_buffer, 0, sizeof(read_buffer)); - read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - dispatcher->ReadMessage(UserPointer<void>(read_buffer), - MakeUserPointer(&read_buffer_size), 0, - nullptr, MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size)); - EXPECT_STREQ(kHello, read_buffer); - // It should still be readable. - hss = dispatcher->GetHandleSignalsState(); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - // So read from it. - memset(read_buffer, 0, sizeof(read_buffer)); - read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - dispatcher->ReadMessage(UserPointer<void>(read_buffer), - MakeUserPointer(&read_buffer_size), 0, - nullptr, MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kWorld), static_cast<size_t>(read_buffer_size)); - EXPECT_STREQ(kWorld, read_buffer); - // Now it should no longer be readable. - hss = dispatcher->GetHandleSignalsState(); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - // Close everything that belongs to us. - mp0->Close(0); - mp1->Close(1); - EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close()); -} - -#if defined(OS_POSIX) -#define MAYBE_SharedBufferPassing SharedBufferPassing -#else -// Not yet implemented (on Windows). -#define MAYBE_SharedBufferPassing DISABLED_SharedBufferPassing -#endif -TEST_F(RemoteMessagePipeTest, MAYBE_SharedBufferPassing) { - static const char kHello[] = "hello"; - Waiter waiter; - HandleSignalsState hss; - uint32_t context = 0; - - scoped_refptr<ChannelEndpoint> ep0; - scoped_refptr<MessagePipe> mp0(MessagePipe::CreateLocalProxy(&ep0)); - scoped_refptr<ChannelEndpoint> ep1; - scoped_refptr<MessagePipe> mp1(MessagePipe::CreateProxyLocal(&ep1)); - BootstrapChannelEndpoints(ep0, ep1); - - // We'll try to pass this dispatcher. - scoped_refptr<SharedBufferDispatcher> dispatcher; - EXPECT_EQ(MOJO_RESULT_OK, SharedBufferDispatcher::Create( - platform_support(), - SharedBufferDispatcher::kDefaultCreateOptions, - 100, &dispatcher)); - ASSERT_TRUE(dispatcher); - - // Make a mapping. - scoped_ptr<embedder::PlatformSharedBufferMapping> mapping0; - EXPECT_EQ(MOJO_RESULT_OK, dispatcher->MapBuffer( - 0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping0)); - ASSERT_TRUE(mapping0); - ASSERT_TRUE(mapping0->GetBase()); - ASSERT_EQ(100u, mapping0->GetLength()); - static_cast<char*>(mapping0->GetBase())[0] = 'A'; - static_cast<char*>(mapping0->GetBase())[50] = 'B'; - static_cast<char*>(mapping0->GetBase())[99] = 'C'; - - // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do - // it later, it might already be readable.) - waiter.Init(); - ASSERT_EQ( - MOJO_RESULT_OK, - mp1->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, nullptr)); - - // Write to MP 0, port 0. - { - DispatcherTransport transport( - test::DispatcherTryStartTransport(dispatcher.get())); - EXPECT_TRUE(transport.is_valid()); - - std::vector<DispatcherTransport> transports; - transports.push_back(transport); - EXPECT_EQ( - MOJO_RESULT_OK, - mp0->WriteMessage(0, UserPointer<const void>(kHello), sizeof(kHello), - &transports, MOJO_WRITE_MESSAGE_FLAG_NONE)); - transport.End(); - - // |dispatcher| should have been closed. This is |DCHECK()|ed when the - // |dispatcher| is destroyed. - EXPECT_TRUE(dispatcher->HasOneRef()); - dispatcher = nullptr; - } - - // Wait. - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(123u, context); - hss = HandleSignalsState(); - mp1->RemoveAwakable(1, &waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Read from MP 1, port 1. - char read_buffer[100] = {0}; - uint32_t read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer)); - DispatcherVector read_dispatchers; - uint32_t read_num_dispatchers = 10; // Maximum to get. - EXPECT_EQ( - MOJO_RESULT_OK, - mp1->ReadMessage(1, UserPointer<void>(read_buffer), - MakeUserPointer(&read_buffer_size), &read_dispatchers, - &read_num_dispatchers, MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size)); - EXPECT_STREQ(kHello, read_buffer); - EXPECT_EQ(1u, read_dispatchers.size()); - EXPECT_EQ(1u, read_num_dispatchers); - ASSERT_TRUE(read_dispatchers[0]); - EXPECT_TRUE(read_dispatchers[0]->HasOneRef()); - - EXPECT_EQ(Dispatcher::kTypeSharedBuffer, read_dispatchers[0]->GetType()); - dispatcher = static_cast<SharedBufferDispatcher*>(read_dispatchers[0].get()); - - // Make another mapping. - scoped_ptr<embedder::PlatformSharedBufferMapping> mapping1; - EXPECT_EQ(MOJO_RESULT_OK, dispatcher->MapBuffer( - 0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping1)); - ASSERT_TRUE(mapping1); - ASSERT_TRUE(mapping1->GetBase()); - ASSERT_EQ(100u, mapping1->GetLength()); - EXPECT_NE(mapping1->GetBase(), mapping0->GetBase()); - EXPECT_EQ('A', static_cast<char*>(mapping1->GetBase())[0]); - EXPECT_EQ('B', static_cast<char*>(mapping1->GetBase())[50]); - EXPECT_EQ('C', static_cast<char*>(mapping1->GetBase())[99]); - - // Write stuff either way. - static_cast<char*>(mapping1->GetBase())[1] = 'x'; - EXPECT_EQ('x', static_cast<char*>(mapping0->GetBase())[1]); - static_cast<char*>(mapping0->GetBase())[2] = 'y'; - EXPECT_EQ('y', static_cast<char*>(mapping1->GetBase())[2]); - - // Kill the first mapping; the second should still be valid. - mapping0.reset(); - EXPECT_EQ('A', static_cast<char*>(mapping1->GetBase())[0]); - - // Close everything that belongs to us. - mp0->Close(0); - mp1->Close(1); - EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close()); - - // The second mapping should still be good. - EXPECT_EQ('x', static_cast<char*>(mapping1->GetBase())[1]); -} - -#if defined(OS_POSIX) -#define MAYBE_PlatformHandlePassing PlatformHandlePassing -#else -// Not yet implemented (on Windows). -#define MAYBE_PlatformHandlePassing DISABLED_PlatformHandlePassing -#endif -TEST_F(RemoteMessagePipeTest, MAYBE_PlatformHandlePassing) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - static const char kHello[] = "hello"; - static const char kWorld[] = "world"; - Waiter waiter; - uint32_t context = 0; - HandleSignalsState hss; - - scoped_refptr<ChannelEndpoint> ep0; - scoped_refptr<MessagePipe> mp0(MessagePipe::CreateLocalProxy(&ep0)); - scoped_refptr<ChannelEndpoint> ep1; - scoped_refptr<MessagePipe> mp1(MessagePipe::CreateProxyLocal(&ep1)); - BootstrapChannelEndpoints(ep0, ep1); - - base::FilePath unused; - base::ScopedFILE fp( - base::CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused)); - EXPECT_EQ(sizeof(kHello), fwrite(kHello, 1, sizeof(kHello), fp.get())); - // We'll try to pass this dispatcher, which will cause a |PlatformHandle| to - // be passed. - scoped_refptr<PlatformHandleDispatcher> dispatcher( - new PlatformHandleDispatcher( - mojo::test::PlatformHandleFromFILE(fp.Pass()))); - - // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do - // it later, it might already be readable.) - waiter.Init(); - ASSERT_EQ( - MOJO_RESULT_OK, - mp1->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, nullptr)); - - // Write to MP 0, port 0. - { - DispatcherTransport transport( - test::DispatcherTryStartTransport(dispatcher.get())); - EXPECT_TRUE(transport.is_valid()); - - std::vector<DispatcherTransport> transports; - transports.push_back(transport); - EXPECT_EQ( - MOJO_RESULT_OK, - mp0->WriteMessage(0, UserPointer<const void>(kWorld), sizeof(kWorld), - &transports, MOJO_WRITE_MESSAGE_FLAG_NONE)); - transport.End(); - - // |dispatcher| should have been closed. This is |DCHECK()|ed when the - // |dispatcher| is destroyed. - EXPECT_TRUE(dispatcher->HasOneRef()); - dispatcher = nullptr; - } - - // Wait. - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(123u, context); - hss = HandleSignalsState(); - mp1->RemoveAwakable(1, &waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Read from MP 1, port 1. - char read_buffer[100] = {0}; - uint32_t read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer)); - DispatcherVector read_dispatchers; - uint32_t read_num_dispatchers = 10; // Maximum to get. - EXPECT_EQ( - MOJO_RESULT_OK, - mp1->ReadMessage(1, UserPointer<void>(read_buffer), - MakeUserPointer(&read_buffer_size), &read_dispatchers, - &read_num_dispatchers, MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kWorld), static_cast<size_t>(read_buffer_size)); - EXPECT_STREQ(kWorld, read_buffer); - EXPECT_EQ(1u, read_dispatchers.size()); - EXPECT_EQ(1u, read_num_dispatchers); - ASSERT_TRUE(read_dispatchers[0]); - EXPECT_TRUE(read_dispatchers[0]->HasOneRef()); - - EXPECT_EQ(Dispatcher::kTypePlatformHandle, read_dispatchers[0]->GetType()); - dispatcher = - static_cast<PlatformHandleDispatcher*>(read_dispatchers[0].get()); - - embedder::ScopedPlatformHandle h = dispatcher->PassPlatformHandle().Pass(); - EXPECT_TRUE(h.is_valid()); - - fp = mojo::test::FILEFromPlatformHandle(h.Pass(), "rb").Pass(); - EXPECT_FALSE(h.is_valid()); - EXPECT_TRUE(fp); - - rewind(fp.get()); - memset(read_buffer, 0, sizeof(read_buffer)); - EXPECT_EQ(sizeof(kHello), - fread(read_buffer, 1, sizeof(read_buffer), fp.get())); - EXPECT_STREQ(kHello, read_buffer); - - // Close everything that belongs to us. - mp0->Close(0); - mp1->Close(1); - EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close()); -} - -// Test racing closes (on each end). -// Note: A flaky failure would almost certainly indicate a problem in the code -// itself (not in the test). Also, any logged warnings/errors would also -// probably be indicative of bugs. -TEST_F(RemoteMessagePipeTest, RacingClosesStress) { - base::TimeDelta delay = base::TimeDelta::FromMilliseconds(5); - - for (unsigned i = 0; i < 256; i++) { - DVLOG(2) << "---------------------------------------- " << i; - scoped_refptr<ChannelEndpoint> ep0; - scoped_refptr<MessagePipe> mp0(MessagePipe::CreateLocalProxy(&ep0)); - BootstrapChannelEndpointNoWait(0, ep0); - - scoped_refptr<ChannelEndpoint> ep1; - scoped_refptr<MessagePipe> mp1(MessagePipe::CreateProxyLocal(&ep1)); - BootstrapChannelEndpointNoWait(1, ep1); - - if (i & 1u) { - io_thread()->task_runner()->PostTask( - FROM_HERE, base::Bind(&base::PlatformThread::Sleep, delay)); - } - if (i & 2u) - base::PlatformThread::Sleep(delay); - - mp0->Close(0); - - if (i & 4u) { - io_thread()->task_runner()->PostTask( - FROM_HERE, base::Bind(&base::PlatformThread::Sleep, delay)); - } - if (i & 8u) - base::PlatformThread::Sleep(delay); - - mp1->Close(1); - - RestoreInitialState(); - } -} - -// Tests passing an end of a message pipe over a remote message pipe, and then -// passing that end back. -// TODO(vtl): Also test passing a message pipe across two remote message pipes. -TEST_F(RemoteMessagePipeTest, PassMessagePipeHandleAcrossAndBack) { - static const char kHello[] = "hello"; - static const char kWorld[] = "world"; - Waiter waiter; - HandleSignalsState hss; - uint32_t context = 0; - - scoped_refptr<ChannelEndpoint> ep0; - scoped_refptr<MessagePipe> mp0(MessagePipe::CreateLocalProxy(&ep0)); - scoped_refptr<ChannelEndpoint> ep1; - scoped_refptr<MessagePipe> mp1(MessagePipe::CreateProxyLocal(&ep1)); - BootstrapChannelEndpoints(ep0, ep1); - - // We'll try to pass this dispatcher. - scoped_refptr<MessagePipeDispatcher> dispatcher( - new MessagePipeDispatcher(MessagePipeDispatcher::kDefaultCreateOptions)); - scoped_refptr<MessagePipe> local_mp(MessagePipe::CreateLocalLocal()); - dispatcher->Init(local_mp, 0); - - // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do - // it later, it might already be readable.) - waiter.Init(); - ASSERT_EQ( - MOJO_RESULT_OK, - mp1->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123, nullptr)); - - // Write to MP 0, port 0. - { - DispatcherTransport transport( - test::DispatcherTryStartTransport(dispatcher.get())); - EXPECT_TRUE(transport.is_valid()); - - std::vector<DispatcherTransport> transports; - transports.push_back(transport); - EXPECT_EQ( - MOJO_RESULT_OK, - mp0->WriteMessage(0, UserPointer<const void>(kHello), sizeof(kHello), - &transports, MOJO_WRITE_MESSAGE_FLAG_NONE)); - transport.End(); - - // |dispatcher| should have been closed. This is |DCHECK()|ed when the - // |dispatcher| is destroyed. - EXPECT_TRUE(dispatcher->HasOneRef()); - dispatcher = nullptr; - } - - // Wait. - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(123u, context); - hss = HandleSignalsState(); - mp1->RemoveAwakable(1, &waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Read from MP 1, port 1. - char read_buffer[100] = {0}; - uint32_t read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer)); - DispatcherVector read_dispatchers; - uint32_t read_num_dispatchers = 10; // Maximum to get. - EXPECT_EQ( - MOJO_RESULT_OK, - mp1->ReadMessage(1, UserPointer<void>(read_buffer), - MakeUserPointer(&read_buffer_size), &read_dispatchers, - &read_num_dispatchers, MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size)); - EXPECT_STREQ(kHello, read_buffer); - EXPECT_EQ(1u, read_dispatchers.size()); - EXPECT_EQ(1u, read_num_dispatchers); - ASSERT_TRUE(read_dispatchers[0]); - EXPECT_TRUE(read_dispatchers[0]->HasOneRef()); - - EXPECT_EQ(Dispatcher::kTypeMessagePipe, read_dispatchers[0]->GetType()); - dispatcher = static_cast<MessagePipeDispatcher*>(read_dispatchers[0].get()); - read_dispatchers.clear(); - - // Now pass it back. - - // Prepare to wait on MP 0, port 0. (Add the waiter now. Otherwise, if we do - // it later, it might already be readable.) - waiter.Init(); - ASSERT_EQ( - MOJO_RESULT_OK, - mp0->AddAwakable(0, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 456, nullptr)); - - // Write to MP 1, port 1. - { - DispatcherTransport transport( - test::DispatcherTryStartTransport(dispatcher.get())); - EXPECT_TRUE(transport.is_valid()); - - std::vector<DispatcherTransport> transports; - transports.push_back(transport); - EXPECT_EQ( - MOJO_RESULT_OK, - mp1->WriteMessage(1, UserPointer<const void>(kWorld), sizeof(kWorld), - &transports, MOJO_WRITE_MESSAGE_FLAG_NONE)); - transport.End(); - - // |dispatcher| should have been closed. This is |DCHECK()|ed when the - // |dispatcher| is destroyed. - EXPECT_TRUE(dispatcher->HasOneRef()); - dispatcher = nullptr; - } - - // Wait. - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(456u, context); - hss = HandleSignalsState(); - mp0->RemoveAwakable(0, &waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Read from MP 0, port 0. - read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer)); - read_num_dispatchers = 10; // Maximum to get. - EXPECT_EQ( - MOJO_RESULT_OK, - mp0->ReadMessage(0, UserPointer<void>(read_buffer), - MakeUserPointer(&read_buffer_size), &read_dispatchers, - &read_num_dispatchers, MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kWorld), static_cast<size_t>(read_buffer_size)); - EXPECT_STREQ(kWorld, read_buffer); - EXPECT_EQ(1u, read_dispatchers.size()); - EXPECT_EQ(1u, read_num_dispatchers); - ASSERT_TRUE(read_dispatchers[0]); - EXPECT_TRUE(read_dispatchers[0]->HasOneRef()); - - EXPECT_EQ(Dispatcher::kTypeMessagePipe, read_dispatchers[0]->GetType()); - dispatcher = static_cast<MessagePipeDispatcher*>(read_dispatchers[0].get()); - read_dispatchers.clear(); - - // Add the waiter now, before it becomes readable to avoid a race. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - dispatcher->AddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 789, - nullptr)); - - // Write to "local_mp", port 1. - EXPECT_EQ( - MOJO_RESULT_OK, - local_mp->WriteMessage(1, UserPointer<const void>(kHello), sizeof(kHello), - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Wait for the dispatcher to become readable. - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(789u, context); - hss = HandleSignalsState(); - dispatcher->RemoveAwakable(&waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Read from the dispatcher. - memset(read_buffer, 0, sizeof(read_buffer)); - read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - dispatcher->ReadMessage(UserPointer<void>(read_buffer), - MakeUserPointer(&read_buffer_size), 0, - nullptr, MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size)); - EXPECT_STREQ(kHello, read_buffer); - - // Prepare to wait on "local_mp", port 1. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - local_mp->AddAwakable(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 789, - nullptr)); - - // Write to the dispatcher. - EXPECT_EQ(MOJO_RESULT_OK, dispatcher->WriteMessage( - UserPointer<const void>(kHello), sizeof(kHello), - nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Wait. - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_EQ(789u, context); - hss = HandleSignalsState(); - local_mp->RemoveAwakable(1, &waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - EXPECT_EQ(kAllSignals, hss.satisfiable_signals); - - // Read from "local_mp", port 1. - memset(read_buffer, 0, sizeof(read_buffer)); - read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - local_mp->ReadMessage(1, UserPointer<void>(read_buffer), - MakeUserPointer(&read_buffer_size), nullptr, - nullptr, MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size)); - EXPECT_STREQ(kHello, read_buffer); - - // TODO(vtl): Also test the cases where messages are written and read (at - // various points) on the message pipe being passed around. - - // Close everything that belongs to us. - mp0->Close(0); - mp1->Close(1); - EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close()); - // Note that |local_mp|'s port 0 belong to |dispatcher|, which was closed. - local_mp->Close(1); -} - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/run_all_unittests.cc b/mojo/edk/system/run_all_unittests.cc deleted file mode 100644 index cd61337..0000000 --- a/mojo/edk/system/run_all_unittests.cc +++ /dev/null @@ -1,24 +0,0 @@ -// 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 "base/bind.h" -#include "base/test/launcher/unit_test_launcher.h" -#include "base/test/test_suite.h" -#include "testing/gtest/include/gtest/gtest.h" - -int main(int argc, char** argv) { -// Silence death test thread warnings on Linux. We can afford to run our death -// tests a little more slowly (< 10 ms per death test on a Z620). -// On android, we need to run in the default mode, as the threadsafe mode -// relies on execve which is not available. -#if !defined(OS_ANDROID) - testing::GTEST_FLAG(death_test_style) = "threadsafe"; -#endif - - base::TestSuite test_suite(argc, argv); - - return base::LaunchUnitTests( - argc, argv, - base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite))); -} diff --git a/mojo/edk/system/shared_buffer_dispatcher.cc b/mojo/edk/system/shared_buffer_dispatcher.cc deleted file mode 100644 index db823d5..0000000 --- a/mojo/edk/system/shared_buffer_dispatcher.cc +++ /dev/null @@ -1,276 +0,0 @@ -// 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/edk/system/shared_buffer_dispatcher.h" - -#include <limits> - -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "mojo/edk/embedder/platform_support.h" -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/configuration.h" -#include "mojo/edk/system/memory.h" -#include "mojo/edk/system/options_validation.h" -#include "mojo/public/c/system/macros.h" - -namespace mojo { -namespace system { - -namespace { - -struct SerializedSharedBufferDispatcher { - size_t num_bytes; - size_t platform_handle_index; -}; - -} // namespace - -// static -const MojoCreateSharedBufferOptions - SharedBufferDispatcher::kDefaultCreateOptions = { - static_cast<uint32_t>(sizeof(MojoCreateSharedBufferOptions)), - MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE}; - -// static -MojoResult SharedBufferDispatcher::ValidateCreateOptions( - UserPointer<const MojoCreateSharedBufferOptions> in_options, - MojoCreateSharedBufferOptions* out_options) { - const MojoCreateSharedBufferOptionsFlags kKnownFlags = - MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE; - - *out_options = kDefaultCreateOptions; - if (in_options.IsNull()) - return MOJO_RESULT_OK; - - UserOptionsReader<MojoCreateSharedBufferOptions> reader(in_options); - if (!reader.is_valid()) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (!OPTIONS_STRUCT_HAS_MEMBER(MojoCreateSharedBufferOptions, flags, reader)) - return MOJO_RESULT_OK; - if ((reader.options().flags & ~kKnownFlags)) - return MOJO_RESULT_UNIMPLEMENTED; - out_options->flags = reader.options().flags; - - // Checks for fields beyond |flags|: - - // (Nothing here yet.) - - return MOJO_RESULT_OK; -} - -// static -MojoResult SharedBufferDispatcher::Create( - embedder::PlatformSupport* platform_support, - const MojoCreateSharedBufferOptions& /*validated_options*/, - uint64_t num_bytes, - scoped_refptr<SharedBufferDispatcher>* result) { - if (!num_bytes) - return MOJO_RESULT_INVALID_ARGUMENT; - if (num_bytes > GetConfiguration().max_shared_memory_num_bytes) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - - scoped_refptr<embedder::PlatformSharedBuffer> shared_buffer( - platform_support->CreateSharedBuffer(static_cast<size_t>(num_bytes))); - if (!shared_buffer) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - - *result = new SharedBufferDispatcher(shared_buffer); - return MOJO_RESULT_OK; -} - -Dispatcher::Type SharedBufferDispatcher::GetType() const { - return kTypeSharedBuffer; -} - -// static -scoped_refptr<SharedBufferDispatcher> SharedBufferDispatcher::Deserialize( - Channel* channel, - const void* source, - size_t size, - embedder::PlatformHandleVector* platform_handles) { - DCHECK(channel); - - if (size != sizeof(SerializedSharedBufferDispatcher)) { - LOG(ERROR) << "Invalid serialized shared buffer dispatcher (bad size)"; - return nullptr; - } - - const SerializedSharedBufferDispatcher* serialization = - static_cast<const SerializedSharedBufferDispatcher*>(source); - size_t num_bytes = serialization->num_bytes; - size_t platform_handle_index = serialization->platform_handle_index; - - if (!num_bytes) { - LOG(ERROR) - << "Invalid serialized shared buffer dispatcher (invalid num_bytes)"; - return nullptr; - } - - if (!platform_handles || platform_handle_index >= platform_handles->size()) { - LOG(ERROR) - << "Invalid serialized shared buffer dispatcher (missing handles)"; - return nullptr; - } - - // Starts off invalid, which is what we want. - embedder::PlatformHandle platform_handle; - // We take ownership of the handle, so we have to invalidate the one in - // |platform_handles|. - std::swap(platform_handle, (*platform_handles)[platform_handle_index]); - - // Wrapping |platform_handle| in a |ScopedPlatformHandle| means that it'll be - // closed even if creation fails. - scoped_refptr<embedder::PlatformSharedBuffer> shared_buffer( - channel->platform_support()->CreateSharedBufferFromHandle( - num_bytes, embedder::ScopedPlatformHandle(platform_handle))); - if (!shared_buffer) { - LOG(ERROR) - << "Invalid serialized shared buffer dispatcher (invalid num_bytes?)"; - return nullptr; - } - - return scoped_refptr<SharedBufferDispatcher>( - new SharedBufferDispatcher(shared_buffer)); -} - -SharedBufferDispatcher::SharedBufferDispatcher( - scoped_refptr<embedder::PlatformSharedBuffer> shared_buffer) - : shared_buffer_(shared_buffer) { - DCHECK(shared_buffer_); -} - -SharedBufferDispatcher::~SharedBufferDispatcher() { -} - -// static -MojoResult SharedBufferDispatcher::ValidateDuplicateOptions( - UserPointer<const MojoDuplicateBufferHandleOptions> in_options, - MojoDuplicateBufferHandleOptions* out_options) { - const MojoDuplicateBufferHandleOptionsFlags kKnownFlags = - MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE; - static const MojoDuplicateBufferHandleOptions kDefaultOptions = { - static_cast<uint32_t>(sizeof(MojoDuplicateBufferHandleOptions)), - MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE}; - - *out_options = kDefaultOptions; - if (in_options.IsNull()) - return MOJO_RESULT_OK; - - UserOptionsReader<MojoDuplicateBufferHandleOptions> reader(in_options); - if (!reader.is_valid()) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (!OPTIONS_STRUCT_HAS_MEMBER(MojoDuplicateBufferHandleOptions, flags, - reader)) - return MOJO_RESULT_OK; - if ((reader.options().flags & ~kKnownFlags)) - return MOJO_RESULT_UNIMPLEMENTED; - out_options->flags = reader.options().flags; - - // Checks for fields beyond |flags|: - - // (Nothing here yet.) - - return MOJO_RESULT_OK; -} - -void SharedBufferDispatcher::CloseImplNoLock() { - lock().AssertAcquired(); - DCHECK(shared_buffer_); - shared_buffer_ = nullptr; -} - -scoped_refptr<Dispatcher> -SharedBufferDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() { - lock().AssertAcquired(); - DCHECK(shared_buffer_); - scoped_refptr<embedder::PlatformSharedBuffer> shared_buffer; - shared_buffer.swap(shared_buffer_); - return scoped_refptr<Dispatcher>(new SharedBufferDispatcher(shared_buffer)); -} - -MojoResult SharedBufferDispatcher::DuplicateBufferHandleImplNoLock( - UserPointer<const MojoDuplicateBufferHandleOptions> options, - scoped_refptr<Dispatcher>* new_dispatcher) { - lock().AssertAcquired(); - - MojoDuplicateBufferHandleOptions validated_options; - MojoResult result = ValidateDuplicateOptions(options, &validated_options); - if (result != MOJO_RESULT_OK) - return result; - - *new_dispatcher = new SharedBufferDispatcher(shared_buffer_); - return MOJO_RESULT_OK; -} - -MojoResult SharedBufferDispatcher::MapBufferImplNoLock( - uint64_t offset, - uint64_t num_bytes, - MojoMapBufferFlags flags, - scoped_ptr<embedder::PlatformSharedBufferMapping>* mapping) { - lock().AssertAcquired(); - DCHECK(shared_buffer_); - - if (offset > static_cast<uint64_t>(std::numeric_limits<size_t>::max())) - return MOJO_RESULT_INVALID_ARGUMENT; - if (num_bytes > static_cast<uint64_t>(std::numeric_limits<size_t>::max())) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (!shared_buffer_->IsValidMap(static_cast<size_t>(offset), - static_cast<size_t>(num_bytes))) - return MOJO_RESULT_INVALID_ARGUMENT; - - DCHECK(mapping); - *mapping = shared_buffer_->MapNoCheck(static_cast<size_t>(offset), - static_cast<size_t>(num_bytes)); - if (!*mapping) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - - return MOJO_RESULT_OK; -} - -void SharedBufferDispatcher::StartSerializeImplNoLock( - Channel* /*channel*/, - size_t* max_size, - size_t* max_platform_handles) { - DCHECK(HasOneRef()); // Only one ref => no need to take the lock. - *max_size = sizeof(SerializedSharedBufferDispatcher); - *max_platform_handles = 1; -} - -bool SharedBufferDispatcher::EndSerializeAndCloseImplNoLock( - Channel* /*channel*/, - void* destination, - size_t* actual_size, - embedder::PlatformHandleVector* platform_handles) { - DCHECK(HasOneRef()); // Only one ref => no need to take the lock. - DCHECK(shared_buffer_); - - SerializedSharedBufferDispatcher* serialization = - static_cast<SerializedSharedBufferDispatcher*>(destination); - // If there's only one reference to |shared_buffer_|, then it's ours (and no - // one else can make any more references to it), so we can just take its - // handle. - embedder::ScopedPlatformHandle platform_handle( - shared_buffer_->HasOneRef() ? shared_buffer_->PassPlatformHandle() - : shared_buffer_->DuplicatePlatformHandle()); - if (!platform_handle.is_valid()) { - shared_buffer_ = nullptr; - return false; - } - - serialization->num_bytes = shared_buffer_->GetNumBytes(); - serialization->platform_handle_index = platform_handles->size(); - platform_handles->push_back(platform_handle.release()); - *actual_size = sizeof(SerializedSharedBufferDispatcher); - - shared_buffer_ = nullptr; - - return true; -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/shared_buffer_dispatcher.h b/mojo/edk/system/shared_buffer_dispatcher.h deleted file mode 100644 index db04d2a..0000000 --- a/mojo/edk/system/shared_buffer_dispatcher.h +++ /dev/null @@ -1,103 +0,0 @@ -// 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_EDK_SYSTEM_SHARED_BUFFER_DISPATCHER_H_ -#define MOJO_EDK_SYSTEM_SHARED_BUFFER_DISPATCHER_H_ - -#include "base/macros.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/system/memory.h" -#include "mojo/edk/system/simple_dispatcher.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { - -namespace embedder { -class PlatformSupport; -} - -namespace system { - -// TODO(vtl): We derive from SimpleDispatcher, even though we don't currently -// have anything that's waitable. I want to add a "transferrable" wait flag -// (which would entail overriding |GetHandleSignalsStateImplNoLock()|, etc.). -class MOJO_SYSTEM_IMPL_EXPORT SharedBufferDispatcher : public SimpleDispatcher { - public: - // The default options to use for |MojoCreateSharedBuffer()|. (Real uses - // should obtain this via |ValidateCreateOptions()| with a null |in_options|; - // this is exposed directly for testing convenience.) - static const MojoCreateSharedBufferOptions kDefaultCreateOptions; - - // Validates and/or sets default options for |MojoCreateSharedBufferOptions|. - // If non-null, |in_options| must point to a struct of at least - // |in_options->struct_size| bytes. |out_options| must point to a (current) - // |MojoCreateSharedBufferOptions| and will be entirely overwritten on success - // (it may be partly overwritten on failure). - static MojoResult ValidateCreateOptions( - UserPointer<const MojoCreateSharedBufferOptions> in_options, - MojoCreateSharedBufferOptions* out_options); - - // Static factory method: |validated_options| must be validated (obviously). - // On failure, |*result| will be left as-is. - static MojoResult Create( - embedder::PlatformSupport* platform_support, - const MojoCreateSharedBufferOptions& validated_options, - uint64_t num_bytes, - scoped_refptr<SharedBufferDispatcher>* result); - - // |Dispatcher| public methods: - Type GetType() const override; - - // The "opposite" of |SerializeAndClose()|. (Typically this is called by - // |Dispatcher::Deserialize()|.) - static scoped_refptr<SharedBufferDispatcher> Deserialize( - Channel* channel, - const void* source, - size_t size, - embedder::PlatformHandleVector* platform_handles); - - private: - explicit SharedBufferDispatcher( - scoped_refptr<embedder::PlatformSharedBuffer> shared_buffer_); - ~SharedBufferDispatcher() override; - - // Validates and/or sets default options for - // |MojoDuplicateBufferHandleOptions|. If non-null, |in_options| must point to - // a struct of at least |in_options->struct_size| bytes. |out_options| must - // point to a (current) |MojoDuplicateBufferHandleOptions| and will be - // entirely overwritten on success (it may be partly overwritten on failure). - static MojoResult ValidateDuplicateOptions( - UserPointer<const MojoDuplicateBufferHandleOptions> in_options, - MojoDuplicateBufferHandleOptions* out_options); - - // |Dispatcher| protected methods: - void CloseImplNoLock() override; - scoped_refptr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock() - override; - MojoResult DuplicateBufferHandleImplNoLock( - UserPointer<const MojoDuplicateBufferHandleOptions> options, - scoped_refptr<Dispatcher>* new_dispatcher) override; - MojoResult MapBufferImplNoLock( - uint64_t offset, - uint64_t num_bytes, - MojoMapBufferFlags flags, - scoped_ptr<embedder::PlatformSharedBufferMapping>* mapping) override; - void StartSerializeImplNoLock(Channel* channel, - size_t* max_size, - size_t* max_platform_handles) override; - bool EndSerializeAndCloseImplNoLock( - Channel* channel, - void* destination, - size_t* actual_size, - embedder::PlatformHandleVector* platform_handles) override; - - scoped_refptr<embedder::PlatformSharedBuffer> shared_buffer_; - - DISALLOW_COPY_AND_ASSIGN(SharedBufferDispatcher); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_SHARED_BUFFER_DISPATCHER_H_ diff --git a/mojo/edk/system/shared_buffer_dispatcher_unittest.cc b/mojo/edk/system/shared_buffer_dispatcher_unittest.cc deleted file mode 100644 index 29dcb57..0000000 --- a/mojo/edk/system/shared_buffer_dispatcher_unittest.cc +++ /dev/null @@ -1,279 +0,0 @@ -// 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/edk/system/shared_buffer_dispatcher.h" - -#include <limits> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/embedder/simple_platform_support.h" -#include "mojo/edk/system/dispatcher.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -// NOTE(vtl): There's currently not much to test for in -// |SharedBufferDispatcher::ValidateCreateOptions()|, but the tests should be -// expanded if/when options are added, so I've kept the general form of the -// tests from data_pipe_unittest.cc. - -const uint32_t kSizeOfCreateOptions = sizeof(MojoCreateSharedBufferOptions); - -// Does a cursory sanity check of |validated_options|. Calls -// |ValidateCreateOptions()| on already-validated options. The validated options -// should be valid, and the revalidated copy should be the same. -void RevalidateCreateOptions( - const MojoCreateSharedBufferOptions& validated_options) { - EXPECT_EQ(kSizeOfCreateOptions, validated_options.struct_size); - // Nothing to check for flags. - - MojoCreateSharedBufferOptions revalidated_options = {}; - EXPECT_EQ(MOJO_RESULT_OK, - SharedBufferDispatcher::ValidateCreateOptions( - MakeUserPointer(&validated_options), &revalidated_options)); - EXPECT_EQ(validated_options.struct_size, revalidated_options.struct_size); - EXPECT_EQ(validated_options.flags, revalidated_options.flags); -} - -class SharedBufferDispatcherTest : public testing::Test { - public: - SharedBufferDispatcherTest() {} - ~SharedBufferDispatcherTest() override {} - - embedder::PlatformSupport* platform_support() { return &platform_support_; } - - private: - embedder::SimplePlatformSupport platform_support_; - - DISALLOW_COPY_AND_ASSIGN(SharedBufferDispatcherTest); -}; - -// Tests valid inputs to |ValidateCreateOptions()|. -TEST_F(SharedBufferDispatcherTest, ValidateCreateOptionsValid) { - // Default options. - { - MojoCreateSharedBufferOptions validated_options = {}; - EXPECT_EQ(MOJO_RESULT_OK, SharedBufferDispatcher::ValidateCreateOptions( - NullUserPointer(), &validated_options)); - RevalidateCreateOptions(validated_options); - } - - // Different flags. - MojoCreateSharedBufferOptionsFlags flags_values[] = { - MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE}; - for (size_t i = 0; i < arraysize(flags_values); i++) { - const MojoCreateSharedBufferOptionsFlags flags = flags_values[i]; - - // Different capacities (size 1). - for (uint32_t capacity = 1; capacity <= 100 * 1000 * 1000; capacity *= 10) { - MojoCreateSharedBufferOptions options = { - kSizeOfCreateOptions, // |struct_size|. - flags // |flags|. - }; - MojoCreateSharedBufferOptions validated_options = {}; - EXPECT_EQ(MOJO_RESULT_OK, - SharedBufferDispatcher::ValidateCreateOptions( - MakeUserPointer(&options), &validated_options)) - << capacity; - RevalidateCreateOptions(validated_options); - EXPECT_EQ(options.flags, validated_options.flags); - } - } -} - -TEST_F(SharedBufferDispatcherTest, ValidateCreateOptionsInvalid) { - // Invalid |struct_size|. - { - MojoCreateSharedBufferOptions options = { - 1, // |struct_size|. - MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE // |flags|. - }; - MojoCreateSharedBufferOptions unused; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - SharedBufferDispatcher::ValidateCreateOptions( - MakeUserPointer(&options), &unused)); - } - - // Unknown |flags|. - { - MojoCreateSharedBufferOptions options = { - kSizeOfCreateOptions, // |struct_size|. - ~0u // |flags|. - }; - MojoCreateSharedBufferOptions unused; - EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, - SharedBufferDispatcher::ValidateCreateOptions( - MakeUserPointer(&options), &unused)); - } -} - -TEST_F(SharedBufferDispatcherTest, CreateAndMapBuffer) { - scoped_refptr<SharedBufferDispatcher> dispatcher; - EXPECT_EQ(MOJO_RESULT_OK, SharedBufferDispatcher::Create( - platform_support(), - SharedBufferDispatcher::kDefaultCreateOptions, - 100, &dispatcher)); - ASSERT_TRUE(dispatcher); - EXPECT_EQ(Dispatcher::kTypeSharedBuffer, dispatcher->GetType()); - - // Make a couple of mappings. - scoped_ptr<embedder::PlatformSharedBufferMapping> mapping1; - EXPECT_EQ(MOJO_RESULT_OK, dispatcher->MapBuffer( - 0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping1)); - ASSERT_TRUE(mapping1); - ASSERT_TRUE(mapping1->GetBase()); - EXPECT_EQ(100u, mapping1->GetLength()); - // Write something. - static_cast<char*>(mapping1->GetBase())[50] = 'x'; - - scoped_ptr<embedder::PlatformSharedBufferMapping> mapping2; - EXPECT_EQ(MOJO_RESULT_OK, dispatcher->MapBuffer( - 50, 50, MOJO_MAP_BUFFER_FLAG_NONE, &mapping2)); - ASSERT_TRUE(mapping2); - ASSERT_TRUE(mapping2->GetBase()); - EXPECT_EQ(50u, mapping2->GetLength()); - EXPECT_EQ('x', static_cast<char*>(mapping2->GetBase())[0]); - - EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close()); - - // Check that we can still read/write to mappings after the dispatcher has - // gone away. - static_cast<char*>(mapping2->GetBase())[1] = 'y'; - EXPECT_EQ('y', static_cast<char*>(mapping1->GetBase())[51]); -} - -TEST_F(SharedBufferDispatcherTest, DuplicateBufferHandle) { - scoped_refptr<SharedBufferDispatcher> dispatcher1; - EXPECT_EQ(MOJO_RESULT_OK, SharedBufferDispatcher::Create( - platform_support(), - SharedBufferDispatcher::kDefaultCreateOptions, - 100, &dispatcher1)); - - // Map and write something. - scoped_ptr<embedder::PlatformSharedBufferMapping> mapping; - EXPECT_EQ(MOJO_RESULT_OK, dispatcher1->MapBuffer( - 0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping)); - static_cast<char*>(mapping->GetBase())[0] = 'x'; - mapping.reset(); - - // Duplicate |dispatcher1| and then close it. - scoped_refptr<Dispatcher> dispatcher2; - EXPECT_EQ(MOJO_RESULT_OK, dispatcher1->DuplicateBufferHandle( - NullUserPointer(), &dispatcher2)); - ASSERT_TRUE(dispatcher2); - EXPECT_EQ(Dispatcher::kTypeSharedBuffer, dispatcher2->GetType()); - - EXPECT_EQ(MOJO_RESULT_OK, dispatcher1->Close()); - - // Map |dispatcher2| and read something. - EXPECT_EQ(MOJO_RESULT_OK, dispatcher2->MapBuffer( - 0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping)); - EXPECT_EQ('x', static_cast<char*>(mapping->GetBase())[0]); - - EXPECT_EQ(MOJO_RESULT_OK, dispatcher2->Close()); -} - -TEST_F(SharedBufferDispatcherTest, DuplicateBufferHandleOptionsValid) { - scoped_refptr<SharedBufferDispatcher> dispatcher1; - EXPECT_EQ(MOJO_RESULT_OK, SharedBufferDispatcher::Create( - platform_support(), - SharedBufferDispatcher::kDefaultCreateOptions, - 100, &dispatcher1)); - - MojoDuplicateBufferHandleOptions options[] = { - {sizeof(MojoDuplicateBufferHandleOptions), - MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE}, - {sizeof(MojoDuplicateBufferHandleOptionsFlags), ~0u}}; - for (size_t i = 0; i < arraysize(options); i++) { - scoped_refptr<Dispatcher> dispatcher2; - EXPECT_EQ(MOJO_RESULT_OK, dispatcher1->DuplicateBufferHandle( - MakeUserPointer(&options[i]), &dispatcher2)); - ASSERT_TRUE(dispatcher2); - EXPECT_EQ(Dispatcher::kTypeSharedBuffer, dispatcher2->GetType()); - EXPECT_EQ(MOJO_RESULT_OK, dispatcher2->Close()); - } - - EXPECT_EQ(MOJO_RESULT_OK, dispatcher1->Close()); -} - -TEST_F(SharedBufferDispatcherTest, DuplicateBufferHandleOptionsInvalid) { - scoped_refptr<SharedBufferDispatcher> dispatcher1; - EXPECT_EQ(MOJO_RESULT_OK, SharedBufferDispatcher::Create( - platform_support(), - SharedBufferDispatcher::kDefaultCreateOptions, - 100, &dispatcher1)); - - // Invalid |struct_size|. - { - MojoDuplicateBufferHandleOptions options = { - 1u, MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE}; - scoped_refptr<Dispatcher> dispatcher2; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - dispatcher1->DuplicateBufferHandle(MakeUserPointer(&options), - &dispatcher2)); - EXPECT_FALSE(dispatcher2); - } - - // Unknown |flags|. - { - MojoDuplicateBufferHandleOptions options = { - sizeof(MojoDuplicateBufferHandleOptions), ~0u}; - scoped_refptr<Dispatcher> dispatcher2; - EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, - dispatcher1->DuplicateBufferHandle(MakeUserPointer(&options), - &dispatcher2)); - EXPECT_FALSE(dispatcher2); - } - - EXPECT_EQ(MOJO_RESULT_OK, dispatcher1->Close()); -} - -TEST_F(SharedBufferDispatcherTest, CreateInvalidNumBytes) { - // Size too big. - scoped_refptr<SharedBufferDispatcher> dispatcher; - EXPECT_EQ( - MOJO_RESULT_RESOURCE_EXHAUSTED, - SharedBufferDispatcher::Create( - platform_support(), SharedBufferDispatcher::kDefaultCreateOptions, - std::numeric_limits<uint64_t>::max(), &dispatcher)); - EXPECT_FALSE(dispatcher); - - // Zero size. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - SharedBufferDispatcher::Create( - platform_support(), - SharedBufferDispatcher::kDefaultCreateOptions, 0, &dispatcher)); - EXPECT_FALSE(dispatcher); -} - -TEST_F(SharedBufferDispatcherTest, MapBufferInvalidArguments) { - scoped_refptr<SharedBufferDispatcher> dispatcher; - EXPECT_EQ(MOJO_RESULT_OK, SharedBufferDispatcher::Create( - platform_support(), - SharedBufferDispatcher::kDefaultCreateOptions, - 100, &dispatcher)); - - scoped_ptr<embedder::PlatformSharedBufferMapping> mapping; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - dispatcher->MapBuffer(0, 101, MOJO_MAP_BUFFER_FLAG_NONE, &mapping)); - EXPECT_FALSE(mapping); - - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - dispatcher->MapBuffer(1, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping)); - EXPECT_FALSE(mapping); - - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - dispatcher->MapBuffer(0, 0, MOJO_MAP_BUFFER_FLAG_NONE, &mapping)); - EXPECT_FALSE(mapping); - - EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close()); -} - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/simple_dispatcher.cc b/mojo/edk/system/simple_dispatcher.cc deleted file mode 100644 index f7db875..0000000 --- a/mojo/edk/system/simple_dispatcher.cc +++ /dev/null @@ -1,61 +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/edk/system/simple_dispatcher.h" - -#include "base/logging.h" - -namespace mojo { -namespace system { - -SimpleDispatcher::SimpleDispatcher() { -} - -SimpleDispatcher::~SimpleDispatcher() { -} - -void SimpleDispatcher::HandleSignalsStateChangedNoLock() { - lock().AssertAcquired(); - awakable_list_.AwakeForStateChange(GetHandleSignalsStateImplNoLock()); -} - -void SimpleDispatcher::CancelAllAwakablesNoLock() { - lock().AssertAcquired(); - awakable_list_.CancelAll(); -} - -MojoResult SimpleDispatcher::AddAwakableImplNoLock( - Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state) { - lock().AssertAcquired(); - - HandleSignalsState state(GetHandleSignalsStateImplNoLock()); - if (state.satisfies(signals)) { - if (signals_state) - *signals_state = state; - return MOJO_RESULT_ALREADY_EXISTS; - } - if (!state.can_satisfy(signals)) { - if (signals_state) - *signals_state = state; - return MOJO_RESULT_FAILED_PRECONDITION; - } - - awakable_list_.Add(awakable, signals, context); - return MOJO_RESULT_OK; -} - -void SimpleDispatcher::RemoveAwakableImplNoLock( - Awakable* awakable, - HandleSignalsState* signals_state) { - lock().AssertAcquired(); - awakable_list_.Remove(awakable); - if (signals_state) - *signals_state = GetHandleSignalsStateImplNoLock(); -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/simple_dispatcher.h b/mojo/edk/system/simple_dispatcher.h deleted file mode 100644 index eddf614..0000000 --- a/mojo/edk/system/simple_dispatcher.h +++ /dev/null @@ -1,51 +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_EDK_SYSTEM_SIMPLE_DISPATCHER_H_ -#define MOJO_EDK_SYSTEM_SIMPLE_DISPATCHER_H_ - -#include <list> - -#include "base/macros.h" -#include "mojo/edk/system/awakable_list.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -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.cc). This class implements the standard waiter-signalling mechanism -// in that case. -class MOJO_SYSTEM_IMPL_EXPORT SimpleDispatcher : public Dispatcher { - protected: - SimpleDispatcher(); - ~SimpleDispatcher() override; - - // To be called by subclasses when the state changes (so - // |GetHandleSignalsStateImplNoLock()| should be checked again). Must be - // called under lock. - void HandleSignalsStateChangedNoLock(); - - // |Dispatcher| protected methods: - void CancelAllAwakablesNoLock() override; - MojoResult AddAwakableImplNoLock(Awakable* awakable, - MojoHandleSignals signals, - uint32_t context, - HandleSignalsState* signals_state) override; - void RemoveAwakableImplNoLock(Awakable* awakable, - HandleSignalsState* signals_state) override; - - private: - // Protected by |lock()|: - AwakableList awakable_list_; - - DISALLOW_COPY_AND_ASSIGN(SimpleDispatcher); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_SIMPLE_DISPATCHER_H_ diff --git a/mojo/edk/system/simple_dispatcher_unittest.cc b/mojo/edk/system/simple_dispatcher_unittest.cc deleted file mode 100644 index b8e57e9..0000000 --- a/mojo/edk/system/simple_dispatcher_unittest.cc +++ /dev/null @@ -1,609 +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. - -// NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a -// heavily-loaded system). Sorry. |test::EpsilonTimeout()| may be increased to -// increase tolerance and reduce observed flakiness (though doing so reduces the -// meaningfulness of the test). - -#include "mojo/edk/system/simple_dispatcher.h" - -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_vector.h" -#include "base/synchronization/lock.h" -#include "base/threading/platform_thread.h" // For |Sleep()|. -#include "base/time/time.h" -#include "mojo/edk/system/test_utils.h" -#include "mojo/edk/system/waiter.h" -#include "mojo/edk/system/waiter_test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -class MockSimpleDispatcher : public SimpleDispatcher { - public: - MockSimpleDispatcher() - : state_(MOJO_HANDLE_SIGNAL_NONE, - MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE) {} - - void SetSatisfiedSignals(MojoHandleSignals new_satisfied_signals) { - base::AutoLock locker(lock()); - - // Any new signals that are set should be satisfiable. - CHECK_EQ(new_satisfied_signals & ~state_.satisfied_signals, - new_satisfied_signals & ~state_.satisfied_signals & - state_.satisfiable_signals); - - if (new_satisfied_signals == state_.satisfied_signals) - return; - - state_.satisfied_signals = new_satisfied_signals; - HandleSignalsStateChangedNoLock(); - } - - void SetSatisfiableSignals(MojoHandleSignals new_satisfiable_signals) { - base::AutoLock locker(lock()); - - // Satisfied implies satisfiable. - CHECK_EQ(new_satisfiable_signals & state_.satisfied_signals, - state_.satisfied_signals); - - if (new_satisfiable_signals == state_.satisfiable_signals) - return; - - state_.satisfiable_signals = new_satisfiable_signals; - HandleSignalsStateChangedNoLock(); - } - - Type GetType() const override { return kTypeUnknown; } - - private: - friend class base::RefCountedThreadSafe<MockSimpleDispatcher>; - ~MockSimpleDispatcher() override {} - - scoped_refptr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock() - override { - scoped_refptr<MockSimpleDispatcher> rv(new MockSimpleDispatcher()); - rv->state_ = state_; - return scoped_refptr<Dispatcher>(rv.get()); - } - - // |Dispatcher| override: - HandleSignalsState GetHandleSignalsStateImplNoLock() const override { - lock().AssertAcquired(); - return state_; - } - - // Protected by |lock()|: - HandleSignalsState state_; - - DISALLOW_COPY_AND_ASSIGN(MockSimpleDispatcher); -}; - -#if defined(OS_WIN) -// http://crbug.com/396404 -#define MAYBE_Basic DISABLED_Basic -#else -#define MAYBE_Basic Basic -#endif -TEST(SimpleDispatcherTest, MAYBE_Basic) { - test::Stopwatch stopwatch; - - scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); - Waiter w; - uint32_t context = 0; - HandleSignalsState hss; - - // Try adding a readable waiter when already readable. - w.Init(); - d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfiable_signals); - // Shouldn't need to remove the waiter (it was not added). - - // Wait (forever) for writable when already writable. - w.Init(); - d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE); - ASSERT_EQ(MOJO_RESULT_OK, - d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 1, nullptr)); - d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_WRITABLE); - stopwatch.Start(); - EXPECT_EQ(MOJO_RESULT_OK, w.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout()); - EXPECT_EQ(1u, context); - hss = HandleSignalsState(); - d->RemoveAwakable(&w, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfiable_signals); - - // Wait for zero time for writable when already writable. - w.Init(); - d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE); - ASSERT_EQ(MOJO_RESULT_OK, - d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 2, nullptr)); - d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_WRITABLE); - stopwatch.Start(); - EXPECT_EQ(MOJO_RESULT_OK, w.Wait(0, &context)); - EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout()); - EXPECT_EQ(2u, context); - hss = HandleSignalsState(); - d->RemoveAwakable(&w, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfiable_signals); - - // Wait for non-zero, finite time for writable when already writable. - w.Init(); - d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE); - ASSERT_EQ(MOJO_RESULT_OK, - d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 3, nullptr)); - d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_WRITABLE); - stopwatch.Start(); - EXPECT_EQ(MOJO_RESULT_OK, - w.Wait(2 * test::EpsilonTimeout().InMicroseconds(), &context)); - EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout()); - EXPECT_EQ(3u, context); - hss = HandleSignalsState(); - d->RemoveAwakable(&w, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfiable_signals); - - // Wait for zero time for writable when not writable (will time out). - w.Init(); - d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE); - ASSERT_EQ(MOJO_RESULT_OK, - d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 4, nullptr)); - stopwatch.Start(); - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, w.Wait(0, nullptr)); - EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout()); - hss = HandleSignalsState(); - d->RemoveAwakable(&w, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfiable_signals); - - // Wait for non-zero, finite time for writable when not writable (will time - // out). - w.Init(); - d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE); - ASSERT_EQ(MOJO_RESULT_OK, - d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 5, nullptr)); - stopwatch.Start(); - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - w.Wait(2 * test::EpsilonTimeout().InMicroseconds(), nullptr)); - base::TimeDelta elapsed = stopwatch.Elapsed(); - EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout()); - EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout()); - hss = HandleSignalsState(); - d->RemoveAwakable(&w, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfiable_signals); - - EXPECT_EQ(MOJO_RESULT_OK, d->Close()); -} - -TEST(SimpleDispatcherTest, BasicUnsatisfiable) { - test::Stopwatch stopwatch; - - scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); - Waiter w; - uint32_t context = 0; - HandleSignalsState hss; - - // Try adding a writable waiter when it can never be writable. - w.Init(); - d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE); - d->SetSatisfiedSignals(0); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 1, &hss)); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals); - // Shouldn't need to remove the waiter (it was not added). - - // Wait (forever) for writable and then it becomes never writable. - w.Init(); - d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE | - MOJO_HANDLE_SIGNAL_WRITABLE); - ASSERT_EQ(MOJO_RESULT_OK, - d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 2, nullptr)); - d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE); - stopwatch.Start(); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - w.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout()); - EXPECT_EQ(2u, context); - hss = HandleSignalsState(); - d->RemoveAwakable(&w, &hss); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals); - - // Wait for zero time for writable and then it becomes never writable. - w.Init(); - d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE | - MOJO_HANDLE_SIGNAL_WRITABLE); - ASSERT_EQ(MOJO_RESULT_OK, - d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 3, nullptr)); - d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE); - stopwatch.Start(); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, w.Wait(0, &context)); - EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout()); - EXPECT_EQ(3u, context); - hss = HandleSignalsState(); - d->RemoveAwakable(&w, &hss); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals); - - // Wait for non-zero, finite time for writable and then it becomes never - // writable. - w.Init(); - d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE | - MOJO_HANDLE_SIGNAL_WRITABLE); - ASSERT_EQ(MOJO_RESULT_OK, - d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 4, nullptr)); - d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE); - stopwatch.Start(); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - w.Wait(2 * test::EpsilonTimeout().InMicroseconds(), &context)); - EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout()); - EXPECT_EQ(4u, context); - hss = HandleSignalsState(); - d->RemoveAwakable(&w, &hss); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals); - - EXPECT_EQ(MOJO_RESULT_OK, d->Close()); -} - -TEST(SimpleDispatcherTest, BasicClosed) { - test::Stopwatch stopwatch; - - scoped_refptr<MockSimpleDispatcher> d; - Waiter w; - uint32_t context = 0; - HandleSignalsState hss; - - // Try adding a writable waiter when the dispatcher has been closed. - d = new MockSimpleDispatcher(); - w.Init(); - EXPECT_EQ(MOJO_RESULT_OK, d->Close()); - hss = HandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 1, &hss)); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(0u, hss.satisfiable_signals); - // Shouldn't need to remove the waiter (it was not added). - - // Wait (forever) for writable and then the dispatcher is closed. - d = new MockSimpleDispatcher(); - w.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 2, nullptr)); - EXPECT_EQ(MOJO_RESULT_OK, d->Close()); - stopwatch.Start(); - EXPECT_EQ(MOJO_RESULT_CANCELLED, w.Wait(MOJO_DEADLINE_INDEFINITE, &context)); - EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout()); - EXPECT_EQ(2u, context); - // Don't need to remove waiters from closed dispatchers. - - // Wait for zero time for writable and then the dispatcher is closed. - d = new MockSimpleDispatcher(); - w.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 3, nullptr)); - EXPECT_EQ(MOJO_RESULT_OK, d->Close()); - stopwatch.Start(); - EXPECT_EQ(MOJO_RESULT_CANCELLED, w.Wait(0, &context)); - EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout()); - EXPECT_EQ(3u, context); - // Don't need to remove waiters from closed dispatchers. - - // Wait for non-zero, finite time for writable and then the dispatcher is - // closed. - d = new MockSimpleDispatcher(); - w.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 4, nullptr)); - EXPECT_EQ(MOJO_RESULT_OK, d->Close()); - stopwatch.Start(); - EXPECT_EQ(MOJO_RESULT_CANCELLED, - w.Wait(2 * test::EpsilonTimeout().InMicroseconds(), &context)); - EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout()); - EXPECT_EQ(4u, context); - // Don't need to remove waiters from closed dispatchers. -} - -#if defined(OS_WIN) -// http://crbug.com/396393 -#define MAYBE_BasicThreaded DISABLED_BasicThreaded -#else -#define MAYBE_BasicThreaded BasicThreaded -#endif -TEST(SimpleDispatcherTest, MAYBE_BasicThreaded) { - test::Stopwatch stopwatch; - bool did_wait; - MojoResult result; - uint32_t context; - HandleSignalsState hss; - - // Wait for readable (already readable). - { - scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); - { - d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE); - test::WaiterThread thread(d, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, 1, &did_wait, &result, - &context, &hss); - stopwatch.Start(); - thread.Start(); - } // Joins the thread. - // If we closed earlier, then probably we'd get a |MOJO_RESULT_CANCELLED|. - EXPECT_EQ(MOJO_RESULT_OK, d->Close()); - } - EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout()); - EXPECT_FALSE(did_wait); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, result); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfiable_signals); - - // Wait for readable and becomes readable after some time. - { - scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); - { - test::WaiterThread thread(d, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, 2, &did_wait, &result, - &context, &hss); - stopwatch.Start(); - thread.Start(); - base::PlatformThread::Sleep(2 * test::EpsilonTimeout()); - d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE); - } // Joins the thread. - EXPECT_EQ(MOJO_RESULT_OK, d->Close()); - } - base::TimeDelta elapsed = stopwatch.Elapsed(); - EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout()); - EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout()); - EXPECT_TRUE(did_wait); - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(2u, context); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfiable_signals); - - // Wait for readable and becomes never-readable after some time. - { - scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); - { - test::WaiterThread thread(d, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, 3, &did_wait, &result, - &context, &hss); - stopwatch.Start(); - thread.Start(); - base::PlatformThread::Sleep(2 * test::EpsilonTimeout()); - d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_NONE); - } // Joins the thread. - EXPECT_EQ(MOJO_RESULT_OK, d->Close()); - } - elapsed = stopwatch.Elapsed(); - EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout()); - EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout()); - EXPECT_TRUE(did_wait); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); - EXPECT_EQ(3u, context); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(0u, hss.satisfiable_signals); - - // Wait for readable and dispatcher gets closed. - { - scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); - test::WaiterThread thread(d, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, 4, &did_wait, &result, - &context, &hss); - stopwatch.Start(); - thread.Start(); - base::PlatformThread::Sleep(2 * test::EpsilonTimeout()); - EXPECT_EQ(MOJO_RESULT_OK, d->Close()); - } // Joins the thread. - elapsed = stopwatch.Elapsed(); - EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout()); - EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout()); - EXPECT_TRUE(did_wait); - EXPECT_EQ(MOJO_RESULT_CANCELLED, result); - EXPECT_EQ(4u, context); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(0u, hss.satisfiable_signals); - - // Wait for readable and times out. - { - scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); - { - test::WaiterThread thread(d, MOJO_HANDLE_SIGNAL_READABLE, - 2 * test::EpsilonTimeout().InMicroseconds(), 5, - &did_wait, &result, &context, &hss); - stopwatch.Start(); - thread.Start(); - base::PlatformThread::Sleep(1 * test::EpsilonTimeout()); - // Not what we're waiting for. - d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_WRITABLE); - } // Joins the thread (after its wait times out). - // If we closed earlier, then probably we'd get a |MOJO_RESULT_CANCELLED|. - EXPECT_EQ(MOJO_RESULT_OK, d->Close()); - } - elapsed = stopwatch.Elapsed(); - EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout()); - EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout()); - EXPECT_TRUE(did_wait); - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, result); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfiable_signals); -} - -#if defined(OS_WIN) -// http://crbug.com/387124 -#define MAYBE_MultipleWaiters DISABLED_MultipleWaiters -#else -#define MAYBE_MultipleWaiters MultipleWaiters -#endif -TEST(SimpleDispatcherTest, MAYBE_MultipleWaiters) { - static const uint32_t kNumWaiters = 20; - - bool did_wait[kNumWaiters]; - MojoResult result[kNumWaiters]; - uint32_t context[kNumWaiters]; - HandleSignalsState hss[kNumWaiters]; - - // All wait for readable and becomes readable after some time. - { - scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); - ScopedVector<test::WaiterThread> threads; - for (uint32_t i = 0; i < kNumWaiters; i++) { - threads.push_back(new test::WaiterThread( - d, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, i, - &did_wait[i], &result[i], &context[i], &hss[i])); - threads.back()->Start(); - } - base::PlatformThread::Sleep(2 * test::EpsilonTimeout()); - d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE); - EXPECT_EQ(MOJO_RESULT_OK, d->Close()); - } // Joins the threads. - for (uint32_t i = 0; i < kNumWaiters; i++) { - EXPECT_TRUE(did_wait[i]) << i; - EXPECT_EQ(MOJO_RESULT_OK, result[i]) << i; - EXPECT_EQ(i, context[i]) << i; - // Since we closed before joining, we can't say much about what each thread - // saw as the state. - } - - // Some wait for readable, some for writable, and becomes readable after some - // time. - { - scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); - ScopedVector<test::WaiterThread> threads; - for (uint32_t i = 0; i < kNumWaiters / 2; i++) { - threads.push_back(new test::WaiterThread( - d, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, i, - &did_wait[i], &result[i], &context[i], &hss[i])); - threads.back()->Start(); - } - for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) { - threads.push_back(new test::WaiterThread( - d, MOJO_HANDLE_SIGNAL_WRITABLE, MOJO_DEADLINE_INDEFINITE, i, - &did_wait[i], &result[i], &context[i], &hss[i])); - threads.back()->Start(); - } - base::PlatformThread::Sleep(2 * test::EpsilonTimeout()); - d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE); - // This will wake up the ones waiting to write. - EXPECT_EQ(MOJO_RESULT_OK, d->Close()); - } // Joins the threads. - for (uint32_t i = 0; i < kNumWaiters / 2; i++) { - EXPECT_TRUE(did_wait[i]) << i; - EXPECT_EQ(MOJO_RESULT_OK, result[i]) << i; - EXPECT_EQ(i, context[i]) << i; - // Since we closed before joining, we can't say much about what each thread - // saw as the state. - } - for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) { - EXPECT_TRUE(did_wait[i]) << i; - EXPECT_EQ(MOJO_RESULT_CANCELLED, result[i]) << i; - EXPECT_EQ(i, context[i]) << i; - // Since we closed before joining, we can't say much about what each thread - // saw as the state. - } - - // Some wait for readable, some for writable, and becomes readable and - // never-writable after some time. - { - scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); - ScopedVector<test::WaiterThread> threads; - for (uint32_t i = 0; i < kNumWaiters / 2; i++) { - threads.push_back(new test::WaiterThread( - d, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, i, - &did_wait[i], &result[i], &context[i], &hss[i])); - threads.back()->Start(); - } - for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) { - threads.push_back(new test::WaiterThread( - d, MOJO_HANDLE_SIGNAL_WRITABLE, MOJO_DEADLINE_INDEFINITE, i, - &did_wait[i], &result[i], &context[i], &hss[i])); - threads.back()->Start(); - } - base::PlatformThread::Sleep(1 * test::EpsilonTimeout()); - d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE); - base::PlatformThread::Sleep(1 * test::EpsilonTimeout()); - d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE); - EXPECT_EQ(MOJO_RESULT_OK, d->Close()); - } // Joins the threads. - for (uint32_t i = 0; i < kNumWaiters / 2; i++) { - EXPECT_TRUE(did_wait[i]) << i; - EXPECT_EQ(MOJO_RESULT_OK, result[i]) << i; - EXPECT_EQ(i, context[i]) << i; - // Since we closed before joining, we can't say much about what each thread - // saw as the state. - } - for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) { - EXPECT_TRUE(did_wait[i]) << i; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result[i]) << i; - EXPECT_EQ(i, context[i]) << i; - // Since we closed before joining, we can't say much about what each thread - // saw as the state. - } - - // Some wait for readable, some for writable, and becomes readable after some - // time. - { - scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); - ScopedVector<test::WaiterThread> threads; - for (uint32_t i = 0; i < kNumWaiters / 2; i++) { - threads.push_back(new test::WaiterThread( - d, MOJO_HANDLE_SIGNAL_READABLE, - 3 * test::EpsilonTimeout().InMicroseconds(), i, &did_wait[i], - &result[i], &context[i], &hss[i])); - threads.back()->Start(); - } - for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) { - threads.push_back(new test::WaiterThread( - d, MOJO_HANDLE_SIGNAL_WRITABLE, - 1 * test::EpsilonTimeout().InMicroseconds(), i, &did_wait[i], - &result[i], &context[i], &hss[i])); - threads.back()->Start(); - } - base::PlatformThread::Sleep(2 * test::EpsilonTimeout()); - d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE); - // All those waiting for writable should have timed out. - EXPECT_EQ(MOJO_RESULT_OK, d->Close()); - } // Joins the threads. - for (uint32_t i = 0; i < kNumWaiters / 2; i++) { - EXPECT_TRUE(did_wait[i]) << i; - EXPECT_EQ(MOJO_RESULT_OK, result[i]) << i; - EXPECT_EQ(i, context[i]) << i; - // Since we closed before joining, we can't say much about what each thread - // saw as the state. - } - for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) { - EXPECT_TRUE(did_wait[i]) << i; - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, result[i]) << i; - // Since we closed before joining, we can't say much about what each thread - // saw as the state. - } -} - -// TODO(vtl): Stress test? - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/system_impl_export.h b/mojo/edk/system/system_impl_export.h deleted file mode 100644 index 5bbf005..0000000 --- a/mojo/edk/system/system_impl_export.h +++ /dev/null @@ -1,29 +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_EDK_SYSTEM_SYSTEM_IMPL_EXPORT_H_ -#define MOJO_EDK_SYSTEM_SYSTEM_IMPL_EXPORT_H_ - -#if defined(COMPONENT_BUILD) -#if defined(WIN32) - -#if defined(MOJO_SYSTEM_IMPL_IMPLEMENTATION) -#define MOJO_SYSTEM_IMPL_EXPORT __declspec(dllexport) -#else -#define MOJO_SYSTEM_IMPL_EXPORT __declspec(dllimport) -#endif // defined(MOJO_SYSTEM_IMPL_IMPLEMENTATION) - -#else // defined(WIN32) -#if defined(MOJO_SYSTEM_IMPL_IMPLEMENTATION) -#define MOJO_SYSTEM_IMPL_EXPORT __attribute__((visibility("default"))) -#else -#define MOJO_SYSTEM_IMPL_EXPORT -#endif -#endif - -#else // defined(COMPONENT_BUILD) -#define MOJO_SYSTEM_IMPL_EXPORT -#endif - -#endif // MOJO_EDK_SYSTEM_SYSTEM_IMPL_EXPORT_H_ diff --git a/mojo/edk/system/test_utils.cc b/mojo/edk/system/test_utils.cc deleted file mode 100644 index d43a818..0000000 --- a/mojo/edk/system/test_utils.cc +++ /dev/null @@ -1,32 +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/edk/system/test_utils.h" - -#include "base/test/test_timeouts.h" -#include "build/build_config.h" - -namespace mojo { -namespace system { -namespace test { - -base::TimeDelta EpsilonTimeout() { -// Originally, our epsilon timeout was 10 ms, which was mostly fine but flaky on -// some Windows bots. I don't recall ever seeing flakes on other bots. At 30 ms -// tests seem reliable on Windows bots, but not at 25 ms. We'd like this timeout -// to be as small as possible (see the description in the .h file). -// -// Currently, |tiny_timeout()| is usually 100 ms (possibly scaled under ASAN, -// etc.). Based on this, set it to (usually be) 30 ms on Windows and 20 ms -// elsewhere. -#if defined(OS_WIN) || defined(OS_ANDROID) - return (TestTimeouts::tiny_timeout() * 3) / 10; -#else - return (TestTimeouts::tiny_timeout() * 2) / 10; -#endif -} - -} // namespace test -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/test_utils.h b/mojo/edk/system/test_utils.h deleted file mode 100644 index b16c516..0000000 --- a/mojo/edk/system/test_utils.h +++ /dev/null @@ -1,43 +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_EDK_SYSTEM_TEST_UTILS_H_ -#define MOJO_EDK_SYSTEM_TEST_UTILS_H_ - -#include "base/macros.h" -#include "base/time/time.h" - -namespace mojo { -namespace system { -namespace test { - -// A timeout smaller than |TestTimeouts::tiny_timeout()|. Warning: This may lead -// to flakiness, but this is unavoidable if, e.g., you're trying to ensure that -// functions with timeouts are reasonably accurate. We want this to be as small -// as possible without causing too much flakiness. -base::TimeDelta EpsilonTimeout(); - -// Stopwatch ------------------------------------------------------------------- - -// A simple "stopwatch" for measuring time elapsed from a given starting point. -class Stopwatch { - public: - Stopwatch() {} - ~Stopwatch() {} - - void Start() { start_time_ = base::TimeTicks::Now(); } - - base::TimeDelta Elapsed() { return base::TimeTicks::Now() - start_time_; } - - private: - base::TimeTicks start_time_; - - DISALLOW_COPY_AND_ASSIGN(Stopwatch); -}; - -} // namespace test -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_TEST_UTILS_H_ diff --git a/mojo/edk/system/transport_data.cc b/mojo/edk/system/transport_data.cc deleted file mode 100644 index d388151..0000000 --- a/mojo/edk/system/transport_data.cc +++ /dev/null @@ -1,337 +0,0 @@ -// 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/edk/system/transport_data.h" - -#include <string.h> - -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/configuration.h" -#include "mojo/edk/system/message_in_transit.h" - -namespace mojo { -namespace system { - -// The maximum amount of space needed per platform handle. -// (|{Channel,RawChannel}::GetSerializedPlatformHandleSize()| should always -// return a value which is at most this. This is only used to calculate -// |TransportData::kMaxBufferSize|. This value should be a multiple of the -// alignment in order to simplify calculations, even though the actual amount of -// space needed need not be a multiple of the alignment. -const size_t kMaxSizePerPlatformHandle = 8; -static_assert(kMaxSizePerPlatformHandle % MessageInTransit::kMessageAlignment == - 0, - "kMaxSizePerPlatformHandle not a multiple of alignment"); - -STATIC_CONST_MEMBER_DEFINITION const size_t - TransportData::kMaxSerializedDispatcherSize; -STATIC_CONST_MEMBER_DEFINITION const size_t - TransportData::kMaxSerializedDispatcherPlatformHandles; - -// static -size_t TransportData::GetMaxBufferSize() { - // In additional to the header, for each attached (Mojo) handle there'll be a - // handle table entry and serialized dispatcher data. - return sizeof(Header) + - GetConfiguration().max_message_num_handles * - (sizeof(HandleTableEntry) + kMaxSerializedDispatcherSize) + - GetMaxPlatformHandles() * kMaxSizePerPlatformHandle; -} - -// static -size_t TransportData::GetMaxPlatformHandles() { - return GetConfiguration().max_message_num_handles * - kMaxSerializedDispatcherPlatformHandles; -} - -struct TransportData::PrivateStructForCompileAsserts { - static_assert(sizeof(Header) % MessageInTransit::kMessageAlignment == 0, - "sizeof(MessageInTransit::Header) not a multiple of alignment"); - static_assert(kMaxSerializedDispatcherSize % - MessageInTransit::kMessageAlignment == - 0, - "kMaxSerializedDispatcherSize not a multiple of alignment"); - static_assert(sizeof(HandleTableEntry) % - MessageInTransit::kMessageAlignment == - 0, - "sizeof(MessageInTransit::HandleTableEntry) not a multiple of " - "alignment"); -}; - -TransportData::TransportData(scoped_ptr<DispatcherVector> dispatchers, - Channel* channel) { - DCHECK(dispatchers); - DCHECK(channel); - - const size_t num_handles = dispatchers->size(); - DCHECK_GT(num_handles, 0u); - - // The offset to the start of the (Mojo) handle table. - const size_t handle_table_start_offset = sizeof(Header); - // The offset to the start of the serialized dispatcher data. - const size_t serialized_dispatcher_start_offset = - handle_table_start_offset + num_handles * sizeof(HandleTableEntry); - // The estimated size of the secondary buffer. We compute this estimate below. - // It must be at least as big as the (eventual) actual size. - size_t estimated_size = serialized_dispatcher_start_offset; - size_t estimated_num_platform_handles = 0; -#if DCHECK_IS_ON() - std::vector<size_t> all_max_sizes(num_handles); - std::vector<size_t> all_max_platform_handles(num_handles); -#endif - for (size_t i = 0; i < num_handles; i++) { - if (Dispatcher* dispatcher = (*dispatchers)[i].get()) { - size_t max_size = 0; - size_t max_platform_handles = 0; - Dispatcher::TransportDataAccess::StartSerialize( - dispatcher, channel, &max_size, &max_platform_handles); - - DCHECK_LE(max_size, kMaxSerializedDispatcherSize); - estimated_size += MessageInTransit::RoundUpMessageAlignment(max_size); - DCHECK_LE(estimated_size, GetMaxBufferSize()); - - DCHECK_LE(max_platform_handles, kMaxSerializedDispatcherPlatformHandles); - estimated_num_platform_handles += max_platform_handles; - DCHECK_LE(estimated_num_platform_handles, GetMaxPlatformHandles()); - -#if DCHECK_IS_ON() - all_max_sizes[i] = max_size; - all_max_platform_handles[i] = max_platform_handles; -#endif - } - } - - size_t size_per_platform_handle = 0; - if (estimated_num_platform_handles > 0) { - size_per_platform_handle = channel->GetSerializedPlatformHandleSize(); - DCHECK_LE(size_per_platform_handle, kMaxSizePerPlatformHandle); - estimated_size += estimated_num_platform_handles * size_per_platform_handle; - estimated_size = MessageInTransit::RoundUpMessageAlignment(estimated_size); - DCHECK_LE(estimated_size, GetMaxBufferSize()); - } - - buffer_.reset(static_cast<char*>( - base::AlignedAlloc(estimated_size, MessageInTransit::kMessageAlignment))); - // Entirely clear out the secondary buffer, since then we won't have to worry - // about clearing padding or unused space (e.g., if a dispatcher fails to - // serialize). - memset(buffer_.get(), 0, estimated_size); - - if (estimated_num_platform_handles > 0) { - DCHECK(!platform_handles_); - platform_handles_.reset(new embedder::PlatformHandleVector()); - } - - Header* header = reinterpret_cast<Header*>(buffer_.get()); - header->num_handles = static_cast<uint32_t>(num_handles); - // (Okay to leave |platform_handle_table_offset|, |num_platform_handles|, and - // |unused| be zero; we'll set the former two later if necessary.) - - HandleTableEntry* handle_table = reinterpret_cast<HandleTableEntry*>( - buffer_.get() + handle_table_start_offset); - size_t current_offset = serialized_dispatcher_start_offset; - for (size_t i = 0; i < num_handles; i++) { - Dispatcher* dispatcher = (*dispatchers)[i].get(); - if (!dispatcher) { - static_assert(Dispatcher::kTypeUnknown == 0, - "Value of Dispatcher::kTypeUnknown must be 0"); - continue; - } - -#if DCHECK_IS_ON() - size_t old_platform_handles_size = - platform_handles_ ? platform_handles_->size() : 0; -#endif - - void* destination = buffer_.get() + current_offset; - size_t actual_size = 0; - if (Dispatcher::TransportDataAccess::EndSerializeAndClose( - dispatcher, channel, destination, &actual_size, - platform_handles_.get())) { - handle_table[i].type = static_cast<int32_t>(dispatcher->GetType()); - handle_table[i].offset = static_cast<uint32_t>(current_offset); - handle_table[i].size = static_cast<uint32_t>(actual_size); -// (Okay to not set |unused| since we cleared the entire buffer.) - -#if DCHECK_IS_ON() - DCHECK_LE(actual_size, all_max_sizes[i]); - DCHECK_LE(platform_handles_ - ? (platform_handles_->size() - old_platform_handles_size) - : 0, - all_max_platform_handles[i]); -#endif - } else { - // Nothing to do on failure, since |buffer_| was cleared, and - // |kTypeUnknown| is zero. The handle was simply closed. - LOG(ERROR) << "Failed to serialize handle to remote message pipe"; - } - - current_offset += MessageInTransit::RoundUpMessageAlignment(actual_size); - DCHECK_LE(current_offset, estimated_size); - DCHECK_LE(platform_handles_ ? platform_handles_->size() : 0, - estimated_num_platform_handles); - } - - if (platform_handles_ && platform_handles_->size() > 0) { - header->platform_handle_table_offset = - static_cast<uint32_t>(current_offset); - header->num_platform_handles = - static_cast<uint32_t>(platform_handles_->size()); - current_offset += platform_handles_->size() * size_per_platform_handle; - current_offset = MessageInTransit::RoundUpMessageAlignment(current_offset); - } - - // There's no aligned realloc, so it's no good way to release unused space (if - // we overshot our estimated space requirements). - buffer_size_ = current_offset; - - // |dispatchers_| will be destroyed as it goes out of scope. -} - -TransportData::TransportData( - embedder::ScopedPlatformHandleVectorPtr platform_handles) - : buffer_size_(sizeof(Header)), platform_handles_(platform_handles.Pass()) { - buffer_.reset(static_cast<char*>( - base::AlignedAlloc(buffer_size_, MessageInTransit::kMessageAlignment))); - memset(buffer_.get(), 0, buffer_size_); - - Header* header = reinterpret_cast<Header*>(buffer_.get()); - header->num_platform_handles = - static_cast<uint32_t>(platform_handles_->size()); -} - -TransportData::~TransportData() { -} - -// static -const char* TransportData::ValidateBuffer( - size_t serialized_platform_handle_size, - const void* buffer, - size_t buffer_size) { - DCHECK(buffer); - DCHECK_GT(buffer_size, 0u); - - // Always make sure that the buffer size is sane; if it's not, someone's - // messing with us. - if (buffer_size < sizeof(Header) || buffer_size > GetMaxBufferSize() || - buffer_size % MessageInTransit::kMessageAlignment != 0) - return "Invalid message secondary buffer size"; - - const Header* header = static_cast<const Header*>(buffer); - const size_t num_handles = header->num_handles; - - // Sanity-check |num_handles| (before multiplying it against anything). - if (num_handles > GetConfiguration().max_message_num_handles) - return "Message handle payload too large"; - - if (buffer_size < sizeof(Header) + num_handles * sizeof(HandleTableEntry)) - return "Message secondary buffer too small"; - - if (header->num_platform_handles == 0) { - // Then |platform_handle_table_offset| should also be zero. - if (header->platform_handle_table_offset != 0) { - return "Message has no handles attached, but platform handle table " - "present"; - } - } else { - if (header->num_platform_handles > - GetConfiguration().max_message_num_handles * - kMaxSerializedDispatcherPlatformHandles) - return "Message has too many platform handles attached"; - - static const char kInvalidPlatformHandleTableOffset[] = - "Message has invalid platform handle table offset"; - // This doesn't check that the platform handle table doesn't alias other - // stuff, but it doesn't matter, since it's all read-only. - if (header->platform_handle_table_offset % - MessageInTransit::kMessageAlignment != - 0) - return kInvalidPlatformHandleTableOffset; - - // ">" instead of ">=" since the size per handle may be zero. - if (header->platform_handle_table_offset > buffer_size) - return kInvalidPlatformHandleTableOffset; - - // We already checked |platform_handle_table_offset| and - // |num_platform_handles|, so the addition and multiplication are okay. - if (header->platform_handle_table_offset + - header->num_platform_handles * serialized_platform_handle_size > - buffer_size) - return kInvalidPlatformHandleTableOffset; - } - - const HandleTableEntry* handle_table = - reinterpret_cast<const HandleTableEntry*>( - static_cast<const char*>(buffer) + sizeof(Header)); - static const char kInvalidSerializedDispatcher[] = - "Message contains invalid serialized dispatcher"; - for (size_t i = 0; i < num_handles; i++) { - size_t offset = handle_table[i].offset; - if (offset % MessageInTransit::kMessageAlignment != 0) - return kInvalidSerializedDispatcher; - - size_t size = handle_table[i].size; - if (size > kMaxSerializedDispatcherSize || size > buffer_size) - return kInvalidSerializedDispatcher; - - // Note: This is an overflow-safe check for |offset + size > buffer_size| - // (we know that |size <= buffer_size| from the previous check). - if (offset > buffer_size - size) - return kInvalidSerializedDispatcher; - } - - return nullptr; -} - -// static -void TransportData::GetPlatformHandleTable(const void* transport_data_buffer, - size_t* num_platform_handles, - const void** platform_handle_table) { - DCHECK(transport_data_buffer); - DCHECK(num_platform_handles); - DCHECK(platform_handle_table); - - const Header* header = static_cast<const Header*>(transport_data_buffer); - *num_platform_handles = header->num_platform_handles; - *platform_handle_table = static_cast<const char*>(transport_data_buffer) + - header->platform_handle_table_offset; -} - -// static -scoped_ptr<DispatcherVector> TransportData::DeserializeDispatchers( - const void* buffer, - size_t buffer_size, - embedder::ScopedPlatformHandleVectorPtr platform_handles, - Channel* channel) { - DCHECK(buffer); - DCHECK_GT(buffer_size, 0u); - DCHECK(channel); - - const Header* header = static_cast<const Header*>(buffer); - const size_t num_handles = header->num_handles; - scoped_ptr<DispatcherVector> dispatchers(new DispatcherVector(num_handles)); - - const HandleTableEntry* handle_table = - reinterpret_cast<const HandleTableEntry*>( - static_cast<const char*>(buffer) + sizeof(Header)); - for (size_t i = 0; i < num_handles; i++) { - size_t offset = handle_table[i].offset; - size_t size = handle_table[i].size; - // Should already have been checked by |ValidateBuffer()|: - DCHECK_EQ(offset % MessageInTransit::kMessageAlignment, 0u); - DCHECK_LE(offset, buffer_size); - DCHECK_LE(offset + size, buffer_size); - - const void* source = static_cast<const char*>(buffer) + offset; - (*dispatchers)[i] = Dispatcher::TransportDataAccess::Deserialize( - channel, handle_table[i].type, source, size, platform_handles.get()); - } - - return dispatchers.Pass(); -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/transport_data.h b/mojo/edk/system/transport_data.h deleted file mode 100644 index 1258f49..0000000 --- a/mojo/edk/system/transport_data.h +++ /dev/null @@ -1,189 +0,0 @@ -// 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_EDK_SYSTEM_TRANSPORT_DATA_H_ -#define MOJO_EDK_SYSTEM_TRANSPORT_DATA_H_ - -#include <stdint.h> - -#include <vector> - -#include "base/macros.h" -#include "base/memory/aligned_memory.h" -#include "base/memory/scoped_ptr.h" -#include "build/build_config.h" -#include "mojo/edk/embedder/platform_handle.h" -#include "mojo/edk/embedder/platform_handle_vector.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace system { - -class Channel; - -// This class is used by |MessageInTransit| to represent handles (|Dispatcher|s) -// in various stages of serialization. -// -// The stages are: -// - Before reaching |TransportData|: Turn |DispatcherTransport|s into -// |Dispatcher|s that are "owned" by (and attached to) a |MessageInTransit|. -// This invalidates the handles in the space of the sending application -// (and, e.g., if another thread is waiting on such a handle, it'll be -// notified of this invalidation). -// - Serialize these dispatchers into the |TransportData|: First, for each -// attached dispatcher, there's an entry in the |TransportData|'s "handle -// table", which points to a segment of (dispatcher-type-dependent) data. -// - During the serialization of the dispatchers, |PlatformHandle|s may be -// detached from the dispatchers and attached to the |TransportData|. -// - Before sending the |MessageInTransit|, including its main buffer and the -// |TransportData|'s buffer, the |Channel| sends any |PlatformHandle|s (in a -// platform-, and possibly sandbox-situation-, specific way) first. In doing -// so, it appends a "platform handle table" to the |TransportData| -// containing information about how to deserialize these |PlatformHandle|s. -// - Finally, at this point, to send the |MessageInTransit|, there only -// remains "inert" data: the |MessageInTransit|'s main buffer and data from -// the |TransportData|, consisting of the "handle table" (one entry for each -// attached dispatcher), dispatcher-type-specific data (one segment for each -// entry in the "handle table"), and the "platform handle table" (one entry -// for each attached |PlatformHandle|). -// -// To receive a message (|MessageInTransit|), the "reverse" happens: -// - On POSIX, receive and buffer |PlatformHandle|s (i.e., FDs), which were -// sent before the "inert" data. -// - Receive the "inert" data from the |MessageInTransit|. Examine its -// "platform handle table". On POSIX, match its entries with the buffered -// |PlatformHandle|s, which were previously received. On Windows, do what's -// necessary to obtain |PlatformHandle|s (e.g.: i. if the sender is fully -// trusted and able to duplicate handle into the receiver, then just pick -// out the |HANDLE| value; ii. if the receiver is fully trusted and able to -// duplicate handles from the receiver, do the |DuplicateHandle()|; iii. -// otherwise, talk to a broker to get handles). Reattach all the -// |PlatformHandle|s to the |MessageInTransit|. -// - For each entry in the "handle table", use serialized dispatcher data to -// reconstitute a dispatcher, taking ownership of associated -// |PlatformHandle|s (and detaching them). Attach these dispatchers to the -// |MessageInTransit|. -// - At this point, the |MessageInTransit| consists of its main buffer -// (primarily the data payload) and the attached dispatchers; the -// |TransportData| can be discarded. -// - When |MojoReadMessage()| is to give data to the application, attach the -// dispatchers to the (global, "core") handle table, getting handles; give -// the application the data payload and these handles. -// -// TODO(vtl): Everything above involving |PlatformHandle|s. -class MOJO_SYSTEM_IMPL_EXPORT TransportData { - public: - // The maximum size of a single serialized dispatcher. This must be a multiple - // of |kMessageAlignment|. - static const size_t kMaxSerializedDispatcherSize = 10000; - - // The maximum number of platform handles to attach for a single serialized - // dispatcher. - static const size_t kMaxSerializedDispatcherPlatformHandles = 2; - - // The maximum possible size of a valid transport data buffer. - static size_t GetMaxBufferSize(); - - // The maximum total number of platform handles that may be attached. - static size_t GetMaxPlatformHandles(); - - TransportData(scoped_ptr<DispatcherVector> dispatchers, Channel* channel); - - // This is used for users of |MessageInTransit|/|TransportData|/|RawChannel| - // that want to simply transport data and platform handles, and not - // |Dispatcher|s. (|Header| will be present, and zero except for - // |num_platform_handles|, and |platform_handle_table_offset| if necessary.) - explicit TransportData( - embedder::ScopedPlatformHandleVectorPtr platform_handles); - - ~TransportData(); - - const void* buffer() const { return buffer_.get(); } - void* buffer() { return buffer_.get(); } - size_t buffer_size() const { return buffer_size_; } - - uint32_t platform_handle_table_offset() const { - return header()->platform_handle_table_offset; - } - - // Gets attached platform-specific handles; this may return null if there are - // none. Note that the caller may mutate the set of platform-specific handles. - const embedder::PlatformHandleVector* platform_handles() const { - return platform_handles_.get(); - } - embedder::PlatformHandleVector* platform_handles() { - return platform_handles_.get(); - } - - // Receive-side functions: - - // Checks if the given buffer (from the "wire") looks like a valid - // |TransportData| buffer. (Should only be called if |buffer_size| is - // nonzero.) Returns null if valid, and a pointer to a human-readable error - // message (for debug/logging purposes) on error. Note: This checks the - // validity of the handle table entries (i.e., does range checking), but does - // not check that the validity of the actual serialized dispatcher - // information. - static const char* ValidateBuffer(size_t serialized_platform_handle_size, - const void* buffer, - size_t buffer_size); - - // Gets the platform handle table from a (valid) |TransportData| buffer (which - // should have been validated using |ValidateBuffer()| first). - static void GetPlatformHandleTable(const void* transport_data_buffer, - size_t* num_platform_handles, - const void** platform_handle_table); - - // Deserializes dispatchers from the given (serialized) transport data buffer - // (typically from a |MessageInTransit::View|) and vector of platform handles. - // |buffer| should be non-null and |buffer_size| should be nonzero. - static scoped_ptr<DispatcherVector> DeserializeDispatchers( - const void* buffer, - size_t buffer_size, - embedder::ScopedPlatformHandleVectorPtr platform_handles, - Channel* channel); - - private: - // To allow us to make compile-assertions about |Header|, etc. in the .cc - // file. - struct PrivateStructForCompileAsserts; - - // Header for the "secondary buffer"/"transport data". Must be a multiple of - // |MessageInTransit::kMessageAlignment| in size. Must be POD. - struct Header { - uint32_t num_handles; - // TODO(vtl): Not used yet: - uint32_t platform_handle_table_offset; - uint32_t num_platform_handles; - uint32_t unused; - }; - - struct HandleTableEntry { - int32_t type; // From |Dispatcher::Type| (|kTypeUnknown| for "invalid"). - uint32_t offset; // Relative to the start of the "secondary buffer". - uint32_t size; // (Not including any padding.) - uint32_t unused; - }; - - const Header* header() const { - return reinterpret_cast<const Header*>(buffer_.get()); - } - - size_t buffer_size_; - scoped_ptr<char, base::AlignedFreeDeleter> buffer_; // Never null. - - // Any platform-specific handles attached to this message (for inter-process - // transport). The vector (if any) owns the handles that it contains (and is - // responsible for closing them). - // TODO(vtl): With C++11, change it to a vector of |ScopedPlatformHandle|s. - embedder::ScopedPlatformHandleVectorPtr platform_handles_; - - DISALLOW_COPY_AND_ASSIGN(TransportData); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_TRANSPORT_DATA_H_ diff --git a/mojo/edk/system/unique_identifier.cc b/mojo/edk/system/unique_identifier.cc deleted file mode 100644 index e851e2b..0000000 --- a/mojo/edk/system/unique_identifier.cc +++ /dev/null @@ -1,29 +0,0 @@ -// 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/edk/system/unique_identifier.h" - -#include <ostream> - -#include "base/strings/string_number_conversions.h" -#include "crypto/random.h" - -namespace mojo { -namespace system { - -std::ostream& operator<<(std::ostream& out, - const UniqueIdentifier& unique_identifier) { - return out << base::HexEncode(unique_identifier.data_, - sizeof(unique_identifier.data_)); -} - -// static -UniqueIdentifier UniqueIdentifier::Generate() { - UniqueIdentifier rv; - crypto::RandBytes(rv.data_, sizeof(rv.data_)); - return rv; -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/unique_identifier.h b/mojo/edk/system/unique_identifier.h deleted file mode 100644 index a7f68ba..0000000 --- a/mojo/edk/system/unique_identifier.h +++ /dev/null @@ -1,92 +0,0 @@ -// 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_EDK_SYSTEM_UNIQUE_IDENTIFIER_H_ -#define MOJO_EDK_SYSTEM_UNIQUE_IDENTIFIER_H_ - -#include <stdint.h> -#include <string.h> - -#include <iosfwd> - -#include "base/compiler_specific.h" -#include "base/containers/hash_tables.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace system { - -class UniqueIdentifier; - -} // namespace system -} // namespace mojo - -namespace BASE_HASH_NAMESPACE { - -// Declare this before |UniqueIdentifier|, so that it can be friended. -template <> -struct hash<mojo::system::UniqueIdentifier>; - -} // BASE_HASH_NAMESPACE - -namespace mojo { -namespace system { - -// Declare this before |UniqueIdentifier|, so that it can be friended. -MOJO_SYSTEM_IMPL_EXPORT std::ostream& operator<<( - std::ostream& out, - const UniqueIdentifier& unique_identifier); - -// |UniqueIdentifier| is a POD class whose value is used to uniquely identify -// things. -class MOJO_SYSTEM_IMPL_EXPORT UniqueIdentifier { - public: - // This generates a new identifier. Uniqueness is "guaranteed" (i.e., - // probabilistically) for identifiers. - static UniqueIdentifier Generate(); - - bool operator==(const UniqueIdentifier& other) const { - return memcmp(data_, other.data_, sizeof(data_)) == 0; - } - bool operator!=(const UniqueIdentifier& other) const { - return !operator==(other); - } - bool operator<(const UniqueIdentifier& other) const { - return memcmp(data_, other.data_, sizeof(data_)) < 0; - } - - private: - friend BASE_HASH_NAMESPACE::hash<mojo::system::UniqueIdentifier>; - friend std::ostream& operator<<(std::ostream&, const UniqueIdentifier&); - - explicit UniqueIdentifier() {} - - char data_[16]; - - // Copying and assignment allowed. -}; -static_assert(sizeof(UniqueIdentifier) == 16, - "UniqueIdentifier has wrong size."); -// We want to be able to take any buffer and cast it to a |UniqueIdentifier|. -static_assert(ALIGNOF(UniqueIdentifier) == 1, - "UniqueIdentifier requires nontrivial alignment."); - -} // namespace system -} // namespace mojo - -namespace BASE_HASH_NAMESPACE { - -template <> -struct hash<mojo::system::UniqueIdentifier> { - size_t operator()(mojo::system::UniqueIdentifier unique_identifier) const { - return base::HashInts64( - *reinterpret_cast<uint64_t*>(unique_identifier.data_), - *reinterpret_cast<uint64_t*>(unique_identifier.data_ + - sizeof(uint64_t))); - } -}; - -} // BASE_HASH_NAMESPACE - -#endif // MOJO_EDK_SYSTEM_UNIQUE_IDENTIFIER_H_ diff --git a/mojo/edk/system/unique_identifier_unittest.cc b/mojo/edk/system/unique_identifier_unittest.cc deleted file mode 100644 index 2be4f6e..0000000 --- a/mojo/edk/system/unique_identifier_unittest.cc +++ /dev/null @@ -1,106 +0,0 @@ -// 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/edk/system/unique_identifier.h" - -#include <set> -#include <sstream> - -#include "base/containers/hash_tables.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -TEST(UniqueIdentifierTest, Basic) { - // (This also checks copy constructibility.) - UniqueIdentifier id1 = UniqueIdentifier::Generate(); - - EXPECT_EQ(id1, id1); - EXPECT_FALSE(id1 != id1); - EXPECT_FALSE(id1 < id1); - - UniqueIdentifier id2 = UniqueIdentifier::Generate(); - - EXPECT_FALSE(id2 == id1); - EXPECT_NE(id2, id1); - EXPECT_TRUE((id1 < id2) ^ (id2 < id1)); - - // Test copyability. - id2 = id1; -} - -TEST(UniqueIdentifierTest, Logging) { - std::ostringstream oss1; - UniqueIdentifier id1 = UniqueIdentifier::Generate(); - oss1 << id1; - EXPECT_FALSE(oss1.str().empty()); - - std::ostringstream oss2; - UniqueIdentifier id2 = UniqueIdentifier::Generate(); - oss2 << id2; - EXPECT_FALSE(oss2.str().empty()); - - EXPECT_NE(id1, id2); - EXPECT_NE(oss1.str(), oss2.str()); -} - -TEST(UniqueIdentifierTest, StdSet) { - std::set<UniqueIdentifier> s; - EXPECT_TRUE(s.empty()); - - UniqueIdentifier id1 = UniqueIdentifier::Generate(); - EXPECT_TRUE(s.find(id1) == s.end()); - s.insert(id1); - EXPECT_TRUE(s.find(id1) != s.end()); - EXPECT_FALSE(s.empty()); - - UniqueIdentifier id2 = UniqueIdentifier::Generate(); - EXPECT_TRUE(s.find(id2) == s.end()); - s.insert(id2); - EXPECT_TRUE(s.find(id2) != s.end()); - // Make sure |id1| is still in |s|. - EXPECT_TRUE(s.find(id1) != s.end()); - - s.erase(id1); - EXPECT_TRUE(s.find(id1) == s.end()); - // Make sure |id2| is still in |s|. - EXPECT_TRUE(s.find(id2) != s.end()); - - s.erase(id2); - EXPECT_TRUE(s.find(id2) == s.end()); - EXPECT_TRUE(s.empty()); -} - -TEST(UniqueIdentifierTest, HashSet) { - base::hash_set<UniqueIdentifier> s; - EXPECT_TRUE(s.empty()); - - UniqueIdentifier id1 = UniqueIdentifier::Generate(); - EXPECT_TRUE(s.find(id1) == s.end()); - s.insert(id1); - EXPECT_TRUE(s.find(id1) != s.end()); - EXPECT_FALSE(s.empty()); - - UniqueIdentifier id2 = UniqueIdentifier::Generate(); - EXPECT_TRUE(s.find(id2) == s.end()); - s.insert(id2); - EXPECT_TRUE(s.find(id2) != s.end()); - // Make sure |id1| is still in |s|. - EXPECT_TRUE(s.find(id1) != s.end()); - - s.erase(id1); - EXPECT_TRUE(s.find(id1) == s.end()); - // Make sure |id2| is still in |s|. - EXPECT_TRUE(s.find(id2) != s.end()); - - s.erase(id2); - EXPECT_TRUE(s.find(id2) == s.end()); - EXPECT_TRUE(s.empty()); -} - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/waiter.cc b/mojo/edk/system/waiter.cc deleted file mode 100644 index f18edc8..0000000 --- a/mojo/edk/system/waiter.cc +++ /dev/null @@ -1,100 +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/edk/system/waiter.h" - -#include <limits> - -#include "base/logging.h" -#include "base/time/time.h" - -namespace mojo { -namespace system { - -Waiter::Waiter() - : cv_(&lock_), -#ifndef NDEBUG - initialized_(false), -#endif - awoken_(false), - awake_result_(MOJO_RESULT_INTERNAL), - awake_context_(static_cast<uint32_t>(-1)) { -} - -Waiter::~Waiter() { -} - -void Waiter::Init() { -#ifndef NDEBUG - initialized_ = true; -#endif - awoken_ = false; - // NOTE(vtl): If performance ever becomes an issue, we can disable the setting - // of |awake_result_| (except the first one in |Awake()|) in Release builds. - awake_result_ = MOJO_RESULT_INTERNAL; -} - -// TODO(vtl): Fast-path the |deadline == 0| case? -MojoResult Waiter::Wait(MojoDeadline deadline, uint32_t* context) { - base::AutoLock locker(lock_); - -#ifndef NDEBUG - DCHECK(initialized_); - // It'll need to be re-initialized after this. - initialized_ = false; -#endif - - // Fast-path the already-awoken case: - if (awoken_) { - DCHECK_NE(awake_result_, MOJO_RESULT_INTERNAL); - if (context) - *context = static_cast<uint32_t>(awake_context_); - return awake_result_; - } - - // |MojoDeadline| is actually a |uint64_t|, but we need a signed quantity. - // Treat any out-of-range deadline as "forever" (which is wrong, but okay - // since 2^63 microseconds is ~300000 years). Note that this also takes care - // of the |MOJO_DEADLINE_INDEFINITE| (= 2^64 - 1) case. - if (deadline > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) { - do { - cv_.Wait(); - } while (!awoken_); - } else { - // NOTE(vtl): This is very inefficient on POSIX, since pthreads condition - // variables take an absolute deadline. - const base::TimeTicks end_time = - base::TimeTicks::Now() + - base::TimeDelta::FromMicroseconds(static_cast<int64_t>(deadline)); - do { - base::TimeTicks now_time = base::TimeTicks::Now(); - if (now_time >= end_time) - return MOJO_RESULT_DEADLINE_EXCEEDED; - - cv_.TimedWait(end_time - now_time); - } while (!awoken_); - } - - DCHECK_NE(awake_result_, MOJO_RESULT_INTERNAL); - if (context) - *context = static_cast<uint32_t>(awake_context_); - return awake_result_; -} - -bool Waiter::Awake(MojoResult result, uintptr_t context) { - base::AutoLock locker(lock_); - - if (awoken_) - return true; - - awoken_ = true; - awake_result_ = result; - awake_context_ = context; - cv_.Signal(); - // |cv_.Wait()|/|cv_.TimedWait()| will return after |lock_| is released. - return true; -} - -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/waiter.h b/mojo/edk/system/waiter.h deleted file mode 100644 index b9b63cd..0000000 --- a/mojo/edk/system/waiter.h +++ /dev/null @@ -1,79 +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_EDK_SYSTEM_WAITER_H_ -#define MOJO_EDK_SYSTEM_WAITER_H_ - -#include <stdint.h> - -#include "base/macros.h" -#include "base/synchronization/condition_variable.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/system/awakable.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { -namespace system { - -// IMPORTANT (all-caps gets your attention, right?): |Waiter| methods are called -// under other locks, in particular, |Dispatcher::lock_|s, so |Waiter| methods -// must never call out to other objects (in particular, |Dispatcher|s). This -// class is thread-safe. -class MOJO_SYSTEM_IMPL_EXPORT Waiter : public Awakable { - public: - Waiter(); - ~Waiter(); - - // A |Waiter| can be used multiple times; |Init()| should be called before - // each time it's used. - void Init(); - - // Waits until a suitable |Awake()| is called. (|context| may be null, in - // which case, obviously no context is ever returned.) - // Returns: - // - The result given to the first call to |Awake()| (possibly before this - // call to |Wait()|); in this case, |*context| is set to the value passed - // to that call to |Awake()|. - // - |MOJO_RESULT_DEADLINE_EXCEEDED| if the deadline was exceeded; in this - // case |*context| is not modified. - // - // Usually, the context passed to |Awake()| will be the value passed to - // |Dispatcher::AddAwakable()|, which is usually the index to the array of - // handles passed to |MojoWaitMany()| (or 0 for |MojoWait()|). - // - // Typical |Awake()| results are: - // - |MOJO_RESULT_OK| if one of the flags passed to - // |MojoWait()|/|MojoWaitMany()| (hence |Dispatcher::AddAwakable()|) was - // satisfied; - // - |MOJO_RESULT_CANCELLED| if a handle (on which - // |MojoWait()|/|MojoWaitMany()| was called) was closed (hence the - // dispatcher closed); and - // - |MOJO_RESULT_FAILED_PRECONDITION| if one of the set of flags passed to - // |MojoWait()|/|MojoWaitMany()| cannot or can no longer be satisfied by - // the corresponding handle (e.g., if the other end of a message or data - // pipe is closed). - MojoResult Wait(MojoDeadline deadline, uint32_t* context); - - // Wake the waiter up with the given result and context (or no-op if it's been - // woken up already). - bool Awake(MojoResult result, uintptr_t context) override; - - private: - base::ConditionVariable cv_; // Associated to |lock_|. - base::Lock lock_; // Protects the following members. -#ifndef NDEBUG - bool initialized_; -#endif - bool awoken_; - MojoResult awake_result_; - uintptr_t awake_context_; - - DISALLOW_COPY_AND_ASSIGN(Waiter); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_WAITER_H_ diff --git a/mojo/edk/system/waiter_test_utils.cc b/mojo/edk/system/waiter_test_utils.cc deleted file mode 100644 index ea243ed1..0000000 --- a/mojo/edk/system/waiter_test_utils.cc +++ /dev/null @@ -1,70 +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/edk/system/waiter_test_utils.h" - -namespace mojo { -namespace system { -namespace test { - -SimpleWaiterThread::SimpleWaiterThread(MojoResult* result, uint32_t* context) - : base::SimpleThread("waiter_thread"), result_(result), context_(context) { - waiter_.Init(); - *result_ = -5420734; // Totally invalid result. - *context_ = 23489023; // "Random". -} - -SimpleWaiterThread::~SimpleWaiterThread() { - Join(); -} - -void SimpleWaiterThread::Run() { - *result_ = waiter_.Wait(MOJO_DEADLINE_INDEFINITE, context_); -} - -WaiterThread::WaiterThread(scoped_refptr<Dispatcher> dispatcher, - MojoHandleSignals handle_signals, - MojoDeadline deadline, - uint32_t context, - bool* did_wait_out, - MojoResult* result_out, - uint32_t* context_out, - HandleSignalsState* signals_state_out) - : base::SimpleThread("waiter_thread"), - dispatcher_(dispatcher), - handle_signals_(handle_signals), - deadline_(deadline), - context_(context), - did_wait_out_(did_wait_out), - result_out_(result_out), - context_out_(context_out), - signals_state_out_(signals_state_out) { - *did_wait_out_ = false; - // Initialize these with invalid results (so that we'll be sure to catch any - // case where they're not set). - *result_out_ = -8542346; - *context_out_ = 89023444; - *signals_state_out_ = HandleSignalsState(~0u, ~0u); -} - -WaiterThread::~WaiterThread() { - Join(); -} - -void WaiterThread::Run() { - waiter_.Init(); - - *result_out_ = dispatcher_->AddAwakable(&waiter_, handle_signals_, context_, - signals_state_out_); - if (*result_out_ != MOJO_RESULT_OK) - return; - - *did_wait_out_ = true; - *result_out_ = waiter_.Wait(deadline_, context_out_); - dispatcher_->RemoveAwakable(&waiter_, signals_state_out_); -} - -} // namespace test -} // namespace system -} // namespace mojo diff --git a/mojo/edk/system/waiter_test_utils.h b/mojo/edk/system/waiter_test_utils.h deleted file mode 100644 index b11d78d..0000000 --- a/mojo/edk/system/waiter_test_utils.h +++ /dev/null @@ -1,105 +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_EDK_SYSTEM_WAITER_TEST_UTILS_H_ -#define MOJO_EDK_SYSTEM_WAITER_TEST_UTILS_H_ - -#include <stdint.h> - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/threading/simple_thread.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/handle_signals_state.h" -#include "mojo/edk/system/waiter.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { -namespace system { -namespace test { - -// This is a very simple thread that has a |Waiter|, on which it waits -// indefinitely (and records the result). It will create and initialize the -// |Waiter| on creation, but the caller must start the thread with |Start()|. It -// will join the thread on destruction. -// -// One usually uses it like: -// -// MojoResult result; -// { -// AwakableList awakable_list; -// test::SimpleWaiterThread thread(&result); -// awakable_list.Add(thread.waiter(), ...); -// thread.Start(); -// ... some stuff to wake the waiter ... -// awakable_list.Remove(thread.waiter()); -// } // Join |thread|. -// EXPECT_EQ(..., result); -// -// There's a bit of unrealism in its use: In this sort of usage, calls such as -// |Waiter::Init()|, |AddAwakable()|, and |RemoveAwakable()| are done in the -// main (test) thread, not the waiter thread (as would actually happen in real -// code). (We accept this unrealism for simplicity, since |AwakableList| is -// thread-unsafe so making it more realistic would require adding nontrivial -// synchronization machinery.) -class SimpleWaiterThread : public base::SimpleThread { - public: - // For the duration of the lifetime of this object, |*result| belongs to it - // (in the sense that it will write to it whenever it wants). - SimpleWaiterThread(MojoResult* result, uint32_t* context); - ~SimpleWaiterThread() override; // Joins the thread. - - Waiter* waiter() { return &waiter_; } - - private: - void Run() override; - - MojoResult* const result_; - uint32_t* const context_; - Waiter waiter_; - - DISALLOW_COPY_AND_ASSIGN(SimpleWaiterThread); -}; - -// This is a more complex and realistic thread that has a |Waiter|, on which it -// waits for the given deadline (with the given flags). Unlike -// |SimpleWaiterThread|, it requires the machinery of |Dispatcher|. -class WaiterThread : public base::SimpleThread { - public: - // Note: |*did_wait_out|, |*result_out|, |*context_out| and - // |*signals_state_out| "belong" to this object (i.e., may be modified by, on - // some other thread) while it's alive. - WaiterThread(scoped_refptr<Dispatcher> dispatcher, - MojoHandleSignals handle_signals, - MojoDeadline deadline, - uint32_t context, - bool* did_wait_out, - MojoResult* result_out, - uint32_t* context_out, - HandleSignalsState* signals_state_out); - ~WaiterThread() override; - - private: - void Run() override; - - const scoped_refptr<Dispatcher> dispatcher_; - const MojoHandleSignals handle_signals_; - const MojoDeadline deadline_; - const uint32_t context_; - bool* const did_wait_out_; - MojoResult* const result_out_; - uint32_t* const context_out_; - HandleSignalsState* const signals_state_out_; - - Waiter waiter_; - - DISALLOW_COPY_AND_ASSIGN(WaiterThread); -}; - -} // namespace test -} // namespace system -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_WAITER_TEST_UTILS_H_ diff --git a/mojo/edk/system/waiter_unittest.cc b/mojo/edk/system/waiter_unittest.cc deleted file mode 100644 index 859ff0d..0000000 --- a/mojo/edk/system/waiter_unittest.cc +++ /dev/null @@ -1,302 +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. - -// NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a -// heavily-loaded system). Sorry. |test::EpsilonTimeout()| may be increased to -// increase tolerance and reduce observed flakiness (though doing so reduces the -// meaningfulness of the test). - -#include "mojo/edk/system/waiter.h" - -#include <stdint.h> - -#include "base/macros.h" -#include "base/synchronization/lock.h" -#include "base/threading/platform_thread.h" // For |Sleep()|. -#include "base/threading/simple_thread.h" -#include "base/time/time.h" -#include "mojo/edk/system/test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -const int64_t kMicrosPerMs = 1000; -const int64_t kPollTimeMicros = 10 * kMicrosPerMs; // 10 ms. - -class WaitingThread : public base::SimpleThread { - public: - explicit WaitingThread(MojoDeadline deadline) - : base::SimpleThread("waiting_thread"), - deadline_(deadline), - done_(false), - result_(MOJO_RESULT_UNKNOWN), - context_(static_cast<uint32_t>(-1)) { - waiter_.Init(); - } - - ~WaitingThread() override { Join(); } - - void WaitUntilDone(MojoResult* result, - uint32_t* context, - base::TimeDelta* elapsed) { - for (;;) { - { - base::AutoLock locker(lock_); - if (done_) { - *result = result_; - *context = context_; - *elapsed = elapsed_; - break; - } - } - - base::PlatformThread::Sleep( - base::TimeDelta::FromMicroseconds(kPollTimeMicros)); - } - } - - Waiter* waiter() { return &waiter_; } - - private: - void Run() override { - test::Stopwatch stopwatch; - MojoResult result; - uint32_t context = static_cast<uint32_t>(-1); - base::TimeDelta elapsed; - - stopwatch.Start(); - result = waiter_.Wait(deadline_, &context); - elapsed = stopwatch.Elapsed(); - - { - base::AutoLock locker(lock_); - done_ = true; - result_ = result; - context_ = context; - elapsed_ = elapsed; - } - } - - const MojoDeadline deadline_; - Waiter waiter_; // Thread-safe. - - base::Lock lock_; // Protects the following members. - bool done_; - MojoResult result_; - uint32_t context_; - base::TimeDelta elapsed_; - - DISALLOW_COPY_AND_ASSIGN(WaitingThread); -}; - -TEST(WaiterTest, Basic) { - MojoResult result; - uint32_t context; - base::TimeDelta elapsed; - - // Finite deadline. - - // Awake immediately after thread start. - { - WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds()); - thread.Start(); - thread.waiter()->Awake(MOJO_RESULT_OK, 1); - thread.WaitUntilDone(&result, &context, &elapsed); - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(1u, context); - EXPECT_LT(elapsed, test::EpsilonTimeout()); - } - - // Awake before after thread start. - { - WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds()); - thread.waiter()->Awake(MOJO_RESULT_CANCELLED, 2); - thread.Start(); - thread.WaitUntilDone(&result, &context, &elapsed); - EXPECT_EQ(MOJO_RESULT_CANCELLED, result); - EXPECT_EQ(2u, context); - EXPECT_LT(elapsed, test::EpsilonTimeout()); - } - - // Awake some time after thread start. - { - WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds()); - thread.Start(); - base::PlatformThread::Sleep(2 * test::EpsilonTimeout()); - thread.waiter()->Awake(1, 3); - thread.WaitUntilDone(&result, &context, &elapsed); - EXPECT_EQ(1, result); - EXPECT_EQ(3u, context); - EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout()); - EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout()); - } - - // Awake some longer time after thread start. - { - WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds()); - thread.Start(); - base::PlatformThread::Sleep(5 * test::EpsilonTimeout()); - thread.waiter()->Awake(2, 4); - thread.WaitUntilDone(&result, &context, &elapsed); - EXPECT_EQ(2, result); - EXPECT_EQ(4u, context); - EXPECT_GT(elapsed, (5 - 1) * test::EpsilonTimeout()); - EXPECT_LT(elapsed, (5 + 1) * test::EpsilonTimeout()); - } - - // Don't awake -- time out (on another thread). - { - WaitingThread thread(2 * test::EpsilonTimeout().InMicroseconds()); - thread.Start(); - thread.WaitUntilDone(&result, &context, &elapsed); - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, result); - EXPECT_EQ(static_cast<uint32_t>(-1), context); - EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout()); - EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout()); - } - - // No (indefinite) deadline. - - // Awake immediately after thread start. - { - WaitingThread thread(MOJO_DEADLINE_INDEFINITE); - thread.Start(); - thread.waiter()->Awake(MOJO_RESULT_OK, 5); - thread.WaitUntilDone(&result, &context, &elapsed); - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(5u, context); - EXPECT_LT(elapsed, test::EpsilonTimeout()); - } - - // Awake before after thread start. - { - WaitingThread thread(MOJO_DEADLINE_INDEFINITE); - thread.waiter()->Awake(MOJO_RESULT_CANCELLED, 6); - thread.Start(); - thread.WaitUntilDone(&result, &context, &elapsed); - EXPECT_EQ(MOJO_RESULT_CANCELLED, result); - EXPECT_EQ(6u, context); - EXPECT_LT(elapsed, test::EpsilonTimeout()); - } - - // Awake some time after thread start. - { - WaitingThread thread(MOJO_DEADLINE_INDEFINITE); - thread.Start(); - base::PlatformThread::Sleep(2 * test::EpsilonTimeout()); - thread.waiter()->Awake(1, 7); - thread.WaitUntilDone(&result, &context, &elapsed); - EXPECT_EQ(1, result); - EXPECT_EQ(7u, context); - EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout()); - EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout()); - } - - // Awake some longer time after thread start. - { - WaitingThread thread(MOJO_DEADLINE_INDEFINITE); - thread.Start(); - base::PlatformThread::Sleep(5 * test::EpsilonTimeout()); - thread.waiter()->Awake(2, 8); - thread.WaitUntilDone(&result, &context, &elapsed); - EXPECT_EQ(2, result); - EXPECT_EQ(8u, context); - EXPECT_GT(elapsed, (5 - 1) * test::EpsilonTimeout()); - EXPECT_LT(elapsed, (5 + 1) * test::EpsilonTimeout()); - } -} - -TEST(WaiterTest, TimeOut) { - test::Stopwatch stopwatch; - base::TimeDelta elapsed; - - Waiter waiter; - uint32_t context = 123; - - waiter.Init(); - stopwatch.Start(); - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, &context)); - elapsed = stopwatch.Elapsed(); - EXPECT_LT(elapsed, test::EpsilonTimeout()); - EXPECT_EQ(123u, context); - - waiter.Init(); - stopwatch.Start(); - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - waiter.Wait(2 * test::EpsilonTimeout().InMicroseconds(), &context)); - elapsed = stopwatch.Elapsed(); - EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout()); - EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout()); - EXPECT_EQ(123u, context); - - waiter.Init(); - stopwatch.Start(); - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - waiter.Wait(5 * test::EpsilonTimeout().InMicroseconds(), &context)); - elapsed = stopwatch.Elapsed(); - EXPECT_GT(elapsed, (5 - 1) * test::EpsilonTimeout()); - EXPECT_LT(elapsed, (5 + 1) * test::EpsilonTimeout()); - EXPECT_EQ(123u, context); -} - -// The first |Awake()| should always win. -TEST(WaiterTest, MultipleAwakes) { - MojoResult result; - uint32_t context; - base::TimeDelta elapsed; - - { - WaitingThread thread(MOJO_DEADLINE_INDEFINITE); - thread.Start(); - thread.waiter()->Awake(MOJO_RESULT_OK, 1); - thread.waiter()->Awake(1, 2); - thread.WaitUntilDone(&result, &context, &elapsed); - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(1u, context); - EXPECT_LT(elapsed, test::EpsilonTimeout()); - } - - { - WaitingThread thread(MOJO_DEADLINE_INDEFINITE); - thread.waiter()->Awake(1, 3); - thread.Start(); - thread.waiter()->Awake(MOJO_RESULT_OK, 4); - thread.WaitUntilDone(&result, &context, &elapsed); - EXPECT_EQ(1, result); - EXPECT_EQ(3u, context); - EXPECT_LT(elapsed, test::EpsilonTimeout()); - } - - { - WaitingThread thread(MOJO_DEADLINE_INDEFINITE); - thread.Start(); - thread.waiter()->Awake(10, 5); - base::PlatformThread::Sleep(2 * test::EpsilonTimeout()); - thread.waiter()->Awake(20, 6); - thread.WaitUntilDone(&result, &context, &elapsed); - EXPECT_EQ(10, result); - EXPECT_EQ(5u, context); - EXPECT_LT(elapsed, test::EpsilonTimeout()); - } - - { - WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds()); - thread.Start(); - base::PlatformThread::Sleep(1 * test::EpsilonTimeout()); - thread.waiter()->Awake(MOJO_RESULT_FAILED_PRECONDITION, 7); - base::PlatformThread::Sleep(2 * test::EpsilonTimeout()); - thread.waiter()->Awake(MOJO_RESULT_OK, 8); - thread.WaitUntilDone(&result, &context, &elapsed); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); - EXPECT_EQ(7u, context); - EXPECT_GT(elapsed, (1 - 1) * test::EpsilonTimeout()); - EXPECT_LT(elapsed, (1 + 1) * test::EpsilonTimeout()); - } -} - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/edk/test/BUILD.gn b/mojo/edk/test/BUILD.gn deleted file mode 100644 index d05f6d4..0000000 --- a/mojo/edk/test/BUILD.gn +++ /dev/null @@ -1,119 +0,0 @@ -# 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. - -import("../mojo_edk.gni") - -mojo_edk_source_set("test_support") { - testonly = true - sources = [ - "multiprocess_test_helper.cc", - "multiprocess_test_helper.h", - "test_utils.h", - "test_utils_posix.cc", - "test_utils_win.cc", - ] - - deps = [ - "//base", - "//base/test:test_support", - "//testing/gtest", - ] - - mojo_edk_deps = [ "mojo/edk/system" ] -} - -mojo_edk_source_set("run_all_unittests") { - testonly = true - sources = [ - "run_all_unittests.cc", - ] - - deps = [ - ":test_support_impl", - "//base", - "//base/test:test_support", - "//testing/gtest", - ] - - mojo_edk_deps = [ "mojo/edk/system" ] - - mojo_sdk_deps = [ "mojo/public/c/test_support" ] -} - -mojo_edk_source_set("run_all_perftests") { - testonly = true - deps = [ - ":test_support_impl", - "//base", - "//base/test:test_support", - ] - - mojo_edk_deps = [ "mojo/edk/system" ] - - mojo_sdk_deps = [ "mojo/public/c/test_support" ] - - sources = [ - "run_all_perftests.cc", - ] -} - -mojo_edk_source_set("test_support_impl") { - testonly = true - deps = [ - "//base", - "//base/test:test_support", - ] - - mojo_sdk_deps = [ "mojo/public/c/test_support" ] - - sources = [ - "test_support_impl.cc", - "test_support_impl.h", - ] -} - -# Public SDK test targets follow. These targets are not defined within the -# public SDK itself as running the unittests requires the EDK. - -test("mojo_public_application_unittests") { - deps = [ - ":run_all_unittests", - "../../public/cpp/application/tests", - ] -} - -test("mojo_public_bindings_unittests") { - deps = [ - ":run_all_unittests", - "../../public/cpp/bindings/tests", - ] -} - -test("mojo_public_environment_unittests") { - deps = [ - ":run_all_unittests", - "../../public/cpp/environment/tests", - ] -} - -test("mojo_public_system_perftests") { - deps = [ - ":run_all_perftests", - "../../public/c/system/tests:perftests", - ] -} - -test("mojo_public_system_unittests") { - deps = [ - ":run_all_unittests", - "../../public/cpp/system/tests", - ] -} - -test("mojo_public_utility_unittests") { - deps = [ - ":run_all_unittests", - "../../public/cpp/utility/tests", - ] -} diff --git a/mojo/edk/test/multiprocess_test_helper.cc b/mojo/edk/test/multiprocess_test_helper.cc deleted file mode 100644 index 6dd8b72..0000000 --- a/mojo/edk/test/multiprocess_test_helper.cc +++ /dev/null @@ -1,84 +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/edk/test/multiprocess_test_helper.h" - -#include "base/command_line.h" -#include "base/logging.h" -#include "base/process/kill.h" -#include "base/process/process_handle.h" -#include "build/build_config.h" -#include "mojo/edk/embedder/platform_channel_pair.h" - -namespace mojo { -namespace test { - -MultiprocessTestHelper::MultiprocessTestHelper() { - platform_channel_pair_.reset(new embedder::PlatformChannelPair()); - server_platform_handle = platform_channel_pair_->PassServerHandle(); -} - -MultiprocessTestHelper::~MultiprocessTestHelper() { - CHECK(!test_child_.IsValid()); - server_platform_handle.reset(); - platform_channel_pair_.reset(); -} - -void MultiprocessTestHelper::StartChild(const std::string& test_child_name) { - CHECK(platform_channel_pair_); - CHECK(!test_child_name.empty()); - CHECK(!test_child_.IsValid()); - - std::string test_child_main = test_child_name + "TestChildMain"; - - base::CommandLine command_line( - base::GetMultiProcessTestChildBaseCommandLine()); - embedder::HandlePassingInformation handle_passing_info; - platform_channel_pair_->PrepareToPassClientHandleToChildProcess( - &command_line, &handle_passing_info); - - base::LaunchOptions options; -#if defined(OS_POSIX) - options.fds_to_remap = &handle_passing_info; -#elif defined(OS_WIN) - options.start_hidden = true; - options.handles_to_inherit = &handle_passing_info; -#else -#error "Not supported yet." -#endif - - test_child_ = - base::SpawnMultiProcessTestChild(test_child_main, command_line, options); - platform_channel_pair_->ChildProcessLaunched(); - - CHECK(test_child_.IsValid()); -} - -int MultiprocessTestHelper::WaitForChildShutdown() { - CHECK(test_child_.IsValid()); - - int rv = -1; - CHECK( - test_child_.WaitForExitWithTimeout(TestTimeouts::action_timeout(), &rv)); - test_child_.Close(); - return rv; -} - -bool MultiprocessTestHelper::WaitForChildTestShutdown() { - return WaitForChildShutdown() == 0; -} - -// static -void MultiprocessTestHelper::ChildSetup() { - CHECK(base::CommandLine::InitializedForCurrentProcess()); - client_platform_handle = - embedder::PlatformChannelPair::PassClientHandleFromParentProcess( - *base::CommandLine::ForCurrentProcess()); -} - -// static -embedder::ScopedPlatformHandle MultiprocessTestHelper::client_platform_handle; - -} // namespace test -} // namespace mojo diff --git a/mojo/edk/test/multiprocess_test_helper.h b/mojo/edk/test/multiprocess_test_helper.h deleted file mode 100644 index e40b309c..0000000 --- a/mojo/edk/test/multiprocess_test_helper.h +++ /dev/null @@ -1,92 +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_EDK_TEST_MULTIPROCESS_TEST_HELPER_H_ -#define MOJO_EDK_TEST_MULTIPROCESS_TEST_HELPER_H_ - -#include <string> - -#include "base/macros.h" -#include "base/process/process.h" -#include "base/test/multiprocess_test.h" -#include "base/test/test_timeouts.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "testing/multiprocess_func_list.h" - -namespace mojo { - -namespace embedder { -class PlatformChannelPair; -} - -namespace test { - -class MultiprocessTestHelper { - public: - MultiprocessTestHelper(); - ~MultiprocessTestHelper(); - - // Start a child process and run the "main" function "named" |test_child_name| - // declared using |MOJO_MULTIPROCESS_TEST_CHILD_MAIN()| or - // |MOJO_MULTIPROCESS_TEST_CHILD_TEST()| (below). - void StartChild(const std::string& test_child_name); - // Wait for the child process to terminate. - // Returns the exit code of the child process. Note that, though it's declared - // to be an |int|, the exit code is subject to mangling by the OS. E.g., we - // usually return -1 on error in the child (e.g., if |test_child_name| was not - // found), but this is mangled to 255 on Linux. You should only rely on codes - // 0-127 being preserved, and -1 being outside the range 0-127. - int WaitForChildShutdown(); - - // Like |WaitForChildShutdown()|, but returns true on success (exit code of 0) - // and false otherwise. You probably want to do something like - // |EXPECT_TRUE(WaitForChildTestShutdown());|. Mainly for use with - // |MOJO_MULTIPROCESS_TEST_CHILD_TEST()|. - bool WaitForChildTestShutdown(); - - // For use by |MOJO_MULTIPROCESS_TEST_CHILD_MAIN()| only: - static void ChildSetup(); - - // For use in the main process: - embedder::ScopedPlatformHandle server_platform_handle; - - // For use (and only valid) in the child process: - static embedder::ScopedPlatformHandle client_platform_handle; - - private: - scoped_ptr<embedder::PlatformChannelPair> platform_channel_pair_; - - // Valid after |StartChild()| and before |WaitForChildShutdown()|. - base::Process test_child_; - - DISALLOW_COPY_AND_ASSIGN(MultiprocessTestHelper); -}; - -// Use this to declare the child process's "main()" function for tests using -// |MultiprocessTestHelper|. It returns an |int|, which will be the process's -// exit code (but see the comment about |WaitForChildShutdown()|). -#define MOJO_MULTIPROCESS_TEST_CHILD_MAIN(test_child_name) \ - MULTIPROCESS_TEST_MAIN_WITH_SETUP( \ - test_child_name##TestChildMain, \ - ::mojo::test::MultiprocessTestHelper::ChildSetup) - -// Use this (and |WaitForChildTestShutdown()|) for the child process's "main()", -// if you want to use |EXPECT_...()| or |ASSERT_...()|; it has a |void| return -// type. (Note that while an |ASSERT_...()| failure will abort the test in the -// child, it will not abort the test in the parent.) -#define MOJO_MULTIPROCESS_TEST_CHILD_TEST(test_child_name) \ - void test_child_name##TestChildTest(); \ - MOJO_MULTIPROCESS_TEST_CHILD_MAIN(test_child_name) { \ - test_child_name##TestChildTest(); \ - return (::testing::Test::HasFatalFailure() || \ - ::testing::Test::HasNonfatalFailure()) \ - ? 1 \ - : 0; \ - } \ - void test_child_name##TestChildTest() - -} // namespace test -} // namespace mojo - -#endif // MOJO_EDK_TEST_MULTIPROCESS_TEST_HELPER_H_ diff --git a/mojo/edk/test/multiprocess_test_helper_unittest.cc b/mojo/edk/test/multiprocess_test_helper_unittest.cc deleted file mode 100644 index 93496fb..0000000 --- a/mojo/edk/test/multiprocess_test_helper_unittest.cc +++ /dev/null @@ -1,194 +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/edk/test/multiprocess_test_helper.h" - -#include "base/logging.h" -#include "build/build_config.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/test/test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_POSIX) -#include <fcntl.h> -#endif - -namespace mojo { -namespace test { -namespace { - -bool IsNonBlocking(const embedder::PlatformHandle& handle) { -#if defined(OS_WIN) - // Haven't figured out a way to query whether a HANDLE was created with - // FILE_FLAG_OVERLAPPED. - return true; -#else - return fcntl(handle.fd, F_GETFL) & O_NONBLOCK; -#endif -} - -bool WriteByte(const embedder::PlatformHandle& handle, char c) { - size_t bytes_written = 0; - BlockingWrite(handle, &c, 1, &bytes_written); - return bytes_written == 1; -} - -bool ReadByte(const embedder::PlatformHandle& handle, char* c) { - size_t bytes_read = 0; - BlockingRead(handle, c, 1, &bytes_read); - return bytes_read == 1; -} - -typedef testing::Test MultiprocessTestHelperTest; - -#if defined(OS_ANDROID) -// Android multi-process tests are not executing the new process. This is flaky. -#define MAYBE_RunChild DISABLED_RunChild -#else -#define MAYBE_RunChild RunChild -#endif // defined(OS_ANDROID) -TEST_F(MultiprocessTestHelperTest, MAYBE_RunChild) { - MultiprocessTestHelper helper; - EXPECT_TRUE(helper.server_platform_handle.is_valid()); - - helper.StartChild("RunChild"); - EXPECT_EQ(123, helper.WaitForChildShutdown()); -} - -MOJO_MULTIPROCESS_TEST_CHILD_MAIN(RunChild) { - CHECK(MultiprocessTestHelper::client_platform_handle.is_valid()); - return 123; -} - -#if defined(OS_ANDROID) -// Android multi-process tests are not executing the new process. This is flaky. -#define MAYBE_TestChildMainNotFound DISABLED_TestChildMainNotFound -#else -#define MAYBE_TestChildMainNotFound TestChildMainNotFound -#endif // defined(OS_ANDROID) -TEST_F(MultiprocessTestHelperTest, MAYBE_TestChildMainNotFound) { - MultiprocessTestHelper helper; - helper.StartChild("NoSuchTestChildMain"); - int result = helper.WaitForChildShutdown(); - EXPECT_FALSE(result >= 0 && result <= 127); -} - -#if defined(OS_ANDROID) -// Android multi-process tests are not executing the new process. This is flaky. -#define MAYBE_PassedChannel DISABLED_PassedChannel -#else -#define MAYBE_PassedChannel PassedChannel -#endif // defined(OS_ANDROID) -TEST_F(MultiprocessTestHelperTest, MAYBE_PassedChannel) { - MultiprocessTestHelper helper; - EXPECT_TRUE(helper.server_platform_handle.is_valid()); - helper.StartChild("PassedChannel"); - - // Take ownership of the handle. - embedder::ScopedPlatformHandle handle = helper.server_platform_handle.Pass(); - - // The handle should be non-blocking. - EXPECT_TRUE(IsNonBlocking(handle.get())); - - // Write a byte. - const char c = 'X'; - EXPECT_TRUE(WriteByte(handle.get(), c)); - - // It'll echo it back to us, incremented. - char d = 0; - EXPECT_TRUE(ReadByte(handle.get(), &d)); - EXPECT_EQ(c + 1, d); - - // And return it, incremented again. - EXPECT_EQ(c + 2, helper.WaitForChildShutdown()); -} - -MOJO_MULTIPROCESS_TEST_CHILD_MAIN(PassedChannel) { - CHECK(MultiprocessTestHelper::client_platform_handle.is_valid()); - - // Take ownership of the handle. - embedder::ScopedPlatformHandle handle = - MultiprocessTestHelper::client_platform_handle.Pass(); - - // The handle should be non-blocking. - EXPECT_TRUE(IsNonBlocking(handle.get())); - - // Read a byte. - char c = 0; - EXPECT_TRUE(ReadByte(handle.get(), &c)); - - // Write it back, incremented. - c++; - EXPECT_TRUE(WriteByte(handle.get(), c)); - - // And return it, incremented again. - c++; - return static_cast<int>(c); -} - -#if defined(OS_ANDROID) -// Android multi-process tests are not executing the new process. This is flaky. -#define MAYBE_ChildTestPasses DISABLED_ChildTestPasses -#else -#define MAYBE_ChildTestPasses ChildTestPasses -#endif // defined(OS_ANDROID) -TEST_F(MultiprocessTestHelperTest, MAYBE_ChildTestPasses) { - MultiprocessTestHelper helper; - EXPECT_TRUE(helper.server_platform_handle.is_valid()); - helper.StartChild("ChildTestPasses"); - EXPECT_TRUE(helper.WaitForChildTestShutdown()); -} - -MOJO_MULTIPROCESS_TEST_CHILD_TEST(ChildTestPasses) { - ASSERT_TRUE(MultiprocessTestHelper::client_platform_handle.is_valid()); - EXPECT_TRUE( - IsNonBlocking(MultiprocessTestHelper::client_platform_handle.get())); -} - -#if defined(OS_ANDROID) -// Android multi-process tests are not executing the new process. This is flaky. -#define MAYBE_ChildTestFailsAssert DISABLED_ChildTestFailsAssert -#else -#define MAYBE_ChildTestFailsAssert ChildTestFailsAssert -#endif // defined(OS_ANDROID) -TEST_F(MultiprocessTestHelperTest, MAYBE_ChildTestFailsAssert) { - MultiprocessTestHelper helper; - EXPECT_TRUE(helper.server_platform_handle.is_valid()); - helper.StartChild("ChildTestFailsAssert"); - EXPECT_FALSE(helper.WaitForChildTestShutdown()); -} - -MOJO_MULTIPROCESS_TEST_CHILD_TEST(ChildTestFailsAssert) { - ASSERT_FALSE(MultiprocessTestHelper::client_platform_handle.is_valid()) - << "DISREGARD: Expected failure in child process"; - ASSERT_FALSE( - IsNonBlocking(MultiprocessTestHelper::client_platform_handle.get())) - << "Not reached"; - CHECK(false) << "Not reached"; -} - -#if defined(OS_ANDROID) -// Android multi-process tests are not executing the new process. This is flaky. -#define MAYBE_ChildTestFailsExpect DISABLED_ChildTestFailsExpect -#else -#define MAYBE_ChildTestFailsExpect ChildTestFailsExpect -#endif // defined(OS_ANDROID) -TEST_F(MultiprocessTestHelperTest, MAYBE_ChildTestFailsExpect) { - MultiprocessTestHelper helper; - EXPECT_TRUE(helper.server_platform_handle.is_valid()); - helper.StartChild("ChildTestFailsExpect"); - EXPECT_FALSE(helper.WaitForChildTestShutdown()); -} - -MOJO_MULTIPROCESS_TEST_CHILD_TEST(ChildTestFailsExpect) { - EXPECT_FALSE(MultiprocessTestHelper::client_platform_handle.is_valid()) - << "DISREGARD: Expected failure #1 in child process"; - EXPECT_FALSE( - IsNonBlocking(MultiprocessTestHelper::client_platform_handle.get())) - << "DISREGARD: Expected failure #2 in child process"; -} - -} // namespace -} // namespace test -} // namespace mojo diff --git a/mojo/edk/test/run_all_perftests.cc b/mojo/edk/test/run_all_perftests.cc deleted file mode 100644 index f0eacc6..0000000 --- a/mojo/edk/test/run_all_perftests.cc +++ /dev/null @@ -1,14 +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 "base/test/perf_test_suite.h" -#include "mojo/edk/embedder/test_embedder.h" -#include "mojo/edk/test/test_support_impl.h" -#include "mojo/public/tests/test_support_private.h" - -int main(int argc, char** argv) { - mojo::embedder::test::InitWithSimplePlatformSupport(); - mojo::test::TestSupport::Init(new mojo::test::TestSupportImpl()); - return base::PerfTestSuite(argc, argv).Run(); -} diff --git a/mojo/edk/test/run_all_unittests.cc b/mojo/edk/test/run_all_unittests.cc deleted file mode 100644 index 166a73e..0000000 --- a/mojo/edk/test/run_all_unittests.cc +++ /dev/null @@ -1,26 +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 "base/bind.h" -#include "base/test/launcher/unit_test_launcher.h" -#include "base/test/test_suite.h" -#include "mojo/edk/embedder/test_embedder.h" -#include "mojo/edk/test/test_support_impl.h" -#include "mojo/public/tests/test_support_private.h" -#include "testing/gtest/include/gtest/gtest.h" - -int main(int argc, char** argv) { - // Silence death test thread warnings on Linux. We can afford to run our death - // tests a little more slowly (< 10 ms per death test on a Z620). - testing::GTEST_FLAG(death_test_style) = "threadsafe"; - - base::TestSuite test_suite(argc, argv); - - mojo::embedder::test::InitWithSimplePlatformSupport(); - mojo::test::TestSupport::Init(new mojo::test::TestSupportImpl()); - - return base::LaunchUnitTests( - argc, argv, - base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite))); -} diff --git a/mojo/edk/test/test_support_impl.cc b/mojo/edk/test/test_support_impl.cc deleted file mode 100644 index 34b32ce..0000000 --- a/mojo/edk/test/test_support_impl.cc +++ /dev/null @@ -1,83 +0,0 @@ -// 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/edk/test/test_support_impl.h" - -#include <stdlib.h> -#include <string.h> - -#include <string> - -#include "base/files/file_enumerator.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/path_service.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/test/perf_log.h" - -namespace mojo { -namespace test { -namespace { - -base::FilePath ResolveSourceRootRelativePath(const char* relative_path) { - base::FilePath path; - if (!PathService::Get(base::DIR_SOURCE_ROOT, &path)) - return base::FilePath(); - - std::vector<std::string> components; - base::SplitString(relative_path, '/', &components); - - for (size_t i = 0; i < components.size(); ++i) { - if (!components[i].empty()) - path = path.AppendASCII(components[i]); - } - - return path; -} - -} // namespace - -TestSupportImpl::TestSupportImpl() { -} - -TestSupportImpl::~TestSupportImpl() { -} - -void TestSupportImpl::LogPerfResult(const char* test_name, - const char* sub_test_name, - double value, - const char* units) { - DCHECK(test_name); - if (sub_test_name) { - std::string name = base::StringPrintf("%s/%s", test_name, sub_test_name); - base::LogPerfResult(name.c_str(), value, units); - } else { - base::LogPerfResult(test_name, value, units); - } -} - -FILE* TestSupportImpl::OpenSourceRootRelativeFile(const char* relative_path) { - return base::OpenFile(ResolveSourceRootRelativePath(relative_path), "rb"); -} - -char** TestSupportImpl::EnumerateSourceRootRelativeDirectory( - const char* relative_path) { - std::vector<std::string> names; - base::FileEnumerator e(ResolveSourceRootRelativePath(relative_path), false, - base::FileEnumerator::FILES); - for (base::FilePath name = e.Next(); !name.empty(); name = e.Next()) - names.push_back(name.BaseName().AsUTF8Unsafe()); - - // |names.size() + 1| for null terminator. - char** rv = static_cast<char**>(calloc(names.size() + 1, sizeof(char*))); - for (size_t i = 0; i < names.size(); ++i) - rv[i] = base::strdup(names[i].c_str()); - return rv; -} - -} // namespace test -} // namespace mojo diff --git a/mojo/edk/test/test_support_impl.h b/mojo/edk/test/test_support_impl.h deleted file mode 100644 index 533bfce..0000000 --- a/mojo/edk/test/test_support_impl.h +++ /dev/null @@ -1,34 +0,0 @@ -// 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_EDK_TEST_TEST_SUPPORT_IMPL_H_ -#define MOJO_EDK_TEST_TEST_SUPPORT_IMPL_H_ - -#include "base/macros.h" -#include "mojo/public/tests/test_support_private.h" - -namespace mojo { -namespace test { - -class TestSupportImpl : public TestSupport { - public: - TestSupportImpl(); - ~TestSupportImpl() override; - - void LogPerfResult(const char* test_name, - const char* sub_test_name, - double value, - const char* units) override; - FILE* OpenSourceRootRelativeFile(const char* relative_path) override; - char** EnumerateSourceRootRelativeDirectory( - const char* relative_path) override; - - private: - DISALLOW_COPY_AND_ASSIGN(TestSupportImpl); -}; - -} // namespace test -} // namespace mojo - -#endif // MOJO_EDK_TEST_TEST_SUPPORT_IMPL_H_ diff --git a/mojo/edk/test/test_utils.h b/mojo/edk/test/test_utils.h deleted file mode 100644 index 9287457..0000000 --- a/mojo/edk/test/test_utils.h +++ /dev/null @@ -1,57 +0,0 @@ -// 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_EDK_TEST_TEST_UTILS_H_ -#define MOJO_EDK_TEST_TEST_UTILS_H_ - -#include <stddef.h> -#include <stdio.h> - -#include <string> - -#include "base/files/file_path.h" -#include "base/files/scoped_file.h" -#include "mojo/edk/embedder/platform_handle.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" - -namespace mojo { -namespace test { - -// On success, |bytes_written| is updated to the number of bytes written; -// otherwise it is untouched. -bool BlockingWrite(const embedder::PlatformHandle& handle, - const void* buffer, - size_t bytes_to_write, - size_t* bytes_written); - -// On success, |bytes_read| is updated to the number of bytes read; otherwise it -// is untouched. -bool BlockingRead(const embedder::PlatformHandle& handle, - void* buffer, - size_t buffer_size, - size_t* bytes_read); - -// If the read is done successfully or would block, the function returns true -// and updates |bytes_read| to the number of bytes read (0 if the read would -// block); otherwise it returns false and leaves |bytes_read| untouched. -// |handle| must already be in non-blocking mode. -bool NonBlockingRead(const embedder::PlatformHandle& handle, - void* buffer, - size_t buffer_size, - size_t* bytes_read); - -// Gets a (scoped) |PlatformHandle| from the given (scoped) |FILE|. -embedder::ScopedPlatformHandle PlatformHandleFromFILE(base::ScopedFILE fp); - -// Gets a (scoped) |FILE| from a (scoped) |PlatformHandle|. -base::ScopedFILE FILEFromPlatformHandle(embedder::ScopedPlatformHandle h, - const char* mode); - -// Returns the path to the mojom js bindings file. -base::FilePath GetFilePathForJSResource(const std::string& path); - -} // namespace test -} // namespace mojo - -#endif // MOJO_EDK_TEST_TEST_UTILS_H_ diff --git a/mojo/edk/test/test_utils_posix.cc b/mojo/edk/test/test_utils_posix.cc deleted file mode 100644 index 6491baf..0000000 --- a/mojo/edk/test/test_utils_posix.cc +++ /dev/null @@ -1,100 +0,0 @@ -// 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/edk/test/test_utils.h" - -#include <fcntl.h> -#include <unistd.h> - -#include "base/base_paths.h" -#include "base/path_service.h" -#include "base/posix/eintr_wrapper.h" - -namespace mojo { -namespace test { - -bool BlockingWrite(const embedder::PlatformHandle& handle, - const void* buffer, - size_t bytes_to_write, - size_t* bytes_written) { - int original_flags = fcntl(handle.fd, F_GETFL); - if (original_flags == -1 || - fcntl(handle.fd, F_SETFL, original_flags & (~O_NONBLOCK)) != 0) { - return false; - } - - ssize_t result = HANDLE_EINTR(write(handle.fd, buffer, bytes_to_write)); - - fcntl(handle.fd, F_SETFL, original_flags); - - if (result < 0) - return false; - - *bytes_written = result; - return true; -} - -bool BlockingRead(const embedder::PlatformHandle& handle, - void* buffer, - size_t buffer_size, - size_t* bytes_read) { - int original_flags = fcntl(handle.fd, F_GETFL); - if (original_flags == -1 || - fcntl(handle.fd, F_SETFL, original_flags & (~O_NONBLOCK)) != 0) { - return false; - } - - ssize_t result = HANDLE_EINTR(read(handle.fd, buffer, buffer_size)); - - fcntl(handle.fd, F_SETFL, original_flags); - - if (result < 0) - return false; - - *bytes_read = result; - return true; -} - -bool NonBlockingRead(const embedder::PlatformHandle& handle, - void* buffer, - size_t buffer_size, - size_t* bytes_read) { - ssize_t result = HANDLE_EINTR(read(handle.fd, buffer, buffer_size)); - - if (result < 0) { - if (errno != EAGAIN && errno != EWOULDBLOCK) - return false; - - *bytes_read = 0; - } else { - *bytes_read = result; - } - - return true; -} - -embedder::ScopedPlatformHandle PlatformHandleFromFILE(base::ScopedFILE fp) { - CHECK(fp); - int rv = dup(fileno(fp.get())); - PCHECK(rv != -1) << "dup"; - return embedder::ScopedPlatformHandle(embedder::PlatformHandle(rv)); -} - -base::ScopedFILE FILEFromPlatformHandle(embedder::ScopedPlatformHandle h, - const char* mode) { - CHECK(h.is_valid()); - base::ScopedFILE rv(fdopen(h.release().fd, mode)); - PCHECK(rv) << "fdopen"; - return rv.Pass(); -} - -base::FilePath GetFilePathForJSResource(const std::string& path) { - std::string binding_path = "gen/" + path + ".js"; - base::FilePath exe_dir; - PathService::Get(base::DIR_EXE, &exe_dir); - return exe_dir.AppendASCII(binding_path); -} - -} // namespace test -} // namespace mojo diff --git a/mojo/edk/test/test_utils_win.cc b/mojo/edk/test/test_utils_win.cc deleted file mode 100644 index 2387945..0000000 --- a/mojo/edk/test/test_utils_win.cc +++ /dev/null @@ -1,124 +0,0 @@ -// 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/edk/test/test_utils.h" - -#include <windows.h> -#include <fcntl.h> -#include <io.h> -#include <string.h> - -#include "base/base_paths.h" -#include "base/path_service.h" -#include "base/strings/string_util.h" - -namespace mojo { -namespace test { - -bool BlockingWrite(const embedder::PlatformHandle& handle, - const void* buffer, - size_t bytes_to_write, - size_t* bytes_written) { - OVERLAPPED overlapped = {0}; - DWORD bytes_written_dword = 0; - - if (!WriteFile(handle.handle, buffer, static_cast<DWORD>(bytes_to_write), - &bytes_written_dword, &overlapped)) { - if (GetLastError() != ERROR_IO_PENDING || - !GetOverlappedResult(handle.handle, &overlapped, &bytes_written_dword, - TRUE)) { - return false; - } - } - - *bytes_written = bytes_written_dword; - return true; -} - -bool BlockingRead(const embedder::PlatformHandle& handle, - void* buffer, - size_t buffer_size, - size_t* bytes_read) { - OVERLAPPED overlapped = {0}; - DWORD bytes_read_dword = 0; - - if (!ReadFile(handle.handle, buffer, static_cast<DWORD>(buffer_size), - &bytes_read_dword, &overlapped)) { - if (GetLastError() != ERROR_IO_PENDING || - !GetOverlappedResult(handle.handle, &overlapped, &bytes_read_dword, - TRUE)) { - return false; - } - } - - *bytes_read = bytes_read_dword; - return true; -} - -bool NonBlockingRead(const embedder::PlatformHandle& handle, - void* buffer, - size_t buffer_size, - size_t* bytes_read) { - OVERLAPPED overlapped = {0}; - DWORD bytes_read_dword = 0; - - if (!ReadFile(handle.handle, buffer, static_cast<DWORD>(buffer_size), - &bytes_read_dword, &overlapped)) { - if (GetLastError() != ERROR_IO_PENDING) - return false; - - CancelIo(handle.handle); - - if (!GetOverlappedResult(handle.handle, &overlapped, &bytes_read_dword, - TRUE)) { - *bytes_read = 0; - return true; - } - } - - *bytes_read = bytes_read_dword; - return true; -} - -embedder::ScopedPlatformHandle PlatformHandleFromFILE(base::ScopedFILE fp) { - CHECK(fp); - - HANDLE rv = INVALID_HANDLE_VALUE; - PCHECK(DuplicateHandle( - GetCurrentProcess(), - reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(fp.get()))), - GetCurrentProcess(), &rv, 0, TRUE, DUPLICATE_SAME_ACCESS)) - << "DuplicateHandle"; - return embedder::ScopedPlatformHandle(embedder::PlatformHandle(rv)); -} - -base::ScopedFILE FILEFromPlatformHandle(embedder::ScopedPlatformHandle h, - const char* mode) { - CHECK(h.is_valid()); - // Microsoft's documentation for |_open_osfhandle()| only discusses these - // flags (and |_O_WTEXT|). Hmmm. - int flags = 0; - if (strchr(mode, 'a')) - flags |= _O_APPEND; - if (strchr(mode, 'r')) - flags |= _O_RDONLY; - if (strchr(mode, 't')) - flags |= _O_TEXT; - base::ScopedFILE rv(_fdopen( - _open_osfhandle(reinterpret_cast<intptr_t>(h.release().handle), flags), - mode)); - PCHECK(rv) << "_fdopen"; - return rv.Pass(); -} - -base::FilePath GetFilePathForJSResource(const std::string& path) { - std::string binding_path = "gen/" + path + ".js"; - base::ReplaceChars(binding_path, "//", "\\", &binding_path); - base::FilePath exe_dir; - PathService::Get(base::DIR_EXE, &exe_dir); - return exe_dir.AppendASCII(binding_path); -} - -} // namespace test -} // namespace mojo |