diff options
Diffstat (limited to 'mojo/edk')
168 files changed, 0 insertions, 34153 deletions
diff --git a/mojo/edk/DEPS b/mojo/edk/DEPS deleted file mode 100644 index 77abb21..0000000 --- a/mojo/edk/DEPS +++ /dev/null @@ -1,14 +0,0 @@ -include_rules = [ - # This code is checked into the chromium repo so it's fine to depend on this. - "+base", - "+crypto", - "+build", - "+gin", - "+native_client/src/public", - "+testing", - "+third_party/ashmem", - "+v8", - - # internal includes. - "+mojo", -] diff --git a/mojo/edk/embedder/BUILD.gn b/mojo/edk/embedder/BUILD.gn deleted file mode 100644 index 8105bed..0000000 --- a/mojo/edk/embedder/BUILD.gn +++ /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. - -import("//build/config/nacl/config.gni") - -source_set("headers") { - sources = [ - "configuration.h", - "connection_params.h", - "embedder.h", - "embedder_internal.h", - "named_platform_channel_pair.h", - "named_platform_handle.h", - "named_platform_handle_utils.h", - "pending_process_connection.h", - "platform_channel_pair.h", - "platform_handle.h", - "platform_handle_utils.h", - "scoped_platform_handle.h", - ] - - public_deps = [ - "//base", - "//mojo/public/cpp/system", - ] -} - -source_set("embedder") { - # This isn't really a standalone target; it must be linked into the - # mojo_system_impl component. - visibility = [ - "//mojo/edk/system", - "//components/nacl:nacl", - ] - - sources = [ - "configuration.h", - "connection_params.cc", - "connection_params.h", - "embedder.cc", - "embedder.h", - "embedder_internal.h", - "entrypoints.cc", - "entrypoints.h", - "pending_process_connection.cc", - "scoped_ipc_support.cc", - "scoped_ipc_support.h", - - # 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" ] - - public_deps = [ - ":headers", - ":platform", - "//base", - "//mojo/public/cpp/system", - ] - - if (!is_nacl) { - deps = [ - "//crypto", - ] - } -} - -source_set("platform") { - # This isn't really a standalone target; it must be linked into the - # mojo_system_impl component. - visibility = [ - ":embedder", - "//mojo/edk/system", - ] - - sources = [ - "named_platform_channel_pair.h", - "named_platform_channel_pair_win.cc", - "named_platform_handle.h", - "named_platform_handle_utils.h", - "named_platform_handle_utils_win.cc", - "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.cc", - "platform_shared_buffer.h", - "scoped_platform_handle.h", - ] - if (!is_nacl) { - sources += [ "named_platform_handle_utils_posix.cc" ] - } - - defines = [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ] - - public_deps = [ - "//mojo/public/cpp/system", - ] - - deps = [ - "//base", - ] - - if (is_android) { - deps += [ "//third_party/ashmem" ] - } - - if (is_nacl && !is_nacl_nonsfi) { - sources -= [ "platform_channel_utils_posix.cc" ] - } -} - -source_set("embedder_unittests") { - testonly = true - - # TODO: Figure out why this visibility check fails on Android. - # visibility = [ "//mojo/edk/system:mojo_system_unittests" ] - - sources = [ - "embedder_unittest.cc", - "platform_channel_pair_posix_unittest.cc", - "platform_shared_buffer_unittest.cc", - ] - - deps = [ - "//base", - "//base/test:test_support", - "//mojo/edk/system", - "//mojo/edk/system:test_utils", - "//mojo/edk/test:test_support", - "//testing/gtest", - ] -} diff --git a/mojo/edk/embedder/README.md b/mojo/edk/embedder/README.md deleted file mode 100644 index fc53bec..0000000 --- a/mojo/edk/embedder/README.md +++ /dev/null @@ -1,346 +0,0 @@ -# ![Mojo Graphic](https://goo.gl/6CdlbH) Mojo Embedder Development Kit (EDK) -This document is a subset of the [Mojo documentation](/mojo). - -[TOC] - -## Overview - -The Mojo EDK is a (binary-unstable) API which enables a process to use Mojo both -internally and for IPC to other Mojo-embedding processes. - -Using any of the API surface in `//mojo/edk/embedder` requires (somewhat -confusingly) a direct dependency on the GN `//mojo/edk/system` target. Despite -this fact, you should never reference any of the headers in `mojo/edk/system` -directly, as everything there is considered to be an internal detail of the EDK. - -**NOTE:** Unless you are introducing a new binary entry point into the system -(*e.g.,* a new executable with a new `main()` definition), you probably don't -need to know anything about the EDK API. Most processes defined in the Chrome -repo today already fully initialize the EDK so that Mojo's other public APIs -"just work" out of the box. - -## Basic Initialization - -In order to use Mojo in a given process, it's necessary to call -`mojo::edk::Init` exactly once: - -``` -#include "mojo/edk/embedder/embedder.h" - -int main(int argc, char** argv) { - mojo::edk::Init(); - - // Now you can create message pipes, write messages, etc - - return 0; -} -``` - -As it happens though, Mojo is less useful without some kind of IPC support as -well, and that's a second initialization step. - -## IPC Initialization - -You also need to provide the system with a background TaskRunner on which it can -watch for inbound I/O from any of the various other processes you will later -connect to it. - -Here we'll just create a new background thread for IPC and let Mojo use that. -Note that in Chromium, we use the existing "IO thread" in the browser process -and content child processes. - -``` -#include "base/threading/thread.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/embedder/scoped_ipc_support.h" - -int main(int argc, char** argv) { - mojo::edk::Init(); - - base::Thread ipc_thread("ipc!"); - ipc_thread.StartWithOptions( - base::Thread::Options(base::MessageLoop::TYPE_IO)); - - // As long as this object is alive, all EDK API surface relevant to IPC - // connections is usable and message pipes which span a process boundary will - // continue to function. - mojo::edk::ScopedIPCSupport ipc_support( - ipc_thread.task_runner(), - mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN); - - return 0; -} -``` - -This process is now fully prepared to use Mojo IPC! - -Note that all existing process types in Chromium already perform this setup -very early during startup. - -## Connecting Two Processes - -Now suppose you're running a process which has initialized Mojo IPC, and you -want to launch another process which you know will also initialize Mojo IPC. -You want to be able to connect Mojo interfaces between these two processes. -Rejoice, because this section was written just for you. - -NOTE: For legacy reasons, some API terminology may refer to concepts of "parent" -and "child" as a relationship between processes being connected by Mojo. This -relationship is today completely orthogonal to any notion of process hierarchy -in the OS, and so use of these APIs is not constrained by an adherence to any -such hierarchy. - -Mojo requires you to bring your own OS pipe to the party, and it will do the -rest. It also provides a convenient mechanism for creating such pipes, known as -a `PlatformChannelPair`. - -You provide one end of this pipe to the EDK in the local process via -`PendingProcessConnection` - which can also be used to create cross-process -message pipes (see the next section) - and you're responsible for getting the -other end into the remote process. - -``` -#include "base/process/process_handle.h" -#include "base/threading/thread.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/embedder/pending_process_connection.h" -#include "mojo/edk/embedder/platform_channel_pair.h" -#include "mojo/edk/embedder/scoped_ipc_support.h" - -// You write this. It launches a new process, passing the pipe handle -// encapsulated by |channel| by any means possible (e.g. on Windows or POSIX -// you may inhert the file descriptor/HANDLE at launch and pass a commandline -// argument to indicate its numeric value). Returns the handle of the new -// process. -base::ProcessHandle LaunchCoolChildProcess( - mojo::edk::ScopedPlatformHandle channel); - -int main(int argc, char** argv) { - mojo::edk::Init(); - - base::Thread ipc_thread("ipc!"); - ipc_thread.StartWithOptions( - base::Thread::Options(base::MessageLoop::TYPE_IO)); - - mojo::edk::ScopedIPCSupport ipc_support( - ipc_thread.task_runner(), - mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN); - - // This is essentially always an OS pipe (domain socket pair, Windows named - // pipe, etc.) - mojo::edk::PlatformChannelPair channel; - - // This is a scoper which encapsulates the intent to connect to another - // process. It exists because process connection is inherently asynchronous, - // things may go wrong, and the lifetime of any associated resources is bound - // by the lifetime of this object regardless of success or failure. - mojo::edk::PendingProcessConnection child; - - base::ProcessHandle child_handle = - LaunchCoolChildProcess(channel.PassClientHandle()); - - // At this point it's safe for |child| to go out of scope and nothing will - // break. - child.Connect(child_handle, channel.PassServerHandle()); - - return 0; -} -``` - -The launched process code uses `SetParentPipeHandle` to get connected, and might -look something like: - -``` -#include "base/threading/thread.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/embedder/scoped_ipc_support.h" - -// You write this. It acquires the ScopedPlatformHandle that was passed by -// whomever launched this process (i.e. LaunchCoolChildProcess above). -mojo::edk::ScopedPlatformHandle GetChannelHandle(); - -int main(int argc, char** argv) { - mojo::edk::Init(); - - base::Thread ipc_thread("ipc!"); - ipc_thread.StartWithOptions( - base::Thread::Options(base::MessageLoop::TYPE_IO)); - - mojo::edk::ScopedIPCSupport ipc_support( - ipc_thread.task_runner(), - mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN); - - mojo::edk::SetParentPipeHandle(GetChannelHandle()); - - return 0; -} -``` - -Now you have IPC initialized between two processes. For some practical examples -of how this is done, you can dig into the various multiprocess tests in the -`mojo_system_unittests` test suite. - -## Bootstrapping Cross-Process Message Pipes - -Having internal Mojo IPC support initialized is pretty useless if you don't have -any message pipes spanning the process boundary. Fortunately, this is made -trivial by the EDK: `PendingProcessConnection` has a -`CreateMessagePipe` method which synthesizes a new solitary message pipe -endpoint for your immediate use, while also generating a magic token string that -can be exchanged for the other end of the pipe via -`mojo::edk::CreateChildMessagePipe`. - -The token exchange can be done by the same process (which is sometimes useful), -or by the process that is eventually connected via `Connect()` on that -`PendingProcessConnection`. This means that you can effectively pass message -pipes on the commandline by passing a token string. - -We can modify our existing sample code as follows: - -``` -#include "base/command_line.h" -#include "base/process/process_handle.h" -#include "base/threading/thread.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/embedder/pending_process_connection.h" -#include "mojo/edk/embedder/platform_channel_pair.h" -#include "mojo/edk/embedder/scoped_ipc_support.h" -#include "mojo/public/cpp/system/message_pipe.h" -#include "local/foo.mojom.h" // You provide this - -base::ProcessHandle LaunchCoolChildProcess( - const base::CommandLine& command_line, - mojo::edk::ScopedPlatformHandle channel); - -int main(int argc, char** argv) { - mojo::edk::Init(); - - base::Thread ipc_thread("ipc!"); - ipc_thread.StartWithOptions( - base::Thread::Options(base::MessageLoop::TYPE_IO)); - - mojo::edk::ScopedIPCSupport ipc_support( - ipc_thread.task_runner(), - mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN); - - mojo::edk::PlatformChannelPair channel; - - mojo::edk::PendingProcessConnection child; - - base::CommandLine command_line; // Assume this is appropriately initialized - - // Create a new message pipe with one end being retrievable in the new - // process. Note that it doesn't matter whether we call CreateMessagePipe() - // before or after Connect(), and we can create as many different pipes as - // we like. - std::string pipe_token; - mojo::ScopedMessagePipeHandle my_pipe = child.CreateMessagePipe(&pipe_token); - command_line.AppendSwitchASCII("primordial-pipe", pipe_token); - - base::ProcessHandle child_handle = - LaunchCoolChildProcess(command_line, channel.PassClientHandle()); - - child.Connect(child_handle, channel.PassServerHandle()); - - // We can start using our end of the pipe immediately. Here we assume the - // other end will eventually be bound to a local::mojom::Foo implementation, - // so we can start making calls on that interface. - // - // Note that this could even be done before the child process is launched and - // it would still work as expected. - local::mojom::FooPtr foo; - foo.Bind(local::mojom::FooPtrInfo(std::move(my_pipe), 0)); - foo->DoSomeStuff(42); - - return 0; -} -``` - -and for the launched process: - - -``` -#include "base/command_line.h" -#include "base/run_loop/run_loop.h" -#include "base/threading/thread.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/embedder/scoped_ipc_support.h" -#include "mojo/public/cpp/bindings/binding.h" -#include "mojo/public/cpp/system/message_pipe.h" -#include "local/foo.mojom.h" // You provide this - -mojo::edk::ScopedPlatformHandle GetChannelHandle(); - -class FooImpl : local::mojom::Foo { - public: - explicit FooImpl(local::mojom::FooRequest request) - : binding_(this, std::move(request)) {} - ~FooImpl() override {} - - void DoSomeStuff(int32_t n) override { - // ... - } - - private: - mojo::Binding<local::mojom::Foo> binding_; - - DISALLOW_COPY_AND_ASSIGN(FooImpl); -}; - -int main(int argc, char** argv) { - base::CommandLine::Init(argc, argv); - - mojo::edk::Init(); - - base::Thread ipc_thread("ipc!"); - ipc_thread.StartWithOptions( - base::Thread::Options(base::MessageLoop::TYPE_IO)); - - mojo::edk::ScopedIPCSupport ipc_support( - ipc_thread.task_runner(), - mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN); - - mojo::edk::SetParentPipeHandle(GetChannelHandle()); - - mojo::ScopedMessagePipeHandle my_pipe = mojo::edk::CreateChildMessagePipe( - base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - "primordial-pipe")); - - local::mojom::FooRequest foo_request; - foo_request.Bind(std::move(my_pipe)); - FooImpl impl(std::move(foo_request)); - - // Run forever! - base::RunLoop().Run(); - - return 0; -} -``` - -Note that the above samples assume an interface definition in -`//local/test.mojom` which would look something like: - -``` -module local.mojom; - -interface Foo { - DoSomeStuff(int32 n); -}; -``` - -Once you've bootstrapped your process connection with a real mojom interface, -you can avoid any further mucking around with EDK APIs or raw message pipe -handles, as everything beyond this point - including the passing of other -interface pipes - can be handled eloquently using -[public bindings APIs](/mojo#High-Level-Bindings-APIs). - -## Setting System Properties - -The public Mojo C System API exposes a -[**`MojoGetProperty`**](/mojo/public/c/system#MojoGetProperty) function for -querying global, embedder-defined property values. These can be set by calling: - -``` -mojo::edk::SetProperty(MojoPropertyType type, const void* value) -``` - diff --git a/mojo/edk/embedder/configuration.h b/mojo/edk/embedder/configuration.h deleted file mode 100644 index 1990fb1..0000000 --- a/mojo/edk/embedder/configuration.h +++ /dev/null @@ -1,65 +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> -#include <stdint.h> - -namespace mojo { -namespace edk { - -// 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; - - // 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 edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_CONFIGURATION_H_ diff --git a/mojo/edk/embedder/connection_params.cc b/mojo/edk/embedder/connection_params.cc deleted file mode 100644 index 9b7ec54..0000000 --- a/mojo/edk/embedder/connection_params.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/embedder/connection_params.h" - -#include <utility> - -namespace mojo { -namespace edk { - -ConnectionParams::ConnectionParams(ScopedPlatformHandle channel) - : channel_(std::move(channel)) {} - -ConnectionParams::ConnectionParams(ConnectionParams&& param) - : channel_(std::move(param.channel_)) {} - -ConnectionParams& ConnectionParams::operator=(ConnectionParams&& param) { - channel_ = std::move(param.channel_); - return *this; -} - -ScopedPlatformHandle ConnectionParams::TakeChannelHandle() { - return std::move(channel_); -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/embedder/connection_params.h b/mojo/edk/embedder/connection_params.h deleted file mode 100644 index 25ffdde..0000000 --- a/mojo/edk/embedder/connection_params.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_EMBEDDER_CONNECTION_PARAMS_H_ -#define MOJO_EDK_EMBEDDER_CONNECTION_PARAMS_H_ - -#include "base/macros.h" -#include "build/build_config.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace edk { - -class MOJO_SYSTEM_IMPL_EXPORT ConnectionParams { - public: - explicit ConnectionParams(ScopedPlatformHandle channel); - - ConnectionParams(ConnectionParams&& param); - ConnectionParams& operator=(ConnectionParams&& param); - - ScopedPlatformHandle TakeChannelHandle(); - - private: - ScopedPlatformHandle channel_; - - DISALLOW_COPY_AND_ASSIGN(ConnectionParams); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_CONNECTION_PARAMS_H_ diff --git a/mojo/edk/embedder/embedder.cc b/mojo/edk/embedder/embedder.cc deleted file mode 100644 index 0fdda5c..0000000 --- a/mojo/edk/embedder/embedder.cc +++ /dev/null @@ -1,158 +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 <stdint.h> -#include <utility> - -#include "base/bind.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/rand_util.h" -#include "base/strings/string_number_conversions.h" -#include "base/task_runner.h" -#include "base/threading/thread_task_runner_handle.h" -#include "mojo/edk/embedder/embedder_internal.h" -#include "mojo/edk/embedder/entrypoints.h" -#include "mojo/edk/embedder/platform_channel_pair.h" -#include "mojo/edk/system/core.h" -#include "mojo/edk/system/node_controller.h" - -#if !defined(OS_NACL) -#include "crypto/random.h" -#endif - -namespace mojo { -namespace edk { - -class Core; -class PlatformSupport; - -namespace internal { - -Core* g_core; - -Core* GetCore() { return g_core; } - -} // namespace internal - -void SetMaxMessageSize(size_t bytes) { -} - -void SetParentPipeHandle(ScopedPlatformHandle pipe) { - CHECK(internal::g_core); - internal::g_core->InitChild(ConnectionParams(std::move(pipe))); -} - -void SetParentPipeHandleFromCommandLine() { - ScopedPlatformHandle platform_channel = - PlatformChannelPair::PassClientHandleFromParentProcess( - *base::CommandLine::ForCurrentProcess()); - CHECK(platform_channel.is_valid()); - SetParentPipeHandle(std::move(platform_channel)); -} - -ScopedMessagePipeHandle ConnectToPeerProcess(ScopedPlatformHandle pipe) { - return ConnectToPeerProcess(std::move(pipe), GenerateRandomToken()); -} - -ScopedMessagePipeHandle ConnectToPeerProcess(ScopedPlatformHandle pipe, - const std::string& peer_token) { - DCHECK(pipe.is_valid()); - DCHECK(!peer_token.empty()); - return internal::g_core->ConnectToPeerProcess(std::move(pipe), peer_token); -} - -void ClosePeerConnection(const std::string& peer_token) { - return internal::g_core->ClosePeerConnection(peer_token); -} - -void Init() { - MojoSystemThunks thunks = MakeSystemThunks(); - size_t expected_size = MojoEmbedderSetSystemThunks(&thunks); - DCHECK_EQ(expected_size, sizeof(thunks)); - - internal::g_core = new Core(); -} - -void SetDefaultProcessErrorCallback(const ProcessErrorCallback& callback) { - internal::g_core->SetDefaultProcessErrorCallback(callback); -} - -MojoResult CreatePlatformHandleWrapper( - ScopedPlatformHandle platform_handle, - MojoHandle* platform_handle_wrapper_handle) { - return internal::g_core->CreatePlatformHandleWrapper( - std::move(platform_handle), platform_handle_wrapper_handle); -} - -MojoResult PassWrappedPlatformHandle(MojoHandle platform_handle_wrapper_handle, - ScopedPlatformHandle* platform_handle) { - return internal::g_core->PassWrappedPlatformHandle( - platform_handle_wrapper_handle, platform_handle); -} - -MojoResult CreateSharedBufferWrapper( - base::SharedMemoryHandle shared_memory_handle, - size_t num_bytes, - bool read_only, - MojoHandle* mojo_wrapper_handle) { - return internal::g_core->CreateSharedBufferWrapper( - shared_memory_handle, num_bytes, read_only, mojo_wrapper_handle); -} - -MojoResult PassSharedMemoryHandle( - MojoHandle mojo_handle, - base::SharedMemoryHandle* shared_memory_handle, - size_t* num_bytes, - bool* read_only) { - return internal::g_core->PassSharedMemoryHandle( - mojo_handle, shared_memory_handle, num_bytes, read_only); -} - -void InitIPCSupport(scoped_refptr<base::TaskRunner> io_thread_task_runner) { - CHECK(internal::g_core); - internal::g_core->SetIOTaskRunner(io_thread_task_runner); -} - -scoped_refptr<base::TaskRunner> GetIOTaskRunner() { - return internal::g_core->GetNodeController()->io_task_runner(); -} - -void ShutdownIPCSupport(const base::Closure& callback) { - CHECK(internal::g_core); - internal::g_core->RequestShutdown(callback); -} - -#if defined(OS_MACOSX) && !defined(OS_IOS) -void SetMachPortProvider(base::PortProvider* port_provider) { - DCHECK(port_provider); - internal::g_core->SetMachPortProvider(port_provider); -} -#endif - -ScopedMessagePipeHandle CreateChildMessagePipe(const std::string& token) { - return internal::g_core->CreateChildMessagePipe(token); -} - -std::string GenerateRandomToken() { - char random_bytes[16]; -#if defined(OS_NACL) - // Not secure. For NaCl only! - base::RandBytes(random_bytes, 16); -#else - crypto::RandBytes(random_bytes, 16); -#endif - return base::HexEncode(random_bytes, 16); -} - -MojoResult SetProperty(MojoPropertyType type, const void* value) { - CHECK(internal::g_core); - return internal::g_core->SetProperty(type, value); -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/embedder/embedder.h b/mojo/edk/embedder/embedder.h deleted file mode 100644 index 97258e5..0000000 --- a/mojo/edk/embedder/embedder.h +++ /dev/null @@ -1,173 +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 <stddef.h> - -#include <memory> -#include <string> - -#include "base/callback.h" -#include "base/command_line.h" -#include "base/memory/ref_counted.h" -#include "base/memory/shared_memory_handle.h" -#include "base/process/process_handle.h" -#include "base/task_runner.h" -#include "mojo/edk/embedder/pending_process_connection.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 base { -class PortProvider; -} - -namespace mojo { -namespace edk { - -// Basic configuration/initialization ------------------------------------------ - -// |Init()| sets up the basic Mojo system environment, making the |Mojo...()| -// functions available and functional. This is never shut down (except in tests -// -- see test_embedder.h). - -// Allows changing the default max message size. Must be called before Init. -MOJO_SYSTEM_IMPL_EXPORT void SetMaxMessageSize(size_t bytes); - -// Should be called as early as possible in a child process with a handle to the -// other end of a pipe provided in the parent to -// PendingProcessConnection::Connect. -MOJO_SYSTEM_IMPL_EXPORT void SetParentPipeHandle(ScopedPlatformHandle pipe); - -// Same as above but extracts the pipe handle from the command line. See -// PlatformChannelPair for details. -MOJO_SYSTEM_IMPL_EXPORT void SetParentPipeHandleFromCommandLine(); - -// Called to connect to a peer process. This should be called only if there -// is no common ancestor for the processes involved within this mojo system. -// Both processes must call this function, each passing one end of a platform -// channel. This returns one end of a message pipe to each process. -MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle -ConnectToPeerProcess(ScopedPlatformHandle pipe); - -// Called to connect to a peer process. This should be called only if there -// is no common ancestor for the processes involved within this mojo system. -// Both processes must call this function, each passing one end of a platform -// channel. This returns one end of a message pipe to each process. |peer_token| -// may be passed to ClosePeerConnection() to close the connection. -MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle -ConnectToPeerProcess(ScopedPlatformHandle pipe, const std::string& peer_token); - -// Closes a connection to a peer process created by ConnectToPeerProcess() -// where the same |peer_token| was used. -MOJO_SYSTEM_IMPL_EXPORT void ClosePeerConnection(const std::string& peer_token); - -// Must be called first, or just after setting configuration parameters, to -// initialize the (global, singleton) system. -MOJO_SYSTEM_IMPL_EXPORT void Init(); - -// Sets a default callback to invoke when an internal error is reported but -// cannot be associated with a specific child process. -MOJO_SYSTEM_IMPL_EXPORT void SetDefaultProcessErrorCallback( - const ProcessErrorCallback& callback); - -// Basic functions ------------------------------------------------------------- - -// The functions in this section are available once |Init()| has been called. - -// 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| is closed -// on success. -MOJO_SYSTEM_IMPL_EXPORT MojoResult -PassWrappedPlatformHandle(MojoHandle platform_handle_wrapper_handle, - ScopedPlatformHandle* platform_handle); - -// Creates a |MojoHandle| that wraps the given |SharedMemoryHandle| (taking -// ownership of it). |num_bytes| is the size of the shared memory object, and -// |read_only| is whether the handle is a read-only handle to shared memory. -// This |MojoHandle| is a Mojo shared buffer and can be manipulated using the -// shared buffer functions and transferred over a message pipe. -MOJO_SYSTEM_IMPL_EXPORT MojoResult -CreateSharedBufferWrapper(base::SharedMemoryHandle shared_memory_handle, - size_t num_bytes, - bool read_only, - MojoHandle* mojo_wrapper_handle); - -// Retrieves the underlying |SharedMemoryHandle| from a shared buffer -// |MojoHandle| and closes the handle. If successful, |num_bytes| will contain -// the size of the shared memory buffer and |read_only| will contain whether the -// buffer handle is read-only. Both |num_bytes| and |read_only| may be null. -// Note: The value of |shared_memory_handle| may be -// base::SharedMemory::NULLHandle(), even if this function returns success. -// Callers should perform appropriate checks. -MOJO_SYSTEM_IMPL_EXPORT MojoResult -PassSharedMemoryHandle(MojoHandle mojo_handle, - base::SharedMemoryHandle* shared_memory_handle, - size_t* num_bytes, - bool* read_only); - -// Initialialization/shutdown for interprocess communication (IPC) ------------- - -// |InitIPCSupport()| sets up the subsystem for interprocess communication, -// making the IPC functions (in the following section) available and functional. -// (This may only be done after |Init()|.) -// -// This subsystem may be shut down using |ShutdownIPCSupport()|. None of the IPC -// functions may be called after this is called. -// -// |io_thread_task_runner| should live at least until |ShutdownIPCSupport()|'s -// callback has been run. -MOJO_SYSTEM_IMPL_EXPORT void InitIPCSupport( - scoped_refptr<base::TaskRunner> io_thread_task_runner); - -// Retrieves the TaskRunner used for IPC I/O, as set by InitIPCSupport. -MOJO_SYSTEM_IMPL_EXPORT scoped_refptr<base::TaskRunner> GetIOTaskRunner(); - -// Shuts down the subsystem initialized by |InitIPCSupport()|. It be called from -// any thread and will attempt to complete shutdown on the I/O thread with which -// the system was initialized. Upon completion, |callback| is invoked on an -// arbitrary thread. -MOJO_SYSTEM_IMPL_EXPORT void ShutdownIPCSupport(const base::Closure& callback); - -#if defined(OS_MACOSX) && !defined(OS_IOS) -// Set the |base::PortProvider| for this process. Can be called on any thread, -// but must be set in the root process before any Mach ports can be transferred. -MOJO_SYSTEM_IMPL_EXPORT void SetMachPortProvider( - base::PortProvider* port_provider); -#endif - -// Creates a message pipe from a token in a child process. This token must have -// been acquired by a corresponding call to -// PendingProcessConnection::CreateMessagePipe. -MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle -CreateChildMessagePipe(const std::string& token); - -// Generates a random ASCII token string for use with various APIs that expect -// a globally unique token string. -MOJO_SYSTEM_IMPL_EXPORT std::string GenerateRandomToken(); - -// Sets system properties that can be read by the MojoGetProperty() API. See the -// documentation for MojoPropertyType for supported property types and their -// corresponding value type. -// -// Default property values: -// |MOJO_PROPERTY_TYPE_SYNC_CALL_ALLOWED| - true -MOJO_SYSTEM_IMPL_EXPORT MojoResult SetProperty(MojoPropertyType type, - const void* value); - -} // namespace edk -} // 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 7deeca1..0000000 --- a/mojo/edk/embedder/embedder_internal.h +++ /dev/null @@ -1,44 +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> - -#include "mojo/edk/system/system_impl_export.h" - -namespace base { -class TaskRunner; -} - -namespace mojo { - -namespace edk { - -class Broker; -class Core; -class ProcessDelegate; - -namespace internal { - -// Instance of |Broker| to use. -extern Broker* g_broker; - -// Instance of |Core| used by the system functions (|Mojo...()|). -extern MOJO_SYSTEM_IMPL_EXPORT Core* g_core; -extern base::TaskRunner* g_delegate_thread_task_runner; -extern ProcessDelegate* g_process_delegate; - -} // namespace internal - -} // namepace edk - -} // 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 388b45c..0000000 --- a/mojo/edk/embedder/embedder_unittest.cc +++ /dev/null @@ -1,603 +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 <stddef.h> -#include <stdint.h> -#include <string.h> - -#include <utility> - -#include "base/base_paths.h" -#include "base/bind.h" -#include "base/command_line.h" -#include "base/files/file.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/memory/shared_memory.h" -#include "base/message_loop/message_loop.h" -#include "base/path_service.h" -#include "base/process/process_handle.h" -#include "base/run_loop.h" -#include "base/synchronization/waitable_event.h" -#include "base/test/test_timeouts.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/embedder/named_platform_handle.h" -#include "mojo/edk/embedder/named_platform_handle_utils.h" -#include "mojo/edk/embedder/pending_process_connection.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/mojo_test_base.h" -#include "mojo/public/c/system/core.h" -#include "mojo/public/cpp/system/handle.h" -#include "mojo/public/cpp/system/message_pipe.h" -#include "mojo/public/cpp/system/wait.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace edk { -namespace { - -// The multiprocess tests that use these don't compile on iOS. -#if !defined(OS_IOS) -const char kHelloWorld[] = "hello world"; -const char kByeWorld[] = "bye world"; -#endif - -using EmbedderTest = test::MojoTestBase; - -TEST_F(EmbedderTest, ChannelBasic) { - MojoHandle server_mp, client_mp; - CreateMessagePipe(&server_mp, &client_mp); - - const std::string kHello = "hello"; - - // We can write to a message pipe handle immediately. - WriteMessage(server_mp, kHello); - EXPECT_EQ(kHello, ReadMessage(client_mp)); - - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(server_mp)); - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(client_mp)); -} - -// Verifies that a MP with pending messages to be written can be sent and the -// pending messages aren't dropped. -TEST_F(EmbedderTest, SendMessagePipeWithWriteQueue) { - MojoHandle server_mp, client_mp; - CreateMessagePipe(&server_mp, &client_mp); - - MojoHandle server_mp2, client_mp2; - CreateMessagePipe(&server_mp2, &client_mp2); - - static const size_t kNumMessages = 1001; - for (size_t i = 1; i <= kNumMessages; i++) - WriteMessage(client_mp2, std::string(i, 'A' + (i % 26))); - - // Now send client2. - WriteMessageWithHandles(server_mp, "hey", &client_mp2, 1); - client_mp2 = MOJO_HANDLE_INVALID; - - // Read client2 just so we can close it later. - EXPECT_EQ("hey", ReadMessageWithHandles(client_mp, &client_mp2, 1)); - EXPECT_NE(MOJO_HANDLE_INVALID, client_mp2); - - // Now verify that all the messages that were written were sent correctly. - for (size_t i = 1; i <= kNumMessages; i++) - ASSERT_EQ(std::string(i, 'A' + (i % 26)), ReadMessage(server_mp2)); - - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(server_mp2)); - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(client_mp2)); - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(server_mp)); - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(client_mp)); -} - -TEST_F(EmbedderTest, ChannelsHandlePassing) { - MojoHandle server_mp, client_mp; - CreateMessagePipe(&server_mp, &client_mp); - EXPECT_NE(server_mp, MOJO_HANDLE_INVALID); - EXPECT_NE(client_mp, MOJO_HANDLE_INVALID); - - MojoHandle h0, h1; - CreateMessagePipe(&h0, &h1); - - // Write a message to |h0| (attaching nothing). - const std::string kHello = "hello"; - WriteMessage(h0, kHello); - - // Write one message to |server_mp|, attaching |h1|. - const std::string kWorld = "world!!!"; - WriteMessageWithHandles(server_mp, kWorld, &h1, 1); - h1 = MOJO_HANDLE_INVALID; - - // Write another message to |h0|. - const std::string kFoo = "foo"; - WriteMessage(h0, kFoo); - - // Wait for |client_mp| to become readable and read a message from it. - EXPECT_EQ(kWorld, ReadMessageWithHandles(client_mp, &h1, 1)); - EXPECT_NE(h1, MOJO_HANDLE_INVALID); - - // Wait for |h1| to become readable and read a message from it. - EXPECT_EQ(kHello, ReadMessage(h1)); - - // Wait for |h1| to become readable (again) and read its second message. - EXPECT_EQ(kFoo, ReadMessage(h1)); - - // Write a message to |h1|. - const std::string kBarBaz = "barbaz"; - WriteMessage(h1, kBarBaz); - - // Wait for |h0| to become readable and read a message from it. - EXPECT_EQ(kBarBaz, ReadMessage(h0)); - - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(server_mp)); - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(client_mp)); - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(h0)); - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(h1)); -} - -TEST_F(EmbedderTest, PipeSetup) { - // Ensures that a pending process connection's message pipe can be claimed by - // the host process itself. - PendingProcessConnection process; - std::string pipe_token; - ScopedMessagePipeHandle parent_mp = process.CreateMessagePipe(&pipe_token); - ScopedMessagePipeHandle child_mp = CreateChildMessagePipe(pipe_token); - - const std::string kHello = "hello"; - WriteMessage(parent_mp.get().value(), kHello); - - EXPECT_EQ(kHello, ReadMessage(child_mp.get().value())); -} - -TEST_F(EmbedderTest, PipeSetup_LaunchDeath) { - PlatformChannelPair pair; - - PendingProcessConnection process; - std::string pipe_token; - ScopedMessagePipeHandle parent_mp = process.CreateMessagePipe(&pipe_token); - process.Connect(base::GetCurrentProcessHandle(), - ConnectionParams(pair.PassServerHandle())); - - // Close the remote end, simulating child death before the child connects to - // the reserved port. - ignore_result(pair.PassClientHandle()); - - EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(parent_mp.get().value(), - MOJO_HANDLE_SIGNAL_PEER_CLOSED)); -} - -TEST_F(EmbedderTest, PipeSetup_LaunchFailure) { - PlatformChannelPair pair; - - auto process = base::MakeUnique<PendingProcessConnection>(); - std::string pipe_token; - ScopedMessagePipeHandle parent_mp = process->CreateMessagePipe(&pipe_token); - - // Ensure that if a PendingProcessConnection goes away before Connect() is - // called, any message pipes associated with it detect peer closure. - process.reset(); - - EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(parent_mp.get().value(), - MOJO_HANDLE_SIGNAL_PEER_CLOSED)); -} - -// 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_IOS) - -TEST_F(EmbedderTest, MultiprocessChannels) { - RUN_CHILD_ON_PIPE(MultiprocessChannelsClient, server_mp) - // 1. Write a message to |server_mp| (attaching nothing). - WriteMessage(server_mp, "hello"); - - // 2. Read a message from |server_mp|. - EXPECT_EQ("world!", ReadMessage(server_mp)); - - // 3. Create a new message pipe (endpoints |mp0| and |mp1|). - MojoHandle mp0, mp1; - CreateMessagePipe(&mp0, &mp1); - - // 4. Write something to |mp0|. - WriteMessage(mp0, "FOO"); - - // 5. Write a message to |server_mp|, attaching |mp1|. - WriteMessageWithHandles(server_mp, "Bar", &mp1, 1); - mp1 = MOJO_HANDLE_INVALID; - - // 6. Read a message from |mp0|, which should have |mp2| attached. - MojoHandle mp2 = MOJO_HANDLE_INVALID; - EXPECT_EQ("quux", ReadMessageWithHandles(mp0, &mp2, 1)); - - // 7. Read a message from |mp2|. - EXPECT_EQ("baz", ReadMessage(mp2)); - - // 8. Close |mp0|. - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(mp0)); - - // 9. Tell the client to quit. - WriteMessage(server_mp, "quit"); - - // 10. Wait on |mp2| (which should eventually fail) and then close it. - MojoHandleSignalsState state; - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - WaitForSignals(mp2, MOJO_HANDLE_SIGNAL_READABLE, &state)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals); - - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(mp2)); - END_CHILD() -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MultiprocessChannelsClient, EmbedderTest, - client_mp) { - // 1. Read the first message from |client_mp|. - EXPECT_EQ("hello", ReadMessage(client_mp)); - - // 2. Write a message to |client_mp| (attaching nothing). - WriteMessage(client_mp, "world!"); - - // 4. Read a message from |client_mp|, which should have |mp1| attached. - MojoHandle mp1; - EXPECT_EQ("Bar", ReadMessageWithHandles(client_mp, &mp1, 1)); - - // 5. Create a new message pipe (endpoints |mp2| and |mp3|). - MojoHandle mp2, mp3; - CreateMessagePipe(&mp2, &mp3); - - // 6. Write a message to |mp3|. - WriteMessage(mp3, "baz"); - - // 7. Close |mp3|. - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(mp3)); - - // 8. Write a message to |mp1|, attaching |mp2|. - WriteMessageWithHandles(mp1, "quux", &mp2, 1); - mp2 = MOJO_HANDLE_INVALID; - - // 9. Read a message from |mp1|. - EXPECT_EQ("FOO", ReadMessage(mp1)); - - EXPECT_EQ("quit", ReadMessage(client_mp)); - - // 10. Wait on |mp1| (which should eventually fail) and then close it. - MojoHandleSignalsState state; - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - WaitForSignals(mp1, MOJO_HANDLE_SIGNAL_READABLE, &state)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals); - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(mp1)); -} - -TEST_F(EmbedderTest, MultiprocessBaseSharedMemory) { - RUN_CHILD_ON_PIPE(MultiprocessSharedMemoryClient, server_mp) - // 1. Create a base::SharedMemory object and create a mojo shared buffer - // from it. - base::SharedMemoryCreateOptions options; - options.size = 123; - base::SharedMemory shared_memory; - ASSERT_TRUE(shared_memory.Create(options)); - base::SharedMemoryHandle shm_handle = base::SharedMemory::DuplicateHandle( - shared_memory.handle()); - MojoHandle sb1; - ASSERT_EQ(MOJO_RESULT_OK, - CreateSharedBufferWrapper(shm_handle, 123, false, &sb1)); - - // 2. Map |sb1| and write something into it. - char* buffer = nullptr; - ASSERT_EQ(MOJO_RESULT_OK, - MojoMapBuffer(sb1, 0, 123, reinterpret_cast<void**>(&buffer), 0)); - ASSERT_TRUE(buffer); - memcpy(buffer, kHelloWorld, sizeof(kHelloWorld)); - - // 3. Duplicate |sb1| into |sb2| and pass to |server_mp|. - MojoHandle sb2 = MOJO_HANDLE_INVALID; - EXPECT_EQ(MOJO_RESULT_OK, MojoDuplicateBufferHandle(sb1, 0, &sb2)); - EXPECT_NE(MOJO_HANDLE_INVALID, sb2); - WriteMessageWithHandles(server_mp, "hello", &sb2, 1); - - // 4. Read a message from |server_mp|. - EXPECT_EQ("bye", ReadMessage(server_mp)); - - // 5. Expect that the contents of the shared buffer have changed. - EXPECT_EQ(kByeWorld, std::string(buffer)); - - // 6. Map the original base::SharedMemory and expect it contains the - // expected value. - ASSERT_TRUE(shared_memory.Map(123)); - EXPECT_EQ(kByeWorld, - std::string(static_cast<char*>(shared_memory.memory()))); - - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(sb1)); - END_CHILD() -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MultiprocessSharedMemoryClient, EmbedderTest, - client_mp) { - // 1. Read the first message from |client_mp|, which should have |sb1| which - // should be a shared buffer handle. - MojoHandle sb1; - EXPECT_EQ("hello", ReadMessageWithHandles(client_mp, &sb1, 1)); - - // 2. Map |sb1|. - char* buffer = nullptr; - ASSERT_EQ(MOJO_RESULT_OK, - MojoMapBuffer(sb1, 0, 123, reinterpret_cast<void**>(&buffer), 0)); - ASSERT_TRUE(buffer); - - // 3. Ensure |buffer| contains the values we expect. - EXPECT_EQ(kHelloWorld, std::string(buffer)); - - // 4. Write into |buffer| and send a message back. - memcpy(buffer, kByeWorld, sizeof(kByeWorld)); - WriteMessage(client_mp, "bye"); - - // 5. Extract the shared memory handle and ensure we can map it and read the - // contents. - base::SharedMemoryHandle shm_handle; - ASSERT_EQ(MOJO_RESULT_OK, - PassSharedMemoryHandle(sb1, &shm_handle, nullptr, nullptr)); - base::SharedMemory shared_memory(shm_handle, false); - ASSERT_TRUE(shared_memory.Map(123)); - EXPECT_NE(buffer, shared_memory.memory()); - EXPECT_EQ(kByeWorld, std::string(static_cast<char*>(shared_memory.memory()))); - - // 6. Close |sb1|. Should fail because |PassSharedMemoryHandle()| should have - // closed the handle. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(sb1)); -} - -#if defined(OS_MACOSX) && !defined(OS_IOS) -TEST_F(EmbedderTest, MultiprocessMachSharedMemory) { - RUN_CHILD_ON_PIPE(MultiprocessSharedMemoryClient, server_mp) - // 1. Create a Mach base::SharedMemory object and create a mojo shared - // buffer from it. - base::SharedMemoryCreateOptions options; - options.size = 123; - base::SharedMemory shared_memory; - ASSERT_TRUE(shared_memory.Create(options)); - base::SharedMemoryHandle shm_handle = base::SharedMemory::DuplicateHandle( - shared_memory.handle()); - MojoHandle sb1; - ASSERT_EQ(MOJO_RESULT_OK, - CreateSharedBufferWrapper(shm_handle, 123, false, &sb1)); - - // 2. Map |sb1| and write something into it. - char* buffer = nullptr; - ASSERT_EQ(MOJO_RESULT_OK, - MojoMapBuffer(sb1, 0, 123, reinterpret_cast<void**>(&buffer), 0)); - ASSERT_TRUE(buffer); - memcpy(buffer, kHelloWorld, sizeof(kHelloWorld)); - - // 3. Duplicate |sb1| into |sb2| and pass to |server_mp|. - MojoHandle sb2 = MOJO_HANDLE_INVALID; - EXPECT_EQ(MOJO_RESULT_OK, MojoDuplicateBufferHandle(sb1, 0, &sb2)); - EXPECT_NE(MOJO_HANDLE_INVALID, sb2); - WriteMessageWithHandles(server_mp, "hello", &sb2, 1); - - // 4. Read a message from |server_mp|. - EXPECT_EQ("bye", ReadMessage(server_mp)); - - // 5. Expect that the contents of the shared buffer have changed. - EXPECT_EQ(kByeWorld, std::string(buffer)); - - // 6. Map the original base::SharedMemory and expect it contains the - // expected value. - ASSERT_TRUE(shared_memory.Map(123)); - EXPECT_EQ(kByeWorld, - std::string(static_cast<char*>(shared_memory.memory()))); - - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(sb1)); - END_CHILD() -} - -enum class HandleType { - POSIX, - MACH, - MACH_NULL, -}; - -const HandleType kTestHandleTypes[] = { - HandleType::MACH, - HandleType::MACH_NULL, - HandleType::POSIX, - HandleType::POSIX, - HandleType::MACH, -}; - -// Test that we can mix file descriptors and mach port handles. -TEST_F(EmbedderTest, MultiprocessMixMachAndFds) { - const size_t kShmSize = 1234; - RUN_CHILD_ON_PIPE(MultiprocessMixMachAndFdsClient, server_mp) - // 1. Create fds or Mach objects and mojo handles from them. - MojoHandle platform_handles[arraysize(kTestHandleTypes)]; - for (size_t i = 0; i < arraysize(kTestHandleTypes); i++) { - const auto type = kTestHandleTypes[i]; - ScopedPlatformHandle scoped_handle; - if (type == HandleType::POSIX) { - // The easiest source of fds is opening /dev/null. - base::File file(base::FilePath("/dev/null"), - base::File::FLAG_OPEN | base::File::FLAG_WRITE); - ASSERT_TRUE(file.IsValid()); - scoped_handle.reset(PlatformHandle(file.TakePlatformFile())); - EXPECT_EQ(PlatformHandle::Type::POSIX, scoped_handle.get().type); - } else if (type == HandleType::MACH_NULL) { - scoped_handle.reset(PlatformHandle( - static_cast<mach_port_t>(MACH_PORT_NULL))); - EXPECT_EQ(PlatformHandle::Type::MACH, scoped_handle.get().type); - } else { - base::SharedMemoryCreateOptions options; - options.size = kShmSize; - base::SharedMemory shared_memory; - ASSERT_TRUE(shared_memory.Create(options)); - base::SharedMemoryHandle shm_handle = - base::SharedMemory::DuplicateHandle(shared_memory.handle()); - scoped_handle.reset(PlatformHandle(shm_handle.GetMemoryObject())); - EXPECT_EQ(PlatformHandle::Type::MACH, scoped_handle.get().type); - } - ASSERT_EQ(MOJO_RESULT_OK, CreatePlatformHandleWrapper( - std::move(scoped_handle), platform_handles + i)); - } - - // 2. Send all the handles to the child. - WriteMessageWithHandles(server_mp, "hello", platform_handles, - arraysize(kTestHandleTypes)); - - // 3. Read a message from |server_mp|. - EXPECT_EQ("bye", ReadMessage(server_mp)); - END_CHILD() -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MultiprocessMixMachAndFdsClient, EmbedderTest, - client_mp) { - const int kNumHandles = arraysize(kTestHandleTypes); - MojoHandle platform_handles[kNumHandles]; - - // 1. Read from |client_mp|, which should have a message containing - // |kNumHandles| handles. - EXPECT_EQ("hello", - ReadMessageWithHandles(client_mp, platform_handles, kNumHandles)); - - // 2. Extract each handle, and verify the type. - for (int i = 0; i < kNumHandles; i++) { - const auto type = kTestHandleTypes[i]; - ScopedPlatformHandle scoped_handle; - ASSERT_EQ(MOJO_RESULT_OK, - PassWrappedPlatformHandle(platform_handles[i], &scoped_handle)); - if (type == HandleType::POSIX) { - EXPECT_NE(0, scoped_handle.get().handle); - EXPECT_EQ(PlatformHandle::Type::POSIX, scoped_handle.get().type); - } else if (type == HandleType::MACH_NULL) { - EXPECT_EQ(static_cast<mach_port_t>(MACH_PORT_NULL), - scoped_handle.get().port); - EXPECT_EQ(PlatformHandle::Type::MACH, scoped_handle.get().type); - } else { - EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL), - scoped_handle.get().port); - EXPECT_EQ(PlatformHandle::Type::MACH, scoped_handle.get().type); - } - } - - // 3. Say bye! - WriteMessage(client_mp, "bye"); -} - -#endif // defined(OS_MACOSX) && !defined(OS_IOS) - -// TODO(vtl): Test immediate write & close. -// TODO(vtl): Test broken-connection cases. - -#endif // !defined(OS_IOS) - -NamedPlatformHandle GenerateChannelName() { -#if defined(OS_POSIX) - base::FilePath temp_dir; - CHECK(base::PathService::Get(base::DIR_TEMP, &temp_dir)); - return NamedPlatformHandle( - temp_dir.AppendASCII(GenerateRandomToken()).value()); -#else - return NamedPlatformHandle(GenerateRandomToken()); -#endif -} - -void CreateClientHandleOnIoThread(const NamedPlatformHandle& named_handle, - ScopedPlatformHandle* output) { - *output = CreateClientHandle(named_handle); -} - -TEST_F(EmbedderTest, ClosePendingPeerConnection) { - NamedPlatformHandle named_handle = GenerateChannelName(); - std::string peer_token = GenerateRandomToken(); - ScopedMessagePipeHandle server_pipe = - ConnectToPeerProcess(CreateServerHandle(named_handle), peer_token); - ClosePeerConnection(peer_token); - EXPECT_EQ(MOJO_RESULT_OK, - Wait(server_pipe.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED)); - base::MessageLoop message_loop; - base::RunLoop run_loop; - ScopedPlatformHandle client_handle; - // Closing the channel involves posting a task to the IO thread to do the - // work. By the time the local message pipe has been observerd as closed, - // that task will have been posted. Therefore, a task to create the client - // connection should be handled after the channel is closed. - GetIOTaskRunner()->PostTaskAndReply( - FROM_HERE, - base::Bind(&CreateClientHandleOnIoThread, named_handle, &client_handle), - run_loop.QuitClosure()); - run_loop.Run(); - EXPECT_FALSE(client_handle.is_valid()); -} - -#if !defined(OS_IOS) - -TEST_F(EmbedderTest, ClosePipeToConnectedPeer) { - set_launch_type(LaunchType::PEER); - auto& controller = StartClient("ClosePipeToConnectedPeerClient"); - MojoHandle server_mp = controller.pipe(); - // 1. Write a message to |server_mp| (attaching nothing). - WriteMessage(server_mp, "hello"); - - // 2. Read a message from |server_mp|. - EXPECT_EQ("world!", ReadMessage(server_mp)); - - controller.ClosePeerConnection(); - - EXPECT_EQ(MOJO_RESULT_OK, - WaitForSignals(server_mp, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); - - EXPECT_EQ(0, controller.WaitForShutdown()); -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ClosePipeToConnectedPeerClient, EmbedderTest, - client_mp) { - // 1. Read the first message from |client_mp|. - EXPECT_EQ("hello", ReadMessage(client_mp)); - - // 2. Write a message to |client_mp| (attaching nothing). - WriteMessage(client_mp, "world!"); - - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(client_mp, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); -} - -TEST_F(EmbedderTest, ClosePipeToConnectingPeer) { - set_launch_type(LaunchType::PEER); - auto& controller = StartClient("ClosePipeToConnectingPeerClient"); - controller.ClosePeerConnection(); - - MojoHandle server_mp = controller.pipe(); - - EXPECT_EQ(MOJO_RESULT_OK, - WaitForSignals(server_mp, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); - - EXPECT_EQ(0, controller.WaitForShutdown()); -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ClosePipeToConnectingPeerClient, EmbedderTest, - client_mp) { - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(client_mp, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); -} - -#endif // !defined(OS_IOS) - -} // namespace -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/embedder/entrypoints.cc b/mojo/edk/embedder/entrypoints.cc deleted file mode 100644 index 9081368..0000000 --- a/mojo/edk/embedder/entrypoints.cc +++ /dev/null @@ -1,283 +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/entrypoints.h" - -#include <stdint.h> - -#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" -#include "mojo/public/c/system/platform_handle.h" - -using mojo::edk::internal::g_core; - -// Definitions of the system functions. -extern "C" { - -MojoTimeTicks MojoGetTimeTicksNowImpl() { - return g_core->GetTimeTicksNow(); -} - -MojoResult MojoCloseImpl(MojoHandle handle) { - return g_core->Close(handle); -} - -MojoResult MojoQueryHandleSignalsStateImpl( - MojoHandle handle, - MojoHandleSignalsState* signals_state) { - return g_core->QueryHandleSignalsState(handle, signals_state); -} - -MojoResult MojoCreateWatcherImpl(MojoWatcherCallback callback, - MojoHandle* watcher_handle) { - return g_core->CreateWatcher(callback, watcher_handle); -} - -MojoResult MojoArmWatcherImpl(MojoHandle watcher_handle, - uint32_t* num_ready_contexts, - uintptr_t* ready_contexts, - MojoResult* ready_results, - MojoHandleSignalsState* ready_signals_states) { - return g_core->ArmWatcher(watcher_handle, num_ready_contexts, ready_contexts, - ready_results, ready_signals_states); -} - -MojoResult MojoWatchImpl(MojoHandle watcher_handle, - MojoHandle handle, - MojoHandleSignals signals, - uintptr_t context) { - return g_core->Watch(watcher_handle, handle, signals, context); -} - -MojoResult MojoCancelWatchImpl(MojoHandle watcher_handle, uintptr_t context) { - return g_core->CancelWatch(watcher_handle, context); -} - -MojoResult MojoAllocMessageImpl(uint32_t num_bytes, - const MojoHandle* handles, - uint32_t num_handles, - MojoAllocMessageFlags flags, - MojoMessageHandle* message) { - return g_core->AllocMessage(num_bytes, handles, num_handles, flags, message); -} - -MojoResult MojoFreeMessageImpl(MojoMessageHandle message) { - return g_core->FreeMessage(message); -} - -MojoResult MojoGetMessageBufferImpl(MojoMessageHandle message, void** buffer) { - return g_core->GetMessageBuffer(message, buffer); -} - -MojoResult MojoCreateMessagePipeImpl( - const MojoCreateMessagePipeOptions* options, - MojoHandle* message_pipe_handle0, - MojoHandle* message_pipe_handle1) { - return g_core->CreateMessagePipe(options, message_pipe_handle0, - message_pipe_handle1); -} - -MojoResult MojoWriteMessageImpl(MojoHandle message_pipe_handle, - const void* bytes, - uint32_t num_bytes, - const MojoHandle* handles, - uint32_t num_handles, - MojoWriteMessageFlags flags) { - return g_core->WriteMessage(message_pipe_handle, bytes, num_bytes, handles, - num_handles, flags); -} - -MojoResult MojoWriteMessageNewImpl(MojoHandle message_pipe_handle, - MojoMessageHandle message, - MojoWriteMessageFlags flags) { - return g_core->WriteMessageNew(message_pipe_handle, message, flags); -} - -MojoResult MojoReadMessageImpl(MojoHandle message_pipe_handle, - void* bytes, - uint32_t* num_bytes, - MojoHandle* handles, - uint32_t* num_handles, - MojoReadMessageFlags flags) { - return g_core->ReadMessage( - message_pipe_handle, bytes, num_bytes, handles, num_handles, flags); -} - -MojoResult MojoReadMessageNewImpl(MojoHandle message_pipe_handle, - MojoMessageHandle* message, - uint32_t* num_bytes, - MojoHandle* handles, - uint32_t* num_handles, - MojoReadMessageFlags flags) { - return g_core->ReadMessageNew( - message_pipe_handle, message, num_bytes, handles, num_handles, flags); -} - -MojoResult MojoFuseMessagePipesImpl(MojoHandle handle0, MojoHandle handle1) { - return g_core->FuseMessagePipes(handle0, handle1); -} - -MojoResult MojoCreateDataPipeImpl(const MojoCreateDataPipeOptions* options, - MojoHandle* data_pipe_producer_handle, - MojoHandle* data_pipe_consumer_handle) { - return g_core->CreateDataPipe(options, data_pipe_producer_handle, - data_pipe_consumer_handle); -} - -MojoResult MojoWriteDataImpl(MojoHandle data_pipe_producer_handle, - const void* elements, - uint32_t* num_elements, - MojoWriteDataFlags flags) { - return g_core->WriteData(data_pipe_producer_handle, elements, num_elements, - flags); -} - -MojoResult MojoBeginWriteDataImpl(MojoHandle data_pipe_producer_handle, - void** buffer, - uint32_t* buffer_num_elements, - MojoWriteDataFlags flags) { - return g_core->BeginWriteData(data_pipe_producer_handle, buffer, - buffer_num_elements, flags); -} - -MojoResult MojoEndWriteDataImpl(MojoHandle data_pipe_producer_handle, - uint32_t num_elements_written) { - return g_core->EndWriteData(data_pipe_producer_handle, num_elements_written); -} - -MojoResult MojoReadDataImpl(MojoHandle data_pipe_consumer_handle, - void* elements, - uint32_t* num_elements, - MojoReadDataFlags flags) { - return g_core->ReadData(data_pipe_consumer_handle, elements, num_elements, - flags); -} - -MojoResult MojoBeginReadDataImpl(MojoHandle data_pipe_consumer_handle, - const void** buffer, - uint32_t* buffer_num_elements, - MojoReadDataFlags flags) { - return g_core->BeginReadData(data_pipe_consumer_handle, buffer, - buffer_num_elements, flags); -} - -MojoResult MojoEndReadDataImpl(MojoHandle data_pipe_consumer_handle, - uint32_t num_elements_read) { - return g_core->EndReadData(data_pipe_consumer_handle, num_elements_read); -} - -MojoResult MojoCreateSharedBufferImpl( - const struct MojoCreateSharedBufferOptions* options, - uint64_t num_bytes, - MojoHandle* shared_buffer_handle) { - return g_core->CreateSharedBuffer(options, num_bytes, shared_buffer_handle); -} - -MojoResult MojoDuplicateBufferHandleImpl( - MojoHandle buffer_handle, - const struct MojoDuplicateBufferHandleOptions* options, - MojoHandle* new_buffer_handle) { - return g_core->DuplicateBufferHandle(buffer_handle, options, - new_buffer_handle); -} - -MojoResult MojoMapBufferImpl(MojoHandle buffer_handle, - uint64_t offset, - uint64_t num_bytes, - void** buffer, - MojoMapBufferFlags flags) { - return g_core->MapBuffer(buffer_handle, offset, num_bytes, buffer, flags); -} - -MojoResult MojoUnmapBufferImpl(void* buffer) { - return g_core->UnmapBuffer(buffer); -} - -MojoResult MojoWrapPlatformHandleImpl(const MojoPlatformHandle* platform_handle, - MojoHandle* mojo_handle) { - return g_core->WrapPlatformHandle(platform_handle, mojo_handle); -} - -MojoResult MojoUnwrapPlatformHandleImpl(MojoHandle mojo_handle, - MojoPlatformHandle* platform_handle) { - return g_core->UnwrapPlatformHandle(mojo_handle, platform_handle); -} - -MojoResult MojoWrapPlatformSharedBufferHandleImpl( - const MojoPlatformHandle* platform_handle, - size_t num_bytes, - MojoPlatformSharedBufferHandleFlags flags, - MojoHandle* mojo_handle) { - return g_core->WrapPlatformSharedBufferHandle(platform_handle, num_bytes, - flags, mojo_handle); -} - -MojoResult MojoUnwrapPlatformSharedBufferHandleImpl( - MojoHandle mojo_handle, - MojoPlatformHandle* platform_handle, - size_t* num_bytes, - MojoPlatformSharedBufferHandleFlags* flags) { - return g_core->UnwrapPlatformSharedBufferHandle(mojo_handle, platform_handle, - num_bytes, flags); -} - -MojoResult MojoNotifyBadMessageImpl(MojoMessageHandle message, - const char* error, - size_t error_num_bytes) { - return g_core->NotifyBadMessage(message, error, error_num_bytes); -} - -MojoResult MojoGetPropertyImpl(MojoPropertyType type, void* value) { - return g_core->GetProperty(type, value); -} - -} // extern "C" - -namespace mojo { -namespace edk { - -MojoSystemThunks MakeSystemThunks() { - MojoSystemThunks system_thunks = {sizeof(MojoSystemThunks), - MojoGetTimeTicksNowImpl, - MojoCloseImpl, - MojoQueryHandleSignalsStateImpl, - MojoCreateMessagePipeImpl, - MojoWriteMessageImpl, - MojoReadMessageImpl, - MojoCreateDataPipeImpl, - MojoWriteDataImpl, - MojoBeginWriteDataImpl, - MojoEndWriteDataImpl, - MojoReadDataImpl, - MojoBeginReadDataImpl, - MojoEndReadDataImpl, - MojoCreateSharedBufferImpl, - MojoDuplicateBufferHandleImpl, - MojoMapBufferImpl, - MojoUnmapBufferImpl, - MojoCreateWatcherImpl, - MojoWatchImpl, - MojoCancelWatchImpl, - MojoArmWatcherImpl, - MojoFuseMessagePipesImpl, - MojoWriteMessageNewImpl, - MojoReadMessageNewImpl, - MojoAllocMessageImpl, - MojoFreeMessageImpl, - MojoGetMessageBufferImpl, - MojoWrapPlatformHandleImpl, - MojoUnwrapPlatformHandleImpl, - MojoWrapPlatformSharedBufferHandleImpl, - MojoUnwrapPlatformSharedBufferHandleImpl, - MojoNotifyBadMessageImpl, - MojoGetPropertyImpl}; - return system_thunks; -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/embedder/entrypoints.h b/mojo/edk/embedder/entrypoints.h deleted file mode 100644 index 8e448c1..0000000 --- a/mojo/edk/embedder/entrypoints.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_EMBEDDER_ENTRYPOINTS_H_ -#define MOJO_EDK_EMBEDDER_ENTRYPOINTS_H_ - -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/thunks.h" - -namespace mojo { -namespace edk { - -// Creates a MojoSystemThunks struct populated with the EDK's implementation of -// each function. This may be used by embedders to populate thunks for -// application loading. -MOJO_SYSTEM_IMPL_EXPORT MojoSystemThunks MakeSystemThunks(); - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_ENTRYPOINTS_H_ diff --git a/mojo/edk/embedder/named_platform_channel_pair.h b/mojo/edk/embedder/named_platform_channel_pair.h deleted file mode 100644 index 5a83ae3..0000000 --- a/mojo/edk/embedder/named_platform_channel_pair.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_EMBEDDER_NAMED_PLATFORM_CHANNEL_PAIR_H_ -#define MOJO_EDK_EMBEDDER_NAMED_PLATFORM_CHANNEL_PAIR_H_ - -#include <string> - -#include "base/macros.h" -#include "base/strings/string16.h" -#include "build/build_config.h" -#include "mojo/edk/embedder/named_platform_handle.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace base { -class CommandLine; -} - -namespace mojo { -namespace edk { - -// This is used to create a named bidirectional pipe to connect new child -// processes. The resulting server handle should be passed to the EDK, and the -// child end passed as a pipe name on the command line to the child process. The -// child process can then retrieve the pipe name from the command line and -// resolve it into a client handle. -class MOJO_SYSTEM_IMPL_EXPORT NamedPlatformChannelPair { - public: - struct Options { -#if defined(OS_WIN) - // If non-empty, a security descriptor to use when creating the pipe. If - // empty, a default security descriptor will be used. See - // kDefaultSecurityDescriptor in named_platform_handle_utils_win.cc. - base::string16 security_descriptor; -#endif - }; - - NamedPlatformChannelPair(const Options& options = {}); - ~NamedPlatformChannelPair(); - - // Note: It is NOT acceptable to use this handle as a generic pipe channel. It - // MUST be passed to PendingProcessConnection::Connect() only. - ScopedPlatformHandle PassServerHandle(); - - // 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) const; - - const NamedPlatformHandle& handle() const { return pipe_handle_; } - - private: - NamedPlatformHandle pipe_handle_; - ScopedPlatformHandle server_handle_; - - DISALLOW_COPY_AND_ASSIGN(NamedPlatformChannelPair); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_CHANNEL_PAIR_H_ diff --git a/mojo/edk/embedder/named_platform_channel_pair_win.cc b/mojo/edk/embedder/named_platform_channel_pair_win.cc deleted file mode 100644 index 96589ff..0000000 --- a/mojo/edk/embedder/named_platform_channel_pair_win.cc +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/embedder/named_platform_channel_pair.h" - -#include <windows.h> - -#include <memory> -#include <string> -#include <utility> - -#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/strings/utf_string_conversions.h" -#include "base/win/windows_version.h" -#include "mojo/edk/embedder/named_platform_handle_utils.h" -#include "mojo/edk/embedder/platform_handle.h" - -namespace mojo { -namespace edk { - -namespace { - -const char kMojoNamedPlatformChannelPipeSwitch[] = - "mojo-named-platform-channel-pipe"; - -std::wstring GeneratePipeName() { - return base::StringPrintf(L"%u.%u.%I64u", GetCurrentProcessId(), - GetCurrentThreadId(), base::RandUint64()); -} - -} // namespace - -NamedPlatformChannelPair::NamedPlatformChannelPair( - const NamedPlatformChannelPair::Options& options) - : pipe_handle_(GeneratePipeName()) { - CreateServerHandleOptions server_handle_options; - server_handle_options.security_descriptor = options.security_descriptor; - server_handle_options.enforce_uniqueness = true; - server_handle_ = CreateServerHandle(pipe_handle_, server_handle_options); - PCHECK(server_handle_.is_valid()); -} - -NamedPlatformChannelPair::~NamedPlatformChannelPair() {} - -ScopedPlatformHandle NamedPlatformChannelPair::PassServerHandle() { - return std::move(server_handle_); -} - -// static -ScopedPlatformHandle -NamedPlatformChannelPair::PassClientHandleFromParentProcess( - const base::CommandLine& command_line) { - // In order to support passing the pipe name on the command line, the pipe - // handle is lazily created from the pipe name when requested. - NamedPlatformHandle handle( - command_line.GetSwitchValueNative(kMojoNamedPlatformChannelPipeSwitch)); - - if (!handle.is_valid()) - return ScopedPlatformHandle(); - - return CreateClientHandle(handle); -} - -void NamedPlatformChannelPair::PrepareToPassClientHandleToChildProcess( - base::CommandLine* command_line) const { - DCHECK(command_line); - - // 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(kMojoNamedPlatformChannelPipeSwitch)) - << "Child command line already has switch --" - << kMojoNamedPlatformChannelPipeSwitch << "=" - << command_line->GetSwitchValueNative( - kMojoNamedPlatformChannelPipeSwitch); - // (Any existing switch won't actually be removed from the command line, but - // the last one appended takes precedence.) - command_line->AppendSwitchNative(kMojoNamedPlatformChannelPipeSwitch, - pipe_handle_.name); -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/embedder/named_platform_handle.h b/mojo/edk/embedder/named_platform_handle.h deleted file mode 100644 index 15ca656..0000000 --- a/mojo/edk/embedder/named_platform_handle.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_H_ -#define MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_H_ - -#include <string> - -#include "base/strings/string16.h" -#include "base/strings/string_piece.h" -#include "base/strings/utf_string_conversions.h" -#include "build/build_config.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace edk { - -#if defined(OS_POSIX) -struct MOJO_SYSTEM_IMPL_EXPORT NamedPlatformHandle { - NamedPlatformHandle() {} - explicit NamedPlatformHandle(const base::StringPiece& name) - : name(name.as_string()) {} - - bool is_valid() const { return !name.empty(); } - - std::string name; -}; -#elif defined(OS_WIN) -struct MOJO_SYSTEM_IMPL_EXPORT NamedPlatformHandle { - NamedPlatformHandle() {} - explicit NamedPlatformHandle(const base::StringPiece& name) - : name(base::UTF8ToUTF16(name)) {} - - explicit NamedPlatformHandle(const base::StringPiece16& name) - : name(name.as_string()) {} - - bool is_valid() const { return !name.empty(); } - - base::string16 pipe_name() const { return L"\\\\.\\pipe\\mojo." + name; } - - base::string16 name; -}; -#else -#error "Platform not yet supported." -#endif - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_H_ diff --git a/mojo/edk/embedder/named_platform_handle_utils.h b/mojo/edk/embedder/named_platform_handle_utils.h deleted file mode 100644 index b767ea0..0000000 --- a/mojo/edk/embedder/named_platform_handle_utils.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_UTILS_H_ -#define MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_UTILS_H_ - -#include "build/build_config.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/system_impl_export.h" - -#if defined(OS_WIN) -#include "base/strings/string16.h" -#endif - -namespace mojo { -namespace edk { - -struct NamedPlatformHandle; - -#if defined(OS_POSIX) - -// The maximum length of the name of a unix domain socket. The standard size on -// linux is 108, mac is 104. To maintain consistency across platforms we -// standardize on the smaller value. -const size_t kMaxSocketNameLength = 104; - -#endif - -struct CreateServerHandleOptions { -#if defined(OS_WIN) - // If true, creating a server handle will fail if another pipe with the same - // name exists. - bool enforce_uniqueness = true; - - // If non-empty, a security descriptor to use when creating the pipe. If - // empty, a default security descriptor will be used. See - // kDefaultSecurityDescriptor in named_platform_handle_utils_win.cc. - base::string16 security_descriptor; -#endif -}; - -// Creates a client platform handle from |handle|. This may block until |handle| -// is ready to receive connections. -MOJO_SYSTEM_IMPL_EXPORT ScopedPlatformHandle -CreateClientHandle(const NamedPlatformHandle& handle); - -// Creates a server platform handle from |handle|. -MOJO_SYSTEM_IMPL_EXPORT ScopedPlatformHandle -CreateServerHandle(const NamedPlatformHandle& handle, - const CreateServerHandleOptions& options = {}); - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_UTILS_H_ diff --git a/mojo/edk/embedder/named_platform_handle_utils_posix.cc b/mojo/edk/embedder/named_platform_handle_utils_posix.cc deleted file mode 100644 index 056f4d6..0000000 --- a/mojo/edk/embedder/named_platform_handle_utils_posix.cc +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/embedder/named_platform_handle_utils.h" - -#include <errno.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <unistd.h> - -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/posix/eintr_wrapper.h" -#include "mojo/edk/embedder/named_platform_handle.h" - -namespace mojo { -namespace edk { -namespace { - -// This function fills in |unix_addr| with the appropriate data for the socket, -// and sets |unix_addr_len| to the length of the data therein. -// Returns true on success, or false on failure (typically because |handle.name| -// violated the naming rules). -bool MakeUnixAddr(const NamedPlatformHandle& handle, - struct sockaddr_un* unix_addr, - size_t* unix_addr_len) { - DCHECK(unix_addr); - DCHECK(unix_addr_len); - DCHECK(handle.is_valid()); - - // We reject handle.name.length() == kMaxSocketNameLength to make room for the - // NUL terminator at the end of the string. - if (handle.name.length() >= kMaxSocketNameLength) { - LOG(ERROR) << "Socket name too long: " << handle.name; - return false; - } - - // Create unix_addr structure. - memset(unix_addr, 0, sizeof(struct sockaddr_un)); - unix_addr->sun_family = AF_UNIX; - strncpy(unix_addr->sun_path, handle.name.c_str(), kMaxSocketNameLength); - *unix_addr_len = - offsetof(struct sockaddr_un, sun_path) + handle.name.length(); - return true; -} - -// This function creates a unix domain socket, and set it as non-blocking. -// If successful, this returns a ScopedPlatformHandle containing the socket. -// Otherwise, this returns an invalid ScopedPlatformHandle. -ScopedPlatformHandle CreateUnixDomainSocket(bool needs_connection) { - // Create the unix domain socket. - PlatformHandle socket_handle(socket(AF_UNIX, SOCK_STREAM, 0)); - socket_handle.needs_connection = needs_connection; - ScopedPlatformHandle handle(socket_handle); - if (!handle.is_valid()) { - PLOG(ERROR) << "Failed to create AF_UNIX socket."; - return ScopedPlatformHandle(); - } - - // Now set it as non-blocking. - if (!base::SetNonBlocking(handle.get().handle)) { - PLOG(ERROR) << "base::SetNonBlocking() failed " << handle.get().handle; - return ScopedPlatformHandle(); - } - return handle; -} - -} // namespace - -ScopedPlatformHandle CreateClientHandle( - const NamedPlatformHandle& named_handle) { - if (!named_handle.is_valid()) - return ScopedPlatformHandle(); - - struct sockaddr_un unix_addr; - size_t unix_addr_len; - if (!MakeUnixAddr(named_handle, &unix_addr, &unix_addr_len)) - return ScopedPlatformHandle(); - - ScopedPlatformHandle handle = CreateUnixDomainSocket(false); - if (!handle.is_valid()) - return ScopedPlatformHandle(); - - if (HANDLE_EINTR(connect(handle.get().handle, - reinterpret_cast<sockaddr*>(&unix_addr), - unix_addr_len)) < 0) { - PLOG(ERROR) << "connect " << named_handle.name; - return ScopedPlatformHandle(); - } - - return handle; -} - -ScopedPlatformHandle CreateServerHandle( - const NamedPlatformHandle& named_handle, - const CreateServerHandleOptions& options) { - if (!named_handle.is_valid()) - return ScopedPlatformHandle(); - - // Make sure the path we need exists. - base::FilePath socket_dir = base::FilePath(named_handle.name).DirName(); - if (!base::CreateDirectory(socket_dir)) { - LOG(ERROR) << "Couldn't create directory: " << socket_dir.value(); - return ScopedPlatformHandle(); - } - - // Delete any old FS instances. - if (unlink(named_handle.name.c_str()) < 0 && errno != ENOENT) { - PLOG(ERROR) << "unlink " << named_handle.name; - return ScopedPlatformHandle(); - } - - struct sockaddr_un unix_addr; - size_t unix_addr_len; - if (!MakeUnixAddr(named_handle, &unix_addr, &unix_addr_len)) - return ScopedPlatformHandle(); - - ScopedPlatformHandle handle = CreateUnixDomainSocket(true); - if (!handle.is_valid()) - return ScopedPlatformHandle(); - - // Bind the socket. - if (bind(handle.get().handle, reinterpret_cast<const sockaddr*>(&unix_addr), - unix_addr_len) < 0) { - PLOG(ERROR) << "bind " << named_handle.name; - return ScopedPlatformHandle(); - } - - // Start listening on the socket. - if (listen(handle.get().handle, SOMAXCONN) < 0) { - PLOG(ERROR) << "listen " << named_handle.name; - unlink(named_handle.name.c_str()); - return ScopedPlatformHandle(); - } - return handle; -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/embedder/named_platform_handle_utils_win.cc b/mojo/edk/embedder/named_platform_handle_utils_win.cc deleted file mode 100644 index a145847..0000000 --- a/mojo/edk/embedder/named_platform_handle_utils_win.cc +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/embedder/named_platform_handle_utils.h" - -#include <sddl.h> -#include <windows.h> - -#include <memory> - -#include "base/logging.h" -#include "base/win/windows_version.h" -#include "mojo/edk/embedder/named_platform_handle.h" - -namespace mojo { -namespace edk { -namespace { - -// A DACL to grant: -// GA = Generic All -// access to: -// SY = LOCAL_SYSTEM -// BA = BUILTIN_ADMINISTRATORS -// OW = OWNER_RIGHTS -constexpr base::char16 kDefaultSecurityDescriptor[] = - L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;OW)"; - -} // namespace - -ScopedPlatformHandle CreateClientHandle( - const NamedPlatformHandle& named_handle) { - if (!named_handle.is_valid()) - return ScopedPlatformHandle(); - - base::string16 pipe_name = named_handle.pipe_name(); - - // Note: This may block. - if (!WaitNamedPipeW(pipe_name.c_str(), NMPWAIT_USE_DEFAULT_WAIT)) - return ScopedPlatformHandle(); - - 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; - ScopedPlatformHandle handle( - PlatformHandle(CreateFileW(pipe_name.c_str(), kDesiredAccess, - 0, // No sharing. - nullptr, OPEN_EXISTING, kFlags, - nullptr))); // No template file. - // The server may have stopped accepting a connection between the - // WaitNamedPipe() and CreateFile(). If this occurs, an invalid handle is - // returned. - DPLOG_IF(ERROR, !handle.is_valid()) - << "Named pipe " << named_handle.pipe_name() - << " could not be opened after WaitNamedPipe succeeded"; - return handle; -} - -ScopedPlatformHandle CreateServerHandle( - const NamedPlatformHandle& named_handle, - const CreateServerHandleOptions& options) { - if (!named_handle.is_valid()) - return ScopedPlatformHandle(); - - PSECURITY_DESCRIPTOR security_desc = nullptr; - ULONG security_desc_len = 0; - PCHECK(ConvertStringSecurityDescriptorToSecurityDescriptor( - options.security_descriptor.empty() ? kDefaultSecurityDescriptor - : options.security_descriptor.c_str(), - SDDL_REVISION_1, &security_desc, &security_desc_len)); - std::unique_ptr<void, decltype(::LocalFree)*> p(security_desc, ::LocalFree); - SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES), - security_desc, FALSE}; - - const DWORD kOpenMode = options.enforce_uniqueness - ? PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | - FILE_FLAG_FIRST_PIPE_INSTANCE - : PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED; - const DWORD kPipeMode = - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_REJECT_REMOTE_CLIENTS; - PlatformHandle handle( - CreateNamedPipeW(named_handle.pipe_name().c_str(), kOpenMode, kPipeMode, - options.enforce_uniqueness ? 1 : 255, // Max instances. - 4096, // Out buffer size. - 4096, // In buffer size. - 5000, // Timeout in milliseconds. - &security_attributes)); - handle.needs_connection = true; - return ScopedPlatformHandle(handle); -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/embedder/pending_process_connection.cc b/mojo/edk/embedder/pending_process_connection.cc deleted file mode 100644 index d6be76e..0000000 --- a/mojo/edk/embedder/pending_process_connection.cc +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/embedder/pending_process_connection.h" - -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/embedder/embedder_internal.h" -#include "mojo/edk/system/core.h" - -namespace mojo { -namespace edk { - -PendingProcessConnection::PendingProcessConnection() - : process_token_(GenerateRandomToken()) { - DCHECK(internal::g_core); -} - -PendingProcessConnection::~PendingProcessConnection() { - if (has_message_pipes_ && !connected_) { - DCHECK(internal::g_core); - internal::g_core->ChildLaunchFailed(process_token_); - } -} - -ScopedMessagePipeHandle PendingProcessConnection::CreateMessagePipe( - std::string* token) { - has_message_pipes_ = true; - DCHECK(internal::g_core); - *token = GenerateRandomToken(); - return internal::g_core->CreateParentMessagePipe(*token, process_token_); -} - -void PendingProcessConnection::Connect( - base::ProcessHandle process, - ConnectionParams connection_params, - const ProcessErrorCallback& error_callback) { - // It's now safe to avoid cleanup in the destructor, as the lifetime of any - // associated resources is effectively bound to the |channel| passed to - // AddChild() below. - DCHECK(!connected_); - connected_ = true; - - DCHECK(internal::g_core); - internal::g_core->AddChild(process, std::move(connection_params), - process_token_, error_callback); -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/embedder/pending_process_connection.h b/mojo/edk/embedder/pending_process_connection.h deleted file mode 100644 index ca18227..0000000 --- a/mojo/edk/embedder/pending_process_connection.h +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_EMBEDDER_PENDING_PROCESS_CONNECTION_H_ -#define MOJO_EDK_EMBEDDER_PENDING_PROCESS_CONNECTION_H_ - -#include <string> - -#include "base/callback.h" -#include "base/macros.h" -#include "base/process/process_handle.h" -#include "mojo/edk/embedder/connection_params.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 edk { - -using ProcessErrorCallback = base::Callback<void(const std::string& error)>; - -// Represents a potential connection to an external process. Use this object -// to make other processes reachable from this one via Mojo IPC. Typical usage -// might look something like: -// -// PendingProcessConnection connection; -// -// std::string pipe_token; -// ScopedMessagePipeHandle pipe = connection.CreateMessagePipe(&pipe_token); -// -// // New pipes to the process are fully functional and can be used right -// // away, even if the process doesn't exist yet. -// GoDoSomethingInteresting(std::move(pipe)); -// -// ScopedPlatformChannelPair channel; -// -// // Give the pipe token to the child process via command-line. -// child_command_line.AppendSwitchASCII("yer-pipe", pipe_token); -// -// // Magic child process launcher which gives one end of the pipe to the -// // new process. -// LaunchProcess(child_command_line, channel.PassClientHandle()); -// -// // Some time later... -// connection.Connect(new_process, channel.PassServerHandle()); -// -// If at any point during the above process, |connection| is destroyed before -// Connect() can be called, |pipe| will imminently behave as if its peer has -// been closed. -// -// Otherwise, if the remote process in this example eventually calls: -// -// mojo::edk::SetParentPipeHandle(std::move(client_channel_handle)); -// -// std::string token = command_line.GetSwitchValueASCII("yer-pipe"); -// ScopedMessagePipeHandle pipe = mojo::edk::CreateChildMessagePipe(token); -// -// it will be connected to this process, and its |pipe| will be connected to -// this process's |pipe|. -// -// If the remote process exits or otherwise closes its client channel handle -// before calling CreateChildMessagePipe for a given message pipe token, -// this process's end of the corresponding message pipe will imminently behave -// as if its peer has been closed. -// -class MOJO_SYSTEM_IMPL_EXPORT PendingProcessConnection { - public: - PendingProcessConnection(); - ~PendingProcessConnection(); - - // Creates a message pipe associated with a new globally unique string value - // which will be placed in |*token|. - // - // The other end of the new pipe is obtainable in the remote process (or in - // this process, to facilitate "single-process mode" in some applications) by - // passing the new |*token| value to mojo::edk::CreateChildMessagePipe. It's - // the caller's responsibility to communicate the value of |*token| to the - // remote process by any means available, e.g. a command-line argument on - // process launch, or some other out-of-band communication channel for an - // existing process. - // - // NOTES: This may be called any number of times to create multiple message - // pipes to the same remote process. This call ALWAYS succeeds, returning - // a valid message pipe handle and populating |*token| with a new unique - // string value. - ScopedMessagePipeHandle CreateMessagePipe(std::string* token); - - // Connects to the process. This must be called at most once, with the process - // handle in |process|. - // - // |connection_param| contains the platform handle of an OS pipe which can be - // used to communicate with the connected process. The other end of that pipe - // must ultimately be passed to mojo::edk::SetParentPipeHandle in the remote - // process, and getting that end of the pipe into the other process is the - // embedder's responsibility. - // - // If this method is not called by the time the PendingProcessConnection is - // destroyed, it's assumed that the process is unavailable (e.g. process - // launch failed or the process has otherwise been terminated early), and - // any associated resources, such as remote endpoints of messages pipes - // created by CreateMessagePipe above) will be cleaned up at that time. - void Connect( - base::ProcessHandle process, - ConnectionParams connection_params, - const ProcessErrorCallback& error_callback = ProcessErrorCallback()); - - private: - // A GUID representing a potential new process to be connected to this one. - const std::string process_token_; - - // Indicates whether this object has been used to create new message pipes. - bool has_message_pipes_ = false; - - // Indicates whether Connect() has been called yet. - bool connected_ = false; - - DISALLOW_COPY_AND_ASSIGN(PendingProcessConnection); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_PENDING_PROCESS_CONNECTION_H_ diff --git a/mojo/edk/embedder/platform_channel_pair.cc b/mojo/edk/embedder/platform_channel_pair.cc deleted file mode 100644 index ee1905a..0000000 --- a/mojo/edk/embedder/platform_channel_pair.cc +++ /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. - -#include "mojo/edk/embedder/platform_channel_pair.h" - -#include <utility> - -#include "base/logging.h" - -namespace mojo { -namespace edk { - -const char PlatformChannelPair::kMojoPlatformChannelHandleSwitch[] = - "mojo-platform-channel-handle"; - -PlatformChannelPair::~PlatformChannelPair() { -} - -ScopedPlatformHandle PlatformChannelPair::PassServerHandle() { - return std::move(server_handle_); -} - -ScopedPlatformHandle PlatformChannelPair::PassClientHandle() { - return std::move(client_handle_); -} - -void PlatformChannelPair::ChildProcessLaunched() { - DCHECK(client_handle_.is_valid()); - client_handle_.reset(); -} - -} // namespace edk -} // 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 9c93f76..0000000 --- a/mojo/edk/embedder/platform_channel_pair.h +++ /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. - -#ifndef MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_PAIR_H_ -#define MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_PAIR_H_ - -#include <memory> - -#include "base/macros.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 edk { - -// 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) -using HandlePassingInformation = base::HandlesToInheritVector; -#elif defined(OS_POSIX) -using HandlePassingInformation = base::FileHandleMappingVector; -#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: - static const char kMojoPlatformChannelHandleSwitch[]; - - // If |client_is_blocking| is true, then the client handle only supports - // blocking reads and writes. The default is nonblocking. - PlatformChannelPair(bool client_is_blocking = false); - ~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). - // TODO(jcivelli): remove the command_line param. http://crbug.com/670106 - static ScopedPlatformHandle PassClientHandleFromParentProcess( - const base::CommandLine& command_line); - - // Like above, but gets the handle from the passed in string. - static ScopedPlatformHandle PassClientHandleFromParentProcessFromString( - const std::string& value); - - // 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; - - // Like above, but returns a string instead of changing the command line. - std::string PrepareToPassClientHandleToChildProcessAsString( - HandlePassingInformation* handle_passing_info) const; - - // To be called once the child process has been successfully launched, to do - // any cleanup necessary. - void ChildProcessLaunched(); - - private: - ScopedPlatformHandle server_handle_; - ScopedPlatformHandle client_handle_; - - DISALLOW_COPY_AND_ASSIGN(PlatformChannelPair); -}; - -} // namespace edk -} // 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 fe9f8f5..0000000 --- a/mojo/edk/embedder/platform_channel_pair_posix.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 "mojo/edk/embedder/platform_channel_pair.h" - -#include <fcntl.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/types.h> -#include <unistd.h> - -#include <limits> - -#include "base/command_line.h" -#include "base/logging.h" -#include "base/posix/global_descriptors.h" -#include "base/rand_util.h" -#include "base/strings/string_number_conversions.h" -#include "build/build_config.h" -#include "mojo/edk/embedder/platform_handle.h" - -#if !defined(OS_NACL_SFI) -#include <sys/socket.h> -#else -#include "native_client/src/public/imc_syscalls.h" -#endif - -#if !defined(SO_PEEK_OFF) -#define SO_PEEK_OFF 42 -#endif - -namespace mojo { -namespace edk { - -namespace { - -#if defined(OS_ANDROID) || defined(__ANDROID__) -enum { - // Leave room for any other descriptors defined in content for example. - // TODO(jcivelli): consider changing base::GlobalDescriptors to generate a - // key when setting the file descriptor (http://crbug.com/676442). - kAndroidClientHandleDescriptor = - base::GlobalDescriptors::kBaseDescriptor + 10000, -}; -#else -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; -} -#endif - -} // namespace - -PlatformChannelPair::PlatformChannelPair(bool client_is_blocking) { - // Create the Unix domain socket. - int fds[2]; - // TODO(vtl): Maybe fail gracefully if |socketpair()| fails. - -#if defined(OS_NACL_SFI) - PCHECK(imc_socketpair(fds) == 0); -#else - PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0); - - // Set the ends to nonblocking. - PCHECK(fcntl(fds[0], F_SETFL, O_NONBLOCK) == 0); - if (!client_is_blocking) - 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) -#endif // defined(OS_NACL_SFI) - - 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); - return PassClientHandleFromParentProcessFromString(client_fd_string); -} - -ScopedPlatformHandle -PlatformChannelPair::PassClientHandleFromParentProcessFromString( - const std::string& value) { - int client_fd = -1; -#if defined(OS_ANDROID) || defined(__ANDROID__) - base::GlobalDescriptors::Key key = -1; - if (value.empty() || !base::StringToUint(value, &key)) { - LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch; - return ScopedPlatformHandle(); - } - client_fd = base::GlobalDescriptors::GetInstance()->Get(key); -#else - if (value.empty() || - !base::StringToInt(value, &client_fd) || - client_fd < base::GlobalDescriptors::kBaseDescriptor) { - LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch; - return ScopedPlatformHandle(); - } -#endif - return ScopedPlatformHandle(PlatformHandle(client_fd)); -} - -void PlatformChannelPair::PrepareToPassClientHandleToChildProcess( - base::CommandLine* command_line, - base::FileHandleMappingVector* handle_passing_info) const { - DCHECK(command_line); - - // 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, - PrepareToPassClientHandleToChildProcessAsString(handle_passing_info)); -} - -std::string -PlatformChannelPair::PrepareToPassClientHandleToChildProcessAsString( - HandlePassingInformation* handle_passing_info) const { -#if defined(OS_ANDROID) || defined(__ANDROID__) - int fd = client_handle_.get().handle; - handle_passing_info->push_back( - std::pair<int, int>(fd, kAndroidClientHandleDescriptor)); - return base::UintToString(kAndroidClientHandleDescriptor); -#else - 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().handle, target_fd)); - return base::IntToString(target_fd); -#endif -} - -} // namespace edk -} // 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 a3fd275..0000000 --- a/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc +++ /dev/null @@ -1,261 +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 <stddef.h> -#include <stdio.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/uio.h> -#include <unistd.h> -#include <deque> -#include <utility> - -#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 edk { -namespace { - -void WaitReadable(PlatformHandle h) { - struct pollfd pfds = {}; - pfds.fd = h.handle; - 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(); - ScopedPlatformHandle client_handle = channel_pair.PassClientHandle(); - - // Write to the client. - static const char kHello[] = "hello"; - EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)), - write(client_handle.get().handle, 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().handle, buffer, sizeof(buffer))); - EXPECT_STREQ(kHello, buffer); - - // Try reading again. - ssize_t result = read(server_handle.get().handle, 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(); - ScopedPlatformHandle client_handle = channel_pair.PassClientHandle(); - - 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(); - ScopedPlatformHandle client_handle = channel_pair.PassClientHandle(); - -// Reduce the number of FDs opened on OS X to avoid test flake. -#if defined(OS_MACOSX) - const size_t kNumHandlesToSend = kPlatformChannelMaxNumHandles / 2; -#else - const size_t kNumHandlesToSend = kPlatformChannelMaxNumHandles; -#endif - - for (size_t i = 1; i < kNumHandlesToSend; 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.GetPath(), &unused)); - ASSERT_TRUE(fp); - ASSERT_EQ(j, fwrite(std::string(j, c).data(), 1, j, fp.get())); - platform_handles->push_back( - test::PlatformHandleFromFILE(std::move(fp)).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[kNumHandlesToSend]; - 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(); - ScopedPlatformHandle client_handle = channel_pair.PassClientHandle(); - - const std::string file_contents("hello world"); - - { - base::FilePath unused; - base::ScopedFILE fp( - base::CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &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(std::move(fp)).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 edk -} // 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 f523ade..0000000 --- a/mojo/edk/embedder/platform_channel_pair_win.cc +++ /dev/null @@ -1,123 +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 edk { - -namespace { - -std::wstring GeneratePipeName() { - return base::StringPrintf(L"\\\\.\\pipe\\mojo.%u.%u.%I64u", - GetCurrentProcessId(), GetCurrentThreadId(), - base::RandUint64()); -} - -} // namespace - -PlatformChannelPair::PlatformChannelPair(bool client_is_blocking) { - std::wstring pipe_name = GeneratePipeName(); - - 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. - DWORD kFlags = SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS; - if (!client_is_blocking) - kFlags |= 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); - return PassClientHandleFromParentProcessFromString(client_handle_string); -} - -ScopedPlatformHandle -PlatformChannelPair::PassClientHandleFromParentProcessFromString( - const std::string& value) { - int client_handle_value = 0; - if (value.empty() || - !base::StringToInt(value, &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); - - // 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, - PrepareToPassClientHandleToChildProcessAsString(handle_passing_info)); -} - -std::string -PlatformChannelPair::PrepareToPassClientHandleToChildProcessAsString( - HandlePassingInformation* handle_passing_info) const { - DCHECK(handle_passing_info); - DCHECK(client_handle_.is_valid()); - - if (base::win::GetVersion() >= base::win::VERSION_VISTA) - handle_passing_info->push_back(client_handle_.get().handle); - - return base::IntToString(HandleToLong(client_handle_.get().handle)); -} - -} // namespace edk -} // 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 689b6ee..0000000 --- a/mojo/edk/embedder/platform_channel_utils_posix.cc +++ /dev/null @@ -1,282 +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 <stddef.h> -#include <sys/socket.h> -#include <unistd.h> - -#include <utility> - -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/posix/eintr_wrapper.h" -#include "build/build_config.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" - -#if !defined(OS_NACL) -#include <sys/uio.h> -#endif - -#if !defined(SO_PEEK_OFF) -#define SO_PEEK_OFF 42 -#endif - -namespace mojo { -namespace edk { -namespace { - -#if !defined(OS_NACL) -bool IsRecoverableError() { - return errno == ECONNABORTED || errno == EMFILE || errno == ENFILE || - errno == ENOMEM || errno == ENOBUFS; -} - -bool GetPeerEuid(PlatformHandle handle, uid_t* peer_euid) { - DCHECK(peer_euid); -#if defined(OS_MACOSX) || defined(OS_OPENBSD) || defined(OS_FREEBSD) - uid_t socket_euid; - gid_t socket_gid; - if (getpeereid(handle.handle, &socket_euid, &socket_gid) < 0) { - PLOG(ERROR) << "getpeereid " << handle.handle; - return false; - } - *peer_euid = socket_euid; - return true; -#else - struct ucred cred; - socklen_t cred_len = sizeof(cred); - if (getsockopt(handle.handle, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) < - 0) { - PLOG(ERROR) << "getsockopt " << handle.handle; - return false; - } - if (static_cast<unsigned>(cred_len) < sizeof(cred)) { - NOTREACHED() << "Truncated ucred from SO_PEERCRED?"; - return false; - } - *peer_euid = cred.uid; - return true; -#endif -} - -bool IsPeerAuthorized(PlatformHandle peer_handle) { - uid_t peer_euid; - if (!GetPeerEuid(peer_handle, &peer_euid)) - return false; - if (peer_euid != geteuid()) { - DLOG(ERROR) << "Client euid is not authorised"; - return false; - } - return true; -} -#endif // !defined(OS_NACL) - -} // namespace - -// 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) || defined(OS_NACL_NONSFI) - // send() is not available under NaCl-nonsfi. - return HANDLE_EINTR(write(h.handle, bytes, num_bytes)); -#else - return send(h.handle, 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.handle, iov, static_cast<int>(num_iov))); -#else - struct msghdr msg = {}; - msg.msg_iov = iov; - msg.msg_iovlen = num_iov; - return HANDLE_EINTR(sendmsg(h.handle, &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].handle; - } - - return HANDLE_EINTR(sendmsg(h.handle, &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].handle; - } - - ssize_t result = HANDLE_EINTR(sendmsg(h.handle, &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, - bool block) { - 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.handle, &msg, block ? 0 : 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; -} - -bool ServerAcceptConnection(PlatformHandle server_handle, - ScopedPlatformHandle* connection_handle, - bool check_peer_user) { - DCHECK(server_handle.is_valid()); - connection_handle->reset(); -#if defined(OS_NACL) - NOTREACHED(); - return false; -#else - ScopedPlatformHandle accept_handle( - PlatformHandle(HANDLE_EINTR(accept(server_handle.handle, NULL, 0)))); - if (!accept_handle.is_valid()) - return IsRecoverableError(); - - // Verify that the IPC channel peer is running as the same user. - if (check_peer_user && !IsPeerAuthorized(accept_handle.get())) { - return true; - } - - if (!base::SetNonBlocking(accept_handle.get().handle)) { - PLOG(ERROR) << "base::SetNonBlocking() failed " - << accept_handle.get().handle; - // It's safe to keep listening on |server_handle| even if the attempt to set - // O_NONBLOCK failed on the client fd. - return true; - } - - *connection_handle = std::move(accept_handle); - return true; -#endif // defined(OS_NACL) -} - -} // namespace edk -} // 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 23cfa92..0000000 --- a/mojo/edk/embedder/platform_channel_utils_posix.h +++ /dev/null @@ -1,87 +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 <memory> - -#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 edk { -class ScopedPlatformHandle; - -// The maximum number of handles that can be sent "at once" using -// |PlatformChannelSendmsgWithHandles()|. This must be less than the Linux -// kernel's SCM_MAX_FD which is 253. -const size_t kPlatformChannelMaxNumHandles = 128; - -// 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, - bool block = false); - -// Returns false if |server_handle| encounters an unrecoverable error. -// Returns true if it's valid to keep listening on |server_handle|. In this -// case, it's possible that a connection wasn't successfully established; then, -// |connection_handle| will be invalid. If |check_peer_user| is True, the -// connection will be rejected if the peer is running as a different user. -MOJO_SYSTEM_IMPL_EXPORT bool ServerAcceptConnection( - PlatformHandle server_handle, - ScopedPlatformHandle* connection_handle, - bool check_peer_user = true); - -} // namespace edk -} // 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 b6b2cd2..0000000 --- a/mojo/edk/embedder/platform_handle.cc +++ /dev/null @@ -1,74 +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/logging.h" - -namespace mojo { -namespace edk { - -void PlatformHandle::CloseIfNecessary() { - if (!is_valid()) - return; - -#if defined(OS_POSIX) - if (type == Type::POSIX) { - bool success = (close(handle) == 0); - DPCHECK(success); - handle = -1; - } -#if defined(OS_MACOSX) && !defined(OS_IOS) - else if (type == Type::MACH) { - kern_return_t rv = mach_port_deallocate(mach_task_self(), port); - DPCHECK(rv == KERN_SUCCESS); - port = MACH_PORT_NULL; - } -#endif // defined(OS_MACOSX) && !defined(OS_IOS) -#elif defined(OS_WIN) - if (owning_process != base::GetCurrentProcessHandle()) { - // This handle may have been duplicated to a new target process but not yet - // sent there. In this case CloseHandle should NOT be called. From MSDN - // documentation for DuplicateHandle[1]: - // - // Normally the target process closes a duplicated handle when that - // process is finished using the handle. To close a duplicated handle - // from the source process, call DuplicateHandle with the following - // parameters: - // - // * Set hSourceProcessHandle to the target process from the - // call that created the handle. - // * Set hSourceHandle to the duplicated handle to close. - // * Set lpTargetHandle to NULL. - // * Set dwOptions to DUPLICATE_CLOSE_SOURCE. - // - // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/ms724251 - // - // NOTE: It's possible for this operation to fail if the owning process - // was terminated or is in the process of being terminated. Either way, - // there is nothing we can reasonably do about failure, so we ignore it. - DuplicateHandle(owning_process, handle, NULL, &handle, 0, FALSE, - DUPLICATE_CLOSE_SOURCE); - return; - } - - bool success = !!CloseHandle(handle); - DPCHECK(success); - handle = INVALID_HANDLE_VALUE; -#else -#error "Platform not yet supported." -#endif -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/embedder/platform_handle.h b/mojo/edk/embedder/platform_handle.h deleted file mode 100644 index 4866e75..0000000 --- a/mojo/edk/embedder/platform_handle.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_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> - -#include "base/process/process_handle.h" -#elif defined(OS_MACOSX) && !defined(OS_IOS) -#include <mach/mach.h> -#endif - -namespace mojo { -namespace edk { - -#if defined(OS_POSIX) -struct MOJO_SYSTEM_IMPL_EXPORT PlatformHandle { - PlatformHandle() {} - explicit PlatformHandle(int handle) : handle(handle) {} -#if defined(OS_MACOSX) && !defined(OS_IOS) - explicit PlatformHandle(mach_port_t port) - : type(Type::MACH), port(port) {} -#endif - - void CloseIfNecessary(); - - bool is_valid() const { -#if defined(OS_MACOSX) && !defined(OS_IOS) - if (type == Type::MACH || type == Type::MACH_NAME) - return port != MACH_PORT_NULL; -#endif - return handle != -1; - } - - enum class Type { - POSIX, -#if defined(OS_MACOSX) && !defined(OS_IOS) - MACH, - // MACH_NAME isn't a real Mach port. But rather the "name" of one that can - // be resolved to a real port later. This distinction is needed so that the - // "port" doesn't try to be closed if CloseIfNecessary() is called. Having - // this also allows us to do checks in other places. - MACH_NAME, -#endif - }; - Type type = Type::POSIX; - - int handle = -1; - - // A POSIX handle may be a listen handle that can accept a connection. - bool needs_connection = false; - -#if defined(OS_MACOSX) && !defined(OS_IOS) - mach_port_t port = MACH_PORT_NULL; -#endif -}; -#elif defined(OS_WIN) -struct MOJO_SYSTEM_IMPL_EXPORT PlatformHandle { - PlatformHandle() : PlatformHandle(INVALID_HANDLE_VALUE) {} - explicit PlatformHandle(HANDLE handle) - : handle(handle), owning_process(base::GetCurrentProcessHandle()) {} - - void CloseIfNecessary(); - - bool is_valid() const { return handle != INVALID_HANDLE_VALUE; } - - HANDLE handle; - - // A Windows HANDLE may be duplicated to another process but not yet sent to - // that process. This tracks the handle's owning process. - base::ProcessHandle owning_process; - - // A Windows HANDLE may be an unconnected named pipe. In this case, we need to - // wait for a connection before communicating on the pipe. - bool needs_connection = false; -}; -#else -#error "Platform not yet supported." -#endif - -} // namespace edk -} // 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 fa683e4..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 edk { - -// 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 edk -} // 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 5604f96..0000000 --- a/mojo/edk/embedder/platform_handle_utils_posix.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 "mojo/edk/embedder/platform_handle_utils.h" - -#include <unistd.h> - -#include "base/logging.h" - -namespace mojo { -namespace edk { - -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). - PlatformHandle duped(dup(platform_handle.handle)); - duped.needs_connection = platform_handle.needs_connection; - return ScopedPlatformHandle(duped); -} - -} // namespace edk -} // 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 32ed49a..0000000 --- a/mojo/edk/embedder/platform_handle_utils_win.cc +++ /dev/null @@ -1,28 +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 edk { - -ScopedPlatformHandle DuplicatePlatformHandle(PlatformHandle platform_handle) { - DCHECK(platform_handle.is_valid()); - - HANDLE new_handle; - CHECK_NE(platform_handle.handle, INVALID_HANDLE_VALUE); - 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 edk -} // 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 9892b23..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 <memory> -#include <vector> - -#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 edk { - -using PlatformHandleVector = std::vector<PlatformHandle>; - -// 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; - } -}; - -using ScopedPlatformHandleVectorPtr = - std::unique_ptr<PlatformHandleVector, PlatformHandleVectorDeleter>; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_PLATFORM_HANDLE_VECTOR_H_ diff --git a/mojo/edk/embedder/platform_shared_buffer.cc b/mojo/edk/embedder/platform_shared_buffer.cc deleted file mode 100644 index 58af44d..0000000 --- a/mojo/edk/embedder/platform_shared_buffer.cc +++ /dev/null @@ -1,325 +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_shared_buffer.h" - -#include <stddef.h> - -#include <utility> - -#include "base/logging.h" -#include "base/memory/ptr_util.h" -#include "base/memory/shared_memory.h" -#include "base/process/process_handle.h" -#include "base/sys_info.h" -#include "mojo/edk/embedder/platform_handle_utils.h" - -#if defined(OS_NACL) -// For getpagesize() on NaCl. -#include <unistd.h> -#endif - -namespace mojo { -namespace edk { - -namespace { - -// Takes ownership of |memory_handle|. -ScopedPlatformHandle SharedMemoryToPlatformHandle( - base::SharedMemoryHandle memory_handle) { -#if defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS)) - return ScopedPlatformHandle(PlatformHandle(memory_handle.fd)); -#elif defined(OS_WIN) - return ScopedPlatformHandle(PlatformHandle(memory_handle.GetHandle())); -#else - return ScopedPlatformHandle(PlatformHandle(memory_handle.GetMemoryObject())); -#endif -} - -} // namespace - -// static -PlatformSharedBuffer* PlatformSharedBuffer::Create(size_t num_bytes) { - DCHECK_GT(num_bytes, 0u); - - PlatformSharedBuffer* rv = new PlatformSharedBuffer(num_bytes, false); - if (!rv->Init()) { - // We can't just delete it directly, due to the "in destructor" (debug) - // check. - scoped_refptr<PlatformSharedBuffer> deleter(rv); - return nullptr; - } - - return rv; -} - -// static -PlatformSharedBuffer* PlatformSharedBuffer::CreateFromPlatformHandle( - size_t num_bytes, - bool read_only, - ScopedPlatformHandle platform_handle) { - DCHECK_GT(num_bytes, 0u); - - PlatformSharedBuffer* rv = new PlatformSharedBuffer(num_bytes, read_only); - if (!rv->InitFromPlatformHandle(std::move(platform_handle))) { - // We can't just delete it directly, due to the "in destructor" (debug) - // check. - scoped_refptr<PlatformSharedBuffer> deleter(rv); - return nullptr; - } - - return rv; -} - -// static -PlatformSharedBuffer* PlatformSharedBuffer::CreateFromPlatformHandlePair( - size_t num_bytes, - ScopedPlatformHandle rw_platform_handle, - ScopedPlatformHandle ro_platform_handle) { - DCHECK_GT(num_bytes, 0u); - DCHECK(rw_platform_handle.is_valid()); - DCHECK(ro_platform_handle.is_valid()); - - PlatformSharedBuffer* rv = new PlatformSharedBuffer(num_bytes, false); - if (!rv->InitFromPlatformHandlePair(std::move(rw_platform_handle), - std::move(ro_platform_handle))) { - // We can't just delete it directly, due to the "in destructor" (debug) - // check. - scoped_refptr<PlatformSharedBuffer> deleter(rv); - return nullptr; - } - - return rv; -} - -// static -PlatformSharedBuffer* PlatformSharedBuffer::CreateFromSharedMemoryHandle( - size_t num_bytes, - bool read_only, - base::SharedMemoryHandle handle) { - DCHECK_GT(num_bytes, 0u); - - PlatformSharedBuffer* rv = new PlatformSharedBuffer(num_bytes, read_only); - rv->InitFromSharedMemoryHandle(handle); - - return rv; -} - -size_t PlatformSharedBuffer::GetNumBytes() const { - return num_bytes_; -} - -bool PlatformSharedBuffer::IsReadOnly() const { - return read_only_; -} - -std::unique_ptr<PlatformSharedBufferMapping> PlatformSharedBuffer::Map( - size_t offset, - size_t length) { - if (!IsValidMap(offset, length)) - return nullptr; - - return MapNoCheck(offset, length); -} - -bool PlatformSharedBuffer::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; -} - -std::unique_ptr<PlatformSharedBufferMapping> PlatformSharedBuffer::MapNoCheck( - size_t offset, - size_t length) { - DCHECK(IsValidMap(offset, length)); - DCHECK(shared_memory_); - base::SharedMemoryHandle handle; - { - base::AutoLock locker(lock_); - handle = base::SharedMemory::DuplicateHandle(shared_memory_->handle()); - } - if (handle == base::SharedMemory::NULLHandle()) - return nullptr; - - std::unique_ptr<PlatformSharedBufferMapping> mapping( - new PlatformSharedBufferMapping(handle, read_only_, offset, length)); - if (mapping->Map()) - return base::WrapUnique(mapping.release()); - - return nullptr; -} - -ScopedPlatformHandle PlatformSharedBuffer::DuplicatePlatformHandle() { - DCHECK(shared_memory_); - base::SharedMemoryHandle handle; - { - base::AutoLock locker(lock_); - handle = base::SharedMemory::DuplicateHandle(shared_memory_->handle()); - } - if (handle == base::SharedMemory::NULLHandle()) - return ScopedPlatformHandle(); - - return SharedMemoryToPlatformHandle(handle); -} - -ScopedPlatformHandle PlatformSharedBuffer::PassPlatformHandle() { - DCHECK(HasOneRef()); - - // The only way to pass a handle from base::SharedMemory is to duplicate it - // and close the original. - ScopedPlatformHandle handle = DuplicatePlatformHandle(); - - base::AutoLock locker(lock_); - shared_memory_->Close(); - return handle; -} - -base::SharedMemoryHandle PlatformSharedBuffer::DuplicateSharedMemoryHandle() { - DCHECK(shared_memory_); - - base::AutoLock locker(lock_); - return base::SharedMemory::DuplicateHandle(shared_memory_->handle()); -} - -PlatformSharedBuffer* PlatformSharedBuffer::CreateReadOnlyDuplicate() { - DCHECK(shared_memory_); - - if (ro_shared_memory_) { - base::AutoLock locker(lock_); - base::SharedMemoryHandle handle; - handle = base::SharedMemory::DuplicateHandle(ro_shared_memory_->handle()); - if (handle == base::SharedMemory::NULLHandle()) - return nullptr; - return CreateFromSharedMemoryHandle(num_bytes_, true, handle); - } - - base::SharedMemoryHandle handle; - bool success; - { - base::AutoLock locker(lock_); - success = shared_memory_->ShareReadOnlyToProcess( - base::GetCurrentProcessHandle(), &handle); - } - if (!success || handle == base::SharedMemory::NULLHandle()) - return nullptr; - - return CreateFromSharedMemoryHandle(num_bytes_, true, handle); -} - -PlatformSharedBuffer::PlatformSharedBuffer(size_t num_bytes, bool read_only) - : num_bytes_(num_bytes), read_only_(read_only) {} - -PlatformSharedBuffer::~PlatformSharedBuffer() {} - -bool PlatformSharedBuffer::Init() { - DCHECK(!shared_memory_); - DCHECK(!read_only_); - - base::SharedMemoryCreateOptions options; - options.size = num_bytes_; - // By default, we can share as read-only. - options.share_read_only = true; - - shared_memory_.reset(new base::SharedMemory); - return shared_memory_->Create(options); -} - -bool PlatformSharedBuffer::InitFromPlatformHandle( - ScopedPlatformHandle platform_handle) { - DCHECK(!shared_memory_); - -#if defined(OS_WIN) - base::SharedMemoryHandle handle(platform_handle.release().handle, - base::GetCurrentProcId()); -#elif defined(OS_MACOSX) && !defined(OS_IOS) - base::SharedMemoryHandle handle; - handle = base::SharedMemoryHandle(platform_handle.release().port, num_bytes_, - base::GetCurrentProcId()); -#else - base::SharedMemoryHandle handle(platform_handle.release().handle, false); -#endif - - shared_memory_.reset(new base::SharedMemory(handle, read_only_)); - return true; -} - -bool PlatformSharedBuffer::InitFromPlatformHandlePair( - ScopedPlatformHandle rw_platform_handle, - ScopedPlatformHandle ro_platform_handle) { -#if defined(OS_MACOSX) - NOTREACHED(); - return false; -#else // defined(OS_MACOSX) - -#if defined(OS_WIN) - base::SharedMemoryHandle handle(rw_platform_handle.release().handle, - base::GetCurrentProcId()); - base::SharedMemoryHandle ro_handle(ro_platform_handle.release().handle, - base::GetCurrentProcId()); -#else // defined(OS_WIN) - base::SharedMemoryHandle handle(rw_platform_handle.release().handle, false); - base::SharedMemoryHandle ro_handle(ro_platform_handle.release().handle, - false); -#endif // defined(OS_WIN) - - DCHECK(!shared_memory_); - shared_memory_.reset(new base::SharedMemory(handle, false)); - ro_shared_memory_.reset(new base::SharedMemory(ro_handle, true)); - return true; - -#endif // defined(OS_MACOSX) -} - -void PlatformSharedBuffer::InitFromSharedMemoryHandle( - base::SharedMemoryHandle handle) { - DCHECK(!shared_memory_); - - shared_memory_.reset(new base::SharedMemory(handle, read_only_)); -} - -PlatformSharedBufferMapping::~PlatformSharedBufferMapping() { - Unmap(); -} - -void* PlatformSharedBufferMapping::GetBase() const { - return base_; -} - -size_t PlatformSharedBufferMapping::GetLength() const { - return length_; -} - -bool PlatformSharedBufferMapping::Map() { - // Mojo shared buffers can be mapped at any offset. However, - // base::SharedMemory must be mapped at a page boundary. So calculate what the - // nearest whole page offset is, and build a mapping that's offset from that. -#if defined(OS_NACL) - // base::SysInfo isn't available under NaCl. - size_t page_size = getpagesize(); -#else - size_t page_size = base::SysInfo::VMAllocationGranularity(); -#endif - size_t offset_rounding = offset_ % page_size; - size_t real_offset = offset_ - offset_rounding; - size_t real_length = length_ + offset_rounding; - - if (!shared_memory_.MapAt(static_cast<off_t>(real_offset), real_length)) - return false; - - base_ = static_cast<char*>(shared_memory_.memory()) + offset_rounding; - return true; -} - -void PlatformSharedBufferMapping::Unmap() { - shared_memory_.Unmap(); -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/embedder/platform_shared_buffer.h b/mojo/edk/embedder/platform_shared_buffer.h deleted file mode 100644 index 45be723..0000000 --- a/mojo/edk/embedder/platform_shared_buffer.h +++ /dev/null @@ -1,178 +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 <memory> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/shared_memory.h" -#include "base/memory/shared_memory_handle.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace edk { - -class PlatformSharedBufferMapping; - -// |PlatformSharedBuffer| is 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. -class MOJO_SYSTEM_IMPL_EXPORT PlatformSharedBuffer - : public base::RefCountedThreadSafe<PlatformSharedBuffer> { - public: - // Creates a shared buffer of size |num_bytes| bytes (initially zero-filled). - // |num_bytes| must be nonzero. Returns null on failure. - static PlatformSharedBuffer* Create(size_t num_bytes); - - // Creates a shared buffer of size |num_bytes| from the existing platform - // handle |platform_handle|. Returns null on failure. - static PlatformSharedBuffer* CreateFromPlatformHandle( - size_t num_bytes, - bool read_only, - ScopedPlatformHandle platform_handle); - - // Creates a shared buffer of size |num_bytes| from the existing pair of - // read/write and read-only handles |rw_platform_handle| and - // |ro_platform_handle|. Returns null on failure. - static PlatformSharedBuffer* CreateFromPlatformHandlePair( - size_t num_bytes, - ScopedPlatformHandle rw_platform_handle, - ScopedPlatformHandle ro_platform_handle); - - // Creates a shared buffer of size |num_bytes| from the existing shared memory - // handle |handle|. - static PlatformSharedBuffer* CreateFromSharedMemoryHandle( - size_t num_bytes, - bool read_only, - base::SharedMemoryHandle handle); - - // Gets the size of shared buffer (in number of bytes). - size_t GetNumBytes() const; - - // Returns whether this shared buffer is read-only. - bool IsReadOnly() const; - - // 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. - std::unique_ptr<PlatformSharedBufferMapping> Map(size_t offset, - size_t length); - - // Checks if |offset| and |length| are valid arguments. - bool IsValidMap(size_t offset, size_t length); - - // Like |Map()|, but doesn't check its arguments (which should have been - // preflighted using |IsValidMap()|). - std::unique_ptr<PlatformSharedBufferMapping> MapNoCheck(size_t offset, - size_t length); - - // Duplicates the underlying platform handle and passes it to the caller. - ScopedPlatformHandle DuplicatePlatformHandle(); - - // Duplicates the underlying shared memory handle and passes it to the caller. - base::SharedMemoryHandle DuplicateSharedMemoryHandle(); - - // 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. - ScopedPlatformHandle PassPlatformHandle(); - - // Create and return a read-only duplicate of this shared buffer. If this - // shared buffer isn't capable of returning a read-only duplicate, then - // nullptr will be returned. - PlatformSharedBuffer* CreateReadOnlyDuplicate(); - - private: - friend class base::RefCountedThreadSafe<PlatformSharedBuffer>; - - PlatformSharedBuffer(size_t num_bytes, bool read_only); - ~PlatformSharedBuffer(); - - // 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); - - bool InitFromPlatformHandlePair(ScopedPlatformHandle rw_platform_handle, - ScopedPlatformHandle ro_platform_handle); - - void InitFromSharedMemoryHandle(base::SharedMemoryHandle handle); - - const size_t num_bytes_; - const bool read_only_; - - base::Lock lock_; - std::unique_ptr<base::SharedMemory> shared_memory_; - - // A separate read-only shared memory for platforms that need it (i.e. Linux - // with sync broker). - std::unique_ptr<base::SharedMemory> ro_shared_memory_; - - DISALLOW_COPY_AND_ASSIGN(PlatformSharedBuffer); -}; - -// A mapping of a |PlatformSharedBuffer| (compararable to a "file view" in -// Windows); see above. Created by |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: - ~PlatformSharedBufferMapping(); - - void* GetBase() const; - size_t GetLength() const; - - private: - friend class PlatformSharedBuffer; - - PlatformSharedBufferMapping(base::SharedMemoryHandle handle, - bool read_only, - size_t offset, - size_t length) - : offset_(offset), - length_(length), - base_(nullptr), - shared_memory_(handle, read_only) {} - - bool Map(); - void Unmap(); - - const size_t offset_; - const size_t length_; - void* base_; - - // Since mapping life cycles are separate from PlatformSharedBuffer and a - // buffer can be mapped multiple times, we have our own SharedMemory object - // created from a duplicate handle. - base::SharedMemory shared_memory_; - - DISALLOW_COPY_AND_ASSIGN(PlatformSharedBufferMapping); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_PLATFORM_SHARED_BUFFER_H_ diff --git a/mojo/edk/embedder/platform_shared_buffer_unittest.cc b/mojo/edk/embedder/platform_shared_buffer_unittest.cc deleted file mode 100644 index f1593f0..0000000 --- a/mojo/edk/embedder/platform_shared_buffer_unittest.cc +++ /dev/null @@ -1,227 +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_shared_buffer.h" - -#include <stddef.h> - -#include <limits> -#include <memory> - -#include "base/memory/ref_counted.h" -#include "base/memory/shared_memory.h" -#include "base/sys_info.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_WIN) -#include "base/win/windows_version.h" -#endif - -namespace mojo { -namespace edk { -namespace { - -TEST(PlatformSharedBufferTest, 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<PlatformSharedBuffer> buffer( - PlatformSharedBuffer::Create(kNumBytes)); - ASSERT_TRUE(buffer); - - // Map it all, scribble some stuff, and then unmap it. - { - EXPECT_TRUE(buffer->IsValidMap(0, kNumBytes)); - std::unique_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. - std::unique_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; - - std::unique_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))); - std::unique_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(PlatformSharedBufferTest, InvalidMappings) { - scoped_refptr<PlatformSharedBuffer> buffer(PlatformSharedBuffer::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(PlatformSharedBufferTest, TooBig) { - // If |size_t| is 32-bit, it's quite possible/likely that |Create()| succeeds - // (since it only involves creating a 4 GB file). - size_t max_size = std::numeric_limits<size_t>::max(); - if (base::SysInfo::AmountOfVirtualMemory() && - max_size > static_cast<size_t>(base::SysInfo::AmountOfVirtualMemory())) - max_size = static_cast<size_t>(base::SysInfo::AmountOfVirtualMemory()); - scoped_refptr<PlatformSharedBuffer> buffer( - PlatformSharedBuffer::Create(max_size)); - // But, assuming |sizeof(size_t) == sizeof(void*)|, mapping all of it should - // always fail. - if (buffer) - EXPECT_FALSE(buffer->Map(0, max_size)); -} - -// 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(PlatformSharedBufferTest, MappingsDistinct) { - scoped_refptr<PlatformSharedBuffer> buffer(PlatformSharedBuffer::Create(100)); - std::unique_ptr<PlatformSharedBufferMapping> mapping1(buffer->Map(0, 100)); - std::unique_ptr<PlatformSharedBufferMapping> mapping2(buffer->Map(0, 100)); - EXPECT_NE(mapping1->GetBase(), mapping2->GetBase()); -} - -TEST(PlatformSharedBufferTest, BufferZeroInitialized) { - static const size_t kSizes[] = {10, 100, 1000, 10000, 100000}; - for (size_t i = 0; i < arraysize(kSizes); i++) { - scoped_refptr<PlatformSharedBuffer> buffer( - PlatformSharedBuffer::Create(kSizes[i])); - std::unique_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(PlatformSharedBufferTest, MappingsOutliveBuffer) { - std::unique_ptr<PlatformSharedBufferMapping> mapping1; - std::unique_ptr<PlatformSharedBufferMapping> mapping2; - - { - scoped_refptr<PlatformSharedBuffer> buffer( - PlatformSharedBuffer::Create(100)); - mapping1 = buffer->Map(0, 100); - mapping2 = buffer->Map(50, 50); - 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]); -} - -TEST(PlatformSharedBufferTest, FromSharedMemoryHandle) { - const size_t kBufferSize = 1234; - base::SharedMemoryCreateOptions options; - options.size = kBufferSize; - base::SharedMemory shared_memory; - ASSERT_TRUE(shared_memory.Create(options)); - ASSERT_TRUE(shared_memory.Map(kBufferSize)); - - base::SharedMemoryHandle shm_handle = - base::SharedMemory::DuplicateHandle(shared_memory.handle()); - scoped_refptr<PlatformSharedBuffer> simple_buffer( - PlatformSharedBuffer::CreateFromSharedMemoryHandle( - kBufferSize, false /* read_only */, shm_handle)); - ASSERT_TRUE(simple_buffer); - - std::unique_ptr<PlatformSharedBufferMapping> mapping = - simple_buffer->Map(0, kBufferSize); - ASSERT_TRUE(mapping); - - const int kOffset = 123; - char* base_memory = static_cast<char*>(shared_memory.memory()); - char* mojo_memory = static_cast<char*>(mapping->GetBase()); - base_memory[kOffset] = 0; - EXPECT_EQ(0, mojo_memory[kOffset]); - base_memory[kOffset] = 'a'; - EXPECT_EQ('a', mojo_memory[kOffset]); - mojo_memory[kOffset] = 'z'; - EXPECT_EQ('z', base_memory[kOffset]); -} - -} // namespace -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/embedder/scoped_ipc_support.cc b/mojo/edk/embedder/scoped_ipc_support.cc deleted file mode 100644 index f67210a..0000000 --- a/mojo/edk/embedder/scoped_ipc_support.cc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/embedder/scoped_ipc_support.h" - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/thread_restrictions.h" -#include "mojo/edk/embedder/embedder.h" - -namespace mojo { -namespace edk { - -ScopedIPCSupport::ScopedIPCSupport( - scoped_refptr<base::TaskRunner> io_thread_task_runner, - ShutdownPolicy shutdown_policy) : shutdown_policy_(shutdown_policy) { - InitIPCSupport(io_thread_task_runner); -} - -ScopedIPCSupport::~ScopedIPCSupport() { - if (shutdown_policy_ == ShutdownPolicy::FAST) { - ShutdownIPCSupport(base::Bind(&base::DoNothing)); - return; - } - - base::WaitableEvent shutdown_event( - base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - ShutdownIPCSupport(base::Bind(&base::WaitableEvent::Signal, - base::Unretained(&shutdown_event))); - - base::ThreadRestrictions::ScopedAllowWait allow_io; - shutdown_event.Wait(); -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/embedder/scoped_ipc_support.h b/mojo/edk/embedder/scoped_ipc_support.h deleted file mode 100644 index 22d8e50..0000000 --- a/mojo/edk/embedder/scoped_ipc_support.h +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_EMBEDDER_SCOPED_IPC_SUPPORT_H_ -#define MOJO_EDK_EMBEDDER_SCOPED_IPC_SUPPORT_H_ - -#include "base/memory/ref_counted.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace base { -class TaskRunner; -} - -namespace mojo { -namespace edk { - -// A simple class that calls |InitIPCSupport()| on construction and -// |ShutdownIPCSupport()| on destruction, blocking the destructor on clean IPC -// shutdown completion. -class MOJO_SYSTEM_IMPL_EXPORT ScopedIPCSupport { - public: - // ShutdownPolicy is a type for specifying the desired Mojo IPC support - // shutdown behavior used during ScopedIPCSupport destruction. - // - // What follows is a quick overview of why shutdown behavior is interesting - // and how you might decide which behavior is right for your use case. - // - // BACKGROUND - // ========== - // - // In order to facilitate efficient and reliable transfer of Mojo message pipe - // endpoints across process boundaries, the underlying model for a message - // pipe is actually a self-collapsing cycle of "ports." See - // //mojo/edk/system/ports for gritty implementation details. - // - // Ports are essentially globally unique identifiers used for system-wide - // message routing. Every message pipe consists of at least two such ports: - // the pipe's two concrete endpoints. - // - // When a message pipe endpoint is transferred over another message pipe, that - // endpoint's port (which subsequently exists only internally with no - // publicly-reachable handle) enters a transient proxying state for the - // remainder of its lifetime. Once sufficient information has been - // proagated throughout the system and this proxying port can be safely - // bypassed, it is garbage-collected. - // - // If a process is terminated while hosting any active proxy ports, this - // will necessarily break the message pipe(s) to which those ports belong. - // - // WHEN TO USE CLEAN SHUTDOWN - // ========================== - // - // Consider three processes, A, B, and C. Suppose A creates a message pipe, - // sending one end to B and the other to C. For some brief period of time, - // messages sent by B or C over this pipe may be proxied through A. - // - // If A is suddenly terminated, there may be no way for B's messages to reach - // C (and vice versa), since the message pipe state may not have been fully - // propagated to all concerned processes in the system. As such, both B and C - // may have no choice but to signal peer closure on their respective ends of - // the pipe, and thus the pipe may be broken despite a lack of intent by - // either B or C. - // - // This can also happen if A creates a pipe and passes one end to B, who then - // passes it along to C. B may temporarily proxy messages for this pipe - // between A and C, and B's sudden demise will in turn beget the pipe's - // own sudden demise. - // - // In situations where these sort of arrangements may occur, potentially - // proxying processes must ensure they are shut down cleanly in order to avoid - // flaky system behavior. - // - // WHEN TO USE FAST SHUTDOWN - // ========================= - // - // As a general rule of thumb, if your process never creates a message pipe - // where both ends are passed to other processes, or never forwards a pipe - // endpoint from one process to another, fast shutdown is safe. Satisfaction - // of these constraints can be difficult to prove though, so clean shutdown is - // a safe default choice. - // - // Content renderer processes are a good example of a case where fast shutdown - // is safe, because as a matter of security and stability, a renderer cannot - // be trusted to do any proxying on behalf of two other processes anyway. - // - // There are other practical scenarios where fast shutdown is safe even if - // the process may have live proxies. For example, content's browser process - // is treated as a sort of master process in the system, in the sense that if - // the browser is terminated, no other part of the system is expected to - // continue normal operation anyway. In this case the side-effects of fast - // shutdown are irrelevant, so fast shutdown is preferred. - enum class ShutdownPolicy { - // Clean shutdown. This causes the ScopedIPCSupport destructor to *block* - // the calling thread until clean shutdown is complete. See explanation - // above for details. - CLEAN, - - // Fast shutdown. In this case a cheap best-effort attempt is made to - // shut down the IPC system, but no effort is made to wait for its - // completion. See explanation above for details. - FAST, - }; - - ScopedIPCSupport(scoped_refptr<base::TaskRunner> io_thread_task_runner, - ShutdownPolicy shutdown_policy); - ~ScopedIPCSupport(); - - private: - const ShutdownPolicy shutdown_policy_; - - DISALLOW_COPY_AND_ASSIGN(ScopedIPCSupport); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_SCOPED_IPC_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 15b80ea..0000000 --- a/mojo/edk/embedder/scoped_platform_handle.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_EMBEDDER_SCOPED_PLATFORM_HANDLE_H_ -#define MOJO_EDK_EMBEDDER_SCOPED_PLATFORM_HANDLE_H_ - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "mojo/edk/embedder/platform_handle.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/macros.h" - -namespace mojo { -namespace edk { - -class MOJO_SYSTEM_IMPL_EXPORT ScopedPlatformHandle { - public: - ScopedPlatformHandle() {} - explicit ScopedPlatformHandle(PlatformHandle handle) : handle_(handle) {} - ~ScopedPlatformHandle() { handle_.CloseIfNecessary(); } - - // Move-only constructor and operator=. - ScopedPlatformHandle(ScopedPlatformHandle&& other) - : handle_(other.release()) {} - - ScopedPlatformHandle& operator=(ScopedPlatformHandle&& other) { - if (this != &other) - handle_ = other.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_; - - DISALLOW_COPY_AND_ASSIGN(ScopedPlatformHandle); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_SCOPED_PLATFORM_HANDLE_H_ diff --git a/mojo/edk/embedder/test_embedder.cc b/mojo/edk/embedder/test_embedder.cc deleted file mode 100644 index 9658010..0000000 --- a/mojo/edk/embedder/test_embedder.cc +++ /dev/null @@ -1,46 +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 <memory> - -#include "base/logging.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/embedder/embedder_internal.h" -#include "mojo/edk/system/core.h" -#include "mojo/edk/system/handle_table.h" - -namespace mojo { - -namespace edk { -namespace internal { - -bool ShutdownCheckNoLeaks(Core* core) { - std::vector<MojoHandle> leaked_handles; - core->GetActiveHandlesForTest(&leaked_handles); - if (leaked_handles.empty()) - return true; - for (auto handle : leaked_handles) - LOG(ERROR) << "Mojo embedder shutdown: Leaking handle " << handle; - return false; -} - -} // namespace internal - -namespace test { - -bool Shutdown() { - CHECK(internal::g_core); - bool rv = internal::ShutdownCheckNoLeaks(internal::g_core); - delete internal::g_core; - internal::g_core = nullptr; - - return rv; -} - -} // namespace test -} // namespace edk - -} // namespace mojo diff --git a/mojo/edk/embedder/test_embedder.h b/mojo/edk/embedder/test_embedder.h deleted file mode 100644 index c64ba17..0000000 --- a/mojo/edk/embedder/test_embedder.h +++ /dev/null @@ -1,28 +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 edk { -namespace test { - -// 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 edk -} // 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 fc1e03c..0000000 --- a/mojo/edk/js/BUILD.gn +++ /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. - -component("js") { - sources = [ - "core.cc", - "core.h", - "drain_data.cc", - "drain_data.h", - "handle.cc", - "handle.h", - "handle_close_observer.h", - "js_export.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", - ] - - deps = [ - "//mojo/public/cpp/system", - ] - defines = [ "MOJO_JS_IMPLEMENTATION" ] -} diff --git a/mojo/edk/js/core.cc b/mojo/edk/js/core.cc deleted file mode 100644 index baccc4c..0000000 --- a/mojo/edk/js/core.cc +++ /dev/null @@ -1,454 +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 <stddef.h> -#include <stdint.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" -#include "mojo/public/cpp/system/wait.h" - -namespace mojo { -namespace edk { -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 QueryHandleSignalsState(const gin::Arguments& args, - mojo::Handle handle) { - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); - if (!handle.is_valid()) { - dictionary.Set("result", MOJO_RESULT_INVALID_ARGUMENT); - } else { - HandleSignalsState state = handle.QuerySignalsState(); - dictionary.Set("result", MOJO_RESULT_OK); - dictionary.Set("satisfiedSignals", state.satisfied_signals); - dictionary.Set("satisfiableSignals", state.satisfiable_signals); - } - return dictionary; -} - -gin::Dictionary WaitHandle(const gin::Arguments& args, - mojo::Handle handle, - MojoHandleSignals signals) { - v8::Isolate* isolate = args.isolate(); - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(isolate); - - MojoHandleSignalsState signals_state; - MojoResult result = Wait(handle, signals, &signals_state); - dictionary.Set("result", result); - - if (result != MOJO_RESULT_OK && result != MOJO_RESULT_FAILED_PRECONDITION) { - 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 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, so release them here. - 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::edk::js::HandleWrapper> ignore_handle; - return gin::Converter<gin::Handle<mojo::edk::js::HandleWrapper>>::FromV8( - args->isolate(), val, &ignore_handle); -} - -gin::Dictionary CreateSharedBuffer(const gin::Arguments& args, - uint64_t num_bytes, - MojoCreateSharedBufferOptionsFlags flags) { - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); - MojoHandle handle = MOJO_HANDLE_INVALID; - MojoCreateSharedBufferOptions options; - // The |flags| is mandatory parameter for CreateSharedBuffer, and it will - // be always initialized in MojoCreateSharedBufferOptions struct. For - // forward compatibility, set struct_size to be 8 bytes (struct_size + flags), - // so that validator will only check the field that is set. - options.struct_size = 8; - options.flags = flags; - MojoResult result = MojoCreateSharedBuffer(&options, num_bytes, &handle); - if (result != MOJO_RESULT_OK) { - dictionary.Set("result", result); - return dictionary; - } - - dictionary.Set("result", result); - dictionary.Set("handle", mojo::Handle(handle)); - - return dictionary; -} - -gin::Dictionary DuplicateBufferHandle( - const gin::Arguments& args, - mojo::Handle handle, - MojoDuplicateBufferHandleOptionsFlags flags) { - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); - MojoHandle duped = MOJO_HANDLE_INVALID; - MojoDuplicateBufferHandleOptions options; - // The |flags| is mandatory parameter for DuplicateBufferHandle, and it will - // be always initialized in MojoDuplicateBufferHandleOptions struct. For - // forward compatibility, set struct_size to be 8 bytes (struct_size + flags), - // so that validator will only check the field that is set. - options.struct_size = 8; - options.flags = flags; - MojoResult result = - MojoDuplicateBufferHandle(handle.value(), &options, &duped); - if (result != MOJO_RESULT_OK) { - dictionary.Set("result", result); - return dictionary; - } - - dictionary.Set("result", result); - dictionary.Set("handle", mojo::Handle(duped)); - - return dictionary; -} - -gin::Dictionary MapBuffer(const gin::Arguments& args, - mojo::Handle handle, - uint64_t offset, - uint64_t num_bytes, - MojoMapBufferFlags flags) { - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); - void* data = nullptr; - MojoResult result = - MojoMapBuffer(handle.value(), offset, num_bytes, &data, flags); - if (result != MOJO_RESULT_OK) { - dictionary.Set("result", result); - return dictionary; - } - - v8::Handle<v8::ArrayBuffer> array_buffer = - v8::ArrayBuffer::New(args.isolate(), data, num_bytes); - - dictionary.Set("result", result); - dictionary.Set("buffer", array_buffer); - - return dictionary; -} - -MojoResult UnmapBuffer(const gin::Arguments& args, - const v8::Handle<v8::ArrayBuffer>& buffer) { - // Buffer must be external, created by MapBuffer - if (!buffer->IsExternal()) - return MOJO_RESULT_INVALID_ARGUMENT; - - return MojoUnmapBuffer(buffer->GetContents().Data()); -} - -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("queryHandleSignalsState", QueryHandleSignalsState) - .SetMethod("wait", WaitHandle) - .SetMethod("createMessagePipe", CreateMessagePipe) - .SetMethod("writeMessage", WriteMessage) - .SetMethod("readMessage", ReadMessage) - .SetMethod("createDataPipe", CreateDataPipe) - .SetMethod("writeData", WriteData) - .SetMethod("readData", ReadData) - .SetMethod("drainData", DoDrainData) - .SetMethod("isHandle", IsHandle) - .SetMethod("createSharedBuffer", CreateSharedBuffer) - .SetMethod("duplicateBufferHandle", DuplicateBufferHandle) - .SetMethod("mapBuffer", MapBuffer) - .SetMethod("unmapBuffer", UnmapBuffer) - - .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("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("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) - .SetValue("CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE", - MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE) - - .SetValue("DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE", - MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE) - - .SetValue("DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY", - MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY) - - .SetValue("MAP_BUFFER_FLAG_NONE", MOJO_MAP_BUFFER_FLAG_NONE) - .Build(); - - data->SetObjectTemplate(&g_wrapper_info, templ); - } - - return templ->NewInstance(); -} - -} // namespace js -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/js/core.h b/mojo/edk/js/core.h deleted file mode 100644 index 97ef5df..0000000 --- a/mojo/edk/js/core.h +++ /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. - -#ifndef MOJO_EDK_JS_CORE_H_ -#define MOJO_EDK_JS_CORE_H_ - -#include "mojo/edk/js/js_export.h" -#include "v8/include/v8.h" - -namespace mojo { -namespace edk { -namespace js { - -class MOJO_JS_EXPORT Core { - public: - static const char kModuleName[]; - static v8::Local<v8::Value> GetModule(v8::Isolate* isolate); -}; - -} // namespace js -} // namespace edk -} // 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 334ced3..0000000 --- a/mojo/edk/js/drain_data.cc +++ /dev/null @@ -1,129 +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 <stddef.h> -#include <stdint.h> - -#include "base/bind.h" -#include "base/memory/ptr_util.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/system/core.h" - -namespace mojo { -namespace edk { -namespace js { - -DrainData::DrainData(v8::Isolate* isolate, mojo::Handle handle) - : isolate_(isolate), - handle_(DataPipeConsumerHandle(handle.value())), - handle_watcher_(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC) { - 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() { - resolver_.Reset(); -} - -void DrainData::WaitForData() { - handle_watcher_.Watch( - handle_.get(), MOJO_HANDLE_SIGNAL_READABLE, - base::Bind(&DrainData::DataReady, base::Unretained(this))); -} - -void DrainData::DataReady(MojoResult result) { - 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); - data_buffers_.push_back(base::MakeUnique<DataBuffer>(p, p + num_bytes)); - 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 edk -} // namespace mojo diff --git a/mojo/edk/js/drain_data.h b/mojo/edk/js/drain_data.h deleted file mode 100644 index 42da90f..0000000 --- a/mojo/edk/js/drain_data.h +++ /dev/null @@ -1,65 +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 <memory> -#include <vector> - -#include "gin/runner.h" -#include "mojo/public/cpp/system/core.h" -#include "mojo/public/cpp/system/simple_watcher.h" -#include "v8/include/v8.h" - -namespace mojo { -namespace edk { -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(); - - // Waits for data to be available. DataReady() will be notified. - void WaitForData(); - - // 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); - - using DataBuffer = std::vector<char>; - - v8::Isolate* isolate_; - ScopedDataPipeConsumerHandle handle_; - SimpleWatcher handle_watcher_; - base::WeakPtr<gin::Runner> runner_; - v8::UniquePersistent<v8::Promise::Resolver> resolver_; - std::vector<std::unique_ptr<DataBuffer>> data_buffers_; -}; - -} // namespace js -} // namespace edk -} // 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 7da8e9f..0000000 --- a/mojo/edk/js/handle.cc +++ /dev/null @@ -1,85 +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 edk { -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 (auto& observer : close_observers_) - observer.OnWillCloseHandle(); -} - -} // namespace js -} // namespace edk -} // 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::edk::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::edk::js::HandleWrapper> handle; - if (!Converter<gin::Handle<mojo::edk::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 60652ed..0000000 --- a/mojo/edk/js/handle.h +++ /dev/null @@ -1,107 +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 <stdint.h> - -#include "base/observer_list.h" -#include "gin/converter.h" -#include "gin/handle.h" -#include "gin/wrappable.h" -#include "mojo/edk/js/js_export.h" -#include "mojo/public/cpp/system/core.h" - -namespace mojo { -namespace edk { -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 MOJO_JS_EXPORT 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_; - base::ObserverList<HandleCloseObserver> close_observers_; -}; - -} // namespace js -} // namespace edk -} // 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_t conversion. It's unfortunate -// there's no way to prevent against accidental use. -// TODO(mpcomplete): define converters for all Handle subtypes. -template <> -struct MOJO_JS_EXPORT 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 MOJO_JS_EXPORT 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 MOJO_JS_EXPORT Converter<gin::Handle<mojo::edk::js::HandleWrapper>> { - static v8::Handle<v8::Value> ToV8( - v8::Isolate* isolate, - const gin::Handle<mojo::edk::js::HandleWrapper>& val) { - return val.ToV8(); - } - - static bool FromV8(v8::Isolate* isolate, - v8::Handle<v8::Value> val, - gin::Handle<mojo::edk::js::HandleWrapper>* out) { - if (val->IsNull()) { - *out = mojo::edk::js::HandleWrapper::Create(isolate, MOJO_HANDLE_INVALID); - return true; - } - - mojo::edk::js::HandleWrapper* object = NULL; - if (!Converter<mojo::edk::js::HandleWrapper*>::FromV8(isolate, val, - &object)) { - return false; - } - *out = gin::Handle<mojo::edk::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 c7b935e..0000000 --- a/mojo/edk/js/handle_close_observer.h +++ /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. - -#ifndef MOJO_EDK_JS_HANDLE_CLOSE_OBSERVER_H_ -#define MOJO_EDK_JS_HANDLE_CLOSE_OBSERVER_H_ - -namespace mojo { -namespace edk { -namespace js { - -class HandleCloseObserver { - public: - virtual void OnWillCloseHandle() = 0; - - protected: - virtual ~HandleCloseObserver() {} -}; - -} // namespace js -} // namespace edk -} // 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 dd2562f..0000000 --- a/mojo/edk/js/handle_unittest.cc +++ /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. - -#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 edk { -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 edk -} // namespace mojo diff --git a/mojo/edk/js/js_export.h b/mojo/edk/js/js_export.h deleted file mode 100644 index 179113c..0000000 --- a/mojo/edk/js/js_export.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_JS_JS_EXPORT_H_ -#define MOJO_EDK_JS_JS_EXPORT_H_ - -// Defines MOJO_JS_EXPORT so that functionality implemented by //mojo/edk/js can -// be exported to consumers. - -#if defined(COMPONENT_BUILD) -#if defined(WIN32) - -#if defined(MOJO_JS_IMPLEMENTATION) -#define MOJO_JS_EXPORT __declspec(dllexport) -#else -#define MOJO_JS_EXPORT __declspec(dllimport) -#endif // defined(MOJO_JS_IMPLEMENTATION) - -#else // defined(WIN32) -#if defined(MOJO_JS_IMPLEMENTATION) -#define MOJO_JS_EXPORT __attribute__((visibility("default"))) -#else -#define MOJO_JS_EXPORT -#endif -#endif - -#else // defined(COMPONENT_BUILD) -#define MOJO_JS_EXPORT -#endif - -#endif // MOJO_EDK_JS_JS_EXPORT_H_ diff --git a/mojo/edk/js/mojo_runner_delegate.cc b/mojo/edk/js/mojo_runner_delegate.cc deleted file mode 100644 index dda0b2c..0000000 --- a/mojo/edk/js/mojo_runner_delegate.cc +++ /dev/null @@ -1,80 +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 edk { -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(Core::kModuleName, Core::GetModule); - AddBuiltinModule(Support::kModuleName, Support::GetModule); - AddBuiltinModule(Threading::kModuleName, 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 edk -} // 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 9ab325c..0000000 --- a/mojo/edk/js/mojo_runner_delegate.h +++ /dev/null @@ -1,36 +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/edk/js/js_export.h" -#include "mojo/public/c/system/core.h" - -namespace mojo { -namespace edk { -namespace js { - -class MOJO_JS_EXPORT 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 edk -} // 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 404cb9b..0000000 --- a/mojo/edk/js/support.cc +++ /dev/null @@ -1,77 +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 edk { -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, true /* one_shot */).get(); -} - -void CancelWait(WaitingCallback* waiting_callback) { - waiting_callback->Cancel(); -} - -WaitingCallback* Watch(const gin::Arguments& args, - gin::Handle<HandleWrapper> handle, - MojoHandleSignals signals, - v8::Handle<v8::Function> callback) { - return WaitingCallback::Create( - args.isolate(), callback, handle, signals, false /* one_shot */).get(); -} - -void CancelWatch(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) - // TODO(rockot): Remove asyncWait and cancelWait. - .SetMethod("asyncWait", AsyncWait) - .SetMethod("cancelWait", CancelWait) - .SetMethod("watch", Watch) - .SetMethod("cancelWatch", CancelWatch) - .Build(); - - data->SetObjectTemplate(&g_wrapper_info, templ); - } - - return templ->NewInstance(); -} - -} // namespace js -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/js/support.h b/mojo/edk/js/support.h deleted file mode 100644 index 551f5ac..0000000 --- a/mojo/edk/js/support.h +++ /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. - -#ifndef MOJO_EDK_JS_SUPPORT_H_ -#define MOJO_EDK_JS_SUPPORT_H_ - -#include "mojo/edk/js/js_export.h" -#include "v8/include/v8.h" - -namespace mojo { -namespace edk { -namespace js { - -class MOJO_JS_EXPORT Support { - public: - static const char kModuleName[]; - static v8::Local<v8::Value> GetModule(v8::Isolate* isolate); -}; - -} // namespace js -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_JS_SUPPORT_H_ diff --git a/mojo/edk/js/tests/BUILD.gn b/mojo/edk/js/tests/BUILD.gn deleted file mode 100644 index f56c4b9..0000000 --- a/mojo/edk/js/tests/BUILD.gn +++ /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. - -import("//mojo/public/tools/bindings/mojom.gni") -import("//testing/test.gni") - -# TODO(hansmuller): The organization of tests in this directory is weird: -# * Really, js_unittests tests public stuff, so that should live in public -# and be reworked as some sort of apptest. -# * Both js_unittests and js_integration_tests should auto-generate their -# tests somehow. The .cc files are just test runner stubs, including -# explicit lists of .js files. - -group("tests") { - testonly = true - deps = [ - ":mojo_js_integration_tests", - ":mojo_js_unittests", - ] -} - -test("mojo_js_integration_tests") { - deps = [ - ":js_to_cpp_bindings", - "//gin:gin_test", - "//mojo/common", - "//mojo/edk/js", - "//mojo/edk/test:run_all_unittests", - "//mojo/public/cpp/bindings", - "//mojo/public/cpp/system", - "//mojo/public/js:bindings", - ] - - sources = [ - "js_to_cpp_tests.cc", - ] - - data = [ - "js_to_cpp_tests.js", - ] - - configs += [ "//v8:external_startup_data" ] -} - -mojom("js_to_cpp_bindings") { - sources = [ - "js_to_cpp.mojom", - ] -} - -test("mojo_js_unittests") { - deps = [ - "//base", - "//gin:gin_test", - "//mojo/edk/js", - "//mojo/edk/test:run_all_unittests", - "//mojo/edk/test:test_support", - "//mojo/public/cpp/system", - "//mojo/public/interfaces/bindings/tests:test_interfaces", - "//mojo/public/js:tests", - ] - - sources = [ - "//mojo/edk/js/handle_unittest.cc", - "run_js_unittests.cc", - ] -} 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 688b22b..0000000 --- a/mojo/edk/js/tests/js_to_cpp.mojom +++ /dev/null @@ -1,54 +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); -}; - -interface JsSide { - SetCppSide(CppSide cpp); - - 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 b6b74e3..0000000 --- a/mojo/edk/js/tests/js_to_cpp_tests.cc +++ /dev/null @@ -1,455 +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 <stddef.h> -#include <stdint.h> - -#include <string> -#include <utility> - -#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 "base/threading/thread_task_runner_handle.h" -#include "gin/array_buffer.h" -#include "gin/public/isolate_holder.h" -#include "gin/v8_initializer.h" -#include "mojo/common/data_pipe_utils.h" -#include "mojo/edk/js/mojo_runner_delegate.h" -#include "mojo/edk/js/tests/js_to_cpp.mojom.h" -#include "mojo/public/cpp/bindings/binding.h" -#include "mojo/public/cpp/bindings/lib/validation_errors.h" -#include "mojo/public/cpp/system/core.h" -#include "mojo/public/cpp/system/wait.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace edk { -namespace js { - -// Global value updated by some checks to prevent compilers from optimizing -// reads out of existence. -uint32_t 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_t kExpectedInt8Value = -65; -const int16_t kExpectedInt16Value = -16961; -const int32_t kExpectedInt32Value = -1145258561; -const int64_t kExpectedInt64Value = -77263311946305LL; - -// Positive numbers with different values in each byte, the last of -// which can survive promotion to double and back. -const uint8_t kExpectedUInt8Value = 65; -const uint16_t kExpectedUInt16Value = 16961; -const uint32_t kExpectedUInt32Value = 1145258561; -const uint64_t 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(ScopedDataPipeConsumerHandle data_pipe_handle) { - std::string buffer; - bool result = common::BlockingCopyToString(std::move(data_pipe_handle), - &buffer); - EXPECT_TRUE(result); - EXPECT_EQ(64u, buffer.size()); - for (int i = 0; i < 64; ++i) { - EXPECT_EQ(i, buffer[i]); - } -} - -void CheckMessagePipe(MessagePipeHandle message_pipe_handle) { - unsigned char buffer[100]; - uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer)); - MojoResult result = Wait(message_pipe_handle, MOJO_HANDLE_SIGNAL_READABLE); - EXPECT_EQ(MOJO_RESULT_OK, result); - result = ReadMessageRaw( - 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.emplace("coming"); - args->string_array.emplace(3); - (*args->string_array)[0] = "one"; - (*args->string_array)[1] = "two"; - (*args->string_array)[2] = "three"; - return args; -} - -void CheckSampleEchoArgs(js_to_cpp::EchoArgsPtr 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); - EXPECT_EQ(std::string("one"), (*arg->string_array)[0]); - EXPECT_EQ(std::string("two"), (*arg->string_array)[1]); - EXPECT_EQ(std::string("three"), (*arg->string_array)[2]); - CheckDataPipe(std::move(arg->data_handle)); - CheckMessagePipe(arg->message_handle.get()); -} - -void CheckSampleEchoArgsList(const js_to_cpp::EchoArgsListPtr& list) { - if (list.is_null()) - return; - CheckSampleEchoArgs(std::move(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 std::string& arg) { - for (size_t i = 0; i < arg.size(); ++i) - g_waste_accumulator += arg[i]; -} - -void CheckCorruptedString(const base::Optional<std::string>& arg) { - if (!arg) - return; - CheckCorruptedString(*arg); -} - -void CheckCorruptedStringArray( - const base::Optional<std::vector<std::string>>& string_array) { - if (!string_array) - 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_(nullptr), - js_side_(nullptr), - mishandled_messages_(0), - binding_(this) {} - ~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_; } - - void Bind(InterfaceRequest<js_to_cpp::CppSide> request) { - binding_.Bind(std::move(request)); - // Keep the pipe open even after validation errors. - binding_.EnableTestingMode(); - } - - // 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_; - mojo::Binding<js_to_cpp::CppSide> binding_; - - 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); - 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_); - - js_to_cpp::JsSidePtr js_side; - auto js_side_proxy = MakeRequest(&js_side); - - cpp_side->set_js_side(js_side.get()); - js_to_cpp::CppSidePtr cpp_side_ptr; - cpp_side->Bind(MakeRequest(&cpp_side_ptr)); - - js_side->SetCppSide(std::move(cpp_side_ptr)); - -#ifdef V8_USE_EXTERNAL_STARTUP_DATA - gin::V8Initializer::LoadV8Snapshot(); - gin::V8Initializer::LoadV8Natives(); -#endif - - gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode, - gin::IsolateHolder::kStableV8Extras, - gin::ArrayBufferAllocator::SharedInstance()); - gin::IsolateHolder instance(base::ThreadTaskRunnerHandle::Get()); - MojoRunnerDelegate delegate; - gin::ShellRunner runner(&delegate, instance.isolate()); - delegate.Start(&runner, js_side_proxy.PassMessagePipe().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) { - // These tests generate a lot of expected validation errors. Suppress logging. - mojo::internal::ScopedSuppressValidationErrorLoggingForTests log_suppression; - - 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) { - // These tests generate a lot of expected validation errors. Suppress logging. - mojo::internal::ScopedSuppressValidationErrorLoggingForTests log_suppression; - - 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 edk -} // 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 6b69fca..0000000 --- a/mojo/edk/js/tests/js_to_cpp_tests.js +++ /dev/null @@ -1,223 +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/bindings', - 'mojo/public/js/connector', - 'mojo/public/js/core', -], function (console, jsToCpp, bindings, connector, core) { - var retainedJsSide; - var retainedJsSideStub; - 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() { - this.binding = new bindings.Binding(jsToCpp.JsSide, this); - } - - JsSideConnection.prototype.setCppSide = function(cppSide) { - this.cppSide_ = cppSide; - this.cppSide_.startTest(); - }; - - 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); - } - - return function(jsSideRequestHandle) { - 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; - } - retainedJsSide = new JsSideConnection; - retainedJsSide.binding.bind(jsSideRequestHandle); - }; -}); diff --git a/mojo/edk/js/tests/run_js_unittests.cc b/mojo/edk/js/tests/run_js_unittests.cc deleted file mode 100644 index 13e796b..0000000 --- a/mojo/edk/js/tests/run_js_unittests.cc +++ /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. - -#include "base/files/file_path.h" -#include "base/macros.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 edk { -namespace js { -namespace { - -class TestRunnerDelegate : public gin::FileRunnerDelegate { - public: - TestRunnerDelegate() { - AddBuiltinModule(gin::Console::kModuleName, gin::Console::GetModule); - AddBuiltinModule(gin::TimerModule::kName, gin::TimerModule::GetModule); - AddBuiltinModule(Core::kModuleName, Core::GetModule); - AddBuiltinModule(Threading::kModuleName, Threading::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("tests") - .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_unittest.js", true); -} - -TEST(JSTest, Validation) { - RunTest("validation_unittest.js", true); -} - -} // namespace -} // namespace js -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/js/threading.cc b/mojo/edk/js/threading.cc deleted file mode 100644 index db9f12d..0000000 --- a/mojo/edk/js/threading.cc +++ /dev/null @@ -1,49 +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 edk { -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 edk -} // namespace mojo diff --git a/mojo/edk/js/threading.h b/mojo/edk/js/threading.h deleted file mode 100644 index 653d076..0000000 --- a/mojo/edk/js/threading.h +++ /dev/null @@ -1,28 +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 "mojo/edk/js/js_export.h" -#include "v8/include/v8.h" - -namespace mojo { -namespace edk { -namespace js { - -class MOJO_JS_EXPORT Threading { - public: - static const char kModuleName[]; - static v8::Local<v8::Value> GetModule(v8::Isolate* isolate); - private: - Threading(); -}; - -} // namespace js -} // namespace edk -} // 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 6ad4bd0..0000000 --- a/mojo/edk/js/waiting_callback.cc +++ /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. - -#include "mojo/edk/js/waiting_callback.h" - -#include "base/bind.h" -#include "base/message_loop/message_loop.h" -#include "gin/per_context_data.h" - -namespace mojo { -namespace edk { -namespace js { - -namespace { - -v8::Handle<v8::Private> GetHiddenPropertyName(v8::Isolate* isolate) { - return v8::Private::ForApi( - isolate, gin::StringToV8(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, - bool one_shot) { - gin::Handle<WaitingCallback> waiting_callback = gin::CreateHandle( - isolate, new WaitingCallback(isolate, callback, one_shot)); - MojoResult result = waiting_callback->watcher_.Watch( - handle_wrapper->get(), signals, - base::Bind(&WaitingCallback::OnHandleReady, - base::Unretained(waiting_callback.get()))); - - // The signals may already be unsatisfiable. - if (result == MOJO_RESULT_FAILED_PRECONDITION) - waiting_callback->OnHandleReady(MOJO_RESULT_FAILED_PRECONDITION); - - return waiting_callback; -} - -void WaitingCallback::Cancel() { - if (watcher_.IsWatching()) - watcher_.Cancel(); -} - -WaitingCallback::WaitingCallback(v8::Isolate* isolate, - v8::Handle<v8::Function> callback, - bool one_shot) - : one_shot_(one_shot), - watcher_(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC), - weak_factory_(this) { - v8::Handle<v8::Context> context = isolate->GetCurrentContext(); - runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr(); - GetWrapper(isolate) - ->SetPrivate(context, GetHiddenPropertyName(isolate), callback) - .FromJust(); -} - -WaitingCallback::~WaitingCallback() { - Cancel(); -} - -void WaitingCallback::OnHandleReady(MojoResult result) { - if (!runner_) - return; - - gin::Runner::Scope scope(runner_.get()); - v8::Isolate* isolate = runner_->GetContextHolder()->isolate(); - - v8::Handle<v8::Value> hidden_value = - GetWrapper(isolate) - ->GetPrivate(runner_->GetContextHolder()->context(), - GetHiddenPropertyName(isolate)) - .ToLocalChecked(); - 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); - - if (one_shot_ || result == MOJO_RESULT_CANCELLED) { - runner_.reset(); - Cancel(); - } -} - -} // namespace js -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/js/waiting_callback.h b/mojo/edk/js/waiting_callback.h deleted file mode 100644 index f97b389..0000000 --- a/mojo/edk/js/waiting_callback.h +++ /dev/null @@ -1,67 +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/macros.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/public/cpp/system/core.h" -#include "mojo/public/cpp/system/simple_watcher.h" - -namespace mojo { -namespace edk { -namespace js { - -class WaitingCallback : public gin::Wrappable<WaitingCallback> { - public: - static gin::WrapperInfo kWrapperInfo; - - // Creates a new WaitingCallback. - // - // If |one_shot| is true, the callback will only ever be called at most once. - // If false, the callback may be called any number of times until the - // WaitingCallback is explicitly cancelled. - static gin::Handle<WaitingCallback> Create( - v8::Isolate* isolate, - v8::Handle<v8::Function> callback, - gin::Handle<HandleWrapper> handle_wrapper, - MojoHandleSignals signals, - bool one_shot); - - // 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, - bool one_shot); - ~WaitingCallback() override; - - // Callback from the Watcher. - void OnHandleReady(MojoResult result); - - // Indicates whether this is a one-shot callback or not. If so, it uses the - // deprecated HandleWatcher to wait for signals; otherwise it uses the new - // system Watcher API. - const bool one_shot_; - - base::WeakPtr<gin::Runner> runner_; - SimpleWatcher watcher_; - base::WeakPtrFactory<WaitingCallback> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(WaitingCallback); -}; - -} // namespace js -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_JS_WAITING_CALLBACK_H_ diff --git a/mojo/edk/system/BUILD.gn b/mojo/edk/system/BUILD.gn deleted file mode 100644 index a68cd44..0000000 --- a/mojo/edk/system/BUILD.gn +++ /dev/null @@ -1,205 +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("//build/config/nacl/config.gni") -import("//testing/test.gni") -import("../../../mojo/public/tools/bindings/mojom.gni") - -if (is_android) { - import("//build/config/android/config.gni") - import("//build/config/android/rules.gni") -} - -component("system") { - output_name = "mojo_system_impl" - - sources = [ - "atomic_flag.h", - "broker.h", - "broker_host.cc", - "broker_host.h", - "broker_posix.cc", - "broker_win.cc", - "channel.cc", - "channel.h", - "channel_posix.cc", - "channel_win.cc", - "configuration.cc", - "configuration.h", - "core.cc", - "core.h", - "data_pipe_consumer_dispatcher.cc", - "data_pipe_consumer_dispatcher.h", - "data_pipe_control_message.cc", - "data_pipe_control_message.h", - "data_pipe_producer_dispatcher.cc", - "data_pipe_producer_dispatcher.h", - "dispatcher.cc", - "dispatcher.h", - "handle_signals_state.h", - "handle_table.cc", - "handle_table.h", - "mapping_table.cc", - "mapping_table.h", - "message_for_transit.cc", - "message_for_transit.h", - "message_pipe_dispatcher.cc", - "message_pipe_dispatcher.h", - "node_channel.cc", - "node_channel.h", - "node_controller.cc", - "node_controller.h", - "options_validation.h", - "platform_handle_dispatcher.cc", - "platform_handle_dispatcher.h", - "ports_message.cc", - "ports_message.h", - "request_context.cc", - "request_context.h", - "shared_buffer_dispatcher.cc", - "shared_buffer_dispatcher.h", - "watch.cc", - "watch.h", - "watcher_dispatcher.cc", - "watcher_dispatcher.h", - "watcher_set.cc", - "watcher_set.h", - ] - - defines = [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ] - - public_deps = [ - "//mojo/edk/embedder", - "//mojo/edk/embedder:platform", - "//mojo/edk/system/ports", - "//mojo/public/c/system", - "//mojo/public/cpp/system", - ] - - deps = [ - "//base", - ] - - if (!is_nacl) { - deps += [ "//crypto" ] - } - - if (is_win) { - cflags = [ "/wd4324" ] # Structure was padded due to __declspec(align()), - # which is uninteresting. - } - - if (is_mac && !is_ios) { - sources += [ - "mach_port_relay.cc", - "mach_port_relay.h", - ] - } - - if (is_nacl && !is_nacl_nonsfi) { - sources -= [ - "broker_host.cc", - "broker_posix.cc", - "channel_posix.cc", - ] - } - - # Use target_os == "chromeos" instead of is_chromeos because we need to - # build NaCl targets (i.e. IRT) for ChromeOS the same as the rest of ChromeOS. - if (is_android || target_os == "chromeos") { - defines += [ "MOJO_EDK_LEGACY_PROTOCOL" ] - } - - allow_circular_includes_from = [ "//mojo/edk/embedder" ] -} - -group("tests") { - testonly = true - deps = [ - ":mojo_system_unittests", - ] - - if (!is_ios) { - deps += [ ":mojo_message_pipe_perftests" ] - } -} - -source_set("test_utils") { - testonly = true - - sources = [ - "test_utils.cc", - "test_utils.h", - ] - - public_deps = [ - "//mojo/public/c/system", - "//mojo/public/cpp/system", - ] - - deps = [ - "//base", - "//base/test:test_support", - "//mojo/edk/test:test_support", - "//testing/gtest:gtest", - ] -} - -test("mojo_system_unittests") { - sources = [ - "channel_unittest.cc", - "core_test_base.cc", - "core_test_base.h", - "core_unittest.cc", - "message_pipe_unittest.cc", - "options_validation_unittest.cc", - "platform_handle_dispatcher_unittest.cc", - "shared_buffer_dispatcher_unittest.cc", - "shared_buffer_unittest.cc", - "signals_unittest.cc", - "watcher_unittest.cc", - ] - - if (!is_ios) { - sources += [ - "data_pipe_unittest.cc", - "multiprocess_message_pipe_unittest.cc", - "platform_wrapper_unittest.cc", - ] - } - - deps = [ - ":test_utils", - "//base", - "//base/test:test_support", - "//mojo/edk/embedder:embedder_unittests", - "//mojo/edk/system", - "//mojo/edk/system/ports:tests", - "//mojo/edk/test:run_all_unittests", - "//mojo/edk/test:test_support", - "//mojo/public/cpp/system", - "//testing/gmock", - "//testing/gtest", - ] - - allow_circular_includes_from = [ "//mojo/edk/embedder:embedder_unittests" ] -} - -if (!is_ios) { - test("mojo_message_pipe_perftests") { - sources = [ - "message_pipe_perftest.cc", - ] - - deps = [ - ":test_utils", - "//base", - "//base/test:test_support", - "//mojo/edk/system", - "//mojo/edk/test:run_all_perftests", - "//mojo/edk/test:test_support", - "//testing/gtest", - ] - } -} diff --git a/mojo/edk/system/atomic_flag.h b/mojo/edk/system/atomic_flag.h deleted file mode 100644 index 6bdcfaa..0000000 --- a/mojo/edk/system/atomic_flag.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_ATOMIC_FLAG_H_ -#define MOJO_EDK_SYSTEM_ATOMIC_FLAG_H_ - -#include "base/atomicops.h" -#include "base/macros.h" - -namespace mojo { -namespace edk { - -// AtomicFlag is a boolean flag that can be set and tested atomically. It is -// intended to be used to fast-path checks where the common case would normally -// release the governing mutex immediately after checking. -// -// Example usage: -// void DoFoo(Bar* bar) { -// AutoLock l(lock_); -// queue_.push_back(bar); -// flag_.Set(true); -// } -// -// void Baz() { -// if (!flag_) // Assume this is the common case. -// return; -// -// AutoLock l(lock_); -// ... drain queue_ ... -// flag_.Set(false); -// } -class AtomicFlag { - public: - AtomicFlag() : flag_(0) {} - ~AtomicFlag() {} - - void Set(bool value) { - base::subtle::Release_Store(&flag_, value ? 1 : 0); - } - - bool Get() const { - return base::subtle::Acquire_Load(&flag_) ? true : false; - } - - operator const bool() const { return Get(); } - - private: - base::subtle::Atomic32 flag_; - - DISALLOW_COPY_AND_ASSIGN(AtomicFlag); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_ATOMIC_FLAG_H_ diff --git a/mojo/edk/system/broker.h b/mojo/edk/system/broker.h deleted file mode 100644 index 1577972..0000000 --- a/mojo/edk/system/broker.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_BROKER_H_ -#define MOJO_EDK_SYSTEM_BROKER_H_ - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" - -namespace mojo { -namespace edk { - -class PlatformSharedBuffer; - -// The Broker is a channel to the parent process, which allows synchronous IPCs. -class Broker { - public: - // Note: This is blocking, and will wait for the first message over - // |platform_handle|. - explicit Broker(ScopedPlatformHandle platform_handle); - ~Broker(); - - // Returns the platform handle that should be used to establish a NodeChannel - // to the parent process. - ScopedPlatformHandle GetParentPlatformHandle(); - - // Request a shared buffer from the parent process. Blocks the current thread. - scoped_refptr<PlatformSharedBuffer> GetSharedBuffer(size_t num_bytes); - - private: - // Handle to the parent process, used for synchronous IPCs. - ScopedPlatformHandle sync_channel_; - - // Handle to the parent process which is recieved in the first first message - // over |sync_channel_|. - ScopedPlatformHandle parent_channel_; - - // Lock to only allow one sync message at a time. This avoids having to deal - // with message ordering since we can only have one request at a time - // in-flight. - base::Lock lock_; - - DISALLOW_COPY_AND_ASSIGN(Broker); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_BROKER_H_ diff --git a/mojo/edk/system/broker_host.cc b/mojo/edk/system/broker_host.cc deleted file mode 100644 index 6096034..0000000 --- a/mojo/edk/system/broker_host.cc +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/broker_host.h" - -#include <utility> - -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" -#include "base/threading/thread_task_runner_handle.h" -#include "mojo/edk/embedder/named_platform_channel_pair.h" -#include "mojo/edk/embedder/named_platform_handle.h" -#include "mojo/edk/embedder/platform_handle_vector.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/system/broker_messages.h" - -namespace mojo { -namespace edk { - -BrokerHost::BrokerHost(base::ProcessHandle client_process, - ScopedPlatformHandle platform_handle) -#if defined(OS_WIN) - : client_process_(client_process) -#endif -{ - CHECK(platform_handle.is_valid()); - - base::MessageLoop::current()->AddDestructionObserver(this); - - channel_ = Channel::Create(this, ConnectionParams(std::move(platform_handle)), - base::ThreadTaskRunnerHandle::Get()); - channel_->Start(); -} - -BrokerHost::~BrokerHost() { - // We're always destroyed on the creation thread, which is the IO thread. - base::MessageLoop::current()->RemoveDestructionObserver(this); - - if (channel_) - channel_->ShutDown(); -} - -bool BrokerHost::PrepareHandlesForClient(PlatformHandleVector* handles) { -#if defined(OS_WIN) - if (!Channel::Message::RewriteHandles( - base::GetCurrentProcessHandle(), client_process_, handles)) { - // NOTE: We only log an error here. We do not signal a logical error or - // prevent any message from being sent. The client should handle unexpected - // invalid handles appropriately. - DLOG(ERROR) << "Failed to rewrite one or more handles to broker client."; - return false; - } -#endif - return true; -} - -bool BrokerHost::SendChannel(ScopedPlatformHandle handle) { - CHECK(handle.is_valid()); - CHECK(channel_); - -#if defined(OS_WIN) - InitData* data; - Channel::MessagePtr message = - CreateBrokerMessage(BrokerMessageType::INIT, 1, 0, &data); - data->pipe_name_length = 0; -#else - Channel::MessagePtr message = - CreateBrokerMessage(BrokerMessageType::INIT, 1, nullptr); -#endif - ScopedPlatformHandleVectorPtr handles; - handles.reset(new PlatformHandleVector(1)); - handles->at(0) = handle.release(); - - // This may legitimately fail on Windows if the client process is in another - // session, e.g., is an elevated process. - if (!PrepareHandlesForClient(handles.get())) - return false; - - message->SetHandles(std::move(handles)); - channel_->Write(std::move(message)); - return true; -} - -#if defined(OS_WIN) - -void BrokerHost::SendNamedChannel(const base::StringPiece16& pipe_name) { - InitData* data; - base::char16* name_data; - Channel::MessagePtr message = CreateBrokerMessage( - BrokerMessageType::INIT, 0, sizeof(*name_data) * pipe_name.length(), - &data, reinterpret_cast<void**>(&name_data)); - data->pipe_name_length = static_cast<uint32_t>(pipe_name.length()); - std::copy(pipe_name.begin(), pipe_name.end(), name_data); - channel_->Write(std::move(message)); -} - -#endif // defined(OS_WIN) - -void BrokerHost::OnBufferRequest(uint32_t num_bytes) { - scoped_refptr<PlatformSharedBuffer> read_only_buffer; - scoped_refptr<PlatformSharedBuffer> buffer = - PlatformSharedBuffer::Create(num_bytes); - if (buffer) - read_only_buffer = buffer->CreateReadOnlyDuplicate(); - if (!read_only_buffer) - buffer = nullptr; - - Channel::MessagePtr message = CreateBrokerMessage( - BrokerMessageType::BUFFER_RESPONSE, buffer ? 2 : 0, nullptr); - if (buffer) { - ScopedPlatformHandleVectorPtr handles; - handles.reset(new PlatformHandleVector(2)); - handles->at(0) = buffer->PassPlatformHandle().release(); - handles->at(1) = read_only_buffer->PassPlatformHandle().release(); - PrepareHandlesForClient(handles.get()); - message->SetHandles(std::move(handles)); - } - - channel_->Write(std::move(message)); -} - -void BrokerHost::OnChannelMessage(const void* payload, - size_t payload_size, - ScopedPlatformHandleVectorPtr handles) { - if (payload_size < sizeof(BrokerMessageHeader)) - return; - - const BrokerMessageHeader* header = - static_cast<const BrokerMessageHeader*>(payload); - switch (header->type) { - case BrokerMessageType::BUFFER_REQUEST: - if (payload_size == - sizeof(BrokerMessageHeader) + sizeof(BufferRequestData)) { - const BufferRequestData* request = - reinterpret_cast<const BufferRequestData*>(header + 1); - OnBufferRequest(request->size); - } - break; - - default: - LOG(ERROR) << "Unexpected broker message type: " << header->type; - break; - } -} - -void BrokerHost::OnChannelError() { delete this; } - -void BrokerHost::WillDestroyCurrentMessageLoop() { delete this; } - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/broker_host.h b/mojo/edk/system/broker_host.h deleted file mode 100644 index a7995d2..0000000 --- a/mojo/edk/system/broker_host.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_BROKER_HOST_H_ -#define MOJO_EDK_SYSTEM_BROKER_HOST_H_ - -#include <stdint.h> - -#include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "base/process/process_handle.h" -#include "base/strings/string_piece.h" -#include "mojo/edk/embedder/platform_handle_vector.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/channel.h" - -namespace mojo { -namespace edk { - -// The BrokerHost is a channel to the child process, which services synchronous -// IPCs. -class BrokerHost : public Channel::Delegate, - public base::MessageLoop::DestructionObserver { - public: - BrokerHost(base::ProcessHandle client_process, ScopedPlatformHandle handle); - - // Send |handle| to the child, to be used to establish a NodeChannel to us. - bool SendChannel(ScopedPlatformHandle handle); - -#if defined(OS_WIN) - // Sends a named channel to the child. Like above, but for named pipes. - void SendNamedChannel(const base::StringPiece16& pipe_name); -#endif - - private: - ~BrokerHost() override; - - bool PrepareHandlesForClient(PlatformHandleVector* handles); - - // Channel::Delegate: - void OnChannelMessage(const void* payload, - size_t payload_size, - ScopedPlatformHandleVectorPtr handles) override; - void OnChannelError() override; - - // base::MessageLoop::DestructionObserver: - void WillDestroyCurrentMessageLoop() override; - - void OnBufferRequest(uint32_t num_bytes); - -#if defined(OS_WIN) - base::ProcessHandle client_process_; -#endif - - scoped_refptr<Channel> channel_; - - DISALLOW_COPY_AND_ASSIGN(BrokerHost); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_BROKER_HOST_H_ diff --git a/mojo/edk/system/broker_messages.h b/mojo/edk/system/broker_messages.h deleted file mode 100644 index 0f0dd9d..0000000 --- a/mojo/edk/system/broker_messages.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_BROKER_MESSAGES_H_ -#define MOJO_EDK_SYSTEM_BROKER_MESSAGES_H_ - -#include "mojo/edk/system/channel.h" - -namespace mojo { -namespace edk { - -#pragma pack(push, 1) - -enum BrokerMessageType : uint32_t { - INIT, - BUFFER_REQUEST, - BUFFER_RESPONSE, -}; - -struct BrokerMessageHeader { - BrokerMessageType type; - uint32_t padding; -}; - -static_assert(IsAlignedForChannelMessage(sizeof(BrokerMessageHeader)), - "Invalid header size."); - -struct BufferRequestData { - uint32_t size; -}; - -#if defined(OS_WIN) -struct InitData { - // NOTE: InitData in the payload is followed by string16 data with exactly - // |pipe_name_length| wide characters (i.e., |pipe_name_length|*2 bytes.) - // This applies to Windows only. - uint32_t pipe_name_length; -}; -#endif - -#pragma pack(pop) - -template <typename T> -inline Channel::MessagePtr CreateBrokerMessage( - BrokerMessageType type, - size_t num_handles, - size_t extra_data_size, - T** out_message_data, - void** out_extra_data = nullptr) { - const size_t message_size = sizeof(BrokerMessageHeader) + - sizeof(**out_message_data) + extra_data_size; - Channel::MessagePtr message(new Channel::Message(message_size, num_handles)); - BrokerMessageHeader* header = - reinterpret_cast<BrokerMessageHeader*>(message->mutable_payload()); - header->type = type; - header->padding = 0; - *out_message_data = reinterpret_cast<T*>(header + 1); - if (out_extra_data) - *out_extra_data = *out_message_data + 1; - return message; -} - -inline Channel::MessagePtr CreateBrokerMessage( - BrokerMessageType type, - size_t num_handles, - std::nullptr_t** dummy_out_data) { - Channel::MessagePtr message( - new Channel::Message(sizeof(BrokerMessageHeader), num_handles)); - BrokerMessageHeader* header = - reinterpret_cast<BrokerMessageHeader*>(message->mutable_payload()); - header->type = type; - header->padding = 0; - return message; -} - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_BROKER_MESSAGES_H_ diff --git a/mojo/edk/system/broker_posix.cc b/mojo/edk/system/broker_posix.cc deleted file mode 100644 index 8742f70..0000000 --- a/mojo/edk/system/broker_posix.cc +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/broker.h" - -#include <fcntl.h> -#include <unistd.h> - -#include <utility> - -#include "base/logging.h" -#include "mojo/edk/embedder/embedder_internal.h" -#include "mojo/edk/embedder/platform_channel_utils_posix.h" -#include "mojo/edk/embedder/platform_handle_utils.h" -#include "mojo/edk/embedder/platform_handle_vector.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/system/broker_messages.h" -#include "mojo/edk/system/channel.h" - -namespace mojo { -namespace edk { - -namespace { - -bool WaitForBrokerMessage(PlatformHandle platform_handle, - BrokerMessageType expected_type, - size_t expected_num_handles, - std::deque<PlatformHandle>* incoming_handles) { - Channel::MessagePtr message( - new Channel::Message(sizeof(BrokerMessageHeader), expected_num_handles)); - std::deque<PlatformHandle> incoming_platform_handles; - ssize_t read_result = PlatformChannelRecvmsg( - platform_handle, const_cast<void*>(message->data()), - message->data_num_bytes(), &incoming_platform_handles, true /* block */); - bool error = false; - if (read_result < 0) { - PLOG(ERROR) << "Recvmsg error"; - error = true; - } else if (static_cast<size_t>(read_result) != message->data_num_bytes()) { - LOG(ERROR) << "Invalid node channel message"; - error = true; - } else if (incoming_platform_handles.size() != expected_num_handles) { - LOG(ERROR) << "Received unexpected number of handles"; - error = true; - } - - if (!error) { - const BrokerMessageHeader* header = - reinterpret_cast<const BrokerMessageHeader*>(message->payload()); - if (header->type != expected_type) { - LOG(ERROR) << "Unexpected message"; - error = true; - } - } - - if (error) { - CloseAllPlatformHandles(&incoming_platform_handles); - } else { - if (incoming_handles) - incoming_handles->swap(incoming_platform_handles); - } - return !error; -} - -} // namespace - -Broker::Broker(ScopedPlatformHandle platform_handle) - : sync_channel_(std::move(platform_handle)) { - CHECK(sync_channel_.is_valid()); - - // Mark the channel as blocking. - int flags = fcntl(sync_channel_.get().handle, F_GETFL); - PCHECK(flags != -1); - flags = fcntl(sync_channel_.get().handle, F_SETFL, flags & ~O_NONBLOCK); - PCHECK(flags != -1); - - // Wait for the first message, which should contain a handle. - std::deque<PlatformHandle> incoming_platform_handles; - if (WaitForBrokerMessage(sync_channel_.get(), BrokerMessageType::INIT, 1, - &incoming_platform_handles)) { - parent_channel_ = ScopedPlatformHandle(incoming_platform_handles.front()); - } -} - -Broker::~Broker() = default; - -ScopedPlatformHandle Broker::GetParentPlatformHandle() { - return std::move(parent_channel_); -} - -scoped_refptr<PlatformSharedBuffer> Broker::GetSharedBuffer(size_t num_bytes) { - base::AutoLock lock(lock_); - - BufferRequestData* buffer_request; - Channel::MessagePtr out_message = CreateBrokerMessage( - BrokerMessageType::BUFFER_REQUEST, 0, 0, &buffer_request); - buffer_request->size = num_bytes; - ssize_t write_result = PlatformChannelWrite( - sync_channel_.get(), out_message->data(), out_message->data_num_bytes()); - if (write_result < 0) { - PLOG(ERROR) << "Error sending sync broker message"; - return nullptr; - } else if (static_cast<size_t>(write_result) != - out_message->data_num_bytes()) { - LOG(ERROR) << "Error sending complete broker message"; - return nullptr; - } - - std::deque<PlatformHandle> incoming_platform_handles; - if (WaitForBrokerMessage(sync_channel_.get(), - BrokerMessageType::BUFFER_RESPONSE, 2, - &incoming_platform_handles)) { - ScopedPlatformHandle rw_handle(incoming_platform_handles.front()); - incoming_platform_handles.pop_front(); - ScopedPlatformHandle ro_handle(incoming_platform_handles.front()); - return PlatformSharedBuffer::CreateFromPlatformHandlePair( - num_bytes, std::move(rw_handle), std::move(ro_handle)); - } - - return nullptr; -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/broker_win.cc b/mojo/edk/system/broker_win.cc deleted file mode 100644 index 063282c..0000000 --- a/mojo/edk/system/broker_win.cc +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <windows.h> - -#include <limits> -#include <utility> - -#include "base/debug/alias.h" -#include "base/numerics/safe_conversions.h" -#include "base/strings/string_piece.h" -#include "mojo/edk/embedder/named_platform_handle.h" -#include "mojo/edk/embedder/named_platform_handle_utils.h" -#include "mojo/edk/embedder/platform_handle.h" -#include "mojo/edk/embedder/platform_handle_vector.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/system/broker.h" -#include "mojo/edk/system/broker_messages.h" -#include "mojo/edk/system/channel.h" - -namespace mojo { -namespace edk { - -namespace { - -// 256 bytes should be enough for anyone! -const size_t kMaxBrokerMessageSize = 256; - -bool TakeHandlesFromBrokerMessage(Channel::Message* message, - size_t num_handles, - ScopedPlatformHandle* out_handles) { - if (message->num_handles() != num_handles) { - DLOG(ERROR) << "Received unexpected number of handles in broker message"; - return false; - } - - ScopedPlatformHandleVectorPtr handles = message->TakeHandles(); - DCHECK(handles); - DCHECK_EQ(handles->size(), num_handles); - DCHECK(out_handles); - - for (size_t i = 0; i < num_handles; ++i) - out_handles[i] = ScopedPlatformHandle((*handles)[i]); - handles->clear(); - return true; -} - -Channel::MessagePtr WaitForBrokerMessage(PlatformHandle platform_handle, - BrokerMessageType expected_type) { - char buffer[kMaxBrokerMessageSize]; - DWORD bytes_read = 0; - BOOL result = ::ReadFile(platform_handle.handle, buffer, - kMaxBrokerMessageSize, &bytes_read, nullptr); - if (!result) { - // The pipe may be broken if the browser side has been closed, e.g. during - // browser shutdown. In that case the ReadFile call will fail and we - // shouldn't continue waiting. - PLOG(ERROR) << "Error reading broker pipe"; - return nullptr; - } - - Channel::MessagePtr message = - Channel::Message::Deserialize(buffer, static_cast<size_t>(bytes_read)); - if (!message || message->payload_size() < sizeof(BrokerMessageHeader)) { - LOG(ERROR) << "Invalid broker message"; - - base::debug::Alias(&buffer[0]); - base::debug::Alias(&bytes_read); - base::debug::Alias(message.get()); - CHECK(false); - return nullptr; - } - - const BrokerMessageHeader* header = - reinterpret_cast<const BrokerMessageHeader*>(message->payload()); - if (header->type != expected_type) { - LOG(ERROR) << "Unexpected broker message type"; - - base::debug::Alias(&buffer[0]); - base::debug::Alias(&bytes_read); - base::debug::Alias(message.get()); - CHECK(false); - return nullptr; - } - - return message; -} - -} // namespace - -Broker::Broker(ScopedPlatformHandle handle) : sync_channel_(std::move(handle)) { - CHECK(sync_channel_.is_valid()); - Channel::MessagePtr message = - WaitForBrokerMessage(sync_channel_.get(), BrokerMessageType::INIT); - - // If we fail to read a message (broken pipe), just return early. The parent - // handle will be null and callers must handle this gracefully. - if (!message) - return; - - if (!TakeHandlesFromBrokerMessage(message.get(), 1, &parent_channel_)) { - // If the message has no handles, we expect it to carry pipe name instead. - const BrokerMessageHeader* header = - static_cast<const BrokerMessageHeader*>(message->payload()); - CHECK_GE(message->payload_size(), - sizeof(BrokerMessageHeader) + sizeof(InitData)); - const InitData* data = reinterpret_cast<const InitData*>(header + 1); - CHECK_EQ(message->payload_size(), - sizeof(BrokerMessageHeader) + sizeof(InitData) + - data->pipe_name_length * sizeof(base::char16)); - const base::char16* name_data = - reinterpret_cast<const base::char16*>(data + 1); - CHECK(data->pipe_name_length); - parent_channel_ = CreateClientHandle(NamedPlatformHandle( - base::StringPiece16(name_data, data->pipe_name_length))); - } -} - -Broker::~Broker() {} - -ScopedPlatformHandle Broker::GetParentPlatformHandle() { - return std::move(parent_channel_); -} - -scoped_refptr<PlatformSharedBuffer> Broker::GetSharedBuffer(size_t num_bytes) { - base::AutoLock lock(lock_); - BufferRequestData* buffer_request; - Channel::MessagePtr out_message = CreateBrokerMessage( - BrokerMessageType::BUFFER_REQUEST, 0, 0, &buffer_request); - buffer_request->size = base::checked_cast<uint32_t>(num_bytes); - DWORD bytes_written = 0; - BOOL result = ::WriteFile(sync_channel_.get().handle, out_message->data(), - static_cast<DWORD>(out_message->data_num_bytes()), - &bytes_written, nullptr); - if (!result || - static_cast<size_t>(bytes_written) != out_message->data_num_bytes()) { - LOG(ERROR) << "Error sending sync broker message"; - return nullptr; - } - - ScopedPlatformHandle handles[2]; - Channel::MessagePtr response = WaitForBrokerMessage( - sync_channel_.get(), BrokerMessageType::BUFFER_RESPONSE); - if (response && - TakeHandlesFromBrokerMessage(response.get(), 2, &handles[0])) { - return PlatformSharedBuffer::CreateFromPlatformHandlePair( - num_bytes, std::move(handles[0]), std::move(handles[1])); - } - - return nullptr; -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/channel.cc b/mojo/edk/system/channel.cc deleted file mode 100644 index 8a44d36..0000000 --- a/mojo/edk/system/channel.cc +++ /dev/null @@ -1,683 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/channel.h" - -#include <stddef.h> -#include <string.h> - -#include <algorithm> -#include <limits> -#include <utility> - -#include "base/macros.h" -#include "base/memory/aligned_memory.h" -#include "base/process/process_handle.h" -#include "mojo/edk/embedder/platform_handle.h" - -#if defined(OS_MACOSX) && !defined(OS_IOS) -#include "base/mac/mach_logging.h" -#elif defined(OS_WIN) -#include "base/win/win_util.h" -#endif - -namespace mojo { -namespace edk { - -namespace { - -static_assert( - IsAlignedForChannelMessage(sizeof(Channel::Message::LegacyHeader)), - "Invalid LegacyHeader size."); - -static_assert(IsAlignedForChannelMessage(sizeof(Channel::Message::Header)), - "Invalid Header size."); - -static_assert(sizeof(Channel::Message::LegacyHeader) == 8, - "LegacyHeader must be 8 bytes on ChromeOS and Android"); - -static_assert(offsetof(Channel::Message::LegacyHeader, num_bytes) == - offsetof(Channel::Message::Header, num_bytes), - "num_bytes should be at the same offset in both Header structs."); -static_assert(offsetof(Channel::Message::LegacyHeader, message_type) == - offsetof(Channel::Message::Header, message_type), - "message_type should be at the same offset in both Header " - "structs."); - -} // namespace - -const size_t kReadBufferSize = 4096; -const size_t kMaxUnusedReadBufferCapacity = 4096; -const size_t kMaxChannelMessageSize = 256 * 1024 * 1024; -const size_t kMaxAttachedHandles = 128; - -Channel::Message::Message(size_t payload_size, size_t max_handles) -#if defined(MOJO_EDK_LEGACY_PROTOCOL) - : Message(payload_size, max_handles, MessageType::NORMAL_LEGACY) { -} -#else - : Message(payload_size, max_handles, MessageType::NORMAL) { -} -#endif - -Channel::Message::Message(size_t payload_size, - size_t max_handles, - MessageType message_type) - : max_handles_(max_handles) { - DCHECK_LE(max_handles_, kMaxAttachedHandles); - - const bool is_legacy_message = (message_type == MessageType::NORMAL_LEGACY); - size_t extra_header_size = 0; -#if defined(OS_WIN) - // On Windows we serialize HANDLEs into the extra header space. - extra_header_size = max_handles_ * sizeof(HandleEntry); -#elif defined(OS_MACOSX) && !defined(OS_IOS) - // On OSX, some of the platform handles may be mach ports, which are - // serialised into the message buffer. Since there could be a mix of fds and - // mach ports, we store the mach ports as an <index, port> pair (of uint32_t), - // so that the original ordering of handles can be re-created. - if (max_handles) { - extra_header_size = - sizeof(MachPortsExtraHeader) + (max_handles * sizeof(MachPortsEntry)); - } -#endif - // Pad extra header data to be aliged to |kChannelMessageAlignment| bytes. - if (!IsAlignedForChannelMessage(extra_header_size)) { - extra_header_size += kChannelMessageAlignment - - (extra_header_size % kChannelMessageAlignment); - } - DCHECK(IsAlignedForChannelMessage(extra_header_size)); - const size_t header_size = - is_legacy_message ? sizeof(LegacyHeader) : sizeof(Header); - DCHECK(extra_header_size == 0 || !is_legacy_message); - - size_ = header_size + extra_header_size + payload_size; - data_ = static_cast<char*>(base::AlignedAlloc(size_, - kChannelMessageAlignment)); - // Only zero out the header and not the payload. Since the payload is going to - // be memcpy'd, zeroing the payload is unnecessary work and a significant - // performance issue when dealing with large messages. Any sanitizer errors - // complaining about an uninitialized read in the payload area should be - // treated as an error and fixed. - memset(data_, 0, header_size + extra_header_size); - - DCHECK_LE(size_, std::numeric_limits<uint32_t>::max()); - legacy_header()->num_bytes = static_cast<uint32_t>(size_); - - DCHECK_LE(header_size + extra_header_size, - std::numeric_limits<uint16_t>::max()); - legacy_header()->message_type = message_type; - - if (is_legacy_message) { - legacy_header()->num_handles = static_cast<uint16_t>(max_handles); - } else { - header()->num_header_bytes = - static_cast<uint16_t>(header_size + extra_header_size); - } - - if (max_handles_ > 0) { -#if defined(OS_WIN) - handles_ = reinterpret_cast<HandleEntry*>(mutable_extra_header()); - // Initialize all handles to invalid values. - for (size_t i = 0; i < max_handles_; ++i) - handles_[i].handle = base::win::HandleToUint32(INVALID_HANDLE_VALUE); -#elif defined(OS_MACOSX) && !defined(OS_IOS) - mach_ports_header_ = - reinterpret_cast<MachPortsExtraHeader*>(mutable_extra_header()); - mach_ports_header_->num_ports = 0; - // Initialize all handles to invalid values. - for (size_t i = 0; i < max_handles_; ++i) { - mach_ports_header_->entries[i] = - {0, static_cast<uint32_t>(MACH_PORT_NULL)}; - } -#endif - } -} - -Channel::Message::~Message() { - base::AlignedFree(data_); -} - -// static -Channel::MessagePtr Channel::Message::Deserialize(const void* data, - size_t data_num_bytes) { - if (data_num_bytes < sizeof(LegacyHeader)) - return nullptr; - - const LegacyHeader* legacy_header = - reinterpret_cast<const LegacyHeader*>(data); - if (legacy_header->num_bytes != data_num_bytes) { - DLOG(ERROR) << "Decoding invalid message: " << legacy_header->num_bytes - << " != " << data_num_bytes; - return nullptr; - } - - const Header* header = nullptr; - if (legacy_header->message_type == MessageType::NORMAL) - header = reinterpret_cast<const Header*>(data); - - uint32_t extra_header_size = 0; - size_t payload_size = 0; - const char* payload = nullptr; - if (!header) { - payload_size = data_num_bytes - sizeof(LegacyHeader); - payload = static_cast<const char*>(data) + sizeof(LegacyHeader); - } else { - if (header->num_bytes < header->num_header_bytes || - header->num_header_bytes < sizeof(Header)) { - DLOG(ERROR) << "Decoding invalid message: " << header->num_bytes << " < " - << header->num_header_bytes; - return nullptr; - } - extra_header_size = header->num_header_bytes - sizeof(Header); - payload_size = data_num_bytes - header->num_header_bytes; - payload = static_cast<const char*>(data) + header->num_header_bytes; - } - -#if defined(OS_WIN) - uint32_t max_handles = extra_header_size / sizeof(HandleEntry); -#elif defined(OS_MACOSX) && !defined(OS_IOS) - if (extra_header_size > 0 && - extra_header_size < sizeof(MachPortsExtraHeader)) { - DLOG(ERROR) << "Decoding invalid message: " << extra_header_size << " < " - << sizeof(MachPortsExtraHeader); - return nullptr; - } - uint32_t max_handles = - extra_header_size == 0 - ? 0 - : (extra_header_size - sizeof(MachPortsExtraHeader)) / - sizeof(MachPortsEntry); -#else - const uint32_t max_handles = 0; -#endif // defined(OS_WIN) - - const uint16_t num_handles = - header ? header->num_handles : legacy_header->num_handles; - if (num_handles > max_handles || max_handles > kMaxAttachedHandles) { - DLOG(ERROR) << "Decoding invalid message: " << num_handles << " > " - << max_handles; - return nullptr; - } - - MessagePtr message( - new Message(payload_size, max_handles, legacy_header->message_type)); - DCHECK_EQ(message->data_num_bytes(), data_num_bytes); - - // Copy all payload bytes. - if (payload_size) - memcpy(message->mutable_payload(), payload, payload_size); - - if (header) { - DCHECK_EQ(message->extra_header_size(), extra_header_size); - DCHECK_EQ(message->header()->num_header_bytes, header->num_header_bytes); - - if (message->extra_header_size()) { - // Copy extra header bytes. - memcpy(message->mutable_extra_header(), - static_cast<const char*>(data) + sizeof(Header), - message->extra_header_size()); - } - message->header()->num_handles = header->num_handles; - } else { - message->legacy_header()->num_handles = legacy_header->num_handles; - } - -#if defined(OS_WIN) - ScopedPlatformHandleVectorPtr handles(new PlatformHandleVector(num_handles)); - for (size_t i = 0; i < num_handles; i++) { - (*handles)[i].handle = - base::win::Uint32ToHandle(message->handles_[i].handle); - } - message->SetHandles(std::move(handles)); -#endif - - return message; -} - -const void* Channel::Message::extra_header() const { - DCHECK(!is_legacy_message()); - return data_ + sizeof(Header); -} - -void* Channel::Message::mutable_extra_header() { - DCHECK(!is_legacy_message()); - return data_ + sizeof(Header); -} - -size_t Channel::Message::extra_header_size() const { - return header()->num_header_bytes - sizeof(Header); -} - -void* Channel::Message::mutable_payload() { - if (is_legacy_message()) - return static_cast<void*>(legacy_header() + 1); - return data_ + header()->num_header_bytes; -} - -const void* Channel::Message::payload() const { - if (is_legacy_message()) - return static_cast<const void*>(legacy_header() + 1); - return data_ + header()->num_header_bytes; -} - -size_t Channel::Message::payload_size() const { - if (is_legacy_message()) - return legacy_header()->num_bytes - sizeof(LegacyHeader); - return size_ - header()->num_header_bytes; -} - -size_t Channel::Message::num_handles() const { - return is_legacy_message() ? legacy_header()->num_handles - : header()->num_handles; -} - -bool Channel::Message::has_handles() const { - return (is_legacy_message() ? legacy_header()->num_handles - : header()->num_handles) > 0; -} - -#if defined(OS_MACOSX) && !defined(OS_IOS) -bool Channel::Message::has_mach_ports() const { - if (!has_handles()) - return false; - - for (const auto& handle : (*handle_vector_)) { - if (handle.type == PlatformHandle::Type::MACH || - handle.type == PlatformHandle::Type::MACH_NAME) { - return true; - } - } - return false; -} -#endif - -bool Channel::Message::is_legacy_message() const { - return legacy_header()->message_type == MessageType::NORMAL_LEGACY; -} - -Channel::Message::LegacyHeader* Channel::Message::legacy_header() const { - return reinterpret_cast<LegacyHeader*>(data_); -} - -Channel::Message::Header* Channel::Message::header() const { - DCHECK(!is_legacy_message()); - return reinterpret_cast<Header*>(data_); -} - -void Channel::Message::SetHandles(ScopedPlatformHandleVectorPtr new_handles) { - if (is_legacy_message()) { - // Old semantics for ChromeOS and Android - if (legacy_header()->num_handles == 0) { - CHECK(!new_handles || new_handles->size() == 0); - return; - } - CHECK(new_handles && new_handles->size() == legacy_header()->num_handles); - std::swap(handle_vector_, new_handles); - return; - } - - if (max_handles_ == 0) { - CHECK(!new_handles || new_handles->size() == 0); - return; - } - - CHECK(new_handles && new_handles->size() <= max_handles_); - header()->num_handles = static_cast<uint16_t>(new_handles->size()); - std::swap(handle_vector_, new_handles); -#if defined(OS_WIN) - memset(handles_, 0, extra_header_size()); - for (size_t i = 0; i < handle_vector_->size(); i++) - handles_[i].handle = base::win::HandleToUint32((*handle_vector_)[i].handle); -#endif // defined(OS_WIN) - -#if defined(OS_MACOSX) && !defined(OS_IOS) - size_t mach_port_index = 0; - if (mach_ports_header_) { - for (size_t i = 0; i < max_handles_; ++i) { - mach_ports_header_->entries[i] = - {0, static_cast<uint32_t>(MACH_PORT_NULL)}; - } - for (size_t i = 0; i < handle_vector_->size(); i++) { - if ((*handle_vector_)[i].type == PlatformHandle::Type::MACH || - (*handle_vector_)[i].type == PlatformHandle::Type::MACH_NAME) { - mach_port_t port = (*handle_vector_)[i].port; - mach_ports_header_->entries[mach_port_index].index = i; - mach_ports_header_->entries[mach_port_index].mach_port = port; - mach_port_index++; - } - } - mach_ports_header_->num_ports = static_cast<uint16_t>(mach_port_index); - } -#endif -} - -ScopedPlatformHandleVectorPtr Channel::Message::TakeHandles() { -#if defined(OS_MACOSX) && !defined(OS_IOS) - if (mach_ports_header_) { - for (size_t i = 0; i < max_handles_; ++i) { - mach_ports_header_->entries[i] = - {0, static_cast<uint32_t>(MACH_PORT_NULL)}; - } - mach_ports_header_->num_ports = 0; - } -#endif - if (is_legacy_message()) - legacy_header()->num_handles = 0; - else - header()->num_handles = 0; - return std::move(handle_vector_); -} - -ScopedPlatformHandleVectorPtr Channel::Message::TakeHandlesForTransport() { -#if defined(OS_WIN) - // Not necessary on Windows. - NOTREACHED(); - return nullptr; -#elif defined(OS_MACOSX) && !defined(OS_IOS) - if (handle_vector_) { - for (auto it = handle_vector_->begin(); it != handle_vector_->end(); ) { - if (it->type == PlatformHandle::Type::MACH || - it->type == PlatformHandle::Type::MACH_NAME) { - // For Mach port names, we can can just leak them. They're not real - // ports anyways. For real ports, they're leaked because this is a child - // process and the remote process will take ownership. - it = handle_vector_->erase(it); - } else { - ++it; - } - } - } - return std::move(handle_vector_); -#else - return std::move(handle_vector_); -#endif -} - -#if defined(OS_WIN) -// static -bool Channel::Message::RewriteHandles(base::ProcessHandle from_process, - base::ProcessHandle to_process, - PlatformHandleVector* handles) { - bool success = true; - for (size_t i = 0; i < handles->size(); ++i) { - if (!(*handles)[i].is_valid()) { - DLOG(ERROR) << "Refusing to duplicate invalid handle."; - continue; - } - DCHECK_EQ((*handles)[i].owning_process, from_process); - BOOL result = DuplicateHandle( - from_process, (*handles)[i].handle, to_process, - &(*handles)[i].handle, 0, FALSE, - DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); - if (result) { - (*handles)[i].owning_process = to_process; - } else { - success = false; - - // If handle duplication fails, the source handle will already be closed - // due to DUPLICATE_CLOSE_SOURCE. Replace the handle in the message with - // an invalid handle. - (*handles)[i].handle = INVALID_HANDLE_VALUE; - (*handles)[i].owning_process = base::GetCurrentProcessHandle(); - } - } - return success; -} -#endif - -// Helper class for managing a Channel's read buffer allocations. This maintains -// a single contiguous buffer with the layout: -// -// [discarded bytes][occupied bytes][unoccupied bytes] -// -// The Reserve() method ensures that a certain capacity of unoccupied bytes are -// available. It does not claim that capacity and only allocates new capacity -// when strictly necessary. -// -// Claim() marks unoccupied bytes as occupied. -// -// Discard() marks occupied bytes as discarded, signifying that their contents -// can be forgotten or overwritten. -// -// Realign() moves occupied bytes to the front of the buffer so that those -// occupied bytes are properly aligned. -// -// The most common Channel behavior in practice should result in very few -// allocations and copies, as memory is claimed and discarded shortly after -// being reserved, and future reservations will immediately reuse discarded -// memory. -class Channel::ReadBuffer { - public: - ReadBuffer() { - size_ = kReadBufferSize; - data_ = static_cast<char*>(base::AlignedAlloc(size_, - kChannelMessageAlignment)); - } - - ~ReadBuffer() { - DCHECK(data_); - base::AlignedFree(data_); - } - - const char* occupied_bytes() const { return data_ + num_discarded_bytes_; } - - size_t num_occupied_bytes() const { - return num_occupied_bytes_ - num_discarded_bytes_; - } - - // Ensures the ReadBuffer has enough contiguous space allocated to hold - // |num_bytes| more bytes; returns the address of the first available byte. - char* Reserve(size_t num_bytes) { - if (num_occupied_bytes_ + num_bytes > size_) { - size_ = std::max(size_ * 2, num_occupied_bytes_ + num_bytes); - void* new_data = base::AlignedAlloc(size_, kChannelMessageAlignment); - memcpy(new_data, data_, num_occupied_bytes_); - base::AlignedFree(data_); - data_ = static_cast<char*>(new_data); - } - - return data_ + num_occupied_bytes_; - } - - // Marks the first |num_bytes| unoccupied bytes as occupied. - void Claim(size_t num_bytes) { - DCHECK_LE(num_occupied_bytes_ + num_bytes, size_); - num_occupied_bytes_ += num_bytes; - } - - // Marks the first |num_bytes| occupied bytes as discarded. This may result in - // shrinkage of the internal buffer, and it is not safe to assume the result - // of a previous Reserve() call is still valid after this. - void Discard(size_t num_bytes) { - DCHECK_LE(num_discarded_bytes_ + num_bytes, num_occupied_bytes_); - num_discarded_bytes_ += num_bytes; - - if (num_discarded_bytes_ == num_occupied_bytes_) { - // We can just reuse the buffer from the beginning in this common case. - num_discarded_bytes_ = 0; - num_occupied_bytes_ = 0; - } - - if (num_discarded_bytes_ > kMaxUnusedReadBufferCapacity) { - // In the uncommon case that we have a lot of discarded data at the - // front of the buffer, simply move remaining data to a smaller buffer. - size_t num_preserved_bytes = num_occupied_bytes_ - num_discarded_bytes_; - size_ = std::max(num_preserved_bytes, kReadBufferSize); - char* new_data = static_cast<char*>( - base::AlignedAlloc(size_, kChannelMessageAlignment)); - memcpy(new_data, data_ + num_discarded_bytes_, num_preserved_bytes); - base::AlignedFree(data_); - data_ = new_data; - num_discarded_bytes_ = 0; - num_occupied_bytes_ = num_preserved_bytes; - } - - if (num_occupied_bytes_ == 0 && size_ > kMaxUnusedReadBufferCapacity) { - // Opportunistically shrink the read buffer back down to a small size if - // it's grown very large. We only do this if there are no remaining - // unconsumed bytes in the buffer to avoid copies in most the common - // cases. - size_ = kMaxUnusedReadBufferCapacity; - base::AlignedFree(data_); - data_ = static_cast<char*>( - base::AlignedAlloc(size_, kChannelMessageAlignment)); - } - } - - void Realign() { - size_t num_bytes = num_occupied_bytes(); - memmove(data_, occupied_bytes(), num_bytes); - num_discarded_bytes_ = 0; - num_occupied_bytes_ = num_bytes; - } - - private: - char* data_ = nullptr; - - // The total size of the allocated buffer. - size_t size_ = 0; - - // The number of discarded bytes at the beginning of the allocated buffer. - size_t num_discarded_bytes_ = 0; - - // The total number of occupied bytes, including discarded bytes. - size_t num_occupied_bytes_ = 0; - - DISALLOW_COPY_AND_ASSIGN(ReadBuffer); -}; - -Channel::Channel(Delegate* delegate) - : delegate_(delegate), read_buffer_(new ReadBuffer) { -} - -Channel::~Channel() { -} - -void Channel::ShutDown() { - delegate_ = nullptr; - ShutDownImpl(); -} - -char* Channel::GetReadBuffer(size_t *buffer_capacity) { - DCHECK(read_buffer_); - size_t required_capacity = *buffer_capacity; - if (!required_capacity) - required_capacity = kReadBufferSize; - - *buffer_capacity = required_capacity; - return read_buffer_->Reserve(required_capacity); -} - -bool Channel::OnReadComplete(size_t bytes_read, size_t *next_read_size_hint) { - bool did_dispatch_message = false; - read_buffer_->Claim(bytes_read); - while (read_buffer_->num_occupied_bytes() >= sizeof(Message::LegacyHeader)) { - // Ensure the occupied data is properly aligned. If it isn't, a SIGBUS could - // happen on architectures that don't allow misaligned words access (i.e. - // anything other than x86). Only re-align when necessary to avoid copies. - if (!IsAlignedForChannelMessage( - reinterpret_cast<uintptr_t>(read_buffer_->occupied_bytes()))) { - read_buffer_->Realign(); - } - - // We have at least enough data available for a LegacyHeader. - const Message::LegacyHeader* legacy_header = - reinterpret_cast<const Message::LegacyHeader*>( - read_buffer_->occupied_bytes()); - - if (legacy_header->num_bytes < sizeof(Message::LegacyHeader) || - legacy_header->num_bytes > kMaxChannelMessageSize) { - LOG(ERROR) << "Invalid message size: " << legacy_header->num_bytes; - return false; - } - - if (read_buffer_->num_occupied_bytes() < legacy_header->num_bytes) { - // Not enough data available to read the full message. Hint to the - // implementation that it should try reading the full size of the message. - *next_read_size_hint = - legacy_header->num_bytes - read_buffer_->num_occupied_bytes(); - return true; - } - - const Message::Header* header = nullptr; - if (legacy_header->message_type != Message::MessageType::NORMAL_LEGACY) { - header = reinterpret_cast<const Message::Header*>(legacy_header); - } - - size_t extra_header_size = 0; - const void* extra_header = nullptr; - size_t payload_size = 0; - void* payload = nullptr; - if (header) { - if (header->num_header_bytes < sizeof(Message::Header) || - header->num_header_bytes > header->num_bytes) { - LOG(ERROR) << "Invalid message header size: " - << header->num_header_bytes; - return false; - } - extra_header_size = header->num_header_bytes - sizeof(Message::Header); - extra_header = extra_header_size ? header + 1 : nullptr; - payload_size = header->num_bytes - header->num_header_bytes; - payload = payload_size - ? reinterpret_cast<Message::Header*>( - const_cast<char*>(read_buffer_->occupied_bytes()) + - header->num_header_bytes) - : nullptr; - } else { - payload_size = legacy_header->num_bytes - sizeof(Message::LegacyHeader); - payload = payload_size - ? const_cast<Message::LegacyHeader*>(&legacy_header[1]) - : nullptr; - } - - const uint16_t num_handles = - header ? header->num_handles : legacy_header->num_handles; - ScopedPlatformHandleVectorPtr handles; - if (num_handles > 0) { - if (!GetReadPlatformHandles(num_handles, extra_header, extra_header_size, - &handles)) { - return false; - } - - if (!handles) { - // Not enough handles available for this message. - break; - } - } - - // We've got a complete message! Dispatch it and try another. - if (legacy_header->message_type != Message::MessageType::NORMAL_LEGACY && - legacy_header->message_type != Message::MessageType::NORMAL) { - if (!OnControlMessage(legacy_header->message_type, payload, payload_size, - std::move(handles))) { - return false; - } - did_dispatch_message = true; - } else if (delegate_) { - delegate_->OnChannelMessage(payload, payload_size, std::move(handles)); - did_dispatch_message = true; - } - - read_buffer_->Discard(legacy_header->num_bytes); - } - - *next_read_size_hint = did_dispatch_message ? 0 : kReadBufferSize; - return true; -} - -void Channel::OnError() { - if (delegate_) - delegate_->OnChannelError(); -} - -bool Channel::OnControlMessage(Message::MessageType message_type, - const void* payload, - size_t payload_size, - ScopedPlatformHandleVectorPtr handles) { - return false; -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/channel.h b/mojo/edk/system/channel.h deleted file mode 100644 index 33a510c..0000000 --- a/mojo/edk/system/channel.h +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_CHANNEL_H_ -#define MOJO_EDK_SYSTEM_CHANNEL_H_ - -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/process/process_handle.h" -#include "base/task_runner.h" -#include "mojo/edk/embedder/connection_params.h" -#include "mojo/edk/embedder/platform_handle_vector.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" - -namespace mojo { -namespace edk { - -const size_t kChannelMessageAlignment = 8; - -constexpr bool IsAlignedForChannelMessage(size_t n) { - return n % kChannelMessageAlignment == 0; -} - -// Channel provides a thread-safe interface to read and write arbitrary -// delimited messages over an underlying I/O channel, optionally transferring -// one or more platform handles in the process. -class MOJO_SYSTEM_IMPL_EXPORT Channel - : public base::RefCountedThreadSafe<Channel> { - public: - struct Message; - - using MessagePtr = std::unique_ptr<Message>; - - // A message to be written to a channel. - struct MOJO_SYSTEM_IMPL_EXPORT Message { - enum class MessageType : uint16_t { - // An old format normal message, that uses the LegacyHeader. - // Only used on Android and ChromeOS. - // TODO(jcivelli): remove legacy support when Arc++ has updated to Mojo - // with normal versioned messages. crbug.com/695645 - NORMAL_LEGACY = 0, -#if defined(OS_MACOSX) - // A control message containing handles to echo back. - HANDLES_SENT, - // A control message containing handles that can now be closed. - HANDLES_SENT_ACK, -#endif - // A normal message that uses Header and can contain extra header values. - NORMAL, - }; - -#pragma pack(push, 1) - // Old message wire format for ChromeOS and Android, used by NORMAL_LEGACY - // messages. - struct LegacyHeader { - // Message size in bytes, including the header. - uint32_t num_bytes; - - // Number of attached handles. - uint16_t num_handles; - - MessageType message_type; - }; - - // Header used by NORMAL messages. - // To preserve backward compatibility with LegacyHeader, the num_bytes and - // message_type field must be at the same offset as in LegacyHeader. - struct Header { - // Message size in bytes, including the header. - uint32_t num_bytes; - - // Total size of header, including extra header data (i.e. HANDLEs on - // windows). - uint16_t num_header_bytes; - - MessageType message_type; - - // Number of attached handles. May be less than the reserved handle - // storage size in this message on platforms that serialise handles as - // data (i.e. HANDLEs on Windows, Mach ports on OSX). - uint16_t num_handles; - - char padding[6]; - }; - -#if defined(OS_MACOSX) && !defined(OS_IOS) - struct MachPortsEntry { - // Index of Mach port in the original vector of PlatformHandles. - uint16_t index; - - // Mach port name. - uint32_t mach_port; - static_assert(sizeof(mach_port_t) <= sizeof(uint32_t), - "mach_port_t must be no larger than uint32_t"); - }; - static_assert(sizeof(MachPortsEntry) == 6, - "sizeof(MachPortsEntry) must be 6 bytes"); - - // Structure of the extra header field when present on OSX. - struct MachPortsExtraHeader { - // Actual number of Mach ports encoded in the extra header. - uint16_t num_ports; - - // Array of encoded Mach ports. If |num_ports| > 0, |entries[0]| through - // to |entries[num_ports-1]| inclusive are valid. - MachPortsEntry entries[0]; - }; - static_assert(sizeof(MachPortsExtraHeader) == 2, - "sizeof(MachPortsExtraHeader) must be 2 bytes"); -#elif defined(OS_WIN) - struct HandleEntry { - // The windows HANDLE. HANDLEs are guaranteed to fit inside 32-bits. - // See: https://msdn.microsoft.com/en-us/library/aa384203(VS.85).aspx - uint32_t handle; - }; - static_assert(sizeof(HandleEntry) == 4, - "sizeof(HandleEntry) must be 4 bytes"); -#endif -#pragma pack(pop) - - // Allocates and owns a buffer for message data with enough capacity for - // |payload_size| bytes plus a header, plus |max_handles| platform handles. - Message(size_t payload_size, size_t max_handles); - Message(size_t payload_size, size_t max_handles, MessageType message_type); - ~Message(); - - // Constructs a Message from serialized message data. - static MessagePtr Deserialize(const void* data, size_t data_num_bytes); - - const void* data() const { return data_; } - size_t data_num_bytes() const { return size_; } - - const void* extra_header() const; - void* mutable_extra_header(); - size_t extra_header_size() const; - - void* mutable_payload(); - const void* payload() const; - size_t payload_size() const; - - size_t num_handles() const; - bool has_handles() const; -#if defined(OS_MACOSX) && !defined(OS_IOS) - bool has_mach_ports() const; -#endif - - bool is_legacy_message() const; - LegacyHeader* legacy_header() const; - Header* header() const; - - // Note: SetHandles() and TakeHandles() invalidate any previous value of - // handles(). - void SetHandles(ScopedPlatformHandleVectorPtr new_handles); - ScopedPlatformHandleVectorPtr TakeHandles(); - // Version of TakeHandles that returns a vector of platform handles suitable - // for transfer over an underlying OS mechanism. i.e. file descriptors over - // a unix domain socket. Any handle that cannot be transferred this way, - // such as Mach ports, will be removed. - ScopedPlatformHandleVectorPtr TakeHandlesForTransport(); - -#if defined(OS_WIN) - // Prepares the handles in this message for use in a different process. - // Upon calling this the handles should belong to |from_process|; after the - // call they'll belong to |to_process|. The source handles are always - // closed by this call. Returns false iff one or more handles failed - // duplication. - static bool RewriteHandles(base::ProcessHandle from_process, - base::ProcessHandle to_process, - PlatformHandleVector* handles); -#endif - - void SetVersionForTest(uint16_t version_number); - - private: - size_t size_ = 0; - size_t max_handles_ = 0; - char* data_ = nullptr; - - ScopedPlatformHandleVectorPtr handle_vector_; - -#if defined(OS_WIN) - // On Windows, handles are serialised into the extra header section. - HandleEntry* handles_ = nullptr; -#elif defined(OS_MACOSX) && !defined(OS_IOS) - // On OSX, handles are serialised into the extra header section. - MachPortsExtraHeader* mach_ports_header_ = nullptr; -#endif - - DISALLOW_COPY_AND_ASSIGN(Message); - }; - - // Delegate methods are called from the I/O task runner with which the Channel - // was created (see Channel::Create). - class Delegate { - public: - virtual ~Delegate() {} - - // Notify of a received message. |payload| is not owned and must not be - // retained; it will be null if |payload_size| is 0. |handles| are - // transferred to the callee. - virtual void OnChannelMessage(const void* payload, - size_t payload_size, - ScopedPlatformHandleVectorPtr handles) = 0; - - // Notify that an error has occured and the Channel will cease operation. - virtual void OnChannelError() = 0; - }; - - // Creates a new Channel around a |platform_handle|, taking ownership of the - // handle. All I/O on the handle will be performed on |io_task_runner|. - // Note that ShutDown() MUST be called on the Channel some time before - // |delegate| is destroyed. - static scoped_refptr<Channel> Create( - Delegate* delegate, - ConnectionParams connection_params, - scoped_refptr<base::TaskRunner> io_task_runner); - - // Request that the channel be shut down. This should always be called before - // releasing the last reference to a Channel to ensure that it's cleaned up - // on its I/O task runner's thread. - // - // Delegate methods will no longer be invoked after this call. - void ShutDown(); - - // Begin processing I/O events. Delegate methods must only be invoked after - // this call. - virtual void Start() = 0; - - // Stop processing I/O events. - virtual void ShutDownImpl() = 0; - - // Queues an outgoing message on the Channel. This message will either - // eventually be written or will fail to write and trigger - // Delegate::OnChannelError. - virtual void Write(MessagePtr message) = 0; - - // Causes the platform handle to leak when this channel is shut down instead - // of closing it. - virtual void LeakHandle() = 0; - - protected: - explicit Channel(Delegate* delegate); - virtual ~Channel(); - - // Called by the implementation when it wants somewhere to stick data. - // |*buffer_capacity| may be set by the caller to indicate the desired buffer - // size. If 0, a sane default size will be used instead. - // - // Returns the address of a buffer which can be written to, and indicates its - // actual capacity in |*buffer_capacity|. - char* GetReadBuffer(size_t* buffer_capacity); - - // Called by the implementation when new data is available in the read - // buffer. Returns false to indicate an error. Upon success, - // |*next_read_size_hint| will be set to a recommended size for the next - // read done by the implementation. - bool OnReadComplete(size_t bytes_read, size_t* next_read_size_hint); - - // Called by the implementation when something goes horribly wrong. It is NOT - // OK to call this synchronously from any public interface methods. - void OnError(); - - // Retrieves the set of platform handles read for a given message. - // |extra_header| and |extra_header_size| correspond to the extra header data. - // Depending on the Channel implementation, this body may encode platform - // handles, or handles may be stored and managed elsewhere by the - // implementation. - // - // Returns |false| on unrecoverable error (i.e. the Channel should be closed). - // Returns |true| otherwise. Note that it is possible on some platforms for an - // insufficient number of handles to be available when this call is made, but - // this is not necessarily an error condition. In such cases this returns - // |true| but |*handles| will also be reset to null. - virtual bool GetReadPlatformHandles( - size_t num_handles, - const void* extra_header, - size_t extra_header_size, - ScopedPlatformHandleVectorPtr* handles) = 0; - - // Handles a received control message. Returns |true| if the message is - // accepted, or |false| otherwise. - virtual bool OnControlMessage(Message::MessageType message_type, - const void* payload, - size_t payload_size, - ScopedPlatformHandleVectorPtr handles); - - private: - friend class base::RefCountedThreadSafe<Channel>; - - class ReadBuffer; - - Delegate* delegate_; - const std::unique_ptr<ReadBuffer> read_buffer_; - - DISALLOW_COPY_AND_ASSIGN(Channel); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_CHANNEL_H_ diff --git a/mojo/edk/system/channel_posix.cc b/mojo/edk/system/channel_posix.cc deleted file mode 100644 index 8b4ca7f..0000000 --- a/mojo/edk/system/channel_posix.cc +++ /dev/null @@ -1,572 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/channel.h" - -#include <errno.h> -#include <sys/socket.h> - -#include <algorithm> -#include <deque> -#include <limits> -#include <memory> - -#include "base/bind.h" -#include "base/location.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" -#include "base/synchronization/lock.h" -#include "base/task_runner.h" -#include "mojo/edk/embedder/platform_channel_utils_posix.h" -#include "mojo/edk/embedder/platform_handle_vector.h" - -#if !defined(OS_NACL) -#include <sys/uio.h> -#endif - -namespace mojo { -namespace edk { - -namespace { - -const size_t kMaxBatchReadCapacity = 256 * 1024; - -// A view over a Channel::Message object. The write queue uses these since -// large messages may need to be sent in chunks. -class MessageView { - public: - // Owns |message|. |offset| indexes the first unsent byte in the message. - MessageView(Channel::MessagePtr message, size_t offset) - : message_(std::move(message)), - offset_(offset), - handles_(message_->TakeHandlesForTransport()) { - DCHECK_GT(message_->data_num_bytes(), offset_); - } - - MessageView(MessageView&& other) { *this = std::move(other); } - - MessageView& operator=(MessageView&& other) { - message_ = std::move(other.message_); - offset_ = other.offset_; - handles_ = std::move(other.handles_); - return *this; - } - - ~MessageView() {} - - const void* data() const { - return static_cast<const char*>(message_->data()) + offset_; - } - - size_t data_num_bytes() const { return message_->data_num_bytes() - offset_; } - - size_t data_offset() const { return offset_; } - void advance_data_offset(size_t num_bytes) { - DCHECK_GT(message_->data_num_bytes(), offset_ + num_bytes); - offset_ += num_bytes; - } - - ScopedPlatformHandleVectorPtr TakeHandles() { return std::move(handles_); } - Channel::MessagePtr TakeMessage() { return std::move(message_); } - - void SetHandles(ScopedPlatformHandleVectorPtr handles) { - handles_ = std::move(handles); - } - - private: - Channel::MessagePtr message_; - size_t offset_; - ScopedPlatformHandleVectorPtr handles_; - - DISALLOW_COPY_AND_ASSIGN(MessageView); -}; - -class ChannelPosix : public Channel, - public base::MessageLoop::DestructionObserver, - public base::MessageLoopForIO::Watcher { - public: - ChannelPosix(Delegate* delegate, - ConnectionParams connection_params, - scoped_refptr<base::TaskRunner> io_task_runner) - : Channel(delegate), - self_(this), - handle_(connection_params.TakeChannelHandle()), - io_task_runner_(io_task_runner) -#if defined(OS_MACOSX) - , - handles_to_close_(new PlatformHandleVector) -#endif - { - CHECK(handle_.is_valid()); - } - - void Start() override { - if (io_task_runner_->RunsTasksOnCurrentThread()) { - StartOnIOThread(); - } else { - io_task_runner_->PostTask( - FROM_HERE, base::Bind(&ChannelPosix::StartOnIOThread, this)); - } - } - - void ShutDownImpl() override { - // Always shut down asynchronously when called through the public interface. - io_task_runner_->PostTask( - FROM_HERE, base::Bind(&ChannelPosix::ShutDownOnIOThread, this)); - } - - void Write(MessagePtr message) override { - bool write_error = false; - { - base::AutoLock lock(write_lock_); - if (reject_writes_) - return; - if (outgoing_messages_.empty()) { - if (!WriteNoLock(MessageView(std::move(message), 0))) - reject_writes_ = write_error = true; - } else { - outgoing_messages_.emplace_back(std::move(message), 0); - } - } - if (write_error) { - // Do not synchronously invoke OnError(). Write() may have been called by - // the delegate and we don't want to re-enter it. - io_task_runner_->PostTask(FROM_HERE, - base::Bind(&ChannelPosix::OnError, this)); - } - } - - void LeakHandle() override { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - leak_handle_ = true; - } - - bool GetReadPlatformHandles( - size_t num_handles, - const void* extra_header, - size_t extra_header_size, - ScopedPlatformHandleVectorPtr* handles) override { - if (num_handles > std::numeric_limits<uint16_t>::max()) - return false; -#if defined(OS_MACOSX) && !defined(OS_IOS) - // On OSX, we can have mach ports which are located in the extra header - // section. - using MachPortsEntry = Channel::Message::MachPortsEntry; - using MachPortsExtraHeader = Channel::Message::MachPortsExtraHeader; - CHECK(extra_header_size >= - sizeof(MachPortsExtraHeader) + num_handles * sizeof(MachPortsEntry)); - const MachPortsExtraHeader* mach_ports_header = - reinterpret_cast<const MachPortsExtraHeader*>(extra_header); - size_t num_mach_ports = mach_ports_header->num_ports; - CHECK(num_mach_ports <= num_handles); - if (incoming_platform_handles_.size() + num_mach_ports < num_handles) { - handles->reset(); - return true; - } - - handles->reset(new PlatformHandleVector(num_handles)); - const MachPortsEntry* mach_ports = mach_ports_header->entries; - for (size_t i = 0, mach_port_index = 0; i < num_handles; ++i) { - if (mach_port_index < num_mach_ports && - mach_ports[mach_port_index].index == i) { - (*handles)->at(i) = PlatformHandle( - static_cast<mach_port_t>(mach_ports[mach_port_index].mach_port)); - CHECK((*handles)->at(i).type == PlatformHandle::Type::MACH); - // These are actually just Mach port names until they're resolved from - // the remote process. - (*handles)->at(i).type = PlatformHandle::Type::MACH_NAME; - mach_port_index++; - } else { - CHECK(!incoming_platform_handles_.empty()); - (*handles)->at(i) = incoming_platform_handles_.front(); - incoming_platform_handles_.pop_front(); - } - } -#else - if (incoming_platform_handles_.size() < num_handles) { - handles->reset(); - return true; - } - - handles->reset(new PlatformHandleVector(num_handles)); - for (size_t i = 0; i < num_handles; ++i) { - (*handles)->at(i) = incoming_platform_handles_.front(); - incoming_platform_handles_.pop_front(); - } -#endif - - return true; - } - - private: - ~ChannelPosix() override { - DCHECK(!read_watcher_); - DCHECK(!write_watcher_); - for (auto handle : incoming_platform_handles_) - handle.CloseIfNecessary(); - } - - void StartOnIOThread() { - DCHECK(!read_watcher_); - DCHECK(!write_watcher_); - read_watcher_.reset( - new base::MessageLoopForIO::FileDescriptorWatcher(FROM_HERE)); - base::MessageLoop::current()->AddDestructionObserver(this); - if (handle_.get().needs_connection) { - base::MessageLoopForIO::current()->WatchFileDescriptor( - handle_.get().handle, false /* persistent */, - base::MessageLoopForIO::WATCH_READ, read_watcher_.get(), this); - } else { - write_watcher_.reset( - new base::MessageLoopForIO::FileDescriptorWatcher(FROM_HERE)); - base::MessageLoopForIO::current()->WatchFileDescriptor( - handle_.get().handle, true /* persistent */, - base::MessageLoopForIO::WATCH_READ, read_watcher_.get(), this); - base::AutoLock lock(write_lock_); - FlushOutgoingMessagesNoLock(); - } - } - - void WaitForWriteOnIOThread() { - base::AutoLock lock(write_lock_); - WaitForWriteOnIOThreadNoLock(); - } - - void WaitForWriteOnIOThreadNoLock() { - if (pending_write_) - return; - if (!write_watcher_) - return; - if (io_task_runner_->RunsTasksOnCurrentThread()) { - pending_write_ = true; - base::MessageLoopForIO::current()->WatchFileDescriptor( - handle_.get().handle, false /* persistent */, - base::MessageLoopForIO::WATCH_WRITE, write_watcher_.get(), this); - } else { - io_task_runner_->PostTask( - FROM_HERE, base::Bind(&ChannelPosix::WaitForWriteOnIOThread, this)); - } - } - - void ShutDownOnIOThread() { - base::MessageLoop::current()->RemoveDestructionObserver(this); - - read_watcher_.reset(); - write_watcher_.reset(); - if (leak_handle_) - ignore_result(handle_.release()); - handle_.reset(); -#if defined(OS_MACOSX) - handles_to_close_.reset(); -#endif - - // May destroy the |this| if it was the last reference. - self_ = nullptr; - } - - // base::MessageLoop::DestructionObserver: - void WillDestroyCurrentMessageLoop() override { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - if (self_) - ShutDownOnIOThread(); - } - - // base::MessageLoopForIO::Watcher: - void OnFileCanReadWithoutBlocking(int fd) override { - CHECK_EQ(fd, handle_.get().handle); - if (handle_.get().needs_connection) { -#if !defined(OS_NACL) - read_watcher_.reset(); - base::MessageLoop::current()->RemoveDestructionObserver(this); - - ScopedPlatformHandle accept_fd; - ServerAcceptConnection(handle_.get(), &accept_fd); - if (!accept_fd.is_valid()) { - OnError(); - return; - } - handle_ = std::move(accept_fd); - StartOnIOThread(); -#else - NOTREACHED(); -#endif - return; - } - - bool read_error = false; - size_t next_read_size = 0; - size_t buffer_capacity = 0; - size_t total_bytes_read = 0; - size_t bytes_read = 0; - do { - buffer_capacity = next_read_size; - char* buffer = GetReadBuffer(&buffer_capacity); - DCHECK_GT(buffer_capacity, 0u); - - ssize_t read_result = PlatformChannelRecvmsg( - handle_.get(), - buffer, - buffer_capacity, - &incoming_platform_handles_); - - if (read_result > 0) { - bytes_read = static_cast<size_t>(read_result); - total_bytes_read += bytes_read; - if (!OnReadComplete(bytes_read, &next_read_size)) { - read_error = true; - break; - } - } else if (read_result == 0 || - (errno != EAGAIN && errno != EWOULDBLOCK)) { - read_error = true; - break; - } - } while (bytes_read == buffer_capacity && - total_bytes_read < kMaxBatchReadCapacity && - next_read_size > 0); - if (read_error) { - // Stop receiving read notifications. - read_watcher_.reset(); - - OnError(); - } - } - - void OnFileCanWriteWithoutBlocking(int fd) override { - bool write_error = false; - { - base::AutoLock lock(write_lock_); - pending_write_ = false; - if (!FlushOutgoingMessagesNoLock()) - reject_writes_ = write_error = true; - } - if (write_error) - OnError(); - } - - // Attempts to write a message directly to the channel. If the full message - // cannot be written, it's queued and a wait is initiated to write the message - // ASAP on the I/O thread. - bool WriteNoLock(MessageView message_view) { - if (handle_.get().needs_connection) { - outgoing_messages_.emplace_front(std::move(message_view)); - return true; - } - size_t bytes_written = 0; - do { - message_view.advance_data_offset(bytes_written); - - ssize_t result; - ScopedPlatformHandleVectorPtr handles = message_view.TakeHandles(); - if (handles && handles->size()) { - iovec iov = { - const_cast<void*>(message_view.data()), - message_view.data_num_bytes() - }; - // TODO: Handle lots of handles. - result = PlatformChannelSendmsgWithHandles( - handle_.get(), &iov, 1, handles->data(), handles->size()); - if (result >= 0) { -#if defined(OS_MACOSX) - // There is a bug on OSX which makes it dangerous to close - // a file descriptor while it is in transit. So instead we - // store the file descriptor in a set and send a message to - // the recipient, which is queued AFTER the message that - // sent the FD. The recipient will reply to the message, - // letting us know that it is now safe to close the file - // descriptor. For more information, see: - // http://crbug.com/298276 - std::vector<int> fds; - for (auto& handle : *handles) - fds.push_back(handle.handle); - { - base::AutoLock l(handles_to_close_lock_); - for (auto& handle : *handles) - handles_to_close_->push_back(handle); - } - MessagePtr fds_message( - new Channel::Message(sizeof(fds[0]) * fds.size(), 0, - Message::MessageType::HANDLES_SENT)); - memcpy(fds_message->mutable_payload(), fds.data(), - sizeof(fds[0]) * fds.size()); - outgoing_messages_.emplace_back(std::move(fds_message), 0); - handles->clear(); -#else - handles.reset(); -#endif // defined(OS_MACOSX) - } - } else { - result = PlatformChannelWrite(handle_.get(), message_view.data(), - message_view.data_num_bytes()); - } - - if (result < 0) { - if (errno != EAGAIN && errno != EWOULDBLOCK -#if defined(OS_MACOSX) - // On OS X if sendmsg() is trying to send fds between processes and - // there isn't enough room in the output buffer to send the fd - // structure over atomically then EMSGSIZE is returned. - // - // EMSGSIZE presents a problem since the system APIs can only call - // us when there's room in the socket buffer and not when there is - // "enough" room. - // - // The current behavior is to return to the event loop when EMSGSIZE - // is received and hopefull service another FD. This is however - // still technically a busy wait since the event loop will call us - // right back until the receiver has read enough data to allow - // passing the FD over atomically. - && errno != EMSGSIZE -#endif - ) { - return false; - } - message_view.SetHandles(std::move(handles)); - outgoing_messages_.emplace_front(std::move(message_view)); - WaitForWriteOnIOThreadNoLock(); - return true; - } - - bytes_written = static_cast<size_t>(result); - } while (bytes_written < message_view.data_num_bytes()); - - return FlushOutgoingMessagesNoLock(); - } - - bool FlushOutgoingMessagesNoLock() { - std::deque<MessageView> messages; - std::swap(outgoing_messages_, messages); - - while (!messages.empty()) { - if (!WriteNoLock(std::move(messages.front()))) - return false; - - messages.pop_front(); - if (!outgoing_messages_.empty()) { - // The message was requeued by WriteNoLock(), so we have to wait for - // pipe to become writable again. Repopulate the message queue and exit. - // If sending the message triggered any control messages, they may be - // in |outgoing_messages_| in addition to or instead of the message - // being sent. - std::swap(messages, outgoing_messages_); - while (!messages.empty()) { - outgoing_messages_.push_front(std::move(messages.back())); - messages.pop_back(); - } - return true; - } - } - - return true; - } - -#if defined(OS_MACOSX) - bool OnControlMessage(Message::MessageType message_type, - const void* payload, - size_t payload_size, - ScopedPlatformHandleVectorPtr handles) override { - switch (message_type) { - case Message::MessageType::HANDLES_SENT: { - if (payload_size == 0) - break; - MessagePtr message(new Channel::Message( - payload_size, 0, Message::MessageType::HANDLES_SENT_ACK)); - memcpy(message->mutable_payload(), payload, payload_size); - Write(std::move(message)); - return true; - } - - case Message::MessageType::HANDLES_SENT_ACK: { - size_t num_fds = payload_size / sizeof(int); - if (num_fds == 0 || payload_size % sizeof(int) != 0) - break; - - const int* fds = reinterpret_cast<const int*>(payload); - if (!CloseHandles(fds, num_fds)) - break; - return true; - } - - default: - break; - } - - return false; - } - - // Closes handles referenced by |fds|. Returns false if |num_fds| is 0, or if - // |fds| does not match a sequence of handles in |handles_to_close_|. - bool CloseHandles(const int* fds, size_t num_fds) { - base::AutoLock l(handles_to_close_lock_); - if (!num_fds) - return false; - - auto start = - std::find_if(handles_to_close_->begin(), handles_to_close_->end(), - [&fds](const PlatformHandle& handle) { - return handle.handle == fds[0]; - }); - if (start == handles_to_close_->end()) - return false; - - auto it = start; - size_t i = 0; - // The FDs in the message should match a sequence of handles in - // |handles_to_close_|. - for (; i < num_fds && it != handles_to_close_->end(); i++, ++it) { - if (it->handle != fds[i]) - return false; - - it->CloseIfNecessary(); - } - if (i != num_fds) - return false; - - handles_to_close_->erase(start, it); - return true; - } -#endif // defined(OS_MACOSX) - - // Keeps the Channel alive at least until explicit shutdown on the IO thread. - scoped_refptr<Channel> self_; - - ScopedPlatformHandle handle_; - scoped_refptr<base::TaskRunner> io_task_runner_; - - // These watchers must only be accessed on the IO thread. - std::unique_ptr<base::MessageLoopForIO::FileDescriptorWatcher> read_watcher_; - std::unique_ptr<base::MessageLoopForIO::FileDescriptorWatcher> write_watcher_; - - std::deque<PlatformHandle> incoming_platform_handles_; - - // Protects |pending_write_| and |outgoing_messages_|. - base::Lock write_lock_; - bool pending_write_ = false; - bool reject_writes_ = false; - std::deque<MessageView> outgoing_messages_; - - bool leak_handle_ = false; - -#if defined(OS_MACOSX) - base::Lock handles_to_close_lock_; - ScopedPlatformHandleVectorPtr handles_to_close_; -#endif - - DISALLOW_COPY_AND_ASSIGN(ChannelPosix); -}; - -} // namespace - -// static -scoped_refptr<Channel> Channel::Create( - Delegate* delegate, - ConnectionParams connection_params, - scoped_refptr<base::TaskRunner> io_task_runner) { - return new ChannelPosix(delegate, std::move(connection_params), - io_task_runner); -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/channel_unittest.cc b/mojo/edk/system/channel_unittest.cc deleted file mode 100644 index ce2c804..0000000 --- a/mojo/edk/system/channel_unittest.cc +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/channel.h" -#include "base/memory/ptr_util.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace edk { -namespace { - -class TestChannel : public Channel { - public: - TestChannel(Channel::Delegate* delegate) : Channel(delegate) {} - - char* GetReadBufferTest(size_t* buffer_capacity) { - return GetReadBuffer(buffer_capacity); - } - - bool OnReadCompleteTest(size_t bytes_read, size_t* next_read_size_hint) { - return OnReadComplete(bytes_read, next_read_size_hint); - } - - MOCK_METHOD4(GetReadPlatformHandles, - bool(size_t num_handles, - const void* extra_header, - size_t extra_header_size, - ScopedPlatformHandleVectorPtr* handles)); - MOCK_METHOD0(Start, void()); - MOCK_METHOD0(ShutDownImpl, void()); - MOCK_METHOD0(LeakHandle, void()); - - void Write(MessagePtr message) {} - - protected: - ~TestChannel() override {} -}; - -// Not using GMock as I don't think it supports movable types. -class MockChannelDelegate : public Channel::Delegate { - public: - MockChannelDelegate() {} - - size_t GetReceivedPayloadSize() const { return payload_size_; } - - const void* GetReceivedPayload() const { return payload_.get(); } - - protected: - void OnChannelMessage(const void* payload, - size_t payload_size, - ScopedPlatformHandleVectorPtr handles) override { - payload_.reset(new char[payload_size]); - memcpy(payload_.get(), payload, payload_size); - payload_size_ = payload_size; - } - - // Notify that an error has occured and the Channel will cease operation. - void OnChannelError() override {} - - private: - size_t payload_size_ = 0; - std::unique_ptr<char[]> payload_; -}; - -Channel::MessagePtr CreateDefaultMessage(bool legacy_message) { - const size_t payload_size = 100; - Channel::MessagePtr message = base::MakeUnique<Channel::Message>( - payload_size, 0, - legacy_message ? Channel::Message::MessageType::NORMAL_LEGACY - : Channel::Message::MessageType::NORMAL); - char* payload = static_cast<char*>(message->mutable_payload()); - for (size_t i = 0; i < payload_size; i++) { - payload[i] = static_cast<char>(i); - } - return message; -} - -void TestMemoryEqual(const void* data1, - size_t data1_size, - const void* data2, - size_t data2_size) { - ASSERT_EQ(data1_size, data2_size); - const unsigned char* data1_char = static_cast<const unsigned char*>(data1); - const unsigned char* data2_char = static_cast<const unsigned char*>(data2); - for (size_t i = 0; i < data1_size; i++) { - // ASSERT so we don't log tons of errors if the data is different. - ASSERT_EQ(data1_char[i], data2_char[i]); - } -} - -void TestMessagesAreEqual(Channel::Message* message1, - Channel::Message* message2, - bool legacy_messages) { - // If any of the message is null, this is probably not what you wanted to - // test. - ASSERT_NE(nullptr, message1); - ASSERT_NE(nullptr, message2); - - ASSERT_EQ(message1->payload_size(), message2->payload_size()); - EXPECT_EQ(message1->has_handles(), message2->has_handles()); - - TestMemoryEqual(message1->payload(), message1->payload_size(), - message2->payload(), message2->payload_size()); - - if (legacy_messages) - return; - - ASSERT_EQ(message1->extra_header_size(), message2->extra_header_size()); - TestMemoryEqual(message1->extra_header(), message1->extra_header_size(), - message2->extra_header(), message2->extra_header_size()); -} - -TEST(ChannelTest, LegacyMessageDeserialization) { - Channel::MessagePtr message = CreateDefaultMessage(true /* legacy_message */); - Channel::MessagePtr deserialized_message = - Channel::Message::Deserialize(message->data(), message->data_num_bytes()); - TestMessagesAreEqual(message.get(), deserialized_message.get(), - true /* legacy_message */); -} - -TEST(ChannelTest, NonLegacyMessageDeserialization) { - Channel::MessagePtr message = - CreateDefaultMessage(false /* legacy_message */); - Channel::MessagePtr deserialized_message = - Channel::Message::Deserialize(message->data(), message->data_num_bytes()); - TestMessagesAreEqual(message.get(), deserialized_message.get(), - false /* legacy_message */); -} - -TEST(ChannelTest, OnReadLegacyMessage) { - size_t buffer_size = 100 * 1024; - Channel::MessagePtr message = CreateDefaultMessage(true /* legacy_message */); - - MockChannelDelegate channel_delegate; - scoped_refptr<TestChannel> channel = new TestChannel(&channel_delegate); - char* read_buffer = channel->GetReadBufferTest(&buffer_size); - ASSERT_LT(message->data_num_bytes(), - buffer_size); // Bad test. Increase buffer - // size. - memcpy(read_buffer, message->data(), message->data_num_bytes()); - - size_t next_read_size_hint = 0; - EXPECT_TRUE(channel->OnReadCompleteTest(message->data_num_bytes(), - &next_read_size_hint)); - - TestMemoryEqual(message->payload(), message->payload_size(), - channel_delegate.GetReceivedPayload(), - channel_delegate.GetReceivedPayloadSize()); -} - -TEST(ChannelTest, OnReadNonLegacyMessage) { - size_t buffer_size = 100 * 1024; - Channel::MessagePtr message = - CreateDefaultMessage(false /* legacy_message */); - - MockChannelDelegate channel_delegate; - scoped_refptr<TestChannel> channel = new TestChannel(&channel_delegate); - char* read_buffer = channel->GetReadBufferTest(&buffer_size); - ASSERT_LT(message->data_num_bytes(), - buffer_size); // Bad test. Increase buffer - // size. - memcpy(read_buffer, message->data(), message->data_num_bytes()); - - size_t next_read_size_hint = 0; - EXPECT_TRUE(channel->OnReadCompleteTest(message->data_num_bytes(), - &next_read_size_hint)); - - TestMemoryEqual(message->payload(), message->payload_size(), - channel_delegate.GetReceivedPayload(), - channel_delegate.GetReceivedPayloadSize()); -} - -} // namespace -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/channel_win.cc b/mojo/edk/system/channel_win.cc deleted file mode 100644 index c15df16..0000000 --- a/mojo/edk/system/channel_win.cc +++ /dev/null @@ -1,360 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/channel.h" - -#include <stdint.h> -#include <windows.h> - -#include <algorithm> -#include <deque> -#include <limits> -#include <memory> - -#include "base/bind.h" -#include "base/location.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" -#include "base/synchronization/lock.h" -#include "base/task_runner.h" -#include "base/win/win_util.h" -#include "mojo/edk/embedder/platform_handle_vector.h" - -namespace mojo { -namespace edk { - -namespace { - -// A view over a Channel::Message object. The write queue uses these since -// large messages may need to be sent in chunks. -class MessageView { - public: - // Owns |message|. |offset| indexes the first unsent byte in the message. - MessageView(Channel::MessagePtr message, size_t offset) - : message_(std::move(message)), - offset_(offset) { - DCHECK_GT(message_->data_num_bytes(), offset_); - } - - MessageView(MessageView&& other) { *this = std::move(other); } - - MessageView& operator=(MessageView&& other) { - message_ = std::move(other.message_); - offset_ = other.offset_; - return *this; - } - - ~MessageView() {} - - const void* data() const { - return static_cast<const char*>(message_->data()) + offset_; - } - - size_t data_num_bytes() const { return message_->data_num_bytes() - offset_; } - - size_t data_offset() const { return offset_; } - void advance_data_offset(size_t num_bytes) { - DCHECK_GE(message_->data_num_bytes(), offset_ + num_bytes); - offset_ += num_bytes; - } - - Channel::MessagePtr TakeChannelMessage() { return std::move(message_); } - - private: - Channel::MessagePtr message_; - size_t offset_; - - DISALLOW_COPY_AND_ASSIGN(MessageView); -}; - -class ChannelWin : public Channel, - public base::MessageLoop::DestructionObserver, - public base::MessageLoopForIO::IOHandler { - public: - ChannelWin(Delegate* delegate, - ScopedPlatformHandle handle, - scoped_refptr<base::TaskRunner> io_task_runner) - : Channel(delegate), - self_(this), - handle_(std::move(handle)), - io_task_runner_(io_task_runner) { - CHECK(handle_.is_valid()); - - wait_for_connect_ = handle_.get().needs_connection; - } - - void Start() override { - io_task_runner_->PostTask( - FROM_HERE, base::Bind(&ChannelWin::StartOnIOThread, this)); - } - - void ShutDownImpl() override { - // Always shut down asynchronously when called through the public interface. - io_task_runner_->PostTask( - FROM_HERE, base::Bind(&ChannelWin::ShutDownOnIOThread, this)); - } - - void Write(MessagePtr message) override { - bool write_error = false; - { - base::AutoLock lock(write_lock_); - if (reject_writes_) - return; - - bool write_now = !delay_writes_ && outgoing_messages_.empty(); - outgoing_messages_.emplace_back(std::move(message), 0); - - if (write_now && !WriteNoLock(outgoing_messages_.front())) - reject_writes_ = write_error = true; - } - if (write_error) { - // Do not synchronously invoke OnError(). Write() may have been called by - // the delegate and we don't want to re-enter it. - io_task_runner_->PostTask(FROM_HERE, - base::Bind(&ChannelWin::OnError, this)); - } - } - - void LeakHandle() override { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - leak_handle_ = true; - } - - bool GetReadPlatformHandles( - size_t num_handles, - const void* extra_header, - size_t extra_header_size, - ScopedPlatformHandleVectorPtr* handles) override { - if (num_handles > std::numeric_limits<uint16_t>::max()) - return false; - using HandleEntry = Channel::Message::HandleEntry; - size_t handles_size = sizeof(HandleEntry) * num_handles; - if (handles_size > extra_header_size) - return false; - DCHECK(extra_header); - handles->reset(new PlatformHandleVector(num_handles)); - const HandleEntry* extra_header_handles = - reinterpret_cast<const HandleEntry*>(extra_header); - for (size_t i = 0; i < num_handles; i++) { - (*handles)->at(i).handle = - base::win::Uint32ToHandle(extra_header_handles[i].handle); - } - return true; - } - - private: - // May run on any thread. - ~ChannelWin() override {} - - void StartOnIOThread() { - base::MessageLoop::current()->AddDestructionObserver(this); - base::MessageLoopForIO::current()->RegisterIOHandler( - handle_.get().handle, this); - - if (wait_for_connect_) { - BOOL ok = ConnectNamedPipe(handle_.get().handle, - &connect_context_.overlapped); - if (ok) { - PLOG(ERROR) << "Unexpected success while waiting for pipe connection"; - OnError(); - return; - } - - const DWORD err = GetLastError(); - switch (err) { - case ERROR_PIPE_CONNECTED: - wait_for_connect_ = false; - break; - case ERROR_IO_PENDING: - AddRef(); - return; - case ERROR_NO_DATA: - OnError(); - return; - } - } - - // Now that we have registered our IOHandler, we can start writing. - { - base::AutoLock lock(write_lock_); - if (delay_writes_) { - delay_writes_ = false; - WriteNextNoLock(); - } - } - - // Keep this alive in case we synchronously run shutdown. - scoped_refptr<ChannelWin> keep_alive(this); - ReadMore(0); - } - - void ShutDownOnIOThread() { - base::MessageLoop::current()->RemoveDestructionObserver(this); - - // BUG(crbug.com/583525): This function is expected to be called once, and - // |handle_| should be valid at this point. - CHECK(handle_.is_valid()); - CancelIo(handle_.get().handle); - if (leak_handle_) - ignore_result(handle_.release()); - handle_.reset(); - - // May destroy the |this| if it was the last reference. - self_ = nullptr; - } - - // base::MessageLoop::DestructionObserver: - void WillDestroyCurrentMessageLoop() override { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - if (self_) - ShutDownOnIOThread(); - } - - // base::MessageLoop::IOHandler: - void OnIOCompleted(base::MessageLoopForIO::IOContext* context, - DWORD bytes_transfered, - DWORD error) override { - if (error != ERROR_SUCCESS) { - OnError(); - } else if (context == &connect_context_) { - DCHECK(wait_for_connect_); - wait_for_connect_ = false; - ReadMore(0); - - base::AutoLock lock(write_lock_); - if (delay_writes_) { - delay_writes_ = false; - WriteNextNoLock(); - } - } else if (context == &read_context_) { - OnReadDone(static_cast<size_t>(bytes_transfered)); - } else { - CHECK(context == &write_context_); - OnWriteDone(static_cast<size_t>(bytes_transfered)); - } - Release(); // Balancing reference taken after ReadFile / WriteFile. - } - - void OnReadDone(size_t bytes_read) { - if (bytes_read > 0) { - size_t next_read_size = 0; - if (OnReadComplete(bytes_read, &next_read_size)) { - ReadMore(next_read_size); - } else { - OnError(); - } - } else if (bytes_read == 0) { - OnError(); - } - } - - void OnWriteDone(size_t bytes_written) { - if (bytes_written == 0) - return; - - bool write_error = false; - { - base::AutoLock lock(write_lock_); - - DCHECK(!outgoing_messages_.empty()); - - MessageView& message_view = outgoing_messages_.front(); - message_view.advance_data_offset(bytes_written); - if (message_view.data_num_bytes() == 0) { - Channel::MessagePtr message = message_view.TakeChannelMessage(); - outgoing_messages_.pop_front(); - - // Clear any handles so they don't get closed on destruction. - ScopedPlatformHandleVectorPtr handles = message->TakeHandles(); - if (handles) - handles->clear(); - } - - if (!WriteNextNoLock()) - reject_writes_ = write_error = true; - } - if (write_error) - OnError(); - } - - void ReadMore(size_t next_read_size_hint) { - size_t buffer_capacity = next_read_size_hint; - char* buffer = GetReadBuffer(&buffer_capacity); - DCHECK_GT(buffer_capacity, 0u); - - BOOL ok = ReadFile(handle_.get().handle, - buffer, - static_cast<DWORD>(buffer_capacity), - NULL, - &read_context_.overlapped); - - if (ok || GetLastError() == ERROR_IO_PENDING) { - AddRef(); // Will be balanced in OnIOCompleted - } else { - OnError(); - } - } - - // Attempts to write a message directly to the channel. If the full message - // cannot be written, it's queued and a wait is initiated to write the message - // ASAP on the I/O thread. - bool WriteNoLock(const MessageView& message_view) { - BOOL ok = WriteFile(handle_.get().handle, - message_view.data(), - static_cast<DWORD>(message_view.data_num_bytes()), - NULL, - &write_context_.overlapped); - - if (ok || GetLastError() == ERROR_IO_PENDING) { - AddRef(); // Will be balanced in OnIOCompleted. - return true; - } - return false; - } - - bool WriteNextNoLock() { - if (outgoing_messages_.empty()) - return true; - return WriteNoLock(outgoing_messages_.front()); - } - - // Keeps the Channel alive at least until explicit shutdown on the IO thread. - scoped_refptr<Channel> self_; - - ScopedPlatformHandle handle_; - scoped_refptr<base::TaskRunner> io_task_runner_; - - base::MessageLoopForIO::IOContext connect_context_; - base::MessageLoopForIO::IOContext read_context_; - base::MessageLoopForIO::IOContext write_context_; - - // Protects |reject_writes_| and |outgoing_messages_|. - base::Lock write_lock_; - - bool delay_writes_ = true; - - bool reject_writes_ = false; - std::deque<MessageView> outgoing_messages_; - - bool wait_for_connect_; - - bool leak_handle_ = false; - - DISALLOW_COPY_AND_ASSIGN(ChannelWin); -}; - -} // namespace - -// static -scoped_refptr<Channel> Channel::Create( - Delegate* delegate, - ConnectionParams connection_params, - scoped_refptr<base::TaskRunner> io_task_runner) { - return new ChannelWin(delegate, connection_params.TakeChannelHandle(), - io_task_runner); -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/configuration.cc b/mojo/edk/system/configuration.cc deleted file mode 100644 index f5eb2b8..0000000 --- a/mojo/edk/system/configuration.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/system/configuration.h" - -namespace mojo { -namespace edk { -namespace internal { - -// These default values should be synced with the documentation in -// mojo/edk/embedder/configuration.h. -Configuration g_configuration = { - 1000000, // max_handle_table_size - 1000000, // max_mapping_table_sze - 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 edk -} // namespace mojo diff --git a/mojo/edk/system/configuration.h b/mojo/edk/system/configuration.h deleted file mode 100644 index 038835f..0000000 --- a/mojo/edk/system/configuration.h +++ /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. - -#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 edk { - -namespace internal { -MOJO_SYSTEM_IMPL_EXPORT extern Configuration g_configuration; -} // namespace internal - -MOJO_SYSTEM_IMPL_EXPORT inline const Configuration& GetConfiguration() { - return internal::g_configuration; -} - -MOJO_SYSTEM_IMPL_EXPORT inline Configuration* GetMutableConfiguration() { - return &internal::g_configuration; -} - -} // namespace edk -} // 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 360e8c3..0000000 --- a/mojo/edk/system/core.cc +++ /dev/null @@ -1,1019 +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 <string.h> - -#include <utility> - -#include "base/bind.h" -#include "base/containers/stack_container.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop.h" -#include "base/rand_util.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/time/time.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/embedder/embedder_internal.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/configuration.h" -#include "mojo/edk/system/data_pipe_consumer_dispatcher.h" -#include "mojo/edk/system/data_pipe_producer_dispatcher.h" -#include "mojo/edk/system/handle_signals_state.h" -#include "mojo/edk/system/message_for_transit.h" -#include "mojo/edk/system/message_pipe_dispatcher.h" -#include "mojo/edk/system/platform_handle_dispatcher.h" -#include "mojo/edk/system/ports/name.h" -#include "mojo/edk/system/ports/node.h" -#include "mojo/edk/system/request_context.h" -#include "mojo/edk/system/shared_buffer_dispatcher.h" -#include "mojo/edk/system/watcher_dispatcher.h" - -namespace mojo { -namespace edk { - -namespace { - -// This is an unnecessarily large limit that is relatively easy to enforce. -const uint32_t kMaxHandlesPerMessage = 1024 * 1024; - -// TODO(rockot): Maybe we could negotiate a debugging pipe ID for cross-process -// pipes too; for now we just use a constant. This only affects bootstrap pipes. -const uint64_t kUnknownPipeIdForDebug = 0x7f7f7f7f7f7f7f7fUL; - -MojoResult MojoPlatformHandleToScopedPlatformHandle( - const MojoPlatformHandle* platform_handle, - ScopedPlatformHandle* out_handle) { - if (platform_handle->struct_size != sizeof(MojoPlatformHandle)) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (platform_handle->type == MOJO_PLATFORM_HANDLE_TYPE_INVALID) { - out_handle->reset(); - return MOJO_RESULT_OK; - } - - PlatformHandle handle; - switch (platform_handle->type) { -#if defined(OS_POSIX) - case MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR: - handle.handle = static_cast<int>(platform_handle->value); - break; -#endif - -#if defined(OS_MACOSX) && !defined(OS_IOS) - case MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT: - handle.type = PlatformHandle::Type::MACH; - handle.port = static_cast<mach_port_t>(platform_handle->value); - break; -#endif - -#if defined(OS_WIN) - case MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE: - handle.handle = reinterpret_cast<HANDLE>(platform_handle->value); - break; -#endif - - default: - return MOJO_RESULT_INVALID_ARGUMENT; - } - - out_handle->reset(handle); - return MOJO_RESULT_OK; -} - -MojoResult ScopedPlatformHandleToMojoPlatformHandle( - ScopedPlatformHandle handle, - MojoPlatformHandle* platform_handle) { - if (platform_handle->struct_size != sizeof(MojoPlatformHandle)) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (!handle.is_valid()) { - platform_handle->type = MOJO_PLATFORM_HANDLE_TYPE_INVALID; - return MOJO_RESULT_OK; - } - -#if defined(OS_POSIX) - switch (handle.get().type) { - case PlatformHandle::Type::POSIX: - platform_handle->type = MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR; - platform_handle->value = static_cast<uint64_t>(handle.release().handle); - break; - -#if defined(OS_MACOSX) && !defined(OS_IOS) - case PlatformHandle::Type::MACH: - platform_handle->type = MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT; - platform_handle->value = static_cast<uint64_t>(handle.release().port); - break; -#endif // defined(OS_MACOSX) && !defined(OS_IOS) - - default: - return MOJO_RESULT_INVALID_ARGUMENT; - } -#elif defined(OS_WIN) - platform_handle->type = MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE; - platform_handle->value = reinterpret_cast<uint64_t>(handle.release().handle); -#endif // defined(OS_WIN) - - return MOJO_RESULT_OK; -} - -} // namespace - -Core::Core() {} - -Core::~Core() { - if (node_controller_ && node_controller_->io_task_runner()) { - // If this races with IO thread shutdown the callback will be dropped and - // the NodeController will be shutdown on this thread anyway, which is also - // just fine. - scoped_refptr<base::TaskRunner> io_task_runner = - node_controller_->io_task_runner(); - io_task_runner->PostTask(FROM_HERE, - base::Bind(&Core::PassNodeControllerToIOThread, - base::Passed(&node_controller_))); - } -} - -void Core::SetIOTaskRunner(scoped_refptr<base::TaskRunner> io_task_runner) { - GetNodeController()->SetIOTaskRunner(io_task_runner); -} - -NodeController* Core::GetNodeController() { - base::AutoLock lock(node_controller_lock_); - if (!node_controller_) - node_controller_.reset(new NodeController(this)); - return node_controller_.get(); -} - -scoped_refptr<Dispatcher> Core::GetDispatcher(MojoHandle handle) { - base::AutoLock lock(handles_lock_); - return handles_.GetDispatcher(handle); -} - -void Core::SetDefaultProcessErrorCallback( - const ProcessErrorCallback& callback) { - default_process_error_callback_ = callback; -} - -void Core::AddChild(base::ProcessHandle process_handle, - ConnectionParams connection_params, - const std::string& child_token, - const ProcessErrorCallback& process_error_callback) { - GetNodeController()->ConnectToChild(process_handle, - std::move(connection_params), child_token, - process_error_callback); -} - -void Core::ChildLaunchFailed(const std::string& child_token) { - RequestContext request_context; - GetNodeController()->CloseChildPorts(child_token); -} - -ScopedMessagePipeHandle Core::ConnectToPeerProcess( - ScopedPlatformHandle pipe_handle, - const std::string& peer_token) { - RequestContext request_context; - ports::PortRef port0, port1; - GetNodeController()->node()->CreatePortPair(&port0, &port1); - MojoHandle handle = AddDispatcher(new MessagePipeDispatcher( - GetNodeController(), port0, kUnknownPipeIdForDebug, 0)); - ConnectionParams connection_params(std::move(pipe_handle)); - GetNodeController()->ConnectToPeer(std::move(connection_params), port1, - peer_token); - return ScopedMessagePipeHandle(MessagePipeHandle(handle)); -} - -void Core::ClosePeerConnection(const std::string& peer_token) { - GetNodeController()->ClosePeerConnection(peer_token); -} - -void Core::InitChild(ConnectionParams connection_params) { - GetNodeController()->ConnectToParent(std::move(connection_params)); -} - -void Core::SetMachPortProvider(base::PortProvider* port_provider) { -#if defined(OS_MACOSX) && !defined(OS_IOS) - GetNodeController()->CreateMachPortRelay(port_provider); -#endif -} - -MojoHandle Core::AddDispatcher(scoped_refptr<Dispatcher> dispatcher) { - base::AutoLock lock(handles_lock_); - return handles_.AddDispatcher(dispatcher); -} - -bool Core::AddDispatchersFromTransit( - const std::vector<Dispatcher::DispatcherInTransit>& dispatchers, - MojoHandle* handles) { - bool failed = false; - { - base::AutoLock lock(handles_lock_); - if (!handles_.AddDispatchersFromTransit(dispatchers, handles)) - failed = true; - } - if (failed) { - for (auto d : dispatchers) - d.dispatcher->Close(); - return false; - } - return true; -} - -MojoResult Core::CreatePlatformHandleWrapper( - ScopedPlatformHandle platform_handle, - MojoHandle* wrapper_handle) { - MojoHandle h = AddDispatcher( - PlatformHandleDispatcher::Create(std::move(platform_handle))); - if (h == MOJO_HANDLE_INVALID) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - *wrapper_handle = h; - return MOJO_RESULT_OK; -} - -MojoResult Core::PassWrappedPlatformHandle( - MojoHandle wrapper_handle, - ScopedPlatformHandle* platform_handle) { - base::AutoLock lock(handles_lock_); - scoped_refptr<Dispatcher> d; - MojoResult result = handles_.GetAndRemoveDispatcher(wrapper_handle, &d); - if (result != MOJO_RESULT_OK) - return result; - if (d->GetType() == Dispatcher::Type::PLATFORM_HANDLE) { - PlatformHandleDispatcher* phd = - static_cast<PlatformHandleDispatcher*>(d.get()); - *platform_handle = phd->PassPlatformHandle(); - } else { - result = MOJO_RESULT_INVALID_ARGUMENT; - } - d->Close(); - return result; -} - -MojoResult Core::CreateSharedBufferWrapper( - base::SharedMemoryHandle shared_memory_handle, - size_t num_bytes, - bool read_only, - MojoHandle* mojo_wrapper_handle) { - DCHECK(num_bytes); - scoped_refptr<PlatformSharedBuffer> platform_buffer = - PlatformSharedBuffer::CreateFromSharedMemoryHandle(num_bytes, read_only, - shared_memory_handle); - if (!platform_buffer) - return MOJO_RESULT_UNKNOWN; - - scoped_refptr<SharedBufferDispatcher> dispatcher; - MojoResult result = SharedBufferDispatcher::CreateFromPlatformSharedBuffer( - platform_buffer, &dispatcher); - if (result != MOJO_RESULT_OK) - return result; - MojoHandle h = AddDispatcher(dispatcher); - if (h == MOJO_HANDLE_INVALID) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - *mojo_wrapper_handle = h; - return MOJO_RESULT_OK; -} - -MojoResult Core::PassSharedMemoryHandle( - MojoHandle mojo_handle, - base::SharedMemoryHandle* shared_memory_handle, - size_t* num_bytes, - bool* read_only) { - if (!shared_memory_handle) - return MOJO_RESULT_INVALID_ARGUMENT; - - scoped_refptr<Dispatcher> dispatcher; - MojoResult result = MOJO_RESULT_OK; - { - base::AutoLock lock(handles_lock_); - // Get the dispatcher and check it before removing it from the handle table - // to ensure that the dispatcher is of the correct type. This ensures we - // don't close and remove the wrong type of dispatcher. - dispatcher = handles_.GetDispatcher(mojo_handle); - if (!dispatcher || dispatcher->GetType() != Dispatcher::Type::SHARED_BUFFER) - return MOJO_RESULT_INVALID_ARGUMENT; - - result = handles_.GetAndRemoveDispatcher(mojo_handle, &dispatcher); - if (result != MOJO_RESULT_OK) - return result; - } - - SharedBufferDispatcher* shm_dispatcher = - static_cast<SharedBufferDispatcher*>(dispatcher.get()); - scoped_refptr<PlatformSharedBuffer> platform_shared_buffer = - shm_dispatcher->PassPlatformSharedBuffer(); - - if (!platform_shared_buffer) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (num_bytes) - *num_bytes = platform_shared_buffer->GetNumBytes(); - if (read_only) - *read_only = platform_shared_buffer->IsReadOnly(); - *shared_memory_handle = platform_shared_buffer->DuplicateSharedMemoryHandle(); - - shm_dispatcher->Close(); - return result; -} - -void Core::RequestShutdown(const base::Closure& callback) { - GetNodeController()->RequestShutdown(callback); -} - -ScopedMessagePipeHandle Core::CreateParentMessagePipe( - const std::string& token, const std::string& child_token) { - RequestContext request_context; - ports::PortRef port0, port1; - GetNodeController()->node()->CreatePortPair(&port0, &port1); - MojoHandle handle = AddDispatcher( - new MessagePipeDispatcher(GetNodeController(), port0, - kUnknownPipeIdForDebug, 0)); - GetNodeController()->ReservePort(token, port1, child_token); - return ScopedMessagePipeHandle(MessagePipeHandle(handle)); -} - -ScopedMessagePipeHandle Core::CreateChildMessagePipe(const std::string& token) { - RequestContext request_context; - ports::PortRef port0, port1; - GetNodeController()->node()->CreatePortPair(&port0, &port1); - MojoHandle handle = AddDispatcher( - new MessagePipeDispatcher(GetNodeController(), port0, - kUnknownPipeIdForDebug, 1)); - GetNodeController()->MergePortIntoParent(token, port1); - return ScopedMessagePipeHandle(MessagePipeHandle(handle)); -} - -MojoResult Core::SetProperty(MojoPropertyType type, const void* value) { - base::AutoLock locker(property_lock_); - switch (type) { - case MOJO_PROPERTY_TYPE_SYNC_CALL_ALLOWED: - property_sync_call_allowed_ = *static_cast<const bool*>(value); - return MOJO_RESULT_OK; - default: - return MOJO_RESULT_INVALID_ARGUMENT; - } -} - -MojoTimeTicks Core::GetTimeTicksNow() { - return base::TimeTicks::Now().ToInternalValue(); -} - -MojoResult Core::Close(MojoHandle handle) { - RequestContext request_context; - scoped_refptr<Dispatcher> dispatcher; - { - base::AutoLock lock(handles_lock_); - MojoResult rv = handles_.GetAndRemoveDispatcher(handle, &dispatcher); - if (rv != MOJO_RESULT_OK) - return rv; - } - dispatcher->Close(); - return MOJO_RESULT_OK; -} - -MojoResult Core::QueryHandleSignalsState( - MojoHandle handle, - MojoHandleSignalsState* signals_state) { - RequestContext request_context; - scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handle); - if (!dispatcher || !signals_state) - return MOJO_RESULT_INVALID_ARGUMENT; - *signals_state = dispatcher->GetHandleSignalsState(); - return MOJO_RESULT_OK; -} - -MojoResult Core::CreateWatcher(MojoWatcherCallback callback, - MojoHandle* watcher_handle) { - RequestContext request_context; - if (!watcher_handle) - return MOJO_RESULT_INVALID_ARGUMENT; - *watcher_handle = AddDispatcher(new WatcherDispatcher(callback)); - if (*watcher_handle == MOJO_HANDLE_INVALID) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - return MOJO_RESULT_OK; -} - -MojoResult Core::Watch(MojoHandle watcher_handle, - MojoHandle handle, - MojoHandleSignals signals, - uintptr_t context) { - RequestContext request_context; - scoped_refptr<Dispatcher> watcher = GetDispatcher(watcher_handle); - if (!watcher || watcher->GetType() != Dispatcher::Type::WATCHER) - return MOJO_RESULT_INVALID_ARGUMENT; - scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handle); - if (!dispatcher) - return MOJO_RESULT_INVALID_ARGUMENT; - return watcher->WatchDispatcher(dispatcher, signals, context); -} - -MojoResult Core::CancelWatch(MojoHandle watcher_handle, uintptr_t context) { - RequestContext request_context; - scoped_refptr<Dispatcher> watcher = GetDispatcher(watcher_handle); - if (!watcher || watcher->GetType() != Dispatcher::Type::WATCHER) - return MOJO_RESULT_INVALID_ARGUMENT; - return watcher->CancelWatch(context); -} - -MojoResult Core::ArmWatcher(MojoHandle watcher_handle, - uint32_t* num_ready_contexts, - uintptr_t* ready_contexts, - MojoResult* ready_results, - MojoHandleSignalsState* ready_signals_states) { - RequestContext request_context; - scoped_refptr<Dispatcher> watcher = GetDispatcher(watcher_handle); - if (!watcher || watcher->GetType() != Dispatcher::Type::WATCHER) - return MOJO_RESULT_INVALID_ARGUMENT; - return watcher->Arm(num_ready_contexts, ready_contexts, ready_results, - ready_signals_states); -} - -MojoResult Core::AllocMessage(uint32_t num_bytes, - const MojoHandle* handles, - uint32_t num_handles, - MojoAllocMessageFlags flags, - MojoMessageHandle* message) { - if (!message) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (num_handles == 0) { // Fast path: no handles. - std::unique_ptr<MessageForTransit> msg; - MojoResult rv = MessageForTransit::Create(&msg, num_bytes, nullptr, 0); - if (rv != MOJO_RESULT_OK) - return rv; - - *message = reinterpret_cast<MojoMessageHandle>(msg.release()); - return MOJO_RESULT_OK; - } - - if (!handles) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (num_handles > kMaxHandlesPerMessage) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - - std::vector<Dispatcher::DispatcherInTransit> dispatchers; - { - base::AutoLock lock(handles_lock_); - MojoResult rv = handles_.BeginTransit(handles, num_handles, &dispatchers); - if (rv != MOJO_RESULT_OK) { - handles_.CancelTransit(dispatchers); - return rv; - } - } - DCHECK_EQ(num_handles, dispatchers.size()); - - std::unique_ptr<MessageForTransit> msg; - MojoResult rv = MessageForTransit::Create( - &msg, num_bytes, dispatchers.data(), num_handles); - - { - base::AutoLock lock(handles_lock_); - if (rv == MOJO_RESULT_OK) { - handles_.CompleteTransitAndClose(dispatchers); - *message = reinterpret_cast<MojoMessageHandle>(msg.release()); - } else { - handles_.CancelTransit(dispatchers); - } - } - - return rv; -} - -MojoResult Core::FreeMessage(MojoMessageHandle message) { - if (!message) - return MOJO_RESULT_INVALID_ARGUMENT; - - delete reinterpret_cast<MessageForTransit*>(message); - - return MOJO_RESULT_OK; -} - -MojoResult Core::GetMessageBuffer(MojoMessageHandle message, void** buffer) { - if (!message) - return MOJO_RESULT_INVALID_ARGUMENT; - - *buffer = reinterpret_cast<MessageForTransit*>(message)->mutable_bytes(); - - return MOJO_RESULT_OK; -} - -MojoResult Core::GetProperty(MojoPropertyType type, void* value) { - base::AutoLock locker(property_lock_); - switch (type) { - case MOJO_PROPERTY_TYPE_SYNC_CALL_ALLOWED: - *static_cast<bool*>(value) = property_sync_call_allowed_; - return MOJO_RESULT_OK; - default: - return MOJO_RESULT_INVALID_ARGUMENT; - } -} - -MojoResult Core::CreateMessagePipe( - const MojoCreateMessagePipeOptions* options, - MojoHandle* message_pipe_handle0, - MojoHandle* message_pipe_handle1) { - RequestContext request_context; - ports::PortRef port0, port1; - GetNodeController()->node()->CreatePortPair(&port0, &port1); - - CHECK(message_pipe_handle0); - CHECK(message_pipe_handle1); - - uint64_t pipe_id = base::RandUint64(); - - *message_pipe_handle0 = AddDispatcher( - new MessagePipeDispatcher(GetNodeController(), port0, pipe_id, 0)); - if (*message_pipe_handle0 == MOJO_HANDLE_INVALID) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - - *message_pipe_handle1 = AddDispatcher( - new MessagePipeDispatcher(GetNodeController(), port1, pipe_id, 1)); - if (*message_pipe_handle1 == MOJO_HANDLE_INVALID) { - scoped_refptr<Dispatcher> unused; - unused->Close(); - - base::AutoLock lock(handles_lock_); - handles_.GetAndRemoveDispatcher(*message_pipe_handle0, &unused); - return MOJO_RESULT_RESOURCE_EXHAUSTED; - } - - return MOJO_RESULT_OK; -} - -MojoResult Core::WriteMessage(MojoHandle message_pipe_handle, - const void* bytes, - uint32_t num_bytes, - const MojoHandle* handles, - uint32_t num_handles, - MojoWriteMessageFlags flags) { - if (num_bytes && !bytes) - return MOJO_RESULT_INVALID_ARGUMENT; - - MojoMessageHandle message; - MojoResult rv = AllocMessage(num_bytes, handles, num_handles, - MOJO_ALLOC_MESSAGE_FLAG_NONE, &message); - if (rv != MOJO_RESULT_OK) - return rv; - - if (num_bytes) { - void* buffer = nullptr; - rv = GetMessageBuffer(message, &buffer); - DCHECK_EQ(rv, MOJO_RESULT_OK); - memcpy(buffer, bytes, num_bytes); - } - - return WriteMessageNew(message_pipe_handle, message, flags); -} - -MojoResult Core::WriteMessageNew(MojoHandle message_pipe_handle, - MojoMessageHandle message, - MojoWriteMessageFlags flags) { - RequestContext request_context; - std::unique_ptr<MessageForTransit> message_for_transit( - reinterpret_cast<MessageForTransit*>(message)); - auto dispatcher = GetDispatcher(message_pipe_handle); - if (!dispatcher) - return MOJO_RESULT_INVALID_ARGUMENT; - - return dispatcher->WriteMessage(std::move(message_for_transit), flags); -} - -MojoResult Core::ReadMessage(MojoHandle message_pipe_handle, - void* bytes, - uint32_t* num_bytes, - MojoHandle* handles, - uint32_t* num_handles, - MojoReadMessageFlags flags) { - CHECK((!num_handles || !*num_handles || handles) && - (!num_bytes || !*num_bytes || bytes)); - RequestContext request_context; - auto dispatcher = GetDispatcher(message_pipe_handle); - if (!dispatcher) - return MOJO_RESULT_INVALID_ARGUMENT; - std::unique_ptr<MessageForTransit> message; - MojoResult rv = - dispatcher->ReadMessage(&message, num_bytes, handles, num_handles, flags, - false /* ignore_num_bytes */); - if (rv != MOJO_RESULT_OK) - return rv; - - if (message && message->num_bytes()) - memcpy(bytes, message->bytes(), message->num_bytes()); - - return MOJO_RESULT_OK; -} - -MojoResult Core::ReadMessageNew(MojoHandle message_pipe_handle, - MojoMessageHandle* message, - uint32_t* num_bytes, - MojoHandle* handles, - uint32_t* num_handles, - MojoReadMessageFlags flags) { - CHECK(message); - CHECK(!num_handles || !*num_handles || handles); - RequestContext request_context; - auto dispatcher = GetDispatcher(message_pipe_handle); - if (!dispatcher) - return MOJO_RESULT_INVALID_ARGUMENT; - std::unique_ptr<MessageForTransit> msg; - MojoResult rv = - dispatcher->ReadMessage(&msg, num_bytes, handles, num_handles, flags, - true /* ignore_num_bytes */); - if (rv != MOJO_RESULT_OK) - return rv; - *message = reinterpret_cast<MojoMessageHandle>(msg.release()); - return MOJO_RESULT_OK; -} - -MojoResult Core::FuseMessagePipes(MojoHandle handle0, MojoHandle handle1) { - RequestContext request_context; - scoped_refptr<Dispatcher> dispatcher0; - scoped_refptr<Dispatcher> dispatcher1; - - bool valid_handles = true; - { - base::AutoLock lock(handles_lock_); - MojoResult result0 = handles_.GetAndRemoveDispatcher(handle0, &dispatcher0); - MojoResult result1 = handles_.GetAndRemoveDispatcher(handle1, &dispatcher1); - if (result0 != MOJO_RESULT_OK || result1 != MOJO_RESULT_OK || - dispatcher0->GetType() != Dispatcher::Type::MESSAGE_PIPE || - dispatcher1->GetType() != Dispatcher::Type::MESSAGE_PIPE) - valid_handles = false; - } - - if (!valid_handles) { - if (dispatcher0) - dispatcher0->Close(); - if (dispatcher1) - dispatcher1->Close(); - return MOJO_RESULT_INVALID_ARGUMENT; - } - - MessagePipeDispatcher* mpd0 = - static_cast<MessagePipeDispatcher*>(dispatcher0.get()); - MessagePipeDispatcher* mpd1 = - static_cast<MessagePipeDispatcher*>(dispatcher1.get()); - - if (!mpd0->Fuse(mpd1)) - return MOJO_RESULT_FAILED_PRECONDITION; - - return MOJO_RESULT_OK; -} - -MojoResult Core::NotifyBadMessage(MojoMessageHandle message, - const char* error, - size_t error_num_bytes) { - if (!message) - return MOJO_RESULT_INVALID_ARGUMENT; - - const PortsMessage& ports_message = - reinterpret_cast<MessageForTransit*>(message)->ports_message(); - if (ports_message.source_node() == ports::kInvalidNodeName) { - DVLOG(1) << "Received invalid message from unknown node."; - if (!default_process_error_callback_.is_null()) - default_process_error_callback_.Run(std::string(error, error_num_bytes)); - return MOJO_RESULT_OK; - } - - GetNodeController()->NotifyBadMessageFrom( - ports_message.source_node(), std::string(error, error_num_bytes)); - return MOJO_RESULT_OK; -} - -MojoResult Core::CreateDataPipe( - const MojoCreateDataPipeOptions* options, - MojoHandle* data_pipe_producer_handle, - MojoHandle* data_pipe_consumer_handle) { - RequestContext request_context; - if (options && options->struct_size != sizeof(MojoCreateDataPipeOptions)) - return MOJO_RESULT_INVALID_ARGUMENT; - - MojoCreateDataPipeOptions create_options; - create_options.struct_size = sizeof(MojoCreateDataPipeOptions); - create_options.flags = options ? options->flags : 0; - create_options.element_num_bytes = options ? options->element_num_bytes : 1; - // TODO(rockot): Use Configuration to get default data pipe capacity. - create_options.capacity_num_bytes = - options && options->capacity_num_bytes ? options->capacity_num_bytes - : 64 * 1024; - - // TODO(rockot): Broker through the parent when necessary. - scoped_refptr<PlatformSharedBuffer> ring_buffer = - GetNodeController()->CreateSharedBuffer( - create_options.capacity_num_bytes); - if (!ring_buffer) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - - ports::PortRef port0, port1; - GetNodeController()->node()->CreatePortPair(&port0, &port1); - - CHECK(data_pipe_producer_handle); - CHECK(data_pipe_consumer_handle); - - uint64_t pipe_id = base::RandUint64(); - - scoped_refptr<Dispatcher> producer = new DataPipeProducerDispatcher( - GetNodeController(), port0, ring_buffer, create_options, - true /* initialized */, pipe_id); - scoped_refptr<Dispatcher> consumer = new DataPipeConsumerDispatcher( - GetNodeController(), port1, ring_buffer, create_options, - true /* initialized */, pipe_id); - - *data_pipe_producer_handle = AddDispatcher(producer); - *data_pipe_consumer_handle = AddDispatcher(consumer); - if (*data_pipe_producer_handle == MOJO_HANDLE_INVALID || - *data_pipe_consumer_handle == MOJO_HANDLE_INVALID) { - if (*data_pipe_producer_handle != MOJO_HANDLE_INVALID) { - scoped_refptr<Dispatcher> unused; - base::AutoLock lock(handles_lock_); - handles_.GetAndRemoveDispatcher(*data_pipe_producer_handle, &unused); - } - producer->Close(); - consumer->Close(); - return MOJO_RESULT_RESOURCE_EXHAUSTED; - } - - return MOJO_RESULT_OK; -} - -MojoResult Core::WriteData(MojoHandle data_pipe_producer_handle, - const void* elements, - uint32_t* num_bytes, - MojoWriteDataFlags flags) { - RequestContext request_context; - 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, - void** buffer, - uint32_t* buffer_num_bytes, - MojoWriteDataFlags flags) { - RequestContext request_context; - 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) { - RequestContext request_context; - 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, - void* elements, - uint32_t* num_bytes, - MojoReadDataFlags flags) { - RequestContext request_context; - 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, - const void** buffer, - uint32_t* buffer_num_bytes, - MojoReadDataFlags flags) { - RequestContext request_context; - 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) { - RequestContext request_context; - 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( - const MojoCreateSharedBufferOptions* options, - uint64_t num_bytes, - MojoHandle* shared_buffer_handle) { - RequestContext request_context; - MojoCreateSharedBufferOptions validated_options = {}; - MojoResult result = SharedBufferDispatcher::ValidateCreateOptions( - options, &validated_options); - if (result != MOJO_RESULT_OK) - return result; - - scoped_refptr<SharedBufferDispatcher> dispatcher; - result = SharedBufferDispatcher::Create( - validated_options, GetNodeController(), num_bytes, &dispatcher); - if (result != MOJO_RESULT_OK) { - DCHECK(!dispatcher); - return result; - } - - *shared_buffer_handle = AddDispatcher(dispatcher); - if (*shared_buffer_handle == MOJO_HANDLE_INVALID) { - LOG(ERROR) << "Handle table full"; - dispatcher->Close(); - return MOJO_RESULT_RESOURCE_EXHAUSTED; - } - - return MOJO_RESULT_OK; -} - -MojoResult Core::DuplicateBufferHandle( - MojoHandle buffer_handle, - const MojoDuplicateBufferHandleOptions* options, - MojoHandle* new_buffer_handle) { - RequestContext request_context; - 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; - - *new_buffer_handle = AddDispatcher(new_dispatcher); - if (*new_buffer_handle == MOJO_HANDLE_INVALID) { - LOG(ERROR) << "Handle table full"; - dispatcher->Close(); - return MOJO_RESULT_RESOURCE_EXHAUSTED; - } - - return MOJO_RESULT_OK; -} - -MojoResult Core::MapBuffer(MojoHandle buffer_handle, - uint64_t offset, - uint64_t num_bytes, - void** buffer, - MojoMapBufferFlags flags) { - RequestContext request_context; - scoped_refptr<Dispatcher> dispatcher(GetDispatcher(buffer_handle)); - if (!dispatcher) - return MOJO_RESULT_INVALID_ARGUMENT; - - std::unique_ptr<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(std::move(mapping)); - } - if (result != MOJO_RESULT_OK) - return result; - - *buffer = address; - return MOJO_RESULT_OK; -} - -MojoResult Core::UnmapBuffer(void* buffer) { - RequestContext request_context; - base::AutoLock lock(mapping_table_lock_); - return mapping_table_.RemoveMapping(buffer); -} - -MojoResult Core::WrapPlatformHandle(const MojoPlatformHandle* platform_handle, - MojoHandle* mojo_handle) { - ScopedPlatformHandle handle; - MojoResult result = MojoPlatformHandleToScopedPlatformHandle(platform_handle, - &handle); - if (result != MOJO_RESULT_OK) - return result; - - return CreatePlatformHandleWrapper(std::move(handle), mojo_handle); -} - -MojoResult Core::UnwrapPlatformHandle(MojoHandle mojo_handle, - MojoPlatformHandle* platform_handle) { - ScopedPlatformHandle handle; - MojoResult result = PassWrappedPlatformHandle(mojo_handle, &handle); - if (result != MOJO_RESULT_OK) - return result; - - return ScopedPlatformHandleToMojoPlatformHandle(std::move(handle), - platform_handle); -} - -MojoResult Core::WrapPlatformSharedBufferHandle( - const MojoPlatformHandle* platform_handle, - size_t size, - MojoPlatformSharedBufferHandleFlags flags, - MojoHandle* mojo_handle) { - DCHECK(size); - ScopedPlatformHandle handle; - MojoResult result = MojoPlatformHandleToScopedPlatformHandle(platform_handle, - &handle); - if (result != MOJO_RESULT_OK) - return result; - - bool read_only = flags & MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_READ_ONLY; - scoped_refptr<PlatformSharedBuffer> platform_buffer = - PlatformSharedBuffer::CreateFromPlatformHandle(size, read_only, - std::move(handle)); - if (!platform_buffer) - return MOJO_RESULT_UNKNOWN; - - scoped_refptr<SharedBufferDispatcher> dispatcher; - result = SharedBufferDispatcher::CreateFromPlatformSharedBuffer( - platform_buffer, &dispatcher); - if (result != MOJO_RESULT_OK) - return result; - - MojoHandle h = AddDispatcher(dispatcher); - if (h == MOJO_HANDLE_INVALID) { - dispatcher->Close(); - return MOJO_RESULT_RESOURCE_EXHAUSTED; - } - - *mojo_handle = h; - return MOJO_RESULT_OK; -} - -MojoResult Core::UnwrapPlatformSharedBufferHandle( - MojoHandle mojo_handle, - MojoPlatformHandle* platform_handle, - size_t* size, - MojoPlatformSharedBufferHandleFlags* flags) { - scoped_refptr<Dispatcher> dispatcher; - MojoResult result = MOJO_RESULT_OK; - { - base::AutoLock lock(handles_lock_); - result = handles_.GetAndRemoveDispatcher(mojo_handle, &dispatcher); - if (result != MOJO_RESULT_OK) - return result; - } - - if (dispatcher->GetType() != Dispatcher::Type::SHARED_BUFFER) { - dispatcher->Close(); - return MOJO_RESULT_INVALID_ARGUMENT; - } - - SharedBufferDispatcher* shm_dispatcher = - static_cast<SharedBufferDispatcher*>(dispatcher.get()); - scoped_refptr<PlatformSharedBuffer> platform_shared_buffer = - shm_dispatcher->PassPlatformSharedBuffer(); - CHECK(platform_shared_buffer); - - CHECK(size); - *size = platform_shared_buffer->GetNumBytes(); - - CHECK(flags); - *flags = MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_NONE; - if (platform_shared_buffer->IsReadOnly()) - *flags |= MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_READ_ONLY; - - ScopedPlatformHandle handle = platform_shared_buffer->PassPlatformHandle(); - return ScopedPlatformHandleToMojoPlatformHandle(std::move(handle), - platform_handle); -} - -void Core::GetActiveHandlesForTest(std::vector<MojoHandle>* handles) { - base::AutoLock lock(handles_lock_); - handles_.GetActiveHandlesForTest(handles); -} - -// static -void Core::PassNodeControllerToIOThread( - std::unique_ptr<NodeController> node_controller) { - // It's OK to leak this reference. At this point we know the IO loop is still - // running, and we know the NodeController will observe its eventual - // destruction. This tells the NodeController to delete itself when that - // happens. - node_controller.release()->DestroyOnIOThreadShutdown(); -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h deleted file mode 100644 index 1f6d865..0000000 --- a/mojo/edk/system/core.h +++ /dev/null @@ -1,297 +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 <memory> -#include <string> -#include <vector> - -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/shared_memory_handle.h" -#include "base/synchronization/lock.h" -#include "base/task_runner.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/handle_signals_state.h" -#include "mojo/edk/system/handle_table.h" -#include "mojo/edk/system/mapping_table.h" -#include "mojo/edk/system/node_controller.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/platform_handle.h" -#include "mojo/public/c/system/types.h" -#include "mojo/public/c/system/watcher.h" -#include "mojo/public/cpp/system/message_pipe.h" - -namespace base { -class PortProvider; -} - -namespace mojo { -namespace edk { - -// |Core| is an object that implements the Mojo system calls. All public methods -// are thread-safe. -class MOJO_SYSTEM_IMPL_EXPORT Core { - public: - Core(); - virtual ~Core(); - - // Called exactly once, shortly after construction, and before any other - // methods are called on this object. - void SetIOTaskRunner(scoped_refptr<base::TaskRunner> io_task_runner); - - // Retrieves the NodeController for the current process. - NodeController* GetNodeController(); - - scoped_refptr<Dispatcher> GetDispatcher(MojoHandle handle); - - void SetDefaultProcessErrorCallback(const ProcessErrorCallback& callback); - - // Called in the parent process any time a new child is launched. - void AddChild(base::ProcessHandle process_handle, - ConnectionParams connection_params, - const std::string& child_token, - const ProcessErrorCallback& process_error_callback); - - // Called in the parent process when a child process fails to launch. - void ChildLaunchFailed(const std::string& child_token); - - // Called to connect to a peer process. This should be called only if there - // is no common ancestor for the processes involved within this mojo system. - // Both processes must call this function, each passing one end of a platform - // channel. This returns one end of a message pipe to each process. - ScopedMessagePipeHandle ConnectToPeerProcess(ScopedPlatformHandle pipe_handle, - const std::string& peer_token); - void ClosePeerConnection(const std::string& peer_token); - - // Called in a child process exactly once during early initialization. - void InitChild(ConnectionParams connection_params); - - // Creates a message pipe endpoint associated with |token|, which a child - // holding the token can later locate and connect to. - ScopedMessagePipeHandle CreateParentMessagePipe( - const std::string& token, const std::string& child_token); - - // Creates a message pipe endpoint and connects it to a pipe the parent has - // associated with |token|. - ScopedMessagePipeHandle CreateChildMessagePipe(const std::string& token); - - // Sets the mach port provider for this process. - void SetMachPortProvider(base::PortProvider* port_provider); - - MojoHandle AddDispatcher(scoped_refptr<Dispatcher> dispatcher); - - // Adds new dispatchers for non-message-pipe handles received in a message. - // |dispatchers| and |handles| should be the same size. - bool AddDispatchersFromTransit( - const std::vector<Dispatcher::DispatcherInTransit>& dispatchers, - MojoHandle* handles); - - // See "mojo/edk/embedder/embedder.h" for more information on these functions. - MojoResult CreatePlatformHandleWrapper(ScopedPlatformHandle platform_handle, - MojoHandle* wrapper_handle); - - MojoResult PassWrappedPlatformHandle(MojoHandle wrapper_handle, - ScopedPlatformHandle* platform_handle); - - MojoResult CreateSharedBufferWrapper( - base::SharedMemoryHandle shared_memory_handle, - size_t num_bytes, - bool read_only, - MojoHandle* mojo_wrapper_handle); - - MojoResult PassSharedMemoryHandle( - MojoHandle mojo_handle, - base::SharedMemoryHandle* shared_memory_handle, - size_t* num_bytes, - bool* read_only); - - // Requests that the EDK tear itself down. |callback| will be called once - // the shutdown process is complete. Note that |callback| is always called - // asynchronously on the calling thread if said thread is running a message - // loop, and the calling thread must continue running a MessageLoop at least - // until the callback is called. If there is no running loop, the |callback| - // may be called from any thread. Beware! - void RequestShutdown(const base::Closure& callback); - - MojoResult SetProperty(MojoPropertyType type, const void* value); - - // --------------------------------------------------------------------------- - - // The following methods are essentially implementations of the Mojo Core - // functions of the Mojo API, with the C interface translated to C++ by - // "mojo/edk/embedder/entrypoints.cc". The best way to understand the contract - // of these methods is to look at the header files defining the corresponding - // API functions, referenced below. - - // These methods correspond to the API functions defined in - // "mojo/public/c/system/functions.h": - MojoTimeTicks GetTimeTicksNow(); - MojoResult Close(MojoHandle handle); - MojoResult QueryHandleSignalsState(MojoHandle handle, - MojoHandleSignalsState* signals_state); - MojoResult CreateWatcher(MojoWatcherCallback callback, - MojoHandle* watcher_handle); - MojoResult Watch(MojoHandle watcher_handle, - MojoHandle handle, - MojoHandleSignals signals, - uintptr_t context); - MojoResult CancelWatch(MojoHandle watcher_handle, uintptr_t context); - MojoResult ArmWatcher(MojoHandle watcher_handle, - uint32_t* num_ready_contexts, - uintptr_t* ready_contexts, - MojoResult* ready_results, - MojoHandleSignalsState* ready_signals_states); - MojoResult AllocMessage(uint32_t num_bytes, - const MojoHandle* handles, - uint32_t num_handles, - MojoAllocMessageFlags flags, - MojoMessageHandle* message); - MojoResult FreeMessage(MojoMessageHandle message); - MojoResult GetMessageBuffer(MojoMessageHandle message, void** buffer); - MojoResult GetProperty(MojoPropertyType type, void* value); - - // These methods correspond to the API functions defined in - // "mojo/public/c/system/message_pipe.h": - MojoResult CreateMessagePipe( - const MojoCreateMessagePipeOptions* options, - MojoHandle* message_pipe_handle0, - MojoHandle* message_pipe_handle1); - MojoResult WriteMessage(MojoHandle message_pipe_handle, - const void* bytes, - uint32_t num_bytes, - const MojoHandle* handles, - uint32_t num_handles, - MojoWriteMessageFlags flags); - MojoResult WriteMessageNew(MojoHandle message_pipe_handle, - MojoMessageHandle message, - MojoWriteMessageFlags flags); - MojoResult ReadMessage(MojoHandle message_pipe_handle, - void* bytes, - uint32_t* num_bytes, - MojoHandle* handles, - uint32_t* num_handles, - MojoReadMessageFlags flags); - MojoResult ReadMessageNew(MojoHandle message_pipe_handle, - MojoMessageHandle* message, - uint32_t* num_bytes, - MojoHandle* handles, - uint32_t* num_handles, - MojoReadMessageFlags flags); - MojoResult FuseMessagePipes(MojoHandle handle0, MojoHandle handle1); - MojoResult NotifyBadMessage(MojoMessageHandle message, - const char* error, - size_t error_num_bytes); - - // These methods correspond to the API functions defined in - // "mojo/public/c/system/data_pipe.h": - MojoResult CreateDataPipe( - const MojoCreateDataPipeOptions* options, - MojoHandle* data_pipe_producer_handle, - MojoHandle* data_pipe_consumer_handle); - MojoResult WriteData(MojoHandle data_pipe_producer_handle, - const void* elements, - uint32_t* num_bytes, - MojoWriteDataFlags flags); - MojoResult BeginWriteData(MojoHandle data_pipe_producer_handle, - void** buffer, - uint32_t* buffer_num_bytes, - MojoWriteDataFlags flags); - MojoResult EndWriteData(MojoHandle data_pipe_producer_handle, - uint32_t num_bytes_written); - MojoResult ReadData(MojoHandle data_pipe_consumer_handle, - void* elements, - uint32_t* num_bytes, - MojoReadDataFlags flags); - MojoResult BeginReadData(MojoHandle data_pipe_consumer_handle, - const void** buffer, - uint32_t* buffer_num_bytes, - MojoReadDataFlags flags); - MojoResult EndReadData(MojoHandle data_pipe_consumer_handle, - uint32_t num_bytes_read); - - // These methods correspond to the API functions defined in - // "mojo/public/c/system/buffer.h": - MojoResult CreateSharedBuffer( - const MojoCreateSharedBufferOptions* options, - uint64_t num_bytes, - MojoHandle* shared_buffer_handle); - MojoResult DuplicateBufferHandle( - MojoHandle buffer_handle, - const MojoDuplicateBufferHandleOptions* options, - MojoHandle* new_buffer_handle); - MojoResult MapBuffer(MojoHandle buffer_handle, - uint64_t offset, - uint64_t num_bytes, - void** buffer, - MojoMapBufferFlags flags); - MojoResult UnmapBuffer(void* buffer); - - // These methods correspond to the API functions defined in - // "mojo/public/c/system/platform_handle.h". - MojoResult WrapPlatformHandle(const MojoPlatformHandle* platform_handle, - MojoHandle* mojo_handle); - MojoResult UnwrapPlatformHandle(MojoHandle mojo_handle, - MojoPlatformHandle* platform_handle); - MojoResult WrapPlatformSharedBufferHandle( - const MojoPlatformHandle* platform_handle, - size_t size, - MojoPlatformSharedBufferHandleFlags flags, - MojoHandle* mojo_handle); - MojoResult UnwrapPlatformSharedBufferHandle( - MojoHandle mojo_handle, - MojoPlatformHandle* platform_handle, - size_t* size, - MojoPlatformSharedBufferHandleFlags* flags); - - void GetActiveHandlesForTest(std::vector<MojoHandle>* handles); - - private: - // Used to pass ownership of our NodeController over to the IO thread in the - // event that we're torn down before said thread. - static void PassNodeControllerToIOThread( - std::unique_ptr<NodeController> node_controller); - - // Guards node_controller_. - // - // TODO(rockot): Consider removing this. It's only needed because we - // initialize node_controller_ lazily and that may happen on any thread. - // Otherwise it's effectively const and shouldn't need to be guarded. - // - // We can get rid of lazy initialization if we defer Mojo initialization far - // enough that zygotes don't do it. The zygote can't create a NodeController. - base::Lock node_controller_lock_; - - // This is lazily initialized on first access. Always use GetNodeController() - // to access it. - std::unique_ptr<NodeController> node_controller_; - - // The default callback to invoke, if any, when a process error is reported - // but cannot be associated with a specific process. - ProcessErrorCallback default_process_error_callback_; - - base::Lock handles_lock_; - HandleTable handles_; - - base::Lock mapping_table_lock_; // Protects |mapping_table_|. - MappingTable mapping_table_; - - base::Lock property_lock_; - // Properties that can be read using the MojoGetProperty() API. - bool property_sync_call_allowed_ = true; - - DISALLOW_COPY_AND_ASSIGN(Core); -}; - -} // namespace edk -} // 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 7751612..0000000 --- a/mojo/edk/system/core_test_base.cc +++ /dev/null @@ -1,272 +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 <stddef.h> -#include <stdint.h> - -#include <vector> - -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "mojo/edk/embedder/embedder_internal.h" -#include "mojo/edk/system/configuration.h" -#include "mojo/edk/system/core.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/message_for_transit.h" - -namespace mojo { -namespace edk { -namespace test { - -namespace { - -// MockDispatcher -------------------------------------------------------------- - -class MockDispatcher : public Dispatcher { - public: - static scoped_refptr<MockDispatcher> Create( - CoreTestBase::MockHandleInfo* info) { - return make_scoped_refptr(new MockDispatcher(info)); - } - - // Dispatcher: - Type GetType() const override { return Type::UNKNOWN; } - - MojoResult Close() override { - info_->IncrementCloseCallCount(); - return MOJO_RESULT_OK; - } - - MojoResult WriteMessage( - std::unique_ptr<MessageForTransit> message, - MojoWriteMessageFlags /*flags*/) override { - info_->IncrementWriteMessageCallCount(); - - if (message->num_bytes() > GetConfiguration().max_message_num_bytes) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - - if (message->num_handles()) - return MOJO_RESULT_UNIMPLEMENTED; - - return MOJO_RESULT_OK; - } - - MojoResult ReadMessage(std::unique_ptr<MessageForTransit>* message, - uint32_t* num_bytes, - MojoHandle* handle, - uint32_t* num_handles, - MojoReadMessageFlags /*flags*/, - bool ignore_num_bytes) override { - info_->IncrementReadMessageCallCount(); - - if (num_handles) - *num_handles = 1; - - return MOJO_RESULT_OK; - } - - MojoResult WriteData(const void* elements, - uint32_t* num_bytes, - MojoWriteDataFlags flags) override { - info_->IncrementWriteDataCallCount(); - return MOJO_RESULT_UNIMPLEMENTED; - } - - MojoResult BeginWriteData(void** buffer, - uint32_t* buffer_num_bytes, - MojoWriteDataFlags flags) override { - info_->IncrementBeginWriteDataCallCount(); - return MOJO_RESULT_UNIMPLEMENTED; - } - - MojoResult EndWriteData(uint32_t num_bytes_written) override { - info_->IncrementEndWriteDataCallCount(); - return MOJO_RESULT_UNIMPLEMENTED; - } - - MojoResult ReadData(void* elements, - uint32_t* num_bytes, - MojoReadDataFlags flags) override { - info_->IncrementReadDataCallCount(); - return MOJO_RESULT_UNIMPLEMENTED; - } - - MojoResult BeginReadData(const void** buffer, - uint32_t* buffer_num_bytes, - MojoReadDataFlags flags) override { - info_->IncrementBeginReadDataCallCount(); - return MOJO_RESULT_UNIMPLEMENTED; - } - - MojoResult EndReadData(uint32_t num_bytes_read) override { - info_->IncrementEndReadDataCallCount(); - return MOJO_RESULT_UNIMPLEMENTED; - } - - private: - explicit MockDispatcher(CoreTestBase::MockHandleInfo* info) : info_(info) { - CHECK(info_); - info_->IncrementCtorCallCount(); - } - - ~MockDispatcher() override { info_->IncrementDtorCallCount(); } - - CoreTestBase::MockHandleInfo* const info_; - - DISALLOW_COPY_AND_ASSIGN(MockDispatcher); -}; - -} // namespace - -// CoreTestBase ---------------------------------------------------------------- - -CoreTestBase::CoreTestBase() { -} - -CoreTestBase::~CoreTestBase() { -} - -MojoHandle CoreTestBase::CreateMockHandle(CoreTestBase::MockHandleInfo* info) { - scoped_refptr<MockDispatcher> dispatcher = MockDispatcher::Create(info); - return core()->AddDispatcher(dispatcher); -} - -Core* CoreTestBase::core() { - return mojo::edk::internal::g_core; -} - -// 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) {} - -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_; -} - -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_++; -} - -} // namespace test -} // namespace edk -} // 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 3d156e3..0000000 --- a/mojo/edk/system/core_test_base.h +++ /dev/null @@ -1,94 +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 <stddef.h> - -#include "base/macros.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/embedder/embedder_internal.h" -#include "mojo/edk/system/test_utils.h" -#include "mojo/public/c/system/types.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace edk { - -class Core; - -namespace test { - -class CoreTestBase_MockHandleInfo; - -class CoreTestBase : public testing::Test { - public: - using MockHandleInfo = CoreTestBase_MockHandleInfo; - - CoreTestBase(); - ~CoreTestBase() override; - - protected: - // |info| must remain alive until the returned handle is closed. - MojoHandle CreateMockHandle(MockHandleInfo* info); - - Core* core(); - - private: - 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; - - // 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(); - - 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_; - - DISALLOW_COPY_AND_ASSIGN(CoreTestBase_MockHandleInfo); -}; - -} // namespace test -} // namespace edk -} // 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 0d60b48..0000000 --- a/mojo/edk/system/core_unittest.cc +++ /dev/null @@ -1,971 +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 "mojo/edk/embedder/embedder_internal.h" -#include "mojo/edk/system/core_test_base.h" -#include "mojo/edk/system/test_utils.h" -#include "mojo/public/cpp/system/wait.h" - -#if defined(OS_WIN) -#include "base/win/windows_version.h" -#endif - -namespace mojo { -namespace edk { -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; - -using CoreTest = test::CoreTestBase; - -TEST_F(CoreTest, GetTimeTicksNow) { - const MojoTimeTicks start = core()->GetTimeTicksNow(); - ASSERT_NE(static_cast<MojoTimeTicks>(0), start) - << "GetTimeTicksNow should return nonzero value"; - test::Sleep(test::DeadlineFromMilliseconds(15)); - const MojoTimeTicks finish = core()->GetTimeTicksNow(); - // Allow for some fuzz in sleep. - ASSERT_GE((finish - start), static_cast<MojoTimeTicks>(8000)) - << "Sleeping should result in increasing time ticks"; -} - -TEST_F(CoreTest, Basic) { - MockHandleInfo info; - - ASSERT_EQ(0u, info.GetCtorCallCount()); - MojoHandle h = CreateMockHandle(&info); - ASSERT_EQ(1u, info.GetCtorCallCount()); - ASSERT_NE(h, MOJO_HANDLE_INVALID); - - ASSERT_EQ(0u, info.GetWriteMessageCallCount()); - ASSERT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h, nullptr, 0, nullptr, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ASSERT_EQ(1u, info.GetWriteMessageCallCount()); - - ASSERT_EQ(0u, info.GetReadMessageCallCount()); - uint32_t num_bytes = 0; - ASSERT_EQ( - MOJO_RESULT_OK, - core()->ReadMessage(h, nullptr, &num_bytes, nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_EQ(1u, info.GetReadMessageCallCount()); - ASSERT_EQ(MOJO_RESULT_OK, - core()->ReadMessage(h, nullptr, nullptr, nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_EQ(2u, info.GetReadMessageCallCount()); - - ASSERT_EQ(0u, info.GetWriteDataCallCount()); - ASSERT_EQ(MOJO_RESULT_UNIMPLEMENTED, - core()->WriteData(h, nullptr, nullptr, MOJO_WRITE_DATA_FLAG_NONE)); - ASSERT_EQ(1u, info.GetWriteDataCallCount()); - - ASSERT_EQ(0u, info.GetBeginWriteDataCallCount()); - ASSERT_EQ(MOJO_RESULT_UNIMPLEMENTED, - core()->BeginWriteData(h, nullptr, nullptr, - MOJO_WRITE_DATA_FLAG_NONE)); - ASSERT_EQ(1u, info.GetBeginWriteDataCallCount()); - - ASSERT_EQ(0u, info.GetEndWriteDataCallCount()); - ASSERT_EQ(MOJO_RESULT_UNIMPLEMENTED, core()->EndWriteData(h, 0)); - ASSERT_EQ(1u, info.GetEndWriteDataCallCount()); - - ASSERT_EQ(0u, info.GetReadDataCallCount()); - ASSERT_EQ(MOJO_RESULT_UNIMPLEMENTED, - core()->ReadData(h, nullptr, nullptr, MOJO_READ_DATA_FLAG_NONE)); - ASSERT_EQ(1u, info.GetReadDataCallCount()); - - ASSERT_EQ(0u, info.GetBeginReadDataCallCount()); - ASSERT_EQ(MOJO_RESULT_UNIMPLEMENTED, - core()->BeginReadData(h, nullptr, nullptr, - MOJO_READ_DATA_FLAG_NONE)); - ASSERT_EQ(1u, info.GetBeginReadDataCallCount()); - - ASSERT_EQ(0u, info.GetEndReadDataCallCount()); - ASSERT_EQ(MOJO_RESULT_UNIMPLEMENTED, core()->EndReadData(h, 0)); - ASSERT_EQ(1u, info.GetEndReadDataCallCount()); - - ASSERT_EQ(0u, info.GetDtorCallCount()); - ASSERT_EQ(0u, info.GetCloseCallCount()); - ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h)); - ASSERT_EQ(1u, info.GetCloseCallCount()); - ASSERT_EQ(1u, info.GetDtorCallCount()); -} - -TEST_F(CoreTest, InvalidArguments) { - // |Close()|: - { - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(MOJO_HANDLE_INVALID)); - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(10)); - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(1000000000)); - - // Test a double-close. - MockHandleInfo info; - MojoHandle h = CreateMockHandle(&info); - ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h)); - ASSERT_EQ(1u, info.GetCloseCallCount()); - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(h)); - ASSERT_EQ(1u, info.GetCloseCallCount()); - } - - // |CreateMessagePipe()|: Nothing to check (apart from things that cause - // death). - - // |WriteMessage()|: - // Only check arguments checked by |Core|, namely |handle|, |handles|, and - // |num_handles|. - { - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WriteMessage(MOJO_HANDLE_INVALID, nullptr, 0, - nullptr, 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. - ASSERT_NE( - MOJO_RESULT_OK, - core()->WriteMessage(h, nullptr, 0, handles, - std::numeric_limits<uint32_t>::max(), - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ASSERT_EQ(0u, info.GetWriteMessageCallCount()); - - // Null |bytes| with non-zero message size. - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WriteMessage(h, nullptr, 1, nullptr, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ASSERT_EQ(0u, info.GetWriteMessageCallCount()); - - // Null |handles| with non-zero handle count. - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WriteMessage(h, nullptr, 0, nullptr, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ASSERT_EQ(0u, info.GetWriteMessageCallCount()); - - // Huge handle count (plausibly big). - ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, - core()->WriteMessage( - h, nullptr, 0, handles, - std::numeric_limits<uint32_t>::max() / sizeof(handles[0]), - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ASSERT_EQ(0u, info.GetWriteMessageCallCount()); - - // Invalid handle in |handles|. - ASSERT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - core()->WriteMessage(h, nullptr, 0, handles, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ASSERT_EQ(0u, info.GetWriteMessageCallCount()); - - // Two invalid handles in |handles|. - ASSERT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - core()->WriteMessage(h, nullptr, 0, handles, 2, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ASSERT_EQ(0u, info.GetWriteMessageCallCount()); - - // Can't send a handle over itself. Note that this will also cause |h| to be - // closed. - handles[0] = h; - ASSERT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - core()->WriteMessage(h, nullptr, 0, handles, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ASSERT_EQ(0u, info.GetWriteMessageCallCount()); - - h = CreateMockHandle(&info); - - MockHandleInfo info2; - - // This is "okay", but |MockDispatcher| doesn't implement it. - handles[0] = CreateMockHandle(&info2); - ASSERT_EQ( - MOJO_RESULT_UNIMPLEMENTED, - core()->WriteMessage(h, nullptr, 0, handles, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ASSERT_EQ(1u, info.GetWriteMessageCallCount()); - - // One of the |handles| is still invalid. - handles[0] = CreateMockHandle(&info2); - ASSERT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - core()->WriteMessage(h, nullptr, 0, handles, 2, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ASSERT_EQ(1u, info.GetWriteMessageCallCount()); - - // One of the |handles| is the same as |h|. Both handles are closed. - handles[0] = CreateMockHandle(&info2); - handles[1] = h; - ASSERT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - core()->WriteMessage(h, nullptr, 0, handles, 2, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ASSERT_EQ(1u, info.GetWriteMessageCallCount()); - - h = CreateMockHandle(&info); - - // Can't send a handle twice in the same message. - handles[0] = CreateMockHandle(&info2); - handles[1] = handles[0]; - ASSERT_EQ( - MOJO_RESULT_BUSY, - core()->WriteMessage(h, nullptr, 0, handles, 2, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ASSERT_EQ(1u, info.GetWriteMessageCallCount()); - - ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h)); - } - - // |ReadMessage()|: - // Only check arguments checked by |Core|, namely |handle|, |handles|, and - // |num_handles|. - { - ASSERT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - core()->ReadMessage(MOJO_HANDLE_INVALID, nullptr, nullptr, nullptr, - nullptr, MOJO_READ_MESSAGE_FLAG_NONE)); - - MockHandleInfo info; - MojoHandle h = CreateMockHandle(&info); - - // Okay. - uint32_t handle_count = 0; - ASSERT_EQ(MOJO_RESULT_OK, - core()->ReadMessage( - h, nullptr, nullptr, nullptr, &handle_count, - MOJO_READ_MESSAGE_FLAG_NONE)); - // Checked by |Core|, shouldn't go through to the dispatcher. - ASSERT_EQ(1u, info.GetReadMessageCallCount()); - - ASSERT_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) { -#if defined(OFFICIAL_BUILD) - const char kMemoryCheckFailedRegex[] = ""; -#else - const char kMemoryCheckFailedRegex[] = "Check failed"; -#endif - - // |CreateMessagePipe()|: - { - MojoHandle h; - ASSERT_DEATH_IF_SUPPORTED( - core()->CreateMessagePipe(nullptr, nullptr, nullptr), - kMemoryCheckFailedRegex); - ASSERT_DEATH_IF_SUPPORTED( - core()->CreateMessagePipe(nullptr, &h, nullptr), - kMemoryCheckFailedRegex); - ASSERT_DEATH_IF_SUPPORTED( - core()->CreateMessagePipe(nullptr, nullptr, &h), - kMemoryCheckFailedRegex); - } - - // |ReadMessage()|: - // Only check arguments checked by |Core|, namely |handle|, |handles|, and - // |num_handles|. - { - MockHandleInfo info; - MojoHandle h = CreateMockHandle(&info); - - uint32_t handle_count = 1; - ASSERT_DEATH_IF_SUPPORTED( - core()->ReadMessage(h, nullptr, nullptr, nullptr, &handle_count, - MOJO_READ_MESSAGE_FLAG_NONE), - kMemoryCheckFailedRegex); - - ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h)); - } -} - -TEST_F(CoreTest, MessagePipe) { - MojoHandle h[2]; - MojoHandleSignalsState hss[2]; - - ASSERT_EQ(MOJO_RESULT_OK, core()->CreateMessagePipe(nullptr, &h[0], &h[1])); - // Should get two distinct, valid handles. - ASSERT_NE(h[0], MOJO_HANDLE_INVALID); - ASSERT_NE(h[1], MOJO_HANDLE_INVALID); - ASSERT_NE(h[0], h[1]); - - // Neither should be readable. - hss[0] = kEmptyMojoHandleSignalsState; - hss[1] = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, core()->QueryHandleSignalsState(h[0], &hss[0])); - EXPECT_EQ(MOJO_RESULT_OK, core()->QueryHandleSignalsState(h[1], &hss[1])); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals); - ASSERT_EQ(kAllSignals, hss[0].satisfiable_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[1].satisfied_signals); - ASSERT_EQ(kAllSignals, hss[1].satisfiable_signals); - - // Try to read anyway. - char buffer[1] = {'a'}; - uint32_t buffer_size = 1; - ASSERT_EQ( - MOJO_RESULT_SHOULD_WAIT, - core()->ReadMessage(h[0], buffer, &buffer_size, nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - // Check that it left its inputs alone. - ASSERT_EQ('a', buffer[0]); - ASSERT_EQ(1u, buffer_size); - - // Write to |h[1]|. - buffer[0] = 'b'; - ASSERT_EQ( - MOJO_RESULT_OK, - core()->WriteMessage(h[1], buffer, 1, nullptr, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Wait for |h[0]| to become readable. - EXPECT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h[0]), - MOJO_HANDLE_SIGNAL_READABLE, &hss[0])); - - // Read from |h[0]|. - // First, get only the size. - buffer_size = 0; - ASSERT_EQ( - MOJO_RESULT_RESOURCE_EXHAUSTED, - core()->ReadMessage(h[0], nullptr, &buffer_size, nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_EQ(1u, buffer_size); - // Then actually read it. - buffer[0] = 'c'; - buffer_size = 1; - ASSERT_EQ( - MOJO_RESULT_OK, - core()->ReadMessage(h[0], buffer, &buffer_size, nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_EQ('b', buffer[0]); - ASSERT_EQ(1u, buffer_size); - - // |h[0]| should no longer be readable. - hss[0] = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, core()->QueryHandleSignalsState(h[0], &hss[0])); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals); - ASSERT_EQ(kAllSignals, hss[0].satisfiable_signals); - - // Write to |h[0]|. - buffer[0] = 'd'; - ASSERT_EQ( - MOJO_RESULT_OK, - core()->WriteMessage(h[0], buffer, 1, nullptr, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Close |h[0]|. - ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h[0])); - - // Wait for |h[1]| to learn about the other end's closure. - EXPECT_EQ( - MOJO_RESULT_OK, - mojo::Wait(mojo::Handle(h[1]), MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss[1])); - - // Check that |h[1]| is no longer writable (and will never be). - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss[1].satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss[1].satisfiable_signals); - - // Check that |h[1]| is still readable (for the moment). - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss[1].satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss[1].satisfiable_signals); - - // Discard a message from |h[1]|. - ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, - core()->ReadMessage(h[1], nullptr, nullptr, nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)); - - // |h[1]| is no longer readable (and will never be). - hss[1] = kFullMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, core()->QueryHandleSignalsState(h[1], &hss[1])); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss[1].satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss[1].satisfiable_signals); - - // Try writing to |h[1]|. - buffer[0] = 'e'; - ASSERT_EQ( - MOJO_RESULT_FAILED_PRECONDITION, - core()->WriteMessage(h[1], buffer, 1, nullptr, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - ASSERT_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]; - ASSERT_EQ(MOJO_RESULT_OK, - core()->CreateMessagePipe(nullptr, &h_passing[0], &h_passing[1])); - - // Make sure that |h_passing[]| work properly. - ASSERT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h_passing[0], kHello, kHelloSize, nullptr, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h_passing[1]), - MOJO_HANDLE_SIGNAL_READABLE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - ASSERT_EQ(kAllSignals, hss.satisfiable_signals); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - ASSERT_EQ(MOJO_RESULT_OK, - core()->ReadMessage( - h_passing[1], buffer, &num_bytes, handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_EQ(kHelloSize, num_bytes); - ASSERT_STREQ(kHello, buffer); - ASSERT_EQ(0u, num_handles); - - // Make sure that you can't pass either of the message pipe's handles over - // itself. - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WriteMessage(h_passing[0], kHello, kHelloSize, - &h_passing[0], 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ASSERT_EQ(MOJO_RESULT_OK, - core()->CreateMessagePipe(nullptr, &h_passing[0], &h_passing[1])); - - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->WriteMessage(h_passing[0], kHello, kHelloSize, - &h_passing[1], 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ASSERT_EQ(MOJO_RESULT_OK, - core()->CreateMessagePipe(nullptr, &h_passing[0], &h_passing[1])); - - MojoHandle h_passed[2]; - ASSERT_EQ(MOJO_RESULT_OK, - core()->CreateMessagePipe(nullptr, &h_passed[0], &h_passed[1])); - - // Make sure that |h_passed[]| work properly. - ASSERT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h_passed[0], kHello, kHelloSize, nullptr, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - hss = kEmptyMojoHandleSignalsState; - ASSERT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h_passed[1]), - MOJO_HANDLE_SIGNAL_READABLE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - ASSERT_EQ(kAllSignals, hss.satisfiable_signals); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - ASSERT_EQ(MOJO_RESULT_OK, - core()->ReadMessage( - h_passed[1], buffer, &num_bytes, handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_EQ(kHelloSize, num_bytes); - ASSERT_STREQ(kHello, buffer); - ASSERT_EQ(0u, num_handles); - - // Send |h_passed[1]| from |h_passing[0]| to |h_passing[1]|. - ASSERT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h_passing[0], kWorld, kWorldSize, - &h_passed[1], 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - hss = kEmptyMojoHandleSignalsState; - ASSERT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h_passing[1]), - MOJO_HANDLE_SIGNAL_READABLE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - ASSERT_EQ(kAllSignals, hss.satisfiable_signals); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - ASSERT_EQ(MOJO_RESULT_OK, - core()->ReadMessage( - h_passing[1], buffer, &num_bytes, handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_EQ(kWorldSize, num_bytes); - ASSERT_STREQ(kWorld, buffer); - ASSERT_EQ(1u, num_handles); - h_received = handles[0]; - ASSERT_NE(h_received, MOJO_HANDLE_INVALID); - ASSERT_NE(h_received, h_passing[0]); - ASSERT_NE(h_received, h_passing[1]); - ASSERT_NE(h_received, h_passed[0]); - - // Note: We rely on the Mojo system not re-using handle values very often. - ASSERT_NE(h_received, h_passed[1]); - - // |h_passed[1]| should no longer be valid; check that trying to close it - // fails. See above note. - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(h_passed[1])); - - // Write to |h_passed[0]|. Should receive on |h_received|. - ASSERT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h_passed[0], kHello, kHelloSize, nullptr, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - hss = kEmptyMojoHandleSignalsState; - ASSERT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h_received), - MOJO_HANDLE_SIGNAL_READABLE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - ASSERT_EQ(kAllSignals, hss.satisfiable_signals); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - ASSERT_EQ(MOJO_RESULT_OK, - core()->ReadMessage( - h_received, buffer, &num_bytes, handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_EQ(kHelloSize, num_bytes); - ASSERT_STREQ(kHello, buffer); - ASSERT_EQ(0u, num_handles); - - ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[0])); - ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[1])); - ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h_passed[0])); - ASSERT_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; - - ASSERT_EQ(MOJO_RESULT_OK, - core()->CreateDataPipe(nullptr, &ph, &ch)); - // Should get two distinct, valid handles. - ASSERT_NE(ph, MOJO_HANDLE_INVALID); - ASSERT_NE(ch, MOJO_HANDLE_INVALID); - ASSERT_NE(ph, ch); - - // Producer should be never-readable, but already writable. - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, core()->QueryHandleSignalsState(ph, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - ASSERT_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_OK, core()->QueryHandleSignalsState(ch, &hss)); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // Write. - signed char elements[2] = {'A', 'B'}; - uint32_t num_bytes = 2u; - ASSERT_EQ(MOJO_RESULT_OK, - core()->WriteData(ph, elements, &num_bytes, - MOJO_WRITE_DATA_FLAG_NONE)); - ASSERT_EQ(2u, num_bytes); - - // Wait for the data to arrive to the consumer. - EXPECT_EQ(MOJO_RESULT_OK, - mojo::Wait(mojo::Handle(ch), MOJO_HANDLE_SIGNAL_READABLE, &hss)); - - // Consumer should now be readable. - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, core()->QueryHandleSignalsState(ch, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // Peek one character. - elements[0] = -1; - elements[1] = -1; - num_bytes = 1u; - ASSERT_EQ(MOJO_RESULT_OK, - core()->ReadData( - ch, elements, &num_bytes, - MOJO_READ_DATA_FLAG_NONE | MOJO_READ_DATA_FLAG_PEEK)); - ASSERT_EQ('A', elements[0]); - ASSERT_EQ(-1, elements[1]); - - // Read one character. - elements[0] = -1; - elements[1] = -1; - num_bytes = 1u; - ASSERT_EQ(MOJO_RESULT_OK, core()->ReadData(ch, elements, &num_bytes, - MOJO_READ_DATA_FLAG_NONE)); - ASSERT_EQ('A', elements[0]); - ASSERT_EQ(-1, elements[1]); - - // Two-phase write. - void* write_ptr = nullptr; - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, - core()->BeginWriteData(ph, &write_ptr, &num_bytes, - MOJO_WRITE_DATA_FLAG_NONE)); - // We count on the default options providing a decent buffer size. - ASSERT_GE(num_bytes, 3u); - - // Trying to do a normal write during a two-phase write should fail. - elements[0] = 'X'; - num_bytes = 1u; - ASSERT_EQ(MOJO_RESULT_BUSY, - core()->WriteData(ph, elements, &num_bytes, - MOJO_WRITE_DATA_FLAG_NONE)); - - // Actually write the data, and complete it now. - static_cast<char*>(write_ptr)[0] = 'C'; - static_cast<char*>(write_ptr)[1] = 'D'; - static_cast<char*>(write_ptr)[2] = 'E'; - ASSERT_EQ(MOJO_RESULT_OK, core()->EndWriteData(ph, 3u)); - - // Wait for the data to arrive to the consumer. - ASSERT_EQ(MOJO_RESULT_OK, - mojo::Wait(mojo::Handle(ch), MOJO_HANDLE_SIGNAL_READABLE, &hss)); - - // Query how much data we have. - num_bytes = 0; - ASSERT_EQ(MOJO_RESULT_OK, - core()->ReadData(ch, nullptr, &num_bytes, - MOJO_READ_DATA_FLAG_QUERY)); - ASSERT_GE(num_bytes, 1u); - - // Try to query with peek. Should fail. - num_bytes = 0; - ASSERT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - core()->ReadData(ch, nullptr, &num_bytes, - MOJO_READ_DATA_FLAG_QUERY | MOJO_READ_DATA_FLAG_PEEK)); - ASSERT_EQ(0u, num_bytes); - - // Try to discard ten characters, in all-or-none mode. Should fail. - num_bytes = 10; - ASSERT_EQ(MOJO_RESULT_OUT_OF_RANGE, - core()->ReadData( - ch, nullptr, &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; - ASSERT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - core()->ReadData(ch, nullptr, &num_bytes, - MOJO_READ_DATA_FLAG_DISCARD | MOJO_READ_DATA_FLAG_PEEK)); - - // Discard a character. - num_bytes = 1; - ASSERT_EQ(MOJO_RESULT_OK, - core()->ReadData( - ch, nullptr, &num_bytes, - MOJO_READ_DATA_FLAG_DISCARD | MOJO_READ_DATA_FLAG_ALL_OR_NONE)); - - // Ensure the 3 bytes were read. - ASSERT_EQ(MOJO_RESULT_OK, - mojo::Wait(mojo::Handle(ch), MOJO_HANDLE_SIGNAL_READABLE, &hss)); - - // Try a two-phase read of the remaining three bytes with peek. Should fail. - const void* read_ptr = nullptr; - num_bytes = 3; - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - core()->BeginReadData(ch, &read_ptr, &num_bytes, - MOJO_READ_DATA_FLAG_PEEK)); - - // Read the remaining two characters, in two-phase mode (all-or-none). - num_bytes = 3; - ASSERT_EQ(MOJO_RESULT_OK, - core()->BeginReadData(ch, &read_ptr, &num_bytes, - MOJO_READ_DATA_FLAG_ALL_OR_NONE)); - // Note: Count on still being able to do the contiguous read here. - ASSERT_EQ(3u, num_bytes); - - // Discarding right now should fail. - num_bytes = 1; - ASSERT_EQ(MOJO_RESULT_BUSY, - core()->ReadData(ch, nullptr, &num_bytes, - MOJO_READ_DATA_FLAG_DISCARD)); - - // Actually check our data and end the two-phase read. - ASSERT_EQ('C', static_cast<const char*>(read_ptr)[0]); - ASSERT_EQ('D', static_cast<const char*>(read_ptr)[1]); - ASSERT_EQ('E', static_cast<const char*>(read_ptr)[2]); - ASSERT_EQ(MOJO_RESULT_OK, core()->EndReadData(ch, 3u)); - - // Consumer should now be no longer readable. - hss = kFullMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, core()->QueryHandleSignalsState(ch, &hss)); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // TODO(vtl): More. - - // Close the producer. - ASSERT_EQ(MOJO_RESULT_OK, core()->Close(ph)); - - // Wait for this to get to the consumer. - EXPECT_EQ(MOJO_RESULT_OK, - mojo::Wait(mojo::Handle(ch), MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss)); - - // The consumer should now be never-readable. - hss = kFullMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, core()->QueryHandleSignalsState(ch, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - ASSERT_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]; - ASSERT_EQ(MOJO_RESULT_OK, - core()->CreateMessagePipe(nullptr, &h_passing[0], &h_passing[1])); - - MojoHandle ph, ch; - ASSERT_EQ(MOJO_RESULT_OK, - core()->CreateDataPipe(nullptr, &ph, &ch)); - - // Send |ch| from |h_passing[0]| to |h_passing[1]|. - ASSERT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h_passing[0], kHello, kHelloSize, &ch, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - hss = kEmptyMojoHandleSignalsState; - ASSERT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h_passing[1]), - MOJO_HANDLE_SIGNAL_READABLE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - ASSERT_EQ(kAllSignals, hss.satisfiable_signals); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - ASSERT_EQ(MOJO_RESULT_OK, - core()->ReadMessage( - h_passing[1], buffer, &num_bytes, handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_EQ(kHelloSize, num_bytes); - ASSERT_STREQ(kHello, buffer); - ASSERT_EQ(1u, num_handles); - MojoHandle ch_received = handles[0]; - ASSERT_NE(ch_received, MOJO_HANDLE_INVALID); - ASSERT_NE(ch_received, h_passing[0]); - ASSERT_NE(ch_received, h_passing[1]); - ASSERT_NE(ch_received, ph); - - // Note: We rely on the Mojo system not re-using handle values very often. - ASSERT_NE(ch_received, ch); - - // |ch| should no longer be valid; check that trying to close it fails. See - // above note. - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(ch)); - - // Write to |ph|. Should receive on |ch_received|. - num_bytes = kWorldSize; - ASSERT_EQ(MOJO_RESULT_OK, - core()->WriteData(ph, kWorld, &num_bytes, - MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)); - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(ch_received), - MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - num_bytes = kBufferSize; - ASSERT_EQ(MOJO_RESULT_OK, - core()->ReadData(ch_received, buffer, &num_bytes, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_EQ(kWorldSize, num_bytes); - ASSERT_STREQ(kWorld, buffer); - - // Now pass |ph| in the same direction. - ASSERT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h_passing[0], kWorld, kWorldSize, &ph, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - hss = kEmptyMojoHandleSignalsState; - ASSERT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h_passing[1]), - MOJO_HANDLE_SIGNAL_READABLE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - ASSERT_EQ(kAllSignals, hss.satisfiable_signals); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - ASSERT_EQ(MOJO_RESULT_OK, - core()->ReadMessage( - h_passing[1], buffer, &num_bytes, handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_EQ(kWorldSize, num_bytes); - ASSERT_STREQ(kWorld, buffer); - ASSERT_EQ(1u, num_handles); - MojoHandle ph_received = handles[0]; - ASSERT_NE(ph_received, MOJO_HANDLE_INVALID); - ASSERT_NE(ph_received, h_passing[0]); - ASSERT_NE(ph_received, h_passing[1]); - ASSERT_NE(ph_received, ch_received); - - // Again, rely on the Mojo system not re-using handle values very often. - ASSERT_NE(ph_received, ph); - - // |ph| should no longer be valid; check that trying to close it fails. See - // above note. - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(ph)); - - // Write to |ph_received|. Should receive on |ch_received|. - num_bytes = kHelloSize; - ASSERT_EQ(MOJO_RESULT_OK, - core()->WriteData(ph_received, kHello, &num_bytes, - MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)); - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(ch_received), - MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - num_bytes = kBufferSize; - ASSERT_EQ(MOJO_RESULT_OK, - core()->ReadData(ch_received, buffer, &num_bytes, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_EQ(kHelloSize, num_bytes); - ASSERT_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, &write_ptr, &num_bytes, - MOJO_WRITE_DATA_FLAG_NONE)); - ASSERT_GE(num_bytes, 1u); - ASSERT_EQ(MOJO_RESULT_BUSY, - core()->WriteMessage(h_passing[0], kHello, kHelloSize, &ph, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // But |ch| can, even if |ph| is in a two-phase write. - ASSERT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h_passing[0], kHello, kHelloSize, &ch, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ch = MOJO_HANDLE_INVALID; - EXPECT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h_passing[1]), - MOJO_HANDLE_SIGNAL_READABLE)); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - ASSERT_EQ(MOJO_RESULT_OK, - core()->ReadMessage( - h_passing[1], buffer, &num_bytes, handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_EQ(kHelloSize, num_bytes); - ASSERT_STREQ(kHello, buffer); - ASSERT_EQ(1u, num_handles); - ch = handles[0]; - ASSERT_NE(ch, MOJO_HANDLE_INVALID); - - // Complete the two-phase write. - static_cast<char*>(write_ptr)[0] = 'x'; - ASSERT_EQ(MOJO_RESULT_OK, core()->EndWriteData(ph, 1)); - - // Wait for |ch| to be readable. - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, - mojo::Wait(mojo::Handle(ch), MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - 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, &read_ptr, &num_bytes, - MOJO_READ_DATA_FLAG_ALL_OR_NONE)); - ASSERT_EQ(MOJO_RESULT_BUSY, - core()->WriteMessage(h_passing[0], kHello, kHelloSize, &ch, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // But |ph| can, even if |ch| is in a two-phase read. - ASSERT_EQ(MOJO_RESULT_OK, - core()->WriteMessage(h_passing[0], kWorld, kWorldSize, &ph, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ph = MOJO_HANDLE_INVALID; - hss = kEmptyMojoHandleSignalsState; - EXPECT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h_passing[1]), - MOJO_HANDLE_SIGNAL_READABLE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - ASSERT_EQ(kAllSignals, hss.satisfiable_signals); - num_bytes = kBufferSize; - num_handles = arraysize(handles); - ASSERT_EQ(MOJO_RESULT_OK, - core()->ReadMessage( - h_passing[1], buffer, &num_bytes, handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_EQ(kWorldSize, num_bytes); - ASSERT_STREQ(kWorld, buffer); - ASSERT_EQ(1u, num_handles); - ph = handles[0]; - ASSERT_NE(ph, MOJO_HANDLE_INVALID); - - // Complete the two-phase read. - ASSERT_EQ('x', static_cast<const char*>(read_ptr)[0]); - ASSERT_EQ(MOJO_RESULT_OK, core()->EndReadData(ch, 1)); - - ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[0])); - ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[1])); - ASSERT_EQ(MOJO_RESULT_OK, core()->Close(ph)); - ASSERT_EQ(MOJO_RESULT_OK, core()->Close(ch)); -} - -struct TestAsyncWaiter { - TestAsyncWaiter() : result(MOJO_RESULT_UNKNOWN) {} - - void Awake(MojoResult r) { result = r; } - - MojoResult result; -}; - -// TODO(vtl): Test |DuplicateBufferHandle()| and |MapBuffer()|. - -} // namespace -} // namespace edk -} // namespace mojo 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 f338732..0000000 --- a/mojo/edk/system/data_pipe_consumer_dispatcher.cc +++ /dev/null @@ -1,562 +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 <stddef.h> -#include <stdint.h> - -#include <algorithm> -#include <limits> -#include <utility> - -#include "base/bind.h" -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" -#include "mojo/edk/embedder/embedder_internal.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/system/core.h" -#include "mojo/edk/system/data_pipe_control_message.h" -#include "mojo/edk/system/node_controller.h" -#include "mojo/edk/system/ports_message.h" -#include "mojo/edk/system/request_context.h" -#include "mojo/public/c/system/data_pipe.h" - -namespace mojo { -namespace edk { - -namespace { - -const uint8_t kFlagPeerClosed = 0x01; - -#pragma pack(push, 1) - -struct SerializedState { - MojoCreateDataPipeOptions options; - uint64_t pipe_id; - uint32_t read_offset; - uint32_t bytes_available; - uint8_t flags; - char padding[7]; -}; - -static_assert(sizeof(SerializedState) % 8 == 0, - "Invalid SerializedState size."); - -#pragma pack(pop) - -} // namespace - -// A PortObserver which forwards to a DataPipeConsumerDispatcher. This owns a -// reference to the dispatcher to ensure it lives as long as the observed port. -class DataPipeConsumerDispatcher::PortObserverThunk - : public NodeController::PortObserver { - public: - explicit PortObserverThunk( - scoped_refptr<DataPipeConsumerDispatcher> dispatcher) - : dispatcher_(dispatcher) {} - - private: - ~PortObserverThunk() override {} - - // NodeController::PortObserver: - void OnPortStatusChanged() override { dispatcher_->OnPortStatusChanged(); } - - scoped_refptr<DataPipeConsumerDispatcher> dispatcher_; - - DISALLOW_COPY_AND_ASSIGN(PortObserverThunk); -}; - -DataPipeConsumerDispatcher::DataPipeConsumerDispatcher( - NodeController* node_controller, - const ports::PortRef& control_port, - scoped_refptr<PlatformSharedBuffer> shared_ring_buffer, - const MojoCreateDataPipeOptions& options, - bool initialized, - uint64_t pipe_id) - : options_(options), - node_controller_(node_controller), - control_port_(control_port), - pipe_id_(pipe_id), - watchers_(this), - shared_ring_buffer_(shared_ring_buffer) { - if (initialized) { - base::AutoLock lock(lock_); - InitializeNoLock(); - } -} - -Dispatcher::Type DataPipeConsumerDispatcher::GetType() const { - return Type::DATA_PIPE_CONSUMER; -} - -MojoResult DataPipeConsumerDispatcher::Close() { - base::AutoLock lock(lock_); - DVLOG(1) << "Closing data pipe consumer " << pipe_id_; - return CloseNoLock(); -} - -MojoResult DataPipeConsumerDispatcher::ReadData(void* elements, - uint32_t* num_bytes, - MojoReadDataFlags flags) { - base::AutoLock lock(lock_); - - if (!shared_ring_buffer_ || in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (in_two_phase_read_) - return MOJO_RESULT_BUSY; - - const bool had_new_data = new_data_available_; - new_data_available_ = false; - - if ((flags & MOJO_READ_DATA_FLAG_QUERY)) { - if ((flags & MOJO_READ_DATA_FLAG_PEEK) || - (flags & MOJO_READ_DATA_FLAG_DISCARD)) - return MOJO_RESULT_INVALID_ARGUMENT; - DCHECK(!(flags & MOJO_READ_DATA_FLAG_DISCARD)); // Handled above. - DVLOG_IF(2, elements) - << "Query mode: ignoring non-null |elements|"; - *num_bytes = static_cast<uint32_t>(bytes_available_); - - if (had_new_data) - watchers_.NotifyState(GetHandleSignalsStateNoLock()); - return MOJO_RESULT_OK; - } - - bool discard = false; - if ((flags & MOJO_READ_DATA_FLAG_DISCARD)) { - // These flags are mutally exclusive. - if (flags & MOJO_READ_DATA_FLAG_PEEK) - return MOJO_RESULT_INVALID_ARGUMENT; - DVLOG_IF(2, elements) - << "Discard mode: ignoring non-null |elements|"; - discard = true; - } - - uint32_t max_num_bytes_to_read = *num_bytes; - if (max_num_bytes_to_read % options_.element_num_bytes != 0) - return MOJO_RESULT_INVALID_ARGUMENT; - - bool all_or_none = flags & MOJO_READ_DATA_FLAG_ALL_OR_NONE; - uint32_t min_num_bytes_to_read = - all_or_none ? max_num_bytes_to_read : 0; - - if (min_num_bytes_to_read > bytes_available_) { - if (had_new_data) - watchers_.NotifyState(GetHandleSignalsStateNoLock()); - return peer_closed_ ? MOJO_RESULT_FAILED_PRECONDITION - : MOJO_RESULT_OUT_OF_RANGE; - } - - uint32_t bytes_to_read = std::min(max_num_bytes_to_read, bytes_available_); - if (bytes_to_read == 0) { - if (had_new_data) - watchers_.NotifyState(GetHandleSignalsStateNoLock()); - return peer_closed_ ? MOJO_RESULT_FAILED_PRECONDITION - : MOJO_RESULT_SHOULD_WAIT; - } - - if (!discard) { - uint8_t* data = static_cast<uint8_t*>(ring_buffer_mapping_->GetBase()); - CHECK(data); - - uint8_t* destination = static_cast<uint8_t*>(elements); - CHECK(destination); - - DCHECK_LE(read_offset_, options_.capacity_num_bytes); - uint32_t tail_bytes_to_copy = - std::min(options_.capacity_num_bytes - read_offset_, bytes_to_read); - uint32_t head_bytes_to_copy = bytes_to_read - tail_bytes_to_copy; - if (tail_bytes_to_copy > 0) - memcpy(destination, data + read_offset_, tail_bytes_to_copy); - if (head_bytes_to_copy > 0) - memcpy(destination + tail_bytes_to_copy, data, head_bytes_to_copy); - } - *num_bytes = bytes_to_read; - - bool peek = !!(flags & MOJO_READ_DATA_FLAG_PEEK); - if (discard || !peek) { - read_offset_ = (read_offset_ + bytes_to_read) % options_.capacity_num_bytes; - bytes_available_ -= bytes_to_read; - - base::AutoUnlock unlock(lock_); - NotifyRead(bytes_to_read); - } - - // We may have just read the last available data and thus changed the signals - // state. - watchers_.NotifyState(GetHandleSignalsStateNoLock()); - - return MOJO_RESULT_OK; -} - -MojoResult DataPipeConsumerDispatcher::BeginReadData(const void** buffer, - uint32_t* buffer_num_bytes, - MojoReadDataFlags flags) { - base::AutoLock lock(lock_); - if (!shared_ring_buffer_ || in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (in_two_phase_read_) - return MOJO_RESULT_BUSY; - - // 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; - - const bool had_new_data = new_data_available_; - new_data_available_ = false; - - if (bytes_available_ == 0) { - if (had_new_data) - watchers_.NotifyState(GetHandleSignalsStateNoLock()); - return peer_closed_ ? MOJO_RESULT_FAILED_PRECONDITION - : MOJO_RESULT_SHOULD_WAIT; - } - - DCHECK_LT(read_offset_, options_.capacity_num_bytes); - uint32_t bytes_to_read = std::min(bytes_available_, - options_.capacity_num_bytes - read_offset_); - - CHECK(ring_buffer_mapping_); - uint8_t* data = static_cast<uint8_t*>(ring_buffer_mapping_->GetBase()); - CHECK(data); - - in_two_phase_read_ = true; - *buffer = data + read_offset_; - *buffer_num_bytes = bytes_to_read; - two_phase_max_bytes_read_ = bytes_to_read; - - if (had_new_data) - watchers_.NotifyState(GetHandleSignalsStateNoLock()); - - return MOJO_RESULT_OK; -} - -MojoResult DataPipeConsumerDispatcher::EndReadData(uint32_t num_bytes_read) { - base::AutoLock lock(lock_); - if (!in_two_phase_read_) - return MOJO_RESULT_FAILED_PRECONDITION; - - if (in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - - CHECK(shared_ring_buffer_); - - MojoResult rv; - if (num_bytes_read > two_phase_max_bytes_read_ || - num_bytes_read % options_.element_num_bytes != 0) { - rv = MOJO_RESULT_INVALID_ARGUMENT; - } else { - rv = MOJO_RESULT_OK; - read_offset_ = - (read_offset_ + num_bytes_read) % options_.capacity_num_bytes; - - DCHECK_GE(bytes_available_, num_bytes_read); - bytes_available_ -= num_bytes_read; - - base::AutoUnlock unlock(lock_); - NotifyRead(num_bytes_read); - } - - in_two_phase_read_ = false; - two_phase_max_bytes_read_ = 0; - - watchers_.NotifyState(GetHandleSignalsStateNoLock()); - - return rv; -} - -HandleSignalsState DataPipeConsumerDispatcher::GetHandleSignalsState() const { - base::AutoLock lock(lock_); - return GetHandleSignalsStateNoLock(); -} - -MojoResult DataPipeConsumerDispatcher::AddWatcherRef( - const scoped_refptr<WatcherDispatcher>& watcher, - uintptr_t context) { - base::AutoLock lock(lock_); - if (is_closed_ || in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - return watchers_.Add(watcher, context, GetHandleSignalsStateNoLock()); -} - -MojoResult DataPipeConsumerDispatcher::RemoveWatcherRef( - WatcherDispatcher* watcher, - uintptr_t context) { - base::AutoLock lock(lock_); - if (is_closed_ || in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - return watchers_.Remove(watcher, context); -} - -void DataPipeConsumerDispatcher::StartSerialize(uint32_t* num_bytes, - uint32_t* num_ports, - uint32_t* num_handles) { - base::AutoLock lock(lock_); - DCHECK(in_transit_); - *num_bytes = static_cast<uint32_t>(sizeof(SerializedState)); - *num_ports = 1; - *num_handles = 1; -} - -bool DataPipeConsumerDispatcher::EndSerialize( - void* destination, - ports::PortName* ports, - PlatformHandle* platform_handles) { - SerializedState* state = static_cast<SerializedState*>(destination); - memcpy(&state->options, &options_, sizeof(MojoCreateDataPipeOptions)); - memset(state->padding, 0, sizeof(state->padding)); - - base::AutoLock lock(lock_); - DCHECK(in_transit_); - state->pipe_id = pipe_id_; - state->read_offset = read_offset_; - state->bytes_available = bytes_available_; - state->flags = peer_closed_ ? kFlagPeerClosed : 0; - - ports[0] = control_port_.name(); - - buffer_handle_for_transit_ = shared_ring_buffer_->DuplicatePlatformHandle(); - platform_handles[0] = buffer_handle_for_transit_.get(); - - return true; -} - -bool DataPipeConsumerDispatcher::BeginTransit() { - base::AutoLock lock(lock_); - if (in_transit_) - return false; - in_transit_ = !in_two_phase_read_; - return in_transit_; -} - -void DataPipeConsumerDispatcher::CompleteTransitAndClose() { - node_controller_->SetPortObserver(control_port_, nullptr); - - base::AutoLock lock(lock_); - DCHECK(in_transit_); - in_transit_ = false; - transferred_ = true; - ignore_result(buffer_handle_for_transit_.release()); - CloseNoLock(); -} - -void DataPipeConsumerDispatcher::CancelTransit() { - base::AutoLock lock(lock_); - DCHECK(in_transit_); - in_transit_ = false; - buffer_handle_for_transit_.reset(); - UpdateSignalsStateNoLock(); -} - -// static -scoped_refptr<DataPipeConsumerDispatcher> -DataPipeConsumerDispatcher::Deserialize(const void* data, - size_t num_bytes, - const ports::PortName* ports, - size_t num_ports, - PlatformHandle* handles, - size_t num_handles) { - if (num_ports != 1 || num_handles != 1 || - num_bytes != sizeof(SerializedState)) { - return nullptr; - } - - const SerializedState* state = static_cast<const SerializedState*>(data); - - NodeController* node_controller = internal::g_core->GetNodeController(); - ports::PortRef port; - if (node_controller->node()->GetPort(ports[0], &port) != ports::OK) - return nullptr; - - PlatformHandle buffer_handle; - std::swap(buffer_handle, handles[0]); - scoped_refptr<PlatformSharedBuffer> ring_buffer = - PlatformSharedBuffer::CreateFromPlatformHandle( - state->options.capacity_num_bytes, - false /* read_only */, - ScopedPlatformHandle(buffer_handle)); - if (!ring_buffer) { - DLOG(ERROR) << "Failed to deserialize shared buffer handle."; - return nullptr; - } - - scoped_refptr<DataPipeConsumerDispatcher> dispatcher = - new DataPipeConsumerDispatcher(node_controller, port, ring_buffer, - state->options, false /* initialized */, - state->pipe_id); - - { - base::AutoLock lock(dispatcher->lock_); - dispatcher->read_offset_ = state->read_offset; - dispatcher->bytes_available_ = state->bytes_available; - dispatcher->new_data_available_ = state->bytes_available > 0; - dispatcher->peer_closed_ = state->flags & kFlagPeerClosed; - dispatcher->InitializeNoLock(); - dispatcher->UpdateSignalsStateNoLock(); - } - - return dispatcher; -} - -DataPipeConsumerDispatcher::~DataPipeConsumerDispatcher() { - DCHECK(is_closed_ && !shared_ring_buffer_ && !ring_buffer_mapping_ && - !in_transit_); -} - -void DataPipeConsumerDispatcher::InitializeNoLock() { - lock_.AssertAcquired(); - - if (shared_ring_buffer_) { - DCHECK(!ring_buffer_mapping_); - ring_buffer_mapping_ = - shared_ring_buffer_->Map(0, options_.capacity_num_bytes); - if (!ring_buffer_mapping_) { - DLOG(ERROR) << "Failed to map shared buffer."; - shared_ring_buffer_ = nullptr; - } - } - - base::AutoUnlock unlock(lock_); - node_controller_->SetPortObserver( - control_port_, - make_scoped_refptr(new PortObserverThunk(this))); -} - -MojoResult DataPipeConsumerDispatcher::CloseNoLock() { - lock_.AssertAcquired(); - if (is_closed_ || in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - is_closed_ = true; - ring_buffer_mapping_.reset(); - shared_ring_buffer_ = nullptr; - - watchers_.NotifyClosed(); - if (!transferred_) { - base::AutoUnlock unlock(lock_); - node_controller_->ClosePort(control_port_); - } - - return MOJO_RESULT_OK; -} - -HandleSignalsState -DataPipeConsumerDispatcher::GetHandleSignalsStateNoLock() const { - lock_.AssertAcquired(); - - HandleSignalsState rv; - if (shared_ring_buffer_ && bytes_available_) { - if (!in_two_phase_read_) { - rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_READABLE; - if (new_data_available_) - rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE; - } - rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE; - } else if (!peer_closed_ && shared_ring_buffer_) { - rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE; - } - - if (shared_ring_buffer_) { - if (new_data_available_ || !peer_closed_) - rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE; - } - - if (peer_closed_) - rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED; - rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED; - - return rv; -} - -void DataPipeConsumerDispatcher::NotifyRead(uint32_t num_bytes) { - DVLOG(1) << "Data pipe consumer " << pipe_id_ << " notifying peer: " - << num_bytes << " bytes read. [control_port=" - << control_port_.name() << "]"; - - SendDataPipeControlMessage(node_controller_, control_port_, - DataPipeCommand::DATA_WAS_READ, num_bytes); -} - -void DataPipeConsumerDispatcher::OnPortStatusChanged() { - DCHECK(RequestContext::current()); - - base::AutoLock lock(lock_); - - // We stop observing the control port as soon it's transferred, but this can - // race with events which are raised right before that happens. This is fine - // to ignore. - if (transferred_) - return; - - DVLOG(1) << "Control port status changed for data pipe producer " << pipe_id_; - - UpdateSignalsStateNoLock(); -} - -void DataPipeConsumerDispatcher::UpdateSignalsStateNoLock() { - lock_.AssertAcquired(); - - bool was_peer_closed = peer_closed_; - size_t previous_bytes_available = bytes_available_; - - ports::PortStatus port_status; - int rv = node_controller_->node()->GetStatus(control_port_, &port_status); - if (rv != ports::OK || !port_status.receiving_messages) { - DVLOG(1) << "Data pipe consumer " << pipe_id_ << " is aware of peer closure" - << " [control_port=" << control_port_.name() << "]"; - peer_closed_ = true; - } else if (rv == ports::OK && port_status.has_messages && !in_transit_) { - ports::ScopedMessage message; - do { - int rv = node_controller_->node()->GetMessage( - control_port_, &message, nullptr); - if (rv != ports::OK) - peer_closed_ = true; - if (message) { - if (message->num_payload_bytes() < sizeof(DataPipeControlMessage)) { - peer_closed_ = true; - break; - } - - const DataPipeControlMessage* m = - static_cast<const DataPipeControlMessage*>( - message->payload_bytes()); - - if (m->command != DataPipeCommand::DATA_WAS_WRITTEN) { - DLOG(ERROR) << "Unexpected control message from producer."; - peer_closed_ = true; - break; - } - - if (static_cast<size_t>(bytes_available_) + m->num_bytes > - options_.capacity_num_bytes) { - DLOG(ERROR) << "Producer claims to have written too many bytes."; - peer_closed_ = true; - break; - } - - DVLOG(1) << "Data pipe consumer " << pipe_id_ << " is aware that " - << m->num_bytes << " bytes were written. [control_port=" - << control_port_.name() << "]"; - - bytes_available_ += m->num_bytes; - } - } while (message); - } - - bool has_new_data = bytes_available_ != previous_bytes_available; - if (has_new_data) - new_data_available_ = true; - - if (peer_closed_ != was_peer_closed || has_new_data) - watchers_.NotifyState(GetHandleSignalsStateNoLock()); -} - -} // namespace edk -} // 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 120c7a3..0000000 --- a/mojo/edk/system/data_pipe_consumer_dispatcher.h +++ /dev/null @@ -1,123 +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 <stddef.h> -#include <stdint.h> - -#include <memory> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/embedder/platform_handle_vector.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/ports/port_ref.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/edk/system/watcher_set.h" - -namespace mojo { -namespace edk { - -class NodeController; - -// 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 final - : public Dispatcher { - public: - DataPipeConsumerDispatcher( - NodeController* node_controller, - const ports::PortRef& control_port, - scoped_refptr<PlatformSharedBuffer> shared_ring_buffer, - const MojoCreateDataPipeOptions& options, - bool initialized, - uint64_t pipe_id); - - // Dispatcher: - Type GetType() const override; - MojoResult Close() override; - MojoResult ReadData(void* elements, - uint32_t* num_bytes, - MojoReadDataFlags flags) override; - MojoResult BeginReadData(const void** buffer, - uint32_t* buffer_num_bytes, - MojoReadDataFlags flags) override; - MojoResult EndReadData(uint32_t num_bytes_read) override; - HandleSignalsState GetHandleSignalsState() const override; - MojoResult AddWatcherRef(const scoped_refptr<WatcherDispatcher>& watcher, - uintptr_t context) override; - MojoResult RemoveWatcherRef(WatcherDispatcher* watcher, - uintptr_t context) override; - void StartSerialize(uint32_t* num_bytes, - uint32_t* num_ports, - uint32_t* num_handles) override; - bool EndSerialize(void* destination, - ports::PortName* ports, - PlatformHandle* handles) override; - bool BeginTransit() override; - void CompleteTransitAndClose() override; - void CancelTransit() override; - - static scoped_refptr<DataPipeConsumerDispatcher> - Deserialize(const void* data, - size_t num_bytes, - const ports::PortName* ports, - size_t num_ports, - PlatformHandle* handles, - size_t num_handles); - - private: - class PortObserverThunk; - friend class PortObserverThunk; - - ~DataPipeConsumerDispatcher() override; - - void InitializeNoLock(); - MojoResult CloseNoLock(); - HandleSignalsState GetHandleSignalsStateNoLock() const; - void NotifyRead(uint32_t num_bytes); - void OnPortStatusChanged(); - void UpdateSignalsStateNoLock(); - - const MojoCreateDataPipeOptions options_; - NodeController* const node_controller_; - const ports::PortRef control_port_; - const uint64_t pipe_id_; - - // Guards access to the fields below. - mutable base::Lock lock_; - - WatcherSet watchers_; - - scoped_refptr<PlatformSharedBuffer> shared_ring_buffer_; - std::unique_ptr<PlatformSharedBufferMapping> ring_buffer_mapping_; - ScopedPlatformHandle buffer_handle_for_transit_; - - bool in_two_phase_read_ = false; - uint32_t two_phase_max_bytes_read_ = 0; - - bool in_transit_ = false; - bool is_closed_ = false; - bool peer_closed_ = false; - bool transferred_ = false; - - uint32_t read_offset_ = 0; - uint32_t bytes_available_ = 0; - - // Indicates whether any new data is available since the last read attempt. - bool new_data_available_ = false; - - DISALLOW_COPY_AND_ASSIGN(DataPipeConsumerDispatcher); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_DATA_PIPE_CONSUMER_DISPATCHER_H_ diff --git a/mojo/edk/system/data_pipe_control_message.cc b/mojo/edk/system/data_pipe_control_message.cc deleted file mode 100644 index 23873b8..0000000 --- a/mojo/edk/system/data_pipe_control_message.cc +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/data_pipe_control_message.h" - -#include "mojo/edk/embedder/platform_handle_vector.h" -#include "mojo/edk/system/node_controller.h" -#include "mojo/edk/system/ports_message.h" - -namespace mojo { -namespace edk { - -void SendDataPipeControlMessage(NodeController* node_controller, - const ports::PortRef& port, - DataPipeCommand command, - uint32_t num_bytes) { - std::unique_ptr<PortsMessage> message = - PortsMessage::NewUserMessage(sizeof(DataPipeControlMessage), 0, 0); - CHECK(message); - - DataPipeControlMessage* data = - static_cast<DataPipeControlMessage*>(message->mutable_payload_bytes()); - data->command = command; - data->num_bytes = num_bytes; - - int rv = node_controller->SendMessage(port, std::move(message)); - if (rv != ports::OK && rv != ports::ERROR_PORT_PEER_CLOSED) { - DLOG(ERROR) << "Unexpected failure sending data pipe control message: " - << rv; - } -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/data_pipe_control_message.h b/mojo/edk/system/data_pipe_control_message.h deleted file mode 100644 index ec84ea3..0000000 --- a/mojo/edk/system/data_pipe_control_message.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_DATA_PIPE_CONTROL_MESSAGE_H_ -#define MOJO_EDK_SYSTEM_DATA_PIPE_CONTROL_MESSAGE_H_ - -#include <stdint.h> - -#include <memory> - -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/ports/port_ref.h" -#include "mojo/public/c/system/macros.h" - -namespace mojo { -namespace edk { - -class NodeController; - -enum DataPipeCommand : uint32_t { - // Signal to the consumer that new data is available. - DATA_WAS_WRITTEN, - - // Signal to the producer that data has been consumed. - DATA_WAS_READ, -}; - -// Message header for messages sent over a data pipe control port. -struct MOJO_ALIGNAS(8) DataPipeControlMessage { - DataPipeCommand command; - uint32_t num_bytes; -}; - -void SendDataPipeControlMessage(NodeController* node_controller, - const ports::PortRef& port, - DataPipeCommand command, - uint32_t num_bytes); - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_DATA_PIPE_CONTROL_MESSAGE_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 b0102a6..0000000 --- a/mojo/edk/system/data_pipe_producer_dispatcher.cc +++ /dev/null @@ -1,507 +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 <stddef.h> -#include <stdint.h> - -#include <utility> - -#include "base/bind.h" -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" -#include "mojo/edk/embedder/embedder_internal.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/system/configuration.h" -#include "mojo/edk/system/core.h" -#include "mojo/edk/system/data_pipe_control_message.h" -#include "mojo/edk/system/node_controller.h" -#include "mojo/edk/system/ports_message.h" -#include "mojo/edk/system/request_context.h" -#include "mojo/public/c/system/data_pipe.h" - -namespace mojo { -namespace edk { - -namespace { - -const uint8_t kFlagPeerClosed = 0x01; - -#pragma pack(push, 1) - -struct SerializedState { - MojoCreateDataPipeOptions options; - uint64_t pipe_id; - uint32_t write_offset; - uint32_t available_capacity; - uint8_t flags; - char padding[7]; -}; - -static_assert(sizeof(SerializedState) % 8 == 0, - "Invalid SerializedState size."); - -#pragma pack(pop) - -} // namespace - -// A PortObserver which forwards to a DataPipeProducerDispatcher. This owns a -// reference to the dispatcher to ensure it lives as long as the observed port. -class DataPipeProducerDispatcher::PortObserverThunk - : public NodeController::PortObserver { - public: - explicit PortObserverThunk( - scoped_refptr<DataPipeProducerDispatcher> dispatcher) - : dispatcher_(dispatcher) {} - - private: - ~PortObserverThunk() override {} - - // NodeController::PortObserver: - void OnPortStatusChanged() override { dispatcher_->OnPortStatusChanged(); } - - scoped_refptr<DataPipeProducerDispatcher> dispatcher_; - - DISALLOW_COPY_AND_ASSIGN(PortObserverThunk); -}; - -DataPipeProducerDispatcher::DataPipeProducerDispatcher( - NodeController* node_controller, - const ports::PortRef& control_port, - scoped_refptr<PlatformSharedBuffer> shared_ring_buffer, - const MojoCreateDataPipeOptions& options, - bool initialized, - uint64_t pipe_id) - : options_(options), - node_controller_(node_controller), - control_port_(control_port), - pipe_id_(pipe_id), - watchers_(this), - shared_ring_buffer_(shared_ring_buffer), - available_capacity_(options_.capacity_num_bytes) { - if (initialized) { - base::AutoLock lock(lock_); - InitializeNoLock(); - } -} - -Dispatcher::Type DataPipeProducerDispatcher::GetType() const { - return Type::DATA_PIPE_PRODUCER; -} - -MojoResult DataPipeProducerDispatcher::Close() { - base::AutoLock lock(lock_); - DVLOG(1) << "Closing data pipe producer " << pipe_id_; - return CloseNoLock(); -} - -MojoResult DataPipeProducerDispatcher::WriteData(const void* elements, - uint32_t* num_bytes, - MojoWriteDataFlags flags) { - base::AutoLock lock(lock_); - if (!shared_ring_buffer_ || in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (in_two_phase_write_) - return MOJO_RESULT_BUSY; - - if (peer_closed_) - return MOJO_RESULT_FAILED_PRECONDITION; - - if (*num_bytes % options_.element_num_bytes != 0) - return MOJO_RESULT_INVALID_ARGUMENT; - if (*num_bytes == 0) - return MOJO_RESULT_OK; // Nothing to do. - - if ((flags & MOJO_WRITE_DATA_FLAG_ALL_OR_NONE) && - (*num_bytes > available_capacity_)) { - // Don't return "should wait" since you can't wait for a specified amount of - // data. - return MOJO_RESULT_OUT_OF_RANGE; - } - - DCHECK_LE(available_capacity_, options_.capacity_num_bytes); - uint32_t num_bytes_to_write = std::min(*num_bytes, available_capacity_); - if (num_bytes_to_write == 0) - return MOJO_RESULT_SHOULD_WAIT; - - *num_bytes = num_bytes_to_write; - - CHECK(ring_buffer_mapping_); - uint8_t* data = static_cast<uint8_t*>(ring_buffer_mapping_->GetBase()); - CHECK(data); - - const uint8_t* source = static_cast<const uint8_t*>(elements); - CHECK(source); - - DCHECK_LE(write_offset_, options_.capacity_num_bytes); - uint32_t tail_bytes_to_write = - std::min(options_.capacity_num_bytes - write_offset_, - num_bytes_to_write); - uint32_t head_bytes_to_write = num_bytes_to_write - tail_bytes_to_write; - - DCHECK_GT(tail_bytes_to_write, 0u); - memcpy(data + write_offset_, source, tail_bytes_to_write); - if (head_bytes_to_write > 0) - memcpy(data, source + tail_bytes_to_write, head_bytes_to_write); - - DCHECK_LE(num_bytes_to_write, available_capacity_); - available_capacity_ -= num_bytes_to_write; - write_offset_ = (write_offset_ + num_bytes_to_write) % - options_.capacity_num_bytes; - - watchers_.NotifyState(GetHandleSignalsStateNoLock()); - - base::AutoUnlock unlock(lock_); - NotifyWrite(num_bytes_to_write); - - return MOJO_RESULT_OK; -} - -MojoResult DataPipeProducerDispatcher::BeginWriteData( - void** buffer, - uint32_t* buffer_num_bytes, - MojoWriteDataFlags flags) { - base::AutoLock lock(lock_); - if (!shared_ring_buffer_ || in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - - // These flags may not be used in two-phase mode. - if (flags & MOJO_WRITE_DATA_FLAG_ALL_OR_NONE) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (in_two_phase_write_) - return MOJO_RESULT_BUSY; - if (peer_closed_) - return MOJO_RESULT_FAILED_PRECONDITION; - - if (available_capacity_ == 0) { - return peer_closed_ ? MOJO_RESULT_FAILED_PRECONDITION - : MOJO_RESULT_SHOULD_WAIT; - } - - in_two_phase_write_ = true; - *buffer_num_bytes = std::min(options_.capacity_num_bytes - write_offset_, - available_capacity_); - DCHECK_GT(*buffer_num_bytes, 0u); - - CHECK(ring_buffer_mapping_); - uint8_t* data = static_cast<uint8_t*>(ring_buffer_mapping_->GetBase()); - *buffer = data + write_offset_; - - return MOJO_RESULT_OK; -} - -MojoResult DataPipeProducerDispatcher::EndWriteData( - uint32_t num_bytes_written) { - base::AutoLock lock(lock_); - if (is_closed_ || in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - - if (!in_two_phase_write_) - return MOJO_RESULT_FAILED_PRECONDITION; - - DCHECK(shared_ring_buffer_); - DCHECK(ring_buffer_mapping_); - - // Note: Allow successful completion of the two-phase write even if the other - // side has been closed. - MojoResult rv = MOJO_RESULT_OK; - if (num_bytes_written > available_capacity_ || - num_bytes_written % options_.element_num_bytes != 0 || - write_offset_ + num_bytes_written > options_.capacity_num_bytes) { - rv = MOJO_RESULT_INVALID_ARGUMENT; - } else { - DCHECK_LE(num_bytes_written + write_offset_, options_.capacity_num_bytes); - available_capacity_ -= num_bytes_written; - write_offset_ = (write_offset_ + num_bytes_written) % - options_.capacity_num_bytes; - - base::AutoUnlock unlock(lock_); - NotifyWrite(num_bytes_written); - } - - in_two_phase_write_ = false; - - // If we're now writable, we *became* writable (since we weren't writable - // during the two-phase write), so notify watchers. - watchers_.NotifyState(GetHandleSignalsStateNoLock()); - - return rv; -} - -HandleSignalsState DataPipeProducerDispatcher::GetHandleSignalsState() const { - base::AutoLock lock(lock_); - return GetHandleSignalsStateNoLock(); -} - -MojoResult DataPipeProducerDispatcher::AddWatcherRef( - const scoped_refptr<WatcherDispatcher>& watcher, - uintptr_t context) { - base::AutoLock lock(lock_); - if (is_closed_ || in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - return watchers_.Add(watcher, context, GetHandleSignalsStateNoLock()); -} - -MojoResult DataPipeProducerDispatcher::RemoveWatcherRef( - WatcherDispatcher* watcher, - uintptr_t context) { - base::AutoLock lock(lock_); - if (is_closed_ || in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - return watchers_.Remove(watcher, context); -} - -void DataPipeProducerDispatcher::StartSerialize(uint32_t* num_bytes, - uint32_t* num_ports, - uint32_t* num_handles) { - base::AutoLock lock(lock_); - DCHECK(in_transit_); - *num_bytes = sizeof(SerializedState); - *num_ports = 1; - *num_handles = 1; -} - -bool DataPipeProducerDispatcher::EndSerialize( - void* destination, - ports::PortName* ports, - PlatformHandle* platform_handles) { - SerializedState* state = static_cast<SerializedState*>(destination); - memcpy(&state->options, &options_, sizeof(MojoCreateDataPipeOptions)); - memset(state->padding, 0, sizeof(state->padding)); - - base::AutoLock lock(lock_); - DCHECK(in_transit_); - state->pipe_id = pipe_id_; - state->write_offset = write_offset_; - state->available_capacity = available_capacity_; - state->flags = peer_closed_ ? kFlagPeerClosed : 0; - - ports[0] = control_port_.name(); - - buffer_handle_for_transit_ = shared_ring_buffer_->DuplicatePlatformHandle(); - platform_handles[0] = buffer_handle_for_transit_.get(); - - return true; -} - -bool DataPipeProducerDispatcher::BeginTransit() { - base::AutoLock lock(lock_); - if (in_transit_) - return false; - in_transit_ = !in_two_phase_write_; - return in_transit_; -} - -void DataPipeProducerDispatcher::CompleteTransitAndClose() { - node_controller_->SetPortObserver(control_port_, nullptr); - - base::AutoLock lock(lock_); - DCHECK(in_transit_); - transferred_ = true; - in_transit_ = false; - ignore_result(buffer_handle_for_transit_.release()); - CloseNoLock(); -} - -void DataPipeProducerDispatcher::CancelTransit() { - base::AutoLock lock(lock_); - DCHECK(in_transit_); - in_transit_ = false; - buffer_handle_for_transit_.reset(); - - HandleSignalsState state = GetHandleSignalsStateNoLock(); - watchers_.NotifyState(state); -} - -// static -scoped_refptr<DataPipeProducerDispatcher> -DataPipeProducerDispatcher::Deserialize(const void* data, - size_t num_bytes, - const ports::PortName* ports, - size_t num_ports, - PlatformHandle* handles, - size_t num_handles) { - if (num_ports != 1 || num_handles != 1 || - num_bytes != sizeof(SerializedState)) { - return nullptr; - } - - const SerializedState* state = static_cast<const SerializedState*>(data); - - NodeController* node_controller = internal::g_core->GetNodeController(); - ports::PortRef port; - if (node_controller->node()->GetPort(ports[0], &port) != ports::OK) - return nullptr; - - PlatformHandle buffer_handle; - std::swap(buffer_handle, handles[0]); - scoped_refptr<PlatformSharedBuffer> ring_buffer = - PlatformSharedBuffer::CreateFromPlatformHandle( - state->options.capacity_num_bytes, - false /* read_only */, - ScopedPlatformHandle(buffer_handle)); - if (!ring_buffer) { - DLOG(ERROR) << "Failed to deserialize shared buffer handle."; - return nullptr; - } - - scoped_refptr<DataPipeProducerDispatcher> dispatcher = - new DataPipeProducerDispatcher(node_controller, port, ring_buffer, - state->options, false /* initialized */, - state->pipe_id); - - { - base::AutoLock lock(dispatcher->lock_); - dispatcher->write_offset_ = state->write_offset; - dispatcher->available_capacity_ = state->available_capacity; - dispatcher->peer_closed_ = state->flags & kFlagPeerClosed; - dispatcher->InitializeNoLock(); - dispatcher->UpdateSignalsStateNoLock(); - } - - return dispatcher; -} - -DataPipeProducerDispatcher::~DataPipeProducerDispatcher() { - DCHECK(is_closed_ && !in_transit_ && !shared_ring_buffer_ && - !ring_buffer_mapping_); -} - -void DataPipeProducerDispatcher::InitializeNoLock() { - lock_.AssertAcquired(); - - if (shared_ring_buffer_) { - ring_buffer_mapping_ = - shared_ring_buffer_->Map(0, options_.capacity_num_bytes); - if (!ring_buffer_mapping_) { - DLOG(ERROR) << "Failed to map shared buffer."; - shared_ring_buffer_ = nullptr; - } - } - - base::AutoUnlock unlock(lock_); - node_controller_->SetPortObserver( - control_port_, - make_scoped_refptr(new PortObserverThunk(this))); -} - -MojoResult DataPipeProducerDispatcher::CloseNoLock() { - lock_.AssertAcquired(); - if (is_closed_ || in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - is_closed_ = true; - ring_buffer_mapping_.reset(); - shared_ring_buffer_ = nullptr; - - watchers_.NotifyClosed(); - if (!transferred_) { - base::AutoUnlock unlock(lock_); - node_controller_->ClosePort(control_port_); - } - - return MOJO_RESULT_OK; -} - -HandleSignalsState DataPipeProducerDispatcher::GetHandleSignalsStateNoLock() - const { - lock_.AssertAcquired(); - HandleSignalsState rv; - if (!peer_closed_) { - if (!in_two_phase_write_ && shared_ring_buffer_ && available_capacity_ > 0) - 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 DataPipeProducerDispatcher::NotifyWrite(uint32_t num_bytes) { - DVLOG(1) << "Data pipe producer " << pipe_id_ << " notifying peer: " - << num_bytes << " bytes written. [control_port=" - << control_port_.name() << "]"; - - SendDataPipeControlMessage(node_controller_, control_port_, - DataPipeCommand::DATA_WAS_WRITTEN, num_bytes); -} - -void DataPipeProducerDispatcher::OnPortStatusChanged() { - DCHECK(RequestContext::current()); - - base::AutoLock lock(lock_); - - // We stop observing the control port as soon it's transferred, but this can - // race with events which are raised right before that happens. This is fine - // to ignore. - if (transferred_) - return; - - DVLOG(1) << "Control port status changed for data pipe producer " << pipe_id_; - - UpdateSignalsStateNoLock(); -} - -void DataPipeProducerDispatcher::UpdateSignalsStateNoLock() { - lock_.AssertAcquired(); - - bool was_peer_closed = peer_closed_; - size_t previous_capacity = available_capacity_; - - ports::PortStatus port_status; - int rv = node_controller_->node()->GetStatus(control_port_, &port_status); - if (rv != ports::OK || !port_status.receiving_messages) { - DVLOG(1) << "Data pipe producer " << pipe_id_ << " is aware of peer closure" - << " [control_port=" << control_port_.name() << "]"; - peer_closed_ = true; - } else if (rv == ports::OK && port_status.has_messages && !in_transit_) { - ports::ScopedMessage message; - do { - int rv = node_controller_->node()->GetMessage( - control_port_, &message, nullptr); - if (rv != ports::OK) - peer_closed_ = true; - if (message) { - if (message->num_payload_bytes() < sizeof(DataPipeControlMessage)) { - peer_closed_ = true; - break; - } - - const DataPipeControlMessage* m = - static_cast<const DataPipeControlMessage*>( - message->payload_bytes()); - - if (m->command != DataPipeCommand::DATA_WAS_READ) { - DLOG(ERROR) << "Unexpected message from consumer."; - peer_closed_ = true; - break; - } - - if (static_cast<size_t>(available_capacity_) + m->num_bytes > - options_.capacity_num_bytes) { - DLOG(ERROR) << "Consumer claims to have read too many bytes."; - break; - } - - DVLOG(1) << "Data pipe producer " << pipe_id_ << " is aware that " - << m->num_bytes << " bytes were read. [control_port=" - << control_port_.name() << "]"; - - available_capacity_ += m->num_bytes; - } - } while (message); - } - - if (peer_closed_ != was_peer_closed || - available_capacity_ != previous_capacity) { - watchers_.NotifyState(GetHandleSignalsStateNoLock()); - } -} - -} // namespace edk -} // 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 1eddd5d..0000000 --- a/mojo/edk/system/data_pipe_producer_dispatcher.h +++ /dev/null @@ -1,123 +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 <stddef.h> -#include <stdint.h> - -#include <memory> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/embedder/platform_handle_vector.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/ports/port_ref.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/edk/system/watcher_set.h" - -namespace mojo { -namespace edk { - -struct DataPipeControlMessage; -class NodeController; - -// 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 final - : public Dispatcher { - public: - DataPipeProducerDispatcher( - NodeController* node_controller, - const ports::PortRef& port, - scoped_refptr<PlatformSharedBuffer> shared_ring_buffer, - const MojoCreateDataPipeOptions& options, - bool initialized, - uint64_t pipe_id); - - // Dispatcher: - Type GetType() const override; - MojoResult Close() override; - MojoResult WriteData(const void* elements, - uint32_t* num_bytes, - MojoReadDataFlags flags) override; - MojoResult BeginWriteData(void** buffer, - uint32_t* buffer_num_bytes, - MojoWriteDataFlags flags) override; - MojoResult EndWriteData(uint32_t num_bytes_written) override; - HandleSignalsState GetHandleSignalsState() const override; - MojoResult AddWatcherRef(const scoped_refptr<WatcherDispatcher>& watcher, - uintptr_t context) override; - MojoResult RemoveWatcherRef(WatcherDispatcher* watcher, - uintptr_t context) override; - void StartSerialize(uint32_t* num_bytes, - uint32_t* num_ports, - uint32_t* num_handles) override; - bool EndSerialize(void* destination, - ports::PortName* ports, - PlatformHandle* handles) override; - bool BeginTransit() override; - void CompleteTransitAndClose() override; - void CancelTransit() override; - - static scoped_refptr<DataPipeProducerDispatcher> - Deserialize(const void* data, - size_t num_bytes, - const ports::PortName* ports, - size_t num_ports, - PlatformHandle* handles, - size_t num_handles); - - private: - class PortObserverThunk; - friend class PortObserverThunk; - - ~DataPipeProducerDispatcher() override; - - void OnSharedBufferCreated(const scoped_refptr<PlatformSharedBuffer>& buffer); - void InitializeNoLock(); - MojoResult CloseNoLock(); - HandleSignalsState GetHandleSignalsStateNoLock() const; - void NotifyWrite(uint32_t num_bytes); - void OnPortStatusChanged(); - void UpdateSignalsStateNoLock(); - bool ProcessMessageNoLock(const DataPipeControlMessage& message, - ScopedPlatformHandleVectorPtr handles); - - const MojoCreateDataPipeOptions options_; - NodeController* const node_controller_; - const ports::PortRef control_port_; - const uint64_t pipe_id_; - - // Guards access to the fields below. - mutable base::Lock lock_; - - WatcherSet watchers_; - - bool buffer_requested_ = false; - - scoped_refptr<PlatformSharedBuffer> shared_ring_buffer_; - std::unique_ptr<PlatformSharedBufferMapping> ring_buffer_mapping_; - ScopedPlatformHandle buffer_handle_for_transit_; - - bool in_transit_ = false; - bool is_closed_ = false; - bool peer_closed_ = false; - bool transferred_ = false; - bool in_two_phase_write_ = false; - - uint32_t write_offset_ = 0; - uint32_t available_capacity_; - - DISALLOW_COPY_AND_ASSIGN(DataPipeProducerDispatcher); -}; - -} // namespace edk -} // 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 79c1f75..0000000 --- a/mojo/edk/system/data_pipe_unittest.cc +++ /dev/null @@ -1,2034 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stddef.h> -#include <stdint.h> - -#include <memory> - -#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/run_loop.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/embedder/platform_channel_pair.h" -#include "mojo/edk/system/test_utils.h" -#include "mojo/edk/test/mojo_test_base.h" -#include "mojo/public/c/system/data_pipe.h" -#include "mojo/public/c/system/functions.h" -#include "mojo/public/c/system/message_pipe.h" -#include "mojo/public/cpp/system/simple_watcher.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace edk { -namespace { - -const uint32_t kSizeOfOptions = - static_cast<uint32_t>(sizeof(MojoCreateDataPipeOptions)); - -// In various places, we have to poll (since, e.g., we can't yet wait for a -// certain amount of data to be available). This is the maximum number of -// iterations (separated by a short sleep). -// TODO(vtl): Get rid of this. -const size_t kMaxPoll = 100; - -// Used in Multiprocess test. -const size_t kMultiprocessCapacity = 37; -const char kMultiprocessTestData[] = "hello i'm a string that is 36 bytes"; -const int kMultiprocessMaxIter = 5; - -// TODO(rockot): There are many uses of ASSERT where EXPECT would be more -// appropriate. Fix this. - -class DataPipeTest : public test::MojoTestBase { - public: - DataPipeTest() : producer_(MOJO_HANDLE_INVALID), - consumer_(MOJO_HANDLE_INVALID) {} - - ~DataPipeTest() override { - if (producer_ != MOJO_HANDLE_INVALID) - CHECK_EQ(MOJO_RESULT_OK, MojoClose(producer_)); - if (consumer_ != MOJO_HANDLE_INVALID) - CHECK_EQ(MOJO_RESULT_OK, MojoClose(consumer_)); - } - - MojoResult Create(const MojoCreateDataPipeOptions* options) { - return MojoCreateDataPipe(options, &producer_, &consumer_); - } - - MojoResult WriteData(const void* elements, - uint32_t* num_bytes, - bool all_or_none = false) { - return MojoWriteData(producer_, elements, num_bytes, - all_or_none ? MOJO_WRITE_DATA_FLAG_ALL_OR_NONE - : MOJO_WRITE_DATA_FLAG_NONE); - } - - MojoResult ReadData(void* elements, - uint32_t* num_bytes, - bool all_or_none = false, - bool peek = false) { - MojoReadDataFlags flags = MOJO_READ_DATA_FLAG_NONE; - if (all_or_none) - flags |= MOJO_READ_DATA_FLAG_ALL_OR_NONE; - if (peek) - flags |= MOJO_READ_DATA_FLAG_PEEK; - return MojoReadData(consumer_, elements, num_bytes, flags); - } - - MojoResult QueryData(uint32_t* num_bytes) { - return MojoReadData(consumer_, nullptr, num_bytes, - MOJO_READ_DATA_FLAG_QUERY); - } - - MojoResult DiscardData(uint32_t* num_bytes, bool all_or_none = false) { - MojoReadDataFlags flags = MOJO_READ_DATA_FLAG_DISCARD; - if (all_or_none) - flags |= MOJO_READ_DATA_FLAG_ALL_OR_NONE; - return MojoReadData(consumer_, nullptr, num_bytes, flags); - } - - MojoResult BeginReadData(const void** elements, - uint32_t* num_bytes, - bool all_or_none = false) { - MojoReadDataFlags flags = MOJO_READ_DATA_FLAG_NONE; - if (all_or_none) - flags |= MOJO_READ_DATA_FLAG_ALL_OR_NONE; - return MojoBeginReadData(consumer_, elements, num_bytes, flags); - } - - MojoResult EndReadData(uint32_t num_bytes_read) { - return MojoEndReadData(consumer_, num_bytes_read); - } - - MojoResult BeginWriteData(void** elements, - uint32_t* num_bytes, - bool all_or_none = false) { - MojoReadDataFlags flags = MOJO_WRITE_DATA_FLAG_NONE; - if (all_or_none) - flags |= MOJO_WRITE_DATA_FLAG_ALL_OR_NONE; - return MojoBeginWriteData(producer_, elements, num_bytes, flags); - } - - MojoResult EndWriteData(uint32_t num_bytes_written) { - return MojoEndWriteData(producer_, num_bytes_written); - } - - MojoResult CloseProducer() { - MojoResult rv = MojoClose(producer_); - producer_ = MOJO_HANDLE_INVALID; - return rv; - } - - MojoResult CloseConsumer() { - MojoResult rv = MojoClose(consumer_); - consumer_ = MOJO_HANDLE_INVALID; - return rv; - } - - MojoHandle producer_, consumer_; - - private: - DISALLOW_COPY_AND_ASSIGN(DataPipeTest); -}; - -TEST_F(DataPipeTest, Basic) { - 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|. - }; - - ASSERT_EQ(MOJO_RESULT_OK, Create(&options)); - - // We can write to a data pipe handle immediately. - int32_t elements[10] = {}; - uint32_t num_bytes = 0; - - num_bytes = - static_cast<uint32_t>(arraysize(elements) * sizeof(elements[0])); - - elements[0] = 123; - elements[1] = 456; - num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0])); - ASSERT_EQ(MOJO_RESULT_OK, WriteData(&elements[0], &num_bytes)); - - // Now wait for the other side to become readable. - MojoHandleSignalsState state; - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &state)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - state.satisfied_signals); - - elements[0] = -1; - elements[1] = -1; - ASSERT_EQ(MOJO_RESULT_OK, ReadData(&elements[0], &num_bytes)); - ASSERT_EQ(static_cast<uint32_t>(2u * sizeof(elements[0])), num_bytes); - ASSERT_EQ(elements[0], 123); - ASSERT_EQ(elements[1], 456); -} - -// Tests creation of data pipes with various (valid) options. -TEST_F(DataPipeTest, CreateAndMaybeTransfer) { - MojoCreateDataPipeOptions test_options[] = { - // Default options. - {}, - // Trivial element size, non-default capacity. - {kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - 1, // |element_num_bytes|. - 1000}, // |capacity_num_bytes|. - // Nontrivial element size, non-default capacity. - {kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - 4, // |element_num_bytes|. - 4000}, // |capacity_num_bytes|. - // Nontrivial element size, default capacity. - {kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - 100, // |element_num_bytes|. - 0} // |capacity_num_bytes|. - }; - for (size_t i = 0; i < arraysize(test_options); i++) { - MojoHandle producer_handle, consumer_handle; - MojoCreateDataPipeOptions* options = - i ? &test_options[i] : nullptr; - ASSERT_EQ(MOJO_RESULT_OK, - MojoCreateDataPipe(options, &producer_handle, &consumer_handle)); - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(producer_handle)); - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(consumer_handle)); - } -} - -TEST_F(DataPipeTest, 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|. - }; - - ASSERT_EQ(MOJO_RESULT_OK, Create(&options)); - MojoHandleSignalsState hss; - - int32_t elements[10] = {}; - uint32_t num_bytes = 0; - - // Try reading; nothing there yet. - num_bytes = - static_cast<uint32_t>(arraysize(elements) * sizeof(elements[0])); - ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, ReadData(elements, &num_bytes)); - - // Query; nothing there yet. - num_bytes = 0; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - ASSERT_EQ(0u, num_bytes); - - // Discard; nothing there yet. - num_bytes = static_cast<uint32_t>(5u * sizeof(elements[0])); - ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, DiscardData(&num_bytes)); - - // Read with invalid |num_bytes|. - num_bytes = sizeof(elements[0]) + 1; - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, ReadData(elements, &num_bytes)); - - // Write two elements. - elements[0] = 123; - elements[1] = 456; - num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0])); - ASSERT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes)); - // It should have written everything (even without "all or none"). - ASSERT_EQ(2u * sizeof(elements[0]), num_bytes); - - // Wait. - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // Query. - // TODO(vtl): It's theoretically possible (though not with the current - // implementation/configured limits) that not all the data has arrived yet. - // (The theoretically-correct assertion here is that |num_bytes| is |1 * ...| - // or |2 * ...|.) - num_bytes = 0; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - ASSERT_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])); - ASSERT_EQ(MOJO_RESULT_OK, ReadData(elements, &num_bytes)); - ASSERT_EQ(1u * sizeof(elements[0]), num_bytes); - ASSERT_EQ(123, elements[0]); - ASSERT_EQ(-1, elements[1]); - - // Query. - // TODO(vtl): See previous TODO. (If we got 2 elements there, however, we - // should get 1 here.) - num_bytes = 0; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - ASSERT_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])); - ASSERT_EQ(MOJO_RESULT_OK, ReadData(elements, &num_bytes, false, true)); - ASSERT_EQ(1u * sizeof(elements[0]), num_bytes); - ASSERT_EQ(456, elements[0]); - ASSERT_EQ(-1, elements[1]); - - // Query. Still has 1 element remaining. - num_bytes = 0; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - ASSERT_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])); - ASSERT_EQ(MOJO_RESULT_OUT_OF_RANGE, - ReadData(elements, &num_bytes, true, false)); - ASSERT_EQ(-1, elements[0]); - ASSERT_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])); - ASSERT_EQ(MOJO_RESULT_OK, ReadData(elements, &num_bytes, false, false)); - ASSERT_EQ(1u * sizeof(elements[0]), num_bytes); - ASSERT_EQ(456, elements[0]); - ASSERT_EQ(-1, elements[1]); - - // Query. - num_bytes = 0; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - ASSERT_EQ(0u, num_bytes); -} - -// 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_F(DataPipeTest, BasicProducerWaiting) { - // Note: We take advantage of the fact that current for current - // implementations 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|. - }; - Create(&options); - MojoHandleSignalsState hss; - - // Never readable. Already writable. - hss = GetSignalsState(producer_); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | 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])); - ASSERT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true)); - ASSERT_EQ(static_cast<uint32_t>(2u * sizeof(elements[0])), num_bytes); - - // Wait for data to become available to the consumer. - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // Peek one element. - elements[0] = -1; - elements[1] = -1; - num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0])); - ASSERT_EQ(MOJO_RESULT_OK, ReadData(elements, &num_bytes, true, true)); - ASSERT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes); - ASSERT_EQ(123, elements[0]); - ASSERT_EQ(-1, elements[1]); - - // Read one element. - elements[0] = -1; - elements[1] = -1; - num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0])); - ASSERT_EQ(MOJO_RESULT_OK, ReadData(elements, &num_bytes, true, false)); - ASSERT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes); - ASSERT_EQ(123, elements[0]); - ASSERT_EQ(-1, elements[1]); - - // Try writing, using a two-phase write. - void* buffer = nullptr; - num_bytes = static_cast<uint32_t>(3u * sizeof(elements[0])); - ASSERT_EQ(MOJO_RESULT_OK, BeginWriteData(&buffer, &num_bytes)); - EXPECT_TRUE(buffer); - ASSERT_GE(num_bytes, static_cast<uint32_t>(1u * sizeof(elements[0]))); - - static_cast<int32_t*>(buffer)[0] = 789; - ASSERT_EQ(MOJO_RESULT_OK, EndWriteData(static_cast<uint32_t>( - 1u * sizeof(elements[0])))); - - // Read one element, using a two-phase read. - const void* read_buffer = nullptr; - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, - BeginReadData(&read_buffer, &num_bytes, false)); - EXPECT_TRUE(read_buffer); - // The two-phase read should be able to read at least one element. - ASSERT_GE(num_bytes, static_cast<uint32_t>(1u * sizeof(elements[0]))); - ASSERT_EQ(456, static_cast<const int32_t*>(read_buffer)[0]); - ASSERT_EQ(MOJO_RESULT_OK, EndReadData(static_cast<uint32_t>( - 1u * sizeof(elements[0])))); - - // Write one element. - elements[0] = 123; - num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0])); - ASSERT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes)); - ASSERT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes); - - // Close the consumer. - CloseConsumer(); - - // It should now be never-writable. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(producer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); -} - -TEST_F(DataPipeTest, PeerClosedProducerWaiting) { - 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|. - }; - ASSERT_EQ(MOJO_RESULT_OK, Create(&options)); - MojoHandleSignalsState hss; - - // Close the consumer. - CloseConsumer(); - - // It should be signaled. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(producer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); -} - -TEST_F(DataPipeTest, PeerClosedConsumerWaiting) { - 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|. - }; - ASSERT_EQ(MOJO_RESULT_OK, Create(&options)); - MojoHandleSignalsState hss; - - // Close the producer. - CloseProducer(); - - // It should be signaled. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); -} - -TEST_F(DataPipeTest, 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|. - }; - ASSERT_EQ(MOJO_RESULT_OK, Create(&options)); - MojoHandleSignalsState hss; - - // Never writable. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_WRITABLE, &hss)); - EXPECT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // Write two elements. - int32_t elements[2] = {123, 456}; - uint32_t num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0])); - ASSERT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true)); - - // Wait for readability. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // Discard one element. - num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0])); - ASSERT_EQ(MOJO_RESULT_OK, DiscardData(&num_bytes, true)); - ASSERT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes); - - // Should still be readable. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // Peek one element. - elements[0] = -1; - elements[1] = -1; - num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0])); - ASSERT_EQ(MOJO_RESULT_OK, ReadData(elements, &num_bytes, true, true)); - ASSERT_EQ(456, elements[0]); - ASSERT_EQ(-1, elements[1]); - - // Should still be readable. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // Read one element. - elements[0] = -1; - elements[1] = -1; - num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0])); - ASSERT_EQ(MOJO_RESULT_OK, ReadData(elements, &num_bytes, true)); - ASSERT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes); - ASSERT_EQ(456, elements[0]); - ASSERT_EQ(-1, elements[1]); - - // Write one element. - elements[0] = 789; - elements[1] = -1; - num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0])); - ASSERT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true)); - - // Waiting should now succeed. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // Close the producer. - CloseProducer(); - - // Should still be readable. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_TRUE(hss.satisfied_signals & (MOJO_HANDLE_SIGNAL_READABLE | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // Wait for the peer closed signal. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // Read one element. - elements[0] = -1; - elements[1] = -1; - num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0])); - ASSERT_EQ(MOJO_RESULT_OK, ReadData(elements, &num_bytes, true)); - ASSERT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes); - ASSERT_EQ(789, elements[0]); - ASSERT_EQ(-1, elements[1]); - - // Should be never-readable. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); -} - -TEST_F(DataPipeTest, ConsumerNewDataReadable) { - 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|. - }; - EXPECT_EQ(MOJO_RESULT_OK, Create(&options)); - - int32_t elements[2] = {123, 456}; - uint32_t num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0])); - EXPECT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true)); - - // The consumer handle should appear to be readable and have new data. - EXPECT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE)); - EXPECT_TRUE(GetSignalsState(consumer_).satisfied_signals & - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE); - - // Now try to read a minimum of 6 elements. - int32_t read_elements[6]; - uint32_t num_read_bytes = sizeof(read_elements); - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - MojoReadData(consumer_, read_elements, &num_read_bytes, - MOJO_READ_DATA_FLAG_ALL_OR_NONE)); - - // The consumer should still appear to be readable but not with new data. - EXPECT_TRUE(GetSignalsState(consumer_).satisfied_signals & - MOJO_HANDLE_SIGNAL_READABLE); - EXPECT_FALSE(GetSignalsState(consumer_).satisfied_signals & - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE); - - // Write four more elements. - EXPECT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true)); - EXPECT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true)); - - // The consumer handle should once again appear to be readable. - EXPECT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE)); - - // Try again to read a minimum of 6 elements. Should succeed this time. - EXPECT_EQ(MOJO_RESULT_OK, - MojoReadData(consumer_, read_elements, &num_read_bytes, - MOJO_READ_DATA_FLAG_ALL_OR_NONE)); - - // And now the consumer is unreadable. - EXPECT_FALSE(GetSignalsState(consumer_).satisfied_signals & - MOJO_HANDLE_SIGNAL_READABLE); - EXPECT_FALSE(GetSignalsState(consumer_).satisfied_signals & - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE); -} - -// Test with two-phase APIs and also closing the producer with an active -// consumer waiter. -TEST_F(DataPipeTest, ConsumerWaitingTwoPhase) { - 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|. - }; - ASSERT_EQ(MOJO_RESULT_OK, Create(&options)); - MojoHandleSignalsState 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])); - ASSERT_EQ(MOJO_RESULT_OK, BeginWriteData(&buffer, &num_bytes, false)); - 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; - ASSERT_EQ(MOJO_RESULT_OK, EndWriteData(2u * sizeof(elements[0]))); - - // Wait for readability. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - 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])); - ASSERT_EQ(MOJO_RESULT_OK, BeginReadData(&read_buffer, &num_bytes, true)); - EXPECT_TRUE(read_buffer); - ASSERT_EQ(static_cast<uint32_t>(2u * sizeof(elements[0])), num_bytes); - const int32_t* read_elements = static_cast<const int32_t*>(read_buffer); - ASSERT_EQ(123, read_elements[0]); - ASSERT_EQ(MOJO_RESULT_OK, EndReadData(1u * sizeof(elements[0]))); - - // Should still be readable. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - 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])); - ASSERT_EQ(MOJO_RESULT_OK, BeginReadData(&read_buffer, &num_bytes)); - EXPECT_TRUE(read_buffer); - ASSERT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes); - read_elements = static_cast<const int32_t*>(read_buffer); - ASSERT_EQ(456, read_elements[0]); - ASSERT_EQ(MOJO_RESULT_OK, EndReadData(1u * sizeof(elements[0]))); - - // Close the producer. - CloseProducer(); - - // Should be never-readable. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); -} - -// Tests that data pipes aren't writable/readable during two-phase writes/reads. -TEST_F(DataPipeTest, 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|. - }; - ASSERT_EQ(MOJO_RESULT_OK, Create(&options)); - MojoHandleSignalsState hss; - - // It should be writable. - hss = GetSignalsState(producer_); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - ASSERT_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; - ASSERT_EQ(MOJO_RESULT_OK, BeginWriteData(&write_ptr, &num_bytes)); - EXPECT_TRUE(write_ptr); - EXPECT_GE(num_bytes, static_cast<uint32_t>(1u * sizeof(int32_t))); - - // At this point, it shouldn't be writable. - hss = GetSignalsState(producer_); - ASSERT_EQ(0u, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // It shouldn't be readable yet either (we'll wait later). - hss = GetSignalsState(consumer_); - ASSERT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - static_cast<int32_t*>(write_ptr)[0] = 123; - ASSERT_EQ(MOJO_RESULT_OK, EndWriteData(1u * sizeof(int32_t))); - - // It should immediately be writable again. - hss = GetSignalsState(producer_); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // It should become readable. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - 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; - ASSERT_EQ(MOJO_RESULT_OK, BeginWriteData(&write_ptr, &num_bytes)); - EXPECT_TRUE(write_ptr); - EXPECT_GE(num_bytes, static_cast<uint32_t>(1u * sizeof(int32_t))); - - // It should be readable. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // End the two-phase write without writing anything. - ASSERT_EQ(MOJO_RESULT_OK, EndWriteData(0u)); - - // Start a two-phase read. - num_bytes = static_cast<uint32_t>(1u * sizeof(int32_t)); - const void* read_ptr = nullptr; - ASSERT_EQ(MOJO_RESULT_OK, BeginReadData(&read_ptr, &num_bytes)); - EXPECT_TRUE(read_ptr); - ASSERT_EQ(static_cast<uint32_t>(1u * sizeof(int32_t)), num_bytes); - - // At this point, it should still be writable. - hss = GetSignalsState(producer_); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // But not readable. - hss = GetSignalsState(consumer_); - ASSERT_EQ(0u, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // End the two-phase read without reading anything. - ASSERT_EQ(MOJO_RESULT_OK, EndReadData(0u)); - - // It should be readable again. - hss = GetSignalsState(consumer_); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); -} - -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_F(DataPipeTest, 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|. - }; - ASSERT_EQ(MOJO_RESULT_OK, Create(&options)); - MojoHandleSignalsState hss; - - // Try writing more than the total capacity of the pipe. - uint32_t num_bytes = 20u * sizeof(int32_t); - int32_t buffer[100]; - Seq(0, arraysize(buffer), buffer); - ASSERT_EQ(MOJO_RESULT_OUT_OF_RANGE, WriteData(buffer, &num_bytes, true)); - - // Should still be empty. - num_bytes = ~0u; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - ASSERT_EQ(0u, num_bytes); - - // Write some data. - num_bytes = 5u * sizeof(int32_t); - Seq(100, arraysize(buffer), buffer); - ASSERT_EQ(MOJO_RESULT_OK, WriteData(buffer, &num_bytes, true)); - ASSERT_EQ(5u * sizeof(int32_t), num_bytes); - - // Wait for data. - // TODO(vtl): There's no real guarantee that all the data will become - // available at once (except that in current implementations, with reasonable - // limits, it will). Eventually, we'll be able to wait for a specified amount - // of data to become available. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Half full. - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - ASSERT_EQ(5u * sizeof(int32_t), num_bytes); - - // Try writing more than the available capacity of the pipe, but less than the - // total capacity. - num_bytes = 6u * sizeof(int32_t); - Seq(200, arraysize(buffer), buffer); - ASSERT_EQ(MOJO_RESULT_OUT_OF_RANGE, WriteData(buffer, &num_bytes, true)); - - // Try reading too much. - num_bytes = 11u * sizeof(int32_t); - memset(buffer, 0xab, sizeof(buffer)); - ASSERT_EQ(MOJO_RESULT_OUT_OF_RANGE, ReadData(buffer, &num_bytes, true)); - int32_t expected_buffer[100]; - memset(expected_buffer, 0xab, sizeof(expected_buffer)); - ASSERT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer))); - - // Try discarding too much. - num_bytes = 11u * sizeof(int32_t); - ASSERT_EQ(MOJO_RESULT_OUT_OF_RANGE, DiscardData(&num_bytes, true)); - - // Just a little. - num_bytes = 2u * sizeof(int32_t); - Seq(300, arraysize(buffer), buffer); - ASSERT_EQ(MOJO_RESULT_OK, WriteData(buffer, &num_bytes, true)); - ASSERT_EQ(2u * sizeof(int32_t), num_bytes); - - // Just right. - num_bytes = 3u * sizeof(int32_t); - Seq(400, arraysize(buffer), buffer); - ASSERT_EQ(MOJO_RESULT_OK, WriteData(buffer, &num_bytes, true)); - ASSERT_EQ(3u * sizeof(int32_t), num_bytes); - - // TODO(vtl): Hack (see also the TODO above): We can't currently wait for a - // specified amount of data to be available, so poll. - for (size_t i = 0; i < kMaxPoll; i++) { - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - if (num_bytes >= 10u * sizeof(int32_t)) - break; - - test::Sleep(test::EpsilonDeadline()); - } - ASSERT_EQ(10u * sizeof(int32_t), num_bytes); - - // Read half. - num_bytes = 5u * sizeof(int32_t); - memset(buffer, 0xab, sizeof(buffer)); - ASSERT_EQ(MOJO_RESULT_OK, ReadData(buffer, &num_bytes, true)); - ASSERT_EQ(5u * sizeof(int32_t), num_bytes); - memset(expected_buffer, 0xab, sizeof(expected_buffer)); - Seq(100, 5, expected_buffer); - ASSERT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer))); - - // Try reading too much again. - num_bytes = 6u * sizeof(int32_t); - memset(buffer, 0xab, sizeof(buffer)); - ASSERT_EQ(MOJO_RESULT_OUT_OF_RANGE, ReadData(buffer, &num_bytes, true)); - memset(expected_buffer, 0xab, sizeof(expected_buffer)); - ASSERT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer))); - - // Try discarding too much again. - num_bytes = 6u * sizeof(int32_t); - ASSERT_EQ(MOJO_RESULT_OUT_OF_RANGE, DiscardData(&num_bytes, true)); - - // Discard a little. - num_bytes = 2u * sizeof(int32_t); - ASSERT_EQ(MOJO_RESULT_OK, DiscardData(&num_bytes, true)); - ASSERT_EQ(2u * sizeof(int32_t), num_bytes); - - // Three left. - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - ASSERT_EQ(3u * sizeof(int32_t), num_bytes); - - // Close the producer, then test producer-closed cases. - CloseProducer(); - - // Wait. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &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); - - // Try reading too much; "failed precondition" since the producer is closed. - num_bytes = 4u * sizeof(int32_t); - memset(buffer, 0xab, sizeof(buffer)); - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - ReadData(buffer, &num_bytes, true)); - memset(expected_buffer, 0xab, sizeof(expected_buffer)); - ASSERT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer))); - - // Try discarding too much; "failed precondition" again. - num_bytes = 4u * sizeof(int32_t); - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, DiscardData(&num_bytes, true)); - - // Read a little. - num_bytes = 2u * sizeof(int32_t); - memset(buffer, 0xab, sizeof(buffer)); - ASSERT_EQ(MOJO_RESULT_OK, ReadData(buffer, &num_bytes, true)); - ASSERT_EQ(2u * sizeof(int32_t), num_bytes); - memset(expected_buffer, 0xab, sizeof(expected_buffer)); - Seq(400, 2, expected_buffer); - ASSERT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer))); - - // Discard the remaining element. - num_bytes = 1u * sizeof(int32_t); - ASSERT_EQ(MOJO_RESULT_OK, DiscardData(&num_bytes, true)); - ASSERT_EQ(1u * sizeof(int32_t), num_bytes); - - // Empty again. - num_bytes = ~0u; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - ASSERT_EQ(0u, num_bytes); -} - -// Tests that |ProducerWriteData()| and |ConsumerReadData()| writes and reads, -// respectively, as much as possible, even if it may have to "wrap around" the -// internal circular buffer. (Note that the two-phase write and read need not do -// this.) -TEST_F(DataPipeTest, 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|. - }; - - ASSERT_EQ(MOJO_RESULT_OK, Create(&options)); - MojoHandleSignalsState hss; - - // Write 20 bytes. - uint32_t num_bytes = 20u; - ASSERT_EQ(MOJO_RESULT_OK, WriteData(&test_data[0], &num_bytes, true)); - ASSERT_EQ(20u, num_bytes); - - // Wait for data. - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_TRUE(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // Read 10 bytes. - unsigned char read_buffer[1000] = {0}; - num_bytes = 10u; - ASSERT_EQ(MOJO_RESULT_OK, ReadData(read_buffer, &num_bytes, true)); - ASSERT_EQ(10u, num_bytes); - ASSERT_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.) - void* write_buffer_ptr = nullptr; - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, - BeginWriteData(&write_buffer_ptr, &num_bytes, false)); - EXPECT_TRUE(write_buffer_ptr); - ASSERT_EQ(80u, num_bytes); - ASSERT_EQ(MOJO_RESULT_OK, EndWriteData(0)); - - size_t total_num_bytes = 0; - while (total_num_bytes < 90) { - // Wait to write. - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, &hss)); - ASSERT_EQ(hss.satisfied_signals, MOJO_HANDLE_SIGNAL_WRITABLE); - ASSERT_EQ(hss.satisfiable_signals, - MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED); - - // Write as much as we can. - num_bytes = 100; - ASSERT_EQ(MOJO_RESULT_OK, - WriteData(&test_data[20 + total_num_bytes], &num_bytes, false)); - total_num_bytes += num_bytes; - } - - ASSERT_EQ(90u, total_num_bytes); - - num_bytes = 0; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - ASSERT_EQ(100u, 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.) - const void* read_buffer_ptr = nullptr; - num_bytes = 0; - ASSERT_EQ(MOJO_RESULT_OK, BeginReadData(&read_buffer_ptr, &num_bytes, false)); - EXPECT_TRUE(read_buffer_ptr); - ASSERT_EQ(90u, num_bytes); - ASSERT_EQ(MOJO_RESULT_OK, EndReadData(0)); - - // Read as much as possible. We should read 100 bytes. - num_bytes = static_cast<uint32_t>(arraysize(read_buffer) * - sizeof(read_buffer[0])); - memset(read_buffer, 0, num_bytes); - ASSERT_EQ(MOJO_RESULT_OK, ReadData(read_buffer, &num_bytes)); - ASSERT_EQ(100u, num_bytes); - ASSERT_EQ(0, memcmp(read_buffer, &test_data[10], 100u)); -} - -// Tests the behavior of writing (simple and two-phase), closing the producer, -// then reading (simple and two-phase). -TEST_F(DataPipeTest, WriteCloseProducerRead) { - 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|. - }; - ASSERT_EQ(MOJO_RESULT_OK, Create(&options)); - - // Write some data, so we'll have something to read. - uint32_t num_bytes = kTestDataSize; - ASSERT_EQ(MOJO_RESULT_OK, WriteData(kTestData, &num_bytes, false)); - ASSERT_EQ(kTestDataSize, num_bytes); - - // Write it again, so we'll have something left over. - num_bytes = kTestDataSize; - ASSERT_EQ(MOJO_RESULT_OK, WriteData(kTestData, &num_bytes, false)); - ASSERT_EQ(kTestDataSize, num_bytes); - - // Start two-phase write. - void* write_buffer_ptr = nullptr; - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, - BeginWriteData(&write_buffer_ptr, &num_bytes, false)); - EXPECT_TRUE(write_buffer_ptr); - EXPECT_GT(num_bytes, 0u); - - // TODO(vtl): (See corresponding TODO in TwoPhaseAllOrNone.) - for (size_t i = 0; i < kMaxPoll; i++) { - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - if (num_bytes >= 2u * kTestDataSize) - break; - - test::Sleep(test::EpsilonDeadline()); - } - ASSERT_EQ(2u * kTestDataSize, num_bytes); - - // Start two-phase read. - const void* read_buffer_ptr = nullptr; - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, - BeginReadData(&read_buffer_ptr, &num_bytes)); - EXPECT_TRUE(read_buffer_ptr); - ASSERT_EQ(2u * kTestDataSize, num_bytes); - - // Close the producer. - CloseProducer(); - - // The consumer can finish its two-phase read. - ASSERT_EQ(0, memcmp(read_buffer_ptr, kTestData, kTestDataSize)); - ASSERT_EQ(MOJO_RESULT_OK, EndReadData(kTestDataSize)); - - // And start another. - read_buffer_ptr = nullptr; - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, - BeginReadData(&read_buffer_ptr, &num_bytes)); - EXPECT_TRUE(read_buffer_ptr); - ASSERT_EQ(kTestDataSize, num_bytes); -} - - -// Tests the behavior of interrupting a two-phase read and write by closing the -// consumer. -TEST_F(DataPipeTest, TwoPhaseWriteReadCloseConsumer) { - 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|. - }; - ASSERT_EQ(MOJO_RESULT_OK, Create(&options)); - MojoHandleSignalsState hss; - - // Write some data, so we'll have something to read. - uint32_t num_bytes = kTestDataSize; - ASSERT_EQ(MOJO_RESULT_OK, WriteData(kTestData, &num_bytes)); - ASSERT_EQ(kTestDataSize, num_bytes); - - // Start two-phase write. - void* write_buffer_ptr = nullptr; - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, BeginWriteData(&write_buffer_ptr, &num_bytes)); - EXPECT_TRUE(write_buffer_ptr); - ASSERT_GT(num_bytes, kTestDataSize); - - // Wait for data. - // TODO(vtl): (See corresponding TODO in AllOrNone.) - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // Start two-phase read. - const void* read_buffer_ptr = nullptr; - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, BeginReadData(&read_buffer_ptr, &num_bytes)); - EXPECT_TRUE(read_buffer_ptr); - ASSERT_EQ(kTestDataSize, num_bytes); - - // Close the consumer. - CloseConsumer(); - - // Wait for producer to know that the consumer is closed. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(producer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - - // 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. - ASSERT_EQ(MOJO_RESULT_OK, EndWriteData(kTestDataSize)); - - // But trying to write should result in failure. - num_bytes = kTestDataSize; - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, WriteData(kTestData, &num_bytes)); - - // As will trying to start another two-phase write. - write_buffer_ptr = nullptr; - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - BeginWriteData(&write_buffer_ptr, &num_bytes)); -} - -// Tests the behavior of "interrupting" a two-phase write by closing both the -// producer and the consumer. -TEST_F(DataPipeTest, TwoPhaseWriteCloseBoth) { - const uint32_t kTestDataSize = 15u; - - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - 1u, // |element_num_bytes|. - 1000u // |capacity_num_bytes|. - }; - ASSERT_EQ(MOJO_RESULT_OK, Create(&options)); - - // Start two-phase write. - void* write_buffer_ptr = nullptr; - uint32_t num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, BeginWriteData(&write_buffer_ptr, &num_bytes)); - EXPECT_TRUE(write_buffer_ptr); - ASSERT_GT(num_bytes, kTestDataSize); -} - -// Tests the behavior of writing, closing the producer, and then reading (with -// and without data remaining). -TEST_F(DataPipeTest, WriteCloseProducerReadNoData) { - 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|. - }; - ASSERT_EQ(MOJO_RESULT_OK, Create(&options)); - MojoHandleSignalsState hss; - - // Write some data, so we'll have something to read. - uint32_t num_bytes = kTestDataSize; - ASSERT_EQ(MOJO_RESULT_OK, WriteData(kTestData, &num_bytes)); - ASSERT_EQ(kTestDataSize, num_bytes); - - // Close the producer. - CloseProducer(); - - // Wait. (Note that once the consumer knows that the producer is closed, it - // must also know about all the data that was sent.) - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // Peek that data. - char buffer[1000]; - num_bytes = static_cast<uint32_t>(sizeof(buffer)); - ASSERT_EQ(MOJO_RESULT_OK, ReadData(buffer, &num_bytes, false, true)); - ASSERT_EQ(kTestDataSize, num_bytes); - ASSERT_EQ(0, memcmp(buffer, kTestData, kTestDataSize)); - - // Read that data. - memset(buffer, 0, 1000); - num_bytes = static_cast<uint32_t>(sizeof(buffer)); - ASSERT_EQ(MOJO_RESULT_OK, ReadData(buffer, &num_bytes)); - ASSERT_EQ(kTestDataSize, num_bytes); - ASSERT_EQ(0, memcmp(buffer, kTestData, kTestDataSize)); - - // A second read should fail. - num_bytes = static_cast<uint32_t>(sizeof(buffer)); - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, ReadData(buffer, &num_bytes)); - - // A two-phase read should also fail. - const void* read_buffer_ptr = nullptr; - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - BeginReadData(&read_buffer_ptr, &num_bytes)); - - // Ditto for discard. - num_bytes = 10u; - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, DiscardData(&num_bytes)); -} - -// Test that during a two phase read the memory stays valid even if more data -// comes in. -TEST_F(DataPipeTest, TwoPhaseReadMemoryStable) { - 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|. - }; - ASSERT_EQ(MOJO_RESULT_OK, Create(&options)); - MojoHandleSignalsState hss; - - // Write some data. - uint32_t num_bytes = kTestDataSize; - ASSERT_EQ(MOJO_RESULT_OK, WriteData(kTestData, &num_bytes)); - ASSERT_EQ(kTestDataSize, num_bytes); - - // Wait for the data. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // Begin a two-phase read. - const void* read_buffer_ptr = nullptr; - uint32_t read_buffer_size = 0u; - ASSERT_EQ(MOJO_RESULT_OK, BeginReadData(&read_buffer_ptr, &read_buffer_size)); - - // Write more data. - const char kExtraData[] = "bye world"; - const uint32_t kExtraDataSize = static_cast<uint32_t>(sizeof(kExtraData)); - num_bytes = kExtraDataSize; - ASSERT_EQ(MOJO_RESULT_OK, WriteData(kExtraData, &num_bytes)); - ASSERT_EQ(kExtraDataSize, num_bytes); - - // Close the producer. - CloseProducer(); - - // Wait. (Note that once the consumer knows that the producer is closed, it - // must also have received the extra data). - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // Read the two phase memory to check it's still valid. - ASSERT_EQ(0, memcmp(read_buffer_ptr, kTestData, kTestDataSize)); - EndReadData(read_buffer_size); -} - -// Test that two-phase reads/writes behave correctly when given invalid -// arguments. -TEST_F(DataPipeTest, 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|. - }; - ASSERT_EQ(MOJO_RESULT_OK, Create(&options)); - MojoHandleSignalsState hss; - - // No data. - uint32_t num_bytes = 1000u; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - ASSERT_EQ(0u, num_bytes); - - // Try "ending" a two-phase write when one isn't active. - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - EndWriteData(1u * sizeof(int32_t))); - - // Wait a bit, to make sure that if a signal were (incorrectly) sent, it'd - // have time to propagate. - test::Sleep(test::EpsilonDeadline()); - - // Still no data. - num_bytes = 1000u; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - ASSERT_EQ(0u, num_bytes); - - // Try ending a two-phase write with an invalid amount (too much). - num_bytes = 0u; - void* write_ptr = nullptr; - ASSERT_EQ(MOJO_RESULT_OK, BeginWriteData(&write_ptr, &num_bytes)); - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - EndWriteData(num_bytes + static_cast<uint32_t>(sizeof(int32_t)))); - - // But the two-phase write still ended. - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, EndWriteData(0u)); - - // Wait a bit (as above). - test::Sleep(test::EpsilonDeadline()); - - // Still no data. - num_bytes = 1000u; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - ASSERT_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; - ASSERT_EQ(MOJO_RESULT_OK, BeginWriteData(&write_ptr, &num_bytes)); - EXPECT_GE(num_bytes, 1u); - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, EndWriteData(1u)); - - // But the two-phase write still ended. - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, EndWriteData(0u)); - - // Wait a bit (as above). - test::Sleep(test::EpsilonDeadline()); - - // Still no data. - num_bytes = 1000u; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - ASSERT_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); - ASSERT_EQ(MOJO_RESULT_OK, WriteData(&element, &num_bytes)); - - // Wait for data. - // TODO(vtl): (See corresponding TODO in AllOrNone.) - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // One element available. - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - ASSERT_EQ(1u * sizeof(int32_t), num_bytes); - - // Try "ending" a two-phase read when one isn't active. - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, EndReadData(1u * sizeof(int32_t))); - - // Still one element available. - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - ASSERT_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; - ASSERT_EQ(MOJO_RESULT_OK, BeginReadData(&read_ptr, &num_bytes)); - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - EndReadData(num_bytes + static_cast<uint32_t>(sizeof(int32_t)))); - - // Still one element available. - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - ASSERT_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; - ASSERT_EQ(MOJO_RESULT_OK, BeginReadData(&read_ptr, &num_bytes)); - ASSERT_EQ(1u * sizeof(int32_t), num_bytes); - ASSERT_EQ(123, static_cast<const int32_t*>(read_ptr)[0]); - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, EndReadData(1u)); - - // Still one element available. - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); - ASSERT_EQ(1u * sizeof(int32_t), num_bytes); -} - -// Test that a producer can be sent over a MP. -TEST_F(DataPipeTest, SendProducer) { - 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|. - }; - ASSERT_EQ(MOJO_RESULT_OK, Create(&options)); - MojoHandleSignalsState hss; - - // Write some data. - uint32_t num_bytes = kTestDataSize; - ASSERT_EQ(MOJO_RESULT_OK, WriteData(kTestData, &num_bytes)); - ASSERT_EQ(kTestDataSize, num_bytes); - - // Wait for the data. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // Check the data. - const void* read_buffer = nullptr; - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, - BeginReadData(&read_buffer, &num_bytes, false)); - ASSERT_EQ(0, memcmp(read_buffer, kTestData, kTestDataSize)); - EndReadData(num_bytes); - - // Now send the producer over a MP so that it's serialized. - MojoHandle pipe0, pipe1; - ASSERT_EQ(MOJO_RESULT_OK, - MojoCreateMessagePipe(nullptr, &pipe0, &pipe1)); - - ASSERT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(pipe0, nullptr, 0, &producer_, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - producer_ = MOJO_HANDLE_INVALID; - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(pipe1, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - uint32_t num_handles = 1; - ASSERT_EQ(MOJO_RESULT_OK, - MojoReadMessage(pipe1, nullptr, 0, &producer_, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_EQ(num_handles, 1u); - - // Write more data. - const char kExtraData[] = "bye world"; - const uint32_t kExtraDataSize = static_cast<uint32_t>(sizeof(kExtraData)); - num_bytes = kExtraDataSize; - ASSERT_EQ(MOJO_RESULT_OK, WriteData(kExtraData, &num_bytes)); - ASSERT_EQ(kExtraDataSize, num_bytes); - - // Wait for it. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - hss.satisfiable_signals); - - // Check the second write. - num_bytes = 0u; - ASSERT_EQ(MOJO_RESULT_OK, - BeginReadData(&read_buffer, &num_bytes, false)); - ASSERT_EQ(0, memcmp(read_buffer, kExtraData, kExtraDataSize)); - EndReadData(num_bytes); - - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(pipe0)); - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(pipe1)); -} - -// Ensures that if a data pipe consumer whose producer has closed is passed over -// a message pipe, the deserialized dispatcher is also marked as having a closed -// peer. -TEST_F(DataPipeTest, ConsumerWithClosedProducerSent) { - 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|. - }; - - ASSERT_EQ(MOJO_RESULT_OK, Create(&options)); - - // We can write to a data pipe handle immediately. - int32_t data = 123; - uint32_t num_bytes = sizeof(data); - ASSERT_EQ(MOJO_RESULT_OK, WriteData(&data, &num_bytes)); - ASSERT_EQ(MOJO_RESULT_OK, CloseProducer()); - - // Now wait for the other side to become readable and to see the peer closed. - MojoHandleSignalsState state; - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &state)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - state.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - state.satisfiable_signals); - - // Now send the consumer over a MP so that it's serialized. - MojoHandle pipe0, pipe1; - ASSERT_EQ(MOJO_RESULT_OK, - MojoCreateMessagePipe(nullptr, &pipe0, &pipe1)); - - ASSERT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(pipe0, nullptr, 0, &consumer_, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - consumer_ = MOJO_HANDLE_INVALID; - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(pipe1, MOJO_HANDLE_SIGNAL_READABLE, &state)); - uint32_t num_handles = 1; - ASSERT_EQ(MOJO_RESULT_OK, - MojoReadMessage(pipe1, nullptr, 0, &consumer_, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_EQ(num_handles, 1u); - - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &state)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - state.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | - MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - state.satisfiable_signals); - - int32_t read_data; - ASSERT_EQ(MOJO_RESULT_OK, ReadData(&read_data, &num_bytes)); - ASSERT_EQ(sizeof(read_data), num_bytes); - ASSERT_EQ(data, read_data); - - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(pipe0)); - ASSERT_EQ(MOJO_RESULT_OK, MojoClose(pipe1)); -} - -bool WriteAllData(MojoHandle producer, - const void* elements, - uint32_t num_bytes) { - for (size_t i = 0; i < kMaxPoll; i++) { - // Write as much data as we can. - uint32_t write_bytes = num_bytes; - MojoResult result = MojoWriteData(producer, elements, &write_bytes, - MOJO_WRITE_DATA_FLAG_NONE); - if (result == MOJO_RESULT_OK) { - num_bytes -= write_bytes; - elements = static_cast<const uint8_t*>(elements) + write_bytes; - if (num_bytes == 0) - return true; - } else { - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, result); - } - - MojoHandleSignalsState hss = MojoHandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_OK, test::MojoTestBase::WaitForSignals( - producer, MOJO_HANDLE_SIGNAL_WRITABLE, &hss)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - } - - return false; -} - -// If |expect_empty| is true, expect |consumer| to be empty after reading. -bool ReadAllData(MojoHandle consumer, - void* elements, - uint32_t num_bytes, - bool expect_empty) { - for (size_t i = 0; i < kMaxPoll; i++) { - // Read as much data as we can. - uint32_t read_bytes = num_bytes; - MojoResult result = - MojoReadData(consumer, elements, &read_bytes, MOJO_READ_DATA_FLAG_NONE); - if (result == MOJO_RESULT_OK) { - num_bytes -= read_bytes; - elements = static_cast<uint8_t*>(elements) + read_bytes; - if (num_bytes == 0) { - if (expect_empty) { - // Expect no more data. - test::Sleep(test::TinyDeadline()); - MojoReadData(consumer, nullptr, &num_bytes, - MOJO_READ_DATA_FLAG_QUERY); - EXPECT_EQ(0u, num_bytes); - } - return true; - } - } else { - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, result); - } - - MojoHandleSignalsState hss = MojoHandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_OK, test::MojoTestBase::WaitForSignals( - consumer, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - // Peer could have become closed while we're still waiting for data. - EXPECT_TRUE(MOJO_HANDLE_SIGNAL_READABLE & hss.satisfied_signals); - EXPECT_TRUE(hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE); - EXPECT_TRUE(hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_PEER_CLOSED); - } - - return num_bytes == 0; -} - -#if !defined(OS_IOS) - -TEST_F(DataPipeTest, Multiprocess) { - const uint32_t kTestDataSize = - static_cast<uint32_t>(sizeof(kMultiprocessTestData)); - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - 1, // |element_num_bytes|. - kMultiprocessCapacity // |capacity_num_bytes|. - }; - ASSERT_EQ(MOJO_RESULT_OK, Create(&options)); - - RUN_CHILD_ON_PIPE(MultiprocessClient, server_mp) - // Send some data before serialising and sending the data pipe over. - // This is the first write so we don't need to use WriteAllData. - uint32_t num_bytes = kTestDataSize; - ASSERT_EQ(MOJO_RESULT_OK, WriteData(kMultiprocessTestData, &num_bytes, - MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)); - ASSERT_EQ(kTestDataSize, num_bytes); - - // Send child process the data pipe. - ASSERT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(server_mp, nullptr, 0, &consumer_, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Send a bunch of data of varying sizes. - uint8_t buffer[100]; - int seq = 0; - for (int i = 0; i < kMultiprocessMaxIter; ++i) { - for (uint32_t size = 1; size <= kMultiprocessCapacity; size++) { - for (unsigned int j = 0; j < size; ++j) - buffer[j] = seq + j; - EXPECT_TRUE(WriteAllData(producer_, buffer, size)); - seq += size; - } - } - - // Write the test string in again. - ASSERT_TRUE(WriteAllData(producer_, kMultiprocessTestData, kTestDataSize)); - - // Swap ends. - ASSERT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(server_mp, nullptr, 0, &producer_, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Receive the consumer from the other side. - producer_ = MOJO_HANDLE_INVALID; - MojoHandleSignalsState hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(server_mp, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - MojoHandle handles[2]; - uint32_t num_handles = arraysize(handles); - ASSERT_EQ(MOJO_RESULT_OK, - MojoReadMessage(server_mp, nullptr, 0, handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_EQ(1u, num_handles); - consumer_ = handles[0]; - - // Read the test string twice. Once for when we sent it, and once for the - // other end sending it. - for (int i = 0; i < 2; ++i) { - EXPECT_TRUE(ReadAllData(consumer_, buffer, kTestDataSize, i == 1)); - EXPECT_EQ(0, memcmp(buffer, kMultiprocessTestData, kTestDataSize)); - } - - WriteMessage(server_mp, "quit"); - - // Don't have to close the consumer here because it will be done for us. - END_CHILD() -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MultiprocessClient, DataPipeTest, client_mp) { - const uint32_t kTestDataSize = - static_cast<uint32_t>(sizeof(kMultiprocessTestData)); - - // Receive the data pipe from the other side. - MojoHandle consumer = MOJO_HANDLE_INVALID; - MojoHandleSignalsState hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(client_mp, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - MojoHandle handles[2]; - uint32_t num_handles = arraysize(handles); - ASSERT_EQ(MOJO_RESULT_OK, - MojoReadMessage(client_mp, nullptr, 0, handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_EQ(1u, num_handles); - consumer = handles[0]; - - // Read the initial string that was sent. - int32_t buffer[100]; - EXPECT_TRUE(ReadAllData(consumer, buffer, kTestDataSize, false)); - EXPECT_EQ(0, memcmp(buffer, kMultiprocessTestData, kTestDataSize)); - - // Receive the main data and check it is correct. - int seq = 0; - uint8_t expected_buffer[100]; - for (int i = 0; i < kMultiprocessMaxIter; ++i) { - for (uint32_t size = 1; size <= kMultiprocessCapacity; ++size) { - for (unsigned int j = 0; j < size; ++j) - expected_buffer[j] = seq + j; - EXPECT_TRUE(ReadAllData(consumer, buffer, size, false)); - EXPECT_EQ(0, memcmp(buffer, expected_buffer, size)); - - seq += size; - } - } - - // Swap ends. - ASSERT_EQ(MOJO_RESULT_OK, MojoWriteMessage(client_mp, nullptr, 0, &consumer, - 1, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Receive the producer from the other side. - MojoHandle producer = MOJO_HANDLE_INVALID; - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(client_mp, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - num_handles = arraysize(handles); - ASSERT_EQ(MOJO_RESULT_OK, - MojoReadMessage(client_mp, nullptr, 0, handles, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_EQ(1u, num_handles); - producer = handles[0]; - - // Write the test string one more time. - EXPECT_TRUE(WriteAllData(producer, kMultiprocessTestData, kTestDataSize)); - - // We swapped ends, so close the producer. - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); - - // Wait to receive a "quit" message before exiting. - EXPECT_EQ("quit", ReadMessage(client_mp)); -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(WriteAndCloseProducer, DataPipeTest, h) { - MojoHandle p; - std::string message = ReadMessageWithHandles(h, &p, 1); - - // Write some data to the producer and close it. - uint32_t num_bytes = static_cast<uint32_t>(message.size()); - EXPECT_EQ(MOJO_RESULT_OK, MojoWriteData(p, message.data(), &num_bytes, - MOJO_WRITE_DATA_FLAG_NONE)); - EXPECT_EQ(num_bytes, static_cast<uint32_t>(message.size())); - - // Close the producer before quitting. - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(p)); - - // Wait for a quit message. - EXPECT_EQ("quit", ReadMessage(h)); -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReadAndCloseConsumer, DataPipeTest, h) { - MojoHandle c; - std::string expected_message = ReadMessageWithHandles(h, &c, 1); - - // Wait for the consumer to become readable. - EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(c, MOJO_HANDLE_SIGNAL_READABLE)); - - // Drain the consumer and expect to find the given message. - uint32_t num_bytes = static_cast<uint32_t>(expected_message.size()); - std::vector<char> bytes(expected_message.size()); - EXPECT_EQ(MOJO_RESULT_OK, MojoReadData(c, bytes.data(), &num_bytes, - MOJO_READ_DATA_FLAG_NONE)); - EXPECT_EQ(num_bytes, static_cast<uint32_t>(bytes.size())); - - std::string message(bytes.data(), bytes.size()); - EXPECT_EQ(expected_message, message); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c)); - - // Wait for a quit message. - EXPECT_EQ("quit", ReadMessage(h)); -} - -TEST_F(DataPipeTest, SendConsumerAndCloseProducer) { - // Create a new data pipe. - MojoHandle p, c; - EXPECT_EQ(MOJO_RESULT_OK, MojoCreateDataPipe(nullptr, &p ,&c)); - - RUN_CHILD_ON_PIPE(WriteAndCloseProducer, producer_client) - RUN_CHILD_ON_PIPE(ReadAndCloseConsumer, consumer_client) - const std::string kMessage = "Hello, world!"; - WriteMessageWithHandles(producer_client, kMessage, &p, 1); - WriteMessageWithHandles(consumer_client, kMessage, &c, 1); - - WriteMessage(consumer_client, "quit"); - END_CHILD() - - WriteMessage(producer_client, "quit"); - END_CHILD() -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(CreateAndWrite, DataPipeTest, h) { - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - 1, // |element_num_bytes|. - kMultiprocessCapacity // |capacity_num_bytes|. - }; - - MojoHandle p, c; - ASSERT_EQ(MOJO_RESULT_OK, MojoCreateDataPipe(&options, &p, &c)); - - const std::string kMessage = "Hello, world!"; - WriteMessageWithHandles(h, kMessage, &c, 1); - - // Write some data to the producer and close it. - uint32_t num_bytes = static_cast<uint32_t>(kMessage.size()); - EXPECT_EQ(MOJO_RESULT_OK, MojoWriteData(p, kMessage.data(), &num_bytes, - MOJO_WRITE_DATA_FLAG_NONE)); - EXPECT_EQ(num_bytes, static_cast<uint32_t>(kMessage.size())); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(p)); - - // Wait for a quit message. - EXPECT_EQ("quit", ReadMessage(h)); -} - -TEST_F(DataPipeTest, CreateInChild) { - RUN_CHILD_ON_PIPE(CreateAndWrite, child) - MojoHandle c; - std::string expected_message = ReadMessageWithHandles(child, &c, 1); - - // Wait for the consumer to become readable. - EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(c, MOJO_HANDLE_SIGNAL_READABLE)); - - // Drain the consumer and expect to find the given message. - uint32_t num_bytes = static_cast<uint32_t>(expected_message.size()); - std::vector<char> bytes(expected_message.size()); - EXPECT_EQ(MOJO_RESULT_OK, MojoReadData(c, bytes.data(), &num_bytes, - MOJO_READ_DATA_FLAG_NONE)); - EXPECT_EQ(num_bytes, static_cast<uint32_t>(bytes.size())); - - std::string message(bytes.data(), bytes.size()); - EXPECT_EQ(expected_message, message); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c)); - WriteMessage(child, "quit"); - END_CHILD() -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(DataPipeStatusChangeInTransitClient, - DataPipeTest, parent) { - // This test verifies that peer closure is detectable through various - // mechanisms when it races with handle transfer. - - MojoHandle handles[6]; - EXPECT_EQ("o_O", ReadMessageWithHandles(parent, handles, 6)); - MojoHandle* producers = &handles[0]; - MojoHandle* consumers = &handles[3]; - - // Wait on producer 0 - EXPECT_EQ(MOJO_RESULT_OK, - WaitForSignals(producers[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED)); - - // Wait on consumer 0 - EXPECT_EQ(MOJO_RESULT_OK, - WaitForSignals(consumers[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED)); - - base::MessageLoop message_loop; - - // Wait on producer 1 and consumer 1 using SimpleWatchers. - { - base::RunLoop run_loop; - int count = 0; - auto callback = base::Bind( - [] (base::RunLoop* loop, int* count, MojoResult result) { - EXPECT_EQ(MOJO_RESULT_OK, result); - if (++*count == 2) - loop->Quit(); - }, - &run_loop, &count); - SimpleWatcher producer_watcher(FROM_HERE, - SimpleWatcher::ArmingPolicy::AUTOMATIC); - SimpleWatcher consumer_watcher(FROM_HERE, - SimpleWatcher::ArmingPolicy::AUTOMATIC); - producer_watcher.Watch(Handle(producers[1]), MOJO_HANDLE_SIGNAL_PEER_CLOSED, - callback); - consumer_watcher.Watch(Handle(consumers[1]), MOJO_HANDLE_SIGNAL_PEER_CLOSED, - callback); - run_loop.Run(); - EXPECT_EQ(2, count); - } - - // Wait on producer 2 by polling with MojoWriteData. - MojoResult result; - do { - uint32_t num_bytes = 0; - result = MojoWriteData( - producers[2], nullptr, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE); - } while (result == MOJO_RESULT_OK); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); - - // Wait on consumer 2 by polling with MojoReadData. - do { - char byte; - uint32_t num_bytes = 1; - result = MojoReadData( - consumers[2], &byte, &num_bytes, MOJO_READ_DATA_FLAG_NONE); - } while (result == MOJO_RESULT_SHOULD_WAIT); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); - - for (size_t i = 0; i < 6; ++i) - CloseHandle(handles[i]); -} - -TEST_F(DataPipeTest, StatusChangeInTransit) { - MojoHandle producers[6]; - MojoHandle consumers[6]; - for (size_t i = 0; i < 6; ++i) - CreateDataPipe(&producers[i], &consumers[i], 1); - - RUN_CHILD_ON_PIPE(DataPipeStatusChangeInTransitClient, child) - MojoHandle handles[] = { producers[0], producers[1], producers[2], - consumers[3], consumers[4], consumers[5] }; - - // Send 3 producers and 3 consumers, and let their transfer race with their - // peers' closure. - WriteMessageWithHandles(child, "o_O", handles, 6); - - for (size_t i = 0; i < 3; ++i) - CloseHandle(consumers[i]); - for (size_t i = 3; i < 6; ++i) - CloseHandle(producers[i]); - END_CHILD() -} - -#endif // !defined(OS_IOS) - -} // namespace -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/dispatcher.cc b/mojo/edk/system/dispatcher.cc deleted file mode 100644 index 7cdbe91..0000000 --- a/mojo/edk/system/dispatcher.cc +++ /dev/null @@ -1,198 +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/data_pipe_consumer_dispatcher.h" -#include "mojo/edk/system/data_pipe_producer_dispatcher.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 edk { - -Dispatcher::DispatcherInTransit::DispatcherInTransit() {} - -Dispatcher::DispatcherInTransit::DispatcherInTransit( - const DispatcherInTransit& other) = default; - -Dispatcher::DispatcherInTransit::~DispatcherInTransit() {} - -MojoResult Dispatcher::WatchDispatcher(scoped_refptr<Dispatcher> dispatcher, - MojoHandleSignals signals, - uintptr_t context) { - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::CancelWatch(uintptr_t context) { - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::Arm(uint32_t* num_ready_contexts, - uintptr_t* ready_contexts, - MojoResult* ready_results, - MojoHandleSignalsState* ready_signals_states) { - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::WriteMessage(std::unique_ptr<MessageForTransit> message, - MojoWriteMessageFlags flags) { - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::ReadMessage(std::unique_ptr<MessageForTransit>* message, - uint32_t* num_bytes, - MojoHandle* handles, - uint32_t* num_handles, - MojoReadMessageFlags flags, - bool read_any_size) { - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::DuplicateBufferHandle( - const MojoDuplicateBufferHandleOptions* options, - scoped_refptr<Dispatcher>* new_dispatcher) { - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::MapBuffer( - uint64_t offset, - uint64_t num_bytes, - MojoMapBufferFlags flags, - std::unique_ptr<PlatformSharedBufferMapping>* mapping) { - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::ReadData(void* elements, - uint32_t* num_bytes, - MojoReadDataFlags flags) { - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::BeginReadData(const void** buffer, - uint32_t* buffer_num_bytes, - MojoReadDataFlags flags) { - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::EndReadData(uint32_t num_bytes_read) { - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::WriteData(const void* elements, - uint32_t* num_bytes, - MojoWriteDataFlags flags) { - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::BeginWriteData(void** buffer, - uint32_t* buffer_num_bytes, - MojoWriteDataFlags flags) { - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::EndWriteData(uint32_t num_bytes_written) { - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::AddWaitingDispatcher( - const scoped_refptr<Dispatcher>& dispatcher, - MojoHandleSignals signals, - uintptr_t context) { - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::RemoveWaitingDispatcher( - const scoped_refptr<Dispatcher>& dispatcher) { - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::GetReadyDispatchers(uint32_t* count, - DispatcherVector* dispatchers, - MojoResult* results, - uintptr_t* contexts) { - return MOJO_RESULT_INVALID_ARGUMENT; -} - -HandleSignalsState Dispatcher::GetHandleSignalsState() const { - return HandleSignalsState(); -} - -MojoResult Dispatcher::AddWatcherRef( - const scoped_refptr<WatcherDispatcher>& watcher, - uintptr_t context) { - return MOJO_RESULT_INVALID_ARGUMENT; -} - -MojoResult Dispatcher::RemoveWatcherRef(WatcherDispatcher* watcher, - uintptr_t context) { - return MOJO_RESULT_INVALID_ARGUMENT; -} - -void Dispatcher::StartSerialize(uint32_t* num_bytes, - uint32_t* num_ports, - uint32_t* num_platform_handles) { - *num_bytes = 0; - *num_ports = 0; - *num_platform_handles = 0; -} - -bool Dispatcher::EndSerialize(void* destination, - ports::PortName* ports, - PlatformHandle* handles) { - LOG(ERROR) << "Attempting to serialize a non-transferrable dispatcher."; - return true; -} - -bool Dispatcher::BeginTransit() { return true; } - -void Dispatcher::CompleteTransitAndClose() {} - -void Dispatcher::CancelTransit() {} - -// static -scoped_refptr<Dispatcher> Dispatcher::Deserialize( - Type type, - const void* bytes, - size_t num_bytes, - const ports::PortName* ports, - size_t num_ports, - PlatformHandle* platform_handles, - size_t num_platform_handles) { - switch (type) { - case Type::MESSAGE_PIPE: - return MessagePipeDispatcher::Deserialize( - bytes, num_bytes, ports, num_ports, platform_handles, - num_platform_handles); - case Type::SHARED_BUFFER: - return SharedBufferDispatcher::Deserialize( - bytes, num_bytes, ports, num_ports, platform_handles, - num_platform_handles); - case Type::DATA_PIPE_CONSUMER: - return DataPipeConsumerDispatcher::Deserialize( - bytes, num_bytes, ports, num_ports, platform_handles, - num_platform_handles); - case Type::DATA_PIPE_PRODUCER: - return DataPipeProducerDispatcher::Deserialize( - bytes, num_bytes, ports, num_ports, platform_handles, - num_platform_handles); - case Type::PLATFORM_HANDLE: - return PlatformHandleDispatcher::Deserialize( - bytes, num_bytes, ports, num_ports, platform_handles, - num_platform_handles); - default: - LOG(ERROR) << "Deserializing invalid dispatcher type."; - return nullptr; - } -} - -Dispatcher::Dispatcher() {} - -Dispatcher::~Dispatcher() {} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/dispatcher.h b/mojo/edk/system/dispatcher.h deleted file mode 100644 index db1f1f1..0000000 --- a/mojo/edk/system/dispatcher.h +++ /dev/null @@ -1,245 +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 <memory> -#include <ostream> -#include <vector> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/embedder/platform_handle.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/system/handle_signals_state.h" -#include "mojo/edk/system/ports/name.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/edk/system/watch.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 edk { - -class Dispatcher; -class MessageForTransit; - -using DispatcherVector = std::vector<scoped_refptr<Dispatcher>>; - -// A |Dispatcher| implements Mojo EDK calls that are associated with a -// particular MojoHandle. -class MOJO_SYSTEM_IMPL_EXPORT Dispatcher - : public base::RefCountedThreadSafe<Dispatcher> { - public: - struct DispatcherInTransit { - DispatcherInTransit(); - DispatcherInTransit(const DispatcherInTransit& other); - ~DispatcherInTransit(); - - scoped_refptr<Dispatcher> dispatcher; - MojoHandle local_handle; - }; - - enum class Type { - UNKNOWN = 0, - MESSAGE_PIPE, - DATA_PIPE_PRODUCER, - DATA_PIPE_CONSUMER, - SHARED_BUFFER, - WATCHER, - - // "Private" types (not exposed via the public interface): - PLATFORM_HANDLE = -1, - }; - - // All Dispatchers must minimally implement these methods. - - virtual Type GetType() const = 0; - virtual MojoResult Close() = 0; - - ///////////// Watcher API //////////////////// - - virtual MojoResult WatchDispatcher(scoped_refptr<Dispatcher> dispatcher, - MojoHandleSignals signals, - uintptr_t context); - virtual MojoResult CancelWatch(uintptr_t context); - virtual MojoResult Arm(uint32_t* num_ready_contexts, - uintptr_t* ready_contexts, - MojoResult* ready_results, - MojoHandleSignalsState* ready_signals_states); - - ///////////// Message pipe API ///////////// - - virtual MojoResult WriteMessage(std::unique_ptr<MessageForTransit> message, - MojoWriteMessageFlags flags); - - virtual MojoResult ReadMessage(std::unique_ptr<MessageForTransit>* message, - uint32_t* num_bytes, - MojoHandle* handles, - uint32_t* num_handles, - MojoReadMessageFlags flags, - bool read_any_size); - - ///////////// Shared buffer API ///////////// - - // |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). - virtual MojoResult DuplicateBufferHandle( - const MojoDuplicateBufferHandleOptions* options, - scoped_refptr<Dispatcher>* new_dispatcher); - - virtual MojoResult MapBuffer( - uint64_t offset, - uint64_t num_bytes, - MojoMapBufferFlags flags, - std::unique_ptr<PlatformSharedBufferMapping>* mapping); - - ///////////// Data pipe consumer API ///////////// - - virtual MojoResult ReadData(void* elements, - uint32_t* num_bytes, - MojoReadDataFlags flags); - - virtual MojoResult BeginReadData(const void** buffer, - uint32_t* buffer_num_bytes, - MojoReadDataFlags flags); - - virtual MojoResult EndReadData(uint32_t num_bytes_read); - - ///////////// Data pipe producer API ///////////// - - virtual MojoResult WriteData(const void* elements, - uint32_t* num_bytes, - MojoWriteDataFlags flags); - - virtual MojoResult BeginWriteData(void** buffer, - uint32_t* buffer_num_bytes, - MojoWriteDataFlags flags); - - virtual MojoResult EndWriteData(uint32_t num_bytes_written); - - ///////////// Wait set API ///////////// - - // Adds a dispatcher to wait on. When the dispatcher satisfies |signals|, it - // will be returned in the next call to |GetReadyDispatchers()|. If - // |dispatcher| has been added, it must be removed before adding again, - // otherwise |MOJO_RESULT_ALREADY_EXISTS| will be returned. - virtual MojoResult AddWaitingDispatcher( - const scoped_refptr<Dispatcher>& dispatcher, - MojoHandleSignals signals, - uintptr_t context); - - // Removes a dispatcher to wait on. If |dispatcher| has not been added, - // |MOJO_RESULT_NOT_FOUND| will be returned. - virtual MojoResult RemoveWaitingDispatcher( - const scoped_refptr<Dispatcher>& dispatcher); - - // Returns a set of ready dispatchers. |*count| is the maximum number of - // dispatchers to return, and will contain the number of dispatchers returned - // in |dispatchers| on completion. - virtual MojoResult GetReadyDispatchers(uint32_t* count, - DispatcherVector* dispatchers, - MojoResult* results, - uintptr_t* contexts); - - ///////////// General-purpose API for all handle types ///////// - - // 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. - virtual HandleSignalsState GetHandleSignalsState() const; - - // Adds a WatcherDispatcher reference to this dispatcher, to be notified of - // all subsequent changes to handle state including signal changes or closure. - // The reference is associated with a |context| for disambiguation of - // removals. - virtual MojoResult AddWatcherRef( - const scoped_refptr<WatcherDispatcher>& watcher, - uintptr_t context); - - // Removes a WatcherDispatcher reference from this dispatcher. - virtual MojoResult RemoveWatcherRef(WatcherDispatcher* watcher, - uintptr_t context); - - // Informs the caller of the total serialized size (in bytes) and the total - // number of platform handles and ports needed to transfer this dispatcher - // across a message pipe. - // - // Must eventually be followed by a call to EndSerializeAndClose(). Note that - // StartSerialize() and EndSerialize() are always called in sequence, and - // only between calls to BeginTransit() and either (but not both) - // CompleteTransitAndClose() or CancelTransit(). - // - // For this reason it is IMPERATIVE that the implementation ensure a - // consistent serializable state between BeginTransit() and - // CompleteTransitAndClose()/CancelTransit(). - virtual void StartSerialize(uint32_t* num_bytes, - uint32_t* num_ports, - uint32_t* num_platform_handles); - - // Serializes this dispatcher into |destination|, |ports|, and |handles|. - // Returns true iff successful, false otherwise. In either case the dispatcher - // will close. - // - // NOTE: Transit MAY still fail after this call returns. Implementations - // should not assume PlatformHandle ownership has transferred until - // CompleteTransitAndClose() is called. In other words, if CancelTransit() is - // called, the implementation should retain its PlatformHandles in working - // condition. - virtual bool EndSerialize(void* destination, - ports::PortName* ports, - PlatformHandle* handles); - - // Does whatever is necessary to begin transit of the dispatcher. This - // should return |true| if transit is OK, or false if the underlying resource - // is deemed busy by the implementation. - virtual bool BeginTransit(); - - // Does whatever is necessary to complete transit of the dispatcher, including - // closure. This is only called upon successfully transmitting an outgoing - // message containing this serialized dispatcher. - virtual void CompleteTransitAndClose(); - - // Does whatever is necessary to cancel transit of the dispatcher. The - // dispatcher should remain in a working state and resume normal operation. - virtual void CancelTransit(); - - // Deserializes a specific dispatcher type from an incoming message. - static scoped_refptr<Dispatcher> Deserialize( - Type type, - const void* bytes, - size_t num_bytes, - const ports::PortName* ports, - size_t num_ports, - PlatformHandle* platform_handles, - size_t num_platform_handles); - - protected: - friend class base::RefCountedThreadSafe<Dispatcher>; - - Dispatcher(); - virtual ~Dispatcher(); - - DISALLOW_COPY_AND_ASSIGN(Dispatcher); -}; - -// So logging macros and |DCHECK_EQ()|, etc. work. -MOJO_SYSTEM_IMPL_EXPORT inline std::ostream& operator<<(std::ostream& out, - Dispatcher::Type type) { - return out << static_cast<int>(type); -} - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_DISPATCHER_H_ diff --git a/mojo/edk/system/handle_signals_state.h b/mojo/edk/system/handle_signals_state.h deleted file mode 100644 index f241278..0000000 --- a/mojo/edk/system/handle_signals_state.h +++ /dev/null @@ -1,13 +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 "mojo/public/cpp/system/handle_signals_state.h" - -// TODO(rockot): Remove this header and use the C++ system library type -// directly inside the EDK. - -#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 b570793..0000000 --- a/mojo/edk/system/handle_table.cc +++ /dev/null @@ -1,135 +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 <stdint.h> - -#include <limits> - -namespace mojo { -namespace edk { - -HandleTable::HandleTable() {} - -HandleTable::~HandleTable() {} - -MojoHandle HandleTable::AddDispatcher(scoped_refptr<Dispatcher> dispatcher) { - // Oops, we're out of handles. - if (next_available_handle_ == MOJO_HANDLE_INVALID) - return MOJO_HANDLE_INVALID; - - MojoHandle handle = next_available_handle_++; - auto result = - handles_.insert(std::make_pair(handle, Entry(std::move(dispatcher)))); - DCHECK(result.second); - - return handle; -} - -bool HandleTable::AddDispatchersFromTransit( - const std::vector<Dispatcher::DispatcherInTransit>& dispatchers, - MojoHandle* handles) { - // Oops, we're out of handles. - if (next_available_handle_ == MOJO_HANDLE_INVALID) - return false; - - DCHECK_LE(dispatchers.size(), std::numeric_limits<uint32_t>::max()); - // If this insertion would cause handle overflow, we're out of handles. - if (next_available_handle_ + dispatchers.size() < next_available_handle_) - return false; - - for (size_t i = 0; i < dispatchers.size(); ++i) { - MojoHandle handle = next_available_handle_++; - auto result = handles_.insert( - std::make_pair(handle, Entry(dispatchers[i].dispatcher))); - DCHECK(result.second); - handles[i] = handle; - } - - return true; -} - -scoped_refptr<Dispatcher> HandleTable::GetDispatcher(MojoHandle handle) const { - auto it = handles_.find(handle); - if (it == handles_.end()) - return nullptr; - return it->second.dispatcher; -} - -MojoResult HandleTable::GetAndRemoveDispatcher( - MojoHandle handle, - scoped_refptr<Dispatcher>* dispatcher) { - auto it = handles_.find(handle); - if (it == handles_.end()) - return MOJO_RESULT_INVALID_ARGUMENT; - if (it->second.busy) - return MOJO_RESULT_BUSY; - - *dispatcher = std::move(it->second.dispatcher); - handles_.erase(it); - return MOJO_RESULT_OK; -} - -MojoResult HandleTable::BeginTransit( - const MojoHandle* handles, - uint32_t num_handles, - std::vector<Dispatcher::DispatcherInTransit>* dispatchers) { - dispatchers->clear(); - dispatchers->reserve(num_handles); - for (size_t i = 0; i < num_handles; ++i) { - auto it = handles_.find(handles[i]); - if (it == handles_.end()) - return MOJO_RESULT_INVALID_ARGUMENT; - if (it->second.busy) - return MOJO_RESULT_BUSY; - - Dispatcher::DispatcherInTransit d; - d.local_handle = handles[i]; - d.dispatcher = it->second.dispatcher; - if (!d.dispatcher->BeginTransit()) - return MOJO_RESULT_BUSY; - it->second.busy = true; - dispatchers->push_back(d); - } - return MOJO_RESULT_OK; -} - -void HandleTable::CompleteTransitAndClose( - const std::vector<Dispatcher::DispatcherInTransit>& dispatchers) { - for (const auto& dispatcher : dispatchers) { - auto it = handles_.find(dispatcher.local_handle); - DCHECK(it != handles_.end() && it->second.busy); - handles_.erase(it); - dispatcher.dispatcher->CompleteTransitAndClose(); - } -} - -void HandleTable::CancelTransit( - const std::vector<Dispatcher::DispatcherInTransit>& dispatchers) { - for (const auto& dispatcher : dispatchers) { - auto it = handles_.find(dispatcher.local_handle); - DCHECK(it != handles_.end() && it->second.busy); - it->second.busy = false; - dispatcher.dispatcher->CancelTransit(); - } -} - -void HandleTable::GetActiveHandlesForTest(std::vector<MojoHandle>* handles) { - handles->clear(); - for (const auto& entry : handles_) - handles->push_back(entry.first); -} - -HandleTable::Entry::Entry() {} - -HandleTable::Entry::Entry(scoped_refptr<Dispatcher> dispatcher) - : dispatcher(std::move(dispatcher)) {} - -HandleTable::Entry::Entry(const Entry& other) = default; - -HandleTable::Entry::~Entry() {} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/handle_table.h b/mojo/edk/system/handle_table.h deleted file mode 100644 index 882d540..0000000 --- a/mojo/edk/system/handle_table.h +++ /dev/null @@ -1,75 +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 <stdint.h> - -#include <vector> - -#include "base/containers/hash_tables.h" -#include "base/macros.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { -namespace edk { - -class HandleTable { - public: - HandleTable(); - ~HandleTable(); - - MojoHandle AddDispatcher(scoped_refptr<Dispatcher> dispatcher); - - // Inserts multiple dispatchers received from message transit, populating - // |handles| with their newly allocated handles. Returns |true| on success. - bool AddDispatchersFromTransit( - const std::vector<Dispatcher::DispatcherInTransit>& dispatchers, - MojoHandle* handles); - - scoped_refptr<Dispatcher> GetDispatcher(MojoHandle handle) const; - MojoResult GetAndRemoveDispatcher(MojoHandle, - scoped_refptr<Dispatcher>* dispatcher); - - // Marks handles as busy and populates |dispatchers|. Returns MOJO_RESULT_BUSY - // if any of the handles are already in transit; MOJO_RESULT_INVALID_ARGUMENT - // if any of the handles are invalid; or MOJO_RESULT_OK if successful. - MojoResult BeginTransit( - const MojoHandle* handles, - uint32_t num_handles, - std::vector<Dispatcher::DispatcherInTransit>* dispatchers); - - void CompleteTransitAndClose( - const std::vector<Dispatcher::DispatcherInTransit>& dispatchers); - void CancelTransit( - const std::vector<Dispatcher::DispatcherInTransit>& dispatchers); - - void GetActiveHandlesForTest(std::vector<MojoHandle> *handles); - - private: - struct Entry { - Entry(); - explicit Entry(scoped_refptr<Dispatcher> dispatcher); - Entry(const Entry& other); - ~Entry(); - - scoped_refptr<Dispatcher> dispatcher; - bool busy = false; - }; - - using HandleMap = base::hash_map<MojoHandle, Entry>; - - HandleMap handles_; - - uint32_t next_available_handle_ = 1; - - DISALLOW_COPY_AND_ASSIGN(HandleTable); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_HANDLE_TABLE_H_ diff --git a/mojo/edk/system/mach_port_relay.cc b/mojo/edk/system/mach_port_relay.cc deleted file mode 100644 index f05cf22..0000000 --- a/mojo/edk/system/mach_port_relay.cc +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/mach_port_relay.h" - -#include <mach/mach.h> - -#include <utility> - -#include "base/logging.h" -#include "base/mac/mach_port_util.h" -#include "base/mac/scoped_mach_port.h" -#include "base/metrics/histogram_macros.h" -#include "base/process/process.h" -#include "mojo/edk/embedder/platform_handle_vector.h" - -namespace mojo { -namespace edk { - -namespace { - -// Errors that can occur in the broker (privileged parent) process. -// These match tools/metrics/histograms.xml. -// This enum is append-only. -enum class BrokerUMAError : int { - SUCCESS = 0, - // Couldn't get a task port for the process with a given pid. - ERROR_TASK_FOR_PID = 1, - // Couldn't make a port with receive rights in the destination process. - ERROR_MAKE_RECEIVE_PORT = 2, - // Couldn't change the attributes of a Mach port. - ERROR_SET_ATTRIBUTES = 3, - // Couldn't extract a right from the destination. - ERROR_EXTRACT_DEST_RIGHT = 4, - // Couldn't send a Mach port in a call to mach_msg(). - ERROR_SEND_MACH_PORT = 5, - // Couldn't extract a right from the source. - ERROR_EXTRACT_SOURCE_RIGHT = 6, - ERROR_MAX -}; - -// Errors that can occur in a child process. -// These match tools/metrics/histograms.xml. -// This enum is append-only. -enum class ChildUMAError : int { - SUCCESS = 0, - // An error occurred while trying to receive a Mach port with mach_msg(). - ERROR_RECEIVE_MACH_MESSAGE = 1, - ERROR_MAX -}; - -void ReportBrokerError(BrokerUMAError error) { - UMA_HISTOGRAM_ENUMERATION("Mojo.MachPortRelay.BrokerError", - static_cast<int>(error), - static_cast<int>(BrokerUMAError::ERROR_MAX)); -} - -void ReportChildError(ChildUMAError error) { - UMA_HISTOGRAM_ENUMERATION("Mojo.MachPortRelay.ChildError", - static_cast<int>(error), - static_cast<int>(ChildUMAError::ERROR_MAX)); -} - -} // namespace - -// static -bool MachPortRelay::ReceivePorts(PlatformHandleVector* handles) { - DCHECK(handles); - - for (size_t i = 0; i < handles->size(); i++) { - PlatformHandle* handle = handles->data() + i; - DCHECK(handle->type != PlatformHandle::Type::MACH); - if (handle->type != PlatformHandle::Type::MACH_NAME) - continue; - - if (handle->port == MACH_PORT_NULL) { - handle->type = PlatformHandle::Type::MACH; - continue; - } - - base::mac::ScopedMachReceiveRight message_port(handle->port); - base::mac::ScopedMachSendRight received_port( - base::ReceiveMachPort(message_port.get())); - if (received_port.get() == MACH_PORT_NULL) { - ReportChildError(ChildUMAError::ERROR_RECEIVE_MACH_MESSAGE); - handle->port = MACH_PORT_NULL; - LOG(ERROR) << "Error receiving mach port"; - return false; - } - - ReportChildError(ChildUMAError::SUCCESS); - handle->port = received_port.release(); - handle->type = PlatformHandle::Type::MACH; - } - - return true; -} - -MachPortRelay::MachPortRelay(base::PortProvider* port_provider) - : port_provider_(port_provider) { - DCHECK(port_provider); - port_provider_->AddObserver(this); -} - -MachPortRelay::~MachPortRelay() { - port_provider_->RemoveObserver(this); -} - -bool MachPortRelay::SendPortsToProcess(Channel::Message* message, - base::ProcessHandle process) { - DCHECK(message); - mach_port_t task_port = port_provider_->TaskForPid(process); - if (task_port == MACH_PORT_NULL) { - // Callers check the port provider for the task port before calling this - // function, in order to queue pending messages. Therefore, if this fails, - // it should be considered a genuine, bona fide, electrified, six-car error. - ReportBrokerError(BrokerUMAError::ERROR_TASK_FOR_PID); - return false; - } - - size_t num_sent = 0; - bool error = false; - ScopedPlatformHandleVectorPtr handles = message->TakeHandles(); - // Message should have handles, otherwise there's no point in calling this - // function. - DCHECK(handles); - for (size_t i = 0; i < handles->size(); i++) { - PlatformHandle* handle = &(*handles)[i]; - DCHECK(handle->type != PlatformHandle::Type::MACH_NAME); - if (handle->type != PlatformHandle::Type::MACH) - continue; - - if (handle->port == MACH_PORT_NULL) { - handle->type = PlatformHandle::Type::MACH_NAME; - num_sent++; - continue; - } - - mach_port_name_t intermediate_port; - base::MachCreateError error_code; - intermediate_port = base::CreateIntermediateMachPort( - task_port, base::mac::ScopedMachSendRight(handle->port), &error_code); - if (intermediate_port == MACH_PORT_NULL) { - BrokerUMAError uma_error; - switch (error_code) { - case base::MachCreateError::ERROR_MAKE_RECEIVE_PORT: - uma_error = BrokerUMAError::ERROR_MAKE_RECEIVE_PORT; - break; - case base::MachCreateError::ERROR_SET_ATTRIBUTES: - uma_error = BrokerUMAError::ERROR_SET_ATTRIBUTES; - break; - case base::MachCreateError::ERROR_EXTRACT_DEST_RIGHT: - uma_error = BrokerUMAError::ERROR_EXTRACT_DEST_RIGHT; - break; - case base::MachCreateError::ERROR_SEND_MACH_PORT: - uma_error = BrokerUMAError::ERROR_SEND_MACH_PORT; - break; - } - ReportBrokerError(uma_error); - handle->port = MACH_PORT_NULL; - error = true; - break; - } - - ReportBrokerError(BrokerUMAError::SUCCESS); - handle->port = intermediate_port; - handle->type = PlatformHandle::Type::MACH_NAME; - num_sent++; - } - DCHECK(error || num_sent); - message->SetHandles(std::move(handles)); - - return !error; -} - -bool MachPortRelay::ExtractPortRights(Channel::Message* message, - base::ProcessHandle process) { - DCHECK(message); - - mach_port_t task_port = port_provider_->TaskForPid(process); - if (task_port == MACH_PORT_NULL) { - ReportBrokerError(BrokerUMAError::ERROR_TASK_FOR_PID); - return false; - } - - size_t num_received = 0; - bool error = false; - ScopedPlatformHandleVectorPtr handles = message->TakeHandles(); - // Message should have handles, otherwise there's no point in calling this - // function. - DCHECK(handles); - for (size_t i = 0; i < handles->size(); i++) { - PlatformHandle* handle = handles->data() + i; - DCHECK(handle->type != PlatformHandle::Type::MACH); - if (handle->type != PlatformHandle::Type::MACH_NAME) - continue; - - if (handle->port == MACH_PORT_NULL) { - handle->type = PlatformHandle::Type::MACH; - num_received++; - continue; - } - - mach_port_t extracted_right = MACH_PORT_NULL; - mach_msg_type_name_t extracted_right_type; - kern_return_t kr = - mach_port_extract_right(task_port, handle->port, - MACH_MSG_TYPE_MOVE_SEND, - &extracted_right, &extracted_right_type); - if (kr != KERN_SUCCESS) { - ReportBrokerError(BrokerUMAError::ERROR_EXTRACT_SOURCE_RIGHT); - error = true; - break; - } - - ReportBrokerError(BrokerUMAError::SUCCESS); - DCHECK_EQ(static_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND), - extracted_right_type); - handle->port = extracted_right; - handle->type = PlatformHandle::Type::MACH; - num_received++; - } - DCHECK(error || num_received); - message->SetHandles(std::move(handles)); - - return !error; -} - -void MachPortRelay::AddObserver(Observer* observer) { - base::AutoLock locker(observers_lock_); - bool inserted = observers_.insert(observer).second; - DCHECK(inserted); -} - -void MachPortRelay::RemoveObserver(Observer* observer) { - base::AutoLock locker(observers_lock_); - observers_.erase(observer); -} - -void MachPortRelay::OnReceivedTaskPort(base::ProcessHandle process) { - base::AutoLock locker(observers_lock_); - for (auto* observer : observers_) - observer->OnProcessReady(process); -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/mach_port_relay.h b/mojo/edk/system/mach_port_relay.h deleted file mode 100644 index 87bc56c..0000000 --- a/mojo/edk/system/mach_port_relay.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_MACH_PORT_RELAY_H_ -#define MOJO_EDK_SYSTEM_MACH_PORT_RELAY_H_ - -#include <set> - -#include "base/macros.h" -#include "base/process/port_provider_mac.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/channel.h" - -namespace mojo { -namespace edk { - -// The MachPortRelay is used by a privileged process, usually the root process, -// to manipulate Mach ports in a child process. Ports can be added to and -// extracted from a child process that has registered itself with the -// |base::PortProvider| used by this class. -class MachPortRelay : public base::PortProvider::Observer { - public: - class Observer { - public: - // Called by the MachPortRelay to notify observers that a new process is - // ready for Mach ports to be sent/received. There are no guarantees about - // the thread this is called on, including the presence of a MessageLoop. - // Implementations must not call AddObserver() or RemoveObserver() during - // this function, as doing so will deadlock. - virtual void OnProcessReady(base::ProcessHandle process) = 0; - }; - - // Used by a child process to receive Mach ports from a sender (privileged) - // process. Each Mach port in |handles| is interpreted as an intermediate Mach - // port. It replaces each Mach port with the final Mach port received from the - // intermediate port. This method takes ownership of the intermediate Mach - // port and gives ownership of the final Mach port to the caller. Any handles - // that are not Mach ports will remain unchanged, and the number and ordering - // of handles is preserved. - // Returns |false| on failure and there is no guarantee about whether a Mach - // port is intermediate or final. - // - // See SendPortsToProcess() for the definition of intermediate and final Mach - // ports. - static bool ReceivePorts(PlatformHandleVector* handles); - - explicit MachPortRelay(base::PortProvider* port_provider); - ~MachPortRelay() override; - - // Sends the Mach ports attached to |message| to |process|. - // For each Mach port attached to |message|, a new Mach port, the intermediate - // port, is created in |process|. The message's Mach port is then sent over - // this intermediate port and the message is modified to refer to the name of - // the intermediate port. The Mach port received over the intermediate port in - // the child is referred to as the final Mach port. - // Returns |false| on failure and |message| may contain a mix of actual Mach - // ports and names. - bool SendPortsToProcess(Channel::Message* message, - base::ProcessHandle process); - - // Extracts the Mach ports attached to |message| from |process|. - // Any Mach ports attached to |message| are names and not actual Mach ports - // that are valid in this process. For each of those Mach port names, a send - // right is extracted from |process| and the port name is replaced with the - // send right. - // Returns |false| on failure and |message| may contain a mix of actual Mach - // ports and names. - bool ExtractPortRights(Channel::Message* message, - base::ProcessHandle process); - - // Observer interface. - void AddObserver(Observer* observer); - void RemoveObserver(Observer* observer); - - base::PortProvider* port_provider() const { return port_provider_; } - - private: - // base::PortProvider::Observer implementation. - void OnReceivedTaskPort(base::ProcessHandle process) override; - - base::PortProvider* const port_provider_; - - base::Lock observers_lock_; - std::set<Observer*> observers_; - - DISALLOW_COPY_AND_ASSIGN(MachPortRelay); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_MACH_PORT_RELAY_H_ diff --git a/mojo/edk/system/mapping_table.cc b/mojo/edk/system/mapping_table.cc deleted file mode 100644 index 8509443..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 edk { - -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( - std::unique_ptr<PlatformSharedBufferMapping> mapping) { - DCHECK(mapping); - - if (address_to_mapping_map_.size() >= - GetConfiguration().max_mapping_table_sze) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - - void* address = 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(void* address) { - AddressToMappingMap::iterator it = address_to_mapping_map_.find(address); - if (it == address_to_mapping_map_.end()) - return MOJO_RESULT_INVALID_ARGUMENT; - PlatformSharedBufferMapping* mapping_to_delete = it->second; - address_to_mapping_map_.erase(it); - delete mapping_to_delete; - return MOJO_RESULT_OK; -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/mapping_table.h b/mojo/edk/system/mapping_table.h deleted file mode 100644 index 00167e3..0000000 --- a/mojo/edk/system/mapping_table.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_SYSTEM_MAPPING_TABLE_H_ -#define MOJO_EDK_SYSTEM_MAPPING_TABLE_H_ - -#include <stdint.h> - -#include <memory> -#include <vector> - -#include "base/containers/hash_tables.h" -#include "base/macros.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { - -namespace edk { -class Core; -class PlatformSharedBufferMapping; - -// 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(std::unique_ptr<PlatformSharedBufferMapping> mapping); - MojoResult RemoveMapping(void* address); - - private: - friend bool internal::ShutdownCheckNoLeaks(Core*); - - using AddressToMappingMap = - base::hash_map<void*, PlatformSharedBufferMapping*>; - AddressToMappingMap address_to_mapping_map_; - - DISALLOW_COPY_AND_ASSIGN(MappingTable); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_MAPPING_TABLE_H_ diff --git a/mojo/edk/system/message_for_transit.cc b/mojo/edk/system/message_for_transit.cc deleted file mode 100644 index 26658e1..0000000 --- a/mojo/edk/system/message_for_transit.cc +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/message_for_transit.h" - -#include <vector> - -#include "mojo/edk/embedder/platform_handle_vector.h" - -namespace mojo { -namespace edk { - -namespace { - -static_assert(sizeof(MessageForTransit::MessageHeader) % 8 == 0, - "Invalid MessageHeader size."); -static_assert(sizeof(MessageForTransit::DispatcherHeader) % 8 == 0, - "Invalid DispatcherHeader size."); - -} // namespace - -MessageForTransit::~MessageForTransit() {} - -// static -MojoResult MessageForTransit::Create( - std::unique_ptr<MessageForTransit>* message, - uint32_t num_bytes, - const Dispatcher::DispatcherInTransit* dispatchers, - uint32_t num_dispatchers) { - // A structure for retaining information about every Dispatcher that will be - // sent with this message. - struct DispatcherInfo { - uint32_t num_bytes; - uint32_t num_ports; - uint32_t num_handles; - }; - - // This is only the base header size. It will grow as we accumulate the - // size of serialized state for each dispatcher. - size_t header_size = sizeof(MessageHeader) + - num_dispatchers * sizeof(DispatcherHeader); - size_t num_ports = 0; - size_t num_handles = 0; - - std::vector<DispatcherInfo> dispatcher_info(num_dispatchers); - for (size_t i = 0; i < num_dispatchers; ++i) { - Dispatcher* d = dispatchers[i].dispatcher.get(); - d->StartSerialize(&dispatcher_info[i].num_bytes, - &dispatcher_info[i].num_ports, - &dispatcher_info[i].num_handles); - header_size += dispatcher_info[i].num_bytes; - num_ports += dispatcher_info[i].num_ports; - num_handles += dispatcher_info[i].num_handles; - } - - // We now have enough information to fully allocate the message storage. - std::unique_ptr<PortsMessage> msg = PortsMessage::NewUserMessage( - header_size + num_bytes, num_ports, num_handles); - if (!msg) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - - // Populate the message header with information about serialized dispatchers. - // - // The front of the message is always a MessageHeader followed by a - // DispatcherHeader for each dispatcher to be sent. - MessageHeader* header = - static_cast<MessageHeader*>(msg->mutable_payload_bytes()); - DispatcherHeader* dispatcher_headers = - reinterpret_cast<DispatcherHeader*>(header + 1); - - // Serialized dispatcher state immediately follows the series of - // DispatcherHeaders. - char* dispatcher_data = - reinterpret_cast<char*>(dispatcher_headers + num_dispatchers); - - header->num_dispatchers = num_dispatchers; - - // |header_size| is the total number of bytes preceding the message payload, - // including all dispatcher headers and serialized dispatcher state. - DCHECK_LE(header_size, std::numeric_limits<uint32_t>::max()); - header->header_size = static_cast<uint32_t>(header_size); - - if (num_dispatchers > 0) { - ScopedPlatformHandleVectorPtr handles( - new PlatformHandleVector(num_handles)); - size_t port_index = 0; - size_t handle_index = 0; - bool fail = false; - for (size_t i = 0; i < num_dispatchers; ++i) { - Dispatcher* d = dispatchers[i].dispatcher.get(); - DispatcherHeader* dh = &dispatcher_headers[i]; - const DispatcherInfo& info = dispatcher_info[i]; - - // Fill in the header for this dispatcher. - dh->type = static_cast<int32_t>(d->GetType()); - dh->num_bytes = info.num_bytes; - dh->num_ports = info.num_ports; - dh->num_platform_handles = info.num_handles; - - // Fill in serialized state, ports, and platform handles. We'll cancel - // the send if the dispatcher implementation rejects for some reason. - if (!d->EndSerialize(static_cast<void*>(dispatcher_data), - msg->mutable_ports() + port_index, - handles->data() + handle_index)) { - fail = true; - break; - } - - dispatcher_data += info.num_bytes; - port_index += info.num_ports; - handle_index += info.num_handles; - } - - if (fail) { - // Release any platform handles we've accumulated. Their dispatchers - // retain ownership when message creation fails, so these are not actually - // leaking. - handles->clear(); - return MOJO_RESULT_INVALID_ARGUMENT; - } - - // Take ownership of all the handles and move them into message storage. - msg->SetHandles(std::move(handles)); - } - - message->reset(new MessageForTransit(std::move(msg))); - return MOJO_RESULT_OK; -} - -MessageForTransit::MessageForTransit(std::unique_ptr<PortsMessage> message) - : message_(std::move(message)) { -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/message_for_transit.h b/mojo/edk/system/message_for_transit.h deleted file mode 100644 index 6103a77..0000000 --- a/mojo/edk/system/message_for_transit.h +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_MESSAGE_FOR_TRANSIT_H_ -#define MOJO_EDK_SYSTEM_MESSAGE_FOR_TRANSIT_H_ - -#include <stdint.h> - -#include <memory> - -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/ports_message.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace edk { - -// MessageForTransit holds onto a PortsMessage which may be sent via -// |MojoWriteMessage()| or which may have been received on a pipe endpoint. -// Instances of this class are exposed to Mojo system API consumers via the -// opaque pointers used with |MojoCreateMessage()|, |MojoDestroyMessage()|, -// |MojoWriteMessageNew()|, and |MojoReadMessageNew()|. -class MOJO_SYSTEM_IMPL_EXPORT MessageForTransit { - public: -#pragma pack(push, 1) - // Header attached to every message. - struct MessageHeader { - // The number of serialized dispatchers included in this header. - uint32_t num_dispatchers; - - // Total size of the header, including serialized dispatcher data. - uint32_t header_size; - }; - - // Header for each dispatcher in a message, immediately following the message - // header. - struct DispatcherHeader { - // The type of the dispatcher, correpsonding to the Dispatcher::Type enum. - int32_t type; - - // The size of the serialized dispatcher, not including this header. - uint32_t num_bytes; - - // The number of ports needed to deserialize this dispatcher. - uint32_t num_ports; - - // The number of platform handles needed to deserialize this dispatcher. - uint32_t num_platform_handles; - }; -#pragma pack(pop) - - ~MessageForTransit(); - - // A static constructor for building outbound messages. - static MojoResult Create( - std::unique_ptr<MessageForTransit>* message, - uint32_t num_bytes, - const Dispatcher::DispatcherInTransit* dispatchers, - uint32_t num_dispatchers); - - // A static constructor for wrapping inbound messages. - static std::unique_ptr<MessageForTransit> WrapPortsMessage( - std::unique_ptr<PortsMessage> message) { - return base::WrapUnique(new MessageForTransit(std::move(message))); - } - - const void* bytes() const { - DCHECK(message_); - return static_cast<const void*>( - static_cast<const char*>(message_->payload_bytes()) + - header()->header_size); - } - - void* mutable_bytes() { - DCHECK(message_); - return static_cast<void*>( - static_cast<char*>(message_->mutable_payload_bytes()) + - header()->header_size); - } - - size_t num_bytes() const { - size_t header_size = header()->header_size; - DCHECK_GE(message_->num_payload_bytes(), header_size); - return message_->num_payload_bytes() - header_size; - } - - size_t num_handles() const { return header()->num_dispatchers; } - - const PortsMessage& ports_message() const { return *message_; } - - std::unique_ptr<PortsMessage> TakePortsMessage() { - return std::move(message_); - } - - private: - explicit MessageForTransit(std::unique_ptr<PortsMessage> message); - - const MessageForTransit::MessageHeader* header() const { - DCHECK(message_); - return static_cast<const MessageForTransit::MessageHeader*>( - message_->payload_bytes()); - } - - std::unique_ptr<PortsMessage> message_; - - DISALLOW_COPY_AND_ASSIGN(MessageForTransit); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_MESSAGE_FOR_TRANSIT_H_ diff --git a/mojo/edk/system/message_pipe_dispatcher.cc b/mojo/edk/system/message_pipe_dispatcher.cc deleted file mode 100644 index 1db56c0..0000000 --- a/mojo/edk/system/message_pipe_dispatcher.cc +++ /dev/null @@ -1,554 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/message_pipe_dispatcher.h" - -#include <limits> -#include <memory> - -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "mojo/edk/embedder/embedder_internal.h" -#include "mojo/edk/system/core.h" -#include "mojo/edk/system/message_for_transit.h" -#include "mojo/edk/system/node_controller.h" -#include "mojo/edk/system/ports/message_filter.h" -#include "mojo/edk/system/ports_message.h" -#include "mojo/edk/system/request_context.h" - -namespace mojo { -namespace edk { - -namespace { - -using DispatcherHeader = MessageForTransit::DispatcherHeader; -using MessageHeader = MessageForTransit::MessageHeader; - -#pragma pack(push, 1) - -struct SerializedState { - uint64_t pipe_id; - int8_t endpoint; - char padding[7]; -}; - -static_assert(sizeof(SerializedState) % 8 == 0, - "Invalid SerializedState size."); - -#pragma pack(pop) - -} // namespace - -// A PortObserver which forwards to a MessagePipeDispatcher. This owns a -// reference to the MPD to ensure it lives as long as the observed port. -class MessagePipeDispatcher::PortObserverThunk - : public NodeController::PortObserver { - public: - explicit PortObserverThunk(scoped_refptr<MessagePipeDispatcher> dispatcher) - : dispatcher_(dispatcher) {} - - private: - ~PortObserverThunk() override {} - - // NodeController::PortObserver: - void OnPortStatusChanged() override { dispatcher_->OnPortStatusChanged(); } - - scoped_refptr<MessagePipeDispatcher> dispatcher_; - - DISALLOW_COPY_AND_ASSIGN(PortObserverThunk); -}; - -// A MessageFilter used by ReadMessage to determine whether a message should -// actually be consumed yet. -class ReadMessageFilter : public ports::MessageFilter { - public: - // Creates a new ReadMessageFilter which captures and potentially modifies - // various (unowned) local state within MessagePipeDispatcher::ReadMessage. - ReadMessageFilter(bool read_any_size, - bool may_discard, - uint32_t* num_bytes, - uint32_t* num_handles, - bool* no_space, - bool* invalid_message) - : read_any_size_(read_any_size), - may_discard_(may_discard), - num_bytes_(num_bytes), - num_handles_(num_handles), - no_space_(no_space), - invalid_message_(invalid_message) {} - - ~ReadMessageFilter() override {} - - // ports::MessageFilter: - bool Match(const ports::Message& m) override { - const PortsMessage& message = static_cast<const PortsMessage&>(m); - if (message.num_payload_bytes() < sizeof(MessageHeader)) { - *invalid_message_ = true; - return true; - } - - const MessageHeader* header = - static_cast<const MessageHeader*>(message.payload_bytes()); - if (header->header_size > message.num_payload_bytes()) { - *invalid_message_ = true; - return true; - } - - uint32_t bytes_to_read = 0; - uint32_t bytes_available = - static_cast<uint32_t>(message.num_payload_bytes()) - - header->header_size; - if (num_bytes_) { - bytes_to_read = std::min(*num_bytes_, bytes_available); - *num_bytes_ = bytes_available; - } - - uint32_t handles_to_read = 0; - uint32_t handles_available = header->num_dispatchers; - if (num_handles_) { - handles_to_read = std::min(*num_handles_, handles_available); - *num_handles_ = handles_available; - } - - if (handles_to_read < handles_available || - (!read_any_size_ && bytes_to_read < bytes_available)) { - *no_space_ = true; - return may_discard_; - } - - return true; - } - - private: - const bool read_any_size_; - const bool may_discard_; - uint32_t* const num_bytes_; - uint32_t* const num_handles_; - bool* const no_space_; - bool* const invalid_message_; - - DISALLOW_COPY_AND_ASSIGN(ReadMessageFilter); -}; - -#if DCHECK_IS_ON() - -// A MessageFilter which never matches a message. Used to peek at the size of -// the next available message on a port, for debug logging only. -class PeekSizeMessageFilter : public ports::MessageFilter { - public: - PeekSizeMessageFilter() {} - ~PeekSizeMessageFilter() override {} - - // ports::MessageFilter: - bool Match(const ports::Message& message) override { - message_size_ = message.num_payload_bytes(); - return false; - } - - size_t message_size() const { return message_size_; } - - private: - size_t message_size_ = 0; - - DISALLOW_COPY_AND_ASSIGN(PeekSizeMessageFilter); -}; - -#endif // DCHECK_IS_ON() - -MessagePipeDispatcher::MessagePipeDispatcher(NodeController* node_controller, - const ports::PortRef& port, - uint64_t pipe_id, - int endpoint) - : node_controller_(node_controller), - port_(port), - pipe_id_(pipe_id), - endpoint_(endpoint), - watchers_(this) { - DVLOG(2) << "Creating new MessagePipeDispatcher for port " << port.name() - << " [pipe_id=" << pipe_id << "; endpoint=" << endpoint << "]"; - - node_controller_->SetPortObserver( - port_, - make_scoped_refptr(new PortObserverThunk(this))); -} - -bool MessagePipeDispatcher::Fuse(MessagePipeDispatcher* other) { - node_controller_->SetPortObserver(port_, nullptr); - node_controller_->SetPortObserver(other->port_, nullptr); - - ports::PortRef port0; - { - base::AutoLock lock(signal_lock_); - port0 = port_; - port_closed_.Set(true); - watchers_.NotifyClosed(); - } - - ports::PortRef port1; - { - base::AutoLock lock(other->signal_lock_); - port1 = other->port_; - other->port_closed_.Set(true); - other->watchers_.NotifyClosed(); - } - - // Both ports are always closed by this call. - int rv = node_controller_->MergeLocalPorts(port0, port1); - return rv == ports::OK; -} - -Dispatcher::Type MessagePipeDispatcher::GetType() const { - return Type::MESSAGE_PIPE; -} - -MojoResult MessagePipeDispatcher::Close() { - base::AutoLock lock(signal_lock_); - DVLOG(2) << "Closing message pipe " << pipe_id_ << " endpoint " << endpoint_ - << " [port=" << port_.name() << "]"; - return CloseNoLock(); -} - -MojoResult MessagePipeDispatcher::WriteMessage( - std::unique_ptr<MessageForTransit> message, - MojoWriteMessageFlags flags) { - if (port_closed_ || in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - - size_t num_bytes = message->num_bytes(); - int rv = node_controller_->SendMessage(port_, message->TakePortsMessage()); - - DVLOG(4) << "Sent message on pipe " << pipe_id_ << " endpoint " << endpoint_ - << " [port=" << port_.name() << "; rv=" << rv - << "; num_bytes=" << num_bytes << "]"; - - if (rv != ports::OK) { - if (rv == ports::ERROR_PORT_UNKNOWN || - rv == ports::ERROR_PORT_STATE_UNEXPECTED || - rv == ports::ERROR_PORT_CANNOT_SEND_PEER) { - return MOJO_RESULT_INVALID_ARGUMENT; - } else if (rv == ports::ERROR_PORT_PEER_CLOSED) { - return MOJO_RESULT_FAILED_PRECONDITION; - } - - NOTREACHED(); - return MOJO_RESULT_UNKNOWN; - } - - return MOJO_RESULT_OK; -} - -MojoResult MessagePipeDispatcher::ReadMessage( - std::unique_ptr<MessageForTransit>* message, - uint32_t* num_bytes, - MojoHandle* handles, - uint32_t* num_handles, - MojoReadMessageFlags flags, - bool read_any_size) { - // We can't read from a port that's closed or in transit! - if (port_closed_ || in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - - bool no_space = false; - bool may_discard = flags & MOJO_READ_MESSAGE_FLAG_MAY_DISCARD; - bool invalid_message = false; - - // Grab a message if the provided handles buffer is large enough. If the input - // |num_bytes| is provided and |read_any_size| is false, we also ensure - // that it specifies a size at least as large as the next available payload. - // - // If |read_any_size| is true, the input value of |*num_bytes| is ignored. - // This flag exists to support both new and old API behavior. - - ports::ScopedMessage ports_message; - ReadMessageFilter filter(read_any_size, may_discard, num_bytes, num_handles, - &no_space, &invalid_message); - int rv = node_controller_->node()->GetMessage(port_, &ports_message, &filter); - - if (invalid_message) - return MOJO_RESULT_UNKNOWN; - - if (rv != ports::OK && rv != ports::ERROR_PORT_PEER_CLOSED) { - if (rv == ports::ERROR_PORT_UNKNOWN || - rv == ports::ERROR_PORT_STATE_UNEXPECTED) - return MOJO_RESULT_INVALID_ARGUMENT; - - NOTREACHED(); - return MOJO_RESULT_UNKNOWN; // TODO: Add a better error code here? - } - - if (no_space) { - if (may_discard) { - // May have been the last message on the pipe. Need to update signals just - // in case. - base::AutoLock lock(signal_lock_); - watchers_.NotifyState(GetHandleSignalsStateNoLock()); - } - // |*num_handles| (and/or |*num_bytes| if |read_any_size| is false) wasn't - // sufficient to hold this message's data. The message will still be in - // queue unless MOJO_READ_MESSAGE_FLAG_MAY_DISCARD was set. - return MOJO_RESULT_RESOURCE_EXHAUSTED; - } - - if (!ports_message) { - // No message was available in queue. - - if (rv == ports::OK) - return MOJO_RESULT_SHOULD_WAIT; - - // Peer is closed and there are no more messages to read. - DCHECK_EQ(rv, ports::ERROR_PORT_PEER_CLOSED); - return MOJO_RESULT_FAILED_PRECONDITION; - } - - // Alright! We have a message and the caller has provided sufficient storage - // in which to receive it. - - { - // We need to update anyone watching our signals in case that was the last - // available message. - base::AutoLock lock(signal_lock_); - watchers_.NotifyState(GetHandleSignalsStateNoLock()); - } - - std::unique_ptr<PortsMessage> msg( - static_cast<PortsMessage*>(ports_message.release())); - - const MessageHeader* header = - static_cast<const MessageHeader*>(msg->payload_bytes()); - const DispatcherHeader* dispatcher_headers = - reinterpret_cast<const DispatcherHeader*>(header + 1); - - if (header->num_dispatchers > std::numeric_limits<uint16_t>::max()) - return MOJO_RESULT_UNKNOWN; - - // Deserialize dispatchers. - if (header->num_dispatchers > 0) { - CHECK(handles); - std::vector<DispatcherInTransit> dispatchers(header->num_dispatchers); - size_t data_payload_index = sizeof(MessageHeader) + - header->num_dispatchers * sizeof(DispatcherHeader); - if (data_payload_index > header->header_size) - return MOJO_RESULT_UNKNOWN; - const char* dispatcher_data = reinterpret_cast<const char*>( - dispatcher_headers + header->num_dispatchers); - size_t port_index = 0; - size_t platform_handle_index = 0; - ScopedPlatformHandleVectorPtr msg_handles = msg->TakeHandles(); - const size_t num_msg_handles = msg_handles ? msg_handles->size() : 0; - for (size_t i = 0; i < header->num_dispatchers; ++i) { - const DispatcherHeader& dh = dispatcher_headers[i]; - Type type = static_cast<Type>(dh.type); - - size_t next_payload_index = data_payload_index + dh.num_bytes; - if (msg->num_payload_bytes() < next_payload_index || - next_payload_index < data_payload_index) { - return MOJO_RESULT_UNKNOWN; - } - - size_t next_port_index = port_index + dh.num_ports; - if (msg->num_ports() < next_port_index || next_port_index < port_index) - return MOJO_RESULT_UNKNOWN; - - size_t next_platform_handle_index = - platform_handle_index + dh.num_platform_handles; - if (num_msg_handles < next_platform_handle_index || - next_platform_handle_index < platform_handle_index) { - return MOJO_RESULT_UNKNOWN; - } - - PlatformHandle* out_handles = - num_msg_handles ? msg_handles->data() + platform_handle_index - : nullptr; - dispatchers[i].dispatcher = Dispatcher::Deserialize( - type, dispatcher_data, dh.num_bytes, msg->ports() + port_index, - dh.num_ports, out_handles, dh.num_platform_handles); - if (!dispatchers[i].dispatcher) - return MOJO_RESULT_UNKNOWN; - - dispatcher_data += dh.num_bytes; - data_payload_index = next_payload_index; - port_index = next_port_index; - platform_handle_index = next_platform_handle_index; - } - - if (!node_controller_->core()->AddDispatchersFromTransit(dispatchers, - handles)) - return MOJO_RESULT_UNKNOWN; - } - - CHECK(msg); - *message = MessageForTransit::WrapPortsMessage(std::move(msg)); - return MOJO_RESULT_OK; -} - -HandleSignalsState -MessagePipeDispatcher::GetHandleSignalsState() const { - base::AutoLock lock(signal_lock_); - return GetHandleSignalsStateNoLock(); -} - -MojoResult MessagePipeDispatcher::AddWatcherRef( - const scoped_refptr<WatcherDispatcher>& watcher, - uintptr_t context) { - base::AutoLock lock(signal_lock_); - if (port_closed_ || in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - return watchers_.Add(watcher, context, GetHandleSignalsStateNoLock()); -} - -MojoResult MessagePipeDispatcher::RemoveWatcherRef(WatcherDispatcher* watcher, - uintptr_t context) { - base::AutoLock lock(signal_lock_); - if (port_closed_ || in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - return watchers_.Remove(watcher, context); -} - -void MessagePipeDispatcher::StartSerialize(uint32_t* num_bytes, - uint32_t* num_ports, - uint32_t* num_handles) { - *num_bytes = static_cast<uint32_t>(sizeof(SerializedState)); - *num_ports = 1; - *num_handles = 0; -} - -bool MessagePipeDispatcher::EndSerialize(void* destination, - ports::PortName* ports, - PlatformHandle* handles) { - SerializedState* state = static_cast<SerializedState*>(destination); - state->pipe_id = pipe_id_; - state->endpoint = static_cast<int8_t>(endpoint_); - memset(state->padding, 0, sizeof(state->padding)); - ports[0] = port_.name(); - return true; -} - -bool MessagePipeDispatcher::BeginTransit() { - base::AutoLock lock(signal_lock_); - if (in_transit_ || port_closed_) - return false; - in_transit_.Set(true); - return in_transit_; -} - -void MessagePipeDispatcher::CompleteTransitAndClose() { - node_controller_->SetPortObserver(port_, nullptr); - - base::AutoLock lock(signal_lock_); - port_transferred_ = true; - in_transit_.Set(false); - CloseNoLock(); -} - -void MessagePipeDispatcher::CancelTransit() { - base::AutoLock lock(signal_lock_); - in_transit_.Set(false); - - // Something may have happened while we were waiting for potential transit. - watchers_.NotifyState(GetHandleSignalsStateNoLock()); -} - -// static -scoped_refptr<Dispatcher> MessagePipeDispatcher::Deserialize( - const void* data, - size_t num_bytes, - const ports::PortName* ports, - size_t num_ports, - PlatformHandle* handles, - size_t num_handles) { - if (num_ports != 1 || num_handles || num_bytes != sizeof(SerializedState)) - return nullptr; - - const SerializedState* state = static_cast<const SerializedState*>(data); - - ports::PortRef port; - CHECK_EQ( - ports::OK, - internal::g_core->GetNodeController()->node()->GetPort(ports[0], &port)); - - return new MessagePipeDispatcher(internal::g_core->GetNodeController(), port, - state->pipe_id, state->endpoint); -} - -MessagePipeDispatcher::~MessagePipeDispatcher() { - DCHECK(port_closed_ && !in_transit_); -} - -MojoResult MessagePipeDispatcher::CloseNoLock() { - signal_lock_.AssertAcquired(); - if (port_closed_ || in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - - port_closed_.Set(true); - watchers_.NotifyClosed(); - - if (!port_transferred_) { - base::AutoUnlock unlock(signal_lock_); - node_controller_->ClosePort(port_); - } - - return MOJO_RESULT_OK; -} - -HandleSignalsState MessagePipeDispatcher::GetHandleSignalsStateNoLock() const { - HandleSignalsState rv; - - ports::PortStatus port_status; - if (node_controller_->node()->GetStatus(port_, &port_status) != ports::OK) { - CHECK(in_transit_ || port_transferred_ || port_closed_); - return HandleSignalsState(); - } - - if (port_status.has_messages) { - rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_READABLE; - rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE; - } - if (port_status.receiving_messages) - rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE; - if (!port_status.peer_closed) { - rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_WRITABLE; - rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_WRITABLE; - rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE; - } else { - rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED; - } - rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED; - return rv; -} - -void MessagePipeDispatcher::OnPortStatusChanged() { - DCHECK(RequestContext::current()); - - base::AutoLock lock(signal_lock_); - - // We stop observing our port as soon as it's transferred, but this can race - // with events which are raised right before that happens. This is fine to - // ignore. - if (port_transferred_) - return; - -#if DCHECK_IS_ON() - ports::PortStatus port_status; - if (node_controller_->node()->GetStatus(port_, &port_status) == ports::OK) { - if (port_status.has_messages) { - ports::ScopedMessage unused; - PeekSizeMessageFilter filter; - node_controller_->node()->GetMessage(port_, &unused, &filter); - DVLOG(4) << "New message detected on message pipe " << pipe_id_ - << " endpoint " << endpoint_ << " [port=" << port_.name() - << "; size=" << filter.message_size() << "]"; - } - if (port_status.peer_closed) { - DVLOG(2) << "Peer closure detected on message pipe " << pipe_id_ - << " endpoint " << endpoint_ << " [port=" << port_.name() << "]"; - } - } -#endif - - watchers_.NotifyState(GetHandleSignalsStateNoLock()); -} - -} // namespace edk -} // 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 574ad66..0000000 --- a/mojo/edk/system/message_pipe_dispatcher.h +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_MESSAGE_PIPE_DISPATCHER_H_ -#define MOJO_EDK_SYSTEM_MESSAGE_PIPE_DISPATCHER_H_ - -#include <stdint.h> - -#include <memory> -#include <queue> - -#include "base/macros.h" -#include "mojo/edk/system/atomic_flag.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/message_for_transit.h" -#include "mojo/edk/system/ports/port_ref.h" -#include "mojo/edk/system/watcher_set.h" - -namespace mojo { -namespace edk { - -class NodeController; - -class MessagePipeDispatcher : public Dispatcher { - public: - // Constructs a MessagePipeDispatcher permanently tied to a specific port. - // |connected| must indicate the state of the port at construction time; if - // the port is initialized with a peer, |connected| must be true. Otherwise it - // must be false. - // - // A MessagePipeDispatcher may not be transferred while in a disconnected - // state, and one can never return to a disconnected once connected. - // - // |pipe_id| is a unique identifier which can be used to track pipe endpoints - // as they're passed around. |endpoint| is either 0 or 1 and again is only - // used for tracking pipes (one side is always 0, the other is always 1.) - MessagePipeDispatcher(NodeController* node_controller, - const ports::PortRef& port, - uint64_t pipe_id, - int endpoint); - - // Fuses this pipe with |other|. Returns |true| on success or |false| on - // failure. Regardless of the return value, both dispatchers are closed by - // this call. - bool Fuse(MessagePipeDispatcher* other); - - // Dispatcher: - Type GetType() const override; - MojoResult Close() override; - MojoResult WriteMessage(std::unique_ptr<MessageForTransit> message, - MojoWriteMessageFlags flags) override; - MojoResult ReadMessage(std::unique_ptr<MessageForTransit>* message, - uint32_t* num_bytes, - MojoHandle* handles, - uint32_t* num_handles, - MojoReadMessageFlags flags, - bool read_any_size) override; - HandleSignalsState GetHandleSignalsState() const override; - MojoResult AddWatcherRef(const scoped_refptr<WatcherDispatcher>& watcher, - uintptr_t context) override; - MojoResult RemoveWatcherRef(WatcherDispatcher* watcher, - uintptr_t context) override; - void StartSerialize(uint32_t* num_bytes, - uint32_t* num_ports, - uint32_t* num_handles) override; - bool EndSerialize(void* destination, - ports::PortName* ports, - PlatformHandle* handles) override; - bool BeginTransit() override; - void CompleteTransitAndClose() override; - void CancelTransit() override; - - static scoped_refptr<Dispatcher> Deserialize( - const void* data, - size_t num_bytes, - const ports::PortName* ports, - size_t num_ports, - PlatformHandle* handles, - size_t num_handles); - - private: - class PortObserverThunk; - friend class PortObserverThunk; - - ~MessagePipeDispatcher() override; - - MojoResult CloseNoLock(); - HandleSignalsState GetHandleSignalsStateNoLock() const; - void OnPortStatusChanged(); - - // These are safe to access from any thread without locking. - NodeController* const node_controller_; - const ports::PortRef port_; - const uint64_t pipe_id_; - const int endpoint_; - - // Guards access to all the fields below. - mutable base::Lock signal_lock_; - - // This is not the same is |port_transferred_|. It's only held true between - // BeginTransit() and Complete/CancelTransit(). - AtomicFlag in_transit_; - - bool port_transferred_ = false; - AtomicFlag port_closed_; - WatcherSet watchers_; - - DISALLOW_COPY_AND_ASSIGN(MessagePipeDispatcher); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_MESSAGE_PIPE_DISPATCHER_H_ diff --git a/mojo/edk/system/message_pipe_perftest.cc b/mojo/edk/system/message_pipe_perftest.cc deleted file mode 100644 index 9866c47..0000000 --- a/mojo/edk/system/message_pipe_perftest.cc +++ /dev/null @@ -1,167 +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 <stddef.h> -#include <stdint.h> - -#include <memory> -#include <utility> - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/strings/stringprintf.h" -#include "base/test/perf_time_logger.h" -#include "base/threading/thread.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/handle_signals_state.h" -#include "mojo/edk/system/test_utils.h" -#include "mojo/edk/test/mojo_test_base.h" -#include "mojo/edk/test/test_utils.h" -#include "mojo/public/c/system/functions.h" -#include "mojo/public/cpp/system/message_pipe.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace edk { -namespace { - -class MessagePipePerfTest : public test::MojoTestBase { - public: - MessagePipePerfTest() : message_count_(0), message_size_(0) {} - - void SetUpMeasurement(int message_count, size_t message_size) { - message_count_ = message_count; - message_size_ = message_size; - payload_ = std::string(message_size, '*'); - read_buffer_.resize(message_size * 2); - } - - protected: - void WriteWaitThenRead(MojoHandle mp) { - CHECK_EQ(MojoWriteMessage(mp, payload_.data(), - static_cast<uint32_t>(payload_.size()), nullptr, - 0, MOJO_WRITE_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - HandleSignalsState hss; - CHECK_EQ(WaitForSignals(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss), - MOJO_RESULT_OK); - uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer_.size()); - CHECK_EQ(MojoReadMessage(mp, &read_buffer_[0], &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(MojoHandle mp) { - CHECK_EQ(MojoWriteMessage(mp, "", 0, nullptr, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - } - - void Measure(MojoHandle 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(); - } - - protected: - void RunPingPongServer(MojoHandle mp) { - // 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); - } - - static int RunPingPongClient(MojoHandle mp) { - 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 = WaitForSignals(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(MojoReadMessage(mp, &buffer[0], - &read_size, nullptr, - 0, MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - - // Empty message indicates quit. - if (read_size == 0) - break; - - CHECK_EQ(MojoWriteMessage(mp, &buffer[0], - read_size, - nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - } - - return rv; - } - - private: - int message_count_; - size_t message_size_; - std::string payload_; - std::string read_buffer_; - std::unique_ptr<base::PerfTimeLogger> perf_logger_; - - DISALLOW_COPY_AND_ASSIGN(MessagePipePerfTest); -}; - -TEST_F(MessagePipePerfTest, PingPong) { - MojoHandle server_handle, client_handle; - CreateMessagePipe(&server_handle, &client_handle); - - base::Thread client_thread("PingPongClient"); - client_thread.Start(); - client_thread.task_runner()->PostTask( - FROM_HERE, - base::Bind(base::IgnoreResult(&RunPingPongClient), client_handle)); - - RunPingPongServer(server_handle); -} - -// 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. -DEFINE_TEST_CLIENT_WITH_PIPE(PingPongClient, MessagePipePerfTest, h) { - return RunPingPongClient(h); -} - -// 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. -TEST_F(MessagePipePerfTest, MultiprocessPingPong) { - RUN_CHILD_ON_PIPE(PingPongClient, h) - RunPingPongServer(h); - END_CHILD() -} - -} // namespace -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/message_pipe_unittest.cc b/mojo/edk/system/message_pipe_unittest.cc deleted file mode 100644 index e6f1ff6..0000000 --- a/mojo/edk/system/message_pipe_unittest.cc +++ /dev/null @@ -1,699 +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 <string.h> - -#include "base/memory/ref_counted.h" -#include "mojo/edk/system/test_utils.h" -#include "mojo/edk/test/mojo_test_base.h" -#include "mojo/public/c/system/core.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { -namespace edk { -namespace { - -const MojoHandleSignals kAllSignals = MOJO_HANDLE_SIGNAL_READABLE | - MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED; -static const char kHelloWorld[] = "hello world"; - -class MessagePipeTest : public test::MojoTestBase { - public: - MessagePipeTest() { - CHECK_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(nullptr, &pipe0_, &pipe1_)); - } - - ~MessagePipeTest() override { - if (pipe0_ != MOJO_HANDLE_INVALID) - CHECK_EQ(MOJO_RESULT_OK, MojoClose(pipe0_)); - if (pipe1_ != MOJO_HANDLE_INVALID) - CHECK_EQ(MOJO_RESULT_OK, MojoClose(pipe1_)); - } - - MojoResult WriteMessage(MojoHandle message_pipe_handle, - const void* bytes, - uint32_t num_bytes) { - return MojoWriteMessage(message_pipe_handle, bytes, num_bytes, nullptr, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE); - } - - MojoResult ReadMessage(MojoHandle message_pipe_handle, - void* bytes, - uint32_t* num_bytes, - bool may_discard = false) { - return MojoReadMessage(message_pipe_handle, bytes, num_bytes, nullptr, 0, - may_discard ? MOJO_READ_MESSAGE_FLAG_MAY_DISCARD : - MOJO_READ_MESSAGE_FLAG_NONE); - } - - MojoHandle pipe0_, pipe1_; - - private: - DISALLOW_COPY_AND_ASSIGN(MessagePipeTest); -}; - -using FuseMessagePipeTest = test::MojoTestBase; - -TEST_F(MessagePipeTest, WriteData) { - ASSERT_EQ(MOJO_RESULT_OK, - WriteMessage(pipe0_, kHelloWorld, sizeof(kHelloWorld))); -} - -// 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_F(MessagePipeTest, Basic) { - 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; - ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, ReadMessage(pipe0_, buffer, &buffer_size)); - ASSERT_EQ(kBufferSize, buffer_size); - ASSERT_EQ(123, buffer[0]); - ASSERT_EQ(456, buffer[1]); - - // Ditto for port 1. - buffer[0] = 123; - buffer[1] = 456; - buffer_size = kBufferSize; - ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, ReadMessage(pipe1_, buffer, &buffer_size)); - - // Write from port 1 (to port 0). - buffer[0] = 789012345; - buffer[1] = 0; - ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0]))); - - MojoHandleSignalsState state; - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state)); - - // Read from port 0. - buffer[0] = 123; - buffer[1] = 456; - buffer_size = kBufferSize; - ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe0_, buffer, &buffer_size)); - ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); - ASSERT_EQ(789012345, buffer[0]); - ASSERT_EQ(456, buffer[1]); - - // Read again from port 0 -- it should be empty. - buffer_size = kBufferSize; - ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, ReadMessage(pipe0_, buffer, &buffer_size)); - - // Write two messages from port 0 (to port 1). - buffer[0] = 123456789; - buffer[1] = 0; - ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe0_, buffer, sizeof(buffer[0]))); - buffer[0] = 234567890; - buffer[1] = 0; - ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe0_, buffer, sizeof(buffer[0]))); - - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &state)); - - // 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; - ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, - ReadMessage(pipe1_, nullptr, &buffer_size)); - ASSERT_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; - ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, - ReadMessage(pipe1_, buffer, &buffer_size)); - ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); - ASSERT_EQ(123, buffer[0]); - ASSERT_EQ(456, buffer[1]); - - // Read from port 1. - buffer[0] = 123; - buffer[1] = 456; - buffer_size = kBufferSize; - ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe1_, buffer, &buffer_size)); - ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); - ASSERT_EQ(123456789, buffer[0]); - ASSERT_EQ(456, buffer[1]); - - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &state)); - - // Read again from port 1. - buffer[0] = 123; - buffer[1] = 456; - buffer_size = kBufferSize; - ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe1_, buffer, &buffer_size)); - ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); - ASSERT_EQ(234567890, buffer[0]); - ASSERT_EQ(456, buffer[1]); - - // Read again from port 1 -- it should be empty. - buffer_size = kBufferSize; - ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, ReadMessage(pipe1_, buffer, &buffer_size)); - - // Write from port 0 (to port 1). - buffer[0] = 345678901; - buffer[1] = 0; - ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe0_, buffer, sizeof(buffer[0]))); - - // Close port 0. - MojoClose(pipe0_); - pipe0_ = MOJO_HANDLE_INVALID; - - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &state)); - - // Try to write from port 1 (to port 0). - buffer[0] = 456789012; - buffer[1] = 0; - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - WriteMessage(pipe1_, buffer, sizeof(buffer[0]))); - - // Read from port 1; should still get message (even though port 0 was closed). - buffer[0] = 123; - buffer[1] = 456; - buffer_size = kBufferSize; - ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe1_, buffer, &buffer_size)); - ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); - ASSERT_EQ(345678901, buffer[0]); - ASSERT_EQ(456, buffer[1]); - - // Read again from port 1 -- it should be empty (and port 0 is closed). - buffer_size = kBufferSize; - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - ReadMessage(pipe1_, buffer, &buffer_size)); -} - -TEST_F(MessagePipeTest, CloseWithQueuedIncomingMessages) { - 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; - ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, kBufferSize)); - } - - MojoHandleSignalsState state; - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state)); - - // Port 0 shouldn't be empty. - buffer_size = 0; - ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, - ReadMessage(pipe0_, nullptr, &buffer_size)); - ASSERT_EQ(kBufferSize, buffer_size); - - // Close port 0 first, which should have outstanding (incoming) messages. - MojoClose(pipe0_); - MojoClose(pipe1_); - pipe0_ = pipe1_ = MOJO_HANDLE_INVALID; -} - -TEST_F(MessagePipeTest, DiscardMode) { - 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; - ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0]))); - - MojoHandleSignalsState state; - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state)); - - // Read/discard from port 0 (no buffer); get size. - buffer_size = 0; - ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, - ReadMessage(pipe0_, nullptr, &buffer_size, true)); - ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); - - // Read again from port 0 -- it should be empty. - buffer_size = kBufferSize; - ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, - ReadMessage(pipe0_, buffer, &buffer_size, true)); - - // Write from port 1 (to port 0). - buffer[0] = 890123456; - buffer[1] = 0; - ASSERT_EQ(MOJO_RESULT_OK, - WriteMessage(pipe1_, buffer, sizeof(buffer[0]))); - - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state)); - - // Read from port 0 (buffer big enough). - buffer[0] = 123; - buffer[1] = 456; - buffer_size = kBufferSize; - ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe0_, buffer, &buffer_size, true)); - ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); - ASSERT_EQ(890123456, buffer[0]); - ASSERT_EQ(456, buffer[1]); - - // Read again from port 0 -- it should be empty. - buffer_size = kBufferSize; - ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, - ReadMessage(pipe0_, buffer, &buffer_size, true)); - - // Write from port 1 (to port 0). - buffer[0] = 901234567; - buffer[1] = 0; - ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0]))); - - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state)); - - // Read/discard from port 0 (buffer too small); get size. - buffer_size = 1; - ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, - ReadMessage(pipe0_, buffer, &buffer_size, true)); - ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); - - // Read again from port 0 -- it should be empty. - buffer_size = kBufferSize; - ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, - ReadMessage(pipe0_, buffer, &buffer_size, true)); - - // Write from port 1 (to port 0). - buffer[0] = 123456789; - buffer[1] = 0; - ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0]))); - - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state)); - - // Discard from port 0. - buffer_size = 1; - ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, - ReadMessage(pipe0_, nullptr, 0, true)); - - // Read again from port 0 -- it should be empty. - buffer_size = kBufferSize; - ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, - ReadMessage(pipe0_, buffer, &buffer_size, true)); -} - -TEST_F(MessagePipeTest, BasicWaiting) { - MojoHandleSignalsState 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). Not yet readable. Peer - // not closed. - hss = GetSignalsState(pipe0_); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - ASSERT_EQ(kAllSignals, hss.satisfiable_signals); - hss = MojoHandleSignalsState(); - - // Write from port 0 (to port 1), to make port 1 readable. - buffer[0] = 123456789; - ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe0_, buffer, kBufferSize)); - - // Port 1 should already be readable now. - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - ASSERT_EQ(kAllSignals, hss.satisfiable_signals); - // ... and still writable. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_WRITABLE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - hss.satisfied_signals); - ASSERT_EQ(kAllSignals, hss.satisfiable_signals); - - // Close port 0. - MojoClose(pipe0_); - pipe0_ = MOJO_HANDLE_INVALID; - - // Port 1 should be signaled with peer closed. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Port 1 should not be writable. - hss = MojoHandleSignalsState(); - - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_WRITABLE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // But it should still be readable. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Read from port 1. - buffer[0] = 0; - buffer_size = kBufferSize; - ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe1_, buffer, &buffer_size)); - ASSERT_EQ(123456789, buffer[0]); - - // Now port 1 should no longer be readable. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); -} - -TEST_F(MessagePipeTest, InvalidMessageObjects) { - // null message - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoFreeMessage(MOJO_MESSAGE_HANDLE_INVALID)); - - // null message - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoGetMessageBuffer(MOJO_MESSAGE_HANDLE_INVALID, nullptr)); - - // Non-zero num_handles with null handles array. - ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoAllocMessage(0, nullptr, 1, MOJO_ALLOC_MESSAGE_FLAG_NONE, - nullptr)); -} - -TEST_F(MessagePipeTest, AllocAndFreeMessage) { - const std::string kMessage = "Hello, world."; - MojoMessageHandle message = MOJO_MESSAGE_HANDLE_INVALID; - ASSERT_EQ(MOJO_RESULT_OK, - MojoAllocMessage(static_cast<uint32_t>(kMessage.size()), nullptr, 0, - MOJO_ALLOC_MESSAGE_FLAG_NONE, &message)); - ASSERT_NE(MOJO_MESSAGE_HANDLE_INVALID, message); - ASSERT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message)); -} - -TEST_F(MessagePipeTest, WriteAndReadMessageObject) { - const std::string kMessage = "Hello, world."; - MojoMessageHandle message = MOJO_MESSAGE_HANDLE_INVALID; - EXPECT_EQ(MOJO_RESULT_OK, - MojoAllocMessage(static_cast<uint32_t>(kMessage.size()), nullptr, 0, - MOJO_ALLOC_MESSAGE_FLAG_NONE, &message)); - ASSERT_NE(MOJO_MESSAGE_HANDLE_INVALID, message); - - void* buffer = nullptr; - EXPECT_EQ(MOJO_RESULT_OK, MojoGetMessageBuffer(message, &buffer)); - ASSERT_TRUE(buffer); - memcpy(buffer, kMessage.data(), kMessage.size()); - - MojoHandle a, b; - CreateMessagePipe(&a, &b); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWriteMessageNew(a, message, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE)); - uint32_t num_bytes = 0; - uint32_t num_handles = 0; - EXPECT_EQ(MOJO_RESULT_OK, - MojoReadMessageNew(b, &message, &num_bytes, nullptr, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE)); - ASSERT_NE(MOJO_MESSAGE_HANDLE_INVALID, message); - EXPECT_EQ(static_cast<uint32_t>(kMessage.size()), num_bytes); - EXPECT_EQ(0u, num_handles); - - EXPECT_EQ(MOJO_RESULT_OK, MojoGetMessageBuffer(message, &buffer)); - ASSERT_TRUE(buffer); - - EXPECT_EQ(0, strncmp(static_cast<const char*>(buffer), kMessage.data(), - num_bytes)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); -} - -#if !defined(OS_IOS) - -const size_t kPingPongHandlesPerIteration = 50; -const size_t kPingPongIterations = 500; - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(HandlePingPong, MessagePipeTest, h) { - // Waits for a handle to become readable and writes it back to the sender. - for (size_t i = 0; i < kPingPongIterations; i++) { - MojoHandle handles[kPingPongHandlesPerIteration]; - ReadMessageWithHandles(h, handles, kPingPongHandlesPerIteration); - WriteMessageWithHandles(h, "", handles, kPingPongHandlesPerIteration); - } - - EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE)); - char msg[4]; - uint32_t num_bytes = 4; - EXPECT_EQ(MOJO_RESULT_OK, ReadMessage(h, msg, &num_bytes)); -} - -// This test is flaky: http://crbug.com/585784 -TEST_F(MessagePipeTest, DISABLED_DataPipeConsumerHandlePingPong) { - MojoHandle p, c[kPingPongHandlesPerIteration]; - for (size_t i = 0; i < kPingPongHandlesPerIteration; ++i) { - EXPECT_EQ(MOJO_RESULT_OK, MojoCreateDataPipe(nullptr, &p, &c[i])); - MojoClose(p); - } - - RUN_CHILD_ON_PIPE(HandlePingPong, h) - for (size_t i = 0; i < kPingPongIterations; i++) { - WriteMessageWithHandles(h, "", c, kPingPongHandlesPerIteration); - ReadMessageWithHandles(h, c, kPingPongHandlesPerIteration); - } - WriteMessage(h, "quit", 4); - END_CHILD() - for (size_t i = 0; i < kPingPongHandlesPerIteration; ++i) - MojoClose(c[i]); -} - -// This test is flaky: http://crbug.com/585784 -TEST_F(MessagePipeTest, DISABLED_DataPipeProducerHandlePingPong) { - MojoHandle p[kPingPongHandlesPerIteration], c; - for (size_t i = 0; i < kPingPongHandlesPerIteration; ++i) { - EXPECT_EQ(MOJO_RESULT_OK, MojoCreateDataPipe(nullptr, &p[i], &c)); - MojoClose(c); - } - - RUN_CHILD_ON_PIPE(HandlePingPong, h) - for (size_t i = 0; i < kPingPongIterations; i++) { - WriteMessageWithHandles(h, "", p, kPingPongHandlesPerIteration); - ReadMessageWithHandles(h, p, kPingPongHandlesPerIteration); - } - WriteMessage(h, "quit", 4); - END_CHILD() - for (size_t i = 0; i < kPingPongHandlesPerIteration; ++i) - MojoClose(p[i]); -} - -TEST_F(MessagePipeTest, SharedBufferHandlePingPong) { - MojoHandle buffers[kPingPongHandlesPerIteration]; - for (size_t i = 0; i <kPingPongHandlesPerIteration; ++i) - EXPECT_EQ(MOJO_RESULT_OK, MojoCreateSharedBuffer(nullptr, 1, &buffers[i])); - - RUN_CHILD_ON_PIPE(HandlePingPong, h) - for (size_t i = 0; i < kPingPongIterations; i++) { - WriteMessageWithHandles(h, "", buffers, kPingPongHandlesPerIteration); - ReadMessageWithHandles(h, buffers, kPingPongHandlesPerIteration); - } - WriteMessage(h, "quit", 4); - END_CHILD() - for (size_t i = 0; i < kPingPongHandlesPerIteration; ++i) - MojoClose(buffers[i]); -} - -#endif // !defined(OS_IOS) - -TEST_F(FuseMessagePipeTest, Basic) { - // Test that we can fuse pipes and they still work. - - MojoHandle a, b, c, d; - CreateMessagePipe(&a, &b); - CreateMessagePipe(&c, &d); - - EXPECT_EQ(MOJO_RESULT_OK, MojoFuseMessagePipes(b, c)); - - // Handles b and c should be closed. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c)); - - const std::string kTestMessage1 = "Hello, world!"; - const std::string kTestMessage2 = "Goodbye, world!"; - - WriteMessage(a, kTestMessage1); - EXPECT_EQ(kTestMessage1, ReadMessage(d)); - - WriteMessage(d, kTestMessage2); - EXPECT_EQ(kTestMessage2, ReadMessage(a)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); -} - -TEST_F(FuseMessagePipeTest, FuseAfterPeerWrite) { - // Test that messages written before fusion are eventually delivered. - - MojoHandle a, b, c, d; - CreateMessagePipe(&a, &b); - CreateMessagePipe(&c, &d); - - const std::string kTestMessage1 = "Hello, world!"; - const std::string kTestMessage2 = "Goodbye, world!"; - WriteMessage(a, kTestMessage1); - WriteMessage(d, kTestMessage2); - - EXPECT_EQ(MOJO_RESULT_OK, MojoFuseMessagePipes(b, c)); - - // Handles b and c should be closed. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c)); - - EXPECT_EQ(kTestMessage1, ReadMessage(d)); - EXPECT_EQ(kTestMessage2, ReadMessage(a)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); -} - -TEST_F(FuseMessagePipeTest, NoFuseAfterWrite) { - // Test that a pipe endpoint which has been written to cannot be fused. - - MojoHandle a, b, c, d; - CreateMessagePipe(&a, &b); - CreateMessagePipe(&c, &d); - - WriteMessage(b, "shouldn't have done that!"); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoFuseMessagePipes(b, c)); - - // Handles b and c should be closed. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); -} - -TEST_F(FuseMessagePipeTest, NoFuseSelf) { - // Test that a pipe's own endpoints can't be fused together. - - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoFuseMessagePipes(a, b)); - - // Handles a and b should be closed. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(a)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b)); -} - -TEST_F(FuseMessagePipeTest, FuseInvalidArguments) { - MojoHandle a, b, c, d; - CreateMessagePipe(&a, &b); - CreateMessagePipe(&c, &d); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); - - // Can't fuse an invalid handle. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoFuseMessagePipes(b, c)); - - // Handle c should be closed. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c)); - - // Can't fuse a non-message pipe handle. - MojoHandle e, f; - CreateDataPipe(&e, &f, 16); - - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoFuseMessagePipes(e, d)); - - // Handles d and e should be closed. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(d)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(e)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(f)); -} - -TEST_F(FuseMessagePipeTest, FuseAfterPeerClosure) { - // Test that peer closure prior to fusion can still be detected after fusion. - - MojoHandle a, b, c, d; - CreateMessagePipe(&a, &b); - CreateMessagePipe(&c, &d); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); - EXPECT_EQ(MOJO_RESULT_OK, MojoFuseMessagePipes(b, c)); - - // Handles b and c should be closed. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c)); - - EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(d, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); -} - -TEST_F(FuseMessagePipeTest, FuseAfterPeerWriteAndClosure) { - // Test that peer write and closure prior to fusion still results in the - // both message arrival and awareness of peer closure. - - MojoHandle a, b, c, d; - CreateMessagePipe(&a, &b); - CreateMessagePipe(&c, &d); - - const std::string kTestMessage = "ayyy lmao"; - WriteMessage(a, kTestMessage); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoFuseMessagePipes(b, c)); - - // Handles b and c should be closed. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c)); - - EXPECT_EQ(kTestMessage, ReadMessage(d)); - EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(d, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); -} - -TEST_F(MessagePipeTest, ClosePipesStressTest) { - // Stress test to exercise https://crbug.com/665869. - const size_t kNumPipes = 100000; - for (size_t i = 0; i < kNumPipes; ++i) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - MojoClose(a); - MojoClose(b); - } -} - -} // namespace -} // namespace edk -} // 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 37248d1..0000000 --- a/mojo/edk/system/multiprocess_message_pipe_unittest.cc +++ /dev/null @@ -1,1366 +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 <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <string.h> - -#include <string> -#include <utility> -#include <vector> - -#include "base/bind.h" -#include "base/containers/hash_tables.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/logging.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "base/strings/string_split.h" -#include "build/build_config.h" -#include "mojo/edk/embedder/platform_channel_pair.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/handle_signals_state.h" -#include "mojo/edk/system/test_utils.h" -#include "mojo/edk/test/mojo_test_base.h" -#include "mojo/edk/test/test_utils.h" -#include "mojo/public/c/system/buffer.h" -#include "mojo/public/c/system/functions.h" -#include "mojo/public/c/system/types.h" -#include "mojo/public/cpp/system/simple_watcher.h" -#include "mojo/public/cpp/system/wait.h" -#include "testing/gtest/include/gtest/gtest.h" - - -namespace mojo { -namespace edk { -namespace { - -class MultiprocessMessagePipeTest : public test::MojoTestBase { - protected: - // Convenience class for tests which will control command-driven children. - // See the CommandDrivenClient definition below. - class CommandDrivenClientController { - public: - explicit CommandDrivenClientController(MojoHandle h) : h_(h) {} - - void Send(const std::string& command) { - WriteMessage(h_, command); - EXPECT_EQ("ok", ReadMessage(h_)); - } - - void SendHandle(const std::string& name, MojoHandle p) { - WriteMessageWithHandles(h_, "take:" + name, &p, 1); - EXPECT_EQ("ok", ReadMessage(h_)); - } - - MojoHandle RetrieveHandle(const std::string& name) { - WriteMessage(h_, "return:" + name); - MojoHandle p; - EXPECT_EQ("ok", ReadMessageWithHandles(h_, &p, 1)); - return p; - } - - void Exit() { WriteMessage(h_, "exit"); } - - private: - MojoHandle h_; - }; -}; - -class MultiprocessMessagePipeTestWithPeerSupport - : public MultiprocessMessagePipeTest, - public testing::WithParamInterface<test::MojoTestBase::LaunchType> { - protected: - void SetUp() override { - test::MojoTestBase::SetUp(); - set_launch_type(GetParam()); - } -}; - -// 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. -DEFINE_TEST_CLIENT_WITH_PIPE(EchoEcho, MultiprocessMessagePipeTest, h) { - 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 = WaitForSignals(h, 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(MojoReadMessage(h, &read_buffer[0], - &read_buffer_size, nullptr, - 0, 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(MojoWriteMessage(h, write_buffer.data(), - static_cast<uint32_t>(write_buffer.size()), - nullptr, 0u, MOJO_WRITE_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - } - - return rv; -} - -TEST_P(MultiprocessMessagePipeTestWithPeerSupport, Basic) { - RUN_CHILD_ON_PIPE(EchoEcho, h) - std::string hello("hello"); - ASSERT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(h, hello.data(), - static_cast<uint32_t>(hello.size()), nullptr, 0u, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - HandleSignalsState hss; - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(h, 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(MojoReadMessage(h, &read_buffer[0], - &read_buffer_size, nullptr, 0, - MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - read_buffer.resize(read_buffer_size); - VLOG(2) << "Parent got: " << read_buffer; - ASSERT_EQ(hello + hello, read_buffer); - - std::string quitquitquit("quitquitquit"); - CHECK_EQ(MojoWriteMessage(h, quitquitquit.data(), - static_cast<uint32_t>(quitquitquit.size()), - nullptr, 0u, MOJO_WRITE_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - END_CHILD_AND_EXPECT_EXIT_CODE(1 % 100); -} - -TEST_P(MultiprocessMessagePipeTestWithPeerSupport, QueueMessages) { - static const size_t kNumMessages = 1001; - RUN_CHILD_ON_PIPE(EchoEcho, h) - for (size_t i = 0; i < kNumMessages; i++) { - std::string write_buffer(i, 'A' + (i % 26)); - ASSERT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(h, write_buffer.data(), - static_cast<uint32_t>(write_buffer.size()), - nullptr, 0u, MOJO_WRITE_MESSAGE_FLAG_NONE)); - } - - for (size_t i = 0; i < kNumMessages; i++) { - HandleSignalsState hss; - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(h, 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. - ASSERT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE)); - ASSERT_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()); - ASSERT_EQ(MojoReadMessage(h, &read_buffer[0], - &read_buffer_size, nullptr, 0, - MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - read_buffer.resize(read_buffer_size); - - ASSERT_EQ(std::string(i * 2, 'A' + (i % 26)), read_buffer); - } - - const std::string quitquitquit("quitquitquit"); - ASSERT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(h, quitquitquit.data(), - static_cast<uint32_t>(quitquitquit.size()), - nullptr, 0u, MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Wait for it to become readable, which should fail (since we sent - // "quitquitquit"). - HandleSignalsState hss; - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - END_CHILD_AND_EXPECT_EXIT_CODE(static_cast<int>(kNumMessages % 100)); -} - -DEFINE_TEST_CLIENT_WITH_PIPE(CheckSharedBuffer, MultiprocessMessagePipeTest, - h) { - // Wait for the first message from our parent. - HandleSignalsState hss; - CHECK_EQ(WaitForSignals(h, 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()); - MojoHandle handles[10]; - uint32_t num_handlers = arraysize(handles); // Maximum number to receive - CHECK_EQ(MojoReadMessage(h, &read_buffer[0], - &num_bytes, &handles[0], - &num_handlers, MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - read_buffer.resize(num_bytes); - CHECK_EQ(read_buffer, std::string("go 1")); - CHECK_EQ(num_handlers, 1u); - - // Make a mapping. - void* buffer; - CHECK_EQ(MojoMapBuffer(handles[0], 0, 100, &buffer, - MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE), - MOJO_RESULT_OK); - - // Write some stuff to the shared buffer. - static const char kHello[] = "hello"; - memcpy(buffer, kHello, sizeof(kHello)); - - // We should be able to close the dispatcher now. - MojoClose(handles[0]); - - // And send a message to signal that we've written stuff. - const std::string go2("go 2"); - CHECK_EQ(MojoWriteMessage(h, go2.data(), - static_cast<uint32_t>(go2.size()), nullptr, 0u, - MOJO_WRITE_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - - // Now wait for our parent to send us a message. - hss = HandleSignalsState(); - CHECK_EQ(WaitForSignals(h, 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(MojoReadMessage(h, &read_buffer[0], &num_bytes, - nullptr, 0, 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(buffer, kWorld, sizeof(kWorld)), 0); - - // And we're done. - - return 0; -} - -TEST_F(MultiprocessMessagePipeTest, SharedBufferPassing) { - RUN_CHILD_ON_PIPE(CheckSharedBuffer, h) - // Make a shared buffer. - MojoCreateSharedBufferOptions options; - options.struct_size = sizeof(options); - options.flags = MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE; - - MojoHandle shared_buffer; - ASSERT_EQ(MOJO_RESULT_OK, - MojoCreateSharedBuffer(&options, 100, &shared_buffer)); - - // Send the shared buffer. - const std::string go1("go 1"); - - MojoHandle duplicated_shared_buffer; - ASSERT_EQ(MOJO_RESULT_OK, - MojoDuplicateBufferHandle( - shared_buffer, - nullptr, - &duplicated_shared_buffer)); - MojoHandle handles[1]; - handles[0] = duplicated_shared_buffer; - ASSERT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(h, &go1[0], - static_cast<uint32_t>(go1.size()), &handles[0], - arraysize(handles), - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Wait for a message from the child. - HandleSignalsState hss; - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(h, 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()); - ASSERT_EQ(MOJO_RESULT_OK, - MojoReadMessage(h, &read_buffer[0], - &num_bytes, nullptr, 0, - MOJO_READ_MESSAGE_FLAG_NONE)); - read_buffer.resize(num_bytes); - ASSERT_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"; - void* buffer; - CHECK_EQ(MojoMapBuffer(shared_buffer, 0, 100, &buffer, - MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE), - MOJO_RESULT_OK); - ASSERT_EQ(0, memcmp(buffer, kHello, sizeof(kHello))); - - // Now we'll write some stuff to the shared buffer. - static const char kWorld[] = "world!!!"; - memcpy(buffer, kWorld, sizeof(kWorld)); - - // And send a message to signal that we've written stuff. - const std::string go3("go 3"); - ASSERT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(h, &go3[0], - static_cast<uint32_t>(go3.size()), nullptr, 0u, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Wait for |h| to become readable, which should fail. - hss = HandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - END_CHILD() -} - -DEFINE_TEST_CLIENT_WITH_PIPE(CheckPlatformHandleFile, - MultiprocessMessagePipeTest, h) { - HandleSignalsState hss; - CHECK_EQ(WaitForSignals(h, 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()); - MojoHandle handles[255]; // Maximum number to receive. - uint32_t num_handlers = arraysize(handles); - - CHECK_EQ(MojoReadMessage(h, &read_buffer[0], - &num_bytes, &handles[0], - &num_handlers, MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - - 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) { - ScopedPlatformHandle h; - CHECK_EQ(PassWrappedPlatformHandle(handles[i], &h), MOJO_RESULT_OK); - CHECK(h.is_valid()); - MojoClose(handles[i]); - - base::ScopedFILE fp(test::FILEFromPlatformHandle(std::move(h), "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 MultiprocessMessagePipeTest, - public testing::WithParamInterface<size_t> {}; - -TEST_P(MultiprocessMessagePipeTestWithPipeCount, PlatformHandlePassing) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - RUN_CHILD_ON_PIPE(CheckPlatformHandleFile, h) - std::vector<MojoHandle> handles; - - size_t pipe_count = GetParam(); - for (size_t i = 0; i < pipe_count; ++i) { - base::FilePath unused; - base::ScopedFILE fp( - CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &unused)); - const std::string world("world"); - CHECK_EQ(fwrite(&world[0], 1, world.size(), fp.get()), world.size()); - fflush(fp.get()); - rewind(fp.get()); - MojoHandle handle; - ASSERT_EQ( - CreatePlatformHandleWrapper( - ScopedPlatformHandle(test::PlatformHandleFromFILE(std::move(fp))), - &handle), - MOJO_RESULT_OK); - handles.push_back(handle); - } - - char message[128]; - snprintf(message, sizeof(message), "hello %d", - static_cast<int>(pipe_count)); - ASSERT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(h, message, - static_cast<uint32_t>(strlen(message)), - &handles[0], - static_cast<uint32_t>(handles.size()), - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Wait for it to become readable, which should fail. - HandleSignalsState hss; - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - END_CHILD() -} - -// Android multi-process tests are not executing the new process. This is flaky. -#if !defined(OS_ANDROID) -INSTANTIATE_TEST_CASE_P(PipeCount, - MultiprocessMessagePipeTestWithPipeCount, - // TODO(rockot): Re-enable the 140-pipe case when - // ChannelPosix has support for sending lots of handles. - testing::Values(1u, 128u /*, 140u*/)); -#endif - -DEFINE_TEST_CLIENT_WITH_PIPE(CheckMessagePipe, MultiprocessMessagePipeTest, h) { - // Wait for the first message from our parent. - HandleSignalsState hss; - CHECK_EQ(WaitForSignals(h, 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 message pipe. - MojoHandle handles[10]; - uint32_t num_handlers = arraysize(handles); - CHECK_EQ(MojoReadMessage(h, nullptr, - nullptr, &handles[0], - &num_handlers, MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - CHECK_EQ(num_handlers, 1u); - - // Read data from the received message pipe. - CHECK_EQ(WaitForSignals(handles[0], 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 read_buffer_size = static_cast<uint32_t>(read_buffer.size()); - CHECK_EQ(MojoReadMessage(handles[0], &read_buffer[0], - &read_buffer_size, nullptr, - 0, MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - read_buffer.resize(read_buffer_size); - CHECK_EQ(read_buffer, std::string("hello")); - - // Now write some data into the message pipe. - std::string write_buffer = "world"; - CHECK_EQ(MojoWriteMessage(handles[0], write_buffer.data(), - static_cast<uint32_t>(write_buffer.size()), nullptr, - 0u, MOJO_WRITE_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - MojoClose(handles[0]); - return 0; -} - -TEST_P(MultiprocessMessagePipeTestWithPeerSupport, MessagePipePassing) { - RUN_CHILD_ON_PIPE(CheckMessagePipe, h) - MojoCreateSharedBufferOptions options; - options.struct_size = sizeof(options); - options.flags = MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE; - - MojoHandle mp1, mp2; - ASSERT_EQ(MOJO_RESULT_OK, - MojoCreateMessagePipe(nullptr, &mp1, &mp2)); - - // Write a string into one end of the new message pipe and send the other - // end. - const std::string hello("hello"); - ASSERT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(mp1, &hello[0], - static_cast<uint32_t>(hello.size()), nullptr, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ASSERT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(h, nullptr, 0, &mp2, 1, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Wait for a message from the child. - HandleSignalsState hss; - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(mp1, 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 read_buffer_size = static_cast<uint32_t>(read_buffer.size()); - CHECK_EQ(MojoReadMessage(mp1, &read_buffer[0], - &read_buffer_size, nullptr, - 0, MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - read_buffer.resize(read_buffer_size); - CHECK_EQ(read_buffer, std::string("world")); - - MojoClose(mp1); - END_CHILD() -} - -TEST_P(MultiprocessMessagePipeTestWithPeerSupport, MessagePipeTwoPassing) { - RUN_CHILD_ON_PIPE(CheckMessagePipe, h) - MojoHandle mp1, mp2; - ASSERT_EQ(MOJO_RESULT_OK, - MojoCreateMessagePipe(nullptr, &mp2, &mp1)); - - // Write a string into one end of the new message pipe and send the other - // end. - const std::string hello("hello"); - ASSERT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(mp1, &hello[0], - static_cast<uint32_t>(hello.size()), nullptr, 0u, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ASSERT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(h, nullptr, 0u, &mp2, 1u, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Wait for a message from the child. - HandleSignalsState hss; - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(mp1, 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 read_buffer_size = static_cast<uint32_t>(read_buffer.size()); - CHECK_EQ(MojoReadMessage(mp1, &read_buffer[0], - &read_buffer_size, nullptr, - 0, MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - read_buffer.resize(read_buffer_size); - CHECK_EQ(read_buffer, std::string("world")); - END_CHILD(); -} - -DEFINE_TEST_CLIENT_WITH_PIPE(DataPipeConsumer, MultiprocessMessagePipeTest, h) { - // Wait for the first message from our parent. - HandleSignalsState hss; - CHECK_EQ(WaitForSignals(h, 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 message pipe. - MojoHandle handles[10]; - uint32_t num_handlers = arraysize(handles); - CHECK_EQ(MojoReadMessage(h, nullptr, - nullptr, &handles[0], - &num_handlers, MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - CHECK_EQ(num_handlers, 1u); - - // Read data from the received message pipe. - CHECK_EQ(WaitForSignals(handles[0], 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 read_buffer_size = static_cast<uint32_t>(read_buffer.size()); - CHECK_EQ(MojoReadMessage(handles[0], &read_buffer[0], - &read_buffer_size, nullptr, - 0, MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - read_buffer.resize(read_buffer_size); - CHECK_EQ(read_buffer, std::string("hello")); - - // Now write some data into the message pipe. - std::string write_buffer = "world"; - CHECK_EQ(MojoWriteMessage(handles[0], write_buffer.data(), - static_cast<uint32_t>(write_buffer.size()), - nullptr, 0u, MOJO_WRITE_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - MojoClose(handles[0]); - return 0; -} - -TEST_F(MultiprocessMessagePipeTest, DataPipeConsumer) { - RUN_CHILD_ON_PIPE(DataPipeConsumer, h) - MojoCreateSharedBufferOptions options; - options.struct_size = sizeof(options); - options.flags = MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE; - - MojoHandle mp1, mp2; - ASSERT_EQ(MOJO_RESULT_OK, - MojoCreateMessagePipe(nullptr, &mp2, &mp1)); - - // Write a string into one end of the new message pipe and send the other - // end. - const std::string hello("hello"); - ASSERT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(mp1, &hello[0], - static_cast<uint32_t>(hello.size()), nullptr, 0u, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - ASSERT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(h, nullptr, 0, &mp2, 1u, - MOJO_WRITE_MESSAGE_FLAG_NONE)); - - // Wait for a message from the child. - HandleSignalsState hss; - ASSERT_EQ(MOJO_RESULT_OK, - WaitForSignals(mp1, 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 read_buffer_size = static_cast<uint32_t>(read_buffer.size()); - CHECK_EQ(MojoReadMessage(mp1, &read_buffer[0], - &read_buffer_size, nullptr, - 0, MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - read_buffer.resize(read_buffer_size); - CHECK_EQ(read_buffer, std::string("world")); - - MojoClose(mp1); - END_CHILD(); -} - -TEST_P(MultiprocessMessagePipeTestWithPeerSupport, CreateMessagePipe) { - MojoHandle p0, p1; - CreateMessagePipe(&p0, &p1); - VerifyTransmission(p0, p1, std::string(10 * 1024 * 1024, 'a')); - VerifyTransmission(p1, p0, std::string(10 * 1024 * 1024, 'e')); - - CloseHandle(p0); - CloseHandle(p1); -} - -TEST_P(MultiprocessMessagePipeTestWithPeerSupport, PassMessagePipeLocal) { - MojoHandle p0, p1; - CreateMessagePipe(&p0, &p1); - VerifyTransmission(p0, p1, "testing testing"); - VerifyTransmission(p1, p0, "one two three"); - - MojoHandle p2, p3; - - CreateMessagePipe(&p2, &p3); - VerifyTransmission(p2, p3, "testing testing"); - VerifyTransmission(p3, p2, "one two three"); - - // Pass p2 over p0 to p1. - const std::string message = "ceci n'est pas une pipe"; - WriteMessageWithHandles(p0, message, &p2, 1); - EXPECT_EQ(message, ReadMessageWithHandles(p1, &p2, 1)); - - CloseHandle(p0); - CloseHandle(p1); - - // Verify that the received handle (now in p2) still works. - VerifyTransmission(p2, p3, "Easy come, easy go; will you let me go?"); - VerifyTransmission(p3, p2, "Bismillah! NO! We will not let you go!"); - - CloseHandle(p2); - CloseHandle(p3); -} - -// Echos the primordial channel until "exit". -DEFINE_TEST_CLIENT_WITH_PIPE(ChannelEchoClient, MultiprocessMessagePipeTest, - h) { - for (;;) { - std::string message = ReadMessage(h); - if (message == "exit") - break; - WriteMessage(h, message); - } - return 0; -} - -TEST_P(MultiprocessMessagePipeTestWithPeerSupport, MultiprocessChannelPipe) { - RUN_CHILD_ON_PIPE(ChannelEchoClient, h) - VerifyEcho(h, "in an interstellar burst"); - VerifyEcho(h, "i am back to save the universe"); - VerifyEcho(h, std::string(10 * 1024 * 1024, 'o')); - - WriteMessage(h, "exit"); - END_CHILD() -} - -// Receives a pipe handle from the primordial channel and echos on it until -// "exit". Used to test simple pipe transfer across processes via channels. -DEFINE_TEST_CLIENT_WITH_PIPE(EchoServiceClient, MultiprocessMessagePipeTest, - h) { - MojoHandle p; - ReadMessageWithHandles(h, &p, 1); - for (;;) { - std::string message = ReadMessage(p); - if (message == "exit") - break; - WriteMessage(p, message); - } - return 0; -} - -TEST_P(MultiprocessMessagePipeTestWithPeerSupport, - PassMessagePipeCrossProcess) { - MojoHandle p0, p1; - CreateMessagePipe(&p0, &p1); - RUN_CHILD_ON_PIPE(EchoServiceClient, h) - - // Pass one end of the pipe to the other process. - WriteMessageWithHandles(h, "here take this", &p1, 1); - - VerifyEcho(p0, "and you may ask yourself"); - VerifyEcho(p0, "where does that highway go?"); - VerifyEcho(p0, std::string(20 * 1024 * 1024, 'i')); - - WriteMessage(p0, "exit"); - END_CHILD() - CloseHandle(p0); -} - -// Receives a pipe handle from the primordial channel and reads new handles -// from it. Each read handle establishes a new echo channel. -DEFINE_TEST_CLIENT_WITH_PIPE(EchoServiceFactoryClient, - MultiprocessMessagePipeTest, h) { - MojoHandle p; - ReadMessageWithHandles(h, &p, 1); - - std::vector<Handle> handles(2); - handles[0] = Handle(h); - handles[1] = Handle(p); - std::vector<MojoHandleSignals> signals(2, MOJO_HANDLE_SIGNAL_READABLE); - for (;;) { - size_t index; - CHECK_EQ( - mojo::WaitMany(handles.data(), signals.data(), handles.size(), &index), - MOJO_RESULT_OK); - DCHECK_LE(index, handles.size()); - if (index == 0) { - // If data is available on the first pipe, it should be an exit command. - EXPECT_EQ(std::string("exit"), ReadMessage(h)); - break; - } else if (index == 1) { - // If the second pipe, it should be a new handle requesting echo service. - MojoHandle echo_request; - ReadMessageWithHandles(p, &echo_request, 1); - handles.push_back(Handle(echo_request)); - signals.push_back(MOJO_HANDLE_SIGNAL_READABLE); - } else { - // Otherwise it was one of our established echo pipes. Echo! - WriteMessage(handles[index].value(), ReadMessage(handles[index].value())); - } - } - - for (size_t i = 1; i < handles.size(); ++i) - CloseHandle(handles[i].value()); - - return 0; -} - -TEST_P(MultiprocessMessagePipeTestWithPeerSupport, - PassMoarMessagePipesCrossProcess) { - MojoHandle echo_factory_proxy, echo_factory_request; - CreateMessagePipe(&echo_factory_proxy, &echo_factory_request); - - MojoHandle echo_proxy_a, echo_request_a; - CreateMessagePipe(&echo_proxy_a, &echo_request_a); - - MojoHandle echo_proxy_b, echo_request_b; - CreateMessagePipe(&echo_proxy_b, &echo_request_b); - - MojoHandle echo_proxy_c, echo_request_c; - CreateMessagePipe(&echo_proxy_c, &echo_request_c); - - RUN_CHILD_ON_PIPE(EchoServiceFactoryClient, h) - WriteMessageWithHandles( - h, "gief factory naow plz", &echo_factory_request, 1); - - WriteMessageWithHandles(echo_factory_proxy, "give me an echo service plz!", - &echo_request_a, 1); - WriteMessageWithHandles(echo_factory_proxy, "give me one too!", - &echo_request_b, 1); - - VerifyEcho(echo_proxy_a, "i came here for an argument"); - VerifyEcho(echo_proxy_a, "shut your festering gob"); - VerifyEcho(echo_proxy_a, "mumble mumble mumble"); - - VerifyEcho(echo_proxy_b, "wubalubadubdub"); - VerifyEcho(echo_proxy_b, "wubalubadubdub"); - - WriteMessageWithHandles(echo_factory_proxy, "hook me up also thanks", - &echo_request_c, 1); - - VerifyEcho(echo_proxy_a, "the frobinators taste like frobinators"); - VerifyEcho(echo_proxy_b, "beep bop boop"); - VerifyEcho(echo_proxy_c, "zzzzzzzzzzzzzzzzzzzzzzzzzz"); - - WriteMessage(h, "exit"); - END_CHILD() - - CloseHandle(echo_factory_proxy); - CloseHandle(echo_proxy_a); - CloseHandle(echo_proxy_b); - CloseHandle(echo_proxy_c); -} - -TEST_P(MultiprocessMessagePipeTestWithPeerSupport, - ChannelPipesWithMultipleChildren) { - RUN_CHILD_ON_PIPE(ChannelEchoClient, a) - RUN_CHILD_ON_PIPE(ChannelEchoClient, b) - VerifyEcho(a, "hello child 0"); - VerifyEcho(b, "hello child 1"); - - WriteMessage(a, "exit"); - WriteMessage(b, "exit"); - END_CHILD() - END_CHILD() -} - -// Reads and turns a pipe handle some number of times to create lots of -// transient proxies. -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(PingPongPipeClient, - MultiprocessMessagePipeTest, h) { - const size_t kNumBounces = 50; - MojoHandle p0, p1; - ReadMessageWithHandles(h, &p0, 1); - ReadMessageWithHandles(h, &p1, 1); - for (size_t i = 0; i < kNumBounces; ++i) { - WriteMessageWithHandles(h, "", &p1, 1); - ReadMessageWithHandles(h, &p1, 1); - } - WriteMessageWithHandles(h, "", &p0, 1); - WriteMessage(p1, "bye"); - MojoClose(p1); - EXPECT_EQ("quit", ReadMessage(h)); -} - -TEST_P(MultiprocessMessagePipeTestWithPeerSupport, PingPongPipe) { - MojoHandle p0, p1; - CreateMessagePipe(&p0, &p1); - - RUN_CHILD_ON_PIPE(PingPongPipeClient, h) - const size_t kNumBounces = 50; - WriteMessageWithHandles(h, "", &p0, 1); - WriteMessageWithHandles(h, "", &p1, 1); - for (size_t i = 0; i < kNumBounces; ++i) { - ReadMessageWithHandles(h, &p1, 1); - WriteMessageWithHandles(h, "", &p1, 1); - } - ReadMessageWithHandles(h, &p0, 1); - WriteMessage(h, "quit"); - END_CHILD() - - EXPECT_EQ("bye", ReadMessage(p0)); - - // We should still be able to observe peer closure from the other end. - EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(p0, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); -} - -// Parses commands from the parent pipe and does whatever it's asked to do. -DEFINE_TEST_CLIENT_WITH_PIPE(CommandDrivenClient, MultiprocessMessagePipeTest, - h) { - base::hash_map<std::string, MojoHandle> named_pipes; - for (;;) { - MojoHandle p; - auto parts = base::SplitString(ReadMessageWithOptionalHandle(h, &p), ":", - base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); - CHECK(!parts.empty()); - std::string command = parts[0]; - if (command == "take") { - // Take a pipe. - CHECK_EQ(parts.size(), 2u); - CHECK_NE(p, MOJO_HANDLE_INVALID); - named_pipes[parts[1]] = p; - WriteMessage(h, "ok"); - } else if (command == "return") { - // Return a pipe. - CHECK_EQ(parts.size(), 2u); - CHECK_EQ(p, MOJO_HANDLE_INVALID); - p = named_pipes[parts[1]]; - CHECK_NE(p, MOJO_HANDLE_INVALID); - named_pipes.erase(parts[1]); - WriteMessageWithHandles(h, "ok", &p, 1); - } else if (command == "say") { - // Say something to a named pipe. - CHECK_EQ(parts.size(), 3u); - CHECK_EQ(p, MOJO_HANDLE_INVALID); - p = named_pipes[parts[1]]; - CHECK_NE(p, MOJO_HANDLE_INVALID); - CHECK(!parts[2].empty()); - WriteMessage(p, parts[2]); - WriteMessage(h, "ok"); - } else if (command == "hear") { - // Expect to read something from a named pipe. - CHECK_EQ(parts.size(), 3u); - CHECK_EQ(p, MOJO_HANDLE_INVALID); - p = named_pipes[parts[1]]; - CHECK_NE(p, MOJO_HANDLE_INVALID); - CHECK(!parts[2].empty()); - CHECK_EQ(parts[2], ReadMessage(p)); - WriteMessage(h, "ok"); - } else if (command == "pass") { - // Pass one named pipe over another named pipe. - CHECK_EQ(parts.size(), 3u); - CHECK_EQ(p, MOJO_HANDLE_INVALID); - p = named_pipes[parts[1]]; - MojoHandle carrier = named_pipes[parts[2]]; - CHECK_NE(p, MOJO_HANDLE_INVALID); - CHECK_NE(carrier, MOJO_HANDLE_INVALID); - named_pipes.erase(parts[1]); - WriteMessageWithHandles(carrier, "got a pipe for ya", &p, 1); - WriteMessage(h, "ok"); - } else if (command == "catch") { - // Expect to receive one named pipe from another named pipe. - CHECK_EQ(parts.size(), 3u); - CHECK_EQ(p, MOJO_HANDLE_INVALID); - MojoHandle carrier = named_pipes[parts[2]]; - CHECK_NE(carrier, MOJO_HANDLE_INVALID); - ReadMessageWithHandles(carrier, &p, 1); - CHECK_NE(p, MOJO_HANDLE_INVALID); - named_pipes[parts[1]] = p; - WriteMessage(h, "ok"); - } else if (command == "exit") { - CHECK_EQ(parts.size(), 1u); - break; - } - } - - for (auto& pipe : named_pipes) - CloseHandle(pipe.second); - - return 0; -} - -TEST_F(MultiprocessMessagePipeTest, ChildToChildPipes) { - RUN_CHILD_ON_PIPE(CommandDrivenClient, h0) - RUN_CHILD_ON_PIPE(CommandDrivenClient, h1) - CommandDrivenClientController a(h0); - CommandDrivenClientController b(h1); - - // Create a pipe and pass each end to a different client. - MojoHandle p0, p1; - CreateMessagePipe(&p0, &p1); - a.SendHandle("x", p0); - b.SendHandle("y", p1); - - // Make sure they can talk. - a.Send("say:x:hello"); - b.Send("hear:y:hello"); - - b.Send("say:y:i love multiprocess pipes!"); - a.Send("hear:x:i love multiprocess pipes!"); - - a.Exit(); - b.Exit(); - END_CHILD() - END_CHILD() -} - -TEST_F(MultiprocessMessagePipeTest, MoreChildToChildPipes) { - RUN_CHILD_ON_PIPE(CommandDrivenClient, h0) - RUN_CHILD_ON_PIPE(CommandDrivenClient, h1) - RUN_CHILD_ON_PIPE(CommandDrivenClient, h2) - RUN_CHILD_ON_PIPE(CommandDrivenClient, h3) - CommandDrivenClientController a(h0), b(h1), c(h2), d(h3); - - // Connect a to b and c to d - - MojoHandle p0, p1; - - CreateMessagePipe(&p0, &p1); - a.SendHandle("b_pipe", p0); - b.SendHandle("a_pipe", p1); - - MojoHandle p2, p3; - - CreateMessagePipe(&p2, &p3); - c.SendHandle("d_pipe", p2); - d.SendHandle("c_pipe", p3); - - // Connect b to c via a and d - MojoHandle p4, p5; - CreateMessagePipe(&p4, &p5); - a.SendHandle("d_pipe", p4); - d.SendHandle("a_pipe", p5); - - // Have |a| pass its new |d|-pipe to |b|. It will eventually connect - // to |c|. - a.Send("pass:d_pipe:b_pipe"); - b.Send("catch:c_pipe:a_pipe"); - - // Have |d| pass its new |a|-pipe to |c|. It will now be connected to - // |b|. - d.Send("pass:a_pipe:c_pipe"); - c.Send("catch:b_pipe:d_pipe"); - - // Make sure b and c and talk. - b.Send("say:c_pipe:it's a beautiful day"); - c.Send("hear:b_pipe:it's a beautiful day"); - - // Create x and y and have b and c exchange them. - MojoHandle x, y; - CreateMessagePipe(&x, &y); - b.SendHandle("x", x); - c.SendHandle("y", y); - b.Send("pass:x:c_pipe"); - c.Send("pass:y:b_pipe"); - b.Send("catch:y:c_pipe"); - c.Send("catch:x:b_pipe"); - - // Make sure the pipe still works in both directions. - b.Send("say:y:hello"); - c.Send("hear:x:hello"); - c.Send("say:x:goodbye"); - b.Send("hear:y:goodbye"); - - // Take both pipes back. - y = c.RetrieveHandle("x"); - x = b.RetrieveHandle("y"); - - VerifyTransmission(x, y, "still works"); - VerifyTransmission(y, x, "in both directions"); - - CloseHandle(x); - CloseHandle(y); - - a.Exit(); - b.Exit(); - c.Exit(); - d.Exit(); - END_CHILD() - END_CHILD() - END_CHILD() - END_CHILD() -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReceivePipeWithClosedPeer, - MultiprocessMessagePipeTest, h) { - MojoHandle p; - EXPECT_EQ("foo", ReadMessageWithHandles(h, &p, 1)); - EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); -} - -TEST_P(MultiprocessMessagePipeTestWithPeerSupport, SendPipeThenClosePeer) { - RUN_CHILD_ON_PIPE(ReceivePipeWithClosedPeer, h) - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - // Send |a| and immediately close |b|. The child should observe closure. - WriteMessageWithHandles(h, "foo", &a, 1); - MojoClose(b); - END_CHILD() -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(SendOtherChildPipeWithClosedPeer, - MultiprocessMessagePipeTest, h) { - // Create a new pipe and send one end to the parent, who will connect it to - // a client running ReceivePipeWithClosedPeerFromOtherChild. - MojoHandle application_proxy, application_request; - CreateMessagePipe(&application_proxy, &application_request); - WriteMessageWithHandles(h, "c2a plz", &application_request, 1); - - // Create another pipe and send one end to the remote "application". - MojoHandle service_proxy, service_request; - CreateMessagePipe(&service_proxy, &service_request); - WriteMessageWithHandles(application_proxy, "c2s lol", &service_request, 1); - - // Immediately close the service proxy. The "application" should detect this. - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(service_proxy)); - - // Wait for quit. - EXPECT_EQ("quit", ReadMessage(h)); -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReceivePipeWithClosedPeerFromOtherChild, - MultiprocessMessagePipeTest, h) { - // Receive a pipe from the parent. This is akin to an "application request". - MojoHandle application_client; - EXPECT_EQ("c2a", ReadMessageWithHandles(h, &application_client, 1)); - - // Receive a pipe from the "application" "client". - MojoHandle service_client; - EXPECT_EQ("c2s lol", - ReadMessageWithHandles(application_client, &service_client, 1)); - - // Wait for the service client to signal closure. - EXPECT_EQ(MOJO_RESULT_OK, - WaitForSignals(service_client, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(service_client)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(application_client)); -} - -#if defined(OS_ANDROID) -// Android multi-process tests are not executing the new process. This is flaky. -#define MAYBE_SendPipeWithClosedPeerBetweenChildren \ - DISABLED_SendPipeWithClosedPeerBetweenChildren -#else -#define MAYBE_SendPipeWithClosedPeerBetweenChildren \ - SendPipeWithClosedPeerBetweenChildren -#endif -TEST_F(MultiprocessMessagePipeTest, - MAYBE_SendPipeWithClosedPeerBetweenChildren) { - RUN_CHILD_ON_PIPE(SendOtherChildPipeWithClosedPeer, kid_a) - RUN_CHILD_ON_PIPE(ReceivePipeWithClosedPeerFromOtherChild, kid_b) - // Receive an "application request" from the first child and forward it - // to the second child. - MojoHandle application_request; - EXPECT_EQ("c2a plz", - ReadMessageWithHandles(kid_a, &application_request, 1)); - - WriteMessageWithHandles(kid_b, "c2a", &application_request, 1); - END_CHILD() - - WriteMessage(kid_a, "quit"); - END_CHILD() -} - -TEST_P(MultiprocessMessagePipeTestWithPeerSupport, SendClosePeerSend) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - MojoHandle c, d; - CreateMessagePipe(&c, &d); - - // Send |a| over |c|, immediately close |b|, then send |a| back over |d|. - WriteMessageWithHandles(c, "foo", &a, 1); - EXPECT_EQ("foo", ReadMessageWithHandles(d, &a, 1)); - WriteMessageWithHandles(d, "bar", &a, 1); - EXPECT_EQ("bar", ReadMessageWithHandles(c, &a, 1)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); - - // We should be able to detect peer closure on |a|. - EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(a, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(WriteCloseSendPeerClient, - MultiprocessMessagePipeTest, h) { - MojoHandle pipe[2]; - EXPECT_EQ("foo", ReadMessageWithHandles(h, pipe, 2)); - - // Write some messages to the first endpoint and then close it. - WriteMessage(pipe[0], "baz"); - WriteMessage(pipe[0], "qux"); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(pipe[0])); - - MojoHandle c, d; - CreateMessagePipe(&c, &d); - - // Pass the orphaned endpoint over another pipe before passing it back to - // the parent, just for some extra proxying goodness. - WriteMessageWithHandles(c, "foo", &pipe[1], 1); - EXPECT_EQ("foo", ReadMessageWithHandles(d, &pipe[1], 1)); - - // And finally pass it back to the parent. - WriteMessageWithHandles(h, "bar", &pipe[1], 1); - - EXPECT_EQ("quit", ReadMessage(h)); -} - -TEST_P(MultiprocessMessagePipeTestWithPeerSupport, WriteCloseSendPeer) { - MojoHandle pipe[2]; - CreateMessagePipe(&pipe[0], &pipe[1]); - - RUN_CHILD_ON_PIPE(WriteCloseSendPeerClient, h) - // Pass the pipe to the child. - WriteMessageWithHandles(h, "foo", pipe, 2); - - // Read back an endpoint which should have messages on it. - MojoHandle p; - EXPECT_EQ("bar", ReadMessageWithHandles(h, &p, 1)); - - EXPECT_EQ("baz", ReadMessage(p)); - EXPECT_EQ("qux", ReadMessage(p)); - - // Expect to have peer closure signaled. - EXPECT_EQ(MOJO_RESULT_OK, - WaitForSignals(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); - - WriteMessage(h, "quit"); - END_CHILD() -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MessagePipeStatusChangeInTransitClient, - MultiprocessMessagePipeTest, parent) { - // This test verifies that peer closure is detectable through various - // mechanisms when it races with handle transfer. - MojoHandle handles[4]; - EXPECT_EQ("o_O", ReadMessageWithHandles(parent, handles, 4)); - - EXPECT_EQ(MOJO_RESULT_OK, - WaitForSignals(handles[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED)); - - base::MessageLoop message_loop; - - // Wait on handle 1 using a SimpleWatcher. - { - base::RunLoop run_loop; - SimpleWatcher watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC); - watcher.Watch(Handle(handles[1]), MOJO_HANDLE_SIGNAL_PEER_CLOSED, - base::Bind( - [](base::RunLoop* loop, MojoResult result) { - EXPECT_EQ(MOJO_RESULT_OK, result); - loop->Quit(); - }, - &run_loop)); - run_loop.Run(); - } - - // Wait on handle 2 by polling with MojoReadMessage. - MojoResult result; - do { - result = MojoReadMessage(handles[2], nullptr, nullptr, nullptr, nullptr, - MOJO_READ_MESSAGE_FLAG_NONE); - } while (result == MOJO_RESULT_SHOULD_WAIT); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); - - // Wait on handle 3 by polling with MojoWriteMessage. - do { - result = MojoWriteMessage(handles[3], nullptr, 0, nullptr, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE); - } while (result == MOJO_RESULT_OK); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); - - for (size_t i = 0; i < 4; ++i) - CloseHandle(handles[i]); -} - -TEST_F(MultiprocessMessagePipeTest, MessagePipeStatusChangeInTransit) { - MojoHandle local_handles[4]; - MojoHandle sent_handles[4]; - for (size_t i = 0; i < 4; ++i) - CreateMessagePipe(&local_handles[i], &sent_handles[i]); - - RUN_CHILD_ON_PIPE(MessagePipeStatusChangeInTransitClient, child) - // Send 4 handles and let their transfer race with their peers' closure. - WriteMessageWithHandles(child, "o_O", sent_handles, 4); - for (size_t i = 0; i < 4; ++i) - CloseHandle(local_handles[i]); - END_CHILD() -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(BadMessageClient, MultiprocessMessagePipeTest, - parent) { - MojoHandle pipe; - EXPECT_EQ("hi", ReadMessageWithHandles(parent, &pipe, 1)); - WriteMessage(pipe, "derp"); - EXPECT_EQ("bye", ReadMessage(parent)); -} - -void OnProcessError(std::string* out_error, const std::string& error) { - *out_error = error; -} - -TEST_F(MultiprocessMessagePipeTest, NotifyBadMessage) { - const std::string kFirstErrorMessage = "everything is terrible!"; - const std::string kSecondErrorMessage = "not the bits you're looking for"; - - std::string first_process_error; - std::string second_process_error; - - set_process_error_callback(base::Bind(&OnProcessError, &first_process_error)); - RUN_CHILD_ON_PIPE(BadMessageClient, child1) - set_process_error_callback(base::Bind(&OnProcessError, - &second_process_error)); - RUN_CHILD_ON_PIPE(BadMessageClient, child2) - MojoHandle a, b, c, d; - CreateMessagePipe(&a, &b); - CreateMessagePipe(&c, &d); - WriteMessageWithHandles(child1, "hi", &b, 1); - WriteMessageWithHandles(child2, "hi", &d, 1); - - // Read a message from the pipe we sent to child1 and flag it as bad. - ASSERT_EQ(MOJO_RESULT_OK, WaitForSignals(a, MOJO_HANDLE_SIGNAL_READABLE)); - uint32_t num_bytes = 0; - MojoMessageHandle message; - ASSERT_EQ(MOJO_RESULT_OK, - MojoReadMessageNew(a, &message, &num_bytes, nullptr, 0, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoNotifyBadMessage(message, kFirstErrorMessage.data(), - kFirstErrorMessage.size())); - EXPECT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message)); - - // Read a message from the pipe we sent to child2 and flag it as bad. - ASSERT_EQ(MOJO_RESULT_OK, WaitForSignals(c, MOJO_HANDLE_SIGNAL_READABLE)); - ASSERT_EQ(MOJO_RESULT_OK, - MojoReadMessageNew(c, &message, &num_bytes, nullptr, 0, - MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoNotifyBadMessage(message, kSecondErrorMessage.data(), - kSecondErrorMessage.size())); - EXPECT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message)); - - WriteMessage(child2, "bye"); - END_CHILD(); - - WriteMessage(child1, "bye"); - END_CHILD() - - // The error messages should match the processes which triggered them. - EXPECT_NE(std::string::npos, first_process_error.find(kFirstErrorMessage)); - EXPECT_NE(std::string::npos, second_process_error.find(kSecondErrorMessage)); -} -INSTANTIATE_TEST_CASE_P( - , - MultiprocessMessagePipeTestWithPeerSupport, - testing::Values(test::MojoTestBase::LaunchType::CHILD, - test::MojoTestBase::LaunchType::PEER, - test::MojoTestBase::LaunchType::NAMED_CHILD, - test::MojoTestBase::LaunchType::NAMED_PEER)); -} // namespace -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/node_channel.cc b/mojo/edk/system/node_channel.cc deleted file mode 100644 index b0f770d..0000000 --- a/mojo/edk/system/node_channel.cc +++ /dev/null @@ -1,905 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/node_channel.h" - -#include <cstring> -#include <limits> -#include <sstream> - -#include "base/bind.h" -#include "base/location.h" -#include "base/logging.h" -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/request_context.h" - -#if defined(OS_MACOSX) && !defined(OS_IOS) -#include "mojo/edk/system/mach_port_relay.h" -#endif - -namespace mojo { -namespace edk { - -namespace { - -template <typename T> -T Align(T t) { - const auto k = kChannelMessageAlignment; - return t + (k - (t % k)) % k; -} - -// NOTE: Please ONLY append messages to the end of this enum. -enum class MessageType : uint32_t { - ACCEPT_CHILD, - ACCEPT_PARENT, - ADD_BROKER_CLIENT, - BROKER_CLIENT_ADDED, - ACCEPT_BROKER_CLIENT, - PORTS_MESSAGE, - REQUEST_PORT_MERGE, - REQUEST_INTRODUCTION, - INTRODUCE, -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) - RELAY_PORTS_MESSAGE, -#endif - BROADCAST, -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) - PORTS_MESSAGE_FROM_RELAY, -#endif - ACCEPT_PEER, -}; - -struct Header { - MessageType type; - uint32_t padding; -}; - -static_assert(IsAlignedForChannelMessage(sizeof(Header)), - "Invalid header size."); - -struct AcceptChildData { - ports::NodeName parent_name; - ports::NodeName token; -}; - -struct AcceptParentData { - ports::NodeName token; - ports::NodeName child_name; -}; - -struct AcceptPeerData { - ports::NodeName token; - ports::NodeName peer_name; - ports::PortName port_name; -}; - -// This message may include a process handle on plaforms that require it. -struct AddBrokerClientData { - ports::NodeName client_name; -#if !defined(OS_WIN) - uint32_t process_handle; - uint32_t padding; -#endif -}; - -#if !defined(OS_WIN) -static_assert(sizeof(base::ProcessHandle) == sizeof(uint32_t), - "Unexpected pid size"); -static_assert(sizeof(AddBrokerClientData) % kChannelMessageAlignment == 0, - "Invalid AddBrokerClientData size."); -#endif - -// This data is followed by a platform channel handle to the broker. -struct BrokerClientAddedData { - ports::NodeName client_name; -}; - -// This data may be followed by a platform channel handle to the broker. If not, -// then the parent is the broker and its channel should be used as such. -struct AcceptBrokerClientData { - ports::NodeName broker_name; -}; - -// This is followed by arbitrary payload data which is interpreted as a token -// string for port location. -struct RequestPortMergeData { - ports::PortName connector_port_name; -}; - -// Used for both REQUEST_INTRODUCTION and INTRODUCE. -// -// For INTRODUCE the message also includes a valid platform handle for a channel -// the receiver may use to communicate with the named node directly, or an -// invalid platform handle if the node is unknown to the sender or otherwise -// cannot be introduced. -struct IntroductionData { - ports::NodeName name; -}; - -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) -// This struct is followed by the full payload of a message to be relayed. -struct RelayPortsMessageData { - ports::NodeName destination; -}; - -// This struct is followed by the full payload of a relayed message. -struct PortsMessageFromRelayData { - ports::NodeName source; -}; -#endif - -template <typename DataType> -Channel::MessagePtr CreateMessage(MessageType type, - size_t payload_size, - size_t num_handles, - DataType** out_data) { - Channel::MessagePtr message( - new Channel::Message(sizeof(Header) + payload_size, num_handles)); - Header* header = reinterpret_cast<Header*>(message->mutable_payload()); - header->type = type; - header->padding = 0; - *out_data = reinterpret_cast<DataType*>(&header[1]); - return message; -} - -template <typename DataType> -bool GetMessagePayload(const void* bytes, - size_t num_bytes, - DataType** out_data) { - static_assert(sizeof(DataType) > 0, "DataType must have non-zero size."); - if (num_bytes < sizeof(Header) + sizeof(DataType)) - return false; - *out_data = reinterpret_cast<const DataType*>( - static_cast<const char*>(bytes) + sizeof(Header)); - return true; -} - -} // namespace - -// static -scoped_refptr<NodeChannel> NodeChannel::Create( - Delegate* delegate, - ConnectionParams connection_params, - scoped_refptr<base::TaskRunner> io_task_runner, - const ProcessErrorCallback& process_error_callback) { -#if defined(OS_NACL_SFI) - LOG(FATAL) << "Multi-process not yet supported on NaCl-SFI"; - return nullptr; -#else - return new NodeChannel(delegate, std::move(connection_params), io_task_runner, - process_error_callback); -#endif -} - -// static -Channel::MessagePtr NodeChannel::CreatePortsMessage(size_t payload_size, - void** payload, - size_t num_handles) { - return CreateMessage(MessageType::PORTS_MESSAGE, payload_size, num_handles, - payload); -} - -// static -void NodeChannel::GetPortsMessageData(Channel::Message* message, - void** data, - size_t* num_data_bytes) { - *data = reinterpret_cast<Header*>(message->mutable_payload()) + 1; - *num_data_bytes = message->payload_size() - sizeof(Header); -} - -void NodeChannel::Start() { -#if defined(OS_MACOSX) && !defined(OS_IOS) - MachPortRelay* relay = delegate_->GetMachPortRelay(); - if (relay) - relay->AddObserver(this); -#endif - - base::AutoLock lock(channel_lock_); - // ShutDown() may have already been called, in which case |channel_| is null. - if (channel_) - channel_->Start(); -} - -void NodeChannel::ShutDown() { -#if defined(OS_MACOSX) && !defined(OS_IOS) - MachPortRelay* relay = delegate_->GetMachPortRelay(); - if (relay) - relay->RemoveObserver(this); -#endif - - base::AutoLock lock(channel_lock_); - if (channel_) { - channel_->ShutDown(); - channel_ = nullptr; - } -} - -void NodeChannel::LeakHandleOnShutdown() { - base::AutoLock lock(channel_lock_); - if (channel_) { - channel_->LeakHandle(); - } -} - -void NodeChannel::NotifyBadMessage(const std::string& error) { - if (!process_error_callback_.is_null()) - process_error_callback_.Run("Received bad user message: " + error); -} - -void NodeChannel::SetRemoteProcessHandle(base::ProcessHandle process_handle) { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - base::AutoLock lock(remote_process_handle_lock_); - DCHECK_EQ(base::kNullProcessHandle, remote_process_handle_); - CHECK_NE(remote_process_handle_, base::GetCurrentProcessHandle()); - remote_process_handle_ = process_handle; -#if defined(OS_WIN) - DCHECK(!scoped_remote_process_handle_.is_valid()); - scoped_remote_process_handle_.reset(PlatformHandle(process_handle)); -#endif -} - -bool NodeChannel::HasRemoteProcessHandle() { - base::AutoLock lock(remote_process_handle_lock_); - return remote_process_handle_ != base::kNullProcessHandle; -} - -base::ProcessHandle NodeChannel::CopyRemoteProcessHandle() { - base::AutoLock lock(remote_process_handle_lock_); -#if defined(OS_WIN) - if (remote_process_handle_ != base::kNullProcessHandle) { - // Privileged nodes use this to pass their childrens' process handles to the - // broker on launch. - HANDLE handle = remote_process_handle_; - BOOL result = DuplicateHandle( - base::GetCurrentProcessHandle(), remote_process_handle_, - base::GetCurrentProcessHandle(), &handle, 0, FALSE, - DUPLICATE_SAME_ACCESS); - DPCHECK(result); - return handle; - } - return base::kNullProcessHandle; -#else - return remote_process_handle_; -#endif -} - -void NodeChannel::SetRemoteNodeName(const ports::NodeName& name) { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - remote_node_name_ = name; -} - -void NodeChannel::AcceptChild(const ports::NodeName& parent_name, - const ports::NodeName& token) { - AcceptChildData* data; - Channel::MessagePtr message = CreateMessage( - MessageType::ACCEPT_CHILD, sizeof(AcceptChildData), 0, &data); - data->parent_name = parent_name; - data->token = token; - WriteChannelMessage(std::move(message)); -} - -void NodeChannel::AcceptParent(const ports::NodeName& token, - const ports::NodeName& child_name) { - AcceptParentData* data; - Channel::MessagePtr message = CreateMessage( - MessageType::ACCEPT_PARENT, sizeof(AcceptParentData), 0, &data); - data->token = token; - data->child_name = child_name; - WriteChannelMessage(std::move(message)); -} - -void NodeChannel::AcceptPeer(const ports::NodeName& sender_name, - const ports::NodeName& token, - const ports::PortName& port_name) { - AcceptPeerData* data; - Channel::MessagePtr message = - CreateMessage(MessageType::ACCEPT_PEER, sizeof(AcceptPeerData), 0, &data); - data->token = token; - data->peer_name = sender_name; - data->port_name = port_name; - WriteChannelMessage(std::move(message)); -} - -void NodeChannel::AddBrokerClient(const ports::NodeName& client_name, - base::ProcessHandle process_handle) { - AddBrokerClientData* data; - ScopedPlatformHandleVectorPtr handles(new PlatformHandleVector()); -#if defined(OS_WIN) - handles->push_back(PlatformHandle(process_handle)); -#endif - Channel::MessagePtr message = CreateMessage( - MessageType::ADD_BROKER_CLIENT, sizeof(AddBrokerClientData), - handles->size(), &data); - message->SetHandles(std::move(handles)); - data->client_name = client_name; -#if !defined(OS_WIN) - data->process_handle = process_handle; - data->padding = 0; -#endif - WriteChannelMessage(std::move(message)); -} - -void NodeChannel::BrokerClientAdded(const ports::NodeName& client_name, - ScopedPlatformHandle broker_channel) { - BrokerClientAddedData* data; - ScopedPlatformHandleVectorPtr handles(new PlatformHandleVector()); - if (broker_channel.is_valid()) - handles->push_back(broker_channel.release()); - Channel::MessagePtr message = CreateMessage( - MessageType::BROKER_CLIENT_ADDED, sizeof(BrokerClientAddedData), - handles->size(), &data); - message->SetHandles(std::move(handles)); - data->client_name = client_name; - WriteChannelMessage(std::move(message)); -} - -void NodeChannel::AcceptBrokerClient(const ports::NodeName& broker_name, - ScopedPlatformHandle broker_channel) { - AcceptBrokerClientData* data; - ScopedPlatformHandleVectorPtr handles(new PlatformHandleVector()); - if (broker_channel.is_valid()) - handles->push_back(broker_channel.release()); - Channel::MessagePtr message = CreateMessage( - MessageType::ACCEPT_BROKER_CLIENT, sizeof(AcceptBrokerClientData), - handles->size(), &data); - message->SetHandles(std::move(handles)); - data->broker_name = broker_name; - WriteChannelMessage(std::move(message)); -} - -void NodeChannel::PortsMessage(Channel::MessagePtr message) { - WriteChannelMessage(std::move(message)); -} - -void NodeChannel::RequestPortMerge(const ports::PortName& connector_port_name, - const std::string& token) { - RequestPortMergeData* data; - Channel::MessagePtr message = CreateMessage( - MessageType::REQUEST_PORT_MERGE, - sizeof(RequestPortMergeData) + token.size(), 0, &data); - data->connector_port_name = connector_port_name; - memcpy(data + 1, token.data(), token.size()); - WriteChannelMessage(std::move(message)); -} - -void NodeChannel::RequestIntroduction(const ports::NodeName& name) { - IntroductionData* data; - Channel::MessagePtr message = CreateMessage( - MessageType::REQUEST_INTRODUCTION, sizeof(IntroductionData), 0, &data); - data->name = name; - WriteChannelMessage(std::move(message)); -} - -void NodeChannel::Introduce(const ports::NodeName& name, - ScopedPlatformHandle channel_handle) { - IntroductionData* data; - ScopedPlatformHandleVectorPtr handles(new PlatformHandleVector()); - if (channel_handle.is_valid()) - handles->push_back(channel_handle.release()); - Channel::MessagePtr message = CreateMessage( - MessageType::INTRODUCE, sizeof(IntroductionData), handles->size(), &data); - message->SetHandles(std::move(handles)); - data->name = name; - WriteChannelMessage(std::move(message)); -} - -void NodeChannel::Broadcast(Channel::MessagePtr message) { - DCHECK(!message->has_handles()); - void* data; - Channel::MessagePtr broadcast_message = CreateMessage( - MessageType::BROADCAST, message->data_num_bytes(), 0, &data); - memcpy(data, message->data(), message->data_num_bytes()); - WriteChannelMessage(std::move(broadcast_message)); -} - -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) -void NodeChannel::RelayPortsMessage(const ports::NodeName& destination, - Channel::MessagePtr message) { -#if defined(OS_WIN) - DCHECK(message->has_handles()); - - // Note that this is only used on Windows, and on Windows all platform - // handles are included in the message data. We blindly copy all the data - // here and the relay node (the parent) will duplicate handles as needed. - size_t num_bytes = sizeof(RelayPortsMessageData) + message->data_num_bytes(); - RelayPortsMessageData* data; - Channel::MessagePtr relay_message = CreateMessage( - MessageType::RELAY_PORTS_MESSAGE, num_bytes, 0, &data); - data->destination = destination; - memcpy(data + 1, message->data(), message->data_num_bytes()); - - // When the handles are duplicated in the parent, the source handles will - // be closed. If the parent never receives this message then these handles - // will leak, but that means something else has probably broken and the - // sending process won't likely be around much longer. - ScopedPlatformHandleVectorPtr handles = message->TakeHandles(); - handles->clear(); - -#else - DCHECK(message->has_mach_ports()); - - // On OSX, the handles are extracted from the relayed message and attached to - // the wrapper. The broker then takes the handles attached to the wrapper and - // moves them back to the relayed message. This is necessary because the - // message may contain fds which need to be attached to the outer message so - // that they can be transferred to the broker. - ScopedPlatformHandleVectorPtr handles = message->TakeHandles(); - size_t num_bytes = sizeof(RelayPortsMessageData) + message->data_num_bytes(); - RelayPortsMessageData* data; - Channel::MessagePtr relay_message = CreateMessage( - MessageType::RELAY_PORTS_MESSAGE, num_bytes, handles->size(), &data); - data->destination = destination; - memcpy(data + 1, message->data(), message->data_num_bytes()); - relay_message->SetHandles(std::move(handles)); -#endif // defined(OS_WIN) - - WriteChannelMessage(std::move(relay_message)); -} - -void NodeChannel::PortsMessageFromRelay(const ports::NodeName& source, - Channel::MessagePtr message) { - size_t num_bytes = sizeof(PortsMessageFromRelayData) + - message->payload_size(); - PortsMessageFromRelayData* data; - Channel::MessagePtr relayed_message = CreateMessage( - MessageType::PORTS_MESSAGE_FROM_RELAY, num_bytes, message->num_handles(), - &data); - data->source = source; - if (message->payload_size()) - memcpy(data + 1, message->payload(), message->payload_size()); - relayed_message->SetHandles(message->TakeHandles()); - WriteChannelMessage(std::move(relayed_message)); -} -#endif // defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) - -NodeChannel::NodeChannel(Delegate* delegate, - ConnectionParams connection_params, - scoped_refptr<base::TaskRunner> io_task_runner, - const ProcessErrorCallback& process_error_callback) - : delegate_(delegate), - io_task_runner_(io_task_runner), - process_error_callback_(process_error_callback) -#if !defined(OS_NACL_SFI) - , - channel_( - Channel::Create(this, std::move(connection_params), io_task_runner_)) -#endif -{ -} - -NodeChannel::~NodeChannel() { - ShutDown(); -} - -void NodeChannel::OnChannelMessage(const void* payload, - size_t payload_size, - ScopedPlatformHandleVectorPtr handles) { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - - RequestContext request_context(RequestContext::Source::SYSTEM); - - // Ensure this NodeChannel stays alive through the extent of this method. The - // delegate may have the only other reference to this object and it may choose - // to drop it here in response to, e.g., a malformed message. - scoped_refptr<NodeChannel> keepalive = this; - -#if defined(OS_WIN) - // If we receive handles from a known process, rewrite them to our own - // process. This can occur when a privileged node receives handles directly - // from a privileged descendant. - { - base::AutoLock lock(remote_process_handle_lock_); - if (handles && remote_process_handle_ != base::kNullProcessHandle) { - // Note that we explicitly mark the handles as being owned by the sending - // process before rewriting them, in order to accommodate RewriteHandles' - // internal sanity checks. - for (auto& handle : *handles) - handle.owning_process = remote_process_handle_; - if (!Channel::Message::RewriteHandles(remote_process_handle_, - base::GetCurrentProcessHandle(), - handles.get())) { - DLOG(ERROR) << "Received one or more invalid handles."; - } - } else if (handles) { - // Handles received by an unknown process must already be owned by us. - for (auto& handle : *handles) - handle.owning_process = base::GetCurrentProcessHandle(); - } - } -#elif defined(OS_MACOSX) && !defined(OS_IOS) - // If we're not the root, receive any mach ports from the message. If we're - // the root, the only message containing mach ports should be a - // RELAY_PORTS_MESSAGE. - { - MachPortRelay* relay = delegate_->GetMachPortRelay(); - if (handles && !relay) { - if (!MachPortRelay::ReceivePorts(handles.get())) { - LOG(ERROR) << "Error receiving mach ports."; - } - } - } -#endif // defined(OS_WIN) - - - if (payload_size <= sizeof(Header)) { - delegate_->OnChannelError(remote_node_name_, this); - return; - } - - const Header* header = static_cast<const Header*>(payload); - switch (header->type) { - case MessageType::ACCEPT_CHILD: { - const AcceptChildData* data; - if (GetMessagePayload(payload, payload_size, &data)) { - delegate_->OnAcceptChild(remote_node_name_, data->parent_name, - data->token); - return; - } - break; - } - - case MessageType::ACCEPT_PARENT: { - const AcceptParentData* data; - if (GetMessagePayload(payload, payload_size, &data)) { - delegate_->OnAcceptParent(remote_node_name_, data->token, - data->child_name); - return; - } - break; - } - - case MessageType::ADD_BROKER_CLIENT: { - const AddBrokerClientData* data; - if (GetMessagePayload(payload, payload_size, &data)) { - ScopedPlatformHandle process_handle; -#if defined(OS_WIN) - if (!handles || handles->size() != 1) { - DLOG(ERROR) << "Dropping invalid AddBrokerClient message."; - break; - } - process_handle = ScopedPlatformHandle(handles->at(0)); - handles->clear(); - delegate_->OnAddBrokerClient(remote_node_name_, data->client_name, - process_handle.release().handle); -#else - if (handles && handles->size() != 0) { - DLOG(ERROR) << "Dropping invalid AddBrokerClient message."; - break; - } - delegate_->OnAddBrokerClient(remote_node_name_, data->client_name, - data->process_handle); -#endif - return; - } - break; - } - - case MessageType::BROKER_CLIENT_ADDED: { - const BrokerClientAddedData* data; - if (GetMessagePayload(payload, payload_size, &data)) { - ScopedPlatformHandle broker_channel; - if (!handles || handles->size() != 1) { - DLOG(ERROR) << "Dropping invalid BrokerClientAdded message."; - break; - } - broker_channel = ScopedPlatformHandle(handles->at(0)); - handles->clear(); - delegate_->OnBrokerClientAdded(remote_node_name_, data->client_name, - std::move(broker_channel)); - return; - } - break; - } - - case MessageType::ACCEPT_BROKER_CLIENT: { - const AcceptBrokerClientData* data; - if (GetMessagePayload(payload, payload_size, &data)) { - ScopedPlatformHandle broker_channel; - if (handles && handles->size() > 1) { - DLOG(ERROR) << "Dropping invalid AcceptBrokerClient message."; - break; - } - if (handles && handles->size() == 1) { - broker_channel = ScopedPlatformHandle(handles->at(0)); - handles->clear(); - } - delegate_->OnAcceptBrokerClient(remote_node_name_, data->broker_name, - std::move(broker_channel)); - return; - } - break; - } - - case MessageType::PORTS_MESSAGE: { - size_t num_handles = handles ? handles->size() : 0; - Channel::MessagePtr message( - new Channel::Message(payload_size, num_handles)); - message->SetHandles(std::move(handles)); - memcpy(message->mutable_payload(), payload, payload_size); - delegate_->OnPortsMessage(remote_node_name_, std::move(message)); - return; - } - - case MessageType::REQUEST_PORT_MERGE: { - const RequestPortMergeData* data; - if (GetMessagePayload(payload, payload_size, &data)) { - // Don't accept an empty token. - size_t token_size = payload_size - sizeof(*data) - sizeof(Header); - if (token_size == 0) - break; - std::string token(reinterpret_cast<const char*>(data + 1), token_size); - delegate_->OnRequestPortMerge(remote_node_name_, - data->connector_port_name, token); - return; - } - break; - } - - case MessageType::REQUEST_INTRODUCTION: { - const IntroductionData* data; - if (GetMessagePayload(payload, payload_size, &data)) { - delegate_->OnRequestIntroduction(remote_node_name_, data->name); - return; - } - break; - } - - case MessageType::INTRODUCE: { - const IntroductionData* data; - if (GetMessagePayload(payload, payload_size, &data)) { - if (handles && handles->size() > 1) { - DLOG(ERROR) << "Dropping invalid introduction message."; - break; - } - ScopedPlatformHandle channel_handle; - if (handles && handles->size() == 1) { - channel_handle = ScopedPlatformHandle(handles->at(0)); - handles->clear(); - } - delegate_->OnIntroduce(remote_node_name_, data->name, - std::move(channel_handle)); - return; - } - break; - } - -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) - case MessageType::RELAY_PORTS_MESSAGE: { - base::ProcessHandle from_process; - { - base::AutoLock lock(remote_process_handle_lock_); - from_process = remote_process_handle_; - } - const RelayPortsMessageData* data; - if (GetMessagePayload(payload, payload_size, &data)) { - // Don't try to relay an empty message. - if (payload_size <= sizeof(Header) + sizeof(RelayPortsMessageData)) - break; - - const void* message_start = data + 1; - Channel::MessagePtr message = Channel::Message::Deserialize( - message_start, payload_size - sizeof(Header) - sizeof(*data)); - if (!message) { - DLOG(ERROR) << "Dropping invalid relay message."; - break; - } - #if defined(OS_MACOSX) && !defined(OS_IOS) - message->SetHandles(std::move(handles)); - MachPortRelay* relay = delegate_->GetMachPortRelay(); - if (!relay) { - LOG(ERROR) << "Receiving mach ports without a port relay from " - << remote_node_name_ << ". Dropping message."; - break; - } - { - base::AutoLock lock(pending_mach_messages_lock_); - if (relay->port_provider()->TaskForPid(from_process) == - MACH_PORT_NULL) { - pending_relay_messages_.push( - std::make_pair(data->destination, std::move(message))); - break; - } - } - #endif - delegate_->OnRelayPortsMessage(remote_node_name_, from_process, - data->destination, std::move(message)); - return; - } - break; - } -#endif - - case MessageType::BROADCAST: { - if (payload_size <= sizeof(Header)) - break; - const void* data = static_cast<const void*>( - reinterpret_cast<const Header*>(payload) + 1); - Channel::MessagePtr message = - Channel::Message::Deserialize(data, payload_size - sizeof(Header)); - if (!message || message->has_handles()) { - DLOG(ERROR) << "Dropping invalid broadcast message."; - break; - } - delegate_->OnBroadcast(remote_node_name_, std::move(message)); - return; - } - -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) - case MessageType::PORTS_MESSAGE_FROM_RELAY: - const PortsMessageFromRelayData* data; - if (GetMessagePayload(payload, payload_size, &data)) { - size_t num_bytes = payload_size - sizeof(*data); - if (num_bytes < sizeof(Header)) - break; - num_bytes -= sizeof(Header); - - size_t num_handles = handles ? handles->size() : 0; - Channel::MessagePtr message( - new Channel::Message(num_bytes, num_handles)); - message->SetHandles(std::move(handles)); - if (num_bytes) - memcpy(message->mutable_payload(), data + 1, num_bytes); - delegate_->OnPortsMessageFromRelay( - remote_node_name_, data->source, std::move(message)); - return; - } - break; - -#endif // defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) - - case MessageType::ACCEPT_PEER: { - const AcceptPeerData* data; - if (GetMessagePayload(payload, payload_size, &data)) { - delegate_->OnAcceptPeer(remote_node_name_, data->token, data->peer_name, - data->port_name); - return; - } - break; - } - - default: - break; - } - - DLOG(ERROR) << "Received invalid message. Closing channel."; - delegate_->OnChannelError(remote_node_name_, this); -} - -void NodeChannel::OnChannelError() { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - - RequestContext request_context(RequestContext::Source::SYSTEM); - - ShutDown(); - // |OnChannelError()| may cause |this| to be destroyed, but still need access - // to the name name after that destruction. So may a copy of - // |remote_node_name_| so it can be used if |this| becomes destroyed. - ports::NodeName node_name = remote_node_name_; - delegate_->OnChannelError(node_name, this); -} - -#if defined(OS_MACOSX) && !defined(OS_IOS) -void NodeChannel::OnProcessReady(base::ProcessHandle process) { - io_task_runner_->PostTask(FROM_HERE, base::Bind( - &NodeChannel::ProcessPendingMessagesWithMachPorts, this)); -} - -void NodeChannel::ProcessPendingMessagesWithMachPorts() { - MachPortRelay* relay = delegate_->GetMachPortRelay(); - DCHECK(relay); - - base::ProcessHandle remote_process_handle; - { - base::AutoLock lock(remote_process_handle_lock_); - remote_process_handle = remote_process_handle_; - } - PendingMessageQueue pending_writes; - PendingRelayMessageQueue pending_relays; - { - base::AutoLock lock(pending_mach_messages_lock_); - pending_writes.swap(pending_write_messages_); - pending_relays.swap(pending_relay_messages_); - } - - while (!pending_writes.empty()) { - Channel::MessagePtr message = std::move(pending_writes.front()); - pending_writes.pop(); - if (!relay->SendPortsToProcess(message.get(), remote_process_handle)) { - LOG(ERROR) << "Error on sending mach ports. Remote process is likely " - << "gone. Dropping message."; - return; - } - - base::AutoLock lock(channel_lock_); - if (!channel_) { - DLOG(ERROR) << "Dropping message on closed channel."; - break; - } else { - channel_->Write(std::move(message)); - } - } - - // Ensure this NodeChannel stays alive while flushing relay messages. - scoped_refptr<NodeChannel> keepalive = this; - - while (!pending_relays.empty()) { - ports::NodeName destination = pending_relays.front().first; - Channel::MessagePtr message = std::move(pending_relays.front().second); - pending_relays.pop(); - delegate_->OnRelayPortsMessage(remote_node_name_, remote_process_handle, - destination, std::move(message)); - } -} -#endif - -void NodeChannel::WriteChannelMessage(Channel::MessagePtr message) { -#if defined(OS_WIN) - // Map handles to the destination process. Note: only messages from a - // privileged node should contain handles on Windows. If an unprivileged - // node needs to send handles, it should do so via RelayPortsMessage which - // stashes the handles in the message in such a way that they go undetected - // here (they'll be unpacked and duplicated by a privileged parent.) - - if (message->has_handles()) { - base::ProcessHandle remote_process_handle; - { - base::AutoLock lock(remote_process_handle_lock_); - remote_process_handle = remote_process_handle_; - } - - // Rewrite outgoing handles if we have a handle to the destination process. - if (remote_process_handle != base::kNullProcessHandle) { - ScopedPlatformHandleVectorPtr handles = message->TakeHandles(); - if (!Channel::Message::RewriteHandles(base::GetCurrentProcessHandle(), - remote_process_handle, - handles.get())) { - DLOG(ERROR) << "Failed to duplicate one or more outgoing handles."; - } - message->SetHandles(std::move(handles)); - } - } -#elif defined(OS_MACOSX) && !defined(OS_IOS) - // On OSX, we need to transfer mach ports to the destination process before - // transferring the message itself. - if (message->has_mach_ports()) { - MachPortRelay* relay = delegate_->GetMachPortRelay(); - if (relay) { - base::ProcessHandle remote_process_handle; - { - base::AutoLock lock(remote_process_handle_lock_); - // Expect that the receiving node is a child. - DCHECK(remote_process_handle_ != base::kNullProcessHandle); - remote_process_handle = remote_process_handle_; - } - { - base::AutoLock lock(pending_mach_messages_lock_); - if (relay->port_provider()->TaskForPid(remote_process_handle) == - MACH_PORT_NULL) { - // It is also possible for TaskForPid() to return MACH_PORT_NULL when - // the process has started, then died. In that case, the queued - // message will never be processed. But that's fine since we're about - // to die anyway. - pending_write_messages_.push(std::move(message)); - return; - } - } - - if (!relay->SendPortsToProcess(message.get(), remote_process_handle)) { - LOG(ERROR) << "Error on sending mach ports. Remote process is likely " - << "gone. Dropping message."; - return; - } - } - } -#endif - - base::AutoLock lock(channel_lock_); - if (!channel_) - DLOG(ERROR) << "Dropping message on closed channel."; - else - channel_->Write(std::move(message)); -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/node_channel.h b/mojo/edk/system/node_channel.h deleted file mode 100644 index 95dc341..0000000 --- a/mojo/edk/system/node_channel.h +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_NODE_CHANNEL_H_ -#define MOJO_EDK_SYSTEM_NODE_CHANNEL_H_ - -#include <queue> -#include <unordered_map> -#include <utility> - -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/process/process_handle.h" -#include "base/synchronization/lock.h" -#include "base/task_runner.h" -#include "build/build_config.h" -#include "mojo/edk/embedder/connection_params.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/embedder/platform_handle_vector.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/ports/name.h" - -#if defined(OS_MACOSX) && !defined(OS_IOS) -#include "mojo/edk/system/mach_port_relay.h" -#endif - -namespace mojo { -namespace edk { - -// Wraps a Channel to send and receive Node control messages. -class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>, - public Channel::Delegate -#if defined(OS_MACOSX) && !defined(OS_IOS) - , public MachPortRelay::Observer -#endif - { - public: - class Delegate { - public: - virtual ~Delegate() {} - virtual void OnAcceptChild(const ports::NodeName& from_node, - const ports::NodeName& parent_name, - const ports::NodeName& token) = 0; - virtual void OnAcceptParent(const ports::NodeName& from_node, - const ports::NodeName& token, - const ports::NodeName& child_name) = 0; - virtual void OnAddBrokerClient(const ports::NodeName& from_node, - const ports::NodeName& client_name, - base::ProcessHandle process_handle) = 0; - virtual void OnBrokerClientAdded(const ports::NodeName& from_node, - const ports::NodeName& client_name, - ScopedPlatformHandle broker_channel) = 0; - virtual void OnAcceptBrokerClient(const ports::NodeName& from_node, - const ports::NodeName& broker_name, - ScopedPlatformHandle broker_channel) = 0; - virtual void OnPortsMessage(const ports::NodeName& from_node, - Channel::MessagePtr message) = 0; - virtual void OnRequestPortMerge(const ports::NodeName& from_node, - const ports::PortName& connector_port_name, - const std::string& token) = 0; - virtual void OnRequestIntroduction(const ports::NodeName& from_node, - const ports::NodeName& name) = 0; - virtual void OnIntroduce(const ports::NodeName& from_node, - const ports::NodeName& name, - ScopedPlatformHandle channel_handle) = 0; - virtual void OnBroadcast(const ports::NodeName& from_node, - Channel::MessagePtr message) = 0; -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) - virtual void OnRelayPortsMessage(const ports::NodeName& from_node, - base::ProcessHandle from_process, - const ports::NodeName& destination, - Channel::MessagePtr message) = 0; - virtual void OnPortsMessageFromRelay(const ports::NodeName& from_node, - const ports::NodeName& source_node, - Channel::MessagePtr message) = 0; -#endif - virtual void OnAcceptPeer(const ports::NodeName& from_node, - const ports::NodeName& token, - const ports::NodeName& peer_name, - const ports::PortName& port_name) = 0; - virtual void OnChannelError(const ports::NodeName& node, - NodeChannel* channel) = 0; - -#if defined(OS_MACOSX) && !defined(OS_IOS) - virtual MachPortRelay* GetMachPortRelay() = 0; -#endif - }; - - static scoped_refptr<NodeChannel> Create( - Delegate* delegate, - ConnectionParams connection_params, - scoped_refptr<base::TaskRunner> io_task_runner, - const ProcessErrorCallback& process_error_callback); - - static Channel::MessagePtr CreatePortsMessage(size_t payload_size, - void** payload, - size_t num_handles); - - static void GetPortsMessageData(Channel::Message* message, void** data, - size_t* num_data_bytes); - - // Start receiving messages. - void Start(); - - // Permanently stop the channel from sending or receiving messages. - void ShutDown(); - - // Leaks the pipe handle instead of closing it on shutdown. - void LeakHandleOnShutdown(); - - // Invokes the bad message callback for this channel, if any. - void NotifyBadMessage(const std::string& error); - - // Note: On Windows, we take ownership of the remote process handle. - void SetRemoteProcessHandle(base::ProcessHandle process_handle); - bool HasRemoteProcessHandle(); - // Note: The returned |ProcessHandle| is owned by the caller and should be - // freed if necessary. - base::ProcessHandle CopyRemoteProcessHandle(); - - // Used for context in Delegate calls (via |from_node| arguments.) - void SetRemoteNodeName(const ports::NodeName& name); - - void AcceptChild(const ports::NodeName& parent_name, - const ports::NodeName& token); - void AcceptParent(const ports::NodeName& token, - const ports::NodeName& child_name); - void AcceptPeer(const ports::NodeName& sender_name, - const ports::NodeName& token, - const ports::PortName& port_name); - void AddBrokerClient(const ports::NodeName& client_name, - base::ProcessHandle process_handle); - void BrokerClientAdded(const ports::NodeName& client_name, - ScopedPlatformHandle broker_channel); - void AcceptBrokerClient(const ports::NodeName& broker_name, - ScopedPlatformHandle broker_channel); - void PortsMessage(Channel::MessagePtr message); - void RequestPortMerge(const ports::PortName& connector_port_name, - const std::string& token); - void RequestIntroduction(const ports::NodeName& name); - void Introduce(const ports::NodeName& name, - ScopedPlatformHandle channel_handle); - void Broadcast(Channel::MessagePtr message); - -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) - // Relay the message to the specified node via this channel. This is used to - // pass windows handles between two processes that do not have permission to - // duplicate handles into the other's address space. The relay process is - // assumed to have that permission. - void RelayPortsMessage(const ports::NodeName& destination, - Channel::MessagePtr message); - - // Sends a message to its destination from a relay. This is interpreted by the - // receiver similarly to PortsMessage, but the original source node is - // provided as additional message metadata from the (trusted) relay node. - void PortsMessageFromRelay(const ports::NodeName& source, - Channel::MessagePtr message); -#endif - - private: - friend class base::RefCountedThreadSafe<NodeChannel>; - - using PendingMessageQueue = std::queue<Channel::MessagePtr>; - using PendingRelayMessageQueue = - std::queue<std::pair<ports::NodeName, Channel::MessagePtr>>; - - NodeChannel(Delegate* delegate, - ConnectionParams connection_params, - scoped_refptr<base::TaskRunner> io_task_runner, - const ProcessErrorCallback& process_error_callback); - ~NodeChannel() override; - - // Channel::Delegate: - void OnChannelMessage(const void* payload, - size_t payload_size, - ScopedPlatformHandleVectorPtr handles) override; - void OnChannelError() override; - -#if defined(OS_MACOSX) && !defined(OS_IOS) - // MachPortRelay::Observer: - void OnProcessReady(base::ProcessHandle process) override; - - void ProcessPendingMessagesWithMachPorts(); -#endif - - void WriteChannelMessage(Channel::MessagePtr message); - - Delegate* const delegate_; - const scoped_refptr<base::TaskRunner> io_task_runner_; - const ProcessErrorCallback process_error_callback_; - - base::Lock channel_lock_; - scoped_refptr<Channel> channel_; - - // Must only be accessed from |io_task_runner_|'s thread. - ports::NodeName remote_node_name_; - - base::Lock remote_process_handle_lock_; - base::ProcessHandle remote_process_handle_ = base::kNullProcessHandle; -#if defined(OS_WIN) - ScopedPlatformHandle scoped_remote_process_handle_; -#endif - -#if defined(OS_MACOSX) && !defined(OS_IOS) - base::Lock pending_mach_messages_lock_; - PendingMessageQueue pending_write_messages_; - PendingRelayMessageQueue pending_relay_messages_; -#endif - - DISALLOW_COPY_AND_ASSIGN(NodeChannel); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_NODE_CHANNEL_H_ diff --git a/mojo/edk/system/node_controller.cc b/mojo/edk/system/node_controller.cc deleted file mode 100644 index 73b16b1..0000000 --- a/mojo/edk/system/node_controller.cc +++ /dev/null @@ -1,1470 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/node_controller.h" - -#include <algorithm> -#include <limits> - -#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/metrics/histogram_macros.h" -#include "base/process/process_handle.h" -#include "base/rand_util.h" -#include "base/time/time.h" -#include "base/timer/elapsed_timer.h" -#include "mojo/edk/embedder/embedder_internal.h" -#include "mojo/edk/embedder/named_platform_channel_pair.h" -#include "mojo/edk/embedder/named_platform_handle.h" -#include "mojo/edk/embedder/platform_channel_pair.h" -#include "mojo/edk/system/broker.h" -#include "mojo/edk/system/broker_host.h" -#include "mojo/edk/system/core.h" -#include "mojo/edk/system/ports_message.h" -#include "mojo/edk/system/request_context.h" - -#if defined(OS_MACOSX) && !defined(OS_IOS) -#include "mojo/edk/system/mach_port_relay.h" -#endif - -#if !defined(OS_NACL) -#include "crypto/random.h" -#endif - -namespace mojo { -namespace edk { - -namespace { - -#if defined(OS_NACL) -template <typename T> -void GenerateRandomName(T* out) { base::RandBytes(out, sizeof(T)); } -#else -template <typename T> -void GenerateRandomName(T* out) { crypto::RandBytes(out, sizeof(T)); } -#endif - -ports::NodeName GetRandomNodeName() { - ports::NodeName name; - GenerateRandomName(&name); - return name; -} - -void RecordPeerCount(size_t count) { - DCHECK_LE(count, static_cast<size_t>(std::numeric_limits<int32_t>::max())); - - // 8k is the maximum number of file descriptors allowed in Chrome. - UMA_HISTOGRAM_CUSTOM_COUNTS("Mojo.System.Node.ConnectedPeers", - static_cast<int32_t>(count), - 1 /* min */, - 8000 /* max */, - 50 /* bucket count */); -} - -void RecordPendingChildCount(size_t count) { - DCHECK_LE(count, static_cast<size_t>(std::numeric_limits<int32_t>::max())); - - // 8k is the maximum number of file descriptors allowed in Chrome. - UMA_HISTOGRAM_CUSTOM_COUNTS("Mojo.System.Node.PendingChildren", - static_cast<int32_t>(count), - 1 /* min */, - 8000 /* max */, - 50 /* bucket count */); -} - -bool ParsePortsMessage(Channel::Message* message, - void** data, - size_t* num_data_bytes, - size_t* num_header_bytes, - size_t* num_payload_bytes, - size_t* num_ports_bytes) { - DCHECK(data && num_data_bytes && num_header_bytes && num_payload_bytes && - num_ports_bytes); - - NodeChannel::GetPortsMessageData(message, data, num_data_bytes); - if (!*num_data_bytes) - return false; - - if (!ports::Message::Parse(*data, *num_data_bytes, num_header_bytes, - num_payload_bytes, num_ports_bytes)) { - return false; - } - - return true; -} - -// Used by NodeController to watch for shutdown. Since no IO can happen once -// the IO thread is killed, the NodeController can cleanly drop all its peers -// at that time. -class ThreadDestructionObserver : - public base::MessageLoop::DestructionObserver { - public: - static void Create(scoped_refptr<base::TaskRunner> task_runner, - const base::Closure& callback) { - if (task_runner->RunsTasksOnCurrentThread()) { - // Owns itself. - new ThreadDestructionObserver(callback); - } else { - task_runner->PostTask(FROM_HERE, - base::Bind(&Create, task_runner, callback)); - } - } - - private: - explicit ThreadDestructionObserver(const base::Closure& callback) - : callback_(callback) { - base::MessageLoop::current()->AddDestructionObserver(this); - } - - ~ThreadDestructionObserver() override { - base::MessageLoop::current()->RemoveDestructionObserver(this); - } - - // base::MessageLoop::DestructionObserver: - void WillDestroyCurrentMessageLoop() override { - callback_.Run(); - delete this; - } - - const base::Closure callback_; - - DISALLOW_COPY_AND_ASSIGN(ThreadDestructionObserver); -}; - -} // namespace - -NodeController::~NodeController() {} - -NodeController::NodeController(Core* core) - : core_(core), - name_(GetRandomNodeName()), - node_(new ports::Node(name_, this)) { - DVLOG(1) << "Initializing node " << name_; -} - -#if defined(OS_MACOSX) && !defined(OS_IOS) -void NodeController::CreateMachPortRelay( - base::PortProvider* port_provider) { - base::AutoLock lock(mach_port_relay_lock_); - DCHECK(!mach_port_relay_); - mach_port_relay_.reset(new MachPortRelay(port_provider)); -} -#endif - -void NodeController::SetIOTaskRunner( - scoped_refptr<base::TaskRunner> task_runner) { - io_task_runner_ = task_runner; - ThreadDestructionObserver::Create( - io_task_runner_, - base::Bind(&NodeController::DropAllPeers, base::Unretained(this))); -} - -void NodeController::ConnectToChild( - base::ProcessHandle process_handle, - ConnectionParams connection_params, - const std::string& child_token, - const ProcessErrorCallback& process_error_callback) { - // Generate the temporary remote node name here so that it can be associated - // with the embedder's child_token. If an error occurs in the child process - // after it is launched, but before any reserved ports are connected, this can - // be used to clean up any dangling ports. - ports::NodeName node_name; - GenerateRandomName(&node_name); - - { - base::AutoLock lock(reserved_ports_lock_); - bool inserted = pending_child_tokens_.insert( - std::make_pair(node_name, child_token)).second; - DCHECK(inserted); - } - -#if defined(OS_WIN) - // On Windows, we need to duplicate the process handle because we have no - // control over its lifetime and it may become invalid by the time the posted - // task runs. - HANDLE dup_handle = INVALID_HANDLE_VALUE; - BOOL ok = ::DuplicateHandle( - base::GetCurrentProcessHandle(), process_handle, - base::GetCurrentProcessHandle(), &dup_handle, - 0, FALSE, DUPLICATE_SAME_ACCESS); - DPCHECK(ok); - process_handle = dup_handle; -#endif - - io_task_runner_->PostTask( - FROM_HERE, base::Bind(&NodeController::ConnectToChildOnIOThread, - base::Unretained(this), process_handle, - base::Passed(&connection_params), node_name, - process_error_callback)); -} - -void NodeController::CloseChildPorts(const std::string& child_token) { - std::vector<ports::PortRef> ports_to_close; - { - std::vector<std::string> port_tokens; - base::AutoLock lock(reserved_ports_lock_); - for (const auto& port : reserved_ports_) { - if (port.second.child_token == child_token) { - DVLOG(1) << "Closing reserved port " << port.second.port.name(); - ports_to_close.push_back(port.second.port); - port_tokens.push_back(port.first); - } - } - - for (const auto& token : port_tokens) - reserved_ports_.erase(token); - } - - for (const auto& port : ports_to_close) - node_->ClosePort(port); - - // Ensure local port closure messages are processed. - AcceptIncomingMessages(); -} - -void NodeController::ClosePeerConnection(const std::string& peer_token) { - io_task_runner_->PostTask( - FROM_HERE, base::Bind(&NodeController::ClosePeerConnectionOnIOThread, - base::Unretained(this), peer_token)); -} - -void NodeController::ConnectToParent(ConnectionParams connection_params) { -#if !defined(OS_MACOSX) && !defined(OS_NACL_SFI) - // Use the bootstrap channel for the broker and receive the node's channel - // synchronously as the first message from the broker. - base::ElapsedTimer timer; - broker_.reset(new Broker(connection_params.TakeChannelHandle())); - ScopedPlatformHandle platform_handle = broker_->GetParentPlatformHandle(); - UMA_HISTOGRAM_TIMES("Mojo.System.GetParentPlatformHandleSyncTime", - timer.Elapsed()); - - if (!platform_handle.is_valid()) { - // Most likely the browser side of the channel has already been closed and - // the broker was unable to negotiate a NodeChannel pipe. In this case we - // can cancel parent connection. - DVLOG(1) << "Cannot connect to invalid parent channel."; - CancelPendingPortMerges(); - return; - } - connection_params = ConnectionParams(std::move(platform_handle)); -#endif - - io_task_runner_->PostTask( - FROM_HERE, - base::Bind(&NodeController::ConnectToParentOnIOThread, - base::Unretained(this), base::Passed(&connection_params))); -} - -void NodeController::ConnectToPeer(ConnectionParams connection_params, - const ports::PortRef& port, - const std::string& peer_token) { - ports::NodeName node_name; - GenerateRandomName(&node_name); - io_task_runner_->PostTask( - FROM_HERE, - base::Bind(&NodeController::ConnectToPeerOnIOThread, - base::Unretained(this), base::Passed(&connection_params), - node_name, port, peer_token)); -} - -void NodeController::SetPortObserver(const ports::PortRef& port, - scoped_refptr<PortObserver> observer) { - node_->SetUserData(port, std::move(observer)); -} - -void NodeController::ClosePort(const ports::PortRef& port) { - SetPortObserver(port, nullptr); - int rv = node_->ClosePort(port); - DCHECK_EQ(rv, ports::OK) << " Failed to close port: " << port.name(); - - AcceptIncomingMessages(); -} - -int NodeController::SendMessage(const ports::PortRef& port, - std::unique_ptr<PortsMessage> message) { - ports::ScopedMessage ports_message(message.release()); - int rv = node_->SendMessage(port, std::move(ports_message)); - - AcceptIncomingMessages(); - return rv; -} - -void NodeController::ReservePort(const std::string& token, - const ports::PortRef& port, - const std::string& child_token) { - DVLOG(2) << "Reserving port " << port.name() << "@" << name_ << " for token " - << token; - - base::AutoLock lock(reserved_ports_lock_); - auto result = reserved_ports_.insert( - std::make_pair(token, ReservedPort{port, child_token})); - DCHECK(result.second); -} - -void NodeController::MergePortIntoParent(const std::string& token, - const ports::PortRef& port) { - bool was_merged = false; - { - // This request may be coming from within the process that reserved the - // "parent" side (e.g. for Chrome single-process mode), so if this token is - // reserved locally, merge locally instead. - base::AutoLock lock(reserved_ports_lock_); - auto it = reserved_ports_.find(token); - if (it != reserved_ports_.end()) { - node_->MergePorts(port, name_, it->second.port.name()); - reserved_ports_.erase(it); - was_merged = true; - } - } - if (was_merged) { - AcceptIncomingMessages(); - return; - } - - scoped_refptr<NodeChannel> parent; - bool reject_merge = false; - { - // Hold |pending_port_merges_lock_| while getting |parent|. Otherwise, - // there is a race where the parent can be set, and |pending_port_merges_| - // be processed between retrieving |parent| and adding the merge to - // |pending_port_merges_|. - base::AutoLock lock(pending_port_merges_lock_); - parent = GetParentChannel(); - if (reject_pending_merges_) { - reject_merge = true; - } else if (!parent) { - pending_port_merges_.push_back(std::make_pair(token, port)); - return; - } - } - if (reject_merge) { - node_->ClosePort(port); - DVLOG(2) << "Rejecting port merge for token " << token - << " due to closed parent channel."; - AcceptIncomingMessages(); - return; - } - - parent->RequestPortMerge(port.name(), token); -} - -int NodeController::MergeLocalPorts(const ports::PortRef& port0, - const ports::PortRef& port1) { - int rv = node_->MergeLocalPorts(port0, port1); - AcceptIncomingMessages(); - return rv; -} - -scoped_refptr<PlatformSharedBuffer> NodeController::CreateSharedBuffer( - size_t num_bytes) { -#if !defined(OS_MACOSX) && !defined(OS_NACL_SFI) - // Shared buffer creation failure is fatal, so always use the broker when we - // have one. This does mean that a non-root process that has children will use - // the broker for shared buffer creation even though that process is - // privileged. - if (broker_) { - return broker_->GetSharedBuffer(num_bytes); - } -#endif - return PlatformSharedBuffer::Create(num_bytes); -} - -void NodeController::RequestShutdown(const base::Closure& callback) { - { - base::AutoLock lock(shutdown_lock_); - shutdown_callback_ = callback; - shutdown_callback_flag_.Set(true); - } - - AttemptShutdownIfRequested(); -} - -void NodeController::NotifyBadMessageFrom(const ports::NodeName& source_node, - const std::string& error) { - scoped_refptr<NodeChannel> peer = GetPeerChannel(source_node); - if (peer) - peer->NotifyBadMessage(error); -} - -void NodeController::ConnectToChildOnIOThread( - base::ProcessHandle process_handle, - ConnectionParams connection_params, - ports::NodeName token, - const ProcessErrorCallback& process_error_callback) { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - -#if !defined(OS_MACOSX) && !defined(OS_NACL) - PlatformChannelPair node_channel; - ScopedPlatformHandle server_handle = node_channel.PassServerHandle(); - // BrokerHost owns itself. - BrokerHost* broker_host = - new BrokerHost(process_handle, connection_params.TakeChannelHandle()); - bool channel_ok = broker_host->SendChannel(node_channel.PassClientHandle()); - -#if defined(OS_WIN) - if (!channel_ok) { - // On Windows the above operation may fail if the channel is crossing a - // session boundary. In that case we fall back to a named pipe. - NamedPlatformChannelPair named_channel; - server_handle = named_channel.PassServerHandle(); - broker_host->SendNamedChannel(named_channel.handle().name); - } -#else - CHECK(channel_ok); -#endif // defined(OS_WIN) - - scoped_refptr<NodeChannel> channel = - NodeChannel::Create(this, ConnectionParams(std::move(server_handle)), - io_task_runner_, process_error_callback); - -#else // !defined(OS_MACOSX) && !defined(OS_NACL) - scoped_refptr<NodeChannel> channel = - NodeChannel::Create(this, std::move(connection_params), io_task_runner_, - process_error_callback); -#endif // !defined(OS_MACOSX) && !defined(OS_NACL) - - // We set up the child channel with a temporary name so it can be identified - // as a pending child if it writes any messages to the channel. We may start - // receiving messages from it (though we shouldn't) as soon as Start() is - // called below. - - pending_children_.insert(std::make_pair(token, channel)); - RecordPendingChildCount(pending_children_.size()); - - channel->SetRemoteNodeName(token); - channel->SetRemoteProcessHandle(process_handle); - channel->Start(); - - channel->AcceptChild(name_, token); -} - -void NodeController::ConnectToParentOnIOThread( - ConnectionParams connection_params) { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - - { - base::AutoLock lock(parent_lock_); - DCHECK(parent_name_ == ports::kInvalidNodeName); - - // At this point we don't know the parent's name, so we can't yet insert it - // into our |peers_| map. That will happen as soon as we receive an - // AcceptChild message from them. - bootstrap_parent_channel_ = - NodeChannel::Create(this, std::move(connection_params), io_task_runner_, - ProcessErrorCallback()); - // Prevent the parent pipe handle from being closed on shutdown. Pipe - // closure is used by the parent to detect the child process has exited. - // Relying on message pipes to be closed is not enough because the parent - // may see the message pipe closure before the child is dead, causing the - // child process to be unexpectedly SIGKILL'd. - bootstrap_parent_channel_->LeakHandleOnShutdown(); - } - bootstrap_parent_channel_->Start(); -} - -void NodeController::ConnectToPeerOnIOThread(ConnectionParams connection_params, - ports::NodeName token, - ports::PortRef port, - const std::string& peer_token) { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - - scoped_refptr<NodeChannel> channel = NodeChannel::Create( - this, std::move(connection_params), io_task_runner_, {}); - peer_connections_.insert( - {token, PeerConnection{channel, port, peer_token}}); - peers_by_token_.insert({peer_token, token}); - - channel->SetRemoteNodeName(token); - channel->Start(); - - channel->AcceptPeer(name_, token, port.name()); -} - -void NodeController::ClosePeerConnectionOnIOThread( - const std::string& peer_token) { - RequestContext request_context(RequestContext::Source::SYSTEM); - auto peer = peers_by_token_.find(peer_token); - // The connection may already be closed. - if (peer == peers_by_token_.end()) - return; - - // |peer| may be removed so make a copy of |name|. - ports::NodeName name = peer->second; - DropPeer(name, nullptr); -} - -scoped_refptr<NodeChannel> NodeController::GetPeerChannel( - const ports::NodeName& name) { - base::AutoLock lock(peers_lock_); - auto it = peers_.find(name); - if (it == peers_.end()) - return nullptr; - return it->second; -} - -scoped_refptr<NodeChannel> NodeController::GetParentChannel() { - ports::NodeName parent_name; - { - base::AutoLock lock(parent_lock_); - parent_name = parent_name_; - } - return GetPeerChannel(parent_name); -} - -scoped_refptr<NodeChannel> NodeController::GetBrokerChannel() { - ports::NodeName broker_name; - { - base::AutoLock lock(broker_lock_); - broker_name = broker_name_; - } - return GetPeerChannel(broker_name); -} - -void NodeController::AddPeer(const ports::NodeName& name, - scoped_refptr<NodeChannel> channel, - bool start_channel) { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - - DCHECK(name != ports::kInvalidNodeName); - DCHECK(channel); - - channel->SetRemoteNodeName(name); - - OutgoingMessageQueue pending_messages; - { - base::AutoLock lock(peers_lock_); - if (peers_.find(name) != peers_.end()) { - // This can happen normally if two nodes race to be introduced to each - // other. The losing pipe will be silently closed and introduction should - // not be affected. - DVLOG(1) << "Ignoring duplicate peer name " << name; - return; - } - - auto result = peers_.insert(std::make_pair(name, channel)); - DCHECK(result.second); - - DVLOG(2) << "Accepting new peer " << name << " on node " << name_; - - RecordPeerCount(peers_.size()); - - auto it = pending_peer_messages_.find(name); - if (it != pending_peer_messages_.end()) { - std::swap(pending_messages, it->second); - pending_peer_messages_.erase(it); - } - } - - if (start_channel) - channel->Start(); - - // Flush any queued message we need to deliver to this node. - while (!pending_messages.empty()) { - channel->PortsMessage(std::move(pending_messages.front())); - pending_messages.pop(); - } -} - -void NodeController::DropPeer(const ports::NodeName& name, - NodeChannel* channel) { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - - { - base::AutoLock lock(peers_lock_); - auto it = peers_.find(name); - - if (it != peers_.end()) { - ports::NodeName peer = it->first; - peers_.erase(it); - DVLOG(1) << "Dropped peer " << peer; - } - - pending_peer_messages_.erase(name); - pending_children_.erase(name); - - RecordPeerCount(peers_.size()); - RecordPendingChildCount(pending_children_.size()); - } - - std::vector<ports::PortRef> ports_to_close; - { - // Clean up any reserved ports. - base::AutoLock lock(reserved_ports_lock_); - auto it = pending_child_tokens_.find(name); - if (it != pending_child_tokens_.end()) { - const std::string& child_token = it->second; - - std::vector<std::string> port_tokens; - for (const auto& port : reserved_ports_) { - if (port.second.child_token == child_token) { - DVLOG(1) << "Closing reserved port: " << port.second.port.name(); - ports_to_close.push_back(port.second.port); - port_tokens.push_back(port.first); - } - } - - // We have to erase reserved ports in a two-step manner because the usual - // manner of using the returned iterator from map::erase isn't technically - // valid in C++11 (although it is in C++14). - for (const auto& token : port_tokens) - reserved_ports_.erase(token); - - pending_child_tokens_.erase(it); - } - } - - bool is_parent; - { - base::AutoLock lock(parent_lock_); - is_parent = (name == parent_name_ || channel == bootstrap_parent_channel_); - } - - // If the error comes from the parent channel, we also need to cancel any - // port merge requests, so that errors can be propagated to the message - // pipes. - if (is_parent) - CancelPendingPortMerges(); - - auto peer = peer_connections_.find(name); - if (peer != peer_connections_.end()) { - peers_by_token_.erase(peer->second.peer_token); - ports_to_close.push_back(peer->second.local_port); - peer_connections_.erase(peer); - } - - for (const auto& port : ports_to_close) - node_->ClosePort(port); - - node_->LostConnectionToNode(name); - - AcceptIncomingMessages(); -} - -void NodeController::SendPeerMessage(const ports::NodeName& name, - ports::ScopedMessage message) { - Channel::MessagePtr channel_message = - static_cast<PortsMessage*>(message.get())->TakeChannelMessage(); - - scoped_refptr<NodeChannel> peer = GetPeerChannel(name); -#if defined(OS_WIN) - if (channel_message->has_handles()) { - // If we're sending a message with handles we aren't the destination - // node's parent or broker (i.e. we don't know its process handle), ask - // the broker to relay for us. - scoped_refptr<NodeChannel> broker = GetBrokerChannel(); - if (!peer || !peer->HasRemoteProcessHandle()) { - if (broker) { - broker->RelayPortsMessage(name, std::move(channel_message)); - } else { - base::AutoLock lock(broker_lock_); - pending_relay_messages_[name].emplace(std::move(channel_message)); - } - return; - } - } -#elif defined(OS_MACOSX) && !defined(OS_IOS) - if (channel_message->has_mach_ports()) { - // Messages containing Mach ports are always routed through the broker, even - // if the broker process is the intended recipient. - bool use_broker = false; - { - base::AutoLock lock(parent_lock_); - use_broker = (bootstrap_parent_channel_ || - parent_name_ != ports::kInvalidNodeName); - } - if (use_broker) { - scoped_refptr<NodeChannel> broker = GetBrokerChannel(); - if (broker) { - broker->RelayPortsMessage(name, std::move(channel_message)); - } else { - base::AutoLock lock(broker_lock_); - pending_relay_messages_[name].emplace(std::move(channel_message)); - } - return; - } - } -#endif // defined(OS_WIN) - - if (peer) { - peer->PortsMessage(std::move(channel_message)); - return; - } - - // If we don't know who the peer is and we are the broker, we can only assume - // the peer is invalid, i.e., it's either a junk name or has already been - // disconnected. - scoped_refptr<NodeChannel> broker = GetBrokerChannel(); - if (!broker) { - DVLOG(1) << "Dropping message for unknown peer: " << name; - return; - } - - // If we aren't the broker, assume we just need to be introduced and queue - // until that can be either confirmed or denied by the broker. - bool needs_introduction = false; - { - base::AutoLock lock(peers_lock_); - auto& queue = pending_peer_messages_[name]; - needs_introduction = queue.empty(); - queue.emplace(std::move(channel_message)); - } - if (needs_introduction) - broker->RequestIntroduction(name); -} - -void NodeController::AcceptIncomingMessages() { - // This is an impactically large value which should never be reached in - // practice. See the CHECK below for usage. - constexpr size_t kMaxAcceptedMessages = 1000000; - - size_t num_messages_accepted = 0; - while (incoming_messages_flag_) { - // TODO: We may need to be more careful to avoid starving the rest of the - // thread here. Revisit this if it turns out to be a problem. One - // alternative would be to schedule a task to continue pumping messages - // after flushing once. - - messages_lock_.Acquire(); - if (incoming_messages_.empty()) { - messages_lock_.Release(); - break; - } - - // libstdc++'s deque creates an internal buffer on construction, even when - // the size is 0. So avoid creating it until it is necessary. - std::queue<ports::ScopedMessage> messages; - std::swap(messages, incoming_messages_); - incoming_messages_flag_.Set(false); - messages_lock_.Release(); - - num_messages_accepted += messages.size(); - while (!messages.empty()) { - node_->AcceptMessage(std::move(messages.front())); - messages.pop(); - } - - // This is effectively a safeguard against potential bugs which might lead - // to runaway message cycles. If any such cycles arise, we'll start seeing - // crash reports from this location. - CHECK_LE(num_messages_accepted, kMaxAcceptedMessages); - } - - if (num_messages_accepted >= 4) { - // Note: We avoid logging this histogram for the vast majority of cases. - // See https://crbug.com/685763 for more context. - UMA_HISTOGRAM_CUSTOM_COUNTS("Mojo.System.MessagesAcceptedPerEvent", - static_cast<int32_t>(num_messages_accepted), - 1 /* min */, - 500 /* max */, - 50 /* bucket count */); - } - - AttemptShutdownIfRequested(); -} - -void NodeController::ProcessIncomingMessages() { - RequestContext request_context(RequestContext::Source::SYSTEM); - - { - base::AutoLock lock(messages_lock_); - // Allow a new incoming messages processing task to be posted. This can't be - // done after AcceptIncomingMessages() otherwise a message might be missed. - // Doing it here may result in at most two tasks existing at the same time; - // this running one, and one pending in the task runner. - incoming_messages_task_posted_ = false; - } - - AcceptIncomingMessages(); -} - -void NodeController::DropAllPeers() { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - - std::vector<scoped_refptr<NodeChannel>> all_peers; - { - base::AutoLock lock(parent_lock_); - if (bootstrap_parent_channel_) { - // |bootstrap_parent_channel_| isn't null'd here becuase we rely on its - // existence to determine whether or not this is the root node. Once - // bootstrap_parent_channel_->ShutDown() has been called, - // |bootstrap_parent_channel_| is essentially a dead object and it doesn't - // matter if it's deleted now or when |this| is deleted. - // Note: |bootstrap_parent_channel_| is only modified on the IO thread. - all_peers.push_back(bootstrap_parent_channel_); - } - } - - { - base::AutoLock lock(peers_lock_); - for (const auto& peer : peers_) - all_peers.push_back(peer.second); - for (const auto& peer : pending_children_) - all_peers.push_back(peer.second); - peers_.clear(); - pending_children_.clear(); - pending_peer_messages_.clear(); - peer_connections_.clear(); - } - - for (const auto& peer : all_peers) - peer->ShutDown(); - - if (destroy_on_io_thread_shutdown_) - delete this; -} - -void NodeController::GenerateRandomPortName(ports::PortName* port_name) { - GenerateRandomName(port_name); -} - -void NodeController::AllocMessage(size_t num_header_bytes, - ports::ScopedMessage* message) { - message->reset(new PortsMessage(num_header_bytes, 0, 0, nullptr)); -} - -void NodeController::ForwardMessage(const ports::NodeName& node, - ports::ScopedMessage message) { - DCHECK(message); - bool schedule_pump_task = false; - if (node == name_) { - // NOTE: We need to avoid re-entering the Node instance within - // ForwardMessage. Because ForwardMessage is only ever called - // (synchronously) in response to Node's ClosePort, SendMessage, or - // AcceptMessage, we flush the queue after calling any of those methods. - base::AutoLock lock(messages_lock_); - // |io_task_runner_| may be null in tests or processes that don't require - // multi-process Mojo. - schedule_pump_task = incoming_messages_.empty() && io_task_runner_ && - !incoming_messages_task_posted_; - incoming_messages_task_posted_ |= schedule_pump_task; - incoming_messages_.emplace(std::move(message)); - incoming_messages_flag_.Set(true); - } else { - SendPeerMessage(node, std::move(message)); - } - - if (schedule_pump_task) { - // Normally, the queue is processed after the action that added the local - // message is done (i.e. SendMessage, ClosePort, etc). However, it's also - // possible for a local message to be added as a result of a remote message, - // and OnChannelMessage() doesn't process this queue (although - // OnPortsMessage() does). There may also be other code paths, now or added - // in the future, which cause local messages to be added but don't process - // this message queue. - // - // Instead of adding a call to AcceptIncomingMessages() on every possible - // code path, post a task to the IO thread to process the queue. If the - // current call stack processes the queue, this may end up doing nothing. - io_task_runner_->PostTask( - FROM_HERE, - base::Bind(&NodeController::ProcessIncomingMessages, - base::Unretained(this))); - } -} - -void NodeController::BroadcastMessage(ports::ScopedMessage message) { - CHECK_EQ(message->num_ports(), 0u); - Channel::MessagePtr channel_message = - static_cast<PortsMessage*>(message.get())->TakeChannelMessage(); - CHECK(!channel_message->has_handles()); - - scoped_refptr<NodeChannel> broker = GetBrokerChannel(); - if (broker) - broker->Broadcast(std::move(channel_message)); - else - OnBroadcast(name_, std::move(channel_message)); -} - -void NodeController::PortStatusChanged(const ports::PortRef& port) { - scoped_refptr<ports::UserData> user_data; - node_->GetUserData(port, &user_data); - - PortObserver* observer = static_cast<PortObserver*>(user_data.get()); - if (observer) { - observer->OnPortStatusChanged(); - } else { - DVLOG(2) << "Ignoring status change for " << port.name() << " because it " - << "doesn't have an observer."; - } -} - -void NodeController::OnAcceptChild(const ports::NodeName& from_node, - const ports::NodeName& parent_name, - const ports::NodeName& token) { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - - scoped_refptr<NodeChannel> parent; - { - base::AutoLock lock(parent_lock_); - if (bootstrap_parent_channel_ && parent_name_ == ports::kInvalidNodeName) { - parent_name_ = parent_name; - parent = bootstrap_parent_channel_; - } - } - - if (!parent) { - DLOG(ERROR) << "Unexpected AcceptChild message from " << from_node; - DropPeer(from_node, nullptr); - return; - } - - parent->SetRemoteNodeName(parent_name); - parent->AcceptParent(token, name_); - - // NOTE: The child does not actually add its parent as a peer until - // receiving an AcceptBrokerClient message from the broker. The parent - // will request that said message be sent upon receiving AcceptParent. - - DVLOG(1) << "Child " << name_ << " accepting parent " << parent_name; -} - -void NodeController::OnAcceptParent(const ports::NodeName& from_node, - const ports::NodeName& token, - const ports::NodeName& child_name) { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - - auto it = pending_children_.find(from_node); - if (it == pending_children_.end() || token != from_node) { - DLOG(ERROR) << "Received unexpected AcceptParent message from " - << from_node; - DropPeer(from_node, nullptr); - return; - } - - { - base::AutoLock lock(reserved_ports_lock_); - auto it = pending_child_tokens_.find(from_node); - if (it != pending_child_tokens_.end()) { - std::string token = std::move(it->second); - pending_child_tokens_.erase(it); - pending_child_tokens_[child_name] = std::move(token); - } - } - - scoped_refptr<NodeChannel> channel = it->second; - pending_children_.erase(it); - - DCHECK(channel); - - DVLOG(1) << "Parent " << name_ << " accepted child " << child_name; - - AddPeer(child_name, channel, false /* start_channel */); - - // TODO(rockot/amistry): We could simplify child initialization if we could - // synchronously get a new async broker channel from the broker. For now we do - // it asynchronously since it's only used to facilitate handle passing, not - // handle creation. - scoped_refptr<NodeChannel> broker = GetBrokerChannel(); - if (broker) { - // Inform the broker of this new child. - broker->AddBrokerClient(child_name, channel->CopyRemoteProcessHandle()); - } else { - // If we have no broker, either we need to wait for one, or we *are* the - // broker. - scoped_refptr<NodeChannel> parent = GetParentChannel(); - if (!parent) { - base::AutoLock lock(parent_lock_); - parent = bootstrap_parent_channel_; - } - - if (!parent) { - // Yes, we're the broker. We can initialize the child directly. - channel->AcceptBrokerClient(name_, ScopedPlatformHandle()); - } else { - // We aren't the broker, so wait for a broker connection. - base::AutoLock lock(broker_lock_); - pending_broker_clients_.push(child_name); - } - } -} - -void NodeController::OnAddBrokerClient(const ports::NodeName& from_node, - const ports::NodeName& client_name, - base::ProcessHandle process_handle) { -#if defined(OS_WIN) - // Scoped handle to avoid leaks on error. - ScopedPlatformHandle scoped_process_handle = - ScopedPlatformHandle(PlatformHandle(process_handle)); -#endif - scoped_refptr<NodeChannel> sender = GetPeerChannel(from_node); - if (!sender) { - DLOG(ERROR) << "Ignoring AddBrokerClient from unknown sender."; - return; - } - - if (GetPeerChannel(client_name)) { - DLOG(ERROR) << "Ignoring AddBrokerClient for known client."; - DropPeer(from_node, nullptr); - return; - } - - PlatformChannelPair broker_channel; - ConnectionParams connection_params(broker_channel.PassServerHandle()); - scoped_refptr<NodeChannel> client = - NodeChannel::Create(this, std::move(connection_params), io_task_runner_, - ProcessErrorCallback()); - -#if defined(OS_WIN) - // The broker must have a working handle to the client process in order to - // properly copy other handles to and from the client. - if (!scoped_process_handle.is_valid()) { - DLOG(ERROR) << "Broker rejecting client with invalid process handle."; - return; - } - client->SetRemoteProcessHandle(scoped_process_handle.release().handle); -#else - client->SetRemoteProcessHandle(process_handle); -#endif - - AddPeer(client_name, client, true /* start_channel */); - - DVLOG(1) << "Broker " << name_ << " accepting client " << client_name - << " from peer " << from_node; - - sender->BrokerClientAdded(client_name, broker_channel.PassClientHandle()); -} - -void NodeController::OnBrokerClientAdded(const ports::NodeName& from_node, - const ports::NodeName& client_name, - ScopedPlatformHandle broker_channel) { - scoped_refptr<NodeChannel> client = GetPeerChannel(client_name); - if (!client) { - DLOG(ERROR) << "BrokerClientAdded for unknown child " << client_name; - return; - } - - // This should have come from our own broker. - if (GetBrokerChannel() != GetPeerChannel(from_node)) { - DLOG(ERROR) << "BrokerClientAdded from non-broker node " << from_node; - return; - } - - DVLOG(1) << "Child " << client_name << " accepted by broker " << from_node; - - client->AcceptBrokerClient(from_node, std::move(broker_channel)); -} - -void NodeController::OnAcceptBrokerClient(const ports::NodeName& from_node, - const ports::NodeName& broker_name, - ScopedPlatformHandle broker_channel) { - // This node should already have a parent in bootstrap mode. - ports::NodeName parent_name; - scoped_refptr<NodeChannel> parent; - { - base::AutoLock lock(parent_lock_); - parent_name = parent_name_; - parent = bootstrap_parent_channel_; - bootstrap_parent_channel_ = nullptr; - } - DCHECK(parent_name == from_node); - DCHECK(parent); - - std::queue<ports::NodeName> pending_broker_clients; - std::unordered_map<ports::NodeName, OutgoingMessageQueue> - pending_relay_messages; - { - base::AutoLock lock(broker_lock_); - broker_name_ = broker_name; - std::swap(pending_broker_clients, pending_broker_clients_); - std::swap(pending_relay_messages, pending_relay_messages_); - } - DCHECK(broker_name != ports::kInvalidNodeName); - - // It's now possible to add both the broker and the parent as peers. - // Note that the broker and parent may be the same node. - scoped_refptr<NodeChannel> broker; - if (broker_name == parent_name) { - DCHECK(!broker_channel.is_valid()); - broker = parent; - } else { - DCHECK(broker_channel.is_valid()); - broker = - NodeChannel::Create(this, ConnectionParams(std::move(broker_channel)), - io_task_runner_, ProcessErrorCallback()); - AddPeer(broker_name, broker, true /* start_channel */); - } - - AddPeer(parent_name, parent, false /* start_channel */); - - { - // Complete any port merge requests we have waiting for the parent. - base::AutoLock lock(pending_port_merges_lock_); - for (const auto& request : pending_port_merges_) - parent->RequestPortMerge(request.second.name(), request.first); - pending_port_merges_.clear(); - } - - // Feed the broker any pending children of our own. - while (!pending_broker_clients.empty()) { - const ports::NodeName& child_name = pending_broker_clients.front(); - auto it = pending_children_.find(child_name); - DCHECK(it != pending_children_.end()); - broker->AddBrokerClient(child_name, it->second->CopyRemoteProcessHandle()); - pending_broker_clients.pop(); - } - -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) - // Have the broker relay any messages we have waiting. - for (auto& entry : pending_relay_messages) { - const ports::NodeName& destination = entry.first; - auto& message_queue = entry.second; - while (!message_queue.empty()) { - broker->RelayPortsMessage(destination, std::move(message_queue.front())); - message_queue.pop(); - } - } -#endif - - DVLOG(1) << "Child " << name_ << " accepted by broker " << broker_name; -} - -void NodeController::OnPortsMessage(const ports::NodeName& from_node, - Channel::MessagePtr channel_message) { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - - void* data; - size_t num_data_bytes, num_header_bytes, num_payload_bytes, num_ports_bytes; - if (!ParsePortsMessage(channel_message.get(), &data, &num_data_bytes, - &num_header_bytes, &num_payload_bytes, - &num_ports_bytes)) { - DropPeer(from_node, nullptr); - return; - } - - CHECK(channel_message); - std::unique_ptr<PortsMessage> ports_message( - new PortsMessage(num_header_bytes, - num_payload_bytes, - num_ports_bytes, - std::move(channel_message))); - ports_message->set_source_node(from_node); - node_->AcceptMessage(ports::ScopedMessage(ports_message.release())); - AcceptIncomingMessages(); -} - -void NodeController::OnRequestPortMerge( - const ports::NodeName& from_node, - const ports::PortName& connector_port_name, - const std::string& token) { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - - DVLOG(2) << "Node " << name_ << " received RequestPortMerge for token " - << token << " and port " << connector_port_name << "@" << from_node; - - ports::PortRef local_port; - { - base::AutoLock lock(reserved_ports_lock_); - auto it = reserved_ports_.find(token); - if (it == reserved_ports_.end()) { - DVLOG(1) << "Ignoring request to connect to port for unknown token " - << token; - return; - } - local_port = it->second.port; - reserved_ports_.erase(it); - } - - int rv = node_->MergePorts(local_port, from_node, connector_port_name); - if (rv != ports::OK) - DLOG(ERROR) << "MergePorts failed: " << rv; - - AcceptIncomingMessages(); -} - -void NodeController::OnRequestIntroduction(const ports::NodeName& from_node, - const ports::NodeName& name) { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - - scoped_refptr<NodeChannel> requestor = GetPeerChannel(from_node); - if (from_node == name || name == ports::kInvalidNodeName || !requestor) { - DLOG(ERROR) << "Rejecting invalid OnRequestIntroduction message from " - << from_node; - DropPeer(from_node, nullptr); - return; - } - - scoped_refptr<NodeChannel> new_friend = GetPeerChannel(name); - if (!new_friend) { - // We don't know who they're talking about! - requestor->Introduce(name, ScopedPlatformHandle()); - } else { - PlatformChannelPair new_channel; - requestor->Introduce(name, new_channel.PassServerHandle()); - new_friend->Introduce(from_node, new_channel.PassClientHandle()); - } -} - -void NodeController::OnIntroduce(const ports::NodeName& from_node, - const ports::NodeName& name, - ScopedPlatformHandle channel_handle) { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - - if (!channel_handle.is_valid()) { - node_->LostConnectionToNode(name); - - DVLOG(1) << "Could not be introduced to peer " << name; - base::AutoLock lock(peers_lock_); - pending_peer_messages_.erase(name); - return; - } - - scoped_refptr<NodeChannel> channel = - NodeChannel::Create(this, ConnectionParams(std::move(channel_handle)), - io_task_runner_, ProcessErrorCallback()); - - DVLOG(1) << "Adding new peer " << name << " via parent introduction."; - AddPeer(name, channel, true /* start_channel */); -} - -void NodeController::OnBroadcast(const ports::NodeName& from_node, - Channel::MessagePtr message) { - DCHECK(!message->has_handles()); - - void* data; - size_t num_data_bytes, num_header_bytes, num_payload_bytes, num_ports_bytes; - if (!ParsePortsMessage(message.get(), &data, &num_data_bytes, - &num_header_bytes, &num_payload_bytes, - &num_ports_bytes)) { - DropPeer(from_node, nullptr); - return; - } - - // Broadcast messages must not contain ports. - if (num_ports_bytes > 0) { - DropPeer(from_node, nullptr); - return; - } - - base::AutoLock lock(peers_lock_); - for (auto& iter : peers_) { - // Copy and send the message to each known peer. - Channel::MessagePtr peer_message( - new Channel::Message(message->payload_size(), 0)); - memcpy(peer_message->mutable_payload(), message->payload(), - message->payload_size()); - iter.second->PortsMessage(std::move(peer_message)); - } -} - -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) -void NodeController::OnRelayPortsMessage(const ports::NodeName& from_node, - base::ProcessHandle from_process, - const ports::NodeName& destination, - Channel::MessagePtr message) { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - - if (GetBrokerChannel()) { - // Only the broker should be asked to relay a message. - LOG(ERROR) << "Non-broker refusing to relay message."; - DropPeer(from_node, nullptr); - return; - } - - // The parent should always know which process this came from. - DCHECK(from_process != base::kNullProcessHandle); - -#if defined(OS_WIN) - // Rewrite the handles to this (the parent) process. If the message is - // destined for another child process, the handles will be rewritten to that - // process before going out (see NodeChannel::WriteChannelMessage). - // - // TODO: We could avoid double-duplication. - // - // Note that we explicitly mark the handles as being owned by the sending - // process before rewriting them, in order to accommodate RewriteHandles' - // internal sanity checks. - ScopedPlatformHandleVectorPtr handles = message->TakeHandles(); - for (size_t i = 0; i < handles->size(); ++i) - (*handles)[i].owning_process = from_process; - if (!Channel::Message::RewriteHandles(from_process, - base::GetCurrentProcessHandle(), - handles.get())) { - DLOG(ERROR) << "Failed to relay one or more handles."; - } - message->SetHandles(std::move(handles)); -#else - MachPortRelay* relay = GetMachPortRelay(); - if (!relay) { - LOG(ERROR) << "Receiving Mach ports without a port relay from " - << from_node << ". Dropping message."; - return; - } - if (!relay->ExtractPortRights(message.get(), from_process)) { - // NodeChannel should ensure that MachPortRelay is ready for the remote - // process. At this point, if the port extraction failed, either something - // went wrong in the mach stuff, or the remote process died. - LOG(ERROR) << "Error on receiving Mach ports " << from_node - << ". Dropping message."; - return; - } -#endif // defined(OS_WIN) - - if (destination == name_) { - // Great, we can deliver this message locally. - OnPortsMessage(from_node, std::move(message)); - return; - } - - scoped_refptr<NodeChannel> peer = GetPeerChannel(destination); - if (peer) - peer->PortsMessageFromRelay(from_node, std::move(message)); - else - DLOG(ERROR) << "Dropping relay message for unknown node " << destination; -} - -void NodeController::OnPortsMessageFromRelay(const ports::NodeName& from_node, - const ports::NodeName& source_node, - Channel::MessagePtr message) { - if (GetPeerChannel(from_node) != GetBrokerChannel()) { - LOG(ERROR) << "Refusing relayed message from non-broker node."; - DropPeer(from_node, nullptr); - return; - } - - OnPortsMessage(source_node, std::move(message)); -} -#endif - -void NodeController::OnAcceptPeer(const ports::NodeName& from_node, - const ports::NodeName& token, - const ports::NodeName& peer_name, - const ports::PortName& port_name) { - DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - - auto it = peer_connections_.find(from_node); - if (it == peer_connections_.end()) { - DLOG(ERROR) << "Received unexpected AcceptPeer message from " << from_node; - DropPeer(from_node, nullptr); - return; - } - - scoped_refptr<NodeChannel> channel = std::move(it->second.channel); - ports::PortRef local_port = it->second.local_port; - std::string peer_token = std::move(it->second.peer_token); - peer_connections_.erase(it); - DCHECK(channel); - - // If the peer connection is a self connection (which is used in tests), - // drop the channel to it and skip straight to merging the ports. - if (name_ == peer_name) { - peers_by_token_.erase(peer_token); - } else { - peers_by_token_[peer_token] = peer_name; - peer_connections_.insert( - {peer_name, PeerConnection{nullptr, local_port, peer_token}}); - DVLOG(1) << "Node " << name_ << " accepted peer " << peer_name; - - AddPeer(peer_name, channel, false /* start_channel */); - } - - // We need to choose one side to initiate the port merge. It doesn't matter - // who does it as long as they don't both try. Simple solution: pick the one - // with the "smaller" port name. - if (local_port.name() < port_name) { - node()->MergePorts(local_port, peer_name, port_name); - } -} - -void NodeController::OnChannelError(const ports::NodeName& from_node, - NodeChannel* channel) { - if (io_task_runner_->RunsTasksOnCurrentThread()) { - DropPeer(from_node, channel); - // DropPeer may have caused local port closures, so be sure to process any - // pending local messages. - AcceptIncomingMessages(); - } else { - io_task_runner_->PostTask( - FROM_HERE, - base::Bind(&NodeController::OnChannelError, base::Unretained(this), - from_node, channel)); - } -} - -#if defined(OS_MACOSX) && !defined(OS_IOS) -MachPortRelay* NodeController::GetMachPortRelay() { - { - base::AutoLock lock(parent_lock_); - // Return null if we're not the root. - if (bootstrap_parent_channel_ || parent_name_ != ports::kInvalidNodeName) - return nullptr; - } - - base::AutoLock lock(mach_port_relay_lock_); - return mach_port_relay_.get(); -} -#endif - -void NodeController::CancelPendingPortMerges() { - std::vector<ports::PortRef> ports_to_close; - - { - base::AutoLock lock(pending_port_merges_lock_); - reject_pending_merges_ = true; - for (const auto& port : pending_port_merges_) - ports_to_close.push_back(port.second); - pending_port_merges_.clear(); - } - - for (const auto& port : ports_to_close) - node_->ClosePort(port); -} - -void NodeController::DestroyOnIOThreadShutdown() { - destroy_on_io_thread_shutdown_ = true; -} - -void NodeController::AttemptShutdownIfRequested() { - if (!shutdown_callback_flag_) - return; - - base::Closure callback; - { - base::AutoLock lock(shutdown_lock_); - if (shutdown_callback_.is_null()) - return; - if (!node_->CanShutdownCleanly( - ports::Node::ShutdownPolicy::ALLOW_LOCAL_PORTS)) { - DVLOG(2) << "Unable to cleanly shut down node " << name_; - return; - } - - callback = shutdown_callback_; - shutdown_callback_.Reset(); - shutdown_callback_flag_.Set(false); - } - - DCHECK(!callback.is_null()); - - callback.Run(); -} - -NodeController::PeerConnection::PeerConnection() = default; - -NodeController::PeerConnection::PeerConnection( - const PeerConnection& other) = default; - -NodeController::PeerConnection::PeerConnection( - PeerConnection&& other) = default; - -NodeController::PeerConnection::PeerConnection( - scoped_refptr<NodeChannel> channel, - const ports::PortRef& local_port, - const std::string& peer_token) - : channel(std::move(channel)), - local_port(local_port), - peer_token(peer_token) {} - -NodeController::PeerConnection::~PeerConnection() = default; - -NodeController::PeerConnection& NodeController::PeerConnection:: -operator=(const PeerConnection& other) = default; - -NodeController::PeerConnection& NodeController::PeerConnection:: -operator=(PeerConnection&& other) = default; - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/node_controller.h b/mojo/edk/system/node_controller.h deleted file mode 100644 index 46a2d61..0000000 --- a/mojo/edk/system/node_controller.h +++ /dev/null @@ -1,378 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_NODE_CONTROLLER_H_ -#define MOJO_EDK_SYSTEM_NODE_CONTROLLER_H_ - -#include <memory> -#include <queue> -#include <unordered_map> -#include <unordered_set> -#include <utility> -#include <vector> - -#include "base/callback.h" -#include "base/containers/hash_tables.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/task_runner.h" -#include "mojo/edk/embedder/platform_handle_vector.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/atomic_flag.h" -#include "mojo/edk/system/node_channel.h" -#include "mojo/edk/system/ports/name.h" -#include "mojo/edk/system/ports/node.h" -#include "mojo/edk/system/ports/node_delegate.h" - -namespace base { -class PortProvider; -} - -namespace mojo { -namespace edk { - -class Broker; -class Core; -class MachPortRelay; -class PortsMessage; - -// The owner of ports::Node which facilitates core EDK implementation. All -// public interface methods are safe to call from any thread. -class NodeController : public ports::NodeDelegate, - public NodeChannel::Delegate { - public: - class PortObserver : public ports::UserData { - public: - virtual void OnPortStatusChanged() = 0; - - protected: - ~PortObserver() override {} - }; - - // |core| owns and out-lives us. - explicit NodeController(Core* core); - ~NodeController() override; - - const ports::NodeName& name() const { return name_; } - Core* core() const { return core_; } - ports::Node* node() const { return node_.get(); } - scoped_refptr<base::TaskRunner> io_task_runner() const { - return io_task_runner_; - } - -#if defined(OS_MACOSX) && !defined(OS_IOS) - // Create the relay used to transfer mach ports between processes. - void CreateMachPortRelay(base::PortProvider* port_provider); -#endif - - // Called exactly once, shortly after construction, and before any other - // methods are called on this object. - void SetIOTaskRunner(scoped_refptr<base::TaskRunner> io_task_runner); - - // Connects this node to a child node. This node will initiate a handshake. - void ConnectToChild(base::ProcessHandle process_handle, - ConnectionParams connection_params, - const std::string& child_token, - const ProcessErrorCallback& process_error_callback); - - // Closes all reserved ports which associated with the child process - // |child_token|. - void CloseChildPorts(const std::string& child_token); - - // Close a connection to a peer associated with |peer_token|. - void ClosePeerConnection(const std::string& peer_token); - - // Connects this node to a parent node. The parent node will initiate a - // handshake. - void ConnectToParent(ConnectionParams connection_params); - - // Connects this node to a peer node. On success, |port| will be merged with - // the corresponding port in the peer node. - void ConnectToPeer(ConnectionParams connection_params, - const ports::PortRef& port, - const std::string& peer_token); - - // Sets a port's observer. If |observer| is null the port's current observer - // is removed. - void SetPortObserver(const ports::PortRef& port, - scoped_refptr<PortObserver> observer); - - // Closes a port. Use this in lieu of calling Node::ClosePort() directly, as - // it ensures the port's observer has also been removed. - void ClosePort(const ports::PortRef& port); - - // Sends a message on a port to its peer. - int SendMessage(const ports::PortRef& port_ref, - std::unique_ptr<PortsMessage> message); - - // Reserves a local port |port| associated with |token|. A peer holding a copy - // of |token| can merge one of its own ports into this one. - void ReservePort(const std::string& token, const ports::PortRef& port, - const std::string& child_token); - - // Merges a local port |port| into a port reserved by |token| in the parent. - void MergePortIntoParent(const std::string& token, - const ports::PortRef& port); - - // Merges two local ports together. - int MergeLocalPorts(const ports::PortRef& port0, const ports::PortRef& port1); - - // Creates a new shared buffer for use in the current process. - scoped_refptr<PlatformSharedBuffer> CreateSharedBuffer(size_t num_bytes); - - // Request that the Node be shut down cleanly. This may take an arbitrarily - // long time to complete, at which point |callback| will be called. - // - // Note that while it is safe to continue using the NodeController's public - // interface after requesting shutdown, you do so at your own risk and there - // is NO guarantee that new messages will be sent or ports will complete - // transfer. - void RequestShutdown(const base::Closure& callback); - - // Notifies the NodeController that we received a bad message from the given - // node. - void NotifyBadMessageFrom(const ports::NodeName& source_node, - const std::string& error); - - private: - friend Core; - - using NodeMap = std::unordered_map<ports::NodeName, - scoped_refptr<NodeChannel>>; - using OutgoingMessageQueue = std::queue<Channel::MessagePtr>; - - struct ReservedPort { - ports::PortRef port; - const std::string child_token; - }; - - struct PeerConnection { - PeerConnection(); - PeerConnection(const PeerConnection& other); - PeerConnection(PeerConnection&& other); - PeerConnection(scoped_refptr<NodeChannel> channel, - const ports::PortRef& local_port, - const std::string& peer_token); - ~PeerConnection(); - - PeerConnection& operator=(const PeerConnection& other); - PeerConnection& operator=(PeerConnection&& other); - - - scoped_refptr<NodeChannel> channel; - ports::PortRef local_port; - std::string peer_token; - }; - - void ConnectToChildOnIOThread( - base::ProcessHandle process_handle, - ConnectionParams connection_params, - ports::NodeName token, - const ProcessErrorCallback& process_error_callback); - void ConnectToParentOnIOThread(ConnectionParams connection_params); - - void ConnectToPeerOnIOThread(ConnectionParams connection_params, - ports::NodeName token, - ports::PortRef port, - const std::string& peer_token); - void ClosePeerConnectionOnIOThread(const std::string& node_name); - - scoped_refptr<NodeChannel> GetPeerChannel(const ports::NodeName& name); - scoped_refptr<NodeChannel> GetParentChannel(); - scoped_refptr<NodeChannel> GetBrokerChannel(); - - void AddPeer(const ports::NodeName& name, - scoped_refptr<NodeChannel> channel, - bool start_channel); - void DropPeer(const ports::NodeName& name, NodeChannel* channel); - void SendPeerMessage(const ports::NodeName& name, - ports::ScopedMessage message); - void AcceptIncomingMessages(); - void ProcessIncomingMessages(); - void DropAllPeers(); - - // ports::NodeDelegate: - void GenerateRandomPortName(ports::PortName* port_name) override; - void AllocMessage(size_t num_header_bytes, - ports::ScopedMessage* message) override; - void ForwardMessage(const ports::NodeName& node, - ports::ScopedMessage message) override; - void BroadcastMessage(ports::ScopedMessage message) override; - void PortStatusChanged(const ports::PortRef& port) override; - - // NodeChannel::Delegate: - void OnAcceptChild(const ports::NodeName& from_node, - const ports::NodeName& parent_name, - const ports::NodeName& token) override; - void OnAcceptParent(const ports::NodeName& from_node, - const ports::NodeName& token, - const ports::NodeName& child_name) override; - void OnAddBrokerClient(const ports::NodeName& from_node, - const ports::NodeName& client_name, - base::ProcessHandle process_handle) override; - void OnBrokerClientAdded(const ports::NodeName& from_node, - const ports::NodeName& client_name, - ScopedPlatformHandle broker_channel) override; - void OnAcceptBrokerClient(const ports::NodeName& from_node, - const ports::NodeName& broker_name, - ScopedPlatformHandle broker_channel) override; - void OnPortsMessage(const ports::NodeName& from_node, - Channel::MessagePtr message) override; - void OnRequestPortMerge(const ports::NodeName& from_node, - const ports::PortName& connector_port_name, - const std::string& token) override; - void OnRequestIntroduction(const ports::NodeName& from_node, - const ports::NodeName& name) override; - void OnIntroduce(const ports::NodeName& from_node, - const ports::NodeName& name, - ScopedPlatformHandle channel_handle) override; - void OnBroadcast(const ports::NodeName& from_node, - Channel::MessagePtr message) override; -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) - void OnRelayPortsMessage(const ports::NodeName& from_node, - base::ProcessHandle from_process, - const ports::NodeName& destination, - Channel::MessagePtr message) override; - void OnPortsMessageFromRelay(const ports::NodeName& from_node, - const ports::NodeName& source_node, - Channel::MessagePtr message) override; -#endif - void OnAcceptPeer(const ports::NodeName& from_node, - const ports::NodeName& token, - const ports::NodeName& peer_name, - const ports::PortName& port_name) override; - void OnChannelError(const ports::NodeName& from_node, - NodeChannel* channel) override; -#if defined(OS_MACOSX) && !defined(OS_IOS) - MachPortRelay* GetMachPortRelay() override; -#endif - - // Cancels all pending port merges. These are merges which are supposed to - // be requested from the parent ASAP, and they may be cancelled if the - // connection to the parent is broken or never established. - void CancelPendingPortMerges(); - - // Marks this NodeController for destruction when the IO thread shuts down. - // This is used in case Core is torn down before the IO thread. Must only be - // called on the IO thread. - void DestroyOnIOThreadShutdown(); - - // If there is a registered shutdown callback (meaning shutdown has been - // requested, this checks the Node's status to see if clean shutdown is - // possible. If so, shutdown is performed and the shutdown callback is run. - void AttemptShutdownIfRequested(); - - // These are safe to access from any thread as long as the Node is alive. - Core* const core_; - const ports::NodeName name_; - const std::unique_ptr<ports::Node> node_; - scoped_refptr<base::TaskRunner> io_task_runner_; - - // Guards |peers_| and |pending_peer_messages_|. - base::Lock peers_lock_; - - // Channels to known peers, including parent and children, if any. - NodeMap peers_; - - // Outgoing message queues for peers we've heard of but can't yet talk to. - std::unordered_map<ports::NodeName, OutgoingMessageQueue> - pending_peer_messages_; - - // Guards |reserved_ports_| and |pending_child_tokens_|. - base::Lock reserved_ports_lock_; - - // Ports reserved by token. Key is the port token. - base::hash_map<std::string, ReservedPort> reserved_ports_; - // TODO(amistry): This _really_ needs to be a bimap. Unfortunately, we don't - // have one yet :( - std::unordered_map<ports::NodeName, std::string> pending_child_tokens_; - - // Guards |pending_port_merges_| and |reject_pending_merges_|. - base::Lock pending_port_merges_lock_; - - // A set of port merge requests awaiting parent connection. - std::vector<std::pair<std::string, ports::PortRef>> pending_port_merges_; - - // Indicates that new merge requests should be rejected because the parent has - // disconnected. - bool reject_pending_merges_ = false; - - // Guards |parent_name_| and |bootstrap_parent_channel_|. - base::Lock parent_lock_; - - // The name of our parent node, if any. - ports::NodeName parent_name_; - - // A temporary reference to the parent channel before we know their name. - scoped_refptr<NodeChannel> bootstrap_parent_channel_; - - // Guards |broker_name_|, |pending_broker_clients_|, and - // |pending_relay_messages_|. - base::Lock broker_lock_; - - // The name of our broker node, if any. - ports::NodeName broker_name_; - - // A queue of pending child names waiting to be connected to a broker. - std::queue<ports::NodeName> pending_broker_clients_; - - // Messages waiting to be relayed by the broker once it's known. - std::unordered_map<ports::NodeName, OutgoingMessageQueue> - pending_relay_messages_; - - // Guards |incoming_messages_| and |incoming_messages_task_posted_|. - base::Lock messages_lock_; - std::queue<ports::ScopedMessage> incoming_messages_; - // Ensures that there is only one incoming messages task posted to the IO - // thread. - bool incoming_messages_task_posted_ = false; - // Flag to fast-path checking |incoming_messages_|. - AtomicFlag incoming_messages_flag_; - - // Guards |shutdown_callback_|. - base::Lock shutdown_lock_; - - // Set by RequestShutdown(). If this is non-null, the controller will - // begin polling the Node to see if clean shutdown is possible any time the - // Node's state is modified by the controller. - base::Closure shutdown_callback_; - // Flag to fast-path checking |shutdown_callback_|. - AtomicFlag shutdown_callback_flag_; - - // All other fields below must only be accessed on the I/O thread, i.e., the - // thread on which core_->io_task_runner() runs tasks. - - // Channels to children during handshake. - NodeMap pending_children_; - - using PeerNodeMap = - std::unordered_map<ports::NodeName, PeerConnection>; - PeerNodeMap peer_connections_; - - // Maps from peer token to node name, pending or not. - std::unordered_map<std::string, ports::NodeName> peers_by_token_; - - // Indicates whether this object should delete itself on IO thread shutdown. - // Must only be accessed from the IO thread. - bool destroy_on_io_thread_shutdown_ = false; - -#if !defined(OS_MACOSX) && !defined(OS_NACL_SFI) - // Broker for sync shared buffer creation in children. - std::unique_ptr<Broker> broker_; -#endif - -#if defined(OS_MACOSX) && !defined(OS_IOS) - base::Lock mach_port_relay_lock_; - // Relay for transferring mach ports to/from children. - std::unique_ptr<MachPortRelay> mach_port_relay_; -#endif - - DISALLOW_COPY_AND_ASSIGN(NodeController); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_NODE_CONTROLLER_H_ diff --git a/mojo/edk/system/options_validation.h b/mojo/edk/system/options_validation.h deleted file mode 100644 index e1b337d..0000000 --- a/mojo/edk/system/options_validation.h +++ /dev/null @@ -1,97 +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/system_impl_export.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { -namespace edk { - -template <class Options> -class UserOptionsReader { - public: - // Constructor from a |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(const Options* options) { - CHECK(options && IsAligned<MOJO_ALIGNOF(Options)>(options)); - options_ = GetSizeForReader(options) == 0 ? nullptr : 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_; } - - const Options& options() const { - DCHECK(is_valid()); - return *options_; - } - - // 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(const Options* options) { - uint32_t struct_size = *reinterpret_cast<const uint32_t*>(options); - if (struct_size < sizeof(uint32_t)) - return 0; - - return std::min(static_cast<size_t>(struct_size), sizeof(Options)); - } - - template <size_t alignment> - static bool IsAligned(const void* pointer) { - return reinterpret_cast<uintptr_t>(pointer) % alignment == 0; - } - - const Options* options_; - - 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 edk -} // 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 a01a92c..0000000 --- a/mojo/edk/system/options_validation_unittest.cc +++ /dev/null @@ -1,134 +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 edk { -namespace { - -// Declare a test options struct just as we do in actual public headers. - -using TestOptionsFlags = uint32_t; - -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(&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(&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(&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(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(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(&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) { -#if defined(OFFICIAL_BUILD) - const char kMemoryCheckFailedRegex[] = ""; -#else - const char kMemoryCheckFailedRegex[] = "Check failed"; -#endif - - // Null: - EXPECT_DEATH_IF_SUPPORTED( - { UserOptionsReader<TestOptions> reader((nullptr)); }, - kMemoryCheckFailedRegex); - - // Unaligned: - EXPECT_DEATH_IF_SUPPORTED( - { - UserOptionsReader<TestOptions> reader( - 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(options); - }, - kMemoryCheckFailedRegex); -} - -} // namespace -} // namespace edk -} // 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 3e708c2..0000000 --- a/mojo/edk/system/platform_handle_dispatcher.cc +++ /dev/null @@ -1,104 +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 "base/synchronization/lock.h" -#include "mojo/edk/embedder/platform_handle_vector.h" - -namespace mojo { -namespace edk { - -// static -scoped_refptr<PlatformHandleDispatcher> PlatformHandleDispatcher::Create( - ScopedPlatformHandle platform_handle) { - return new PlatformHandleDispatcher(std::move(platform_handle)); -} - -ScopedPlatformHandle PlatformHandleDispatcher::PassPlatformHandle() { - return std::move(platform_handle_); -} - -Dispatcher::Type PlatformHandleDispatcher::GetType() const { - return Type::PLATFORM_HANDLE; -} - -MojoResult PlatformHandleDispatcher::Close() { - base::AutoLock lock(lock_); - if (is_closed_ || in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - is_closed_ = true; - platform_handle_.reset(); - return MOJO_RESULT_OK; -} - -void PlatformHandleDispatcher::StartSerialize(uint32_t* num_bytes, - uint32_t* num_ports, - uint32_t* num_handles) { - *num_bytes = 0; - *num_ports = 0; - *num_handles = 1; -} - -bool PlatformHandleDispatcher::EndSerialize(void* destination, - ports::PortName* ports, - PlatformHandle* handles) { - base::AutoLock lock(lock_); - if (is_closed_) - return false; - handles[0] = platform_handle_.get(); - return true; -} - -bool PlatformHandleDispatcher::BeginTransit() { - base::AutoLock lock(lock_); - if (in_transit_) - return false; - in_transit_ = !is_closed_; - return in_transit_; -} - -void PlatformHandleDispatcher::CompleteTransitAndClose() { - base::AutoLock lock(lock_); - - in_transit_ = false; - is_closed_ = true; - - // The system has taken ownership of our handle. - ignore_result(platform_handle_.release()); -} - -void PlatformHandleDispatcher::CancelTransit() { - base::AutoLock lock(lock_); - in_transit_ = false; -} - -// static -scoped_refptr<PlatformHandleDispatcher> PlatformHandleDispatcher::Deserialize( - const void* bytes, - size_t num_bytes, - const ports::PortName* ports, - size_t num_ports, - PlatformHandle* handles, - size_t num_handles) { - if (num_bytes || num_ports || num_handles != 1) - return nullptr; - - PlatformHandle handle; - std::swap(handle, handles[0]); - - return PlatformHandleDispatcher::Create(ScopedPlatformHandle(handle)); -} - -PlatformHandleDispatcher::PlatformHandleDispatcher( - ScopedPlatformHandle platform_handle) - : platform_handle_(std::move(platform_handle)) {} - -PlatformHandleDispatcher::~PlatformHandleDispatcher() { - DCHECK(is_closed_ && !in_transit_); - DCHECK(!platform_handle_.is_valid()); -} - -} // namespace edk -} // 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 a36c7a0..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 "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace edk { - -class MOJO_SYSTEM_IMPL_EXPORT PlatformHandleDispatcher : public Dispatcher { - public: - static scoped_refptr<PlatformHandleDispatcher> Create( - ScopedPlatformHandle platform_handle); - - ScopedPlatformHandle PassPlatformHandle(); - - // Dispatcher: - Type GetType() const override; - MojoResult Close() override; - void StartSerialize(uint32_t* num_bytes, - uint32_t* num_ports, - uint32_t* num_handles) override; - bool EndSerialize(void* destination, - ports::PortName* ports, - PlatformHandle* handles) override; - bool BeginTransit() override; - void CompleteTransitAndClose() override; - void CancelTransit() override; - - static scoped_refptr<PlatformHandleDispatcher> Deserialize( - const void* bytes, - size_t num_bytes, - const ports::PortName* ports, - size_t num_ports, - PlatformHandle* handles, - size_t num_handles); - - private: - PlatformHandleDispatcher(ScopedPlatformHandle platform_handle); - ~PlatformHandleDispatcher() override; - - base::Lock lock_; - bool in_transit_ = false; - bool is_closed_ = false; - ScopedPlatformHandle platform_handle_; - - DISALLOW_COPY_AND_ASSIGN(PlatformHandleDispatcher); -}; - -} // namespace edk -} // 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 7a94262..0000000 --- a/mojo/edk/system/platform_handle_dispatcher_unittest.cc +++ /dev/null @@ -1,123 +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 <utility> - -#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/embedder/platform_handle_vector.h" -#include "mojo/edk/test/test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace edk { -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.GetPath(), &unused)); - ASSERT_TRUE(fp); - EXPECT_EQ(sizeof(kHelloWorld), - fwrite(kHelloWorld, 1, sizeof(kHelloWorld), fp.get())); - - ScopedPlatformHandle h(test::PlatformHandleFromFILE(std::move(fp))); - EXPECT_FALSE(fp); - ASSERT_TRUE(h.is_valid()); - - scoped_refptr<PlatformHandleDispatcher> dispatcher = - PlatformHandleDispatcher::Create(std::move(h)); - EXPECT_FALSE(h.is_valid()); - EXPECT_EQ(Dispatcher::Type::PLATFORM_HANDLE, dispatcher->GetType()); - - h = dispatcher->PassPlatformHandle(); - EXPECT_TRUE(h.is_valid()); - - fp = test::FILEFromPlatformHandle(std::move(h), "rb"); - 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(); - EXPECT_FALSE(h.is_valid()); - - EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close()); -} - -TEST(PlatformHandleDispatcherTest, Serialization) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - static const char kFooBar[] = "foo bar"; - - base::FilePath unused; - base::ScopedFILE fp( - CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &unused)); - EXPECT_EQ(sizeof(kFooBar), fwrite(kFooBar, 1, sizeof(kFooBar), fp.get())); - - scoped_refptr<PlatformHandleDispatcher> dispatcher = - PlatformHandleDispatcher::Create( - test::PlatformHandleFromFILE(std::move(fp))); - - uint32_t num_bytes = 0; - uint32_t num_ports = 0; - uint32_t num_handles = 0; - EXPECT_TRUE(dispatcher->BeginTransit()); - dispatcher->StartSerialize(&num_bytes, &num_ports, &num_handles); - - EXPECT_EQ(0u, num_bytes); - EXPECT_EQ(0u, num_ports); - EXPECT_EQ(1u, num_handles); - - ScopedPlatformHandleVectorPtr handles(new PlatformHandleVector(1)); - EXPECT_TRUE(dispatcher->EndSerialize(nullptr, nullptr, handles->data())); - dispatcher->CompleteTransitAndClose(); - - EXPECT_TRUE(handles->at(0).is_valid()); - - ScopedPlatformHandle handle = dispatcher->PassPlatformHandle(); - EXPECT_FALSE(handle.is_valid()); - - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, dispatcher->Close()); - - dispatcher = static_cast<PlatformHandleDispatcher*>( - Dispatcher::Deserialize(Dispatcher::Type::PLATFORM_HANDLE, nullptr, - num_bytes, nullptr, num_ports, handles->data(), - 1).get()); - - EXPECT_FALSE(handles->at(0).is_valid()); - EXPECT_TRUE(dispatcher->GetType() == Dispatcher::Type::PLATFORM_HANDLE); - - fp = test::FILEFromPlatformHandle(dispatcher->PassPlatformHandle(), "rb"); - 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 edk -} // namespace mojo diff --git a/mojo/edk/system/platform_wrapper_unittest.cc b/mojo/edk/system/platform_wrapper_unittest.cc deleted file mode 100644 index f29d62b..0000000 --- a/mojo/edk/system/platform_wrapper_unittest.cc +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stdint.h> -#include <string.h> - -#include <algorithm> -#include <string> -#include <vector> - -#include "base/files/file.h" -#include "base/files/file_util.h" -#include "base/memory/shared_memory.h" -#include "base/process/process_handle.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/test/mojo_test_base.h" -#include "mojo/public/c/system/platform_handle.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_WIN) -#include <windows.h> -#endif - -#if defined(OS_POSIX) -#define SIMPLE_PLATFORM_HANDLE_TYPE MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR -#elif defined(OS_WIN) -#define SIMPLE_PLATFORM_HANDLE_TYPE MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE -#endif - -#if defined(OS_MACOSX) && !defined(OS_IOS) -#define SHARED_BUFFER_PLATFORM_HANDLE_TYPE MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT -#else -#define SHARED_BUFFER_PLATFORM_HANDLE_TYPE SIMPLE_PLATFORM_HANDLE_TYPE -#endif - -uint64_t PlatformHandleValueFromPlatformFile(base::PlatformFile file) { -#if defined(OS_WIN) - return reinterpret_cast<uint64_t>(file); -#else - return static_cast<uint64_t>(file); -#endif -} - -base::PlatformFile PlatformFileFromPlatformHandleValue(uint64_t value) { -#if defined(OS_WIN) - return reinterpret_cast<base::PlatformFile>(value); -#else - return static_cast<base::PlatformFile>(value); -#endif -} - -namespace mojo { -namespace edk { -namespace { - -using PlatformWrapperTest = test::MojoTestBase; - -TEST_F(PlatformWrapperTest, WrapPlatformHandle) { - // Create a temporary file and write a message to it. - base::FilePath temp_file_path; - ASSERT_TRUE(base::CreateTemporaryFile(&temp_file_path)); - const std::string kMessage = "Hello, world!"; - EXPECT_EQ(base::WriteFile(temp_file_path, kMessage.data(), - static_cast<int>(kMessage.size())), - static_cast<int>(kMessage.size())); - - RUN_CHILD_ON_PIPE(ReadPlatformFile, h) - // Open the temporary file for reading, wrap its handle, and send it to - // the child along with the expected message to be read. - base::File file(temp_file_path, - base::File::FLAG_OPEN | base::File::FLAG_READ); - EXPECT_TRUE(file.IsValid()); - - MojoHandle wrapped_handle; - MojoPlatformHandle os_file; - os_file.struct_size = sizeof(MojoPlatformHandle); - os_file.type = SIMPLE_PLATFORM_HANDLE_TYPE; - os_file.value = - PlatformHandleValueFromPlatformFile(file.TakePlatformFile()); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWrapPlatformHandle(&os_file, &wrapped_handle)); - - WriteMessageWithHandles(h, kMessage, &wrapped_handle, 1); - END_CHILD() - - base::DeleteFile(temp_file_path, false); -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReadPlatformFile, PlatformWrapperTest, h) { - // Read a message and a wrapped file handle; unwrap the handle. - MojoHandle wrapped_handle; - std::string message = ReadMessageWithHandles(h, &wrapped_handle, 1); - - MojoPlatformHandle platform_handle; - platform_handle.struct_size = sizeof(MojoPlatformHandle); - ASSERT_EQ(MOJO_RESULT_OK, - MojoUnwrapPlatformHandle(wrapped_handle, &platform_handle)); - EXPECT_EQ(SIMPLE_PLATFORM_HANDLE_TYPE, platform_handle.type); - base::File file(PlatformFileFromPlatformHandleValue(platform_handle.value)); - - // Expect to read the same message from the file. - std::vector<char> data(message.size()); - EXPECT_EQ(file.ReadAtCurrentPos(data.data(), static_cast<int>(data.size())), - static_cast<int>(data.size())); - EXPECT_TRUE(std::equal(message.begin(), message.end(), data.begin())); -} - -TEST_F(PlatformWrapperTest, WrapPlatformSharedBufferHandle) { - // Allocate a new platform shared buffer and write a message into it. - const std::string kMessage = "Hello, world!"; - base::SharedMemory buffer; - buffer.CreateAndMapAnonymous(kMessage.size()); - CHECK(buffer.memory()); - memcpy(buffer.memory(), kMessage.data(), kMessage.size()); - - RUN_CHILD_ON_PIPE(ReadPlatformSharedBuffer, h) - // Wrap the shared memory handle and send it to the child along with the - // expected message. - base::SharedMemoryHandle memory_handle = - base::SharedMemory::DuplicateHandle(buffer.handle()); - MojoPlatformHandle os_buffer; - os_buffer.struct_size = sizeof(MojoPlatformHandle); - os_buffer.type = SHARED_BUFFER_PLATFORM_HANDLE_TYPE; -#if defined(OS_MACOSX) && !defined(OS_IOS) - os_buffer.value = static_cast<uint64_t>(memory_handle.GetMemoryObject()); -#elif defined(OS_POSIX) - os_buffer.value = static_cast<uint64_t>(memory_handle.fd); -#elif defined(OS_WIN) - os_buffer.value = reinterpret_cast<uint64_t>(memory_handle.GetHandle()); -#endif - - MojoHandle wrapped_handle; - ASSERT_EQ(MOJO_RESULT_OK, - MojoWrapPlatformSharedBufferHandle( - &os_buffer, kMessage.size(), - MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_NONE, - &wrapped_handle)); - WriteMessageWithHandles(h, kMessage, &wrapped_handle, 1); - END_CHILD() -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReadPlatformSharedBuffer, PlatformWrapperTest, - h) { - // Read a message and a wrapped shared buffer handle. - MojoHandle wrapped_handle; - std::string message = ReadMessageWithHandles(h, &wrapped_handle, 1); - - // Check the message in the buffer - ExpectBufferContents(wrapped_handle, 0, message); - - // Now unwrap the buffer and verify that the base::SharedMemoryHandle also - // works as expected. - MojoPlatformHandle os_buffer; - os_buffer.struct_size = sizeof(MojoPlatformHandle); - size_t size; - MojoPlatformSharedBufferHandleFlags flags; - ASSERT_EQ(MOJO_RESULT_OK, - MojoUnwrapPlatformSharedBufferHandle(wrapped_handle, &os_buffer, - &size, &flags)); - bool read_only = flags & MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_NONE; - EXPECT_FALSE(read_only); - -#if defined(OS_MACOSX) && !defined(OS_IOS) - ASSERT_EQ(MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT, os_buffer.type); - base::SharedMemoryHandle memory_handle( - static_cast<mach_port_t>(os_buffer.value), size, - base::GetCurrentProcId()); -#elif defined(OS_POSIX) - ASSERT_EQ(MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR, os_buffer.type); - base::SharedMemoryHandle memory_handle(static_cast<int>(os_buffer.value), - false); -#elif defined(OS_WIN) - ASSERT_EQ(MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE, os_buffer.type); - base::SharedMemoryHandle memory_handle( - reinterpret_cast<HANDLE>(os_buffer.value), base::GetCurrentProcId()); -#endif - - base::SharedMemory memory(memory_handle, read_only); - memory.Map(message.size()); - ASSERT_TRUE(memory.memory()); - - EXPECT_TRUE(std::equal(message.begin(), message.end(), - static_cast<const char*>(memory.memory()))); -} - -TEST_F(PlatformWrapperTest, InvalidHandle) { - // Wrap an invalid platform handle and expect to unwrap the same. - - MojoHandle wrapped_handle; - MojoPlatformHandle invalid_handle; - invalid_handle.struct_size = sizeof(MojoPlatformHandle); - invalid_handle.type = MOJO_PLATFORM_HANDLE_TYPE_INVALID; - EXPECT_EQ(MOJO_RESULT_OK, - MojoWrapPlatformHandle(&invalid_handle, &wrapped_handle)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoUnwrapPlatformHandle(wrapped_handle, &invalid_handle)); - EXPECT_EQ(MOJO_PLATFORM_HANDLE_TYPE_INVALID, invalid_handle.type); -} - -TEST_F(PlatformWrapperTest, InvalidArgument) { - // Try to wrap an invalid MojoPlatformHandle struct and expect an error. - MojoHandle wrapped_handle; - MojoPlatformHandle platform_handle; - platform_handle.struct_size = 0; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoWrapPlatformHandle(&platform_handle, &wrapped_handle)); -} - -} // namespace -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/ports/BUILD.gn b/mojo/edk/system/ports/BUILD.gn deleted file mode 100644 index 5c82761..0000000 --- a/mojo/edk/system/ports/BUILD.gn +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//testing/test.gni") - -source_set("ports") { - sources = [ - "event.cc", - "event.h", - "message.cc", - "message.h", - "message_filter.h", - "message_queue.cc", - "message_queue.h", - "name.cc", - "name.h", - "node.cc", - "node.h", - "node_delegate.h", - "port.cc", - "port.h", - "port_ref.cc", - "port_ref.h", - "user_data.h", - ] - - public_deps = [ - "//base", - ] -} - -source_set("tests") { - testonly = true - - sources = [ - "ports_unittest.cc", - ] - - deps = [ - ":ports", - "//base", - "//base/test:test_support", - "//testing/gtest", - ] -} diff --git a/mojo/edk/system/ports/event.cc b/mojo/edk/system/ports/event.cc deleted file mode 100644 index 2e22086..0000000 --- a/mojo/edk/system/ports/event.cc +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/ports/event.h" - -#include <string.h> - -namespace mojo { -namespace edk { -namespace ports { - -namespace { - -const size_t kPortsMessageAlignment = 8; - -static_assert(sizeof(PortDescriptor) % kPortsMessageAlignment == 0, - "Invalid PortDescriptor size."); - -static_assert(sizeof(EventHeader) % kPortsMessageAlignment == 0, - "Invalid EventHeader size."); - -static_assert(sizeof(UserEventData) % kPortsMessageAlignment == 0, - "Invalid UserEventData size."); - -static_assert(sizeof(ObserveProxyEventData) % kPortsMessageAlignment == 0, - "Invalid ObserveProxyEventData size."); - -static_assert(sizeof(ObserveProxyAckEventData) % kPortsMessageAlignment == 0, - "Invalid ObserveProxyAckEventData size."); - -static_assert(sizeof(ObserveClosureEventData) % kPortsMessageAlignment == 0, - "Invalid ObserveClosureEventData size."); - -static_assert(sizeof(MergePortEventData) % kPortsMessageAlignment == 0, - "Invalid MergePortEventData size."); - -} // namespace - -PortDescriptor::PortDescriptor() { - memset(padding, 0, sizeof(padding)); -} - -} // namespace ports -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/ports/event.h b/mojo/edk/system/ports/event.h deleted file mode 100644 index a66dfc1..0000000 --- a/mojo/edk/system/ports/event.h +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_PORTS_EVENT_H_ -#define MOJO_EDK_SYSTEM_PORTS_EVENT_H_ - -#include <stdint.h> - -#include "mojo/edk/system/ports/message.h" -#include "mojo/edk/system/ports/name.h" - -namespace mojo { -namespace edk { -namespace ports { - -#pragma pack(push, 1) - -// TODO: Add static assertions of alignment. - -struct PortDescriptor { - PortDescriptor(); - - NodeName peer_node_name; - PortName peer_port_name; - NodeName referring_node_name; - PortName referring_port_name; - uint64_t next_sequence_num_to_send; - uint64_t next_sequence_num_to_receive; - uint64_t last_sequence_num_to_receive; - bool peer_closed; - char padding[7]; -}; - -enum struct EventType : uint32_t { - kUser, - kPortAccepted, - kObserveProxy, - kObserveProxyAck, - kObserveClosure, - kMergePort, -}; - -struct EventHeader { - EventType type; - uint32_t padding; - PortName port_name; -}; - -struct UserEventData { - uint64_t sequence_num; - uint32_t num_ports; - uint32_t padding; -}; - -struct ObserveProxyEventData { - NodeName proxy_node_name; - PortName proxy_port_name; - NodeName proxy_to_node_name; - PortName proxy_to_port_name; -}; - -struct ObserveProxyAckEventData { - uint64_t last_sequence_num; -}; - -struct ObserveClosureEventData { - uint64_t last_sequence_num; -}; - -struct MergePortEventData { - PortName new_port_name; - PortDescriptor new_port_descriptor; -}; - -#pragma pack(pop) - -inline const EventHeader* GetEventHeader(const Message& message) { - return static_cast<const EventHeader*>(message.header_bytes()); -} - -inline EventHeader* GetMutableEventHeader(Message* message) { - return static_cast<EventHeader*>(message->mutable_header_bytes()); -} - -template <typename EventData> -inline const EventData* GetEventData(const Message& message) { - return reinterpret_cast<const EventData*>( - reinterpret_cast<const char*>(GetEventHeader(message) + 1)); -} - -template <typename EventData> -inline EventData* GetMutableEventData(Message* message) { - return reinterpret_cast<EventData*>( - reinterpret_cast<char*>(GetMutableEventHeader(message) + 1)); -} - -inline const PortDescriptor* GetPortDescriptors(const UserEventData* event) { - return reinterpret_cast<const PortDescriptor*>( - reinterpret_cast<const char*>(event + 1)); -} - -inline PortDescriptor* GetMutablePortDescriptors(UserEventData* event) { - return reinterpret_cast<PortDescriptor*>(reinterpret_cast<char*>(event + 1)); -} - -} // namespace ports -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_PORTS_EVENT_H_ diff --git a/mojo/edk/system/ports/message.cc b/mojo/edk/system/ports/message.cc deleted file mode 100644 index 5d3c000..0000000 --- a/mojo/edk/system/ports/message.cc +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stdlib.h> - -#include <limits> - -#include "base/logging.h" -#include "mojo/edk/system/ports/event.h" - -namespace mojo { -namespace edk { -namespace ports { - -// static -bool Message::Parse(const void* bytes, - size_t num_bytes, - size_t* num_header_bytes, - size_t* num_payload_bytes, - size_t* num_ports_bytes) { - if (num_bytes < sizeof(EventHeader)) - return false; - const EventHeader* header = static_cast<const EventHeader*>(bytes); - switch (header->type) { - case EventType::kUser: - // See below. - break; - case EventType::kPortAccepted: - *num_header_bytes = sizeof(EventHeader); - break; - case EventType::kObserveProxy: - *num_header_bytes = sizeof(EventHeader) + sizeof(ObserveProxyEventData); - break; - case EventType::kObserveProxyAck: - *num_header_bytes = - sizeof(EventHeader) + sizeof(ObserveProxyAckEventData); - break; - case EventType::kObserveClosure: - *num_header_bytes = sizeof(EventHeader) + sizeof(ObserveClosureEventData); - break; - case EventType::kMergePort: - *num_header_bytes = sizeof(EventHeader) + sizeof(MergePortEventData); - break; - default: - return false; - } - - if (header->type == EventType::kUser) { - if (num_bytes < sizeof(EventHeader) + sizeof(UserEventData)) - return false; - const UserEventData* event_data = - reinterpret_cast<const UserEventData*>( - reinterpret_cast<const char*>(header + 1)); - if (event_data->num_ports > std::numeric_limits<uint16_t>::max()) - return false; - *num_header_bytes = sizeof(EventHeader) + - sizeof(UserEventData) + - event_data->num_ports * sizeof(PortDescriptor); - *num_ports_bytes = event_data->num_ports * sizeof(PortName); - if (num_bytes < *num_header_bytes + *num_ports_bytes) - return false; - *num_payload_bytes = num_bytes - *num_header_bytes - *num_ports_bytes; - } else { - if (*num_header_bytes != num_bytes) - return false; - *num_payload_bytes = 0; - *num_ports_bytes = 0; - } - - return true; -} - -Message::Message(size_t num_payload_bytes, size_t num_ports) - : Message(sizeof(EventHeader) + sizeof(UserEventData) + - num_ports * sizeof(PortDescriptor), - num_payload_bytes, num_ports * sizeof(PortName)) { - num_ports_ = num_ports; -} - -Message::Message(size_t num_header_bytes, - size_t num_payload_bytes, - size_t num_ports_bytes) - : start_(nullptr), - num_header_bytes_(num_header_bytes), - num_ports_bytes_(num_ports_bytes), - num_payload_bytes_(num_payload_bytes) { -} - -void Message::InitializeUserMessageHeader(void* start) { - start_ = static_cast<char*>(start); - memset(start_, 0, num_header_bytes_); - GetMutableEventHeader(this)->type = EventType::kUser; - GetMutableEventData<UserEventData>(this)->num_ports = - static_cast<uint32_t>(num_ports_); -} - -} // namespace ports -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/ports/message.h b/mojo/edk/system/ports/message.h deleted file mode 100644 index 95fa046..0000000 --- a/mojo/edk/system/ports/message.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_PORTS_MESSAGE_H_ -#define MOJO_EDK_SYSTEM_PORTS_MESSAGE_H_ - -#include <stddef.h> - -#include <memory> - -#include "mojo/edk/system/ports/name.h" - -namespace mojo { -namespace edk { -namespace ports { - -// A message consists of a header (array of bytes), payload (array of bytes) -// and an array of ports. The header is used by the Node implementation. -// -// This class is designed to be subclassed, and the subclass is responsible for -// providing the underlying storage. The header size will be aligned, and it -// should be followed in memory by the array of ports and finally the payload. -// -// NOTE: This class does not manage the lifetime of the ports it references. -class Message { - public: - virtual ~Message() {} - - // Inspect the message at |bytes| and return the size of each section. Returns - // |false| if the message is malformed and |true| otherwise. - static bool Parse(const void* bytes, - size_t num_bytes, - size_t* num_header_bytes, - size_t* num_payload_bytes, - size_t* num_ports_bytes); - - void* mutable_header_bytes() { return start_; } - const void* header_bytes() const { return start_; } - size_t num_header_bytes() const { return num_header_bytes_; } - - void* mutable_payload_bytes() { - return start_ + num_header_bytes_ + num_ports_bytes_; - } - const void* payload_bytes() const { - return const_cast<Message*>(this)->mutable_payload_bytes(); - } - size_t num_payload_bytes() const { return num_payload_bytes_; } - - PortName* mutable_ports() { - return reinterpret_cast<PortName*>(start_ + num_header_bytes_); - } - const PortName* ports() const { - return const_cast<Message*>(this)->mutable_ports(); - } - size_t num_ports_bytes() const { return num_ports_bytes_; } - size_t num_ports() const { return num_ports_bytes_ / sizeof(PortName); } - - protected: - // Constructs a new Message base for a user message. - // - // Note: You MUST call InitializeUserMessageHeader() before this Message is - // ready for transmission. - Message(size_t num_payload_bytes, size_t num_ports); - - // Constructs a new Message base for an internal message. Do NOT call - // InitializeUserMessageHeader() when using this constructor. - Message(size_t num_header_bytes, - size_t num_payload_bytes, - size_t num_ports_bytes); - - Message(const Message& other) = delete; - void operator=(const Message& other) = delete; - - // Initializes the header in a newly allocated message buffer to carry a - // user message. - void InitializeUserMessageHeader(void* start); - - // Note: storage is [header][ports][payload]. - char* start_ = nullptr; - size_t num_ports_ = 0; - size_t num_header_bytes_ = 0; - size_t num_ports_bytes_ = 0; - size_t num_payload_bytes_ = 0; -}; - -using ScopedMessage = std::unique_ptr<Message>; - -} // namespace ports -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_PORTS_MESSAGE_H_ diff --git a/mojo/edk/system/ports/message_filter.h b/mojo/edk/system/ports/message_filter.h deleted file mode 100644 index bf8fa21..0000000 --- a/mojo/edk/system/ports/message_filter.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_PORTS_MESSAGE_FILTER_H_ -#define MOJO_EDK_SYSTEM_PORTS_MESSAGE_FILTER_H_ - -namespace mojo { -namespace edk { -namespace ports { - -class Message; - -// An interface which can be implemented to filter port messages according to -// arbitrary policy. -class MessageFilter { - public: - virtual ~MessageFilter() {} - - // Returns true of |message| should be accepted by whomever is applying this - // filter. See MessageQueue::GetNextMessage(), for example. - virtual bool Match(const Message& message) = 0; -}; - -} // namespace ports -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_PORTS_MESSAGE_FILTER_H_ diff --git a/mojo/edk/system/ports/message_queue.cc b/mojo/edk/system/ports/message_queue.cc deleted file mode 100644 index defb1b6..0000000 --- a/mojo/edk/system/ports/message_queue.cc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/ports/message_queue.h" - -#include <algorithm> - -#include "base/logging.h" -#include "mojo/edk/system/ports/event.h" -#include "mojo/edk/system/ports/message_filter.h" - -namespace mojo { -namespace edk { -namespace ports { - -inline uint64_t GetSequenceNum(const ScopedMessage& message) { - return GetEventData<UserEventData>(*message)->sequence_num; -} - -// Used by std::{push,pop}_heap functions -inline bool operator<(const ScopedMessage& a, const ScopedMessage& b) { - return GetSequenceNum(a) > GetSequenceNum(b); -} - -MessageQueue::MessageQueue() : MessageQueue(kInitialSequenceNum) {} - -MessageQueue::MessageQueue(uint64_t next_sequence_num) - : next_sequence_num_(next_sequence_num) { - // The message queue is blocked waiting for a message with sequence number - // equal to |next_sequence_num|. -} - -MessageQueue::~MessageQueue() { -#if DCHECK_IS_ON() - size_t num_leaked_ports = 0; - for (const auto& message : heap_) - num_leaked_ports += message->num_ports(); - DVLOG_IF(1, num_leaked_ports > 0) - << "Leaking " << num_leaked_ports << " ports in unreceived messages"; -#endif -} - -bool MessageQueue::HasNextMessage() const { - return !heap_.empty() && GetSequenceNum(heap_[0]) == next_sequence_num_; -} - -void MessageQueue::GetNextMessage(ScopedMessage* message, - MessageFilter* filter) { - if (!HasNextMessage() || (filter && !filter->Match(*heap_[0].get()))) { - message->reset(); - return; - } - - std::pop_heap(heap_.begin(), heap_.end()); - *message = std::move(heap_.back()); - heap_.pop_back(); - - next_sequence_num_++; -} - -void MessageQueue::AcceptMessage(ScopedMessage message, - bool* has_next_message) { - DCHECK(GetEventHeader(*message)->type == EventType::kUser); - - // TODO: Handle sequence number roll-over. - - heap_.emplace_back(std::move(message)); - std::push_heap(heap_.begin(), heap_.end()); - - if (!signalable_) { - *has_next_message = false; - } else { - *has_next_message = (GetSequenceNum(heap_[0]) == next_sequence_num_); - } -} - -void MessageQueue::GetReferencedPorts(std::deque<PortName>* port_names) { - for (const auto& message : heap_) { - for (size_t i = 0; i < message->num_ports(); ++i) - port_names->push_back(message->ports()[i]); - } -} - -} // namespace ports -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/ports/message_queue.h b/mojo/edk/system/ports/message_queue.h deleted file mode 100644 index d9a47ed..0000000 --- a/mojo/edk/system/ports/message_queue.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_PORTS_MESSAGE_QUEUE_H_ -#define MOJO_EDK_SYSTEM_PORTS_MESSAGE_QUEUE_H_ - -#include <stdint.h> - -#include <deque> -#include <functional> -#include <limits> -#include <vector> - -#include "base/macros.h" -#include "mojo/edk/system/ports/message.h" - -namespace mojo { -namespace edk { -namespace ports { - -const uint64_t kInitialSequenceNum = 1; -const uint64_t kInvalidSequenceNum = std::numeric_limits<uint64_t>::max(); - -class MessageFilter; - -// An incoming message queue for a port. MessageQueue keeps track of the highest -// known sequence number and can indicate whether the next sequential message is -// available. Thus the queue enforces message ordering for the consumer without -// enforcing it for the producer (see AcceptMessage() below.) -class MessageQueue { - public: - explicit MessageQueue(); - explicit MessageQueue(uint64_t next_sequence_num); - ~MessageQueue(); - - void set_signalable(bool value) { signalable_ = value; } - - uint64_t next_sequence_num() const { return next_sequence_num_; } - - bool HasNextMessage() const; - - // Gives ownership of the message. If |filter| is non-null, the next message - // will only be retrieved if the filter successfully matches it. - void GetNextMessage(ScopedMessage* message, MessageFilter* filter); - - // Takes ownership of the message. Note: Messages are ordered, so while we - // have added a message to the queue, we may still be waiting on a message - // ahead of this one before we can let any of the messages be returned by - // GetNextMessage. - // - // Furthermore, once has_next_message is set to true, it will remain false - // until GetNextMessage is called enough times to return a null message. - // In other words, has_next_message acts like an edge trigger. - // - void AcceptMessage(ScopedMessage message, bool* has_next_message); - - // Returns all of the ports referenced by messages in this message queue. - void GetReferencedPorts(std::deque<PortName>* ports); - - private: - std::vector<ScopedMessage> heap_; - uint64_t next_sequence_num_; - bool signalable_ = true; - - DISALLOW_COPY_AND_ASSIGN(MessageQueue); -}; - -} // namespace ports -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_PORTS_MESSAGE_QUEUE_H_ diff --git a/mojo/edk/system/ports/name.cc b/mojo/edk/system/ports/name.cc deleted file mode 100644 index ea17698..0000000 --- a/mojo/edk/system/ports/name.cc +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/ports/name.h" - -namespace mojo { -namespace edk { -namespace ports { - -extern const PortName kInvalidPortName = {0, 0}; - -extern const NodeName kInvalidNodeName = {0, 0}; - -std::ostream& operator<<(std::ostream& stream, const Name& name) { - std::ios::fmtflags flags(stream.flags()); - stream << std::hex << std::uppercase << name.v1; - if (name.v2 != 0) - stream << '.' << name.v2; - stream.flags(flags); - return stream; -} - -} // namespace ports -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/ports/name.h b/mojo/edk/system/ports/name.h deleted file mode 100644 index 72e41b9..0000000 --- a/mojo/edk/system/ports/name.h +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_PORTS_NAME_H_ -#define MOJO_EDK_SYSTEM_PORTS_NAME_H_ - -#include <stdint.h> - -#include <ostream> -#include <tuple> - -#include "base/hash.h" - -namespace mojo { -namespace edk { -namespace ports { - -struct Name { - Name(uint64_t v1, uint64_t v2) : v1(v1), v2(v2) {} - uint64_t v1, v2; -}; - -inline bool operator==(const Name& a, const Name& b) { - return a.v1 == b.v1 && a.v2 == b.v2; -} - -inline bool operator!=(const Name& a, const Name& b) { - return !(a == b); -} - -inline bool operator<(const Name& a, const Name& b) { - return std::tie(a.v1, a.v2) < std::tie(b.v1, b.v2); -} - -std::ostream& operator<<(std::ostream& stream, const Name& name); - -struct PortName : Name { - PortName() : Name(0, 0) {} - PortName(uint64_t v1, uint64_t v2) : Name(v1, v2) {} -}; - -extern const PortName kInvalidPortName; - -struct NodeName : Name { - NodeName() : Name(0, 0) {} - NodeName(uint64_t v1, uint64_t v2) : Name(v1, v2) {} -}; - -extern const NodeName kInvalidNodeName; - -} // namespace ports -} // namespace edk -} // namespace mojo - -namespace std { - -template <> -struct hash<mojo::edk::ports::PortName> { - std::size_t operator()(const mojo::edk::ports::PortName& name) const { - return base::HashInts64(name.v1, name.v2); - } -}; - -template <> -struct hash<mojo::edk::ports::NodeName> { - std::size_t operator()(const mojo::edk::ports::NodeName& name) const { - return base::HashInts64(name.v1, name.v2); - } -}; - -} // namespace std - -#endif // MOJO_EDK_SYSTEM_PORTS_NAME_H_ diff --git a/mojo/edk/system/ports/node.cc b/mojo/edk/system/ports/node.cc deleted file mode 100644 index f9a3feb..0000000 --- a/mojo/edk/system/ports/node.cc +++ /dev/null @@ -1,1385 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/ports/node.h" - -#include <string.h> - -#include <utility> - -#include "base/atomicops.h" -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/system/ports/node_delegate.h" - -namespace mojo { -namespace edk { -namespace ports { - -namespace { - -int DebugError(const char* message, int error_code) { - CHECK(false) << "Oops: " << message; - return error_code; -} - -#define OOPS(x) DebugError(#x, x) - -bool CanAcceptMoreMessages(const Port* port) { - // Have we already doled out the last message (i.e., do we expect to NOT - // receive further messages)? - uint64_t next_sequence_num = port->message_queue.next_sequence_num(); - if (port->state == Port::kClosed) - return false; - if (port->peer_closed || port->remove_proxy_on_last_message) { - if (port->last_sequence_num_to_receive == next_sequence_num - 1) - return false; - } - return true; -} - -} // namespace - -class Node::LockedPort { - public: - explicit LockedPort(Port* port) : port_(port) { - port_->lock.AssertAcquired(); - } - - Port* get() const { return port_; } - Port* operator->() const { return port_; } - - private: - Port* const port_; -}; - -Node::Node(const NodeName& name, NodeDelegate* delegate) - : name_(name), - delegate_(delegate) { -} - -Node::~Node() { - if (!ports_.empty()) - DLOG(WARNING) << "Unclean shutdown for node " << name_; -} - -bool Node::CanShutdownCleanly(ShutdownPolicy policy) { - base::AutoLock ports_lock(ports_lock_); - - if (policy == ShutdownPolicy::DONT_ALLOW_LOCAL_PORTS) { -#if DCHECK_IS_ON() - for (auto entry : ports_) { - DVLOG(2) << "Port " << entry.first << " referencing node " - << entry.second->peer_node_name << " is blocking shutdown of " - << "node " << name_ << " (state=" << entry.second->state << ")"; - } -#endif - return ports_.empty(); - } - - DCHECK_EQ(policy, ShutdownPolicy::ALLOW_LOCAL_PORTS); - - // NOTE: This is not efficient, though it probably doesn't need to be since - // relatively few ports should be open during shutdown and shutdown doesn't - // need to be blazingly fast. - bool can_shutdown = true; - for (auto entry : ports_) { - base::AutoLock lock(entry.second->lock); - if (entry.second->peer_node_name != name_ && - entry.second->state != Port::kReceiving) { - can_shutdown = false; -#if DCHECK_IS_ON() - DVLOG(2) << "Port " << entry.first << " referencing node " - << entry.second->peer_node_name << " is blocking shutdown of " - << "node " << name_ << " (state=" << entry.second->state << ")"; -#else - // Exit early when not debugging. - break; -#endif - } - } - - return can_shutdown; -} - -int Node::GetPort(const PortName& port_name, PortRef* port_ref) { - scoped_refptr<Port> port = GetPort(port_name); - if (!port) - return ERROR_PORT_UNKNOWN; - - *port_ref = PortRef(port_name, std::move(port)); - return OK; -} - -int Node::CreateUninitializedPort(PortRef* port_ref) { - PortName port_name; - delegate_->GenerateRandomPortName(&port_name); - - scoped_refptr<Port> port(new Port(kInitialSequenceNum, kInitialSequenceNum)); - int rv = AddPortWithName(port_name, port); - if (rv != OK) - return rv; - - *port_ref = PortRef(port_name, std::move(port)); - return OK; -} - -int Node::InitializePort(const PortRef& port_ref, - const NodeName& peer_node_name, - const PortName& peer_port_name) { - Port* port = port_ref.port(); - - { - base::AutoLock lock(port->lock); - if (port->state != Port::kUninitialized) - return ERROR_PORT_STATE_UNEXPECTED; - - port->state = Port::kReceiving; - port->peer_node_name = peer_node_name; - port->peer_port_name = peer_port_name; - } - - delegate_->PortStatusChanged(port_ref); - - return OK; -} - -int Node::CreatePortPair(PortRef* port0_ref, PortRef* port1_ref) { - int rv; - - rv = CreateUninitializedPort(port0_ref); - if (rv != OK) - return rv; - - rv = CreateUninitializedPort(port1_ref); - if (rv != OK) - return rv; - - rv = InitializePort(*port0_ref, name_, port1_ref->name()); - if (rv != OK) - return rv; - - rv = InitializePort(*port1_ref, name_, port0_ref->name()); - if (rv != OK) - return rv; - - return OK; -} - -int Node::SetUserData(const PortRef& port_ref, - scoped_refptr<UserData> user_data) { - Port* port = port_ref.port(); - - base::AutoLock lock(port->lock); - if (port->state == Port::kClosed) - return ERROR_PORT_STATE_UNEXPECTED; - - port->user_data = std::move(user_data); - - return OK; -} - -int Node::GetUserData(const PortRef& port_ref, - scoped_refptr<UserData>* user_data) { - Port* port = port_ref.port(); - - base::AutoLock lock(port->lock); - if (port->state == Port::kClosed) - return ERROR_PORT_STATE_UNEXPECTED; - - *user_data = port->user_data; - - return OK; -} - -int Node::ClosePort(const PortRef& port_ref) { - std::deque<PortName> referenced_port_names; - - ObserveClosureEventData data; - - NodeName peer_node_name; - PortName peer_port_name; - Port* port = port_ref.port(); - { - // We may need to erase the port, which requires ports_lock_ to be held, - // but ports_lock_ must be acquired before any individual port locks. - base::AutoLock ports_lock(ports_lock_); - - base::AutoLock lock(port->lock); - if (port->state == Port::kUninitialized) { - // If the port was not yet initialized, there's nothing interesting to do. - ErasePort_Locked(port_ref.name()); - return OK; - } - - if (port->state != Port::kReceiving) - return ERROR_PORT_STATE_UNEXPECTED; - - port->state = Port::kClosed; - - // We pass along the sequence number of the last message sent from this - // port to allow the peer to have the opportunity to consume all inbound - // messages before notifying the embedder that this port is closed. - data.last_sequence_num = port->next_sequence_num_to_send - 1; - - peer_node_name = port->peer_node_name; - peer_port_name = port->peer_port_name; - - // If the port being closed still has unread messages, then we need to take - // care to close those ports so as to avoid leaking memory. - port->message_queue.GetReferencedPorts(&referenced_port_names); - - ErasePort_Locked(port_ref.name()); - } - - DVLOG(2) << "Sending ObserveClosure from " << port_ref.name() << "@" << name_ - << " to " << peer_port_name << "@" << peer_node_name; - - delegate_->ForwardMessage( - peer_node_name, - NewInternalMessage(peer_port_name, EventType::kObserveClosure, data)); - - for (const auto& name : referenced_port_names) { - PortRef ref; - if (GetPort(name, &ref) == OK) - ClosePort(ref); - } - return OK; -} - -int Node::GetStatus(const PortRef& port_ref, PortStatus* port_status) { - Port* port = port_ref.port(); - - base::AutoLock lock(port->lock); - - if (port->state != Port::kReceiving) - return ERROR_PORT_STATE_UNEXPECTED; - - port_status->has_messages = port->message_queue.HasNextMessage(); - port_status->receiving_messages = CanAcceptMoreMessages(port); - port_status->peer_closed = port->peer_closed; - return OK; -} - -int Node::GetMessage(const PortRef& port_ref, - ScopedMessage* message, - MessageFilter* filter) { - *message = nullptr; - - DVLOG(4) << "GetMessage for " << port_ref.name() << "@" << name_; - - Port* port = port_ref.port(); - { - base::AutoLock lock(port->lock); - - // This could also be treated like the port being unknown since the - // embedder should no longer be referring to a port that has been sent. - if (port->state != Port::kReceiving) - return ERROR_PORT_STATE_UNEXPECTED; - - // Let the embedder get messages until there are no more before reporting - // that the peer closed its end. - if (!CanAcceptMoreMessages(port)) - return ERROR_PORT_PEER_CLOSED; - - port->message_queue.GetNextMessage(message, filter); - } - - // Allow referenced ports to trigger PortStatusChanged calls. - if (*message) { - for (size_t i = 0; i < (*message)->num_ports(); ++i) { - const PortName& new_port_name = (*message)->ports()[i]; - scoped_refptr<Port> new_port = GetPort(new_port_name); - - DCHECK(new_port) << "Port " << new_port_name << "@" << name_ - << " does not exist!"; - - base::AutoLock lock(new_port->lock); - - DCHECK(new_port->state == Port::kReceiving); - new_port->message_queue.set_signalable(true); - } - } - - return OK; -} - -int Node::SendMessage(const PortRef& port_ref, ScopedMessage message) { - int rv = SendMessageInternal(port_ref, &message); - if (rv != OK) { - // If send failed, close all carried ports. Note that we're careful not to - // close the sending port itself if it happened to be one of the encoded - // ports (an invalid but possible condition.) - for (size_t i = 0; i < message->num_ports(); ++i) { - if (message->ports()[i] == port_ref.name()) - continue; - - PortRef port; - if (GetPort(message->ports()[i], &port) == OK) - ClosePort(port); - } - } - return rv; -} - -int Node::AcceptMessage(ScopedMessage message) { - const EventHeader* header = GetEventHeader(*message); - switch (header->type) { - case EventType::kUser: - return OnUserMessage(std::move(message)); - case EventType::kPortAccepted: - return OnPortAccepted(header->port_name); - case EventType::kObserveProxy: - return OnObserveProxy( - header->port_name, - *GetEventData<ObserveProxyEventData>(*message)); - case EventType::kObserveProxyAck: - return OnObserveProxyAck( - header->port_name, - GetEventData<ObserveProxyAckEventData>(*message)->last_sequence_num); - case EventType::kObserveClosure: - return OnObserveClosure( - header->port_name, - GetEventData<ObserveClosureEventData>(*message)->last_sequence_num); - case EventType::kMergePort: - return OnMergePort(header->port_name, - *GetEventData<MergePortEventData>(*message)); - } - return OOPS(ERROR_NOT_IMPLEMENTED); -} - -int Node::MergePorts(const PortRef& port_ref, - const NodeName& destination_node_name, - const PortName& destination_port_name) { - Port* port = port_ref.port(); - MergePortEventData data; - { - base::AutoLock lock(port->lock); - - DVLOG(1) << "Sending MergePort from " << port_ref.name() << "@" << name_ - << " to " << destination_port_name << "@" << destination_node_name; - - // Send the port-to-merge over to the destination node so it can be merged - // into the port cycle atomically there. - data.new_port_name = port_ref.name(); - WillSendPort(LockedPort(port), destination_node_name, &data.new_port_name, - &data.new_port_descriptor); - } - delegate_->ForwardMessage( - destination_node_name, - NewInternalMessage(destination_port_name, - EventType::kMergePort, data)); - return OK; -} - -int Node::MergeLocalPorts(const PortRef& port0_ref, const PortRef& port1_ref) { - Port* port0 = port0_ref.port(); - Port* port1 = port1_ref.port(); - int rv; - { - // |ports_lock_| must be held when acquiring overlapping port locks. - base::AutoLock ports_lock(ports_lock_); - base::AutoLock port0_lock(port0->lock); - base::AutoLock port1_lock(port1->lock); - - DVLOG(1) << "Merging local ports " << port0_ref.name() << "@" << name_ - << " and " << port1_ref.name() << "@" << name_; - - if (port0->state != Port::kReceiving || port1->state != Port::kReceiving) - rv = ERROR_PORT_STATE_UNEXPECTED; - else - rv = MergePorts_Locked(port0_ref, port1_ref); - } - - if (rv != OK) { - ClosePort(port0_ref); - ClosePort(port1_ref); - } - - return rv; -} - -int Node::LostConnectionToNode(const NodeName& node_name) { - // We can no longer send events to the given node. We also can't expect any - // PortAccepted events. - - DVLOG(1) << "Observing lost connection from node " << name_ - << " to node " << node_name; - - DestroyAllPortsWithPeer(node_name, kInvalidPortName); - return OK; -} - -int Node::OnUserMessage(ScopedMessage message) { - PortName port_name = GetEventHeader(*message)->port_name; - const auto* event = GetEventData<UserEventData>(*message); - -#if DCHECK_IS_ON() - std::ostringstream ports_buf; - for (size_t i = 0; i < message->num_ports(); ++i) { - if (i > 0) - ports_buf << ","; - ports_buf << message->ports()[i]; - } - - DVLOG(4) << "AcceptMessage " << event->sequence_num - << " [ports=" << ports_buf.str() << "] at " - << port_name << "@" << name_; -#endif - - scoped_refptr<Port> port = GetPort(port_name); - - // Even if this port does not exist, cannot receive anymore messages or is - // buffering or proxying messages, we still need these ports to be bound to - // this node. When the message is forwarded, these ports will get transferred - // following the usual method. If the message cannot be accepted, then the - // newly bound ports will simply be closed. - - for (size_t i = 0; i < message->num_ports(); ++i) { - int rv = AcceptPort(message->ports()[i], GetPortDescriptors(event)[i]); - if (rv != OK) - return rv; - } - - bool has_next_message = false; - bool message_accepted = false; - - if (port) { - // We may want to forward messages once the port lock is held, so we must - // acquire |ports_lock_| first. - base::AutoLock ports_lock(ports_lock_); - base::AutoLock lock(port->lock); - - // Reject spurious messages if we've already received the last expected - // message. - if (CanAcceptMoreMessages(port.get())) { - message_accepted = true; - port->message_queue.AcceptMessage(std::move(message), &has_next_message); - - if (port->state == Port::kBuffering) { - has_next_message = false; - } else if (port->state == Port::kProxying) { - has_next_message = false; - - // Forward messages. We forward messages in sequential order here so - // that we maintain the message queue's notion of next sequence number. - // That's useful for the proxy removal process as we can tell when this - // port has seen all of the messages it is expected to see. - int rv = ForwardMessages_Locked(LockedPort(port.get()), port_name); - if (rv != OK) - return rv; - - MaybeRemoveProxy_Locked(LockedPort(port.get()), port_name); - } - } - } - - if (!message_accepted) { - DVLOG(2) << "Message not accepted!\n"; - // Close all newly accepted ports as they are effectively orphaned. - for (size_t i = 0; i < message->num_ports(); ++i) { - PortRef port_ref; - if (GetPort(message->ports()[i], &port_ref) == OK) { - ClosePort(port_ref); - } else { - DLOG(WARNING) << "Cannot close non-existent port!\n"; - } - } - } else if (has_next_message) { - PortRef port_ref(port_name, port); - delegate_->PortStatusChanged(port_ref); - } - - return OK; -} - -int Node::OnPortAccepted(const PortName& port_name) { - scoped_refptr<Port> port = GetPort(port_name); - if (!port) - return ERROR_PORT_UNKNOWN; - - DVLOG(2) << "PortAccepted at " << port_name << "@" << name_ - << " pointing to " - << port->peer_port_name << "@" << port->peer_node_name; - - return BeginProxying(PortRef(port_name, std::move(port))); -} - -int Node::OnObserveProxy(const PortName& port_name, - const ObserveProxyEventData& event) { - if (port_name == kInvalidPortName) { - // An ObserveProxy with an invalid target port name is a broadcast used to - // inform ports when their peer (which was itself a proxy) has become - // defunct due to unexpected node disconnection. - // - // Receiving ports affected by this treat it as equivalent to peer closure. - // Proxies affected by this can be removed and will in turn broadcast their - // own death with a similar message. - CHECK_EQ(event.proxy_to_node_name, kInvalidNodeName); - CHECK_EQ(event.proxy_to_port_name, kInvalidPortName); - DestroyAllPortsWithPeer(event.proxy_node_name, event.proxy_port_name); - return OK; - } - - // The port may have already been closed locally, in which case the - // ObserveClosure message will contain the last_sequence_num field. - // We can then silently ignore this message. - scoped_refptr<Port> port = GetPort(port_name); - if (!port) { - DVLOG(1) << "ObserveProxy: " << port_name << "@" << name_ << " not found"; - return OK; - } - - DVLOG(2) << "ObserveProxy at " << port_name << "@" << name_ << ", proxy at " - << event.proxy_port_name << "@" - << event.proxy_node_name << " pointing to " - << event.proxy_to_port_name << "@" - << event.proxy_to_node_name; - - { - base::AutoLock lock(port->lock); - - if (port->peer_node_name == event.proxy_node_name && - port->peer_port_name == event.proxy_port_name) { - if (port->state == Port::kReceiving) { - port->peer_node_name = event.proxy_to_node_name; - port->peer_port_name = event.proxy_to_port_name; - - ObserveProxyAckEventData ack; - ack.last_sequence_num = port->next_sequence_num_to_send - 1; - - delegate_->ForwardMessage( - event.proxy_node_name, - NewInternalMessage(event.proxy_port_name, - EventType::kObserveProxyAck, - ack)); - } else { - // As a proxy ourselves, we don't know how to honor the ObserveProxy - // event or to populate the last_sequence_num field of ObserveProxyAck. - // Afterall, another port could be sending messages to our peer now - // that we've sent out our own ObserveProxy event. Instead, we will - // send an ObserveProxyAck indicating that the ObserveProxy event - // should be re-sent (last_sequence_num set to kInvalidSequenceNum). - // However, this has to be done after we are removed as a proxy. - // Otherwise, we might just find ourselves back here again, which - // would be akin to a busy loop. - - DVLOG(2) << "Delaying ObserveProxyAck to " - << event.proxy_port_name << "@" << event.proxy_node_name; - - ObserveProxyAckEventData ack; - ack.last_sequence_num = kInvalidSequenceNum; - - port->send_on_proxy_removal.reset( - new std::pair<NodeName, ScopedMessage>( - event.proxy_node_name, - NewInternalMessage(event.proxy_port_name, - EventType::kObserveProxyAck, - ack))); - } - } else { - // Forward this event along to our peer. Eventually, it should find the - // port referring to the proxy. - delegate_->ForwardMessage( - port->peer_node_name, - NewInternalMessage(port->peer_port_name, - EventType::kObserveProxy, - event)); - } - } - return OK; -} - -int Node::OnObserveProxyAck(const PortName& port_name, - uint64_t last_sequence_num) { - DVLOG(2) << "ObserveProxyAck at " << port_name << "@" << name_ - << " (last_sequence_num=" << last_sequence_num << ")"; - - scoped_refptr<Port> port = GetPort(port_name); - if (!port) - return ERROR_PORT_UNKNOWN; // The port may have observed closure first, so - // this is not an "Oops". - - { - base::AutoLock lock(port->lock); - - if (port->state != Port::kProxying) - return OOPS(ERROR_PORT_STATE_UNEXPECTED); - - if (last_sequence_num == kInvalidSequenceNum) { - // Send again. - InitiateProxyRemoval(LockedPort(port.get()), port_name); - return OK; - } - - // We can now remove this port once we have received and forwarded the last - // message addressed to this port. - port->remove_proxy_on_last_message = true; - port->last_sequence_num_to_receive = last_sequence_num; - } - TryRemoveProxy(PortRef(port_name, std::move(port))); - return OK; -} - -int Node::OnObserveClosure(const PortName& port_name, - uint64_t last_sequence_num) { - // OK if the port doesn't exist, as it may have been closed already. - scoped_refptr<Port> port = GetPort(port_name); - if (!port) - return OK; - - // This message tells the port that it should no longer expect more messages - // beyond last_sequence_num. This message is forwarded along until we reach - // the receiving end, and this message serves as an equivalent to - // ObserveProxyAck. - - bool notify_delegate = false; - ObserveClosureEventData forwarded_data; - NodeName peer_node_name; - PortName peer_port_name; - bool try_remove_proxy = false; - { - base::AutoLock lock(port->lock); - - port->peer_closed = true; - port->last_sequence_num_to_receive = last_sequence_num; - - DVLOG(2) << "ObserveClosure at " << port_name << "@" << name_ - << " (state=" << port->state << ") pointing to " - << port->peer_port_name << "@" << port->peer_node_name - << " (last_sequence_num=" << last_sequence_num << ")"; - - // We always forward ObserveClosure, even beyond the receiving port which - // cares about it. This ensures that any dead-end proxies beyond that port - // are notified to remove themselves. - - if (port->state == Port::kReceiving) { - notify_delegate = true; - - // When forwarding along the other half of the port cycle, this will only - // reach dead-end proxies. Tell them we've sent our last message so they - // can go away. - // - // TODO: Repurposing ObserveClosure for this has the desired result but - // may be semantically confusing since the forwarding port is not actually - // closed. Consider replacing this with a new event type. - forwarded_data.last_sequence_num = port->next_sequence_num_to_send - 1; - } else { - // We haven't yet reached the receiving peer of the closed port, so - // forward the message along as-is. - forwarded_data.last_sequence_num = last_sequence_num; - - // See about removing the port if it is a proxy as our peer won't be able - // to participate in proxy removal. - port->remove_proxy_on_last_message = true; - if (port->state == Port::kProxying) - try_remove_proxy = true; - } - - DVLOG(2) << "Forwarding ObserveClosure from " - << port_name << "@" << name_ << " to peer " - << port->peer_port_name << "@" << port->peer_node_name - << " (last_sequence_num=" << forwarded_data.last_sequence_num - << ")"; - - peer_node_name = port->peer_node_name; - peer_port_name = port->peer_port_name; - } - if (try_remove_proxy) - TryRemoveProxy(PortRef(port_name, port)); - - delegate_->ForwardMessage( - peer_node_name, - NewInternalMessage(peer_port_name, EventType::kObserveClosure, - forwarded_data)); - - if (notify_delegate) { - PortRef port_ref(port_name, std::move(port)); - delegate_->PortStatusChanged(port_ref); - } - return OK; -} - -int Node::OnMergePort(const PortName& port_name, - const MergePortEventData& event) { - scoped_refptr<Port> port = GetPort(port_name); - - DVLOG(1) << "MergePort at " << port_name << "@" << name_ << " (state=" - << (port ? port->state : -1) << ") merging with proxy " - << event.new_port_name - << "@" << name_ << " pointing to " - << event.new_port_descriptor.peer_port_name << "@" - << event.new_port_descriptor.peer_node_name << " referred by " - << event.new_port_descriptor.referring_port_name << "@" - << event.new_port_descriptor.referring_node_name; - - bool close_target_port = false; - bool close_new_port = false; - - // Accept the new port. This is now the receiving end of the other port cycle - // to be merged with ours. - int rv = AcceptPort(event.new_port_name, event.new_port_descriptor); - if (rv != OK) { - close_target_port = true; - } else if (port) { - // BeginProxying_Locked may call MaybeRemoveProxy_Locked, which in turn - // needs to hold |ports_lock_|. We also acquire multiple port locks within. - base::AutoLock ports_lock(ports_lock_); - base::AutoLock lock(port->lock); - - if (port->state != Port::kReceiving) { - close_new_port = true; - } else { - scoped_refptr<Port> new_port = GetPort_Locked(event.new_port_name); - base::AutoLock new_port_lock(new_port->lock); - DCHECK(new_port->state == Port::kReceiving); - - // Both ports are locked. Now all we have to do is swap their peer - // information and set them up as proxies. - - PortRef port0_ref(port_name, port); - PortRef port1_ref(event.new_port_name, new_port); - int rv = MergePorts_Locked(port0_ref, port1_ref); - if (rv == OK) - return rv; - - close_new_port = true; - close_target_port = true; - } - } else { - close_new_port = true; - } - - if (close_target_port) { - PortRef target_port; - rv = GetPort(port_name, &target_port); - DCHECK(rv == OK); - - ClosePort(target_port); - } - - if (close_new_port) { - PortRef new_port; - rv = GetPort(event.new_port_name, &new_port); - DCHECK(rv == OK); - - ClosePort(new_port); - } - - return ERROR_PORT_STATE_UNEXPECTED; -} - -int Node::AddPortWithName(const PortName& port_name, scoped_refptr<Port> port) { - base::AutoLock lock(ports_lock_); - - if (!ports_.insert(std::make_pair(port_name, std::move(port))).second) - return OOPS(ERROR_PORT_EXISTS); // Suggests a bad UUID generator. - - DVLOG(2) << "Created port " << port_name << "@" << name_; - return OK; -} - -void Node::ErasePort(const PortName& port_name) { - base::AutoLock lock(ports_lock_); - ErasePort_Locked(port_name); -} - -void Node::ErasePort_Locked(const PortName& port_name) { - ports_lock_.AssertAcquired(); - ports_.erase(port_name); - DVLOG(2) << "Deleted port " << port_name << "@" << name_; -} - -scoped_refptr<Port> Node::GetPort(const PortName& port_name) { - base::AutoLock lock(ports_lock_); - return GetPort_Locked(port_name); -} - -scoped_refptr<Port> Node::GetPort_Locked(const PortName& port_name) { - ports_lock_.AssertAcquired(); - auto iter = ports_.find(port_name); - if (iter == ports_.end()) - return nullptr; - -#if (defined(OS_ANDROID) || defined(__ANDROID__)) && defined(ARCH_CPU_ARM64) - // Workaround for https://crbug.com/665869. - base::subtle::MemoryBarrier(); -#endif - - return iter->second; -} - -int Node::SendMessageInternal(const PortRef& port_ref, ScopedMessage* message) { - ScopedMessage& m = *message; - for (size_t i = 0; i < m->num_ports(); ++i) { - if (m->ports()[i] == port_ref.name()) - return ERROR_PORT_CANNOT_SEND_SELF; - } - - Port* port = port_ref.port(); - NodeName peer_node_name; - { - // We must acquire |ports_lock_| before grabbing any port locks, because - // WillSendMessage_Locked may need to lock multiple ports out of order. - base::AutoLock ports_lock(ports_lock_); - base::AutoLock lock(port->lock); - - if (port->state != Port::kReceiving) - return ERROR_PORT_STATE_UNEXPECTED; - - if (port->peer_closed) - return ERROR_PORT_PEER_CLOSED; - - int rv = WillSendMessage_Locked(LockedPort(port), port_ref.name(), m.get()); - if (rv != OK) - return rv; - - // Beyond this point there's no sense in returning anything but OK. Even if - // message forwarding or acceptance fails, there's nothing the embedder can - // do to recover. Assume that failure beyond this point must be treated as a - // transport failure. - - peer_node_name = port->peer_node_name; - } - - if (peer_node_name != name_) { - delegate_->ForwardMessage(peer_node_name, std::move(m)); - return OK; - } - - int rv = AcceptMessage(std::move(m)); - if (rv != OK) { - // See comment above for why we don't return an error in this case. - DVLOG(2) << "AcceptMessage failed: " << rv; - } - - return OK; -} - -int Node::MergePorts_Locked(const PortRef& port0_ref, - const PortRef& port1_ref) { - Port* port0 = port0_ref.port(); - Port* port1 = port1_ref.port(); - - ports_lock_.AssertAcquired(); - port0->lock.AssertAcquired(); - port1->lock.AssertAcquired(); - - CHECK(port0->state == Port::kReceiving); - CHECK(port1->state == Port::kReceiving); - - // Ports cannot be merged with their own receiving peer! - if (port0->peer_node_name == name_ && - port0->peer_port_name == port1_ref.name()) - return ERROR_PORT_STATE_UNEXPECTED; - - if (port1->peer_node_name == name_ && - port1->peer_port_name == port0_ref.name()) - return ERROR_PORT_STATE_UNEXPECTED; - - // Only merge if both ports have never sent a message. - if (port0->next_sequence_num_to_send == kInitialSequenceNum && - port1->next_sequence_num_to_send == kInitialSequenceNum) { - // Swap the ports' peer information and switch them both into buffering - // (eventually proxying) mode. - - std::swap(port0->peer_node_name, port1->peer_node_name); - std::swap(port0->peer_port_name, port1->peer_port_name); - - port0->state = Port::kBuffering; - if (port0->peer_closed) - port0->remove_proxy_on_last_message = true; - - port1->state = Port::kBuffering; - if (port1->peer_closed) - port1->remove_proxy_on_last_message = true; - - int rv1 = BeginProxying_Locked(LockedPort(port0), port0_ref.name()); - int rv2 = BeginProxying_Locked(LockedPort(port1), port1_ref.name()); - - if (rv1 == OK && rv2 == OK) { - // If either merged port had a closed peer, its new peer needs to be - // informed of this. - if (port1->peer_closed) { - ObserveClosureEventData data; - data.last_sequence_num = port0->last_sequence_num_to_receive; - delegate_->ForwardMessage( - port0->peer_node_name, - NewInternalMessage(port0->peer_port_name, - EventType::kObserveClosure, data)); - } - - if (port0->peer_closed) { - ObserveClosureEventData data; - data.last_sequence_num = port1->last_sequence_num_to_receive; - delegate_->ForwardMessage( - port1->peer_node_name, - NewInternalMessage(port1->peer_port_name, - EventType::kObserveClosure, data)); - } - - return OK; - } - - // If either proxy failed to initialize (e.g. had undeliverable messages - // or ended up in a bad state somehow), we keep the system in a consistent - // state by undoing the peer swap. - std::swap(port0->peer_node_name, port1->peer_node_name); - std::swap(port0->peer_port_name, port1->peer_port_name); - port0->remove_proxy_on_last_message = false; - port1->remove_proxy_on_last_message = false; - port0->state = Port::kReceiving; - port1->state = Port::kReceiving; - } - - return ERROR_PORT_STATE_UNEXPECTED; -} - -void Node::WillSendPort(const LockedPort& port, - const NodeName& to_node_name, - PortName* port_name, - PortDescriptor* port_descriptor) { - port->lock.AssertAcquired(); - - PortName local_port_name = *port_name; - - PortName new_port_name; - delegate_->GenerateRandomPortName(&new_port_name); - - // Make sure we don't send messages to the new peer until after we know it - // exists. In the meantime, just buffer messages locally. - DCHECK(port->state == Port::kReceiving); - port->state = Port::kBuffering; - - // If we already know our peer is closed, we already know this proxy can - // be removed once it receives and forwards its last expected message. - if (port->peer_closed) - port->remove_proxy_on_last_message = true; - - *port_name = new_port_name; - - port_descriptor->peer_node_name = port->peer_node_name; - port_descriptor->peer_port_name = port->peer_port_name; - port_descriptor->referring_node_name = name_; - port_descriptor->referring_port_name = local_port_name; - port_descriptor->next_sequence_num_to_send = port->next_sequence_num_to_send; - port_descriptor->next_sequence_num_to_receive = - port->message_queue.next_sequence_num(); - port_descriptor->last_sequence_num_to_receive = - port->last_sequence_num_to_receive; - port_descriptor->peer_closed = port->peer_closed; - memset(port_descriptor->padding, 0, sizeof(port_descriptor->padding)); - - // Configure the local port to point to the new port. - port->peer_node_name = to_node_name; - port->peer_port_name = new_port_name; -} - -int Node::AcceptPort(const PortName& port_name, - const PortDescriptor& port_descriptor) { - scoped_refptr<Port> port = make_scoped_refptr( - new Port(port_descriptor.next_sequence_num_to_send, - port_descriptor.next_sequence_num_to_receive)); - port->state = Port::kReceiving; - port->peer_node_name = port_descriptor.peer_node_name; - port->peer_port_name = port_descriptor.peer_port_name; - port->last_sequence_num_to_receive = - port_descriptor.last_sequence_num_to_receive; - port->peer_closed = port_descriptor.peer_closed; - - DVLOG(2) << "Accepting port " << port_name << " [peer_closed=" - << port->peer_closed << "; last_sequence_num_to_receive=" - << port->last_sequence_num_to_receive << "]"; - - // A newly accepted port is not signalable until the message referencing the - // new port finds its way to the consumer (see GetMessage). - port->message_queue.set_signalable(false); - - int rv = AddPortWithName(port_name, std::move(port)); - if (rv != OK) - return rv; - - // Allow referring port to forward messages. - delegate_->ForwardMessage( - port_descriptor.referring_node_name, - NewInternalMessage(port_descriptor.referring_port_name, - EventType::kPortAccepted)); - return OK; -} - -int Node::WillSendMessage_Locked(const LockedPort& port, - const PortName& port_name, - Message* message) { - ports_lock_.AssertAcquired(); - port->lock.AssertAcquired(); - - DCHECK(message); - - // Messages may already have a sequence number if they're being forwarded - // by a proxy. Otherwise, use the next outgoing sequence number. - uint64_t* sequence_num = - &GetMutableEventData<UserEventData>(message)->sequence_num; - if (*sequence_num == 0) - *sequence_num = port->next_sequence_num_to_send++; - -#if DCHECK_IS_ON() - std::ostringstream ports_buf; - for (size_t i = 0; i < message->num_ports(); ++i) { - if (i > 0) - ports_buf << ","; - ports_buf << message->ports()[i]; - } -#endif - - if (message->num_ports() > 0) { - // Note: Another thread could be trying to send the same ports, so we need - // to ensure that they are ours to send before we mutate their state. - - std::vector<scoped_refptr<Port>> ports; - ports.resize(message->num_ports()); - - { - for (size_t i = 0; i < message->num_ports(); ++i) { - ports[i] = GetPort_Locked(message->ports()[i]); - DCHECK(ports[i]); - - ports[i]->lock.Acquire(); - int error = OK; - if (ports[i]->state != Port::kReceiving) - error = ERROR_PORT_STATE_UNEXPECTED; - else if (message->ports()[i] == port->peer_port_name) - error = ERROR_PORT_CANNOT_SEND_PEER; - - if (error != OK) { - // Oops, we cannot send this port. - for (size_t j = 0; j <= i; ++j) - ports[i]->lock.Release(); - // Backpedal on the sequence number. - port->next_sequence_num_to_send--; - return error; - } - } - } - - PortDescriptor* port_descriptors = - GetMutablePortDescriptors(GetMutableEventData<UserEventData>(message)); - - for (size_t i = 0; i < message->num_ports(); ++i) { - WillSendPort(LockedPort(ports[i].get()), - port->peer_node_name, - message->mutable_ports() + i, - port_descriptors + i); - } - - for (size_t i = 0; i < message->num_ports(); ++i) - ports[i]->lock.Release(); - } - -#if DCHECK_IS_ON() - DVLOG(4) << "Sending message " - << GetEventData<UserEventData>(*message)->sequence_num - << " [ports=" << ports_buf.str() << "]" - << " from " << port_name << "@" << name_ - << " to " << port->peer_port_name << "@" << port->peer_node_name; -#endif - - GetMutableEventHeader(message)->port_name = port->peer_port_name; - return OK; -} - -int Node::BeginProxying_Locked(const LockedPort& port, - const PortName& port_name) { - ports_lock_.AssertAcquired(); - port->lock.AssertAcquired(); - - if (port->state != Port::kBuffering) - return OOPS(ERROR_PORT_STATE_UNEXPECTED); - - port->state = Port::kProxying; - - int rv = ForwardMessages_Locked(LockedPort(port), port_name); - if (rv != OK) - return rv; - - // We may have observed closure while buffering. In that case, we can advance - // to removing the proxy without sending out an ObserveProxy message. We - // already know the last expected message, etc. - - if (port->remove_proxy_on_last_message) { - MaybeRemoveProxy_Locked(LockedPort(port), port_name); - - // Make sure we propagate closure to our current peer. - ObserveClosureEventData data; - data.last_sequence_num = port->last_sequence_num_to_receive; - delegate_->ForwardMessage( - port->peer_node_name, - NewInternalMessage(port->peer_port_name, - EventType::kObserveClosure, data)); - } else { - InitiateProxyRemoval(LockedPort(port), port_name); - } - - return OK; -} - -int Node::BeginProxying(PortRef port_ref) { - Port* port = port_ref.port(); - { - base::AutoLock ports_lock(ports_lock_); - base::AutoLock lock(port->lock); - - if (port->state != Port::kBuffering) - return OOPS(ERROR_PORT_STATE_UNEXPECTED); - - port->state = Port::kProxying; - - int rv = ForwardMessages_Locked(LockedPort(port), port_ref.name()); - if (rv != OK) - return rv; - } - - bool should_remove; - NodeName peer_node_name; - ScopedMessage closure_message; - { - base::AutoLock lock(port->lock); - if (port->state != Port::kProxying) - return OOPS(ERROR_PORT_STATE_UNEXPECTED); - - should_remove = port->remove_proxy_on_last_message; - if (should_remove) { - // Make sure we propagate closure to our current peer. - ObserveClosureEventData data; - data.last_sequence_num = port->last_sequence_num_to_receive; - peer_node_name = port->peer_node_name; - closure_message = NewInternalMessage(port->peer_port_name, - EventType::kObserveClosure, data); - } else { - InitiateProxyRemoval(LockedPort(port), port_ref.name()); - } - } - - if (should_remove) { - TryRemoveProxy(port_ref); - delegate_->ForwardMessage(peer_node_name, std::move(closure_message)); - } - - return OK; -} - -int Node::ForwardMessages_Locked(const LockedPort& port, - const PortName &port_name) { - ports_lock_.AssertAcquired(); - port->lock.AssertAcquired(); - - for (;;) { - ScopedMessage message; - port->message_queue.GetNextMessage(&message, nullptr); - if (!message) - break; - - int rv = WillSendMessage_Locked(LockedPort(port), port_name, message.get()); - if (rv != OK) - return rv; - - delegate_->ForwardMessage(port->peer_node_name, std::move(message)); - } - return OK; -} - -void Node::InitiateProxyRemoval(const LockedPort& port, - const PortName& port_name) { - port->lock.AssertAcquired(); - - // To remove this node, we start by notifying the connected graph that we are - // a proxy. This allows whatever port is referencing this node to skip it. - // Eventually, this node will receive ObserveProxyAck (or ObserveClosure if - // the peer was closed in the meantime). - - ObserveProxyEventData data; - data.proxy_node_name = name_; - data.proxy_port_name = port_name; - data.proxy_to_node_name = port->peer_node_name; - data.proxy_to_port_name = port->peer_port_name; - - delegate_->ForwardMessage( - port->peer_node_name, - NewInternalMessage(port->peer_port_name, EventType::kObserveProxy, data)); -} - -void Node::MaybeRemoveProxy_Locked(const LockedPort& port, - const PortName& port_name) { - // |ports_lock_| must be held so we can potentilaly ErasePort_Locked(). - ports_lock_.AssertAcquired(); - port->lock.AssertAcquired(); - - DCHECK(port->state == Port::kProxying); - - // Make sure we have seen ObserveProxyAck before removing the port. - if (!port->remove_proxy_on_last_message) - return; - - if (!CanAcceptMoreMessages(port.get())) { - // This proxy port is done. We can now remove it! - ErasePort_Locked(port_name); - - if (port->send_on_proxy_removal) { - NodeName to_node = port->send_on_proxy_removal->first; - ScopedMessage& message = port->send_on_proxy_removal->second; - - delegate_->ForwardMessage(to_node, std::move(message)); - port->send_on_proxy_removal.reset(); - } - } else { - DVLOG(2) << "Cannot remove port " << port_name << "@" << name_ - << " now; waiting for more messages"; - } -} - -void Node::TryRemoveProxy(PortRef port_ref) { - Port* port = port_ref.port(); - bool should_erase = false; - ScopedMessage msg; - NodeName to_node; - { - base::AutoLock lock(port->lock); - - // Port already removed. Nothing to do. - if (port->state == Port::kClosed) - return; - - DCHECK(port->state == Port::kProxying); - - // Make sure we have seen ObserveProxyAck before removing the port. - if (!port->remove_proxy_on_last_message) - return; - - if (!CanAcceptMoreMessages(port)) { - // This proxy port is done. We can now remove it! - should_erase = true; - - if (port->send_on_proxy_removal) { - to_node = port->send_on_proxy_removal->first; - msg = std::move(port->send_on_proxy_removal->second); - port->send_on_proxy_removal.reset(); - } - } else { - DVLOG(2) << "Cannot remove port " << port_ref.name() << "@" << name_ - << " now; waiting for more messages"; - } - } - - if (should_erase) - ErasePort(port_ref.name()); - - if (msg) - delegate_->ForwardMessage(to_node, std::move(msg)); -} - -void Node::DestroyAllPortsWithPeer(const NodeName& node_name, - const PortName& port_name) { - // Wipes out all ports whose peer node matches |node_name| and whose peer port - // matches |port_name|. If |port_name| is |kInvalidPortName|, only the peer - // node is matched. - - std::vector<PortRef> ports_to_notify; - std::vector<PortName> dead_proxies_to_broadcast; - std::deque<PortName> referenced_port_names; - - { - base::AutoLock ports_lock(ports_lock_); - - for (auto iter = ports_.begin(); iter != ports_.end(); ++iter) { - Port* port = iter->second.get(); - { - base::AutoLock port_lock(port->lock); - - if (port->peer_node_name == node_name && - (port_name == kInvalidPortName || - port->peer_port_name == port_name)) { - if (!port->peer_closed) { - // Treat this as immediate peer closure. It's an exceptional - // condition akin to a broken pipe, so we don't care about losing - // messages. - - port->peer_closed = true; - port->last_sequence_num_to_receive = - port->message_queue.next_sequence_num() - 1; - - if (port->state == Port::kReceiving) - ports_to_notify.push_back(PortRef(iter->first, port)); - } - - // We don't expect to forward any further messages, and we don't - // expect to receive a Port{Accepted,Rejected} event. Because we're - // a proxy with no active peer, we cannot use the normal proxy removal - // procedure of forward-propagating an ObserveProxy. Instead we - // broadcast our own death so it can be back-propagated. This is - // inefficient but rare. - if (port->state != Port::kReceiving) { - dead_proxies_to_broadcast.push_back(iter->first); - iter->second->message_queue.GetReferencedPorts( - &referenced_port_names); - } - } - } - } - - for (const auto& proxy_name : dead_proxies_to_broadcast) { - ports_.erase(proxy_name); - DVLOG(2) << "Forcibly deleted port " << proxy_name << "@" << name_; - } - } - - // Wake up any receiving ports who have just observed simulated peer closure. - for (const auto& port : ports_to_notify) - delegate_->PortStatusChanged(port); - - for (const auto& proxy_name : dead_proxies_to_broadcast) { - // Broadcast an event signifying that this proxy is no longer functioning. - ObserveProxyEventData event; - event.proxy_node_name = name_; - event.proxy_port_name = proxy_name; - event.proxy_to_node_name = kInvalidNodeName; - event.proxy_to_port_name = kInvalidPortName; - delegate_->BroadcastMessage(NewInternalMessage( - kInvalidPortName, EventType::kObserveProxy, event)); - - // Also process death locally since the port that points this closed one - // could be on the current node. - // Note: Although this is recursive, only a single port is involved which - // limits the expected branching to 1. - DestroyAllPortsWithPeer(name_, proxy_name); - } - - // Close any ports referenced by the closed proxies. - for (const auto& name : referenced_port_names) { - PortRef ref; - if (GetPort(name, &ref) == OK) - ClosePort(ref); - } -} - -ScopedMessage Node::NewInternalMessage_Helper(const PortName& port_name, - const EventType& type, - const void* data, - size_t num_data_bytes) { - ScopedMessage message; - delegate_->AllocMessage(sizeof(EventHeader) + num_data_bytes, &message); - - EventHeader* header = GetMutableEventHeader(message.get()); - header->port_name = port_name; - header->type = type; - header->padding = 0; - - if (num_data_bytes) - memcpy(header + 1, data, num_data_bytes); - - return message; -} - -} // namespace ports -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/ports/node.h b/mojo/edk/system/ports/node.h deleted file mode 100644 index 55b8d27..0000000 --- a/mojo/edk/system/ports/node.h +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_PORTS_NODE_H_ -#define MOJO_EDK_SYSTEM_PORTS_NODE_H_ - -#include <stddef.h> -#include <stdint.h> - -#include <queue> -#include <unordered_map> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/system/ports/event.h" -#include "mojo/edk/system/ports/message.h" -#include "mojo/edk/system/ports/name.h" -#include "mojo/edk/system/ports/port.h" -#include "mojo/edk/system/ports/port_ref.h" -#include "mojo/edk/system/ports/user_data.h" - -#undef SendMessage // Gah, windows - -namespace mojo { -namespace edk { -namespace ports { - -enum : int { - OK = 0, - ERROR_PORT_UNKNOWN = -10, - ERROR_PORT_EXISTS = -11, - ERROR_PORT_STATE_UNEXPECTED = -12, - ERROR_PORT_CANNOT_SEND_SELF = -13, - ERROR_PORT_PEER_CLOSED = -14, - ERROR_PORT_CANNOT_SEND_PEER = -15, - ERROR_NOT_IMPLEMENTED = -100, -}; - -struct PortStatus { - bool has_messages; - bool receiving_messages; - bool peer_closed; -}; - -class MessageFilter; -class NodeDelegate; - -class Node { - public: - enum class ShutdownPolicy { - DONT_ALLOW_LOCAL_PORTS, - ALLOW_LOCAL_PORTS, - }; - - // Does not take ownership of the delegate. - Node(const NodeName& name, NodeDelegate* delegate); - ~Node(); - - // Returns true iff there are no open ports referring to another node or ports - // in the process of being transferred from this node to another. If this - // returns false, then to ensure clean shutdown, it is necessary to keep the - // node alive and continue routing messages to it via AcceptMessage. This - // method may be called again after AcceptMessage to check if the Node is now - // ready to be destroyed. - // - // If |policy| is set to |ShutdownPolicy::ALLOW_LOCAL_PORTS|, this will return - // |true| even if some ports remain alive, as long as none of them are proxies - // to another node. - bool CanShutdownCleanly( - ShutdownPolicy policy = ShutdownPolicy::DONT_ALLOW_LOCAL_PORTS); - - // Lookup the named port. - int GetPort(const PortName& port_name, PortRef* port_ref); - - // Creates a port on this node. Before the port can be used, it must be - // initialized using InitializePort. This method is useful for bootstrapping - // a connection between two nodes. Generally, ports are created using - // CreatePortPair instead. - int CreateUninitializedPort(PortRef* port_ref); - - // Initializes a newly created port. - int InitializePort(const PortRef& port_ref, - const NodeName& peer_node_name, - const PortName& peer_port_name); - - // Generates a new connected pair of ports bound to this node. These ports - // are initialized and ready to go. - int CreatePortPair(PortRef* port0_ref, PortRef* port1_ref); - - // User data associated with the port. - int SetUserData(const PortRef& port_ref, scoped_refptr<UserData> user_data); - int GetUserData(const PortRef& port_ref, - scoped_refptr<UserData>* user_data); - - // Prevents further messages from being sent from this port or delivered to - // this port. The port is removed, and the port's peer is notified of the - // closure after it has consumed all pending messages. - int ClosePort(const PortRef& port_ref); - - // Returns the current status of the port. - int GetStatus(const PortRef& port_ref, PortStatus* port_status); - - // Returns the next available message on the specified port or returns a null - // message if there are none available. Returns ERROR_PORT_PEER_CLOSED to - // indicate that this port's peer has closed. In such cases GetMessage may - // be called until it yields a null message, indicating that no more messages - // may be read from the port. - // - // If |filter| is non-null, the next available message is returned only if it - // is matched by the filter. If the provided filter does not match the next - // available message, GetMessage() behaves as if there is no message - // available. Ownership of |filter| is not taken, and it must outlive the - // extent of this call. - int GetMessage(const PortRef& port_ref, - ScopedMessage* message, - MessageFilter* filter); - - // Sends a message from the specified port to its peer. Note that the message - // notification may arrive synchronously (via PortStatusChanged() on the - // delegate) if the peer is local to this Node. - int SendMessage(const PortRef& port_ref, ScopedMessage message); - - // Corresponding to NodeDelegate::ForwardMessage. - int AcceptMessage(ScopedMessage message); - - // Called to merge two ports with each other. If you have two independent - // port pairs A <=> B and C <=> D, the net result of merging B and C is a - // single connected port pair A <=> D. - // - // Note that the behavior of this operation is undefined if either port to be - // merged (B or C above) has ever been read from or written to directly, and - // this must ONLY be called on one side of the merge, though it doesn't matter - // which side. - // - // It is safe for the non-merged peers (A and D above) to be transferred, - // closed, and/or written to before, during, or after the merge. - int MergePorts(const PortRef& port_ref, - const NodeName& destination_node_name, - const PortName& destination_port_name); - - // Like above but merges two ports local to this node. Because both ports are - // local this can also verify that neither port has been written to before the - // merge. If this fails for any reason, both ports are closed. Otherwise OK - // is returned and the ports' receiving peers are connected to each other. - int MergeLocalPorts(const PortRef& port0_ref, const PortRef& port1_ref); - - // Called to inform this node that communication with another node is lost - // indefinitely. This triggers cleanup of ports bound to this node. - int LostConnectionToNode(const NodeName& node_name); - - private: - class LockedPort; - - // Note: Functions that end with _Locked require |ports_lock_| to be held - // before calling. - int OnUserMessage(ScopedMessage message); - int OnPortAccepted(const PortName& port_name); - int OnObserveProxy(const PortName& port_name, - const ObserveProxyEventData& event); - int OnObserveProxyAck(const PortName& port_name, uint64_t last_sequence_num); - int OnObserveClosure(const PortName& port_name, uint64_t last_sequence_num); - int OnMergePort(const PortName& port_name, const MergePortEventData& event); - - int AddPortWithName(const PortName& port_name, scoped_refptr<Port> port); - void ErasePort(const PortName& port_name); - void ErasePort_Locked(const PortName& port_name); - scoped_refptr<Port> GetPort(const PortName& port_name); - scoped_refptr<Port> GetPort_Locked(const PortName& port_name); - - int SendMessageInternal(const PortRef& port_ref, ScopedMessage* message); - int MergePorts_Locked(const PortRef& port0_ref, const PortRef& port1_ref); - void WillSendPort(const LockedPort& port, - const NodeName& to_node_name, - PortName* port_name, - PortDescriptor* port_descriptor); - int AcceptPort(const PortName& port_name, - const PortDescriptor& port_descriptor); - - int WillSendMessage_Locked(const LockedPort& port, - const PortName& port_name, - Message* message); - int BeginProxying_Locked(const LockedPort& port, const PortName& port_name); - int BeginProxying(PortRef port_ref); - int ForwardMessages_Locked(const LockedPort& port, const PortName& port_name); - void InitiateProxyRemoval(const LockedPort& port, const PortName& port_name); - void MaybeRemoveProxy_Locked(const LockedPort& port, - const PortName& port_name); - void TryRemoveProxy(PortRef port_ref); - void DestroyAllPortsWithPeer(const NodeName& node_name, - const PortName& port_name); - - ScopedMessage NewInternalMessage_Helper(const PortName& port_name, - const EventType& type, - const void* data, - size_t num_data_bytes); - - ScopedMessage NewInternalMessage(const PortName& port_name, - const EventType& type) { - return NewInternalMessage_Helper(port_name, type, nullptr, 0); - } - - template <typename EventData> - ScopedMessage NewInternalMessage(const PortName& port_name, - const EventType& type, - const EventData& data) { - return NewInternalMessage_Helper(port_name, type, &data, sizeof(data)); - } - - const NodeName name_; - NodeDelegate* const delegate_; - - // Guards |ports_| as well as any operation which needs to hold multiple port - // locks simultaneously. Usage of this is subtle: it must NEVER be acquired - // after a Port lock is acquired, and it must ALWAYS be acquired before - // calling WillSendMessage_Locked or ForwardMessages_Locked. - base::Lock ports_lock_; - std::unordered_map<PortName, scoped_refptr<Port>> ports_; - - DISALLOW_COPY_AND_ASSIGN(Node); -}; - -} // namespace ports -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_PORTS_NODE_H_ diff --git a/mojo/edk/system/ports/node_delegate.h b/mojo/edk/system/ports/node_delegate.h deleted file mode 100644 index 8547302..0000000 --- a/mojo/edk/system/ports/node_delegate.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_PORTS_NODE_DELEGATE_H_ -#define MOJO_EDK_SYSTEM_PORTS_NODE_DELEGATE_H_ - -#include <stddef.h> - -#include "mojo/edk/system/ports/message.h" -#include "mojo/edk/system/ports/name.h" -#include "mojo/edk/system/ports/port_ref.h" - -namespace mojo { -namespace edk { -namespace ports { - -class NodeDelegate { - public: - virtual ~NodeDelegate() {} - - // Port names should be difficult to guess. - virtual void GenerateRandomPortName(PortName* port_name) = 0; - - // Allocate a message, including a header that can be used by the Node - // implementation. |num_header_bytes| will be aligned. The newly allocated - // memory need not be zero-filled. - virtual void AllocMessage(size_t num_header_bytes, - ScopedMessage* message) = 0; - - // Forward a message asynchronously to the specified node. This method MUST - // NOT synchronously call any methods on Node. - virtual void ForwardMessage(const NodeName& node, ScopedMessage message) = 0; - - // Broadcast a message to all nodes. - virtual void BroadcastMessage(ScopedMessage message) = 0; - - // Indicates that the port's status has changed recently. Use Node::GetStatus - // to query the latest status of the port. Note, this event could be spurious - // if another thread is simultaneously modifying the status of the port. - virtual void PortStatusChanged(const PortRef& port_ref) = 0; -}; - -} // namespace ports -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_PORTS_NODE_DELEGATE_H_ diff --git a/mojo/edk/system/ports/port.cc b/mojo/edk/system/ports/port.cc deleted file mode 100644 index e4403ae..0000000 --- a/mojo/edk/system/ports/port.cc +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/ports/port.h" - -namespace mojo { -namespace edk { -namespace ports { - -Port::Port(uint64_t next_sequence_num_to_send, - uint64_t next_sequence_num_to_receive) - : state(kUninitialized), - next_sequence_num_to_send(next_sequence_num_to_send), - last_sequence_num_to_receive(0), - message_queue(next_sequence_num_to_receive), - remove_proxy_on_last_message(false), - peer_closed(false) {} - -Port::~Port() {} - -} // namespace ports -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/ports/port.h b/mojo/edk/system/ports/port.h deleted file mode 100644 index ea53d43..0000000 --- a/mojo/edk/system/ports/port.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_PORTS_PORT_H_ -#define MOJO_EDK_SYSTEM_PORTS_PORT_H_ - -#include <memory> -#include <queue> -#include <utility> -#include <vector> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/system/ports/message_queue.h" -#include "mojo/edk/system/ports/user_data.h" - -namespace mojo { -namespace edk { -namespace ports { - -class Port : public base::RefCountedThreadSafe<Port> { - public: - enum State { - kUninitialized, - kReceiving, - kBuffering, - kProxying, - kClosed - }; - - base::Lock lock; - State state; - NodeName peer_node_name; - PortName peer_port_name; - uint64_t next_sequence_num_to_send; - uint64_t last_sequence_num_to_receive; - MessageQueue message_queue; - std::unique_ptr<std::pair<NodeName, ScopedMessage>> send_on_proxy_removal; - scoped_refptr<UserData> user_data; - bool remove_proxy_on_last_message; - bool peer_closed; - - Port(uint64_t next_sequence_num_to_send, - uint64_t next_sequence_num_to_receive); - - private: - friend class base::RefCountedThreadSafe<Port>; - - ~Port(); - - DISALLOW_COPY_AND_ASSIGN(Port); -}; - -} // namespace ports -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_PORTS_PORT_H_ diff --git a/mojo/edk/system/ports/port_ref.cc b/mojo/edk/system/ports/port_ref.cc deleted file mode 100644 index 675754d..0000000 --- a/mojo/edk/system/ports/port_ref.cc +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/ports/port_ref.h" - -#include "mojo/edk/system/ports/port.h" - -namespace mojo { -namespace edk { -namespace ports { - -PortRef::~PortRef() { -} - -PortRef::PortRef() { -} - -PortRef::PortRef(const PortName& name, scoped_refptr<Port> port) - : name_(name), port_(std::move(port)) {} - -PortRef::PortRef(const PortRef& other) - : name_(other.name_), port_(other.port_) { -} - -PortRef& PortRef::operator=(const PortRef& other) { - if (&other != this) { - name_ = other.name_; - port_ = other.port_; - } - return *this; -} - -} // namespace ports -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/ports/port_ref.h b/mojo/edk/system/ports/port_ref.h deleted file mode 100644 index 59036c3..0000000 --- a/mojo/edk/system/ports/port_ref.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_PORTS_PORT_REF_H_ -#define MOJO_EDK_SYSTEM_PORTS_PORT_REF_H_ - -#include "base/memory/ref_counted.h" -#include "mojo/edk/system/ports/name.h" - -namespace mojo { -namespace edk { -namespace ports { - -class Port; -class Node; - -class PortRef { - public: - ~PortRef(); - PortRef(); - PortRef(const PortName& name, scoped_refptr<Port> port); - - PortRef(const PortRef& other); - PortRef& operator=(const PortRef& other); - - const PortName& name() const { return name_; } - - private: - friend class Node; - Port* port() const { return port_.get(); } - - PortName name_; - scoped_refptr<Port> port_; -}; - -} // namespace ports -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_PORTS_PORT_REF_H_ diff --git a/mojo/edk/system/ports/ports_unittest.cc b/mojo/edk/system/ports/ports_unittest.cc deleted file mode 100644 index cb48b3e..0000000 --- a/mojo/edk/system/ports/ports_unittest.cc +++ /dev/null @@ -1,1478 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <inttypes.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include <map> -#include <queue> -#include <sstream> -#include <utility> - -#include "base/bind.h" -#include "base/callback.h" -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/rand_util.h" -#include "base/strings/string_piece.h" -#include "base/strings/stringprintf.h" -#include "base/synchronization/lock.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/thread.h" -#include "mojo/edk/system/ports/event.h" -#include "mojo/edk/system/ports/node.h" -#include "mojo/edk/system/ports/node_delegate.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace edk { -namespace ports { -namespace test { - -namespace { - -bool MessageEquals(const ScopedMessage& message, const base::StringPiece& s) { - return !strcmp(static_cast<const char*>(message->payload_bytes()), s.data()); -} - -class TestMessage : public Message { - public: - static ScopedMessage NewUserMessage(size_t num_payload_bytes, - size_t num_ports) { - return ScopedMessage(new TestMessage(num_payload_bytes, num_ports)); - } - - TestMessage(size_t num_payload_bytes, size_t num_ports) - : Message(num_payload_bytes, num_ports) { - start_ = new char[num_header_bytes_ + num_ports_bytes_ + num_payload_bytes]; - InitializeUserMessageHeader(start_); - } - - TestMessage(size_t num_header_bytes, - size_t num_payload_bytes, - size_t num_ports_bytes) - : Message(num_header_bytes, - num_payload_bytes, - num_ports_bytes) { - start_ = new char[num_header_bytes + num_payload_bytes + num_ports_bytes]; - } - - ~TestMessage() override { - delete[] start_; - } -}; - -class TestNode; - -class MessageRouter { - public: - virtual ~MessageRouter() {} - - virtual void GeneratePortName(PortName* name) = 0; - virtual void ForwardMessage(TestNode* from_node, - const NodeName& node_name, - ScopedMessage message) = 0; - virtual void BroadcastMessage(TestNode* from_node, ScopedMessage message) = 0; -}; - -class TestNode : public NodeDelegate { - public: - explicit TestNode(uint64_t id) - : node_name_(id, 1), - node_(node_name_, this), - node_thread_(base::StringPrintf("Node %" PRIu64 " thread", id)), - messages_available_event_( - base::WaitableEvent::ResetPolicy::AUTOMATIC, - base::WaitableEvent::InitialState::NOT_SIGNALED), - idle_event_( - base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::SIGNALED) { - } - - ~TestNode() override { - StopWhenIdle(); - node_thread_.Stop(); - } - - const NodeName& name() const { return node_name_; } - - // NOTE: Node is thread-safe. - Node& node() { return node_; } - - base::WaitableEvent& idle_event() { return idle_event_; } - - bool IsIdle() { - base::AutoLock lock(lock_); - return started_ && !dispatching_ && - (incoming_messages_.empty() || (block_on_event_ && blocked_)); - } - - void BlockOnEvent(EventType type) { - base::AutoLock lock(lock_); - blocked_event_type_ = type; - block_on_event_ = true; - } - - void Unblock() { - base::AutoLock lock(lock_); - block_on_event_ = false; - messages_available_event_.Signal(); - } - - void Start(MessageRouter* router) { - router_ = router; - node_thread_.Start(); - node_thread_.task_runner()->PostTask( - FROM_HERE, - base::Bind(&TestNode::ProcessMessages, base::Unretained(this))); - } - - void StopWhenIdle() { - base::AutoLock lock(lock_); - should_quit_ = true; - messages_available_event_.Signal(); - } - - void WakeUp() { messages_available_event_.Signal(); } - - int SendStringMessage(const PortRef& port, const std::string& s) { - size_t size = s.size() + 1; - ScopedMessage message = TestMessage::NewUserMessage(size, 0); - memcpy(message->mutable_payload_bytes(), s.data(), size); - return node_.SendMessage(port, std::move(message)); - } - - int SendStringMessageWithPort(const PortRef& port, - const std::string& s, - const PortName& sent_port_name) { - size_t size = s.size() + 1; - ScopedMessage message = TestMessage::NewUserMessage(size, 1); - memcpy(message->mutable_payload_bytes(), s.data(), size); - message->mutable_ports()[0] = sent_port_name; - return node_.SendMessage(port, std::move(message)); - } - - int SendStringMessageWithPort(const PortRef& port, - const std::string& s, - const PortRef& sent_port) { - return SendStringMessageWithPort(port, s, sent_port.name()); - } - - void set_drop_messages(bool value) { - base::AutoLock lock(lock_); - drop_messages_ = value; - } - - void set_save_messages(bool value) { - base::AutoLock lock(lock_); - save_messages_ = value; - } - - bool ReadMessage(const PortRef& port, ScopedMessage* message) { - return node_.GetMessage(port, message, nullptr) == OK && *message; - } - - bool GetSavedMessage(ScopedMessage* message) { - base::AutoLock lock(lock_); - if (saved_messages_.empty()) { - message->reset(); - return false; - } - std::swap(*message, saved_messages_.front()); - saved_messages_.pop(); - return true; - } - - void EnqueueMessage(ScopedMessage message) { - idle_event_.Reset(); - - // NOTE: This may be called from ForwardMessage and thus must not reenter - // |node_|. - base::AutoLock lock(lock_); - incoming_messages_.emplace(std::move(message)); - messages_available_event_.Signal(); - } - - void GenerateRandomPortName(PortName* port_name) override { - DCHECK(router_); - router_->GeneratePortName(port_name); - } - - void AllocMessage(size_t num_header_bytes, ScopedMessage* message) override { - message->reset(new TestMessage(num_header_bytes, 0, 0)); - } - - void ForwardMessage(const NodeName& node_name, - ScopedMessage message) override { - { - base::AutoLock lock(lock_); - if (drop_messages_) { - DVLOG(1) << "Dropping ForwardMessage from node " - << node_name_ << " to " << node_name; - - base::AutoUnlock unlock(lock_); - ClosePortsInMessage(message.get()); - return; - } - } - - DCHECK(router_); - DVLOG(1) << "ForwardMessage from node " - << node_name_ << " to " << node_name; - router_->ForwardMessage(this, node_name, std::move(message)); - } - - void BroadcastMessage(ScopedMessage message) override { - router_->BroadcastMessage(this, std::move(message)); - } - - void PortStatusChanged(const PortRef& port) override { - // The port may be closed, in which case we ignore the notification. - base::AutoLock lock(lock_); - if (!save_messages_) - return; - - for (;;) { - ScopedMessage message; - { - base::AutoUnlock unlock(lock_); - if (!ReadMessage(port, &message)) - break; - } - - saved_messages_.emplace(std::move(message)); - } - } - - void ClosePortsInMessage(Message* message) { - for (size_t i = 0; i < message->num_ports(); ++i) { - PortRef port; - ASSERT_EQ(OK, node_.GetPort(message->ports()[i], &port)); - EXPECT_EQ(OK, node_.ClosePort(port)); - } - } - - private: - void ProcessMessages() { - for (;;) { - messages_available_event_.Wait(); - - base::AutoLock lock(lock_); - - if (should_quit_) - return; - - dispatching_ = true; - while (!incoming_messages_.empty()) { - if (block_on_event_ && - GetEventHeader(*incoming_messages_.front())->type == - blocked_event_type_) { - blocked_ = true; - // Go idle if we hit a blocked event type. - break; - } else { - blocked_ = false; - } - ScopedMessage message = std::move(incoming_messages_.front()); - incoming_messages_.pop(); - - // NOTE: AcceptMessage() can re-enter this object to call any of the - // NodeDelegate interface methods. - base::AutoUnlock unlock(lock_); - node_.AcceptMessage(std::move(message)); - } - - dispatching_ = false; - started_ = true; - idle_event_.Signal(); - }; - } - - const NodeName node_name_; - Node node_; - MessageRouter* router_ = nullptr; - - base::Thread node_thread_; - base::WaitableEvent messages_available_event_; - base::WaitableEvent idle_event_; - - // Guards fields below. - base::Lock lock_; - bool started_ = false; - bool dispatching_ = false; - bool should_quit_ = false; - bool drop_messages_ = false; - bool save_messages_ = false; - bool blocked_ = false; - bool block_on_event_ = false; - EventType blocked_event_type_; - std::queue<ScopedMessage> incoming_messages_; - std::queue<ScopedMessage> saved_messages_; -}; - -class PortsTest : public testing::Test, public MessageRouter { - public: - void AddNode(TestNode* node) { - { - base::AutoLock lock(lock_); - nodes_[node->name()] = node; - } - node->Start(this); - } - - void RemoveNode(TestNode* node) { - { - base::AutoLock lock(lock_); - nodes_.erase(node->name()); - } - - for (const auto& entry : nodes_) - entry.second->node().LostConnectionToNode(node->name()); - } - - // Waits until all known Nodes are idle. Message forwarding and processing - // is handled in such a way that idleness is a stable state: once all nodes in - // the system are idle, they will remain idle until the test explicitly - // initiates some further event (e.g. sending a message, closing a port, or - // removing a Node). - void WaitForIdle() { - for (;;) { - base::AutoLock global_lock(global_lock_); - bool all_nodes_idle = true; - for (const auto& entry : nodes_) { - if (!entry.second->IsIdle()) - all_nodes_idle = false; - entry.second->WakeUp(); - } - if (all_nodes_idle) - return; - - // Wait for any Node to signal that it's idle. - base::AutoUnlock global_unlock(global_lock_); - std::vector<base::WaitableEvent*> events; - for (const auto& entry : nodes_) - events.push_back(&entry.second->idle_event()); - base::WaitableEvent::WaitMany(events.data(), events.size()); - } - } - - void CreatePortPair(TestNode* node0, - PortRef* port0, - TestNode* node1, - PortRef* port1) { - if (node0 == node1) { - EXPECT_EQ(OK, node0->node().CreatePortPair(port0, port1)); - } else { - EXPECT_EQ(OK, node0->node().CreateUninitializedPort(port0)); - EXPECT_EQ(OK, node1->node().CreateUninitializedPort(port1)); - EXPECT_EQ(OK, node0->node().InitializePort(*port0, node1->name(), - port1->name())); - EXPECT_EQ(OK, node1->node().InitializePort(*port1, node0->name(), - port0->name())); - } - } - - private: - // MessageRouter: - void GeneratePortName(PortName* name) override { - base::AutoLock lock(lock_); - name->v1 = next_port_id_++; - name->v2 = 0; - } - - void ForwardMessage(TestNode* from_node, - const NodeName& node_name, - ScopedMessage message) override { - base::AutoLock global_lock(global_lock_); - base::AutoLock lock(lock_); - // Drop messages from nodes that have been removed. - if (nodes_.find(from_node->name()) == nodes_.end()) { - from_node->ClosePortsInMessage(message.get()); - return; - } - - auto it = nodes_.find(node_name); - if (it == nodes_.end()) { - DVLOG(1) << "Node not found: " << node_name; - return; - } - - it->second->EnqueueMessage(std::move(message)); - } - - void BroadcastMessage(TestNode* from_node, ScopedMessage message) override { - base::AutoLock global_lock(global_lock_); - base::AutoLock lock(lock_); - - // Drop messages from nodes that have been removed. - if (nodes_.find(from_node->name()) == nodes_.end()) - return; - - for (const auto& entry : nodes_) { - TestNode* node = entry.second; - // Broadcast doesn't deliver to the local node. - if (node == from_node) - continue; - - // NOTE: We only need to support broadcast of events. Events have no - // payload or ports bytes. - ScopedMessage new_message( - new TestMessage(message->num_header_bytes(), 0, 0)); - memcpy(new_message->mutable_header_bytes(), message->header_bytes(), - message->num_header_bytes()); - node->EnqueueMessage(std::move(new_message)); - } - } - - base::MessageLoop message_loop_; - - // Acquired before any operation which makes a Node busy, and before testing - // if all nodes are idle. - base::Lock global_lock_; - - base::Lock lock_; - uint64_t next_port_id_ = 1; - std::map<NodeName, TestNode*> nodes_; -}; - -} // namespace - -TEST_F(PortsTest, Basic1) { - TestNode node0(0); - AddNode(&node0); - - TestNode node1(1); - AddNode(&node1); - - PortRef x0, x1; - CreatePortPair(&node0, &x0, &node1, &x1); - - PortRef a0, a1; - EXPECT_EQ(OK, node0.node().CreatePortPair(&a0, &a1)); - EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "hello", a1)); - EXPECT_EQ(OK, node0.node().ClosePort(a0)); - - EXPECT_EQ(OK, node0.node().ClosePort(x0)); - EXPECT_EQ(OK, node1.node().ClosePort(x1)); - - WaitForIdle(); - - EXPECT_TRUE(node0.node().CanShutdownCleanly()); - EXPECT_TRUE(node1.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, Basic2) { - TestNode node0(0); - AddNode(&node0); - - TestNode node1(1); - AddNode(&node1); - - PortRef x0, x1; - CreatePortPair(&node0, &x0, &node1, &x1); - - PortRef b0, b1; - EXPECT_EQ(OK, node0.node().CreatePortPair(&b0, &b1)); - EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "hello", b1)); - EXPECT_EQ(OK, node0.SendStringMessage(b0, "hello again")); - - EXPECT_EQ(OK, node0.node().ClosePort(b0)); - - EXPECT_EQ(OK, node0.node().ClosePort(x0)); - EXPECT_EQ(OK, node1.node().ClosePort(x1)); - - WaitForIdle(); - - EXPECT_TRUE(node0.node().CanShutdownCleanly()); - EXPECT_TRUE(node1.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, Basic3) { - TestNode node0(0); - AddNode(&node0); - - TestNode node1(1); - AddNode(&node1); - - PortRef x0, x1; - CreatePortPair(&node0, &x0, &node1, &x1); - - PortRef a0, a1; - EXPECT_EQ(OK, node0.node().CreatePortPair(&a0, &a1)); - - EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "hello", a1)); - EXPECT_EQ(OK, node0.SendStringMessage(a0, "hello again")); - - EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "foo", a0)); - - PortRef b0, b1; - EXPECT_EQ(OK, node0.node().CreatePortPair(&b0, &b1)); - EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "bar", b1)); - EXPECT_EQ(OK, node0.SendStringMessage(b0, "baz")); - - EXPECT_EQ(OK, node0.node().ClosePort(b0)); - - EXPECT_EQ(OK, node0.node().ClosePort(x0)); - EXPECT_EQ(OK, node1.node().ClosePort(x1)); - - WaitForIdle(); - - EXPECT_TRUE(node0.node().CanShutdownCleanly()); - EXPECT_TRUE(node1.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, LostConnectionToNode1) { - TestNode node0(0); - AddNode(&node0); - - TestNode node1(1); - AddNode(&node1); - node1.set_drop_messages(true); - - PortRef x0, x1; - CreatePortPair(&node0, &x0, &node1, &x1); - - // Transfer a port to node1 and simulate a lost connection to node1. - - PortRef a0, a1; - EXPECT_EQ(OK, node0.node().CreatePortPair(&a0, &a1)); - EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "foo", a1)); - - WaitForIdle(); - - RemoveNode(&node1); - - WaitForIdle(); - - EXPECT_EQ(OK, node0.node().ClosePort(a0)); - EXPECT_EQ(OK, node0.node().ClosePort(x0)); - EXPECT_EQ(OK, node1.node().ClosePort(x1)); - - WaitForIdle(); - - EXPECT_TRUE(node0.node().CanShutdownCleanly()); - EXPECT_TRUE(node1.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, LostConnectionToNode2) { - TestNode node0(0); - AddNode(&node0); - - TestNode node1(1); - AddNode(&node1); - - PortRef x0, x1; - CreatePortPair(&node0, &x0, &node1, &x1); - - PortRef a0, a1; - EXPECT_EQ(OK, node0.node().CreatePortPair(&a0, &a1)); - EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "take a1", a1)); - - WaitForIdle(); - - node1.set_drop_messages(true); - - RemoveNode(&node1); - - WaitForIdle(); - - // a0 should have eventually detected peer closure after node loss. - ScopedMessage message; - EXPECT_EQ(ERROR_PORT_PEER_CLOSED, - node0.node().GetMessage(a0, &message, nullptr)); - EXPECT_FALSE(message); - - EXPECT_EQ(OK, node0.node().ClosePort(a0)); - - EXPECT_EQ(OK, node0.node().ClosePort(x0)); - - EXPECT_EQ(OK, node1.node().GetMessage(x1, &message, nullptr)); - EXPECT_TRUE(message); - node1.ClosePortsInMessage(message.get()); - - EXPECT_EQ(OK, node1.node().ClosePort(x1)); - - WaitForIdle(); - - EXPECT_TRUE(node0.node().CanShutdownCleanly()); - EXPECT_TRUE(node1.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, LostConnectionToNodeWithSecondaryProxy) { - // Tests that a proxy gets cleaned up when its indirect peer lives on a lost - // node. - - TestNode node0(0); - AddNode(&node0); - - TestNode node1(1); - AddNode(&node1); - - TestNode node2(2); - AddNode(&node2); - - // Create A-B spanning nodes 0 and 1 and C-D spanning 1 and 2. - PortRef A, B, C, D; - CreatePortPair(&node0, &A, &node1, &B); - CreatePortPair(&node1, &C, &node2, &D); - - // Create E-F and send F over A to node 1. - PortRef E, F; - EXPECT_EQ(OK, node0.node().CreatePortPair(&E, &F)); - EXPECT_EQ(OK, node0.SendStringMessageWithPort(A, ".", F)); - - WaitForIdle(); - - ScopedMessage message; - ASSERT_TRUE(node1.ReadMessage(B, &message)); - ASSERT_EQ(1u, message->num_ports()); - - EXPECT_EQ(OK, node1.node().GetPort(message->ports()[0], &F)); - - // Send F over C to node 2 and then simulate node 2 loss from node 1. Node 1 - // will trivially become aware of the loss, and this test verifies that the - // port A on node 0 will eventually also become aware of it. - - // Make sure node2 stops processing events when it encounters an ObserveProxy. - node2.BlockOnEvent(EventType::kObserveProxy); - - EXPECT_EQ(OK, node1.SendStringMessageWithPort(C, ".", F)); - WaitForIdle(); - - // Simulate node 1 and 2 disconnecting. - EXPECT_EQ(OK, node1.node().LostConnectionToNode(node2.name())); - - // Let node2 continue processing events and wait for everyone to go idle. - node2.Unblock(); - WaitForIdle(); - - // Port F should be gone. - EXPECT_EQ(ERROR_PORT_UNKNOWN, node1.node().GetPort(F.name(), &F)); - - // Port E should have detected peer closure despite the fact that there is - // no longer a continuous route from F to E over which the event could travel. - PortStatus status; - EXPECT_EQ(OK, node0.node().GetStatus(E, &status)); - EXPECT_TRUE(status.peer_closed); - - EXPECT_EQ(OK, node0.node().ClosePort(A)); - EXPECT_EQ(OK, node1.node().ClosePort(B)); - EXPECT_EQ(OK, node1.node().ClosePort(C)); - EXPECT_EQ(OK, node0.node().ClosePort(E)); - - WaitForIdle(); - - EXPECT_TRUE(node0.node().CanShutdownCleanly()); - EXPECT_TRUE(node1.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, LostConnectionToNodeWithLocalProxy) { - // Tests that a proxy gets cleaned up when its direct peer lives on a lost - // node and it's predecessor lives on the same node. - - TestNode node0(0); - AddNode(&node0); - - TestNode node1(1); - AddNode(&node1); - - PortRef A, B; - CreatePortPair(&node0, &A, &node1, &B); - - PortRef C, D; - EXPECT_EQ(OK, node0.node().CreatePortPair(&C, &D)); - - // Send D but block node0 on an ObserveProxy event. - node0.BlockOnEvent(EventType::kObserveProxy); - EXPECT_EQ(OK, node0.SendStringMessageWithPort(A, ".", D)); - - // node0 won't collapse the proxy but node1 will receive the message before - // going idle. - WaitForIdle(); - - ScopedMessage message; - ASSERT_TRUE(node1.ReadMessage(B, &message)); - ASSERT_EQ(1u, message->num_ports()); - PortRef E; - EXPECT_EQ(OK, node1.node().GetPort(message->ports()[0], &E)); - - RemoveNode(&node1); - - node0.Unblock(); - WaitForIdle(); - - // Port C should have detected peer closure. - PortStatus status; - EXPECT_EQ(OK, node0.node().GetStatus(C, &status)); - EXPECT_TRUE(status.peer_closed); - - EXPECT_EQ(OK, node0.node().ClosePort(A)); - EXPECT_EQ(OK, node1.node().ClosePort(B)); - EXPECT_EQ(OK, node0.node().ClosePort(C)); - EXPECT_EQ(OK, node1.node().ClosePort(E)); - - EXPECT_TRUE(node0.node().CanShutdownCleanly()); - EXPECT_TRUE(node1.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, GetMessage1) { - TestNode node(0); - AddNode(&node); - - PortRef a0, a1; - EXPECT_EQ(OK, node.node().CreatePortPair(&a0, &a1)); - - ScopedMessage message; - EXPECT_EQ(OK, node.node().GetMessage(a0, &message, nullptr)); - EXPECT_FALSE(message); - - EXPECT_EQ(OK, node.node().ClosePort(a1)); - - WaitForIdle(); - - EXPECT_EQ(ERROR_PORT_PEER_CLOSED, - node.node().GetMessage(a0, &message, nullptr)); - EXPECT_FALSE(message); - - EXPECT_EQ(OK, node.node().ClosePort(a0)); - - WaitForIdle(); - - EXPECT_TRUE(node.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, GetMessage2) { - TestNode node(0); - AddNode(&node); - - PortRef a0, a1; - EXPECT_EQ(OK, node.node().CreatePortPair(&a0, &a1)); - - EXPECT_EQ(OK, node.SendStringMessage(a1, "1")); - - ScopedMessage message; - EXPECT_EQ(OK, node.node().GetMessage(a0, &message, nullptr)); - - ASSERT_TRUE(message); - EXPECT_TRUE(MessageEquals(message, "1")); - - EXPECT_EQ(OK, node.node().ClosePort(a0)); - EXPECT_EQ(OK, node.node().ClosePort(a1)); - - EXPECT_TRUE(node.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, GetMessage3) { - TestNode node(0); - AddNode(&node); - - PortRef a0, a1; - EXPECT_EQ(OK, node.node().CreatePortPair(&a0, &a1)); - - const char* kStrings[] = { - "1", - "2", - "3" - }; - - for (size_t i = 0; i < sizeof(kStrings)/sizeof(kStrings[0]); ++i) - EXPECT_EQ(OK, node.SendStringMessage(a1, kStrings[i])); - - ScopedMessage message; - for (size_t i = 0; i < sizeof(kStrings)/sizeof(kStrings[0]); ++i) { - EXPECT_EQ(OK, node.node().GetMessage(a0, &message, nullptr)); - ASSERT_TRUE(message); - EXPECT_TRUE(MessageEquals(message, kStrings[i])); - } - - EXPECT_EQ(OK, node.node().ClosePort(a0)); - EXPECT_EQ(OK, node.node().ClosePort(a1)); - - EXPECT_TRUE(node.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, Delegation1) { - TestNode node0(0); - AddNode(&node0); - - TestNode node1(1); - AddNode(&node1); - - PortRef x0, x1; - CreatePortPair(&node0, &x0, &node1, &x1); - - // In this test, we send a message to a port that has been moved. - - PortRef a0, a1; - EXPECT_EQ(OK, node0.node().CreatePortPair(&a0, &a1)); - EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "a1", a1)); - WaitForIdle(); - - ScopedMessage message; - ASSERT_TRUE(node1.ReadMessage(x1, &message)); - ASSERT_EQ(1u, message->num_ports()); - EXPECT_TRUE(MessageEquals(message, "a1")); - - // This is "a1" from the point of view of node1. - PortName a2_name = message->ports()[0]; - EXPECT_EQ(OK, node1.SendStringMessageWithPort(x1, "a2", a2_name)); - EXPECT_EQ(OK, node0.SendStringMessage(a0, "hello")); - - WaitForIdle(); - - ASSERT_TRUE(node0.ReadMessage(x0, &message)); - ASSERT_EQ(1u, message->num_ports()); - EXPECT_TRUE(MessageEquals(message, "a2")); - - // This is "a2" from the point of view of node1. - PortName a3_name = message->ports()[0]; - - PortRef a3; - EXPECT_EQ(OK, node0.node().GetPort(a3_name, &a3)); - - ASSERT_TRUE(node0.ReadMessage(a3, &message)); - EXPECT_EQ(0u, message->num_ports()); - EXPECT_TRUE(MessageEquals(message, "hello")); - - EXPECT_EQ(OK, node0.node().ClosePort(a0)); - EXPECT_EQ(OK, node0.node().ClosePort(a3)); - - EXPECT_EQ(OK, node0.node().ClosePort(x0)); - EXPECT_EQ(OK, node1.node().ClosePort(x1)); - - EXPECT_TRUE(node0.node().CanShutdownCleanly()); - EXPECT_TRUE(node1.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, Delegation2) { - TestNode node0(0); - AddNode(&node0); - - TestNode node1(1); - AddNode(&node1); - - for (int i = 0; i < 100; ++i) { - // Setup pipe a<->b between node0 and node1. - PortRef A, B; - CreatePortPair(&node0, &A, &node1, &B); - - PortRef C, D; - EXPECT_EQ(OK, node0.node().CreatePortPair(&C, &D)); - - PortRef E, F; - EXPECT_EQ(OK, node0.node().CreatePortPair(&E, &F)); - - node1.set_save_messages(true); - - // Pass D over A to B. - EXPECT_EQ(OK, node0.SendStringMessageWithPort(A, "1", D)); - - // Pass F over C to D. - EXPECT_EQ(OK, node0.SendStringMessageWithPort(C, "1", F)); - - // This message should find its way to node1. - EXPECT_EQ(OK, node0.SendStringMessage(E, "hello")); - - WaitForIdle(); - - EXPECT_EQ(OK, node0.node().ClosePort(C)); - EXPECT_EQ(OK, node0.node().ClosePort(E)); - - EXPECT_EQ(OK, node0.node().ClosePort(A)); - EXPECT_EQ(OK, node1.node().ClosePort(B)); - - bool got_hello = false; - ScopedMessage message; - while (node1.GetSavedMessage(&message)) { - node1.ClosePortsInMessage(message.get()); - if (MessageEquals(message, "hello")) { - got_hello = true; - break; - } - } - - EXPECT_TRUE(got_hello); - - WaitForIdle(); // Because closing ports may have generated tasks. - } - - EXPECT_TRUE(node0.node().CanShutdownCleanly()); - EXPECT_TRUE(node1.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, SendUninitialized) { - TestNode node(0); - AddNode(&node); - - PortRef x0; - EXPECT_EQ(OK, node.node().CreateUninitializedPort(&x0)); - EXPECT_EQ(ERROR_PORT_STATE_UNEXPECTED, node.SendStringMessage(x0, "oops")); - EXPECT_EQ(OK, node.node().ClosePort(x0)); - EXPECT_TRUE(node.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, SendFailure) { - TestNode node(0); - AddNode(&node); - - node.set_save_messages(true); - - PortRef A, B; - EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B)); - - // Try to send A over itself. - - EXPECT_EQ(ERROR_PORT_CANNOT_SEND_SELF, - node.SendStringMessageWithPort(A, "oops", A)); - - // Try to send B over A. - - EXPECT_EQ(ERROR_PORT_CANNOT_SEND_PEER, - node.SendStringMessageWithPort(A, "nope", B)); - - // B should be closed immediately. - EXPECT_EQ(ERROR_PORT_UNKNOWN, node.node().GetPort(B.name(), &B)); - - WaitForIdle(); - - // There should have been no messages accepted. - ScopedMessage message; - EXPECT_FALSE(node.GetSavedMessage(&message)); - - EXPECT_EQ(OK, node.node().ClosePort(A)); - - WaitForIdle(); - - EXPECT_TRUE(node.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, DontLeakUnreceivedPorts) { - TestNode node(0); - AddNode(&node); - - PortRef A, B, C, D; - EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B)); - EXPECT_EQ(OK, node.node().CreatePortPair(&C, &D)); - - EXPECT_EQ(OK, node.SendStringMessageWithPort(A, "foo", D)); - - EXPECT_EQ(OK, node.node().ClosePort(C)); - EXPECT_EQ(OK, node.node().ClosePort(A)); - EXPECT_EQ(OK, node.node().ClosePort(B)); - - WaitForIdle(); - - EXPECT_TRUE(node.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, AllowShutdownWithLocalPortsOpen) { - TestNode node(0); - AddNode(&node); - - PortRef A, B, C, D; - EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B)); - EXPECT_EQ(OK, node.node().CreatePortPair(&C, &D)); - - EXPECT_EQ(OK, node.SendStringMessageWithPort(A, "foo", D)); - - ScopedMessage message; - EXPECT_TRUE(node.ReadMessage(B, &message)); - ASSERT_EQ(1u, message->num_ports()); - EXPECT_TRUE(MessageEquals(message, "foo")); - PortRef E; - ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &E)); - - EXPECT_TRUE( - node.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS)); - - WaitForIdle(); - - EXPECT_TRUE( - node.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS)); - EXPECT_FALSE(node.node().CanShutdownCleanly()); - - EXPECT_EQ(OK, node.node().ClosePort(A)); - EXPECT_EQ(OK, node.node().ClosePort(B)); - EXPECT_EQ(OK, node.node().ClosePort(C)); - EXPECT_EQ(OK, node.node().ClosePort(E)); - - WaitForIdle(); - - EXPECT_TRUE(node.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, ProxyCollapse1) { - TestNode node(0); - AddNode(&node); - - PortRef A, B; - EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B)); - - PortRef X, Y; - EXPECT_EQ(OK, node.node().CreatePortPair(&X, &Y)); - - ScopedMessage message; - - // Send B and receive it as C. - EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", B)); - ASSERT_TRUE(node.ReadMessage(Y, &message)); - ASSERT_EQ(1u, message->num_ports()); - PortRef C; - ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &C)); - - // Send C and receive it as D. - EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", C)); - ASSERT_TRUE(node.ReadMessage(Y, &message)); - ASSERT_EQ(1u, message->num_ports()); - PortRef D; - ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &D)); - - // Send D and receive it as E. - EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", D)); - ASSERT_TRUE(node.ReadMessage(Y, &message)); - ASSERT_EQ(1u, message->num_ports()); - PortRef E; - ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &E)); - - EXPECT_EQ(OK, node.node().ClosePort(X)); - EXPECT_EQ(OK, node.node().ClosePort(Y)); - - EXPECT_EQ(OK, node.node().ClosePort(A)); - EXPECT_EQ(OK, node.node().ClosePort(E)); - - // The node should not idle until all proxies are collapsed. - WaitForIdle(); - - EXPECT_TRUE(node.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, ProxyCollapse2) { - TestNode node(0); - AddNode(&node); - - PortRef A, B; - EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B)); - - PortRef X, Y; - EXPECT_EQ(OK, node.node().CreatePortPair(&X, &Y)); - - ScopedMessage message; - - // Send B and A to create proxies in each direction. - EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", B)); - EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", A)); - - EXPECT_EQ(OK, node.node().ClosePort(X)); - EXPECT_EQ(OK, node.node().ClosePort(Y)); - - // At this point we have a scenario with: - // - // D -> [B] -> C -> [A] - // - // Ensure that the proxies can collapse. The sent ports will be closed - // eventually as a result of Y's closure. - - WaitForIdle(); - - EXPECT_TRUE(node.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, SendWithClosedPeer) { - // This tests that if a port is sent when its peer is already known to be - // closed, the newly created port will be aware of that peer closure, and the - // proxy will eventually collapse. - - TestNode node(0); - AddNode(&node); - - // Send a message from A to B, then close A. - PortRef A, B; - EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B)); - EXPECT_EQ(OK, node.SendStringMessage(A, "hey")); - EXPECT_EQ(OK, node.node().ClosePort(A)); - - // Now send B over X-Y as new port C. - PortRef X, Y; - EXPECT_EQ(OK, node.node().CreatePortPair(&X, &Y)); - EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", B)); - ScopedMessage message; - ASSERT_TRUE(node.ReadMessage(Y, &message)); - ASSERT_EQ(1u, message->num_ports()); - PortRef C; - ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &C)); - - EXPECT_EQ(OK, node.node().ClosePort(X)); - EXPECT_EQ(OK, node.node().ClosePort(Y)); - - WaitForIdle(); - - // C should have received the message originally sent to B, and it should also - // be aware of A's closure. - - ASSERT_TRUE(node.ReadMessage(C, &message)); - EXPECT_TRUE(MessageEquals(message, "hey")); - - PortStatus status; - EXPECT_EQ(OK, node.node().GetStatus(C, &status)); - EXPECT_FALSE(status.receiving_messages); - EXPECT_FALSE(status.has_messages); - EXPECT_TRUE(status.peer_closed); - - node.node().ClosePort(C); - - WaitForIdle(); - - EXPECT_TRUE(node.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, SendWithClosedPeerSent) { - // This tests that if a port is closed while some number of proxies are still - // routing messages (directly or indirectly) to it, that the peer port is - // eventually notified of the closure, and the dead-end proxies will - // eventually be removed. - - TestNode node(0); - AddNode(&node); - - PortRef X, Y; - EXPECT_EQ(OK, node.node().CreatePortPair(&X, &Y)); - - PortRef A, B; - EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B)); - - ScopedMessage message; - - // Send A as new port C. - EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", A)); - - ASSERT_TRUE(node.ReadMessage(Y, &message)); - ASSERT_EQ(1u, message->num_ports()); - PortRef C; - ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &C)); - - // Send C as new port D. - EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", C)); - - ASSERT_TRUE(node.ReadMessage(Y, &message)); - ASSERT_EQ(1u, message->num_ports()); - PortRef D; - ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &D)); - - // Send a message to B through D, then close D. - EXPECT_EQ(OK, node.SendStringMessage(D, "hey")); - EXPECT_EQ(OK, node.node().ClosePort(D)); - - // Now send B as new port E. - - EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", B)); - EXPECT_EQ(OK, node.node().ClosePort(X)); - - ASSERT_TRUE(node.ReadMessage(Y, &message)); - ASSERT_EQ(1u, message->num_ports()); - PortRef E; - ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &E)); - - EXPECT_EQ(OK, node.node().ClosePort(Y)); - - WaitForIdle(); - - // E should receive the message originally sent to B, and it should also be - // aware of D's closure. - - ASSERT_TRUE(node.ReadMessage(E, &message)); - EXPECT_TRUE(MessageEquals(message, "hey")); - - PortStatus status; - EXPECT_EQ(OK, node.node().GetStatus(E, &status)); - EXPECT_FALSE(status.receiving_messages); - EXPECT_FALSE(status.has_messages); - EXPECT_TRUE(status.peer_closed); - - EXPECT_EQ(OK, node.node().ClosePort(E)); - - WaitForIdle(); - - EXPECT_TRUE(node.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, MergePorts) { - TestNode node0(0); - AddNode(&node0); - - TestNode node1(1); - AddNode(&node1); - - // Setup two independent port pairs, A-B on node0 and C-D on node1. - PortRef A, B, C, D; - EXPECT_EQ(OK, node0.node().CreatePortPair(&A, &B)); - EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D)); - - // Write a message on A. - EXPECT_EQ(OK, node0.SendStringMessage(A, "hey")); - - // Initiate a merge between B and C. - EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name())); - - WaitForIdle(); - - // Expect all proxies to be gone once idle. - EXPECT_TRUE( - node0.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS)); - EXPECT_TRUE( - node1.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS)); - - // Expect D to have received the message sent on A. - ScopedMessage message; - ASSERT_TRUE(node1.ReadMessage(D, &message)); - EXPECT_TRUE(MessageEquals(message, "hey")); - - EXPECT_EQ(OK, node0.node().ClosePort(A)); - EXPECT_EQ(OK, node1.node().ClosePort(D)); - - // No more ports should be open. - EXPECT_TRUE(node0.node().CanShutdownCleanly()); - EXPECT_TRUE(node1.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, MergePortWithClosedPeer1) { - // This tests that the right thing happens when initiating a merge on a port - // whose peer has already been closed. - - TestNode node0(0); - AddNode(&node0); - - TestNode node1(1); - AddNode(&node1); - - // Setup two independent port pairs, A-B on node0 and C-D on node1. - PortRef A, B, C, D; - EXPECT_EQ(OK, node0.node().CreatePortPair(&A, &B)); - EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D)); - - // Write a message on A. - EXPECT_EQ(OK, node0.SendStringMessage(A, "hey")); - - // Close A. - EXPECT_EQ(OK, node0.node().ClosePort(A)); - - // Initiate a merge between B and C. - EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name())); - - WaitForIdle(); - - // Expect all proxies to be gone once idle. node0 should have no ports since - // A was explicitly closed. - EXPECT_TRUE(node0.node().CanShutdownCleanly()); - EXPECT_TRUE( - node1.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS)); - - // Expect D to have received the message sent on A. - ScopedMessage message; - ASSERT_TRUE(node1.ReadMessage(D, &message)); - EXPECT_TRUE(MessageEquals(message, "hey")); - - EXPECT_EQ(OK, node1.node().ClosePort(D)); - - // No more ports should be open. - EXPECT_TRUE(node0.node().CanShutdownCleanly()); - EXPECT_TRUE(node1.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, MergePortWithClosedPeer2) { - // This tests that the right thing happens when merging into a port whose peer - // has already been closed. - - TestNode node0(0); - AddNode(&node0); - - TestNode node1(1); - AddNode(&node1); - - // Setup two independent port pairs, A-B on node0 and C-D on node1. - PortRef A, B, C, D; - EXPECT_EQ(OK, node0.node().CreatePortPair(&A, &B)); - EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D)); - - // Write a message on D and close it. - EXPECT_EQ(OK, node0.SendStringMessage(D, "hey")); - EXPECT_EQ(OK, node1.node().ClosePort(D)); - - // Initiate a merge between B and C. - EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name())); - - WaitForIdle(); - - // Expect all proxies to be gone once idle. node1 should have no ports since - // D was explicitly closed. - EXPECT_TRUE( - node0.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS)); - EXPECT_TRUE(node1.node().CanShutdownCleanly()); - - // Expect A to have received the message sent on D. - ScopedMessage message; - ASSERT_TRUE(node0.ReadMessage(A, &message)); - EXPECT_TRUE(MessageEquals(message, "hey")); - - EXPECT_EQ(OK, node0.node().ClosePort(A)); - - // No more ports should be open. - EXPECT_TRUE(node0.node().CanShutdownCleanly()); - EXPECT_TRUE(node1.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, MergePortsWithClosedPeers) { - // This tests that no residual ports are left behind if two ports are merged - // when both of their peers have been closed. - - TestNode node0(0); - AddNode(&node0); - - TestNode node1(1); - AddNode(&node1); - - // Setup two independent port pairs, A-B on node0 and C-D on node1. - PortRef A, B, C, D; - EXPECT_EQ(OK, node0.node().CreatePortPair(&A, &B)); - EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D)); - - // Close A and D. - EXPECT_EQ(OK, node0.node().ClosePort(A)); - EXPECT_EQ(OK, node1.node().ClosePort(D)); - - WaitForIdle(); - - // Initiate a merge between B and C. - EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name())); - - WaitForIdle(); - - // Expect everything to have gone away. - EXPECT_TRUE(node0.node().CanShutdownCleanly()); - EXPECT_TRUE(node1.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, MergePortsWithMovedPeers) { - // This tests that ports can be merged successfully even if their peers are - // moved around. - - TestNode node0(0); - AddNode(&node0); - - TestNode node1(1); - AddNode(&node1); - - // Setup two independent port pairs, A-B on node0 and C-D on node1. - PortRef A, B, C, D; - EXPECT_EQ(OK, node0.node().CreatePortPair(&A, &B)); - EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D)); - - // Set up another pair X-Y for moving ports on node0. - PortRef X, Y; - EXPECT_EQ(OK, node0.node().CreatePortPair(&X, &Y)); - - ScopedMessage message; - - // Move A to new port E. - EXPECT_EQ(OK, node0.SendStringMessageWithPort(X, "foo", A)); - ASSERT_TRUE(node0.ReadMessage(Y, &message)); - ASSERT_EQ(1u, message->num_ports()); - PortRef E; - ASSERT_EQ(OK, node0.node().GetPort(message->ports()[0], &E)); - - EXPECT_EQ(OK, node0.node().ClosePort(X)); - EXPECT_EQ(OK, node0.node().ClosePort(Y)); - - // Write messages on E and D. - EXPECT_EQ(OK, node0.SendStringMessage(E, "hey")); - EXPECT_EQ(OK, node1.SendStringMessage(D, "hi")); - - // Initiate a merge between B and C. - EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name())); - - WaitForIdle(); - - // Expect to receive D's message on E and E's message on D. - ASSERT_TRUE(node0.ReadMessage(E, &message)); - EXPECT_TRUE(MessageEquals(message, "hi")); - ASSERT_TRUE(node1.ReadMessage(D, &message)); - EXPECT_TRUE(MessageEquals(message, "hey")); - - // Close E and D. - EXPECT_EQ(OK, node0.node().ClosePort(E)); - EXPECT_EQ(OK, node1.node().ClosePort(D)); - - WaitForIdle(); - - // Expect everything to have gone away. - EXPECT_TRUE(node0.node().CanShutdownCleanly()); - EXPECT_TRUE(node1.node().CanShutdownCleanly()); -} - -TEST_F(PortsTest, MergePortsFailsGracefully) { - // This tests that the system remains in a well-defined state if something - // goes wrong during port merge. - - TestNode node0(0); - AddNode(&node0); - - TestNode node1(1); - AddNode(&node1); - - // Setup two independent port pairs, A-B on node0 and C-D on node1. - PortRef A, B, C, D; - EXPECT_EQ(OK, node0.node().CreatePortPair(&A, &B)); - EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D)); - - ScopedMessage message; - PortRef X, Y; - EXPECT_EQ(OK, node1.node().CreatePortPair(&X, &Y)); - - // Block the merge from proceeding until we can do something stupid with port - // C. This avoids the test logic racing with async merge logic. - node1.BlockOnEvent(EventType::kMergePort); - - // Initiate the merge between B and C. - EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name())); - - // Move C to a new port E. This is not a sane use of Node's public API but - // is still hypothetically possible. It allows us to force a merge failure - // because C will be in an invalid state by the term the merge is processed. - // As a result, B should be closed. - EXPECT_EQ(OK, node1.SendStringMessageWithPort(X, "foo", C)); - - node1.Unblock(); - - ASSERT_TRUE(node1.ReadMessage(Y, &message)); - ASSERT_EQ(1u, message->num_ports()); - PortRef E; - ASSERT_EQ(OK, node1.node().GetPort(message->ports()[0], &E)); - - EXPECT_EQ(OK, node1.node().ClosePort(X)); - EXPECT_EQ(OK, node1.node().ClosePort(Y)); - - WaitForIdle(); - - // C goes away as a result of normal proxy removal. B should have been closed - // cleanly by the failed MergePorts. - EXPECT_EQ(ERROR_PORT_UNKNOWN, node1.node().GetPort(C.name(), &C)); - EXPECT_EQ(ERROR_PORT_UNKNOWN, node0.node().GetPort(B.name(), &B)); - - // Close A, D, and E. - EXPECT_EQ(OK, node0.node().ClosePort(A)); - EXPECT_EQ(OK, node1.node().ClosePort(D)); - EXPECT_EQ(OK, node1.node().ClosePort(E)); - - WaitForIdle(); - - // Expect everything to have gone away. - EXPECT_TRUE(node0.node().CanShutdownCleanly()); - EXPECT_TRUE(node1.node().CanShutdownCleanly()); -} - -} // namespace test -} // namespace ports -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/ports/user_data.h b/mojo/edk/system/ports/user_data.h deleted file mode 100644 index 73e7d17..0000000 --- a/mojo/edk/system/ports/user_data.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_PORTS_USER_DATA_H_ -#define MOJO_EDK_SYSTEM_PORTS_USER_DATA_H_ - -#include "base/memory/ref_counted.h" - -namespace mojo { -namespace edk { -namespace ports { - -class UserData : public base::RefCountedThreadSafe<UserData> { - protected: - friend class base::RefCountedThreadSafe<UserData>; - - virtual ~UserData() {} -}; - -} // namespace ports -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_PORTS_USER_DATA_H_ diff --git a/mojo/edk/system/ports_message.cc b/mojo/edk/system/ports_message.cc deleted file mode 100644 index 5f3e8c0..0000000 --- a/mojo/edk/system/ports_message.cc +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/ports_message.h" - -#include "base/memory/ptr_util.h" -#include "mojo/edk/system/node_channel.h" - -namespace mojo { -namespace edk { - -// static -std::unique_ptr<PortsMessage> PortsMessage::NewUserMessage( - size_t num_payload_bytes, - size_t num_ports, - size_t num_handles) { - return base::WrapUnique( - new PortsMessage(num_payload_bytes, num_ports, num_handles)); -} - -PortsMessage::~PortsMessage() {} - -PortsMessage::PortsMessage(size_t num_payload_bytes, - size_t num_ports, - size_t num_handles) - : ports::Message(num_payload_bytes, num_ports) { - size_t size = num_header_bytes_ + num_ports_bytes_ + num_payload_bytes; - void* ptr; - channel_message_ = NodeChannel::CreatePortsMessage(size, &ptr, num_handles); - InitializeUserMessageHeader(ptr); -} - -PortsMessage::PortsMessage(size_t num_header_bytes, - size_t num_payload_bytes, - size_t num_ports_bytes, - Channel::MessagePtr channel_message) - : ports::Message(num_header_bytes, - num_payload_bytes, - num_ports_bytes) { - if (channel_message) { - channel_message_ = std::move(channel_message); - void* data; - size_t num_data_bytes; - NodeChannel::GetPortsMessageData(channel_message_.get(), &data, - &num_data_bytes); - start_ = static_cast<char*>(data); - } else { - // TODO: Clean this up. In practice this branch of the constructor should - // only be reached from Node-internal calls to AllocMessage, which never - // carry ports or non-header bytes. - CHECK_EQ(num_payload_bytes, 0u); - CHECK_EQ(num_ports_bytes, 0u); - void* ptr; - channel_message_ = - NodeChannel::CreatePortsMessage(num_header_bytes, &ptr, 0); - start_ = static_cast<char*>(ptr); - } -} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/ports_message.h b/mojo/edk/system/ports_message.h deleted file mode 100644 index 542b981..0000000 --- a/mojo/edk/system/ports_message.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_PORTS_MESSAGE_H__ -#define MOJO_EDK_SYSTEM_PORTS_MESSAGE_H__ - -#include <memory> -#include <utility> - -#include "mojo/edk/embedder/platform_handle_vector.h" -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/ports/message.h" -#include "mojo/edk/system/ports/name.h" - -namespace mojo { -namespace edk { - -class NodeController; - -class PortsMessage : public ports::Message { - public: - static std::unique_ptr<PortsMessage> NewUserMessage(size_t num_payload_bytes, - size_t num_ports, - size_t num_handles); - - ~PortsMessage() override; - - size_t num_handles() const { return channel_message_->num_handles(); } - bool has_handles() const { return channel_message_->has_handles(); } - - void SetHandles(ScopedPlatformHandleVectorPtr handles) { - channel_message_->SetHandles(std::move(handles)); - } - - ScopedPlatformHandleVectorPtr TakeHandles() { - return channel_message_->TakeHandles(); - } - - Channel::MessagePtr TakeChannelMessage() { - return std::move(channel_message_); - } - - void set_source_node(const ports::NodeName& name) { source_node_ = name; } - const ports::NodeName& source_node() const { return source_node_; } - - private: - friend class NodeController; - - // Construct a new user PortsMessage backed by a new Channel::Message. - PortsMessage(size_t num_payload_bytes, size_t num_ports, size_t num_handles); - - // Construct a new PortsMessage backed by a Channel::Message. If - // |channel_message| is null, a new one is allocated internally. - PortsMessage(size_t num_header_bytes, - size_t num_payload_bytes, - size_t num_ports_bytes, - Channel::MessagePtr channel_message); - - Channel::MessagePtr channel_message_; - - // The node name from which this message was received, if known. - ports::NodeName source_node_ = ports::kInvalidNodeName; -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_PORTS_MESSAGE_H__ diff --git a/mojo/edk/system/request_context.cc b/mojo/edk/system/request_context.cc deleted file mode 100644 index 5de65d7..0000000 --- a/mojo/edk/system/request_context.cc +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/request_context.h" - -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/threading/thread_local.h" - -namespace mojo { -namespace edk { - -namespace { - -base::LazyInstance<base::ThreadLocalPointer<RequestContext>>::Leaky - g_current_context = LAZY_INSTANCE_INITIALIZER; - -} // namespace - -RequestContext::RequestContext() : RequestContext(Source::LOCAL_API_CALL) {} - -RequestContext::RequestContext(Source source) - : source_(source), tls_context_(g_current_context.Pointer()) { - // We allow nested RequestContexts to exist as long as they aren't actually - // used for anything. - if (!tls_context_->Get()) - tls_context_->Set(this); -} - -RequestContext::~RequestContext() { - if (IsCurrent()) { - // NOTE: Callbacks invoked by this destructor are allowed to initiate new - // EDK requests on this thread, so we need to reset the thread-local context - // pointer before calling them. We persist the original notification source - // since we're starting over at the bottom of the stack. - tls_context_->Set(nullptr); - - MojoWatcherNotificationFlags flags = MOJO_WATCHER_NOTIFICATION_FLAG_NONE; - if (source_ == Source::SYSTEM) - flags |= MOJO_WATCHER_NOTIFICATION_FLAG_FROM_SYSTEM; - - // We send all cancellation notifications first. This is necessary because - // it's possible that cancelled watches have other pending notifications - // attached to this RequestContext. - // - // From the application's perspective the watch is cancelled as soon as this - // notification is received, and dispatching the cancellation notification - // updates some internal Watch state to ensure no further notifications - // fire. Because notifications on a single Watch are mutually exclusive, - // this is sufficient to guarantee that MOJO_RESULT_CANCELLED is the last - // notification received; which is the guarantee the API makes. - for (const scoped_refptr<Watch>& watch : - watch_cancel_finalizers_.container()) { - static const HandleSignalsState closed_state = {0, 0}; - - // Establish a new RequestContext to capture and run any new notifications - // triggered by the callback invocation. - RequestContext inner_context(source_); - watch->InvokeCallback(MOJO_RESULT_CANCELLED, closed_state, flags); - } - - for (const WatchNotifyFinalizer& watch : - watch_notify_finalizers_.container()) { - RequestContext inner_context(source_); - watch.watch->InvokeCallback(watch.result, watch.state, flags); - } - } else { - // It should be impossible for nested contexts to have finalizers. - DCHECK(watch_notify_finalizers_.container().empty()); - DCHECK(watch_cancel_finalizers_.container().empty()); - } -} - -// static -RequestContext* RequestContext::current() { - DCHECK(g_current_context.Pointer()->Get()); - return g_current_context.Pointer()->Get(); -} - -void RequestContext::AddWatchNotifyFinalizer(scoped_refptr<Watch> watch, - MojoResult result, - const HandleSignalsState& state) { - DCHECK(IsCurrent()); - watch_notify_finalizers_->push_back( - WatchNotifyFinalizer(std::move(watch), result, state)); -} - -void RequestContext::AddWatchCancelFinalizer(scoped_refptr<Watch> watch) { - DCHECK(IsCurrent()); - watch_cancel_finalizers_->push_back(std::move(watch)); -} - -bool RequestContext::IsCurrent() const { - return tls_context_->Get() == this; -} - -RequestContext::WatchNotifyFinalizer::WatchNotifyFinalizer( - scoped_refptr<Watch> watch, - MojoResult result, - const HandleSignalsState& state) - : watch(std::move(watch)), result(result), state(state) {} - -RequestContext::WatchNotifyFinalizer::WatchNotifyFinalizer( - const WatchNotifyFinalizer& other) = default; - -RequestContext::WatchNotifyFinalizer::~WatchNotifyFinalizer() {} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/request_context.h b/mojo/edk/system/request_context.h deleted file mode 100644 index d1f43bd..0000000 --- a/mojo/edk/system/request_context.h +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_REQUEST_CONTEXT_H_ -#define MOJO_EDK_SYSTEM_REQUEST_CONTEXT_H_ - -#include "base/containers/stack_container.h" -#include "base/macros.h" -#include "mojo/edk/system/handle_signals_state.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/edk/system/watch.h" - -namespace base { -template<typename T> class ThreadLocalPointer; -} - -namespace mojo { -namespace edk { - -// A RequestContext is a thread-local object which exists for the duration of -// a single system API call. It is constructed immediately upon EDK entry and -// destructed immediately before returning to the caller, after any internal -// locks have been released. -// -// NOTE: It is legal to construct a RequestContext while another one already -// exists on the current thread, but it is not safe to use the nested context -// for any reason. Therefore it is important to always use -// |RequestContext::current()| rather than referring to any local instance -// directly. -class MOJO_SYSTEM_IMPL_EXPORT RequestContext { - public: - // Identifies the source of the current stack frame's RequestContext. - enum class Source { - LOCAL_API_CALL, - SYSTEM, - }; - - // Constructs a RequestContext with a LOCAL_API_CALL Source. - RequestContext(); - - explicit RequestContext(Source source); - ~RequestContext(); - - // Returns the current thread-local RequestContext. - static RequestContext* current(); - - Source source() const { return source_; } - - // Adds a finalizer to this RequestContext corresponding to a watch callback - // which should be triggered in response to some handle state change. If - // the WatcherDispatcher hasn't been closed by the time this RequestContext is - // destroyed, its WatchCallback will be invoked with |result| and |state| - // arguments. - void AddWatchNotifyFinalizer(scoped_refptr<Watch> watch, - MojoResult result, - const HandleSignalsState& state); - - // Adds a finalizer to this RequestContext corresponding to a watch callback - // which should be triggered to notify of watch cancellation. This appends to - // a separate finalizer list from AddWatchNotifyFinalizer, as pending - // cancellations must always preempt other pending notifications. - void AddWatchCancelFinalizer(scoped_refptr<Watch> watch); - - private: - // Is this request context the current one? - bool IsCurrent() const; - - struct WatchNotifyFinalizer { - WatchNotifyFinalizer(scoped_refptr<Watch> watch, - MojoResult result, - const HandleSignalsState& state); - WatchNotifyFinalizer(const WatchNotifyFinalizer& other); - ~WatchNotifyFinalizer(); - - scoped_refptr<Watch> watch; - MojoResult result; - HandleSignalsState state; - }; - - // NOTE: This upper bound was chosen somewhat arbitrarily after observing some - // rare worst-case behavior in Chrome. A vast majority of RequestContexts only - // ever accumulate 0 or 1 finalizers. - static const size_t kStaticWatchFinalizersCapacity = 8; - - using WatchNotifyFinalizerList = - base::StackVector<WatchNotifyFinalizer, kStaticWatchFinalizersCapacity>; - using WatchCancelFinalizerList = - base::StackVector<scoped_refptr<Watch>, kStaticWatchFinalizersCapacity>; - - const Source source_; - - WatchNotifyFinalizerList watch_notify_finalizers_; - WatchCancelFinalizerList watch_cancel_finalizers_; - - // Pointer to the TLS context. Although this can easily be accessed via the - // global LazyInstance, accessing a LazyInstance has a large cost relative to - // the rest of this class and its usages. - base::ThreadLocalPointer<RequestContext>* tls_context_; - - DISALLOW_COPY_AND_ASSIGN(RequestContext); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_REQUEST_CONTEXT_H_ diff --git a/mojo/edk/system/shared_buffer_dispatcher.cc b/mojo/edk/system/shared_buffer_dispatcher.cc deleted file mode 100644 index df39105..0000000 --- a/mojo/edk/system/shared_buffer_dispatcher.cc +++ /dev/null @@ -1,339 +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 <stddef.h> -#include <stdint.h> - -#include <limits> -#include <memory> -#include <utility> - -#include "base/logging.h" -#include "mojo/edk/embedder/embedder_internal.h" -#include "mojo/edk/system/configuration.h" -#include "mojo/edk/system/node_controller.h" -#include "mojo/edk/system/options_validation.h" - -namespace mojo { -namespace edk { - -namespace { - -#pragma pack(push, 1) - -struct SerializedState { - uint64_t num_bytes; - uint32_t flags; - uint32_t padding; -}; - -const uint32_t kSerializedStateFlagsReadOnly = 1 << 0; - -#pragma pack(pop) - -static_assert(sizeof(SerializedState) % 8 == 0, - "Invalid SerializedState size."); - -} // namespace - -// static -const MojoCreateSharedBufferOptions - SharedBufferDispatcher::kDefaultCreateOptions = { - static_cast<uint32_t>(sizeof(MojoCreateSharedBufferOptions)), - MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE}; - -// static -MojoResult SharedBufferDispatcher::ValidateCreateOptions( - const MojoCreateSharedBufferOptions* in_options, - MojoCreateSharedBufferOptions* out_options) { - const MojoCreateSharedBufferOptionsFlags kKnownFlags = - MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE; - - *out_options = kDefaultCreateOptions; - if (!in_options) - 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( - const MojoCreateSharedBufferOptions& /*validated_options*/, - NodeController* node_controller, - 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<PlatformSharedBuffer> shared_buffer; - if (node_controller) { - shared_buffer = - node_controller->CreateSharedBuffer(static_cast<size_t>(num_bytes)); - } else { - shared_buffer = - PlatformSharedBuffer::Create(static_cast<size_t>(num_bytes)); - } - if (!shared_buffer) - return MOJO_RESULT_RESOURCE_EXHAUSTED; - - *result = CreateInternal(std::move(shared_buffer)); - return MOJO_RESULT_OK; -} - -// static -MojoResult SharedBufferDispatcher::CreateFromPlatformSharedBuffer( - const scoped_refptr<PlatformSharedBuffer>& shared_buffer, - scoped_refptr<SharedBufferDispatcher>* result) { - if (!shared_buffer) - return MOJO_RESULT_INVALID_ARGUMENT; - - *result = CreateInternal(shared_buffer); - return MOJO_RESULT_OK; -} - -// static -scoped_refptr<SharedBufferDispatcher> SharedBufferDispatcher::Deserialize( - const void* bytes, - size_t num_bytes, - const ports::PortName* ports, - size_t num_ports, - PlatformHandle* platform_handles, - size_t num_platform_handles) { - if (num_bytes != sizeof(SerializedState)) { - LOG(ERROR) << "Invalid serialized shared buffer dispatcher (bad size)"; - return nullptr; - } - - const SerializedState* serialization = - static_cast<const SerializedState*>(bytes); - if (!serialization->num_bytes) { - LOG(ERROR) - << "Invalid serialized shared buffer dispatcher (invalid num_bytes)"; - return nullptr; - } - - if (!platform_handles || num_platform_handles != 1 || num_ports) { - LOG(ERROR) - << "Invalid serialized shared buffer dispatcher (missing handles)"; - return nullptr; - } - - // Starts off invalid, which is what we want. - 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); - - // Wrapping |platform_handle| in a |ScopedPlatformHandle| means that it'll be - // closed even if creation fails. - bool read_only = (serialization->flags & kSerializedStateFlagsReadOnly); - scoped_refptr<PlatformSharedBuffer> shared_buffer( - PlatformSharedBuffer::CreateFromPlatformHandle( - static_cast<size_t>(serialization->num_bytes), read_only, - ScopedPlatformHandle(platform_handle))); - if (!shared_buffer) { - LOG(ERROR) - << "Invalid serialized shared buffer dispatcher (invalid num_bytes?)"; - return nullptr; - } - - return CreateInternal(std::move(shared_buffer)); -} - -scoped_refptr<PlatformSharedBuffer> -SharedBufferDispatcher::PassPlatformSharedBuffer() { - base::AutoLock lock(lock_); - if (!shared_buffer_ || in_transit_) - return nullptr; - - scoped_refptr<PlatformSharedBuffer> retval = shared_buffer_; - shared_buffer_ = nullptr; - return retval; -} - -Dispatcher::Type SharedBufferDispatcher::GetType() const { - return Type::SHARED_BUFFER; -} - -MojoResult SharedBufferDispatcher::Close() { - base::AutoLock lock(lock_); - if (in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - - shared_buffer_ = nullptr; - return MOJO_RESULT_OK; -} - -MojoResult SharedBufferDispatcher::DuplicateBufferHandle( - const MojoDuplicateBufferHandleOptions* options, - scoped_refptr<Dispatcher>* new_dispatcher) { - MojoDuplicateBufferHandleOptions validated_options; - MojoResult result = ValidateDuplicateOptions(options, &validated_options); - if (result != MOJO_RESULT_OK) - return result; - - // Note: Since this is "duplicate", we keep our ref to |shared_buffer_|. - base::AutoLock lock(lock_); - if (in_transit_) - return MOJO_RESULT_INVALID_ARGUMENT; - - if ((validated_options.flags & - MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY) && - (!shared_buffer_->IsReadOnly())) { - // If a read-only duplicate is requested and |shared_buffer_| is not - // read-only, make a read-only duplicate of |shared_buffer_|. - scoped_refptr<PlatformSharedBuffer> read_only_buffer = - shared_buffer_->CreateReadOnlyDuplicate(); - if (!read_only_buffer) - return MOJO_RESULT_FAILED_PRECONDITION; - DCHECK(read_only_buffer->IsReadOnly()); - *new_dispatcher = CreateInternal(std::move(read_only_buffer)); - return MOJO_RESULT_OK; - } - - *new_dispatcher = CreateInternal(shared_buffer_); - return MOJO_RESULT_OK; -} - -MojoResult SharedBufferDispatcher::MapBuffer( - uint64_t offset, - uint64_t num_bytes, - MojoMapBufferFlags flags, - std::unique_ptr<PlatformSharedBufferMapping>* mapping) { - 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; - - base::AutoLock lock(lock_); - DCHECK(shared_buffer_); - if (in_transit_ || - !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) { - LOG(ERROR) << "Unable to map: read_only" << shared_buffer_->IsReadOnly(); - return MOJO_RESULT_RESOURCE_EXHAUSTED; - } - - return MOJO_RESULT_OK; -} - -void SharedBufferDispatcher::StartSerialize(uint32_t* num_bytes, - uint32_t* num_ports, - uint32_t* num_platform_handles) { - *num_bytes = sizeof(SerializedState); - *num_ports = 0; - *num_platform_handles = 1; -} - -bool SharedBufferDispatcher::EndSerialize(void* destination, - ports::PortName* ports, - PlatformHandle* handles) { - SerializedState* serialization = - static_cast<SerializedState*>(destination); - base::AutoLock lock(lock_); - serialization->num_bytes = - static_cast<uint64_t>(shared_buffer_->GetNumBytes()); - serialization->flags = - (shared_buffer_->IsReadOnly() ? kSerializedStateFlagsReadOnly : 0); - serialization->padding = 0; - - handle_for_transit_ = shared_buffer_->DuplicatePlatformHandle(); - if (!handle_for_transit_.is_valid()) { - shared_buffer_ = nullptr; - return false; - } - handles[0] = handle_for_transit_.get(); - return true; -} - -bool SharedBufferDispatcher::BeginTransit() { - base::AutoLock lock(lock_); - if (in_transit_) - return false; - in_transit_ = static_cast<bool>(shared_buffer_); - return in_transit_; -} - -void SharedBufferDispatcher::CompleteTransitAndClose() { - base::AutoLock lock(lock_); - in_transit_ = false; - shared_buffer_ = nullptr; - ignore_result(handle_for_transit_.release()); -} - -void SharedBufferDispatcher::CancelTransit() { - base::AutoLock lock(lock_); - in_transit_ = false; - handle_for_transit_.reset(); -} - -SharedBufferDispatcher::SharedBufferDispatcher( - scoped_refptr<PlatformSharedBuffer> shared_buffer) - : shared_buffer_(shared_buffer) { - DCHECK(shared_buffer_); -} - -SharedBufferDispatcher::~SharedBufferDispatcher() { - DCHECK(!shared_buffer_ && !in_transit_); -} - -// static -MojoResult SharedBufferDispatcher::ValidateDuplicateOptions( - const MojoDuplicateBufferHandleOptions* in_options, - MojoDuplicateBufferHandleOptions* out_options) { - const MojoDuplicateBufferHandleOptionsFlags kKnownFlags = - MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY; - static const MojoDuplicateBufferHandleOptions kDefaultOptions = { - static_cast<uint32_t>(sizeof(MojoDuplicateBufferHandleOptions)), - MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE}; - - *out_options = kDefaultOptions; - if (!in_options) - 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; -} - -} // namespace edk -} // 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 6015595..0000000 --- a/mojo/edk/system/shared_buffer_dispatcher.h +++ /dev/null @@ -1,127 +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 <stddef.h> -#include <stdint.h> - -#include <utility> - -#include "base/macros.h" -#include "mojo/edk/embedder/platform_handle_vector.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { - -namespace edk { -class NodeController; - -class MOJO_SYSTEM_IMPL_EXPORT SharedBufferDispatcher final : public Dispatcher { - 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( - const MojoCreateSharedBufferOptions* in_options, - MojoCreateSharedBufferOptions* out_options); - - // Static factory method: |validated_options| must be validated (obviously). - // On failure, |*result| will be left as-is. - // TODO(vtl): This should probably be made to return a scoped_refptr and have - // a MojoResult out parameter instead. - static MojoResult Create( - const MojoCreateSharedBufferOptions& validated_options, - NodeController* node_controller, - uint64_t num_bytes, - scoped_refptr<SharedBufferDispatcher>* result); - - // Create a |SharedBufferDispatcher| from |shared_buffer|. - static MojoResult CreateFromPlatformSharedBuffer( - const scoped_refptr<PlatformSharedBuffer>& shared_buffer, - scoped_refptr<SharedBufferDispatcher>* result); - - // The "opposite" of SerializeAndClose(). Called by Dispatcher::Deserialize(). - static scoped_refptr<SharedBufferDispatcher> Deserialize( - const void* bytes, - size_t num_bytes, - const ports::PortName* ports, - size_t num_ports, - PlatformHandle* platform_handles, - size_t num_platform_handles); - - // Passes the underlying platform shared buffer. This dispatcher must be - // closed after calling this function. - scoped_refptr<PlatformSharedBuffer> PassPlatformSharedBuffer(); - - // Dispatcher: - Type GetType() const override; - MojoResult Close() override; - MojoResult DuplicateBufferHandle( - const MojoDuplicateBufferHandleOptions* options, - scoped_refptr<Dispatcher>* new_dispatcher) override; - MojoResult MapBuffer( - uint64_t offset, - uint64_t num_bytes, - MojoMapBufferFlags flags, - std::unique_ptr<PlatformSharedBufferMapping>* mapping) override; - void StartSerialize(uint32_t* num_bytes, - uint32_t* num_ports, - uint32_t* num_platform_handles) override; - bool EndSerialize(void* destination, - ports::PortName* ports, - PlatformHandle* handles) override; - bool BeginTransit() override; - void CompleteTransitAndClose() override; - void CancelTransit() override; - - private: - static scoped_refptr<SharedBufferDispatcher> CreateInternal( - scoped_refptr<PlatformSharedBuffer> shared_buffer) { - return make_scoped_refptr( - new SharedBufferDispatcher(std::move(shared_buffer))); - } - - explicit SharedBufferDispatcher( - scoped_refptr<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( - const MojoDuplicateBufferHandleOptions* in_options, - MojoDuplicateBufferHandleOptions* out_options); - - // Guards access to |shared_buffer_|. - base::Lock lock_; - - bool in_transit_ = false; - - // We keep a copy of the buffer's platform handle during transit so we can - // close it if something goes wrong. - ScopedPlatformHandle handle_for_transit_; - - scoped_refptr<PlatformSharedBuffer> shared_buffer_; - - DISALLOW_COPY_AND_ASSIGN(SharedBufferDispatcher); -}; - -} // namespace edk -} // 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 c95bdc3..0000000 --- a/mojo/edk/system/shared_buffer_dispatcher_unittest.cc +++ /dev/null @@ -1,312 +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 <stddef.h> -#include <stdint.h> - -#include <limits> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "mojo/edk/embedder/platform_shared_buffer.h" -#include "mojo/edk/system/dispatcher.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace edk { -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( - &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 {} - - private: - 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( - nullptr, &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( - &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( - &options, &unused)); - } - - // Unknown |flags|. - { - MojoCreateSharedBufferOptions options = { - kSizeOfCreateOptions, // |struct_size|. - ~0u // |flags|. - }; - MojoCreateSharedBufferOptions unused; - EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, - SharedBufferDispatcher::ValidateCreateOptions( - &options, &unused)); - } -} - -TEST_F(SharedBufferDispatcherTest, CreateAndMapBuffer) { - scoped_refptr<SharedBufferDispatcher> dispatcher; - EXPECT_EQ(MOJO_RESULT_OK, SharedBufferDispatcher::Create( - SharedBufferDispatcher::kDefaultCreateOptions, - nullptr, 100, &dispatcher)); - ASSERT_TRUE(dispatcher); - EXPECT_EQ(Dispatcher::Type::SHARED_BUFFER, dispatcher->GetType()); - - // Make a couple of mappings. - std::unique_ptr<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'; - - std::unique_ptr<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, CreateAndMapBufferFromPlatformBuffer) { - scoped_refptr<PlatformSharedBuffer> platform_shared_buffer = - PlatformSharedBuffer::Create(100); - ASSERT_TRUE(platform_shared_buffer); - scoped_refptr<SharedBufferDispatcher> dispatcher; - EXPECT_EQ(MOJO_RESULT_OK, - SharedBufferDispatcher::CreateFromPlatformSharedBuffer( - platform_shared_buffer, &dispatcher)); - ASSERT_TRUE(dispatcher); - EXPECT_EQ(Dispatcher::Type::SHARED_BUFFER, dispatcher->GetType()); - - // Make a couple of mappings. - std::unique_ptr<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'; - - std::unique_ptr<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( - SharedBufferDispatcher::kDefaultCreateOptions, - nullptr, 100, &dispatcher1)); - - // Map and write something. - std::unique_ptr<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( - nullptr, &dispatcher2)); - ASSERT_TRUE(dispatcher2); - EXPECT_EQ(Dispatcher::Type::SHARED_BUFFER, 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( - SharedBufferDispatcher::kDefaultCreateOptions, - nullptr, 100, &dispatcher1)); - - MojoDuplicateBufferHandleOptions options[] = { - {sizeof(MojoDuplicateBufferHandleOptions), - MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE}, - {sizeof(MojoDuplicateBufferHandleOptions), - MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY}, - {sizeof(MojoDuplicateBufferHandleOptionsFlags), ~0u}}; - for (size_t i = 0; i < arraysize(options); i++) { - scoped_refptr<Dispatcher> dispatcher2; - EXPECT_EQ(MOJO_RESULT_OK, dispatcher1->DuplicateBufferHandle( - &options[i], &dispatcher2)); - ASSERT_TRUE(dispatcher2); - EXPECT_EQ(Dispatcher::Type::SHARED_BUFFER, dispatcher2->GetType()); - { - std::unique_ptr<PlatformSharedBufferMapping> mapping; - EXPECT_EQ(MOJO_RESULT_OK, dispatcher2->MapBuffer(0, 100, 0, &mapping)); - } - 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( - SharedBufferDispatcher::kDefaultCreateOptions, - nullptr, 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(&options, &dispatcher2)); - EXPECT_FALSE(dispatcher2); - } - - // Unknown |flags|. - { - MojoDuplicateBufferHandleOptions options = { - sizeof(MojoDuplicateBufferHandleOptions), ~0u}; - scoped_refptr<Dispatcher> dispatcher2; - EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, - dispatcher1->DuplicateBufferHandle(&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( - SharedBufferDispatcher::kDefaultCreateOptions, nullptr, - std::numeric_limits<uint64_t>::max(), &dispatcher)); - EXPECT_FALSE(dispatcher); - - // Zero size. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - SharedBufferDispatcher::Create( - SharedBufferDispatcher::kDefaultCreateOptions, nullptr, 0, - &dispatcher)); - EXPECT_FALSE(dispatcher); -} - -TEST_F(SharedBufferDispatcherTest, MapBufferInvalidArguments) { - scoped_refptr<SharedBufferDispatcher> dispatcher; - EXPECT_EQ(MOJO_RESULT_OK, SharedBufferDispatcher::Create( - SharedBufferDispatcher::kDefaultCreateOptions, - nullptr, 100, &dispatcher)); - - std::unique_ptr<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 edk -} // namespace mojo diff --git a/mojo/edk/system/shared_buffer_unittest.cc b/mojo/edk/system/shared_buffer_unittest.cc deleted file mode 100644 index 3a72872..0000000 --- a/mojo/edk/system/shared_buffer_unittest.cc +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <string.h> - -#include <string> -#include <utility> - -#include "base/logging.h" -#include "base/memory/shared_memory.h" -#include "base/strings/string_piece.h" -#include "mojo/edk/test/mojo_test_base.h" -#include "mojo/public/c/system/types.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace edk { -namespace { - -using SharedBufferTest = test::MojoTestBase; - -TEST_F(SharedBufferTest, CreateSharedBuffer) { - const std::string message = "hello"; - MojoHandle h = CreateBuffer(message.size()); - WriteToBuffer(h, 0, message); - ExpectBufferContents(h, 0, message); -} - -TEST_F(SharedBufferTest, DuplicateSharedBuffer) { - const std::string message = "hello"; - MojoHandle h = CreateBuffer(message.size()); - WriteToBuffer(h, 0, message); - - MojoHandle dupe = DuplicateBuffer(h, false); - ExpectBufferContents(dupe, 0, message); -} - -TEST_F(SharedBufferTest, PassSharedBufferLocal) { - const std::string message = "hello"; - MojoHandle h = CreateBuffer(message.size()); - WriteToBuffer(h, 0, message); - - MojoHandle dupe = DuplicateBuffer(h, false); - MojoHandle p0, p1; - CreateMessagePipe(&p0, &p1); - - WriteMessageWithHandles(p0, "...", &dupe, 1); - EXPECT_EQ("...", ReadMessageWithHandles(p1, &dupe, 1)); - - ExpectBufferContents(dupe, 0, message); -} - -#if !defined(OS_IOS) - -// Reads a single message with a shared buffer handle, maps the buffer, copies -// the message contents into it, then exits. -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(CopyToBufferClient, SharedBufferTest, h) { - MojoHandle b; - std::string message = ReadMessageWithHandles(h, &b, 1); - WriteToBuffer(b, 0, message); - - EXPECT_EQ("quit", ReadMessage(h)); -} - -TEST_F(SharedBufferTest, PassSharedBufferCrossProcess) { - const std::string message = "hello"; - MojoHandle b = CreateBuffer(message.size()); - - RUN_CHILD_ON_PIPE(CopyToBufferClient, h) - MojoHandle dupe = DuplicateBuffer(b, false); - WriteMessageWithHandles(h, message, &dupe, 1); - WriteMessage(h, "quit"); - END_CHILD() - - ExpectBufferContents(b, 0, message); -} - -// Creates a new buffer, maps it, writes a message contents to it, unmaps it, -// and finally passes it back to the parent. -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(CreateBufferClient, SharedBufferTest, h) { - std::string message = ReadMessage(h); - MojoHandle b = CreateBuffer(message.size()); - WriteToBuffer(b, 0, message); - WriteMessageWithHandles(h, "have a buffer", &b, 1); - - EXPECT_EQ("quit", ReadMessage(h)); -} - -TEST_F(SharedBufferTest, PassSharedBufferFromChild) { - const std::string message = "hello"; - MojoHandle b; - RUN_CHILD_ON_PIPE(CreateBufferClient, h) - WriteMessage(h, message); - ReadMessageWithHandles(h, &b, 1); - WriteMessage(h, "quit"); - END_CHILD() - - ExpectBufferContents(b, 0, message); -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(CreateAndPassBuffer, SharedBufferTest, h) { - // Receive a pipe handle over the primordial pipe. This will be connected to - // another child process. - MojoHandle other_child; - std::string message = ReadMessageWithHandles(h, &other_child, 1); - - // Create a new shared buffer. - MojoHandle b = CreateBuffer(message.size()); - - // Send a copy of the buffer to the parent and the other child. - MojoHandle dupe = DuplicateBuffer(b, false); - WriteMessageWithHandles(h, "", &b, 1); - WriteMessageWithHandles(other_child, "", &dupe, 1); - - EXPECT_EQ("quit", ReadMessage(h)); -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReceiveAndEditBuffer, SharedBufferTest, h) { - // Receive a pipe handle over the primordial pipe. This will be connected to - // another child process (running CreateAndPassBuffer). - MojoHandle other_child; - std::string message = ReadMessageWithHandles(h, &other_child, 1); - - // Receive a shared buffer from the other child. - MojoHandle b; - ReadMessageWithHandles(other_child, &b, 1); - - // Write the message from the parent into the buffer and exit. - WriteToBuffer(b, 0, message); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); - EXPECT_EQ("quit", ReadMessage(h)); -} - -TEST_F(SharedBufferTest, PassSharedBufferFromChildToChild) { - const std::string message = "hello"; - MojoHandle p0, p1; - CreateMessagePipe(&p0, &p1); - - MojoHandle b; - RUN_CHILD_ON_PIPE(CreateAndPassBuffer, h0) - RUN_CHILD_ON_PIPE(ReceiveAndEditBuffer, h1) - // Send one end of the pipe to each child. The first child will create - // and pass a buffer to the second child and back to us. The second child - // will write our message into the buffer. - WriteMessageWithHandles(h0, message, &p0, 1); - WriteMessageWithHandles(h1, message, &p1, 1); - - // Receive the buffer back from the first child. - ReadMessageWithHandles(h0, &b, 1); - - WriteMessage(h1, "quit"); - END_CHILD() - WriteMessage(h0, "quit"); - END_CHILD() - - // The second child should have written this message. - ExpectBufferContents(b, 0, message); -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(CreateAndPassBufferParent, SharedBufferTest, - parent) { - RUN_CHILD_ON_PIPE(CreateAndPassBuffer, child) - // Read a pipe from the parent and forward it to our child. - MojoHandle pipe; - std::string message = ReadMessageWithHandles(parent, &pipe, 1); - - WriteMessageWithHandles(child, message, &pipe, 1); - - // Read a buffer handle from the child and pass it back to the parent. - MojoHandle buffer; - EXPECT_EQ("", ReadMessageWithHandles(child, &buffer, 1)); - WriteMessageWithHandles(parent, "", &buffer, 1); - - EXPECT_EQ("quit", ReadMessage(parent)); - WriteMessage(child, "quit"); - END_CHILD() -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReceiveAndEditBufferParent, SharedBufferTest, - parent) { - RUN_CHILD_ON_PIPE(ReceiveAndEditBuffer, child) - // Read a pipe from the parent and forward it to our child. - MojoHandle pipe; - std::string message = ReadMessageWithHandles(parent, &pipe, 1); - WriteMessageWithHandles(child, message, &pipe, 1); - - EXPECT_EQ("quit", ReadMessage(parent)); - WriteMessage(child, "quit"); - END_CHILD() -} - -#if defined(OS_ANDROID) || defined(OS_MACOSX) -// Android multi-process tests are not executing the new process. This is flaky. -// Passing shared memory handles between cousins is not currently supported on -// OSX. -#define MAYBE_PassHandleBetweenCousins DISABLED_PassHandleBetweenCousins -#else -#define MAYBE_PassHandleBetweenCousins PassHandleBetweenCousins -#endif -TEST_F(SharedBufferTest, MAYBE_PassHandleBetweenCousins) { - const std::string message = "hello"; - MojoHandle p0, p1; - CreateMessagePipe(&p0, &p1); - - // Spawn two children who will each spawn their own child. Make sure the - // grandchildren (cousins to each other) can pass platform handles. - MojoHandle b; - RUN_CHILD_ON_PIPE(CreateAndPassBufferParent, child1) - RUN_CHILD_ON_PIPE(ReceiveAndEditBufferParent, child2) - MojoHandle pipe[2]; - CreateMessagePipe(&pipe[0], &pipe[1]); - - WriteMessageWithHandles(child1, message, &pipe[0], 1); - WriteMessageWithHandles(child2, message, &pipe[1], 1); - - // Receive the buffer back from the first child. - ReadMessageWithHandles(child1, &b, 1); - - WriteMessage(child2, "quit"); - END_CHILD() - WriteMessage(child1, "quit"); - END_CHILD() - - // The second grandchild should have written this message. - ExpectBufferContents(b, 0, message); -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReadAndMapWriteSharedBuffer, - SharedBufferTest, h) { - // Receive the shared buffer. - MojoHandle b; - EXPECT_EQ("hello", ReadMessageWithHandles(h, &b, 1)); - - // Read from the bufer. - ExpectBufferContents(b, 0, "hello"); - - // Extract the shared memory handle and try to map it writable. - base::SharedMemoryHandle shm_handle; - bool read_only = false; - ASSERT_EQ(MOJO_RESULT_OK, - PassSharedMemoryHandle(b, &shm_handle, nullptr, &read_only)); - base::SharedMemory shared_memory(shm_handle, false); - EXPECT_TRUE(read_only); - EXPECT_FALSE(shared_memory.Map(1234)); - - EXPECT_EQ("quit", ReadMessage(h)); - WriteMessage(h, "ok"); -} - -#if defined(OS_ANDROID) -// Android multi-process tests are not executing the new process. This is flaky. -#define MAYBE_CreateAndPassReadOnlyBuffer DISABLED_CreateAndPassReadOnlyBuffer -#else -#define MAYBE_CreateAndPassReadOnlyBuffer CreateAndPassReadOnlyBuffer -#endif -TEST_F(SharedBufferTest, MAYBE_CreateAndPassReadOnlyBuffer) { - RUN_CHILD_ON_PIPE(ReadAndMapWriteSharedBuffer, h) - // Create a new shared buffer. - MojoHandle b = CreateBuffer(1234); - WriteToBuffer(b, 0, "hello"); - - // Send a read-only copy of the buffer to the child. - MojoHandle dupe = DuplicateBuffer(b, true /* read_only */); - WriteMessageWithHandles(h, "hello", &dupe, 1); - - WriteMessage(h, "quit"); - EXPECT_EQ("ok", ReadMessage(h)); - END_CHILD() -} - -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(CreateAndPassReadOnlyBuffer, - SharedBufferTest, h) { - // Create a new shared buffer. - MojoHandle b = CreateBuffer(1234); - WriteToBuffer(b, 0, "hello"); - - // Send a read-only copy of the buffer to the parent. - MojoHandle dupe = DuplicateBuffer(b, true /* read_only */); - WriteMessageWithHandles(h, "", &dupe, 1); - - EXPECT_EQ("quit", ReadMessage(h)); - WriteMessage(h, "ok"); -} - -#if defined(OS_ANDROID) -// Android multi-process tests are not executing the new process. This is flaky. -#define MAYBE_CreateAndPassFromChildReadOnlyBuffer \ - DISABLED_CreateAndPassFromChildReadOnlyBuffer -#else -#define MAYBE_CreateAndPassFromChildReadOnlyBuffer \ - CreateAndPassFromChildReadOnlyBuffer -#endif -TEST_F(SharedBufferTest, MAYBE_CreateAndPassFromChildReadOnlyBuffer) { - RUN_CHILD_ON_PIPE(CreateAndPassReadOnlyBuffer, h) - MojoHandle b; - EXPECT_EQ("", ReadMessageWithHandles(h, &b, 1)); - ExpectBufferContents(b, 0, "hello"); - - // Extract the shared memory handle and try to map it writable. - base::SharedMemoryHandle shm_handle; - bool read_only = false; - ASSERT_EQ(MOJO_RESULT_OK, - PassSharedMemoryHandle(b, &shm_handle, nullptr, &read_only)); - base::SharedMemory shared_memory(shm_handle, false); - EXPECT_TRUE(read_only); - EXPECT_FALSE(shared_memory.Map(1234)); - - WriteMessage(h, "quit"); - EXPECT_EQ("ok", ReadMessage(h)); - END_CHILD() -} - -#endif // !defined(OS_IOS) - -} // namespace -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/signals_unittest.cc b/mojo/edk/system/signals_unittest.cc deleted file mode 100644 index e8b0cd1..0000000 --- a/mojo/edk/system/signals_unittest.cc +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/test/mojo_test_base.h" -#include "mojo/public/c/system/buffer.h" -#include "mojo/public/c/system/data_pipe.h" -#include "mojo/public/c/system/functions.h" -#include "mojo/public/c/system/message_pipe.h" -#include "mojo/public/c/system/types.h" - -namespace mojo { -namespace edk { -namespace { - -using SignalsTest = test::MojoTestBase; - -TEST_F(SignalsTest, QueryInvalidArguments) { - MojoHandleSignalsState state = {0, 0}; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoQueryHandleSignalsState(MOJO_HANDLE_INVALID, &state)); - - MojoHandle a, b; - CreateMessagePipe(&a, &b); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoQueryHandleSignalsState(a, nullptr)); -} - -TEST_F(SignalsTest, QueryMessagePipeSignals) { - MojoHandleSignalsState state = {0, 0}; - - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(a, &state)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED, - state.satisfiable_signals); - - EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &state)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED, - state.satisfiable_signals); - - WriteMessage(a, "ok"); - EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &state)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - state.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED, - state.satisfiable_signals); - - EXPECT_EQ("ok", ReadMessage(b)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &state)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED, - state.satisfiable_signals); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); - - EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &state)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals); -} - -} // namespace -} // namespace edk -} // 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 4a39cf7..0000000 --- a/mojo/edk/system/test_utils.cc +++ /dev/null @@ -1,76 +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 <stdint.h> - -#include <limits> - -#include "base/logging.h" -#include "base/test/test_timeouts.h" -#include "base/threading/platform_thread.h" // For |Sleep()|. -#include "build/build_config.h" - -namespace mojo { -namespace edk { -namespace test { - -MojoDeadline DeadlineFromMilliseconds(unsigned milliseconds) { - return static_cast<MojoDeadline>(milliseconds) * 1000; -} - -MojoDeadline EpsilonDeadline() { -// 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 (TinyDeadline() * 3) / 10; -#else - return (TinyDeadline() * 2) / 10; -#endif -} - -MojoDeadline TinyDeadline() { - return static_cast<MojoDeadline>( - TestTimeouts::tiny_timeout().InMicroseconds()); -} - -MojoDeadline ActionDeadline() { - return static_cast<MojoDeadline>( - TestTimeouts::action_timeout().InMicroseconds()); -} - -void Sleep(MojoDeadline deadline) { - CHECK_LE(deadline, - static_cast<MojoDeadline>(std::numeric_limits<int64_t>::max())); - base::PlatformThread::Sleep( - base::TimeDelta::FromMicroseconds(static_cast<int64_t>(deadline))); -} - -Stopwatch::Stopwatch() { -} - -Stopwatch::~Stopwatch() { -} - -void Stopwatch::Start() { - start_time_ = base::TimeTicks::Now(); -} - -MojoDeadline Stopwatch::Elapsed() { - int64_t result = (base::TimeTicks::Now() - start_time_).InMicroseconds(); - // |DCHECK_GE|, not |CHECK_GE|, since this may be performance-important. - DCHECK_GE(result, 0); - return static_cast<MojoDeadline>(result); -} - -} // namespace test -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/test_utils.h b/mojo/edk/system/test_utils.h deleted file mode 100644 index 1c90dc1..0000000 --- a/mojo/edk/system/test_utils.h +++ /dev/null @@ -1,59 +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" -#include "mojo/public/c/system/types.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace edk { -namespace test { - -MojoDeadline DeadlineFromMilliseconds(unsigned milliseconds); - -// A timeout smaller than |TestTimeouts::tiny_timeout()|, as a |MojoDeadline|. -// 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. -MojoDeadline EpsilonDeadline(); - -// |TestTimeouts::tiny_timeout()|, as a |MojoDeadline|. (Expect this to be on -// the order of 100 ms.) -MojoDeadline TinyDeadline(); - -// |TestTimeouts::action_timeout()|, as a |MojoDeadline|. (Expect this to be on -// the order of 10 s.) -MojoDeadline ActionDeadline(); - -// Sleeps for at least the specified duration. -void Sleep(MojoDeadline deadline); - -// Stopwatch ------------------------------------------------------------------- - -// A simple "stopwatch" for measuring time elapsed from a given starting point. -class Stopwatch { - public: - Stopwatch(); - ~Stopwatch(); - - void Start(); - // Returns the amount of time elapsed since the last call to |Start()| (in - // microseconds). - MojoDeadline Elapsed(); - - private: - base::TimeTicks start_time_; - - DISALLOW_COPY_AND_ASSIGN(Stopwatch); -}; - -} // namespace test -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_TEST_UTILS_H_ diff --git a/mojo/edk/system/watch.cc b/mojo/edk/system/watch.cc deleted file mode 100644 index cf08ac3..0000000 --- a/mojo/edk/system/watch.cc +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/watch.h" - -#include "mojo/edk/system/request_context.h" -#include "mojo/edk/system/watcher_dispatcher.h" - -namespace mojo { -namespace edk { - -Watch::Watch(const scoped_refptr<WatcherDispatcher>& watcher, - const scoped_refptr<Dispatcher>& dispatcher, - uintptr_t context, - MojoHandleSignals signals) - : watcher_(watcher), - dispatcher_(dispatcher), - context_(context), - signals_(signals) {} - -bool Watch::NotifyState(const HandleSignalsState& state, - bool allowed_to_call_callback) { - AssertWatcherLockAcquired(); - - // NOTE: This method must NEVER call into |dispatcher_| directly, because it - // may be called while |dispatcher_| holds a lock. - - MojoResult rv = MOJO_RESULT_SHOULD_WAIT; - RequestContext* const request_context = RequestContext::current(); - if (state.satisfies(signals_)) { - rv = MOJO_RESULT_OK; - if (allowed_to_call_callback && rv != last_known_result_) { - request_context->AddWatchNotifyFinalizer(this, MOJO_RESULT_OK, state); - } - } else if (!state.can_satisfy(signals_)) { - rv = MOJO_RESULT_FAILED_PRECONDITION; - if (allowed_to_call_callback && rv != last_known_result_) { - request_context->AddWatchNotifyFinalizer( - this, MOJO_RESULT_FAILED_PRECONDITION, state); - } - } - - last_known_signals_state_ = - *static_cast<const MojoHandleSignalsState*>(&state); - last_known_result_ = rv; - return ready(); -} - -void Watch::Cancel() { - RequestContext::current()->AddWatchCancelFinalizer(this); -} - -void Watch::InvokeCallback(MojoResult result, - const HandleSignalsState& state, - MojoWatcherNotificationFlags flags) { - // We hold the lock through invocation to ensure that only one notification - // callback runs for this context at any given time. - base::AutoLock lock(notification_lock_); - if (result == MOJO_RESULT_CANCELLED) { - // Make sure cancellation is the last notification we dispatch. - DCHECK(!is_cancelled_); - is_cancelled_ = true; - } else if (is_cancelled_) { - return; - } - - // NOTE: This will acquire |watcher_|'s internal lock. It's safe because a - // thread can only enter InvokeCallback() from within a RequestContext - // destructor where no dispatcher locks are held. - watcher_->InvokeWatchCallback(context_, result, state, flags); -} - -Watch::~Watch() {} - -#if DCHECK_IS_ON() -void Watch::AssertWatcherLockAcquired() const { - watcher_->lock_.AssertAcquired(); -} -#endif - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/watch.h b/mojo/edk/system/watch.h deleted file mode 100644 index f277de9..0000000 --- a/mojo/edk/system/watch.h +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_WATCH_H_ -#define MOJO_EDK_SYSTEM_WATCH_H_ - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/system/atomic_flag.h" -#include "mojo/edk/system/handle_signals_state.h" - -namespace mojo { -namespace edk { - -class Dispatcher; -class WatcherDispatcher; - -// Encapsulates the state associated with a single watch context within a -// watcher. -// -// Every Watch has its own cancellation state, and is captured by RequestContext -// notification finalizers to avoid redundant context resolution during -// finalizer execution. -class Watch : public base::RefCountedThreadSafe<Watch> { - public: - // Constructs a Watch which represents a watch within |watcher| associated - // with |context|, watching |dispatcher| for |signals|. - Watch(const scoped_refptr<WatcherDispatcher>& watcher, - const scoped_refptr<Dispatcher>& dispatcher, - uintptr_t context, - MojoHandleSignals signals); - - // Notifies the Watch of a potential state change. - // - // If |allowed_to_call_callback| is true, this may add a notification - // finalizer to the current RequestContext to invoke the watcher's callback - // with this watch's context. See return values below. - // - // This is called directly by WatcherDispatcher whenever the Watch's observed - // dispatcher notifies the WatcherDispatcher of a state change. - // - // Returns |true| if the Watch entered or remains in a ready state as a result - // of the state change. If |allowed_to_call_callback| was true in this case, - // the Watch will have also attached a notification finalizer to the current - // RequestContext. - // - // Returns |false| if the - bool NotifyState(const HandleSignalsState& state, - bool allowed_to_call_callback); - - // Notifies the watch of cancellation ASAP. This will always be the last - // notification sent for the watch. - void Cancel(); - - // Finalizer method for RequestContexts. This method is invoked once for every - // notification finalizer added to a RequestContext by this object. This calls - // down into the WatcherDispatcher to do the actual notification call. - void InvokeCallback(MojoResult result, - const HandleSignalsState& state, - MojoWatcherNotificationFlags flags); - - const scoped_refptr<Dispatcher>& dispatcher() const { return dispatcher_; } - uintptr_t context() const { return context_; } - - MojoResult last_known_result() const { - AssertWatcherLockAcquired(); - return last_known_result_; - } - - MojoHandleSignalsState last_known_signals_state() const { - AssertWatcherLockAcquired(); - return last_known_signals_state_; - } - - bool ready() const { - AssertWatcherLockAcquired(); - return last_known_result_ == MOJO_RESULT_OK || - last_known_result_ == MOJO_RESULT_FAILED_PRECONDITION; - } - - private: - friend class base::RefCountedThreadSafe<Watch>; - - ~Watch(); - -#if DCHECK_IS_ON() - void AssertWatcherLockAcquired() const; -#else - void AssertWatcherLockAcquired() const {} -#endif - - const scoped_refptr<WatcherDispatcher> watcher_; - const scoped_refptr<Dispatcher> dispatcher_; - const uintptr_t context_; - const MojoHandleSignals signals_; - - // The result code with which this Watch would notify if currently armed, - // based on the last known signaling state of |dispatcher_|. Guarded by the - // owning WatcherDispatcher's lock. - MojoResult last_known_result_ = MOJO_RESULT_UNKNOWN; - - // The last known signaling state of |dispatcher_|. Guarded by the owning - // WatcherDispatcher's lock. - MojoHandleSignalsState last_known_signals_state_ = {0, 0}; - - // Guards |is_cancelled_| below and mutually excludes individual watch - // notification executions for this same watch context. - // - // Note that this should only be acquired from a RequestContext finalizer to - // ensure that no other internal locks are already held. - base::Lock notification_lock_; - - // Guarded by |notification_lock_|. - bool is_cancelled_ = false; - - DISALLOW_COPY_AND_ASSIGN(Watch); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_WATCH_H_ diff --git a/mojo/edk/system/watcher_dispatcher.cc b/mojo/edk/system/watcher_dispatcher.cc deleted file mode 100644 index 409dd2a..0000000 --- a/mojo/edk/system/watcher_dispatcher.cc +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/watcher_dispatcher.h" - -#include <algorithm> -#include <limits> -#include <map> - -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "mojo/edk/system/watch.h" - -namespace mojo { -namespace edk { - -WatcherDispatcher::WatcherDispatcher(MojoWatcherCallback callback) - : callback_(callback) {} - -void WatcherDispatcher::NotifyHandleState(Dispatcher* dispatcher, - const HandleSignalsState& state) { - base::AutoLock lock(lock_); - auto it = watched_handles_.find(dispatcher); - if (it == watched_handles_.end()) - return; - - // Maybe fire a notification to the watch assoicated with this dispatcher, - // provided we're armed it cares about the new state. - if (it->second->NotifyState(state, armed_)) { - ready_watches_.insert(it->second.get()); - - // If we were armed and got here, we notified the watch. Disarm. - armed_ = false; - } else { - ready_watches_.erase(it->second.get()); - } -} - -void WatcherDispatcher::NotifyHandleClosed(Dispatcher* dispatcher) { - scoped_refptr<Watch> watch; - { - base::AutoLock lock(lock_); - auto it = watched_handles_.find(dispatcher); - if (it == watched_handles_.end()) - return; - - watch = std::move(it->second); - - // Wipe out all state associated with the closed dispatcher. - watches_.erase(watch->context()); - ready_watches_.erase(watch.get()); - watched_handles_.erase(it); - } - - // NOTE: It's important that this is called outside of |lock_| since it - // acquires internal Watch locks. - watch->Cancel(); -} - -void WatcherDispatcher::InvokeWatchCallback( - uintptr_t context, - MojoResult result, - const HandleSignalsState& state, - MojoWatcherNotificationFlags flags) { - { - // We avoid holding the lock during dispatch. It's OK for notification - // callbacks to close this watcher, and it's OK for notifications to race - // with closure, if for example the watcher is closed from another thread - // between this test and the invocation of |callback_| below. - // - // Because cancellation synchronously blocks all future notifications, and - // because notifications themselves are mutually exclusive for any given - // context, we still guarantee that a single MOJO_RESULT_CANCELLED result - // is the last notification received for any given context. - // - // This guarantee is sufficient to make safe, synchronized, per-context - // state management possible in user code. - base::AutoLock lock(lock_); - if (closed_ && result != MOJO_RESULT_CANCELLED) - return; - } - - callback_(context, result, static_cast<MojoHandleSignalsState>(state), flags); -} - -Dispatcher::Type WatcherDispatcher::GetType() const { - return Type::WATCHER; -} - -MojoResult WatcherDispatcher::Close() { - // We swap out all the watched handle information onto the stack so we can - // call into their dispatchers without our own lock held. - std::map<uintptr_t, scoped_refptr<Watch>> watches; - { - base::AutoLock lock(lock_); - DCHECK(!closed_); - closed_ = true; - std::swap(watches, watches_); - watched_handles_.clear(); - } - - // Remove all refs from our watched dispatchers and fire cancellations. - for (auto& entry : watches) { - entry.second->dispatcher()->RemoveWatcherRef(this, entry.first); - entry.second->Cancel(); - } - - return MOJO_RESULT_OK; -} - -MojoResult WatcherDispatcher::WatchDispatcher( - scoped_refptr<Dispatcher> dispatcher, - MojoHandleSignals signals, - uintptr_t context) { - // NOTE: Because it's critical to avoid acquiring any other dispatcher locks - // while |lock_| is held, we defer adding oursevles to the dispatcher until - // after we've updated all our own relevant state and released |lock_|. - { - base::AutoLock lock(lock_); - if (watches_.count(context) || watched_handles_.count(dispatcher.get())) - return MOJO_RESULT_ALREADY_EXISTS; - - scoped_refptr<Watch> watch = new Watch(this, dispatcher, context, signals); - watches_.insert({context, watch}); - auto result = - watched_handles_.insert(std::make_pair(dispatcher.get(), watch)); - DCHECK(result.second); - } - - MojoResult rv = dispatcher->AddWatcherRef(this, context); - if (rv != MOJO_RESULT_OK) { - // Oops. This was not a valid handle to watch. Undo the above work and - // fail gracefully. - base::AutoLock lock(lock_); - watches_.erase(context); - watched_handles_.erase(dispatcher.get()); - return rv; - } - - return MOJO_RESULT_OK; -} - -MojoResult WatcherDispatcher::CancelWatch(uintptr_t context) { - // We may remove the last stored ref to the Watch below, so we retain - // a reference on the stack. - scoped_refptr<Watch> watch; - { - base::AutoLock lock(lock_); - auto it = watches_.find(context); - if (it == watches_.end()) - return MOJO_RESULT_NOT_FOUND; - watch = it->second; - watches_.erase(it); - } - - // Mark the watch as cancelled so no further notifications get through. - watch->Cancel(); - - // We remove the watcher ref for this context before updating any more - // internal watcher state, ensuring that we don't receiving further - // notifications for this context. - watch->dispatcher()->RemoveWatcherRef(this, context); - - { - base::AutoLock lock(lock_); - auto handle_it = watched_handles_.find(watch->dispatcher().get()); - DCHECK(handle_it != watched_handles_.end()); - ready_watches_.erase(handle_it->second.get()); - watched_handles_.erase(handle_it); - } - - return MOJO_RESULT_OK; -} - -MojoResult WatcherDispatcher::Arm( - uint32_t* num_ready_contexts, - uintptr_t* ready_contexts, - MojoResult* ready_results, - MojoHandleSignalsState* ready_signals_states) { - base::AutoLock lock(lock_); - if (num_ready_contexts && - (!ready_contexts || !ready_results || !ready_signals_states)) { - return MOJO_RESULT_INVALID_ARGUMENT; - } - - if (watched_handles_.empty()) - return MOJO_RESULT_NOT_FOUND; - - if (ready_watches_.empty()) { - // Fast path: No watches are ready to notify, so we're done. - armed_ = true; - return MOJO_RESULT_OK; - } - - if (num_ready_contexts) { - DCHECK_LE(ready_watches_.size(), std::numeric_limits<uint32_t>::max()); - *num_ready_contexts = std::min( - *num_ready_contexts, static_cast<uint32_t>(ready_watches_.size())); - - WatchSet::const_iterator next_ready_iter = ready_watches_.begin(); - if (last_watch_to_block_arming_) { - // Find the next watch to notify in simple round-robin order on the - // |ready_watches_| map, wrapping around to the beginning if necessary. - next_ready_iter = ready_watches_.find(last_watch_to_block_arming_); - if (next_ready_iter != ready_watches_.end()) - ++next_ready_iter; - if (next_ready_iter == ready_watches_.end()) - next_ready_iter = ready_watches_.begin(); - } - - for (size_t i = 0; i < *num_ready_contexts; ++i) { - const Watch* const watch = *next_ready_iter; - ready_contexts[i] = watch->context(); - ready_results[i] = watch->last_known_result(); - ready_signals_states[i] = watch->last_known_signals_state(); - - // Iterate and wrap around. - last_watch_to_block_arming_ = watch; - ++next_ready_iter; - if (next_ready_iter == ready_watches_.end()) - next_ready_iter = ready_watches_.begin(); - } - } - - return MOJO_RESULT_FAILED_PRECONDITION; -} - -WatcherDispatcher::~WatcherDispatcher() {} - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/watcher_dispatcher.h b/mojo/edk/system/watcher_dispatcher.h deleted file mode 100644 index 605a315..0000000 --- a/mojo/edk/system/watcher_dispatcher.h +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_WATCHER_DISPATCHER_H_ -#define MOJO_EDK_SYSTEM_WATCHER_DISPATCHER_H_ - -#include <map> -#include <set> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/handle_signals_state.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/watcher.h" - -namespace mojo { -namespace edk { - -class Watch; - -// The dispatcher type which backs watcher handles. -class WatcherDispatcher : public Dispatcher { - public: - // Constructs a new WatcherDispatcher which invokes |callback| when a - // registered watch observes some relevant state change. - explicit WatcherDispatcher(MojoWatcherCallback callback); - - // Methods used by watched dispatchers to notify watchers of events. - void NotifyHandleState(Dispatcher* dispatcher, - const HandleSignalsState& state); - void NotifyHandleClosed(Dispatcher* dispatcher); - - // Method used by RequestContext (indirectly, via Watch) to complete - // notification operations from a safe stack frame to avoid reentrancy. - void InvokeWatchCallback(uintptr_t context, - MojoResult result, - const HandleSignalsState& state, - MojoWatcherNotificationFlags flags); - - // Dispatcher: - Type GetType() const override; - MojoResult Close() override; - MojoResult WatchDispatcher(scoped_refptr<Dispatcher> dispatcher, - MojoHandleSignals signals, - uintptr_t context) override; - MojoResult CancelWatch(uintptr_t context) override; - MojoResult Arm(uint32_t* num_ready_contexts, - uintptr_t* ready_contexts, - MojoResult* ready_results, - MojoHandleSignalsState* ready_signals_states) override; - - private: - friend class Watch; - - using WatchSet = std::set<const Watch*>; - - ~WatcherDispatcher() override; - - const MojoWatcherCallback callback_; - - // Guards access to the fields below. - // - // NOTE: This may be acquired while holding another dispatcher's lock, as - // watched dispatchers call into WatcherDispatcher methods which lock this - // when issuing state change notifications. WatcherDispatcher must therefore - // take caution to NEVER acquire other dispatcher locks while this is held. - base::Lock lock_; - - bool armed_ = false; - bool closed_ = false; - - // A mapping from context to Watch. - std::map<uintptr_t, scoped_refptr<Watch>> watches_; - - // A mapping from watched dispatcher to Watch. - std::map<Dispatcher*, scoped_refptr<Watch>> watched_handles_; - - // The set of all Watch instances which are currently ready to signal. This is - // used for efficient arming behavior, as it allows for O(1) discovery of - // whether or not arming can succeed and quick determination of who's - // responsible if it can't. - WatchSet ready_watches_; - - // Tracks the last Watch whose state was returned by Arm(). This is used to - // ensure consistent round-robin behavior in the event that multiple Watches - // remain ready over the span of several Arm() attempts. - // - // NOTE: This pointer is only used to index |ready_watches_| and may point to - // an invalid object. It must therefore never be dereferenced. - const Watch* last_watch_to_block_arming_ = nullptr; - - DISALLOW_COPY_AND_ASSIGN(WatcherDispatcher); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_WATCHER_DISPATCHER_H_ diff --git a/mojo/edk/system/watcher_set.cc b/mojo/edk/system/watcher_set.cc deleted file mode 100644 index 0355b58..0000000 --- a/mojo/edk/system/watcher_set.cc +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/system/watcher_set.h" - -#include <utility> - -namespace mojo { -namespace edk { - -WatcherSet::WatcherSet(Dispatcher* owner) : owner_(owner) {} - -WatcherSet::~WatcherSet() = default; - -void WatcherSet::NotifyState(const HandleSignalsState& state) { - // Avoid notifying watchers if they have already seen this state. - if (last_known_state_.has_value() && state.equals(last_known_state_.value())) - return; - last_known_state_ = state; - for (const auto& entry : watchers_) - entry.first->NotifyHandleState(owner_, state); -} - -void WatcherSet::NotifyClosed() { - for (const auto& entry : watchers_) - entry.first->NotifyHandleClosed(owner_); -} - -MojoResult WatcherSet::Add(const scoped_refptr<WatcherDispatcher>& watcher, - uintptr_t context, - const HandleSignalsState& current_state) { - auto it = watchers_.find(watcher.get()); - if (it == watchers_.end()) { - auto result = - watchers_.insert(std::make_pair(watcher.get(), Entry{watcher})); - it = result.first; - } - - if (!it->second.contexts.insert(context).second) - return MOJO_RESULT_ALREADY_EXISTS; - - if (last_known_state_.has_value() && - !current_state.equals(last_known_state_.value())) { - // This new state may be relevant to everyone, in which case we just - // notify everyone. - NotifyState(current_state); - } else { - // Otherwise only notify the newly added Watcher. - watcher->NotifyHandleState(owner_, current_state); - } - return MOJO_RESULT_OK; -} - -MojoResult WatcherSet::Remove(WatcherDispatcher* watcher, uintptr_t context) { - auto it = watchers_.find(watcher); - if (it == watchers_.end()) - return MOJO_RESULT_NOT_FOUND; - - ContextSet& contexts = it->second.contexts; - auto context_it = contexts.find(context); - if (context_it == contexts.end()) - return MOJO_RESULT_NOT_FOUND; - - contexts.erase(context_it); - if (contexts.empty()) - watchers_.erase(it); - - return MOJO_RESULT_OK; -} - -WatcherSet::Entry::Entry(const scoped_refptr<WatcherDispatcher>& dispatcher) - : dispatcher(dispatcher) {} - -WatcherSet::Entry::Entry(Entry&& other) = default; - -WatcherSet::Entry::~Entry() = default; - -WatcherSet::Entry& WatcherSet::Entry::operator=(Entry&& other) = default; - -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/system/watcher_set.h b/mojo/edk/system/watcher_set.h deleted file mode 100644 index 2b7ef2c..0000000 --- a/mojo/edk/system/watcher_set.h +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_SYSTEM_WATCHER_SET_H_ -#define MOJO_EDK_SYSTEM_WATCHER_SET_H_ - -#include <map> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/optional.h" -#include "mojo/edk/system/handle_signals_state.h" -#include "mojo/edk/system/watcher_dispatcher.h" - -namespace mojo { -namespace edk { - -// A WatcherSet maintains a set of references to WatcherDispatchers to be -// notified when a handle changes state. -// -// Dispatchers which may be watched by a watcher should own a WatcherSet and -// notify it of all relevant state changes. -class WatcherSet { - public: - // |owner| is the Dispatcher who owns this WatcherSet. - explicit WatcherSet(Dispatcher* owner); - ~WatcherSet(); - - // Notifies all watchers of the handle's current signals state. - void NotifyState(const HandleSignalsState& state); - - // Notifies all watchers that this handle has been closed. - void NotifyClosed(); - - // Adds a new watcher+context. - MojoResult Add(const scoped_refptr<WatcherDispatcher>& watcher, - uintptr_t context, - const HandleSignalsState& current_state); - - // Removes a watcher+context. - MojoResult Remove(WatcherDispatcher* watcher, uintptr_t context); - - private: - using ContextSet = std::set<uintptr_t>; - - struct Entry { - Entry(const scoped_refptr<WatcherDispatcher>& dispatcher); - Entry(Entry&& other); - ~Entry(); - - Entry& operator=(Entry&& other); - - scoped_refptr<WatcherDispatcher> dispatcher; - ContextSet contexts; - - private: - DISALLOW_COPY_AND_ASSIGN(Entry); - }; - - Dispatcher* const owner_; - std::map<WatcherDispatcher*, Entry> watchers_; - base::Optional<HandleSignalsState> last_known_state_; - - DISALLOW_COPY_AND_ASSIGN(WatcherSet); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_SYSTEM_WATCHER_SET_H_ diff --git a/mojo/edk/system/watcher_unittest.cc b/mojo/edk/system/watcher_unittest.cc deleted file mode 100644 index dd396cd..0000000 --- a/mojo/edk/system/watcher_unittest.cc +++ /dev/null @@ -1,1637 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stdint.h> - -#include <map> -#include <memory> -#include <set> - -#include "base/bind.h" -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/platform_thread.h" -#include "base/threading/simple_thread.h" -#include "base/time/time.h" -#include "mojo/edk/test/mojo_test_base.h" -#include "mojo/public/c/system/data_pipe.h" -#include "mojo/public/c/system/types.h" -#include "mojo/public/c/system/watcher.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace edk { -namespace { - -using WatcherTest = test::MojoTestBase; - -class WatchHelper { - public: - using ContextCallback = - base::Callback<void(MojoResult, MojoHandleSignalsState)>; - - WatchHelper() {} - ~WatchHelper() {} - - MojoResult CreateWatcher(MojoHandle* handle) { - return MojoCreateWatcher(&Notify, handle); - } - - uintptr_t CreateContext(const ContextCallback& callback) { - return CreateContextWithCancel(callback, base::Closure()); - } - - uintptr_t CreateContextWithCancel(const ContextCallback& callback, - const base::Closure& cancel_callback) { - auto context = base::MakeUnique<NotificationContext>(callback); - NotificationContext* raw_context = context.get(); - raw_context->SetCancelCallback(base::Bind( - [](std::unique_ptr<NotificationContext> context, - const base::Closure& cancel_callback) { - if (cancel_callback) - cancel_callback.Run(); - }, - base::Passed(&context), cancel_callback)); - return reinterpret_cast<uintptr_t>(raw_context); - } - - private: - class NotificationContext { - public: - explicit NotificationContext(const ContextCallback& callback) - : callback_(callback) {} - - ~NotificationContext() {} - - void SetCancelCallback(const base::Closure& cancel_callback) { - cancel_callback_ = cancel_callback; - } - - void Notify(MojoResult result, MojoHandleSignalsState state) { - if (result == MOJO_RESULT_CANCELLED) - cancel_callback_.Run(); - else - callback_.Run(result, state); - } - - private: - const ContextCallback callback_; - base::Closure cancel_callback_; - - DISALLOW_COPY_AND_ASSIGN(NotificationContext); - }; - - static void Notify(uintptr_t context, - MojoResult result, - MojoHandleSignalsState state, - MojoWatcherNotificationFlags flags) { - reinterpret_cast<NotificationContext*>(context)->Notify(result, state); - } - - DISALLOW_COPY_AND_ASSIGN(WatchHelper); -}; - -class ThreadedRunner : public base::SimpleThread { - public: - explicit ThreadedRunner(const base::Closure& callback) - : SimpleThread("ThreadedRunner"), callback_(callback) {} - ~ThreadedRunner() override {} - - void Run() override { callback_.Run(); } - - private: - const base::Closure callback_; - - DISALLOW_COPY_AND_ASSIGN(ThreadedRunner); -}; - -void ExpectNoNotification(uintptr_t context, - MojoResult result, - MojoHandleSignalsState state, - MojoWatcherNotificationFlags flags) { - NOTREACHED(); -} - -void ExpectOnlyCancel(uintptr_t context, - MojoResult result, - MojoHandleSignalsState state, - MojoWatcherNotificationFlags flags) { - EXPECT_EQ(result, MOJO_RESULT_CANCELLED); -} - -TEST_F(WatcherTest, InvalidArguments) { - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoCreateWatcher(&ExpectNoNotification, nullptr)); - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectNoNotification, &w)); - - // Try to watch unwatchable handles. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoWatch(w, w, MOJO_HANDLE_SIGNAL_READABLE, 0)); - MojoHandle buffer_handle = CreateBuffer(42); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoWatch(w, buffer_handle, MOJO_HANDLE_SIGNAL_READABLE, 0)); - - // Try to cancel a watch on an invalid watcher handle. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoCancelWatch(buffer_handle, 0)); - - // Try to arm an invalid handle. - EXPECT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - MojoArmWatcher(MOJO_HANDLE_INVALID, nullptr, nullptr, nullptr, nullptr)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoArmWatcher(buffer_handle, nullptr, nullptr, nullptr, nullptr)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(buffer_handle)); - - // Try to arm with a non-null count but at least one null output buffer. - uint32_t num_ready_contexts = 1; - uintptr_t ready_context; - MojoResult ready_result; - MojoHandleSignalsState ready_state; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoArmWatcher(w, &num_ready_contexts, nullptr, &ready_result, - &ready_state)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoArmWatcher(w, &num_ready_contexts, &ready_context, nullptr, - &ready_state)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoArmWatcher(w, &num_ready_contexts, &ready_context, - &ready_result, nullptr)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); -} - -TEST_F(WatcherTest, WatchMessagePipeReadable) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - WatchHelper helper; - int num_expected_notifications = 1; - const uintptr_t readable_a_context = helper.CreateContext(base::Bind( - [](base::WaitableEvent* event, int* expected_count, MojoResult result, - MojoHandleSignalsState state) { - EXPECT_GT(*expected_count, 0); - *expected_count -= 1; - - EXPECT_EQ(MOJO_RESULT_OK, result); - event->Signal(); - }, - &event, &num_expected_notifications)); - - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - const char kMessage1[] = "hey hey hey hey"; - const char kMessage2[] = "i said hey"; - const char kMessage3[] = "what's goin' on?"; - - // Writing to |b| multiple times should notify exactly once. - WriteMessage(b, kMessage1); - WriteMessage(b, kMessage2); - event.Wait(); - - // This also shouldn't fire a notification; the watcher is still disarmed. - WriteMessage(b, kMessage3); - - // Arming should fail with relevant information. - constexpr size_t kMaxReadyContexts = 10; - uint32_t num_ready_contexts = kMaxReadyContexts; - uintptr_t ready_contexts[kMaxReadyContexts]; - MojoResult ready_results[kMaxReadyContexts]; - MojoHandleSignalsState ready_states[kMaxReadyContexts]; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoArmWatcher(w, &num_ready_contexts, ready_contexts, - ready_results, ready_states)); - EXPECT_EQ(1u, num_ready_contexts); - EXPECT_EQ(readable_a_context, ready_contexts[0]); - EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); - - // Flush the three messages from above. - EXPECT_EQ(kMessage1, ReadMessage(a)); - EXPECT_EQ(kMessage2, ReadMessage(a)); - EXPECT_EQ(kMessage3, ReadMessage(a)); - - // Now we can rearm the watcher. - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); -} - -TEST_F(WatcherTest, CloseWatchedMessagePipeHandle) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - WatchHelper helper; - const uintptr_t readable_a_context = helper.CreateContextWithCancel( - WatchHelper::ContextCallback(), - base::Bind([](base::WaitableEvent* event) { event->Signal(); }, &event)); - - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); - - // Test that closing a watched handle fires an appropriate notification, even - // when the watcher is unarmed. - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); - event.Wait(); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); -} - -TEST_F(WatcherTest, CloseWatchedMessagePipeHandlePeer) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - WatchHelper helper; - const uintptr_t readable_a_context = helper.CreateContext(base::Bind( - [](base::WaitableEvent* event, MojoResult result, - MojoHandleSignalsState state) { - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); - event->Signal(); - }, - &event)); - - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); - - // Test that closing a watched handle's peer with an armed watcher fires an - // appropriate notification. - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); - event.Wait(); - - // And now arming should fail with correct information about |a|'s state. - constexpr size_t kMaxReadyContexts = 10; - uint32_t num_ready_contexts = kMaxReadyContexts; - uintptr_t ready_contexts[kMaxReadyContexts]; - MojoResult ready_results[kMaxReadyContexts]; - MojoHandleSignalsState ready_states[kMaxReadyContexts]; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoArmWatcher(w, &num_ready_contexts, ready_contexts, - ready_results, ready_states)); - EXPECT_EQ(1u, num_ready_contexts); - EXPECT_EQ(readable_a_context, ready_contexts[0]); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, ready_results[0]); - EXPECT_TRUE(ready_states[0].satisfied_signals & - MOJO_HANDLE_SIGNAL_PEER_CLOSED); - EXPECT_FALSE(ready_states[0].satisfiable_signals & - MOJO_HANDLE_SIGNAL_READABLE); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); -} - -TEST_F(WatcherTest, WatchDataPipeConsumerReadable) { - constexpr size_t kTestPipeCapacity = 64; - MojoHandle producer, consumer; - CreateDataPipe(&producer, &consumer, kTestPipeCapacity); - - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - WatchHelper helper; - int num_expected_notifications = 1; - const uintptr_t readable_consumer_context = helper.CreateContext(base::Bind( - [](base::WaitableEvent* event, int* expected_count, MojoResult result, - MojoHandleSignalsState state) { - EXPECT_GT(*expected_count, 0); - *expected_count -= 1; - - EXPECT_EQ(MOJO_RESULT_OK, result); - event->Signal(); - }, - &event, &num_expected_notifications)); - - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, consumer, MOJO_HANDLE_SIGNAL_READABLE, - readable_consumer_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - const char kMessage1[] = "hey hey hey hey"; - const char kMessage2[] = "i said hey"; - const char kMessage3[] = "what's goin' on?"; - - // Writing to |producer| multiple times should notify exactly once. - WriteData(producer, kMessage1); - WriteData(producer, kMessage2); - event.Wait(); - - // This also shouldn't fire a notification; the watcher is still disarmed. - WriteData(producer, kMessage3); - - // Arming should fail with relevant information. - constexpr size_t kMaxReadyContexts = 10; - uint32_t num_ready_contexts = kMaxReadyContexts; - uintptr_t ready_contexts[kMaxReadyContexts]; - MojoResult ready_results[kMaxReadyContexts]; - MojoHandleSignalsState ready_states[kMaxReadyContexts]; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoArmWatcher(w, &num_ready_contexts, ready_contexts, - ready_results, ready_states)); - EXPECT_EQ(1u, num_ready_contexts); - EXPECT_EQ(readable_consumer_context, ready_contexts[0]); - EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); - - // Flush the three messages from above. - EXPECT_EQ(kMessage1, ReadData(consumer, sizeof(kMessage1) - 1)); - EXPECT_EQ(kMessage2, ReadData(consumer, sizeof(kMessage2) - 1)); - EXPECT_EQ(kMessage3, ReadData(consumer, sizeof(kMessage3) - 1)); - - // Now we can rearm the watcher. - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); -} - -TEST_F(WatcherTest, WatchDataPipeConsumerNewDataReadable) { - constexpr size_t kTestPipeCapacity = 64; - MojoHandle producer, consumer; - CreateDataPipe(&producer, &consumer, kTestPipeCapacity); - - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - WatchHelper helper; - int num_new_data_notifications = 0; - const uintptr_t new_data_context = helper.CreateContext(base::Bind( - [](base::WaitableEvent* event, int* notification_count, MojoResult result, - MojoHandleSignalsState state) { - *notification_count += 1; - - EXPECT_EQ(MOJO_RESULT_OK, result); - event->Signal(); - }, - &event, &num_new_data_notifications)); - - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, consumer, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - new_data_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - const char kMessage1[] = "hey hey hey hey"; - const char kMessage2[] = "i said hey"; - const char kMessage3[] = "what's goin' on?"; - - // Writing to |producer| multiple times should notify exactly once. - WriteData(producer, kMessage1); - WriteData(producer, kMessage2); - event.Wait(); - - // This also shouldn't fire a notification; the watcher is still disarmed. - WriteData(producer, kMessage3); - - // Arming should fail with relevant information. - constexpr size_t kMaxReadyContexts = 10; - uint32_t num_ready_contexts = kMaxReadyContexts; - uintptr_t ready_contexts[kMaxReadyContexts]; - MojoResult ready_results[kMaxReadyContexts]; - MojoHandleSignalsState ready_states[kMaxReadyContexts]; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoArmWatcher(w, &num_ready_contexts, ready_contexts, - ready_results, ready_states)); - EXPECT_EQ(1u, num_ready_contexts); - EXPECT_EQ(new_data_context, ready_contexts[0]); - EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); - - // Attempt to read more data than is available. Should fail but clear the - // NEW_DATA_READABLE signal. - char large_buffer[512]; - uint32_t large_read_size = 512; - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - MojoReadData(consumer, large_buffer, &large_read_size, - MOJO_READ_DATA_FLAG_ALL_OR_NONE)); - - // Attempt to arm again. Should succeed. - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - // Write more data. Should notify. - event.Reset(); - WriteData(producer, kMessage1); - event.Wait(); - - // Reading some data should clear NEW_DATA_READABLE again so we can rearm. - EXPECT_EQ(kMessage1, ReadData(consumer, sizeof(kMessage1) - 1)); - - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - EXPECT_EQ(2, num_new_data_notifications); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); -} - -TEST_F(WatcherTest, WatchDataPipeProducerWritable) { - constexpr size_t kTestPipeCapacity = 8; - MojoHandle producer, consumer; - CreateDataPipe(&producer, &consumer, kTestPipeCapacity); - - // Half the capacity of the data pipe. - const char kTestData[] = "aaaa"; - static_assert((sizeof(kTestData) - 1) * 2 == kTestPipeCapacity, - "Invalid test data for this test."); - - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - WatchHelper helper; - int num_expected_notifications = 1; - const uintptr_t writable_producer_context = helper.CreateContext(base::Bind( - [](base::WaitableEvent* event, int* expected_count, MojoResult result, - MojoHandleSignalsState state) { - EXPECT_GT(*expected_count, 0); - *expected_count -= 1; - - EXPECT_EQ(MOJO_RESULT_OK, result); - event->Signal(); - }, - &event, &num_expected_notifications)); - - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, producer, MOJO_HANDLE_SIGNAL_WRITABLE, - writable_producer_context)); - - // The producer is already writable, so arming should fail with relevant - // information. - constexpr size_t kMaxReadyContexts = 10; - uint32_t num_ready_contexts = kMaxReadyContexts; - uintptr_t ready_contexts[kMaxReadyContexts]; - MojoResult ready_results[kMaxReadyContexts]; - MojoHandleSignalsState ready_states[kMaxReadyContexts]; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoArmWatcher(w, &num_ready_contexts, ready_contexts, - ready_results, ready_states)); - EXPECT_EQ(1u, num_ready_contexts); - EXPECT_EQ(writable_producer_context, ready_contexts[0]); - EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); - EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); - - // Write some data, but don't fill the pipe yet. Arming should fail again. - WriteData(producer, kTestData); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoArmWatcher(w, &num_ready_contexts, ready_contexts, - ready_results, ready_states)); - EXPECT_EQ(1u, num_ready_contexts); - EXPECT_EQ(writable_producer_context, ready_contexts[0]); - EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); - EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); - - // Write more data, filling the pipe to capacity. Arming should succeed now. - WriteData(producer, kTestData); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - // Now read from the pipe, making the producer writable again. Should notify. - EXPECT_EQ(kTestData, ReadData(consumer, sizeof(kTestData) - 1)); - event.Wait(); - - // Arming should fail again. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoArmWatcher(w, &num_ready_contexts, ready_contexts, - ready_results, ready_states)); - EXPECT_EQ(1u, num_ready_contexts); - EXPECT_EQ(writable_producer_context, ready_contexts[0]); - EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); - EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); - - // Fill the pipe once more and arm the watcher. Should succeed. - WriteData(producer, kTestData); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); -}; - -TEST_F(WatcherTest, CloseWatchedDataPipeConsumerHandle) { - constexpr size_t kTestPipeCapacity = 8; - MojoHandle producer, consumer; - CreateDataPipe(&producer, &consumer, kTestPipeCapacity); - - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - WatchHelper helper; - const uintptr_t readable_consumer_context = helper.CreateContextWithCancel( - WatchHelper::ContextCallback(), - base::Bind([](base::WaitableEvent* event) { event->Signal(); }, &event)); - - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, consumer, MOJO_HANDLE_SIGNAL_READABLE, - readable_consumer_context)); - - // Closing the consumer should fire a cancellation notification. - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); - event.Wait(); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); -} - -TEST_F(WatcherTest, CloseWatcherDataPipeConsumerHandlePeer) { - constexpr size_t kTestPipeCapacity = 8; - MojoHandle producer, consumer; - CreateDataPipe(&producer, &consumer, kTestPipeCapacity); - - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - WatchHelper helper; - const uintptr_t readable_consumer_context = helper.CreateContext(base::Bind( - [](base::WaitableEvent* event, MojoResult result, - MojoHandleSignalsState state) { - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); - event->Signal(); - }, - &event)); - - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, consumer, MOJO_HANDLE_SIGNAL_READABLE, - readable_consumer_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - // Closing the producer should fire a notification for an unsatisfiable watch. - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); - event.Wait(); - - // Now attempt to rearm and expect appropriate error feedback. - constexpr size_t kMaxReadyContexts = 10; - uint32_t num_ready_contexts = kMaxReadyContexts; - uintptr_t ready_contexts[kMaxReadyContexts]; - MojoResult ready_results[kMaxReadyContexts]; - MojoHandleSignalsState ready_states[kMaxReadyContexts]; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoArmWatcher(w, &num_ready_contexts, ready_contexts, - ready_results, ready_states)); - EXPECT_EQ(1u, num_ready_contexts); - EXPECT_EQ(readable_consumer_context, ready_contexts[0]); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, ready_results[0]); - EXPECT_FALSE(ready_states[0].satisfiable_signals & - MOJO_HANDLE_SIGNAL_READABLE); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); -} - -TEST_F(WatcherTest, CloseWatchedDataPipeProducerHandle) { - constexpr size_t kTestPipeCapacity = 8; - MojoHandle producer, consumer; - CreateDataPipe(&producer, &consumer, kTestPipeCapacity); - - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - WatchHelper helper; - const uintptr_t writable_producer_context = helper.CreateContextWithCancel( - WatchHelper::ContextCallback(), - base::Bind([](base::WaitableEvent* event) { event->Signal(); }, &event)); - - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, producer, MOJO_HANDLE_SIGNAL_WRITABLE, - writable_producer_context)); - - // Closing the consumer should fire a cancellation notification. - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); - event.Wait(); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); -} - -TEST_F(WatcherTest, CloseWatchedDataPipeProducerHandlePeer) { - constexpr size_t kTestPipeCapacity = 8; - MojoHandle producer, consumer; - CreateDataPipe(&producer, &consumer, kTestPipeCapacity); - - const char kTestMessageFullCapacity[] = "xxxxxxxx"; - static_assert(sizeof(kTestMessageFullCapacity) - 1 == kTestPipeCapacity, - "Invalid test message size for this test."); - - // Make the pipe unwritable initially. - WriteData(producer, kTestMessageFullCapacity); - - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - WatchHelper helper; - const uintptr_t writable_producer_context = helper.CreateContext(base::Bind( - [](base::WaitableEvent* event, MojoResult result, - MojoHandleSignalsState state) { - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); - event->Signal(); - }, - &event)); - - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, producer, MOJO_HANDLE_SIGNAL_WRITABLE, - writable_producer_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - // Closing the consumer should fire a notification for an unsatisfiable watch, - // as the full data pipe can never be read from again and is therefore - // permanently full and unwritable. - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); - event.Wait(); - - // Now attempt to rearm and expect appropriate error feedback. - constexpr size_t kMaxReadyContexts = 10; - uint32_t num_ready_contexts = kMaxReadyContexts; - uintptr_t ready_contexts[kMaxReadyContexts]; - MojoResult ready_results[kMaxReadyContexts]; - MojoHandleSignalsState ready_states[kMaxReadyContexts]; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoArmWatcher(w, &num_ready_contexts, ready_contexts, - ready_results, ready_states)); - EXPECT_EQ(1u, num_ready_contexts); - EXPECT_EQ(writable_producer_context, ready_contexts[0]); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, ready_results[0]); - EXPECT_FALSE(ready_states[0].satisfiable_signals & - MOJO_HANDLE_SIGNAL_WRITABLE); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); -} - -TEST_F(WatcherTest, ArmWithNoWatches) { - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectNoNotification, &w)); - EXPECT_EQ(MOJO_RESULT_NOT_FOUND, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); -} - -TEST_F(WatcherTest, WatchDuplicateContext) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectOnlyCancel, &w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, 0)); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - MojoWatch(w, b, MOJO_HANDLE_SIGNAL_READABLE, 0)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); -} - -TEST_F(WatcherTest, CancelUnknownWatch) { - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectNoNotification, &w)); - EXPECT_EQ(MOJO_RESULT_NOT_FOUND, MojoCancelWatch(w, 1234)); -} - -TEST_F(WatcherTest, ArmWithWatchAlreadySatisfied) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectOnlyCancel, &w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_WRITABLE, 0)); - - // |a| is always writable, so we can never arm this watcher. - constexpr size_t kMaxReadyContexts = 10; - uint32_t num_ready_contexts = kMaxReadyContexts; - uintptr_t ready_contexts[kMaxReadyContexts]; - MojoResult ready_results[kMaxReadyContexts]; - MojoHandleSignalsState ready_states[kMaxReadyContexts]; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoArmWatcher(w, &num_ready_contexts, ready_contexts, - ready_results, ready_states)); - EXPECT_EQ(1u, num_ready_contexts); - EXPECT_EQ(0u, ready_contexts[0]); - EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); - EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); -} - -TEST_F(WatcherTest, ArmWithWatchAlreadyUnsatisfiable) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectOnlyCancel, &w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, 0)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); - - // |b| is closed and never wrote any messages, so |a| won't be readable again. - // MojoArmWatcher() should fail, incidcating as much. - constexpr size_t kMaxReadyContexts = 10; - uint32_t num_ready_contexts = kMaxReadyContexts; - uintptr_t ready_contexts[kMaxReadyContexts]; - MojoResult ready_results[kMaxReadyContexts]; - MojoHandleSignalsState ready_states[kMaxReadyContexts]; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoArmWatcher(w, &num_ready_contexts, ready_contexts, - ready_results, ready_states)); - EXPECT_EQ(1u, num_ready_contexts); - EXPECT_EQ(0u, ready_contexts[0]); - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, ready_results[0]); - EXPECT_TRUE(ready_states[0].satisfied_signals & - MOJO_HANDLE_SIGNAL_PEER_CLOSED); - EXPECT_FALSE(ready_states[0].satisfiable_signals & - MOJO_HANDLE_SIGNAL_READABLE); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); -} - -TEST_F(WatcherTest, MultipleWatches) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - base::WaitableEvent a_event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - base::WaitableEvent b_event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - WatchHelper helper; - int num_a_notifications = 0; - int num_b_notifications = 0; - auto notify_callback = - base::Bind([](base::WaitableEvent* event, int* notification_count, - MojoResult result, MojoHandleSignalsState state) { - *notification_count += 1; - EXPECT_EQ(MOJO_RESULT_OK, result); - event->Signal(); - }); - uintptr_t readable_a_context = helper.CreateContext( - base::Bind(notify_callback, &a_event, &num_a_notifications)); - uintptr_t readable_b_context = helper.CreateContext( - base::Bind(notify_callback, &b_event, &num_b_notifications)); - - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - - // Add two independent watch contexts to watch for |a| or |b| readability. - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, b, MOJO_HANDLE_SIGNAL_READABLE, readable_b_context)); - - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - const char kMessage1[] = "things are happening"; - const char kMessage2[] = "ok. ok. ok. ok."; - const char kMessage3[] = "plz wake up"; - - // Writing to |b| should signal |a|'s watch. - WriteMessage(b, kMessage1); - a_event.Wait(); - a_event.Reset(); - - // Subsequent messages on |b| should not trigger another notification. - WriteMessage(b, kMessage2); - WriteMessage(b, kMessage3); - - // Messages on |a| also shouldn't trigger |b|'s notification, since the - // watcher should be disarmed by now. - WriteMessage(a, kMessage1); - WriteMessage(a, kMessage2); - WriteMessage(a, kMessage3); - - // Arming should fail. Since we only ask for at most one context's information - // that's all we should get back. Which one we get is unspecified. - constexpr size_t kMaxReadyContexts = 10; - uint32_t num_ready_contexts = 1; - uintptr_t ready_contexts[kMaxReadyContexts]; - MojoResult ready_results[kMaxReadyContexts]; - MojoHandleSignalsState ready_states[kMaxReadyContexts]; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoArmWatcher(w, &num_ready_contexts, ready_contexts, - ready_results, ready_states)); - EXPECT_EQ(1u, num_ready_contexts); - EXPECT_TRUE(ready_contexts[0] == readable_a_context || - ready_contexts[0] == readable_b_context); - EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); - EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); - - // Now try arming again, verifying that both contexts are returned. - num_ready_contexts = kMaxReadyContexts; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoArmWatcher(w, &num_ready_contexts, ready_contexts, - ready_results, ready_states)); - EXPECT_EQ(2u, num_ready_contexts); - EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); - EXPECT_EQ(MOJO_RESULT_OK, ready_results[1]); - EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); - EXPECT_TRUE(ready_states[1].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); - EXPECT_TRUE((ready_contexts[0] == readable_a_context && - ready_contexts[1] == readable_b_context) || - (ready_contexts[0] == readable_b_context && - ready_contexts[1] == readable_a_context)); - - // Flush out the test messages so we should be able to successfully rearm. - EXPECT_EQ(kMessage1, ReadMessage(a)); - EXPECT_EQ(kMessage2, ReadMessage(a)); - EXPECT_EQ(kMessage3, ReadMessage(a)); - EXPECT_EQ(kMessage1, ReadMessage(b)); - EXPECT_EQ(kMessage2, ReadMessage(b)); - EXPECT_EQ(kMessage3, ReadMessage(b)); - - // Add a watch which is always satisfied, so we can't arm. Arming should fail - // with only this new watch's information. - uintptr_t writable_c_context = helper.CreateContext(base::Bind( - [](MojoResult result, MojoHandleSignalsState state) { NOTREACHED(); })); - MojoHandle c, d; - CreateMessagePipe(&c, &d); - - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, c, MOJO_HANDLE_SIGNAL_WRITABLE, writable_c_context)); - num_ready_contexts = kMaxReadyContexts; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoArmWatcher(w, &num_ready_contexts, ready_contexts, - ready_results, ready_states)); - EXPECT_EQ(1u, num_ready_contexts); - EXPECT_EQ(writable_c_context, ready_contexts[0]); - EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); - EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); - - // Cancel the new watch and arming should succeed once again. - EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(w, writable_c_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); -} - -TEST_F(WatcherTest, NotifyOtherFromNotificationCallback) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - static const char kTestMessageToA[] = "hello a"; - static const char kTestMessageToB[] = "hello b"; - - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - - WatchHelper helper; - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - - uintptr_t readable_a_context = helper.CreateContext(base::Bind( - [](MojoHandle w, MojoHandle a, MojoResult result, - MojoHandleSignalsState state) { - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ("hello a", ReadMessage(a)); - - // Re-arm the watcher and signal |b|. - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - WriteMessage(a, kTestMessageToB); - }, - w, a)); - - uintptr_t readable_b_context = helper.CreateContext(base::Bind( - [](base::WaitableEvent* event, MojoHandle w, MojoHandle b, - MojoResult result, MojoHandleSignalsState state) { - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(kTestMessageToB, ReadMessage(b)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - event->Signal(); - }, - &event, w, b)); - - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, b, MOJO_HANDLE_SIGNAL_READABLE, readable_b_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - // Send a message to |a|. The relevant watch context should be notified, and - // should in turn send a message to |b|, waking up the other context. The - // second context signals |event|. - WriteMessage(b, kTestMessageToA); - event.Wait(); -} - -TEST_F(WatcherTest, NotifySelfFromNotificationCallback) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - static const char kTestMessageToA[] = "hello a"; - - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - - WatchHelper helper; - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - - int expected_notifications = 10; - uintptr_t readable_a_context = helper.CreateContext(base::Bind( - [](int* expected_count, MojoHandle w, MojoHandle a, MojoHandle b, - base::WaitableEvent* event, MojoResult result, - MojoHandleSignalsState state) { - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ("hello a", ReadMessage(a)); - - EXPECT_GT(*expected_count, 0); - *expected_count -= 1; - if (*expected_count == 0) { - event->Signal(); - return; - } else { - // Re-arm the watcher and signal |a| again. - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - WriteMessage(b, kTestMessageToA); - } - }, - &expected_notifications, w, a, b, &event)); - - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - // Send a message to |a|. When the watch above is notified, it will rearm and - // send another message to |a|. This will happen until - // |expected_notifications| reaches 0. - WriteMessage(b, kTestMessageToA); - event.Wait(); -} - -TEST_F(WatcherTest, ImplicitCancelOtherFromNotificationCallback) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - MojoHandle c, d; - CreateMessagePipe(&c, &d); - - static const char kTestMessageToA[] = "hi a"; - static const char kTestMessageToC[] = "hi c"; - - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - - WatchHelper helper; - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - - uintptr_t readable_a_context = helper.CreateContextWithCancel( - base::Bind([](MojoResult result, MojoHandleSignalsState state) { - NOTREACHED(); - }), - base::Bind([](base::WaitableEvent* event) { event->Signal(); }, &event)); - - uintptr_t readable_c_context = helper.CreateContext(base::Bind( - [](MojoHandle w, MojoHandle a, MojoHandle b, MojoHandle c, - MojoResult result, MojoHandleSignalsState state) { - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(kTestMessageToC, ReadMessage(c)); - - // Now rearm the watcher. - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - // Must result in exactly ONE notification on the above context, for - // CANCELLED only. Because we cannot dispatch notifications until the - // stack unwinds, and because we must never dispatch non-cancellation - // notifications for a handle once it's been closed, we must be certain - // that cancellation due to closure preemptively invalidates any - // pending non-cancellation notifications queued on the current - // RequestContext, such as the one resulting from the WriteMessage here. - WriteMessage(b, kTestMessageToA); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); - - // Rearming should be fine since |a|'s watch should already be - // implicitly cancelled (even though the notification will not have - // been invoked yet.) - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - // Nothing interesting should happen as a result of this. - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); - }, - w, a, b, c)); - - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, c, MOJO_HANDLE_SIGNAL_READABLE, readable_c_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - WriteMessage(d, kTestMessageToC); - event.Wait(); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); -} - -TEST_F(WatcherTest, ExplicitCancelOtherFromNotificationCallback) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - MojoHandle c, d; - CreateMessagePipe(&c, &d); - - static const char kTestMessageToA[] = "hi a"; - static const char kTestMessageToC[] = "hi c"; - - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - - WatchHelper helper; - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - - uintptr_t readable_a_context = helper.CreateContext(base::Bind( - [](MojoResult result, MojoHandleSignalsState state) { NOTREACHED(); })); - - uintptr_t readable_c_context = helper.CreateContext(base::Bind( - [](base::WaitableEvent* event, uintptr_t readable_a_context, MojoHandle w, - MojoHandle a, MojoHandle b, MojoHandle c, MojoResult result, - MojoHandleSignalsState state) { - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(kTestMessageToC, ReadMessage(c)); - - // Now rearm the watcher. - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - // Should result in no notifications on the above context, because the - // watch will have been cancelled by the time the notification callback - // can execute. - WriteMessage(b, kTestMessageToA); - WriteMessage(b, kTestMessageToA); - EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(w, readable_a_context)); - - // Rearming should be fine now. - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - // Nothing interesting should happen as a result of these. - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); - - event->Signal(); - }, - &event, readable_a_context, w, a, b, c)); - - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, c, MOJO_HANDLE_SIGNAL_READABLE, readable_c_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - WriteMessage(d, kTestMessageToC); - event.Wait(); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); -} - -TEST_F(WatcherTest, NestedCancellation) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - MojoHandle c, d; - CreateMessagePipe(&c, &d); - - static const char kTestMessageToA[] = "hey a"; - static const char kTestMessageToC[] = "hey c"; - static const char kTestMessageToD[] = "hey d"; - - // This is a tricky test. It establishes a watch on |b| using one watcher and - // watches on |c| and |d| using another watcher. - // - // A message is written to |d| to wake up |c|'s watch, and the notification - // handler for that event does the following: - // 1. Writes to |a| to eventually wake up |b|'s watcher. - // 2. Rearms |c|'s watcher. - // 3. Writes to |d| to eventually wake up |c|'s watcher again. - // - // Meanwhile, |b|'s watch notification handler cancels |c|'s watch altogether - // before writing to |c| to wake up |d|. - // - // The net result should be that |c|'s context only gets notified once (from - // the first write to |d| above) and everyone else gets notified as expected. - - MojoHandle b_watcher; - MojoHandle cd_watcher; - WatchHelper helper; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&b_watcher)); - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&cd_watcher)); - - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - uintptr_t readable_d_context = helper.CreateContext(base::Bind( - [](base::WaitableEvent* event, MojoHandle d, MojoResult result, - MojoHandleSignalsState state) { - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(kTestMessageToD, ReadMessage(d)); - event->Signal(); - }, - &event, d)); - - static int num_expected_c_notifications = 1; - uintptr_t readable_c_context = helper.CreateContext(base::Bind( - [](MojoHandle cd_watcher, MojoHandle a, MojoHandle c, MojoHandle d, - MojoResult result, MojoHandleSignalsState state) { - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_GT(num_expected_c_notifications--, 0); - - // Trigger an eventual |readable_b_context| notification. - WriteMessage(a, kTestMessageToA); - - EXPECT_EQ(kTestMessageToC, ReadMessage(c)); - EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(cd_watcher, nullptr, nullptr, - nullptr, nullptr)); - - // Trigger another eventual |readable_c_context| notification. - WriteMessage(d, kTestMessageToC); - }, - cd_watcher, a, c, d)); - - uintptr_t readable_b_context = helper.CreateContext(base::Bind( - [](MojoHandle cd_watcher, uintptr_t readable_c_context, MojoHandle c, - MojoResult result, MojoHandleSignalsState state) { - EXPECT_EQ(MOJO_RESULT_OK, - MojoCancelWatch(cd_watcher, readable_c_context)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(cd_watcher, nullptr, nullptr, - nullptr, nullptr)); - - WriteMessage(c, kTestMessageToD); - }, - cd_watcher, readable_c_context, c)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(b_watcher, b, MOJO_HANDLE_SIGNAL_READABLE, - readable_b_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(cd_watcher, c, MOJO_HANDLE_SIGNAL_READABLE, - readable_c_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(cd_watcher, d, MOJO_HANDLE_SIGNAL_READABLE, - readable_d_context)); - - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(b_watcher, nullptr, nullptr, nullptr, nullptr)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(cd_watcher, nullptr, nullptr, nullptr, nullptr)); - - WriteMessage(d, kTestMessageToC); - event.Wait(); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(cd_watcher)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b_watcher)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); -} - -TEST_F(WatcherTest, CancelSelfInNotificationCallback) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - static const char kTestMessageToA[] = "hey a"; - - MojoHandle w; - WatchHelper helper; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - - static uintptr_t readable_a_context = helper.CreateContext(base::Bind( - [](base::WaitableEvent* event, MojoHandle w, MojoHandle a, - MojoResult result, MojoHandleSignalsState state) { - EXPECT_EQ(MOJO_RESULT_OK, result); - - // There should be no problem cancelling this watch from its own - // notification invocation. - EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(w, readable_a_context)); - EXPECT_EQ(kTestMessageToA, ReadMessage(a)); - - // Arming should fail because there are no longer any registered - // watches on the watcher. - EXPECT_EQ(MOJO_RESULT_NOT_FOUND, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - // And closing |a| should be fine (and should not invoke this - // notification with MOJO_RESULT_CANCELLED) for the same reason. - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); - - event->Signal(); - }, - &event, w, a)); - - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - WriteMessage(b, kTestMessageToA); - event.Wait(); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); -} - -TEST_F(WatcherTest, CloseWatcherInNotificationCallback) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - static const char kTestMessageToA1[] = "hey a"; - static const char kTestMessageToA2[] = "hey a again"; - - MojoHandle w; - WatchHelper helper; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - - uintptr_t readable_a_context = helper.CreateContext(base::Bind( - [](base::WaitableEvent* event, MojoHandle w, MojoHandle a, MojoHandle b, - MojoResult result, MojoHandleSignalsState state) { - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(kTestMessageToA1, ReadMessage(a)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - // There should be no problem closing this watcher from its own - // notification callback. - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); - - // And these should not trigger more notifications, because |w| has been - // closed already. - WriteMessage(b, kTestMessageToA2); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); - - event->Signal(); - }, - &event, w, a, b)); - - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - WriteMessage(b, kTestMessageToA1); - event.Wait(); -} - -TEST_F(WatcherTest, CloseWatcherAfterImplicitCancel) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - static const char kTestMessageToA[] = "hey a"; - - MojoHandle w; - WatchHelper helper; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - - uintptr_t readable_a_context = helper.CreateContext(base::Bind( - [](base::WaitableEvent* event, MojoHandle w, MojoHandle a, - MojoResult result, MojoHandleSignalsState state) { - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(kTestMessageToA, ReadMessage(a)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - // This will cue up a notification for |MOJO_RESULT_CANCELLED|... - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); - - // ...but it should never fire because we close the watcher here. - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); - - event->Signal(); - }, - &event, w, a)); - - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - WriteMessage(b, kTestMessageToA); - event.Wait(); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); -} - -TEST_F(WatcherTest, OtherThreadCancelDuringNotification) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - static const char kTestMessageToA[] = "hey a"; - - MojoHandle w; - WatchHelper helper; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - - base::WaitableEvent wait_for_notification( - base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - - base::WaitableEvent wait_for_cancellation( - base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - - static bool callback_done = false; - uintptr_t readable_a_context = helper.CreateContextWithCancel( - base::Bind( - [](base::WaitableEvent* wait_for_notification, MojoHandle w, - MojoHandle a, MojoResult result, MojoHandleSignalsState state) { - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(kTestMessageToA, ReadMessage(a)); - - wait_for_notification->Signal(); - - // Give the other thread sufficient time to race with the completion - // of this callback. There should be no race, since the cancellation - // notification must be mutually exclusive to this notification. - base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1)); - - callback_done = true; - }, - &wait_for_notification, w, a), - base::Bind( - [](base::WaitableEvent* wait_for_cancellation) { - EXPECT_TRUE(callback_done); - wait_for_cancellation->Signal(); - }, - &wait_for_cancellation)); - - ThreadedRunner runner(base::Bind( - [](base::WaitableEvent* wait_for_notification, - base::WaitableEvent* wait_for_cancellation, MojoHandle w, - uintptr_t readable_a_context) { - wait_for_notification->Wait(); - - // Cancel the watch while the notification is still running. - EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(w, readable_a_context)); - - wait_for_cancellation->Wait(); - - EXPECT_TRUE(callback_done); - }, - &wait_for_notification, &wait_for_cancellation, w, readable_a_context)); - runner.Start(); - - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - WriteMessage(b, kTestMessageToA); - runner.Join(); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); -} - -TEST_F(WatcherTest, WatchesCancelEachOtherFromNotifications) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - static const char kTestMessageToA[] = "hey a"; - static const char kTestMessageToB[] = "hey b"; - - base::WaitableEvent wait_for_a_to_notify( - base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - base::WaitableEvent wait_for_b_to_notify( - base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - base::WaitableEvent wait_for_a_to_cancel( - base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - base::WaitableEvent wait_for_b_to_cancel( - base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - - MojoHandle a_watcher; - MojoHandle b_watcher; - WatchHelper helper; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&a_watcher)); - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&b_watcher)); - - // We set up two watchers, one on |a| and one on |b|. They cancel each other - // from within their respective watch notifications. This should be safe, - // i.e., it should not deadlock, in spite of the fact that we also guarantee - // mutually exclusive notification execution (including cancellations) on any - // given watch. - bool a_cancelled = false; - bool b_cancelled = false; - static uintptr_t readable_b_context; - uintptr_t readable_a_context = helper.CreateContextWithCancel( - base::Bind( - [](base::WaitableEvent* wait_for_a_to_notify, - base::WaitableEvent* wait_for_b_to_notify, MojoHandle b_watcher, - MojoHandle a, MojoResult result, MojoHandleSignalsState state) { - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(kTestMessageToA, ReadMessage(a)); - wait_for_a_to_notify->Signal(); - wait_for_b_to_notify->Wait(); - EXPECT_EQ(MOJO_RESULT_OK, - MojoCancelWatch(b_watcher, readable_b_context)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b_watcher)); - }, - &wait_for_a_to_notify, &wait_for_b_to_notify, b_watcher, a), - base::Bind( - [](base::WaitableEvent* wait_for_a_to_cancel, - base::WaitableEvent* wait_for_b_to_cancel, bool* a_cancelled) { - *a_cancelled = true; - wait_for_a_to_cancel->Signal(); - wait_for_b_to_cancel->Wait(); - }, - &wait_for_a_to_cancel, &wait_for_b_to_cancel, &a_cancelled)); - - readable_b_context = helper.CreateContextWithCancel( - base::Bind( - [](base::WaitableEvent* wait_for_a_to_notify, - base::WaitableEvent* wait_for_b_to_notify, - uintptr_t readable_a_context, MojoHandle a_watcher, MojoHandle b, - MojoResult result, MojoHandleSignalsState state) { - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(kTestMessageToB, ReadMessage(b)); - wait_for_b_to_notify->Signal(); - wait_for_a_to_notify->Wait(); - EXPECT_EQ(MOJO_RESULT_OK, - MojoCancelWatch(a_watcher, readable_a_context)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a_watcher)); - }, - &wait_for_a_to_notify, &wait_for_b_to_notify, readable_a_context, - a_watcher, b), - base::Bind( - [](base::WaitableEvent* wait_for_a_to_cancel, - base::WaitableEvent* wait_for_b_to_cancel, bool* b_cancelled) { - *b_cancelled = true; - wait_for_b_to_cancel->Signal(); - wait_for_a_to_cancel->Wait(); - }, - &wait_for_a_to_cancel, &wait_for_b_to_cancel, &b_cancelled)); - - EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(a_watcher, a, MOJO_HANDLE_SIGNAL_READABLE, - readable_a_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(a_watcher, nullptr, nullptr, nullptr, nullptr)); - EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(b_watcher, b, MOJO_HANDLE_SIGNAL_READABLE, - readable_b_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(b_watcher, nullptr, nullptr, nullptr, nullptr)); - - ThreadedRunner runner( - base::Bind([](MojoHandle b) { WriteMessage(b, kTestMessageToA); }, b)); - runner.Start(); - - WriteMessage(a, kTestMessageToB); - - wait_for_a_to_cancel.Wait(); - wait_for_b_to_cancel.Wait(); - runner.Join(); - - EXPECT_TRUE(a_cancelled); - EXPECT_TRUE(b_cancelled); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); -} - -TEST_F(WatcherTest, AlwaysCancel) { - // Basic sanity check to ensure that all possible ways to cancel a watch - // result in a final MOJO_RESULT_CANCELLED notification. - - MojoHandle a, b; - CreateMessagePipe(&a, &b); - - MojoHandle w; - WatchHelper helper; - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - const base::Closure signal_event = - base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event)); - - // Cancel via |MojoCancelWatch()|. - uintptr_t context = helper.CreateContextWithCancel( - WatchHelper::ContextCallback(), signal_event); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, context)); - EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(w, context)); - event.Wait(); - event.Reset(); - - // Cancel by closing the watched handle. - context = helper.CreateContextWithCancel(WatchHelper::ContextCallback(), - signal_event); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, context)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); - event.Wait(); - event.Reset(); - - // Cancel by closing the watcher handle. - context = helper.CreateContextWithCancel(WatchHelper::ContextCallback(), - signal_event); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, b, MOJO_HANDLE_SIGNAL_READABLE, context)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); - event.Wait(); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); -} - -TEST_F(WatcherTest, ArmFailureCirculation) { - // Sanity check to ensure that all ready handles will eventually be returned - // over a finite number of calls to MojoArmWatcher(). - - constexpr size_t kNumTestPipes = 100; - constexpr size_t kNumTestHandles = kNumTestPipes * 2; - MojoHandle handles[kNumTestHandles]; - - // Create a bunch of pipes and make sure they're all readable. - for (size_t i = 0; i < kNumTestPipes; ++i) { - CreateMessagePipe(&handles[i], &handles[i + kNumTestPipes]); - WriteMessage(handles[i], "hey"); - WriteMessage(handles[i + kNumTestPipes], "hay"); - WaitForSignals(handles[i], MOJO_HANDLE_SIGNAL_READABLE); - WaitForSignals(handles[i + kNumTestPipes], MOJO_HANDLE_SIGNAL_READABLE); - } - - // Create a watcher and watch all of them. - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectOnlyCancel, &w)); - for (size_t i = 0; i < kNumTestHandles; ++i) { - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, handles[i], MOJO_HANDLE_SIGNAL_READABLE, i)); - } - - // Keep trying to arm |w| until every watch gets an entry in |ready_contexts|. - // If MojoArmWatcher() is well-behaved, this should terminate eventually. - std::set<uintptr_t> ready_contexts; - while (ready_contexts.size() < kNumTestHandles) { - uint32_t num_ready_contexts = 1; - uintptr_t ready_context; - MojoResult ready_result; - MojoHandleSignalsState ready_state; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoArmWatcher(w, &num_ready_contexts, &ready_context, - &ready_result, &ready_state)); - EXPECT_EQ(1u, num_ready_contexts); - EXPECT_EQ(MOJO_RESULT_OK, ready_result); - ready_contexts.insert(ready_context); - } - - for (size_t i = 0; i < kNumTestHandles; ++i) - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handles[i])); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); -} - -} // namespace -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/test/BUILD.gn b/mojo/edk/test/BUILD.gn deleted file mode 100644 index a15456a..0000000 --- a/mojo/edk/test/BUILD.gn +++ /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. - -import("//testing/test.gni") - -static_library("test_support") { - testonly = true - sources = [ - "mojo_test_base.cc", - "mojo_test_base.h", - "test_utils.h", - "test_utils_posix.cc", - "test_utils_win.cc", - ] - - if (!is_ios) { - sources += [ - "multiprocess_test_helper.cc", - "multiprocess_test_helper.h", - ] - } - - deps = [ - "//base", - "//base/test:test_support", - "//mojo/edk/system", - "//mojo/public/cpp/system", - "//testing/gtest", - ] -} - -source_set("run_all_unittests") { - testonly = true - sources = [ - "run_all_unittests.cc", - ] - - deps = [ - ":test_support", - ":test_support_impl", - "//base", - "//base/test:test_support", - "//mojo/edk/system", - "//mojo/public/c/test_support", - "//testing/gtest", - ] - - if (is_linux && !is_component_build) { - public_configs = [ "//build/config/gcc:rpath_for_built_shared_libraries" ] - } -} - -source_set("run_all_perftests") { - testonly = true - deps = [ - ":test_support_impl", - "//base", - "//base/test:test_support", - "//mojo/edk/system", - "//mojo/edk/test:test_support", - "//mojo/public/c/test_support", - ] - - sources = [ - "run_all_perftests.cc", - ] - - if (is_linux && !is_component_build) { - public_configs = [ "//build/config/gcc:rpath_for_built_shared_libraries" ] - } -} - -static_library("test_support_impl") { - testonly = true - deps = [ - "//base", - "//base/test:test_support", - "//mojo/public/c/test_support", - "//mojo/public/cpp/system", - ] - - 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. -# TODO(vtl): These don't really belong here. (They should be converted to -# apptests, but even apart from that these targets belong somewhere else.) - -group("public_tests") { - testonly = true - deps = [ - ":mojo_public_bindings_unittests", - ":mojo_public_system_perftests", - ":mojo_public_system_unittests", - ] -} - -test("mojo_public_bindings_perftests") { - deps = [ - ":run_all_perftests", - "//mojo/edk/test:test_support", - "//mojo/public/cpp/bindings/tests:perftests", - ] -} - -test("mojo_public_bindings_unittests") { - deps = [ - ":run_all_unittests", - "//mojo/edk/test:test_support", - "//mojo/public/cpp/bindings/tests", - ] -} - -test("mojo_public_system_perftests") { - deps = [ - ":run_all_perftests", - "//mojo/public/c/system/tests:perftests", - ] -} - -test("mojo_public_system_unittests") { - deps = [ - ":run_all_unittests", - "//mojo/public/cpp/system/tests", - ] -} diff --git a/mojo/edk/test/mojo_test_base.cc b/mojo/edk/test/mojo_test_base.cc deleted file mode 100644 index 71a5e3b..0000000 --- a/mojo/edk/test/mojo_test_base.cc +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/test/mojo_test_base.h" - -#include "base/memory/ptr_util.h" -#include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "base/synchronization/waitable_event.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/system/handle_signals_state.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/watcher.h" -#include "mojo/public/cpp/system/wait.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_MACOSX) && !defined(OS_IOS) -#include "base/mac/mach_port_broker.h" -#endif - -namespace mojo { -namespace edk { -namespace test { - -#if defined(OS_MACOSX) && !defined(OS_IOS) -namespace { -base::MachPortBroker* g_mach_broker = nullptr; -} -#endif - -MojoTestBase::MojoTestBase() { -#if defined(OS_MACOSX) && !defined(OS_IOS) - if (!g_mach_broker) { - g_mach_broker = new base::MachPortBroker("mojo_test"); - CHECK(g_mach_broker->Init()); - SetMachPortProvider(g_mach_broker); - } -#endif -} - -MojoTestBase::~MojoTestBase() {} - -MojoTestBase::ClientController& MojoTestBase::StartClient( - const std::string& client_name) { - clients_.push_back(base::MakeUnique<ClientController>( - client_name, this, process_error_callback_, launch_type_)); - return *clients_.back(); -} - -MojoTestBase::ClientController::ClientController( - const std::string& client_name, - MojoTestBase* test, - const ProcessErrorCallback& process_error_callback, - LaunchType launch_type) { -#if !defined(OS_IOS) -#if defined(OS_MACOSX) - // This lock needs to be held while launching the child because the Mach port - // broker only allows task ports to be received from known child processes. - // However, it can only know the child process's pid after the child has - // launched. To prevent a race where the child process sends its task port - // before the pid has been registered, the lock needs to be held over both - // launch and child pid registration. - base::AutoLock lock(g_mach_broker->GetLock()); -#endif - helper_.set_process_error_callback(process_error_callback); - pipe_ = helper_.StartChild(client_name, launch_type); -#if defined(OS_MACOSX) - g_mach_broker->AddPlaceholderForPid(helper_.test_child().Handle()); -#endif -#endif -} - -MojoTestBase::ClientController::~ClientController() { - CHECK(was_shutdown_) - << "Test clients should be waited on explicitly with WaitForShutdown()."; -} - -void MojoTestBase::ClientController::ClosePeerConnection() { -#if !defined(OS_IOS) - helper_.ClosePeerConnection(); -#endif -} - -int MojoTestBase::ClientController::WaitForShutdown() { - was_shutdown_ = true; -#if !defined(OS_IOS) - int retval = helper_.WaitForChildShutdown(); -#if defined(OS_MACOSX) - base::AutoLock lock(g_mach_broker->GetLock()); - g_mach_broker->InvalidatePid(helper_.test_child().Handle()); -#endif - return retval; -#else - NOTREACHED(); - return 1; -#endif -} - -// static -void MojoTestBase::CloseHandle(MojoHandle h) { - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h)); -} - -// static -void MojoTestBase::CreateMessagePipe(MojoHandle *p0, MojoHandle* p1) { - MojoCreateMessagePipe(nullptr, p0, p1); - CHECK_NE(*p0, MOJO_HANDLE_INVALID); - CHECK_NE(*p1, MOJO_HANDLE_INVALID); -} - -// static -void MojoTestBase::WriteMessageWithHandles(MojoHandle mp, - const std::string& message, - const MojoHandle *handles, - uint32_t num_handles) { - CHECK_EQ(MojoWriteMessage(mp, message.data(), - static_cast<uint32_t>(message.size()), - handles, num_handles, MOJO_WRITE_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); -} - -// static -void MojoTestBase::WriteMessage(MojoHandle mp, const std::string& message) { - WriteMessageWithHandles(mp, message, nullptr, 0); -} - -// static -std::string MojoTestBase::ReadMessageWithHandles( - MojoHandle mp, - MojoHandle* handles, - uint32_t expected_num_handles) { - CHECK_EQ(WaitForSignals(mp, MOJO_HANDLE_SIGNAL_READABLE), MOJO_RESULT_OK); - - uint32_t message_size = 0; - uint32_t num_handles = 0; - CHECK_EQ(MojoReadMessage(mp, nullptr, &message_size, nullptr, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_RESOURCE_EXHAUSTED); - CHECK_EQ(expected_num_handles, num_handles); - - std::string message(message_size, 'x'); - CHECK_EQ(MojoReadMessage(mp, &message[0], &message_size, handles, - &num_handles, MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - CHECK_EQ(message_size, message.size()); - CHECK_EQ(num_handles, expected_num_handles); - - return message; -} - -// static -std::string MojoTestBase::ReadMessageWithOptionalHandle(MojoHandle mp, - MojoHandle* handle) { - CHECK_EQ(WaitForSignals(mp, MOJO_HANDLE_SIGNAL_READABLE), MOJO_RESULT_OK); - - uint32_t message_size = 0; - uint32_t num_handles = 0; - CHECK_EQ(MojoReadMessage(mp, nullptr, &message_size, nullptr, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_RESOURCE_EXHAUSTED); - CHECK(num_handles == 0 || num_handles == 1); - - CHECK(handle); - - std::string message(message_size, 'x'); - CHECK_EQ(MojoReadMessage(mp, &message[0], &message_size, handle, - &num_handles, MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - CHECK_EQ(message_size, message.size()); - CHECK(num_handles == 0 || num_handles == 1); - - if (num_handles) - CHECK_NE(*handle, MOJO_HANDLE_INVALID); - else - *handle = MOJO_HANDLE_INVALID; - - return message; -} - -// static -std::string MojoTestBase::ReadMessage(MojoHandle mp) { - return ReadMessageWithHandles(mp, nullptr, 0); -} - -// static -void MojoTestBase::ReadMessage(MojoHandle mp, - char* data, - size_t num_bytes) { - CHECK_EQ(WaitForSignals(mp, MOJO_HANDLE_SIGNAL_READABLE), MOJO_RESULT_OK); - - uint32_t message_size = 0; - uint32_t num_handles = 0; - CHECK_EQ(MojoReadMessage(mp, nullptr, &message_size, nullptr, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_RESOURCE_EXHAUSTED); - CHECK_EQ(num_handles, 0u); - CHECK_EQ(message_size, num_bytes); - - CHECK_EQ(MojoReadMessage(mp, data, &message_size, nullptr, &num_handles, - MOJO_READ_MESSAGE_FLAG_NONE), - MOJO_RESULT_OK); - CHECK_EQ(num_handles, 0u); - CHECK_EQ(message_size, num_bytes); -} - -// static -void MojoTestBase::VerifyTransmission(MojoHandle source, - MojoHandle dest, - const std::string& message) { - WriteMessage(source, message); - - // We don't use EXPECT_EQ; failures on really long messages make life hard. - EXPECT_TRUE(message == ReadMessage(dest)); -} - -// static -void MojoTestBase::VerifyEcho(MojoHandle mp, - const std::string& message) { - VerifyTransmission(mp, mp, message); -} - -// static -MojoHandle MojoTestBase::CreateBuffer(uint64_t size) { - MojoHandle h; - EXPECT_EQ(MojoCreateSharedBuffer(nullptr, size, &h), MOJO_RESULT_OK); - return h; -} - -// static -MojoHandle MojoTestBase::DuplicateBuffer(MojoHandle h, bool read_only) { - MojoHandle new_handle; - MojoDuplicateBufferHandleOptions options = { - sizeof(MojoDuplicateBufferHandleOptions), - MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE - }; - if (read_only) - options.flags |= MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY; - EXPECT_EQ(MOJO_RESULT_OK, - MojoDuplicateBufferHandle(h, &options, &new_handle)); - return new_handle; -} - -// static -void MojoTestBase::WriteToBuffer(MojoHandle h, - size_t offset, - const base::StringPiece& s) { - char* data; - EXPECT_EQ(MOJO_RESULT_OK, - MojoMapBuffer(h, offset, s.size(), reinterpret_cast<void**>(&data), - MOJO_MAP_BUFFER_FLAG_NONE)); - memcpy(data, s.data(), s.size()); - EXPECT_EQ(MOJO_RESULT_OK, MojoUnmapBuffer(static_cast<void*>(data))); -} - -// static -void MojoTestBase::ExpectBufferContents(MojoHandle h, - size_t offset, - const base::StringPiece& s) { - char* data; - EXPECT_EQ(MOJO_RESULT_OK, - MojoMapBuffer(h, offset, s.size(), reinterpret_cast<void**>(&data), - MOJO_MAP_BUFFER_FLAG_NONE)); - EXPECT_EQ(s, base::StringPiece(data, s.size())); - EXPECT_EQ(MOJO_RESULT_OK, MojoUnmapBuffer(static_cast<void*>(data))); -} - -// static -void MojoTestBase::CreateDataPipe(MojoHandle *p0, - MojoHandle* p1, - size_t capacity) { - MojoCreateDataPipeOptions options; - options.struct_size = static_cast<uint32_t>(sizeof(options)); - options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE; - options.element_num_bytes = 1; - options.capacity_num_bytes = static_cast<uint32_t>(capacity); - - MojoCreateDataPipe(&options, p0, p1); - CHECK_NE(*p0, MOJO_HANDLE_INVALID); - CHECK_NE(*p1, MOJO_HANDLE_INVALID); -} - -// static -void MojoTestBase::WriteData(MojoHandle producer, const std::string& data) { - CHECK_EQ(WaitForSignals(producer, MOJO_HANDLE_SIGNAL_WRITABLE), - MOJO_RESULT_OK); - uint32_t num_bytes = static_cast<uint32_t>(data.size()); - CHECK_EQ(MojoWriteData(producer, data.data(), &num_bytes, - MOJO_WRITE_DATA_FLAG_ALL_OR_NONE), - MOJO_RESULT_OK); - CHECK_EQ(num_bytes, static_cast<uint32_t>(data.size())); -} - -// static -std::string MojoTestBase::ReadData(MojoHandle consumer, size_t size) { - CHECK_EQ(WaitForSignals(consumer, MOJO_HANDLE_SIGNAL_READABLE), - MOJO_RESULT_OK); - std::vector<char> buffer(size); - uint32_t num_bytes = static_cast<uint32_t>(size); - CHECK_EQ(MojoReadData(consumer, buffer.data(), &num_bytes, - MOJO_WRITE_DATA_FLAG_ALL_OR_NONE), - MOJO_RESULT_OK); - CHECK_EQ(num_bytes, static_cast<uint32_t>(size)); - - return std::string(buffer.data(), buffer.size()); -} - -// static -MojoHandleSignalsState MojoTestBase::GetSignalsState(MojoHandle handle) { - MojoHandleSignalsState signals_state; - CHECK_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(handle, &signals_state)); - return signals_state; -} - -// static -MojoResult MojoTestBase::WaitForSignals(MojoHandle handle, - MojoHandleSignals signals, - MojoHandleSignalsState* state) { - return Wait(Handle(handle), signals, state); -} - -} // namespace test -} // namespace edk -} // namespace mojo diff --git a/mojo/edk/test/mojo_test_base.h b/mojo/edk/test/mojo_test_base.h deleted file mode 100644 index 35e2c2b..0000000 --- a/mojo/edk/test/mojo_test_base.h +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_TEST_MOJO_TEST_BASE_H_ -#define MOJO_EDK_TEST_MOJO_TEST_BASE_H_ - -#include <memory> -#include <string> -#include <utility> - -#include "base/bind.h" -#include "base/callback.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/test/multiprocess_test_helper.h" -#include "mojo/public/c/system/types.h" -#include "mojo/public/cpp/system/message_pipe.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace edk { -namespace test { - -class MojoTestBase : public testing::Test { - public: - MojoTestBase(); - ~MojoTestBase() override; - - using LaunchType = MultiprocessTestHelper::LaunchType; - using HandlerCallback = base::Callback<void(ScopedMessagePipeHandle)>; - - class ClientController { - public: - ClientController(const std::string& client_name, - MojoTestBase* test, - const ProcessErrorCallback& process_error_callback, - LaunchType launch_type); - ~ClientController(); - - MojoHandle pipe() const { return pipe_.get().value(); } - - void ClosePeerConnection(); - int WaitForShutdown(); - - private: - friend class MojoTestBase; - -#if !defined(OS_IOS) - MultiprocessTestHelper helper_; -#endif - ScopedMessagePipeHandle pipe_; - bool was_shutdown_ = false; - - DISALLOW_COPY_AND_ASSIGN(ClientController); - }; - - // Set the callback to handle bad messages received from test client - // processes. This can be set to a different callback before starting each - // client. - void set_process_error_callback(const ProcessErrorCallback& callback) { - process_error_callback_ = callback; - } - - ClientController& StartClient(const std::string& client_name); - - template <typename HandlerFunc> - void StartClientWithHandler(const std::string& client_name, - HandlerFunc handler) { - int expected_exit_code = 0; - ClientController& c = StartClient(client_name); - handler(c.pipe(), &expected_exit_code); - EXPECT_EQ(expected_exit_code, c.WaitForShutdown()); - } - - // Closes a handle and expects success. - static void CloseHandle(MojoHandle h); - - ////// Message pipe test utilities /////// - - // Creates a new pipe, returning endpoint handles in |p0| and |p1|. - static void CreateMessagePipe(MojoHandle* p0, MojoHandle* p1); - - // Writes a string to the pipe, transferring handles in the process. - static void WriteMessageWithHandles(MojoHandle mp, - const std::string& message, - const MojoHandle* handles, - uint32_t num_handles); - - // Writes a string to the pipe with no handles. - static void WriteMessage(MojoHandle mp, const std::string& message); - - // Reads a string from the pipe, expecting to read an exact number of handles - // in the process. Returns the read string. - static std::string ReadMessageWithHandles(MojoHandle mp, - MojoHandle* handles, - uint32_t expected_num_handles); - - // Reads a string from the pipe, expecting either zero or one handles. - // If no handle is read, |handle| will be reset. - static std::string ReadMessageWithOptionalHandle(MojoHandle mp, - MojoHandle* handle); - - // Reads a string from the pipe, expecting to read no handles. - // Returns the string. - static std::string ReadMessage(MojoHandle mp); - - // Reads a string from the pipe, expecting to read no handles and exactly - // |num_bytes| bytes, which are read into |data|. - static void ReadMessage(MojoHandle mp, char* data, size_t num_bytes); - - // Writes |message| to |in| and expects to read it back from |out|. - static void VerifyTransmission(MojoHandle in, - MojoHandle out, - const std::string& message); - - // Writes |message| to |mp| and expects to read it back from the same handle. - static void VerifyEcho(MojoHandle mp, const std::string& message); - - //////// Shared buffer test utilities ///////// - - // Creates a new shared buffer. - static MojoHandle CreateBuffer(uint64_t size); - - // Duplicates a shared buffer to a new handle. - static MojoHandle DuplicateBuffer(MojoHandle h, bool read_only); - - // Maps a buffer, writes some data into it, and unmaps it. - static void WriteToBuffer(MojoHandle h, - size_t offset, - const base::StringPiece& s); - - // Maps a buffer, tests the value of some of its contents, and unmaps it. - static void ExpectBufferContents(MojoHandle h, - size_t offset, - const base::StringPiece& s); - - //////// Data pipe test utilities ///////// - - // Creates a new data pipe. - static void CreateDataPipe(MojoHandle* producer, - MojoHandle* consumer, - size_t capacity); - - // Writes data to a data pipe. - static void WriteData(MojoHandle producer, const std::string& data); - - // Reads data from a data pipe. - static std::string ReadData(MojoHandle consumer, size_t size); - - // Queries the signals state of |handle|. - static MojoHandleSignalsState GetSignalsState(MojoHandle handle); - - // Helper to block the calling thread waiting for signals to be raised. - static MojoResult WaitForSignals(MojoHandle handle, - MojoHandleSignals signals, - MojoHandleSignalsState* state = nullptr); - - void set_launch_type(LaunchType launch_type) { launch_type_ = launch_type; } - - private: - friend class ClientController; - - std::vector<std::unique_ptr<ClientController>> clients_; - - ProcessErrorCallback process_error_callback_; - - LaunchType launch_type_ = LaunchType::CHILD; - - DISALLOW_COPY_AND_ASSIGN(MojoTestBase); -}; - -// Launches a new child process running the test client |client_name| connected -// to a new message pipe bound to |pipe_name|. |pipe_name| is automatically -// closed on test teardown. -#define RUN_CHILD_ON_PIPE(client_name, pipe_name) \ - StartClientWithHandler( \ - #client_name, \ - [&](MojoHandle pipe_name, int *expected_exit_code) { { - -// Waits for the client to terminate and expects a return code of zero. -#define END_CHILD() \ - } \ - *expected_exit_code = 0; \ - }); - -// Wait for the client to terminate with a specific return code. -#define END_CHILD_AND_EXPECT_EXIT_CODE(code) \ - } \ - *expected_exit_code = code; \ - }); - -// Use this to declare the child process's "main()" function for tests using -// MojoTestBase and MultiprocessTestHelper. It returns an |int|, which will -// will be the process's exit code (but see the comment about -// WaitForChildShutdown()). -// -// The function is defined as a subclass of |test_base| to facilitate shared -// code between test clients and to allow clients to spawn children themselves. -// -// |pipe_name| will be bound to the MojoHandle of a message pipe connected -// to the parent process (see RUN_CHILD_ON_PIPE above.) This pipe handle is -// automatically closed on test client teardown. -#if !defined(OS_IOS) -#define DEFINE_TEST_CLIENT_WITH_PIPE(client_name, test_base, pipe_name) \ - class client_name##_MainFixture : public test_base { \ - void TestBody() override {} \ - public: \ - int Main(MojoHandle); \ - }; \ - MULTIPROCESS_TEST_MAIN_WITH_SETUP( \ - client_name##TestChildMain, \ - ::mojo::edk::test::MultiprocessTestHelper::ChildSetup) { \ - client_name##_MainFixture test; \ - return ::mojo::edk::test::MultiprocessTestHelper::RunClientMain( \ - base::Bind(&client_name##_MainFixture::Main, \ - base::Unretained(&test))); \ - } \ - int client_name##_MainFixture::Main(MojoHandle pipe_name) - -// This is a version of DEFINE_TEST_CLIENT_WITH_PIPE which can be used with -// gtest ASSERT/EXPECT macros. -#define DEFINE_TEST_CLIENT_TEST_WITH_PIPE(client_name, test_base, pipe_name) \ - class client_name##_MainFixture : public test_base { \ - void TestBody() override {} \ - public: \ - void Main(MojoHandle); \ - }; \ - MULTIPROCESS_TEST_MAIN_WITH_SETUP( \ - client_name##TestChildMain, \ - ::mojo::edk::test::MultiprocessTestHelper::ChildSetup) { \ - client_name##_MainFixture test; \ - return ::mojo::edk::test::MultiprocessTestHelper::RunClientTestMain( \ - base::Bind(&client_name##_MainFixture::Main, \ - base::Unretained(&test))); \ - } \ - void client_name##_MainFixture::Main(MojoHandle pipe_name) -#else // !defined(OS_IOS) -#define DEFINE_TEST_CLIENT_WITH_PIPE(client_name, test_base, pipe_name) -#define DEFINE_TEST_CLIENT_TEST_WITH_PIPE(client_name, test_base, pipe_name) -#endif // !defined(OS_IOS) - -} // namespace test -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_TEST_MOJO_TEST_BASE_H_ diff --git a/mojo/edk/test/multiprocess_test_helper.cc b/mojo/edk/test/multiprocess_test_helper.cc deleted file mode 100644 index cf37782..0000000 --- a/mojo/edk/test/multiprocess_test_helper.cc +++ /dev/null @@ -1,263 +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 <functional> -#include <set> -#include <utility> - -#include "base/base_paths.h" -#include "base/base_switches.h" -#include "base/bind.h" -#include "base/command_line.h" -#include "base/files/file_path.h" -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/path_service.h" -#include "base/process/kill.h" -#include "base/process/process_handle.h" -#include "base/run_loop.h" -#include "base/strings/stringprintf.h" -#include "base/task_runner.h" -#include "base/threading/thread_task_runner_handle.h" -#include "build/build_config.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/embedder/named_platform_handle.h" -#include "mojo/edk/embedder/named_platform_handle_utils.h" -#include "mojo/edk/embedder/pending_process_connection.h" -#include "mojo/edk/embedder/platform_channel_pair.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_WIN) -#include "base/win/windows_version.h" -#elif defined(OS_MACOSX) && !defined(OS_IOS) -#include "base/mac/mach_port_broker.h" -#endif - -namespace mojo { -namespace edk { -namespace test { - -namespace { - -const char kMojoPrimordialPipeToken[] = "mojo-primordial-pipe-token"; -const char kMojoNamedPipeName[] = "mojo-named-pipe-name"; - -template <typename Func> -int RunClientFunction(Func handler, bool pass_pipe_ownership_to_main) { - CHECK(MultiprocessTestHelper::primordial_pipe.is_valid()); - ScopedMessagePipeHandle pipe = - std::move(MultiprocessTestHelper::primordial_pipe); - MessagePipeHandle pipe_handle = - pass_pipe_ownership_to_main ? pipe.release() : pipe.get(); - return handler(pipe_handle.value()); -} - -} // namespace - -MultiprocessTestHelper::MultiprocessTestHelper() {} - -MultiprocessTestHelper::~MultiprocessTestHelper() { - CHECK(!test_child_.process.IsValid()); -} - -ScopedMessagePipeHandle MultiprocessTestHelper::StartChild( - const std::string& test_child_name, - LaunchType launch_type) { - return StartChildWithExtraSwitch(test_child_name, std::string(), - std::string(), launch_type); -} - -ScopedMessagePipeHandle MultiprocessTestHelper::StartChildWithExtraSwitch( - const std::string& test_child_name, - const std::string& switch_string, - const std::string& switch_value, - LaunchType launch_type) { - CHECK(!test_child_name.empty()); - CHECK(!test_child_.process.IsValid()); - - std::string test_child_main = test_child_name + "TestChildMain"; - - // Manually construct the new child's commandline to avoid copying unwanted - // values. - base::CommandLine command_line( - base::GetMultiProcessTestChildBaseCommandLine().GetProgram()); - - std::set<std::string> uninherited_args; - uninherited_args.insert("mojo-platform-channel-handle"); - uninherited_args.insert(switches::kTestChildProcess); - - // Copy commandline switches from the parent process, except for the - // multiprocess client name and mojo message pipe handle; this allows test - // clients to spawn other test clients. - for (const auto& entry : - base::CommandLine::ForCurrentProcess()->GetSwitches()) { - if (uninherited_args.find(entry.first) == uninherited_args.end()) - command_line.AppendSwitchNative(entry.first, entry.second); - } - - PlatformChannelPair channel; - NamedPlatformHandle named_pipe; - HandlePassingInformation handle_passing_info; - if (launch_type == LaunchType::CHILD || launch_type == LaunchType::PEER) { - channel.PrepareToPassClientHandleToChildProcess(&command_line, - &handle_passing_info); - } else if (launch_type == LaunchType::NAMED_CHILD || - launch_type == LaunchType::NAMED_PEER) { -#if defined(OS_POSIX) - base::FilePath temp_dir; - CHECK(base::PathService::Get(base::DIR_TEMP, &temp_dir)); - named_pipe = NamedPlatformHandle( - temp_dir.AppendASCII(GenerateRandomToken()).value()); -#else - named_pipe = NamedPlatformHandle(GenerateRandomToken()); -#endif - command_line.AppendSwitchNative(kMojoNamedPipeName, named_pipe.name); - } - - if (!switch_string.empty()) { - CHECK(!command_line.HasSwitch(switch_string)); - if (!switch_value.empty()) - command_line.AppendSwitchASCII(switch_string, switch_value); - else - command_line.AppendSwitch(switch_string); - } - - base::LaunchOptions options; -#if defined(OS_POSIX) - options.fds_to_remap = &handle_passing_info; -#elif defined(OS_WIN) - options.start_hidden = true; - if (base::win::GetVersion() >= base::win::VERSION_VISTA) - options.handles_to_inherit = &handle_passing_info; - else - options.inherit_handles = true; -#else -#error "Not supported yet." -#endif - - // NOTE: In the case of named pipes, it's important that the server handle be - // created before the child process is launched; otherwise the server binding - // the pipe path can race with child's connection to the pipe. - ScopedPlatformHandle server_handle; - if (launch_type == LaunchType::CHILD || launch_type == LaunchType::PEER) { - server_handle = channel.PassServerHandle(); - } else if (launch_type == LaunchType::NAMED_CHILD || - launch_type == LaunchType::NAMED_PEER) { - server_handle = CreateServerHandle(named_pipe); - } - - PendingProcessConnection process; - ScopedMessagePipeHandle pipe; - if (launch_type == LaunchType::CHILD || - launch_type == LaunchType::NAMED_CHILD) { - std::string pipe_token; - pipe = process.CreateMessagePipe(&pipe_token); - command_line.AppendSwitchASCII(kMojoPrimordialPipeToken, pipe_token); - } else if (launch_type == LaunchType::PEER || - launch_type == LaunchType::NAMED_PEER) { - peer_token_ = mojo::edk::GenerateRandomToken(); - pipe = ConnectToPeerProcess(std::move(server_handle), peer_token_); - } - - test_child_ = - base::SpawnMultiProcessTestChild(test_child_main, command_line, options); - if (launch_type == LaunchType::CHILD || launch_type == LaunchType::PEER) - channel.ChildProcessLaunched(); - - if (launch_type == LaunchType::CHILD || - launch_type == LaunchType::NAMED_CHILD) { - DCHECK(server_handle.is_valid()); - process.Connect(test_child_.process.Handle(), - ConnectionParams(std::move(server_handle)), - process_error_callback_); - } - - CHECK(test_child_.process.IsValid()); - return pipe; -} - -int MultiprocessTestHelper::WaitForChildShutdown() { - CHECK(test_child_.process.IsValid()); - - int rv = -1; - WaitForMultiprocessTestChildExit(test_child_.process, - TestTimeouts::action_timeout(), &rv); - test_child_.process.Close(); - return rv; -} - -void MultiprocessTestHelper::ClosePeerConnection() { - DCHECK(!peer_token_.empty()); - ::mojo::edk::ClosePeerConnection(peer_token_); - peer_token_.clear(); -} - -bool MultiprocessTestHelper::WaitForChildTestShutdown() { - return WaitForChildShutdown() == 0; -} - -// static -void MultiprocessTestHelper::ChildSetup() { - CHECK(base::CommandLine::InitializedForCurrentProcess()); - - std::string primordial_pipe_token = - base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - kMojoPrimordialPipeToken); - NamedPlatformHandle named_pipe( - base::CommandLine::ForCurrentProcess()->GetSwitchValueNative( - kMojoNamedPipeName)); - if (!primordial_pipe_token.empty()) { - primordial_pipe = CreateChildMessagePipe(primordial_pipe_token); -#if defined(OS_MACOSX) && !defined(OS_IOS) - CHECK(base::MachPortBroker::ChildSendTaskPortToParent("mojo_test")); -#endif - if (named_pipe.is_valid()) { - SetParentPipeHandle(CreateClientHandle(named_pipe)); - } else { - SetParentPipeHandle( - PlatformChannelPair::PassClientHandleFromParentProcess( - *base::CommandLine::ForCurrentProcess())); - } - } else { - if (named_pipe.is_valid()) { - primordial_pipe = ConnectToPeerProcess(CreateClientHandle(named_pipe)); - } else { - primordial_pipe = ConnectToPeerProcess( - PlatformChannelPair::PassClientHandleFromParentProcess( - *base::CommandLine::ForCurrentProcess())); - } - } -} - -// static -int MultiprocessTestHelper::RunClientMain( - const base::Callback<int(MojoHandle)>& main, - bool pass_pipe_ownership_to_main) { - return RunClientFunction( - [main](MojoHandle handle) { return main.Run(handle); }, - pass_pipe_ownership_to_main); -} - -// static -int MultiprocessTestHelper::RunClientTestMain( - const base::Callback<void(MojoHandle)>& main) { - return RunClientFunction( - [main](MojoHandle handle) { - main.Run(handle); - return (::testing::Test::HasFatalFailure() || - ::testing::Test::HasNonfatalFailure()) - ? 1 - : 0; - }, - true /* close_pipe_on_exit */); -} - -// static -mojo::ScopedMessagePipeHandle MultiprocessTestHelper::primordial_pipe; - -} // namespace test -} // namespace edk -} // 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 dc1c9bc..0000000 --- a/mojo/edk/test/multiprocess_test_helper.h +++ /dev/null @@ -1,110 +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/callback.h" -#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/embedder.h" -#include "mojo/public/cpp/system/message_pipe.h" -#include "testing/multiprocess_func_list.h" - -namespace mojo { - -namespace edk { - -namespace test { - -class MultiprocessTestHelper { - public: - using HandlerCallback = base::Callback<void(ScopedMessagePipeHandle)>; - - enum class LaunchType { - // Launch the child process as a child in the mojo system. - CHILD, - - // Launch the child process as an unrelated peer process in the mojo system. - PEER, - - // Launch the child process as a child in the mojo system, using a named - // pipe. - NAMED_CHILD, - - // Launch the child process as an unrelated peer process in the mojo - // system, using a named pipe. - NAMED_PEER, - }; - - 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). - ScopedMessagePipeHandle StartChild( - const std::string& test_child_name, - LaunchType launch_type = LaunchType::CHILD); - - // Like |StartChild()|, but appends an extra switch (with ASCII value) to the - // command line. (The switch must not already be present in the default - // command line.) - ScopedMessagePipeHandle StartChildWithExtraSwitch( - const std::string& test_child_name, - const std::string& switch_string, - const std::string& switch_value, - LaunchType launch_type); - - void set_process_error_callback(const ProcessErrorCallback& callback) { - process_error_callback_ = callback; - } - - void ClosePeerConnection(); - - // 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());|. - bool WaitForChildTestShutdown(); - - const base::Process& test_child() const { return test_child_.process; } - - // Used by macros in mojo/edk/test/mojo_test_base.h to support multiprocess - // test client initialization. - static void ChildSetup(); - static int RunClientMain(const base::Callback<int(MojoHandle)>& main, - bool pass_pipe_ownership_to_main = false); - static int RunClientTestMain(const base::Callback<void(MojoHandle)>& main); - - // For use (and only valid) in the child process: - static mojo::ScopedMessagePipeHandle primordial_pipe; - - private: - // Valid after |StartChild()| and before |WaitForChildShutdown()|. - base::SpawnChildResult test_child_; - - ProcessErrorCallback process_error_callback_; - - std::string peer_token_; - - DISALLOW_COPY_AND_ASSIGN(MultiprocessTestHelper); -}; - -} // namespace test -} // namespace edk -} // 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 f7e9e83..0000000 --- a/mojo/edk/test/multiprocess_test_helper_unittest.cc +++ /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. - -#include "mojo/edk/test/multiprocess_test_helper.h" - -#include <stddef.h> - -#include <utility> - -#include "base/logging.h" -#include "build/build_config.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/test_utils.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 edk { -namespace test { -namespace { - -bool IsNonBlocking(const 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.handle, F_GETFL) & O_NONBLOCK; -#endif -} - -bool WriteByte(const PlatformHandle& handle, char c) { - size_t bytes_written = 0; - BlockingWrite(handle, &c, 1, &bytes_written); - return bytes_written == 1; -} - -bool ReadByte(const PlatformHandle& handle, char* c) { - size_t bytes_read = 0; - BlockingRead(handle, c, 1, &bytes_read); - return bytes_read == 1; -} - -using MultiprocessTestHelperTest = testing::Test; - -TEST_F(MultiprocessTestHelperTest, 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; -} - -TEST_F(MultiprocessTestHelperTest, TestChildMainNotFound) { - MultiprocessTestHelper helper; - helper.StartChild("NoSuchTestChildMain"); - int result = helper.WaitForChildShutdown(); - EXPECT_FALSE(result >= 0 && result <= 127); -} - -TEST_F(MultiprocessTestHelperTest, PassedChannel) { - MultiprocessTestHelper helper; - EXPECT_TRUE(helper.server_platform_handle.is_valid()); - helper.StartChild("PassedChannel"); - - // Take ownership of the handle. - ScopedPlatformHandle handle = std::move(helper.server_platform_handle); - - // 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. - ScopedPlatformHandle handle = - std::move(MultiprocessTestHelper::client_platform_handle); - - // 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); -} - -TEST_F(MultiprocessTestHelperTest, 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())); -} - -TEST_F(MultiprocessTestHelperTest, 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"; -} - -TEST_F(MultiprocessTestHelperTest, 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 edk -} // 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 3ce3b47..0000000 --- a/mojo/edk/test/run_all_perftests.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/command_line.h" -#include "base/test/multiprocess_test.h" -#include "base/test/perf_test_suite.h" -#include "base/test/test_io_thread.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/embedder/scoped_ipc_support.h" -#include "mojo/edk/test/multiprocess_test_helper.h" -#include "mojo/edk/test/test_support_impl.h" -#include "mojo/public/tests/test_support_private.h" - -int main(int argc, char** argv) { - base::PerfTestSuite test(argc, argv); - - mojo::edk::Init(); - base::TestIOThread test_io_thread(base::TestIOThread::kAutoStart); - mojo::edk::ScopedIPCSupport ipc_support( - test_io_thread.task_runner(), - mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN); - mojo::test::TestSupport::Init(new mojo::edk::test::TestSupportImpl()); - - return test.Run(); -} diff --git a/mojo/edk/test/run_all_unittests.cc b/mojo/edk/test/run_all_unittests.cc deleted file mode 100644 index a057825..0000000 --- a/mojo/edk/test/run_all_unittests.cc +++ /dev/null @@ -1,49 +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 <signal.h> - -#include "base/bind.h" -#include "base/command_line.h" -#include "base/test/launcher/unit_test_launcher.h" -#include "base/test/multiprocess_test.h" -#include "base/test/test_io_thread.h" -#include "base/test/test_suite.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/embedder/scoped_ipc_support.h" -#include "mojo/edk/test/multiprocess_test_helper.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) { -#if !defined(OS_ANDROID) - // 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. - testing::GTEST_FLAG(death_test_style) = "threadsafe"; -#endif -#if defined(OS_ANDROID) - // On android, the test framework has a signal handler that will print a - // [ CRASH ] line when the application crashes. This breaks death test has the - // test runner will consider the death of the child process a test failure. - // Removing the signal handler solves this issue. - signal(SIGABRT, SIG_DFL); -#endif - - base::TestSuite test_suite(argc, argv); - - mojo::edk::Init(); - - mojo::test::TestSupport::Init(new mojo::edk::test::TestSupportImpl()); - base::TestIOThread test_io_thread(base::TestIOThread::kAutoStart); - - mojo::edk::ScopedIPCSupport ipc_support( - test_io_thread.task_runner(), - mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN); - 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 25684e4..0000000 --- a/mojo/edk/test/test_support_impl.cc +++ /dev/null @@ -1,84 +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 <stddef.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 edk { -namespace test { -namespace { - -base::FilePath ResolveSourceRootRelativePath(const char* relative_path) { - base::FilePath path; - if (!PathService::Get(base::DIR_SOURCE_ROOT, &path)) - return base::FilePath(); - - for (const base::StringPiece& component : base::SplitStringPiece( - relative_path, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { - if (!component.empty()) - path = path.AppendASCII(component); - } - - 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 edk -} // 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 ebb5ce6..0000000 --- a/mojo/edk/test/test_support_impl.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_TEST_TEST_SUPPORT_IMPL_H_ -#define MOJO_EDK_TEST_TEST_SUPPORT_IMPL_H_ - -#include <stdio.h> - -#include "base/macros.h" -#include "mojo/public/tests/test_support_private.h" - -namespace mojo { -namespace edk { -namespace test { - -class TestSupportImpl : public mojo::test::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 edk -} // 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 939171b..0000000 --- a/mojo/edk/test/test_utils.h +++ /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. - -#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/scoped_file.h" -#include "mojo/edk/embedder/platform_handle.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" - -namespace mojo { -namespace edk { -namespace test { - -// On success, |bytes_written| is updated to the number of bytes written; -// otherwise it is untouched. -bool BlockingWrite(const 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 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 PlatformHandle& handle, - void* buffer, - size_t buffer_size, - size_t* bytes_read); - -// Gets a (scoped) |PlatformHandle| from the given (scoped) |FILE|. -ScopedPlatformHandle PlatformHandleFromFILE(base::ScopedFILE fp); - -// Gets a (scoped) |FILE| from a (scoped) |PlatformHandle|. -base::ScopedFILE FILEFromPlatformHandle(ScopedPlatformHandle h, - const char* mode); - -} // namespace test -} // namespace edk -} // 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 60d5db5..0000000 --- a/mojo/edk/test/test_utils_posix.cc +++ /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. - -#include "mojo/edk/test/test_utils.h" - -#include <fcntl.h> -#include <stddef.h> -#include <unistd.h> - -#include "base/posix/eintr_wrapper.h" - -namespace mojo { -namespace edk { -namespace test { - -bool BlockingWrite(const PlatformHandle& handle, - const void* buffer, - size_t bytes_to_write, - size_t* bytes_written) { - int original_flags = fcntl(handle.handle, F_GETFL); - if (original_flags == -1 || - fcntl(handle.handle, F_SETFL, original_flags & (~O_NONBLOCK)) != 0) { - return false; - } - - ssize_t result = HANDLE_EINTR(write(handle.handle, buffer, bytes_to_write)); - - fcntl(handle.handle, F_SETFL, original_flags); - - if (result < 0) - return false; - - *bytes_written = result; - return true; -} - -bool BlockingRead(const PlatformHandle& handle, - void* buffer, - size_t buffer_size, - size_t* bytes_read) { - int original_flags = fcntl(handle.handle, F_GETFL); - if (original_flags == -1 || - fcntl(handle.handle, F_SETFL, original_flags & (~O_NONBLOCK)) != 0) { - return false; - } - - ssize_t result = HANDLE_EINTR(read(handle.handle, buffer, buffer_size)); - - fcntl(handle.handle, F_SETFL, original_flags); - - if (result < 0) - return false; - - *bytes_read = result; - return true; -} - -bool NonBlockingRead(const PlatformHandle& handle, - void* buffer, - size_t buffer_size, - size_t* bytes_read) { - ssize_t result = HANDLE_EINTR(read(handle.handle, buffer, buffer_size)); - - if (result < 0) { - if (errno != EAGAIN && errno != EWOULDBLOCK) - return false; - - *bytes_read = 0; - } else { - *bytes_read = result; - } - - return true; -} - -ScopedPlatformHandle PlatformHandleFromFILE(base::ScopedFILE fp) { - CHECK(fp); - int rv = dup(fileno(fp.get())); - PCHECK(rv != -1) << "dup"; - return ScopedPlatformHandle(PlatformHandle(rv)); -} - -base::ScopedFILE FILEFromPlatformHandle(ScopedPlatformHandle h, - const char* mode) { - CHECK(h.is_valid()); - base::ScopedFILE rv(fdopen(h.release().handle, mode)); - PCHECK(rv) << "fdopen"; - return rv; -} - -} // namespace test -} // namespace edk -} // 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 17bf5bb..0000000 --- a/mojo/edk/test/test_utils_win.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/test/test_utils.h" - -#include <windows.h> -#include <fcntl.h> -#include <io.h> -#include <stddef.h> -#include <string.h> - -namespace mojo { -namespace edk { -namespace test { - -bool BlockingWrite(const 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 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 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; -} - -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 ScopedPlatformHandle(PlatformHandle(rv)); -} - -base::ScopedFILE FILEFromPlatformHandle(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; -} - -} // namespace test -} // namespace edk -} // namespace mojo |