diff options
Diffstat (limited to 'chromium/mojo')
269 files changed, 8645 insertions, 8708 deletions
diff --git a/chromium/mojo/BUILD.gn b/chromium/mojo/BUILD.gn index b0ad79f3e8d..f6cd5b6c6ca 100644 --- a/chromium/mojo/BUILD.gn +++ b/chromium/mojo/BUILD.gn @@ -17,7 +17,7 @@ group("mojo") { } if (is_android) { - deps += [ "//mojo/android" ] + deps += [ "//mojo/public/java/system" ] } deps += [ "//services/service_manager:all" ] @@ -35,8 +35,8 @@ group("tests") { test("mojo_unittests") { deps = [ - "//mojo/edk/system:test_sources", - "//mojo/edk/test:run_all_unittests", + "//mojo/core:test_sources", + "//mojo/core/test:run_all_unittests", "//mojo/public/cpp/base:tests", "//mojo/public/cpp/bindings/tests", "//mojo/public/cpp/platform/tests", @@ -46,22 +46,23 @@ test("mojo_unittests") { test("mojo_perftests") { deps = [ - "//mojo/edk/test:run_all_perftests", - "//mojo/edk/test:test_support", + "//mojo/core/test:run_all_perftests", + "//mojo/core/test:test_support", "//mojo/public/c/system/tests:perftests", "//mojo/public/cpp/bindings/tests:perftests", ] if (!is_ios) { sources = [ - "//mojo/edk/system/message_pipe_perftest.cc", + "//mojo/core/message_pipe_perftest.cc", ] deps += [ "//base", "//base/test:test_support", - "//mojo/edk", - "//mojo/edk/system:test_utils", + "//mojo/core:embedder_internal", + "//mojo/core:test_utils", + "//mojo/core/embedder", "//testing/gtest", ] } diff --git a/chromium/mojo/README.md b/chromium/mojo/README.md index 20852f12bde..46d14fa4c3b 100644 --- a/chromium/mojo/README.md +++ b/chromium/mojo/README.md @@ -19,42 +19,73 @@ out [Converting Legacy Chrome IPC To Mojo](/ipc/README.md). ## System Overview -Mojo is a layered collection of runtime libraries providing a platform-agnostic +Mojo is a collection of runtime libraries providing a platform-agnostic abstraction of common IPC primitives, a message IDL format, and a bindings library with code generation for multiple target languages to facilitate convenient message passing across arbitrary inter- and intra-process boundaries. -The documentation here is segmented according to the different isolated layers -and libraries comprising the system. The basic hierarchy of features is as -follows: +The documentation here is segmented according to the different libraries +comprising Mojo. The basic hierarchy of features is as follows: -![Mojo Library Layering: EDK on bottom, different language bindings on top, public system support APIs in the middle](https://docs.google.com/drawings/d/1RwhzKblXUZw-zhy_KDVobAYprYSqxZzopXTUsbwzDPw/pub?w=570&h=324) +![Mojo Library Layering: Core on bottom, language bindings on top, public system support APIs in the middle](https://docs.google.com/drawings/d/1RwhzKblXUZw-zhy_KDVobAYprYSqxZzopXTUsbwzDPw/pub?w=570&h=324) -## Embedder Development Kit (EDK) -Every process to be interconnected via Mojo IPC is called a **Mojo embedder** -and needs to embed the -[**Embedder Development Kit (EDK)**](/mojo/edk/embedder/README.md) library. The -EDK exposes the means for an embedder to physically connect one process to another -using any supported native IPC primitive (*e.g.,* a UNIX domain socket or -Windows named pipe) on the host platform. +## Mojo Core +In order to use any of the more interesting high-level support libraries like +the System APIs or Bindings APIs, a process must first initialize Mojo Core. +This is a one-time initialization which remains active for the remainder of the +process's lifetime. There are two ways to initialize Mojo Core: via the Embedder +API, or through a dynamically linked library. -Details regarding where and how an application process actually embeds and -configures the EDK are generaly hidden from the rest of the application code, -and applications instead use the public System and Bindings APIs to get things -done within processes that embed Mojo. +### Embedding +Many processes to be interconnected via Mojo are **embedders**, meaning that +they statically link against the `//mojo/core/embedder` target and initialize +Mojo support within each process by calling `mojo::core::Init()`. See +[**Mojo Core Embedder API**](/mojo/core/embedder/README.md) for more details. + +This is a reasonable option when you can guarantee that all interconnected +process binaries are linking against precisely the same revision of Mojo Core. +To support other scenarios, use dynamic linking. + +## Dynamic Linking +On some platforms, it's also possible for applications to rely on a +dynamically-linked Mojo Core library (`libmojo_core.so` or `mojo_core.dll`) +instead of statically linking against Mojo Core. + +In order to take advantage of this mechanism, the corresponding library must be +present in either: + + - The working directory of the application + - A directory named by the `MOJO_CORE_LIBRARY_PATH` environment variable + - A directory named explicitly by the application at runtime + +Instead of calling `mojo::core::Init()` as embedders do, an application using +dynamic Mojo Core instead calls `MojoInitialize()` from the C System API. This +call will attempt to locate (see above) and load a Mojo Core library to support +subsequent Mojo API usage within the process. + +Note that the Mojo Core shared library presents a stable, forward-compatible C +ABI which can support all current and future versions of the higher-level, +public (and not binary-stable) System and Bindings APIs. ## C System API -Once the EDK is initialized within a process, the public +Once Mojo is initialized within a process, the public [**C System API**](/mojo/public/c/system/README.md) is usable on any thread for the remainder of the process's lifetime. This is a lightweight API with a -relatively small (and eventually stable) ABI. Typically this API is not used -directly, but it is the foundation upon which all remaining upper layers are -built. It exposes the fundamental capabilities to create and interact with -various types of Mojo handles including **message pipes**, **data pipes**, and -**shared buffers**. +relatively small, stable, forward-compatible ABI, comprising the total public +API surface of the Mojo Core library. -## High-Level System APIs +This API is rarely used directly, but it is the foundation upon which all +higher-level Mojo APIs are built. It exposes the fundamental capabilities to +create and interact Mojo primitives like **message pipes**, **data pipes**, and +**shared buffers**, as well as APIs to help bootstrap connections among +processes. +## Platform Support API +Mojo provides a small collection of abstractions around platform-specific IPC +primitives to facilitate bootstrapping Mojo IPC between two processes. See the +[Platform API](/mojo/public/cpp/platform/README.md) documentation for details. + +## High-Level System APIs There is a relatively small, higher-level system API for each supported language, built upon the low-level C API. Like the C API, direct usage of these system APIs is rare compared to the bindings APIs, but it is sometimes desirable diff --git a/chromium/mojo/android/javatests/DEPS b/chromium/mojo/android/javatests/DEPS deleted file mode 100644 index 78cf465ca0c..00000000000 --- a/chromium/mojo/android/javatests/DEPS +++ /dev/null @@ -1,4 +0,0 @@ -include_rules = [ - # out should be allowed by default, but bots are failing on this. - "+out", -] diff --git a/chromium/mojo/core/BUILD.gn b/chromium/mojo/core/BUILD.gn new file mode 100644 index 00000000000..49a537bcb74 --- /dev/null +++ b/chromium/mojo/core/BUILD.gn @@ -0,0 +1,325 @@ +# Copyright 2018 The Chromium 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") + +component("embedder_internal") { + output_name = "mojo_core_embedder_internal" + public_deps = [ + ":impl_for_embedder", + ] + visibility = [ + ":test_sources", + "//mojo:*", + "//mojo/core/embedder", + ] +} + +# Bits of the EDK library which do not depend on public API linkage. It is +# not allowed for this target or any of its transitive dependencies to depend +# on anything under //mojo/public beyond strict C type definitions. +# +# This is templated because it's consumed by both the ":embedder_internal" +# component library as well as the ":mojo_core" shared library. In the former +# case we want to export symbols, but in the latter case we don't. The template +# stamps out two nearly identical targets which differ only in what symbols they +# export. +template("core_impl_source_set") { + source_set(target_name) { + if (invoker.for_shared_library) { + visibility = [ ":shared_library" ] + } else { + visibility = [ ":embedder_internal" ] + } + + public = [ + "channel.h", + "configuration.h", + "connection_params.h", + "core.h", + "data_pipe_consumer_dispatcher.h", + "data_pipe_control_message.h", + "data_pipe_producer_dispatcher.h", + "dispatcher.h", + "embedder/configuration.h", + "embedder/process_error_callback.h", + "entrypoints.h", + "handle_signals_state.h", + "handle_table.h", + "invitation_dispatcher.h", + "message_pipe_dispatcher.h", + "node_controller.h", + "options_validation.h", + "platform_handle_dispatcher.h", + "platform_handle_utils.h", + "platform_shared_memory_mapping.h", + "request_context.h", + "scoped_process_handle.h", + "shared_buffer_dispatcher.h", + "user_message_impl.h", + ] + + sources = [ + "atomic_flag.h", + "broker.h", + "broker_win.cc", + "channel.cc", + "channel_win.cc", + "configuration.cc", + "connection_params.cc", + "core.cc", + "data_pipe_consumer_dispatcher.cc", + "data_pipe_control_message.cc", + "data_pipe_producer_dispatcher.cc", + "dispatcher.cc", + "entrypoints.cc", + "handle_table.cc", + "invitation_dispatcher.cc", + "message_pipe_dispatcher.cc", + "node_channel.cc", + "node_channel.h", + "node_controller.cc", + "platform_handle_dispatcher.cc", + "platform_handle_in_transit.cc", + "platform_handle_in_transit.h", + "platform_handle_utils.cc", + "platform_shared_memory_mapping.cc", + "request_context.cc", + "scoped_process_handle.cc", + "shared_buffer_dispatcher.cc", + "user_message_impl.cc", + "watch.cc", + "watch.h", + "watcher_dispatcher.cc", + "watcher_dispatcher.h", + "watcher_set.cc", + "watcher_set.h", + ] + + public_deps = [ + "//base", + "//mojo/core/ports", + "//mojo/public/c/system:headers", + "//mojo/public/cpp/platform", + ] + + if (is_fuchsia) { + sources += [ "channel_fuchsia.cc" ] + + public_deps += [ "//third_party/fuchsia-sdk:fdio" ] + } + + if (is_posix) { + if (!is_nacl || is_nacl_nonsfi) { + sources += [ + "broker_posix.cc", + "channel_posix.cc", + ] + } + } + + 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_host.h", + ] + } + + defines = [] + if (invoker.for_shared_library) { + defines += [ "MOJO_CORE_SHARED_LIBRARY" ] + } else { + defines += [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ] + } + + deps = [] + if (is_android) { + deps += [ "//third_party/ashmem" ] + } + if (!is_nacl) { + deps += [ "//crypto" ] + } + + if (is_win) { + cflags = [ "/wd4324" ] # Structure was padded due to __declspec(align()), + # which is uninteresting. + } + + # 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_CORE_LEGACY_PROTOCOL" ] + } + } +} + +core_impl_source_set("impl_for_embedder") { + for_shared_library = false +} + +if (is_chromeos || is_linux || is_android || is_win) { + core_impl_source_set("impl_for_shared_library") { + for_shared_library = true + } + + shared_library("shared_library") { + output_name = "mojo_core" + sources = [ + "mojo_core.cc", + ] + defines = [ "MOJO_CORE_SHARED_LIBRARY" ] + deps = [ + ":impl_for_shared_library", + "//mojo/public/c/system:headers", + ] + if (is_win) { + inputs = [ + "mojo_core.def", + ] + ldflags = [ "/DEF:" + rebase_path("mojo_core.def", root_build_dir) ] + } else { + configs += [ ":export_only_thunks_api" ] + } + } + + if (is_chromeos) { + if (target_cpu == "arm" || target_cpu == "arm64") { + android32_toolchain = "android_clang_arm" + android64_toolchain = "android_clang_arm64" + } else { + android32_toolchain = "android_clang_x86" + android64_toolchain = "android_clang_x64" + } + + group("shared_libraries_for_arc") { + deps = [ + ":shared_library_arc32", + ":shared_library_arc64", + ] + } + + copy("shared_library_arc32") { + sources = [ + "${root_out_dir}/${android32_toolchain}/libmojo_core.so", + ] + outputs = [ + "${root_out_dir}/libmojo_core_arc32.so", + ] + deps = [ + ":shared_library(//build/toolchain/android:${android32_toolchain})", + ] + } + + copy("shared_library_arc64") { + sources = [ + "${root_out_dir}/${android64_toolchain}/libmojo_core.so", + ] + outputs = [ + "${root_out_dir}/libmojo_core_arc64.so", + ] + deps = [ + ":shared_library(//build/toolchain/android:${android64_toolchain})", + ] + } + } + + config("export_only_thunks_api") { + ldflags = [ "-Wl,--version-script=" + + rebase_path("//mojo/core/export_only_thunks_api.lst", + root_build_dir) ] + } + + if (is_chromeos || is_linux || is_win) { + test("mojo_core_unittests") { + sources = [ + "mojo_core_unittest.cc", + "run_all_core_unittests.cc", + ] + + deps = [ + "//base", + "//base/test:test_support", + "//mojo/public/c/system", + "//testing/gtest", + ] + + data_deps = [ + ":shared_library", + ] + } + } +} + +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/core/test:test_support", + "//testing/gtest:gtest", + ] +} + +source_set("test_sources") { + testonly = true + sources = [ + "channel_unittest.cc", + "core_test_base.cc", + "core_test_base.h", + "core_unittest.cc", + "embedder_unittest.cc", + "handle_table_unittest.cc", + "message_pipe_unittest.cc", + "message_unittest.cc", + "options_validation_unittest.cc", + "platform_handle_dispatcher_unittest.cc", + "quota_unittest.cc", + "shared_buffer_dispatcher_unittest.cc", + "shared_buffer_unittest.cc", + "signals_unittest.cc", + "trap_unittest.cc", + ] + + if (!is_ios) { + sources += [ + "data_pipe_unittest.cc", + "invitation_unittest.cc", + "multiprocess_message_pipe_unittest.cc", + "platform_wrapper_unittest.cc", + ] + } + + deps = [ + ":test_utils", + "//base", + "//base/test:test_support", + "//mojo/core:embedder_internal", + "//mojo/core/embedder", + "//mojo/core/ports:tests", + "//mojo/core/test:run_all_unittests", + "//mojo/core/test:test_support", + "//mojo/public/cpp/system", + "//testing/gmock", + "//testing/gtest", + ] +} diff --git a/chromium/mojo/edk/DEPS b/chromium/mojo/core/DEPS index 4cc26fb9900..4cc26fb9900 100644 --- a/chromium/mojo/edk/DEPS +++ b/chromium/mojo/core/DEPS diff --git a/chromium/mojo/edk/system/OWNERS b/chromium/mojo/core/OWNERS index 42444bcd16d..42444bcd16d 100644 --- a/chromium/mojo/edk/system/OWNERS +++ b/chromium/mojo/core/OWNERS diff --git a/chromium/mojo/edk/system/atomic_flag.h b/chromium/mojo/core/atomic_flag.h index 6bdcfaaddd9..075b837ab1b 100644 --- a/chromium/mojo/edk/system/atomic_flag.h +++ b/chromium/mojo/core/atomic_flag.h @@ -2,14 +2,14 @@ // 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_ +#ifndef MOJO_CORE_ATOMIC_FLAG_H_ +#define MOJO_CORE_ATOMIC_FLAG_H_ #include "base/atomicops.h" #include "base/macros.h" namespace mojo { -namespace edk { +namespace core { // 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 @@ -35,13 +35,9 @@ class AtomicFlag { AtomicFlag() : flag_(0) {} ~AtomicFlag() {} - void Set(bool value) { - base::subtle::Release_Store(&flag_, value ? 1 : 0); - } + void Set(bool value) { base::subtle::Release_Store(&flag_, value ? 1 : 0); } - bool Get() const { - return base::subtle::Acquire_Load(&flag_) ? true : false; - } + bool Get() const { return base::subtle::Acquire_Load(&flag_) ? true : false; } operator const bool() const { return Get(); } @@ -51,7 +47,7 @@ class AtomicFlag { DISALLOW_COPY_AND_ASSIGN(AtomicFlag); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_ATOMIC_FLAG_H_ +#endif // MOJO_CORE_ATOMIC_FLAG_H_ diff --git a/chromium/mojo/edk/system/broker.h b/chromium/mojo/core/broker.h index 6961f641e0c..41e0b896649 100644 --- a/chromium/mojo/edk/system/broker.h +++ b/chromium/mojo/core/broker.h @@ -2,31 +2,32 @@ // 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_ +#ifndef MOJO_CORE_BROKER_H_ +#define MOJO_CORE_BROKER_H_ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/writable_shared_memory_region.h" #include "base/synchronization/lock.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" +#include "mojo/public/cpp/platform/platform_channel_endpoint.h" +#include "mojo/public/cpp/platform/platform_handle.h" namespace mojo { -namespace edk { +namespace core { // The Broker is a channel to the broker process, which allows synchronous IPCs // to fulfill shared memory allocation requests on some platforms. class Broker { public: // Note: This is blocking, and will wait for the first message over - // |platform_handle|. - explicit Broker(ScopedInternalPlatformHandle platform_handle); + // the endpoint handle in |handle|. + explicit Broker(PlatformHandle handle); ~Broker(); // Returns the platform handle that should be used to establish a NodeChannel // to the process which is inviting us to join its network. This is the first // handle read off the Broker channel upon construction. - ScopedInternalPlatformHandle GetInviterInternalPlatformHandle(); + PlatformChannelEndpoint GetInviterEndpoint(); // Request a shared buffer from the broker process. Blocks the current thread. base::WritableSharedMemoryRegion GetWritableSharedMemoryRegion( @@ -34,11 +35,11 @@ class Broker { private: // Handle to the broker process, used for synchronous IPCs. - ScopedInternalPlatformHandle sync_channel_; + PlatformHandle sync_channel_; - // Handle to the inviter process which is recieved in the first first message - // over |sync_channel_|. - ScopedInternalPlatformHandle inviter_channel_; + // Channel endpoint connected to the inviter process. Recieved in the first + // first message over |sync_channel_|. + PlatformChannelEndpoint inviter_endpoint_; // 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 @@ -48,7 +49,7 @@ class Broker { DISALLOW_COPY_AND_ASSIGN(Broker); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_BROKER_H_ +#endif // MOJO_CORE_BROKER_H_ diff --git a/chromium/mojo/edk/system/broker_host.cc b/chromium/mojo/core/broker_host.cc index 8966e239654..875cc2fe490 100644 --- a/chromium/mojo/edk/system/broker_host.cc +++ b/chromium/mojo/core/broker_host.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/broker_host.h" #include <utility> @@ -11,17 +11,18 @@ #include "base/memory/ref_counted.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" -#include "mojo/edk/embedder/named_platform_channel_pair.h" -#include "mojo/edk/embedder/named_platform_handle.h" -#include "mojo/edk/embedder/platform_handle_utils.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/broker_messages.h" +#include "mojo/core/broker_messages.h" +#include "mojo/core/platform_handle_utils.h" + +#if defined(OS_WIN) +#include <windows.h> +#endif namespace mojo { -namespace edk { +namespace core { BrokerHost::BrokerHost(base::ProcessHandle client_process, - ScopedInternalPlatformHandle platform_handle, + ConnectionParams connection_params, const ProcessErrorCallback& process_error_callback) : process_error_callback_(process_error_callback) #if defined(OS_WIN) @@ -29,14 +30,13 @@ BrokerHost::BrokerHost(base::ProcessHandle client_process, client_process_(ScopedProcessHandle::CloneFrom(client_process)) #endif { - CHECK(platform_handle.is_valid()); + CHECK(connection_params.endpoint().is_valid() || + connection_params.server_endpoint().is_valid()); base::MessageLoopCurrent::Get()->AddDestructionObserver(this); - channel_ = Channel::Create( - this, - ConnectionParams(TransportProtocol::kLegacy, std::move(platform_handle)), - base::ThreadTaskRunnerHandle::Get()); + channel_ = Channel::Create(this, std::move(connection_params), + base::ThreadTaskRunnerHandle::Get()); channel_->Start(); } @@ -49,21 +49,20 @@ BrokerHost::~BrokerHost() { } bool BrokerHost::PrepareHandlesForClient( - std::vector<ScopedInternalPlatformHandle>* handles) { + std::vector<PlatformHandleInTransit>* handles) { #if defined(OS_WIN) - if (!Channel::Message::RewriteHandles(base::GetCurrentProcessHandle(), - client_process_.get(), 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; + bool handles_ok = true; + for (auto& handle : *handles) { + if (!handle.TransferToProcess(client_process_.Clone())) + handles_ok = false; } -#endif + return handles_ok; +#else return true; +#endif } -bool BrokerHost::SendChannel(ScopedInternalPlatformHandle handle) { +bool BrokerHost::SendChannel(PlatformHandle handle) { CHECK(handle.is_valid()); CHECK(channel_); @@ -76,8 +75,8 @@ bool BrokerHost::SendChannel(ScopedInternalPlatformHandle handle) { Channel::MessagePtr message = CreateBrokerMessage(BrokerMessageType::INIT, 1, nullptr); #endif - std::vector<ScopedInternalPlatformHandle> handles(1); - handles[0] = std::move(handle); + std::vector<PlatformHandleInTransit> handles(1); + handles[0] = PlatformHandleInTransit(std::move(handle)); // This may legitimately fail on Windows if the client process is in another // session, e.g., is an elevated process. @@ -108,18 +107,21 @@ void BrokerHost::OnBufferRequest(uint32_t num_bytes) { base::subtle::PlatformSharedMemoryRegion region = base::subtle::PlatformSharedMemoryRegion::CreateWritable(num_bytes); - std::vector<ScopedInternalPlatformHandle> handles(2); + std::vector<PlatformHandleInTransit> handles(2); if (region.IsValid()) { - ExtractInternalPlatformHandlesFromSharedMemoryRegionHandle( - region.PassPlatformHandle(), &handles[0], &handles[1]); + PlatformHandle h[2]; + ExtractPlatformHandlesFromSharedMemoryRegionHandle( + region.PassPlatformHandle(), &h[0], &h[1]); + handles[0] = PlatformHandleInTransit(std::move(h[0])); + handles[1] = PlatformHandleInTransit(std::move(h[1])); #if !defined(OS_POSIX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) || \ (defined(OS_MACOSX) && !defined(OS_IOS)) // Non-POSIX systems, as well as Android, Fuchsia, and non-iOS Mac, only use // a single handle to represent a writable region. - DCHECK(!handles[1].is_valid()); + DCHECK(!handles[1].handle().is_valid()); handles.resize(1); #else - DCHECK(handles[1].is_valid()); + DCHECK(handles[1].handle().is_valid()); #endif } @@ -137,10 +139,9 @@ void BrokerHost::OnBufferRequest(uint32_t num_bytes) { channel_->Write(std::move(message)); } -void BrokerHost::OnChannelMessage( - const void* payload, - size_t payload_size, - std::vector<ScopedInternalPlatformHandle> handles) { +void BrokerHost::OnChannelMessage(const void* payload, + size_t payload_size, + std::vector<PlatformHandle> handles) { if (payload_size < sizeof(BrokerMessageHeader)) return; @@ -175,5 +176,5 @@ void BrokerHost::WillDestroyCurrentMessageLoop() { delete this; } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/broker_host.h b/chromium/mojo/core/broker_host.h index 1681bdca6c2..3ff36eaa9fd 100644 --- a/chromium/mojo/edk/system/broker_host.h +++ b/chromium/mojo/core/broker_host.h @@ -2,8 +2,8 @@ // 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_ +#ifndef MOJO_CORE_BROKER_HOST_H_ +#define MOJO_CORE_BROKER_HOST_H_ #include <stdint.h> #include <vector> @@ -12,13 +12,16 @@ #include "base/message_loop/message_loop_current.h" #include "base/process/process_handle.h" #include "base/strings/string_piece.h" -#include "mojo/edk/embedder/process_error_callback.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/scoped_process_handle.h" +#include "build/build_config.h" +#include "mojo/core/channel.h" +#include "mojo/core/connection_params.h" +#include "mojo/core/embedder/process_error_callback.h" +#include "mojo/core/platform_handle_in_transit.h" +#include "mojo/core/scoped_process_handle.h" +#include "mojo/public/cpp/platform/platform_handle.h" namespace mojo { -namespace edk { +namespace core { // The BrokerHost is a channel to a broker client process, servicing synchronous // IPCs issued by the client. @@ -26,11 +29,11 @@ class BrokerHost : public Channel::Delegate, public base::MessageLoopCurrent::DestructionObserver { public: BrokerHost(base::ProcessHandle client_process, - ScopedInternalPlatformHandle handle, + ConnectionParams connection_params, const ProcessErrorCallback& process_error_callback); // Send |handle| to the client, to be used to establish a NodeChannel to us. - bool SendChannel(ScopedInternalPlatformHandle handle); + bool SendChannel(PlatformHandle handle); #if defined(OS_WIN) // Sends a named channel to the client. Like above, but for named pipes. @@ -40,14 +43,12 @@ class BrokerHost : public Channel::Delegate, private: ~BrokerHost() override; - bool PrepareHandlesForClient( - std::vector<ScopedInternalPlatformHandle>* handles); + bool PrepareHandlesForClient(std::vector<PlatformHandleInTransit>* handles); // Channel::Delegate: - void OnChannelMessage( - const void* payload, - size_t payload_size, - std::vector<ScopedInternalPlatformHandle> handles) override; + void OnChannelMessage(const void* payload, + size_t payload_size, + std::vector<PlatformHandle> handles) override; void OnChannelError(Channel::Error error) override; // base::MessageLoopCurrent::DestructionObserver: @@ -66,7 +67,7 @@ class BrokerHost : public Channel::Delegate, DISALLOW_COPY_AND_ASSIGN(BrokerHost); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_BROKER_HOST_H_ +#endif // MOJO_CORE_BROKER_HOST_H_ diff --git a/chromium/mojo/edk/system/broker_messages.h b/chromium/mojo/core/broker_messages.h index 88cab4e4ad6..3bad6f5d9d3 100644 --- a/chromium/mojo/edk/system/broker_messages.h +++ b/chromium/mojo/core/broker_messages.h @@ -2,13 +2,14 @@ // 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_ +#ifndef MOJO_CORE_BROKER_MESSAGES_H_ +#define MOJO_CORE_BROKER_MESSAGES_H_ -#include "mojo/edk/system/channel.h" +#include "build/build_config.h" +#include "mojo/core/channel.h" namespace mojo { -namespace edk { +namespace core { #pragma pack(push, 1) @@ -90,7 +91,7 @@ inline Channel::MessagePtr CreateBrokerMessage( return message; } -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_BROKER_MESSAGES_H_ +#endif // MOJO_CORE_BROKER_MESSAGES_H_ diff --git a/chromium/mojo/edk/system/broker_posix.cc b/chromium/mojo/core/broker_posix.cc index 4b5c15c0fbc..a2501af5f55 100644 --- a/chromium/mojo/edk/system/broker_posix.cc +++ b/chromium/mojo/core/broker_posix.cc @@ -2,39 +2,39 @@ // 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 "mojo/core/broker.h" #include <fcntl.h> #include <unistd.h> #include <utility> +#include <vector> #include "base/logging.h" #include "base/memory/platform_shared_memory_region.h" #include "build/build_config.h" -#include "mojo/edk/embedder/platform_channel_utils_posix.h" -#include "mojo/edk/embedder/platform_handle_utils.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/broker_messages.h" -#include "mojo/edk/system/channel.h" +#include "mojo/core/broker_messages.h" +#include "mojo/core/channel.h" +#include "mojo/core/platform_handle_utils.h" +#include "mojo/public/cpp/platform/socket_utils_posix.h" namespace mojo { -namespace edk { +namespace core { namespace { Channel::MessagePtr WaitForBrokerMessage( - const ScopedInternalPlatformHandle& platform_handle, + int socket_fd, BrokerMessageType expected_type, size_t expected_num_handles, size_t expected_data_size, - std::vector<ScopedInternalPlatformHandle>* incoming_handles) { + std::vector<PlatformHandle>* incoming_handles) { Channel::MessagePtr message(new Channel::Message( sizeof(BrokerMessageHeader) + expected_data_size, expected_num_handles)); - base::circular_deque<ScopedInternalPlatformHandle> incoming_platform_handles; - ssize_t read_result = PlatformChannelRecvmsg( - platform_handle, const_cast<void*>(message->data()), - message->data_num_bytes(), &incoming_platform_handles, true /* block */); + std::vector<base::ScopedFD> incoming_fds; + ssize_t read_result = + SocketRecvmsg(socket_fd, const_cast<void*>(message->data()), + message->data_num_bytes(), &incoming_fds, true /* block */); bool error = false; if (read_result < 0) { PLOG(ERROR) << "Recvmsg error"; @@ -42,7 +42,7 @@ Channel::MessagePtr WaitForBrokerMessage( } 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) { + } else if (incoming_fds.size() != expected_num_handles) { LOG(ERROR) << "Received unexpected number of handles"; error = true; } @@ -57,37 +57,38 @@ Channel::MessagePtr WaitForBrokerMessage( return nullptr; } - incoming_handles->resize(incoming_platform_handles.size()); - std::move(incoming_platform_handles.begin(), incoming_platform_handles.end(), - incoming_handles->begin()); + incoming_handles->reserve(incoming_fds.size()); + for (size_t i = 0; i < incoming_fds.size(); ++i) + incoming_handles->emplace_back(std::move(incoming_fds[i])); return message; } } // namespace -Broker::Broker(ScopedInternalPlatformHandle platform_handle) - : sync_channel_(std::move(platform_handle)) { +Broker::Broker(PlatformHandle handle) : sync_channel_(std::move(handle)) { CHECK(sync_channel_.is_valid()); + int fd = sync_channel_.GetFD().get(); // Mark the channel as blocking. - int flags = fcntl(sync_channel_.get().handle, F_GETFL); + int flags = fcntl(fd, F_GETFL); PCHECK(flags != -1); - flags = fcntl(sync_channel_.get().handle, F_SETFL, flags & ~O_NONBLOCK); + flags = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); PCHECK(flags != -1); // Wait for the first message, which should contain a handle. - std::vector<ScopedInternalPlatformHandle> incoming_platform_handles; - if (WaitForBrokerMessage(sync_channel_, BrokerMessageType::INIT, 1, 0, + std::vector<PlatformHandle> incoming_platform_handles; + if (WaitForBrokerMessage(fd, BrokerMessageType::INIT, 1, 0, &incoming_platform_handles)) { - inviter_channel_ = std::move(incoming_platform_handles[0]); + inviter_endpoint_ = + PlatformChannelEndpoint(std::move(incoming_platform_handles[0])); } } Broker::~Broker() = default; -ScopedInternalPlatformHandle Broker::GetInviterInternalPlatformHandle() { - return std::move(inviter_channel_); +PlatformChannelEndpoint Broker::GetInviterEndpoint() { + return std::move(inviter_endpoint_); } base::WritableSharedMemoryRegion Broker::GetWritableSharedMemoryRegion( @@ -98,8 +99,9 @@ base::WritableSharedMemoryRegion Broker::GetWritableSharedMemoryRegion( Channel::MessagePtr out_message = CreateBrokerMessage( BrokerMessageType::BUFFER_REQUEST, 0, 0, &buffer_request); buffer_request->size = num_bytes; - ssize_t write_result = PlatformChannelWrite( - sync_channel_, out_message->data(), out_message->data_num_bytes()); + ssize_t write_result = + SocketWrite(sync_channel_.GetFD().get(), out_message->data(), + out_message->data_num_bytes()); if (write_result < 0) { PLOG(ERROR) << "Error sending sync broker message"; return base::WritableSharedMemoryRegion(); @@ -118,22 +120,21 @@ base::WritableSharedMemoryRegion Broker::GetWritableSharedMemoryRegion( constexpr size_t kNumExpectedHandles = 2; #endif - std::vector<ScopedInternalPlatformHandle> incoming_platform_handles; + std::vector<PlatformHandle> handles; Channel::MessagePtr message = WaitForBrokerMessage( - sync_channel_, BrokerMessageType::BUFFER_RESPONSE, kNumExpectedHandles, - sizeof(BufferResponseData), &incoming_platform_handles); + sync_channel_.GetFD().get(), BrokerMessageType::BUFFER_RESPONSE, + kNumExpectedHandles, sizeof(BufferResponseData), &handles); if (message) { const BufferResponseData* data; if (!GetBrokerMessageData(message.get(), &data)) return base::WritableSharedMemoryRegion(); - if (incoming_platform_handles.size() == 1) - incoming_platform_handles.emplace_back(); + if (handles.size() == 1) + handles.emplace_back(); return base::WritableSharedMemoryRegion::Deserialize( base::subtle::PlatformSharedMemoryRegion::Take( - CreateSharedMemoryRegionHandleFromInternalPlatformHandles( - std::move(incoming_platform_handles[0]), - std::move(incoming_platform_handles[1])), + CreateSharedMemoryRegionHandleFromPlatformHandles( + std::move(handles[0]), std::move(handles[1])), base::subtle::PlatformSharedMemoryRegion::Mode::kWritable, num_bytes, base::UnguessableToken::Deserialize(data->guid_high, @@ -143,5 +144,5 @@ base::WritableSharedMemoryRegion Broker::GetWritableSharedMemoryRegion( return base::WritableSharedMemoryRegion(); } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/broker_win.cc b/chromium/mojo/core/broker_win.cc index a2dea366c0c..3ebc8839ce0 100644 --- a/chromium/mojo/edk/system/broker_win.cc +++ b/chromium/mojo/core/broker_win.cc @@ -11,17 +11,14 @@ #include "base/memory/platform_shared_memory_region.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_utils.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/broker.h" -#include "mojo/edk/system/broker_messages.h" -#include "mojo/edk/system/channel.h" +#include "mojo/core/broker.h" +#include "mojo/core/broker_messages.h" +#include "mojo/core/channel.h" +#include "mojo/core/platform_handle_utils.h" +#include "mojo/public/cpp/platform/named_platform_channel.h" namespace mojo { -namespace edk { +namespace core { namespace { @@ -30,27 +27,27 @@ const size_t kMaxBrokerMessageSize = 256; bool TakeHandlesFromBrokerMessage(Channel::Message* message, size_t num_handles, - ScopedInternalPlatformHandle* out_handles) { + PlatformHandle* out_handles) { if (message->num_handles() != num_handles) { DLOG(ERROR) << "Received unexpected number of handles in broker message"; return false; } - std::vector<ScopedInternalPlatformHandle> handles = message->TakeHandles(); + std::vector<PlatformHandleInTransit> handles = message->TakeHandles(); DCHECK_EQ(handles.size(), num_handles); DCHECK(out_handles); for (size_t i = 0; i < num_handles; ++i) - out_handles[i] = std::move(handles[i]); + out_handles[i] = handles[i].TakeHandle(); return true; } -Channel::MessagePtr WaitForBrokerMessage(InternalPlatformHandle platform_handle, +Channel::MessagePtr WaitForBrokerMessage(HANDLE pipe_handle, BrokerMessageType expected_type) { char buffer[kMaxBrokerMessageSize]; DWORD bytes_read = 0; - BOOL result = ::ReadFile(platform_handle.handle, buffer, - kMaxBrokerMessageSize, &bytes_read, nullptr); + BOOL result = ::ReadFile(pipe_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 @@ -86,18 +83,20 @@ Channel::MessagePtr WaitForBrokerMessage(InternalPlatformHandle platform_handle, } // namespace -Broker::Broker(ScopedInternalPlatformHandle handle) - : sync_channel_(std::move(handle)) { +Broker::Broker(PlatformHandle handle) : sync_channel_(std::move(handle)) { CHECK(sync_channel_.is_valid()); - Channel::MessagePtr message = - WaitForBrokerMessage(sync_channel_.get(), BrokerMessageType::INIT); + Channel::MessagePtr message = WaitForBrokerMessage( + sync_channel_.GetHandle().Get(), BrokerMessageType::INIT); // If we fail to read a message (broken pipe), just return early. The inviter // handle will be null and callers must handle this gracefully. if (!message) return; - if (!TakeHandlesFromBrokerMessage(message.get(), 1, &inviter_channel_)) { + PlatformHandle endpoint_handle; + if (TakeHandlesFromBrokerMessage(message.get(), 1, &endpoint_handle)) { + inviter_endpoint_ = PlatformChannelEndpoint(std::move(endpoint_handle)); + } else { // If the message has no handles, we expect it to carry pipe name instead. const BrokerMessageHeader* header = static_cast<const BrokerMessageHeader*>(message->payload()); @@ -110,15 +109,15 @@ Broker::Broker(ScopedInternalPlatformHandle handle) const base::char16* name_data = reinterpret_cast<const base::char16*>(data + 1); CHECK(data->pipe_name_length); - inviter_channel_ = CreateClientHandle(NamedPlatformHandle( - base::StringPiece16(name_data, data->pipe_name_length))); + inviter_endpoint_ = NamedPlatformChannel::ConnectToServer( + base::StringPiece16(name_data, data->pipe_name_length).as_string()); } } Broker::~Broker() {} -ScopedInternalPlatformHandle Broker::GetInviterInternalPlatformHandle() { - return std::move(inviter_channel_); +PlatformChannelEndpoint Broker::GetInviterEndpoint() { + return std::move(inviter_endpoint_); } base::WritableSharedMemoryRegion Broker::GetWritableSharedMemoryRegion( @@ -129,26 +128,27 @@ base::WritableSharedMemoryRegion Broker::GetWritableSharedMemoryRegion( 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); + BOOL result = + ::WriteFile(sync_channel_.GetHandle().Get(), 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()) { PLOG(ERROR) << "Error sending sync broker message"; return base::WritableSharedMemoryRegion(); } - ScopedInternalPlatformHandle handle; + PlatformHandle handle; Channel::MessagePtr response = WaitForBrokerMessage( - sync_channel_.get(), BrokerMessageType::BUFFER_RESPONSE); + sync_channel_.GetHandle().Get(), BrokerMessageType::BUFFER_RESPONSE); if (response && TakeHandlesFromBrokerMessage(response.get(), 1, &handle)) { BufferResponseData* data; if (!GetBrokerMessageData(response.get(), &data)) return base::WritableSharedMemoryRegion(); return base::WritableSharedMemoryRegion::Deserialize( base::subtle::PlatformSharedMemoryRegion::Take( - CreateSharedMemoryRegionHandleFromInternalPlatformHandles( - std::move(handle), ScopedInternalPlatformHandle()), + CreateSharedMemoryRegionHandleFromPlatformHandles(std::move(handle), + PlatformHandle()), base::subtle::PlatformSharedMemoryRegion::Mode::kWritable, num_bytes, base::UnguessableToken::Deserialize(data->guid_high, @@ -158,5 +158,5 @@ base::WritableSharedMemoryRegion Broker::GetWritableSharedMemoryRegion( return base::WritableSharedMemoryRegion(); } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/channel.cc b/chromium/mojo/core/channel.cc index 0005193ca3d..85d2a629be3 100644 --- a/chromium/mojo/edk/system/channel.cc +++ b/chromium/mojo/core/channel.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/channel.h" #include <stddef.h> #include <string.h> @@ -16,9 +16,8 @@ #include "base/numerics/safe_math.h" #include "base/process/process_handle.h" #include "build/build_config.h" -#include "mojo/edk/embedder/platform_handle.h" -#include "mojo/edk/system/configuration.h" -#include "mojo/edk/system/core.h" +#include "mojo/core/configuration.h" +#include "mojo/core/core.h" #if defined(OS_MACOSX) && !defined(OS_IOS) #include "base/mac/mach_logging.h" @@ -27,7 +26,7 @@ #endif namespace mojo { -namespace edk { +namespace core { namespace { @@ -70,7 +69,7 @@ Channel::Message::Message(size_t payload_size, Channel::Message::Message(size_t capacity, size_t payload_size, size_t max_handles) -#if defined(MOJO_EDK_LEGACY_PROTOCOL) +#if defined(MOJO_CORE_LEGACY_PROTOCOL) : Message(capacity, payload_size, max_handles, MessageType::NORMAL_LEGACY) { } #else @@ -151,8 +150,8 @@ Channel::Message::Message(size_t capacity, 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)}; + mach_ports_header_->entries[i] = {0, + static_cast<uint32_t>(MACH_PORT_NULL)}; } #endif } @@ -163,8 +162,10 @@ Channel::Message::~Message() { } // static -Channel::MessagePtr Channel::Message::Deserialize(const void* data, - size_t data_num_bytes) { +Channel::MessagePtr Channel::Message::Deserialize( + const void* data, + size_t data_num_bytes, + base::ProcessHandle from_process) { if (data_num_bytes < sizeof(LegacyHeader)) return nullptr; @@ -250,10 +251,17 @@ Channel::MessagePtr Channel::Message::Deserialize(const void* data, } #if defined(OS_WIN) - std::vector<ScopedInternalPlatformHandle> handles(num_handles); + std::vector<PlatformHandleInTransit> handles(num_handles); for (size_t i = 0; i < num_handles; i++) { - handles[i] = ScopedInternalPlatformHandle(InternalPlatformHandle( - base::win::Uint32ToHandle(message->handles_[i].handle))); + HANDLE handle = base::win::Uint32ToHandle(message->handles_[i].handle); + if (from_process == base::kNullProcessHandle) { + handles[i] = PlatformHandleInTransit( + PlatformHandle(base::win::ScopedHandle(handle))); + } else { + handles[i] = PlatformHandleInTransit( + PlatformHandleInTransit::TakeIncomingRemoteHandle(handle, + from_process)); + } } message->SetHandles(std::move(handles)); #endif @@ -343,10 +351,8 @@ bool Channel::Message::has_mach_ports() const { return false; for (const auto& handle : handle_vector_) { - if (handle.get().type == InternalPlatformHandle::Type::MACH || - handle.get().type == InternalPlatformHandle::Type::MACH_NAME) { + if (handle.is_mach_port_name() || handle.handle().is_mach_port()) return true; - } } return false; } @@ -365,8 +371,17 @@ Channel::Message::Header* Channel::Message::header() const { return reinterpret_cast<Header*>(data_); } +void Channel::Message::SetHandles(std::vector<PlatformHandle> new_handles) { + std::vector<PlatformHandleInTransit> handles; + handles.reserve(new_handles.size()); + for (auto& h : new_handles) { + handles.emplace_back(PlatformHandleInTransit(std::move(h))); + } + SetHandles(std::move(handles)); +} + void Channel::Message::SetHandles( - std::vector<ScopedInternalPlatformHandle> new_handles) { + std::vector<PlatformHandleInTransit> new_handles) { if (is_legacy_message()) { // Old semantics for ChromeOS and Android if (legacy_header()->num_handles == 0) { @@ -388,39 +403,46 @@ void Channel::Message::SetHandles( 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].get().handle); + for (size_t i = 0; i < handle_vector_.size(); i++) { + HANDLE handle = handle_vector_[i].remote_handle(); + if (handle == INVALID_HANDLE_VALUE) + handle = handle_vector_[i].handle().GetHandle().Get(); + handles_[i].handle = base::win::HandleToUint32(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)}; + 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].get().type == InternalPlatformHandle::Type::MACH || - handle_vector_[i].get().type == - InternalPlatformHandle::Type::MACH_NAME) { - mach_port_t port = handle_vector_[i].get().port; - mach_ports_header_->entries[mach_port_index].index = i; - mach_ports_header_->entries[mach_port_index].mach_port = port; - mach_port_index++; + if (!handle_vector_[i].is_mach_port_name() && + !handle_vector_[i].handle().is_mach_port()) { + DCHECK(handle_vector_[i].handle().is_valid_fd()); + continue; } + + mach_port_t port = handle_vector_[i].is_mach_port_name() + ? handle_vector_[i].mach_port_name() + : handle_vector_[i].handle().GetMachPort().get(); + 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 } -std::vector<ScopedInternalPlatformHandle> Channel::Message::TakeHandles() { +std::vector<PlatformHandleInTransit> 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_->entries[i] = {0, + static_cast<uint32_t>(MACH_PORT_NULL)}; } mach_ports_header_->num_ports = 0; } @@ -432,74 +454,30 @@ std::vector<ScopedInternalPlatformHandle> Channel::Message::TakeHandles() { return std::move(handle_vector_); } -std::vector<ScopedInternalPlatformHandle> +std::vector<PlatformHandleInTransit> Channel::Message::TakeHandlesForTransport() { #if defined(OS_WIN) // Not necessary on Windows. NOTREACHED(); - return std::vector<ScopedInternalPlatformHandle>(); + return std::vector<PlatformHandleInTransit>(); #elif defined(OS_MACOSX) && !defined(OS_IOS) - for (auto it = handle_vector_.begin(); it != handle_vector_.end();) { - if (it->get().type == InternalPlatformHandle::Type::MACH || - it->get().type == InternalPlatformHandle::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. - // TODO(wez): Removing Mach ports here because they are delivered - // out-of-band seems strange; could this be properly hidden inside the - // Mac-specific Channel impl? - ignore_result(it->release()); - it = handle_vector_.erase(it); + std::vector<PlatformHandleInTransit> non_mach_handles; + for (auto& handle : handle_vector_) { + if (handle.is_mach_port_name() || handle.handle().is_mach_port()) { + // Ownership is effectively transferred to the receiving process + // out-of-band via MachPortRelay. + handle.CompleteTransit(); } else { - ++it; + non_mach_handles.emplace_back(std::move(handle)); } } - return std::move(handle_vector_); + handle_vector_.clear(); + return non_mach_handles; #else return std::move(handle_vector_); #endif } -#if defined(OS_WIN) -// static -bool Channel::Message::RewriteHandles( - base::ProcessHandle from_process, - base::ProcessHandle to_process, - std::vector<ScopedInternalPlatformHandle>* 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].get().owning_process, from_process); - BOOL result = - DuplicateHandle(from_process, (*handles)[i].get().handle, to_process, - &(*handles)[i].get().handle, 0, FALSE, - DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); - if (result) { - if (to_process == base::GetCurrentProcessHandle()) { - (*handles)[i].get().owning_process = to_process; - } else { - // If this handle is bound for an external process, make sure it owns - // its own copy of the target process handle. - (*handles)[i].get().owning_process = - ScopedProcessHandle::CloneFrom(to_process).release(); - } - } 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].get().handle = INVALID_HANDLE_VALUE; - (*handles)[i].get().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: // @@ -525,8 +503,8 @@ class Channel::ReadBuffer { public: ReadBuffer() { size_ = kReadBufferSize; - data_ = static_cast<char*>(base::AlignedAlloc(size_, - kChannelMessageAlignment)); + data_ = + static_cast<char*>(base::AlignedAlloc(size_, kChannelMessageAlignment)); } ~ReadBuffer() { @@ -622,18 +600,16 @@ class Channel::ReadBuffer { }; Channel::Channel(Delegate* delegate) - : delegate_(delegate), read_buffer_(new ReadBuffer) { -} + : delegate_(delegate), read_buffer_(new ReadBuffer) {} -Channel::~Channel() { -} +Channel::~Channel() {} void Channel::ShutDown() { - delegate_ = nullptr; ShutDownImpl(); + delegate_ = nullptr; } -char* Channel::GetReadBuffer(size_t *buffer_capacity) { +char* Channel::GetReadBuffer(size_t* buffer_capacity) { DCHECK(read_buffer_); size_t required_capacity = *buffer_capacity; if (!required_capacity) @@ -643,8 +619,8 @@ char* Channel::GetReadBuffer(size_t *buffer_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; +bool Channel::OnReadComplete(size_t bytes_read, size_t* next_read_size_hint) { + bool did_consume_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 @@ -708,10 +684,12 @@ bool Channel::OnReadComplete(size_t bytes_read, size_t *next_read_size_hint) { const uint16_t num_handles = header ? header->num_handles : legacy_header->num_handles; - std::vector<ScopedInternalPlatformHandle> handles; + std::vector<PlatformHandle> handles; + bool deferred = false; if (num_handles > 0) { - if (!GetReadInternalPlatformHandles(num_handles, extra_header, - extra_header_size, &handles)) { + if (!GetReadPlatformHandles(payload, payload_size, num_handles, + extra_header, extra_header_size, &handles, + &deferred)) { return false; } @@ -724,20 +702,23 @@ bool Channel::OnReadComplete(size_t bytes_read, size_t *next_read_size_hint) { // 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) { + DCHECK(!deferred); if (!OnControlMessage(legacy_header->message_type, payload, payload_size, std::move(handles))) { return false; } - did_dispatch_message = true; + did_consume_message = true; + } else if (deferred) { + did_consume_message = true; } else if (delegate_) { delegate_->OnChannelMessage(payload, payload_size, std::move(handles)); - did_dispatch_message = true; + did_consume_message = true; } read_buffer_->Discard(legacy_header->num_bytes); } - *next_read_size_hint = did_dispatch_message ? 0 : kReadBufferSize; + *next_read_size_hint = did_consume_message ? 0 : kReadBufferSize; return true; } @@ -746,13 +727,12 @@ void Channel::OnError(Error error) { delegate_->OnChannelError(error); } -bool Channel::OnControlMessage( - Message::MessageType message_type, - const void* payload, - size_t payload_size, - std::vector<ScopedInternalPlatformHandle> handles) { +bool Channel::OnControlMessage(Message::MessageType message_type, + const void* payload, + size_t payload_size, + std::vector<PlatformHandle> handles) { return false; } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/channel.h b/chromium/mojo/core/channel.h index ef9669cc39a..17108e9f027 100644 --- a/chromium/mojo/edk/system/channel.h +++ b/chromium/mojo/core/channel.h @@ -2,8 +2,8 @@ // 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_ +#ifndef MOJO_CORE_CHANNEL_H_ +#define MOJO_CORE_CHANNEL_H_ #include <vector> @@ -12,11 +12,13 @@ #include "base/process/process_handle.h" #include "base/task_runner.h" #include "build/build_config.h" -#include "mojo/edk/embedder/connection_params.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" +#include "mojo/core/connection_params.h" +#include "mojo/core/platform_handle_in_transit.h" +#include "mojo/core/scoped_process_handle.h" +#include "mojo/public/cpp/platform/platform_handle.h" namespace mojo { -namespace edk { +namespace core { const size_t kChannelMessageAlignment = 8; @@ -39,8 +41,8 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel 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 + // TODO(https://crbug.com/695645): remove legacy support when Arc++ has + // updated to Mojo with normal versioned messages. NORMAL_LEGACY = 0, #if defined(OS_MACOSX) // A control message containing handles to echo back. @@ -88,7 +90,7 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel #if defined(OS_MACOSX) && !defined(OS_IOS) struct MachPortsEntry { - // Index of Mach port in the original vector of InternalPlatformHandles. + // Index of Mach port in the original vector of PlatformHandleInTransits. uint16_t index; // Mach port name. @@ -141,8 +143,12 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel MessageType message_type); ~Message(); - // Constructs a Message from serialized message data. - static MessagePtr Deserialize(const void* data, size_t data_num_bytes); + // Constructs a Message from serialized message data, optionally coming from + // a known remote process. + static MessagePtr Deserialize( + const void* data, + size_t data_num_bytes, + base::ProcessHandle from_process = base::kNullProcessHandle); const void* data() const { return data_; } size_t data_num_bytes() const { return size_; } @@ -179,25 +185,14 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel // Note: SetHandles() and TakeHandles() invalidate any previous value of // handles(). - void SetHandles(std::vector<ScopedInternalPlatformHandle> new_handles); - std::vector<ScopedInternalPlatformHandle> TakeHandles(); + void SetHandles(std::vector<PlatformHandle> new_handles); + void SetHandles(std::vector<PlatformHandleInTransit> new_handles); + std::vector<PlatformHandleInTransit> 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. - std::vector<ScopedInternalPlatformHandle> 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, - std::vector<ScopedInternalPlatformHandle>* handles); -#endif + std::vector<PlatformHandleInTransit> TakeHandlesForTransport(); void SetVersionForTest(uint16_t version_number); @@ -216,7 +211,7 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel // Maximum number of handles which may be attached to this message. size_t max_handles_ = 0; - std::vector<ScopedInternalPlatformHandle> handle_vector_; + std::vector<PlatformHandleInTransit> handle_vector_; #if defined(OS_WIN) // On Windows, handles are serialised into the extra header section. @@ -253,10 +248,9 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel // 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, - std::vector<ScopedInternalPlatformHandle> handles) = 0; + virtual void OnChannelMessage(const void* payload, + size_t payload_size, + std::vector<PlatformHandle> handles) = 0; // Notify that an error has occured and the Channel will cease operation. virtual void OnChannelError(Error error) = 0; @@ -278,6 +272,14 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel // Delegate methods will no longer be invoked after this call. void ShutDown(); + // Sets the process handle of the remote endpoint to which this Channel is + // connected. If called at all, must be called only once, and before Start(). + void set_remote_process(ScopedProcessHandle remote_process) { + DCHECK(!remote_process_.is_valid()); + remote_process_ = std::move(remote_process); + } + const ScopedProcessHandle& remote_process() const { return remote_process_; } + // Begin processing I/O events. Delegate methods must only be invoked after // this call. virtual void Start() = 0; @@ -298,6 +300,8 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel explicit Channel(Delegate* delegate); virtual ~Channel(); + Delegate* delegate() const { return delegate_; } + // 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. @@ -327,19 +331,24 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel // 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 GetReadInternalPlatformHandles( - size_t num_handles, - const void* extra_header, - size_t extra_header_size, - std::vector<ScopedInternalPlatformHandle>* handles) = 0; + // + // If the implementation sets |*deferred| to |true|, it assumes responsibility + // for dispatching the message eventually. It must copy |payload| to retain + // it for later transmission. + virtual bool GetReadPlatformHandles(const void* payload, + size_t payload_size, + size_t num_handles, + const void* extra_header, + size_t extra_header_size, + std::vector<PlatformHandle>* handles, + bool* deferred) = 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, - std::vector<ScopedInternalPlatformHandle> handles); + virtual bool OnControlMessage(Message::MessageType message_type, + const void* payload, + size_t payload_size, + std::vector<PlatformHandle> handles); private: friend class base::RefCountedThreadSafe<Channel>; @@ -349,10 +358,13 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel Delegate* delegate_; const std::unique_ptr<ReadBuffer> read_buffer_; + // Handle to the process on the other end of this Channel, iff known. + ScopedProcessHandle remote_process_; + DISALLOW_COPY_AND_ASSIGN(Channel); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_CHANNEL_H_ +#endif // MOJO_CORE_CHANNEL_H_ diff --git a/chromium/mojo/edk/system/channel_fuchsia.cc b/chromium/mojo/core/channel_fuchsia.cc index 364792cc32b..4386b200b81 100644 --- a/chromium/mojo/edk/system/channel_fuchsia.cc +++ b/chromium/mojo/core/channel_fuchsia.cc @@ -2,10 +2,12 @@ // 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 "mojo/core/channel.h" -#include <fdio/limits.h> -#include <fdio/util.h> +#include <lib/fdio/limits.h> +#include <lib/fdio/util.h> +#include <lib/zx/channel.h> +#include <lib/zx/handle.h> #include <zircon/processargs.h> #include <zircon/status.h> #include <zircon/syscalls.h> @@ -14,30 +16,30 @@ #include "base/bind.h" #include "base/containers/circular_deque.h" #include "base/files/scoped_file.h" -#include "base/fuchsia/scoped_zx_handle.h" +#include "base/fuchsia/fuchsia_logging.h" #include "base/location.h" -#include "base/logging.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/message_loop/message_loop_current.h" #include "base/message_loop/message_pump_for_io.h" +#include "base/stl_util.h" #include "base/synchronization/lock.h" #include "base/task_runner.h" +#include "mojo/core/platform_handle_in_transit.h" namespace mojo { -namespace edk { +namespace core { namespace { const size_t kMaxBatchReadCapacity = 256 * 1024; -bool UnwrapInternalPlatformHandle( - ScopedInternalPlatformHandle handle, - Channel::Message::HandleInfoEntry* info_out, - std::vector<ScopedInternalPlatformHandle>* handles_out) { - DCHECK(handle.get().is_valid()); +bool UnwrapPlatformHandle(PlatformHandleInTransit handle, + Channel::Message::HandleInfoEntry* info_out, + std::vector<PlatformHandleInTransit>* handles_out) { + DCHECK(handle.handle().is_valid()); - if (!handle.get().is_valid_fd()) { + if (!handle.handle().is_valid_fd()) { *info_out = {0u, 0u}; handles_out->emplace_back(std::move(handle)); return true; @@ -51,20 +53,21 @@ bool UnwrapInternalPlatformHandle( // already been dup()d into another FD) we may need to clone. zx_handle_t handles[FDIO_MAX_HANDLES] = {}; uint32_t info[FDIO_MAX_HANDLES] = {}; - zx_status_t result = fdio_transfer_fd(handle.get().as_fd(), 0, handles, info); + zx_status_t result = + fdio_transfer_fd(handle.handle().GetFD().get(), 0, handles, info); if (result > 0) { // On success, the fd in |handle| has been transferred and is no longer - // valid. Release from the ScopedInternalPlatformHandle to avoid close()ing + // valid. Release from the PlatformHandle to avoid close()ing an invalid // an invalid handle. - ignore_result(handle.release()); + handle.CompleteTransit(); } else if (result == ZX_ERR_UNAVAILABLE) { // No luck, try cloning instead. - result = fdio_clone_fd(handle.get().as_fd(), 0, handles, info); + result = fdio_clone_fd(handle.handle().GetFD().get(), 0, handles, info); } if (result <= 0) { - DLOG(ERROR) << "fdio_transfer_fd(" << handle.get().as_fd() - << "): " << zx_status_get_string(result); + ZX_DLOG(ERROR, result) << "fdio_transfer_fd(" + << handle.handle().GetFD().get() << ")"; return false; } DCHECK_LE(result, FDIO_MAX_HANDLES); @@ -75,23 +78,22 @@ bool UnwrapInternalPlatformHandle( for (int i = 0; i < result; ++i) { DCHECK_EQ(PA_HND_TYPE(info[0]), PA_HND_TYPE(info[i])); DCHECK_EQ(0u, PA_HND_SUBTYPE(info[i])); - handles_out->emplace_back(InternalPlatformHandle::ForHandle(handles[i])); + handles_out->emplace_back( + PlatformHandleInTransit(PlatformHandle(zx::handle(handles[i])))); } return true; } -ScopedInternalPlatformHandle WrapInternalPlatformHandles( - Channel::Message::HandleInfoEntry info, - base::circular_deque<base::ScopedZxHandle>* handles) { - ScopedInternalPlatformHandle out_handle; +PlatformHandle WrapPlatformHandles(Channel::Message::HandleInfoEntry info, + base::circular_deque<zx::handle>* handles) { + PlatformHandle out_handle; if (!info.type) { - out_handle.reset( - InternalPlatformHandle::ForHandle(handles->front().release())); + out_handle = PlatformHandle(std::move(handles->front())); handles->pop_front(); } else { if (info.count > FDIO_MAX_HANDLES) - return ScopedInternalPlatformHandle(); + return PlatformHandle(); // Fetch the required number of handles from |handles| and set up type info. zx_handle_t fd_handles[FDIO_MAX_HANDLES] = {}; @@ -106,8 +108,8 @@ ScopedInternalPlatformHandle WrapInternalPlatformHandles( zx_status_t result = fdio_create_fd(fd_handles, fd_infos, info.count, out_fd.receive()); if (result != ZX_OK) { - DLOG(ERROR) << "fdio_create_fd: " << zx_status_get_string(result); - return ScopedInternalPlatformHandle(); + ZX_DLOG(ERROR, result) << "fdio_create_fd"; + return PlatformHandle(); } // The handles are owned by FDIO now, so |release()| them before removing @@ -117,7 +119,7 @@ ScopedInternalPlatformHandle WrapInternalPlatformHandles( handles->pop_front(); } - out_handle.reset(InternalPlatformHandle::ForFd(out_fd.release())); + out_handle = PlatformHandle(std::move(out_fd)); } return out_handle; } @@ -157,9 +159,9 @@ class MessageView { offset_ += num_bytes; } - std::vector<ScopedInternalPlatformHandle> TakeHandles() { + std::vector<PlatformHandleInTransit> TakeHandles() { if (handles_.empty()) - return std::vector<ScopedInternalPlatformHandle>(); + return std::vector<PlatformHandleInTransit>(); // We can only pass Fuchsia handles via IPC, so unwrap any FDIO file- // descriptors in |handles_| into the underlying handles, and serialize the @@ -168,12 +170,12 @@ class MessageView { message_->mutable_extra_header()); memset(handles_info, 0, message_->extra_header_size()); - std::vector<ScopedInternalPlatformHandle> in_handles = std::move(handles_); + std::vector<PlatformHandleInTransit> in_handles = std::move(handles_); handles_.reserve(in_handles.size()); for (size_t i = 0; i < in_handles.size(); i++) { - if (!UnwrapInternalPlatformHandle(std::move(in_handles[i]), - &handles_info[i], &handles_)) - return std::vector<ScopedInternalPlatformHandle>(); + if (!UnwrapPlatformHandle(std::move(in_handles[i]), &handles_info[i], + &handles_)) + return std::vector<PlatformHandleInTransit>(); } return std::move(handles_); } @@ -181,7 +183,7 @@ class MessageView { private: Channel::MessagePtr message_; size_t offset_; - std::vector<ScopedInternalPlatformHandle> handles_; + std::vector<PlatformHandleInTransit> handles_; DISALLOW_COPY_AND_ASSIGN(MessageView); }; @@ -195,7 +197,8 @@ class ChannelFuchsia : public Channel, scoped_refptr<base::TaskRunner> io_task_runner) : Channel(delegate), self_(this), - handle_(connection_params.TakeChannelHandle()), + handle_( + connection_params.TakeEndpoint().TakePlatformHandle().TakeHandle()), io_task_runner_(io_task_runner) { CHECK(handle_.is_valid()); } @@ -238,11 +241,13 @@ class ChannelFuchsia : public Channel, leak_handle_ = true; } - bool GetReadInternalPlatformHandles( - size_t num_handles, - const void* extra_header, - size_t extra_header_size, - std::vector<ScopedInternalPlatformHandle>* handles) override { + bool GetReadPlatformHandles(const void* payload, + size_t payload_size, + size_t num_handles, + const void* extra_header, + size_t extra_header_size, + std::vector<PlatformHandle>* handles, + bool* deferred) override { DCHECK(io_task_runner_->RunsTasksInCurrentSequence()); if (num_handles > std::numeric_limits<uint16_t>::max()) return false; @@ -273,16 +278,13 @@ class ChannelFuchsia : public Channel, handles->reserve(num_handles); for (size_t i = 0; i < num_handles; ++i) { handles->emplace_back( - WrapInternalPlatformHandles(handles_info[i], &incoming_handles_) - .release()); + WrapPlatformHandles(handles_info[i], &incoming_handles_)); } return true; } private: - ~ChannelFuchsia() override { - DCHECK(!read_watch_); - } + ~ChannelFuchsia() override { DCHECK(!read_watch_); } void StartOnIOThread() { DCHECK(!read_watch_); @@ -292,7 +294,7 @@ class ChannelFuchsia : public Channel, read_watch_.reset( new base::MessagePumpForIO::ZxHandleWatchController(FROM_HERE)); base::MessageLoopCurrentForIO::Get()->WatchZxHandle( - handle_.get().as_handle(), true /* persistent */, + handle_.get(), true /* persistent */, ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED, read_watch_.get(), this); } @@ -318,7 +320,7 @@ class ChannelFuchsia : public Channel, // base::MessagePumpForIO::ZxHandleWatcher: void OnZxHandleSignalled(zx_handle_t handle, zx_signals_t signals) override { DCHECK(io_task_runner_->RunsTasksInCurrentSequence()); - CHECK_EQ(handle, handle_.get().as_handle()); + CHECK_EQ(handle, handle_.get()); DCHECK((ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED) & signals); // We always try to read message(s), even if ZX_CHANNEL_PEER_CLOSED, since @@ -338,12 +340,12 @@ class ChannelFuchsia : public Channel, uint32_t handles_read = 0; zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES] = {}; - zx_status_t read_result = zx_channel_read( - handle_.get().as_handle(), 0, buffer, handles, buffer_capacity, - arraysize(handles), &bytes_read, &handles_read); + zx_status_t read_result = + handle_.read(0, buffer, buffer_capacity, &bytes_read, handles, + base::size(handles), &handles_read); if (read_result == ZX_OK) { for (size_t i = 0; i < handles_read; ++i) { - incoming_handles_.push_back(base::ScopedZxHandle(handles[i])); + incoming_handles_.emplace_back(handles[i]); } total_bytes_read += bytes_read; if (!OnReadComplete(bytes_read, &next_read_size)) { @@ -352,13 +354,13 @@ class ChannelFuchsia : public Channel, break; } } else if (read_result == ZX_ERR_BUFFER_TOO_SMALL) { - DCHECK_LE(handles_read, arraysize(handles)); + DCHECK_LE(handles_read, base::size(handles)); next_read_size = bytes_read; } else if (read_result == ZX_ERR_SHOULD_WAIT) { break; } else { - DLOG_IF(ERROR, read_result != ZX_ERR_PEER_CLOSED) - << "zx_channel_read: " << zx_status_get_string(read_result); + ZX_DLOG_IF(ERROR, read_result != ZX_ERR_PEER_CLOSED, read_result) + << "zx_channel_read"; read_error = true; break; } @@ -381,35 +383,34 @@ class ChannelFuchsia : public Channel, do { message_view.advance_data_offset(write_bytes); - std::vector<ScopedInternalPlatformHandle> outgoing_handles = + std::vector<PlatformHandleInTransit> outgoing_handles = message_view.TakeHandles(); zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES] = {}; size_t handles_count = outgoing_handles.size(); - DCHECK_LE(handles_count, arraysize(handles)); + DCHECK_LE(handles_count, base::size(handles)); for (size_t i = 0; i < handles_count; ++i) { - DCHECK(outgoing_handles[i].is_valid()); - handles[i] = outgoing_handles[i].get().as_handle(); + DCHECK(outgoing_handles[i].handle().is_valid()); + handles[i] = outgoing_handles[i].handle().GetHandle().get(); } write_bytes = std::min(message_view.data_num_bytes(), static_cast<size_t>(ZX_CHANNEL_MAX_MSG_BYTES)); - zx_status_t result = - zx_channel_write(handle_.get().as_handle(), 0, message_view.data(), - write_bytes, handles, handles_count); + zx_status_t result = handle_.write(0, message_view.data(), write_bytes, + handles, handles_count); + // zx_channel_write() consumes |handles| whether or not it succeeds, so + // release() our copies now, to avoid them being double-closed. + for (auto& outgoing_handle : outgoing_handles) + outgoing_handle.CompleteTransit(); + if (result != ZX_OK) { // TODO(fuchsia): Handle ZX_ERR_SHOULD_WAIT flow-control errors, once - // the platform starts generating them. See crbug.com/754084. - DLOG_IF(ERROR, result != ZX_ERR_PEER_CLOSED) - << "WriteNoLock(zx_channel_write): " - << zx_status_get_string(result); + // the platform starts generating them. See https://crbug.com/754084. + ZX_DLOG_IF(ERROR, result != ZX_ERR_PEER_CLOSED, result) + << "WriteNoLock(zx_channel_write)"; return false; } - // |handles| have been transferred to the peer process, so release() - // them, to avoid them being double-closed. - for (auto& outgoing_handle : outgoing_handles) - ignore_result(outgoing_handle.release()); } while (write_bytes < message_view.data_num_bytes()); return true; @@ -436,12 +437,12 @@ class ChannelFuchsia : public Channel, // Keeps the Channel alive at least until explicit shutdown on the IO thread. scoped_refptr<Channel> self_; - ScopedInternalPlatformHandle handle_; + zx::channel handle_; scoped_refptr<base::TaskRunner> io_task_runner_; // These members are only used on the IO thread. std::unique_ptr<base::MessagePumpForIO::ZxHandleWatchController> read_watch_; - base::circular_deque<base::ScopedZxHandle> incoming_handles_; + base::circular_deque<zx::handle> incoming_handles_; bool leak_handle_ = false; base::Lock write_lock_; @@ -461,5 +462,5 @@ scoped_refptr<Channel> Channel::Create( std::move(io_task_runner)); } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/channel_posix.cc b/chromium/mojo/core/channel_posix.cc index 42d78d533f1..dc7f3a22aad 100644 --- a/chromium/mojo/edk/system/channel_posix.cc +++ b/chromium/mojo/core/channel_posix.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/channel.h" #include <errno.h> #include <sys/socket.h> @@ -20,15 +20,20 @@ #include "base/message_loop/message_pump_for_io.h" #include "base/synchronization/lock.h" #include "base/task_runner.h" -#include "mojo/edk/embedder/platform_channel_utils_posix.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" +#include "build/build_config.h" +#include "mojo/core/core.h" +#include "mojo/public/cpp/platform/socket_utils_posix.h" #if !defined(OS_NACL) #include <sys/uio.h> #endif +#if defined(OS_MACOSX) && !defined(OS_IOS) +#include "mojo/core/mach_port_relay.h" +#endif + namespace mojo { -namespace edk { +namespace core { namespace { @@ -69,39 +74,52 @@ class MessageView { offset_ += num_bytes; } - std::vector<ScopedInternalPlatformHandle> TakeHandles() { + std::vector<PlatformHandleInTransit> TakeHandles() { return std::move(handles_); } Channel::MessagePtr TakeMessage() { return std::move(message_); } - void SetHandles(std::vector<ScopedInternalPlatformHandle> handles) { + void SetHandles(std::vector<PlatformHandleInTransit> handles) { handles_ = std::move(handles); } private: Channel::MessagePtr message_; size_t offset_; - std::vector<ScopedInternalPlatformHandle> handles_; + std::vector<PlatformHandleInTransit> handles_; DISALLOW_COPY_AND_ASSIGN(MessageView); }; class ChannelPosix : public Channel, +#if defined(OS_MACOSX) && !defined(OS_IOS) + public MachPortRelay::Observer, +#endif public base::MessageLoopCurrent::DestructionObserver, public base::MessagePumpForIO::FdWatcher { 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) - { - CHECK(handle_.is_valid()); + : Channel(delegate), self_(this), io_task_runner_(io_task_runner) { + if (connection_params.server_endpoint().is_valid()) + server_ = connection_params.TakeServerEndpoint(); + else + socket_ = connection_params.TakeEndpoint().TakePlatformHandle().TakeFD(); + + CHECK(server_.is_valid() || socket_.is_valid()); } void Start() override { +#if defined(OS_MACOSX) && !defined(OS_IOS) + auto* relay = Core::Get()->GetMachPortRelay(); + if (relay) { + // We should only have a relay if we know the remote process handle, + // because that means we're in the broker process. + relay->AddObserver(this); + } +#endif + if (io_task_runner_->RunsTasksInCurrentSequence()) { StartOnIOThread(); } else { @@ -117,6 +135,30 @@ class ChannelPosix : public Channel, } void Write(MessagePtr message) override { +#if defined(OS_MACOSX) && !defined(OS_IOS) + // If this message has Mach ports and we have a MachPortRelay, use the relay + // to rewrite the ports as receive rights from which the send right can be + // read. See |MachPortRelay::SendPortsToProcess()|. + // + // Note that if we don't have a relay, the receiving process must, and they + // must also have the ability to extract a send right from the ports that + // are already attached. + MachPortRelay* relay = Core::Get()->GetMachPortRelay(); + if (relay && remote_process().is_valid() && message->has_mach_ports()) { + if (relay->port_provider()->TaskForPid(remote_process().get()) == + MACH_PORT_NULL) { + // We also need to have a task port for the remote process before we can + // send it any other ports. If we don't have one yet, queue the message + // until OnProcessReady() is invoked. + base::AutoLock lock(task_port_wait_lock_); + pending_outgoing_with_mach_ports_.emplace_back(std::move(message)); + return; + } + + relay->SendPortsToProcess(message.get(), remote_process().get()); + } +#endif + bool write_error = false; { base::AutoLock lock(write_lock_); @@ -143,11 +185,13 @@ class ChannelPosix : public Channel, leak_handle_ = true; } - bool GetReadInternalPlatformHandles( - size_t num_handles, - const void* extra_header, - size_t extra_header_size, - std::vector<ScopedInternalPlatformHandle>* handles) override { + bool GetReadPlatformHandles(const void* payload, + size_t payload_size, + size_t num_handles, + const void* extra_header, + size_t extra_header_size, + std::vector<PlatformHandle>* handles, + bool* deferred) override { if (num_handles > std::numeric_limits<uint16_t>::max()) return false; #if defined(OS_MACOSX) && !defined(OS_IOS) @@ -164,37 +208,75 @@ class ChannelPosix : public Channel, size_t num_mach_ports = mach_ports_header->num_ports; if (num_mach_ports > num_handles) return false; - if (incoming_platform_handles_.size() + num_mach_ports < num_handles) + if (incoming_fds_.size() + num_mach_ports < num_handles) return true; - handles->resize(num_handles); + std::vector<PlatformHandleInTransit> handles_in_transit(num_handles); const MachPortsEntry* mach_ports = mach_ports_header->entries; + + // If we know the remote process handle, we assume all incoming Mach ports + // are send right references owned by the remote process. Otherwise they're + // receive ports we can use to read a send right. + const bool extract_send_rights = remote_process().is_valid(); 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).reset(InternalPlatformHandle( - static_cast<mach_port_t>(mach_ports[mach_port_index].mach_port))); - DCHECK_EQ(handles->at(i).get().type, - InternalPlatformHandle::Type::MACH); - // These are actually just Mach port names until they're resolved from - // the remote process. - handles->at(i).get().type = InternalPlatformHandle::Type::MACH_NAME; + mach_port_t port_name = + static_cast<mach_port_t>(mach_ports[mach_port_index].mach_port); + if (extract_send_rights) { + handles_in_transit[i] = + PlatformHandleInTransit::CreateForMachPortName(port_name); + } else { + handles_in_transit[i] = PlatformHandleInTransit( + PlatformHandle(MachPortRelay::ReceiveSendRight( + base::mac::ScopedMachReceiveRight(port_name)))); + } mach_port_index++; } else { - if (incoming_platform_handles_.empty()) + if (incoming_fds_.empty()) return false; - handles->at(i) = std::move(incoming_platform_handles_.front()); - incoming_platform_handles_.pop_front(); + handles_in_transit[i] = PlatformHandleInTransit( + PlatformHandle(std::move(incoming_fds_.front()))); + incoming_fds_.pop_front(); } } + if (extract_send_rights && num_mach_ports) { + MachPortRelay* relay = Core::Get()->GetMachPortRelay(); + DCHECK(relay); + // Extracting send rights requires that we have a task port for the + // remote process, which we may not yet have. + if (relay->port_provider()->TaskForPid(remote_process().get()) != + MACH_PORT_NULL) { + // We do have a task port, so extract the send rights immediately. + for (auto& handle : handles_in_transit) { + if (handle.is_mach_port_name()) { + handle = PlatformHandleInTransit(PlatformHandle(relay->ExtractPort( + handle.mach_port_name(), remote_process().get()))); + } + } + } else { + // No task port, we have to defer this message. + *deferred = true; + base::AutoLock lock(task_port_wait_lock_); + std::vector<uint8_t> data(payload_size); + memcpy(data.data(), payload, payload_size); + pending_incoming_with_mach_ports_.emplace_back( + std::move(data), std::move(handles_in_transit)); + return true; + } + } + + handles->resize(handles_in_transit.size()); + for (size_t i = 0; i < handles->size(); ++i) + handles->at(i) = handles_in_transit[i].TakeHandle(); #else - if (incoming_platform_handles_.size() < num_handles) + if (incoming_fds_.size() < num_handles) return true; handles->resize(num_handles); for (size_t i = 0; i < num_handles; ++i) { - handles->at(i) = std::move(incoming_platform_handles_.front()); - incoming_platform_handles_.pop_front(); + handles->at(i) = PlatformHandle(std::move(incoming_fds_.front())); + incoming_fds_.pop_front(); } #endif @@ -213,15 +295,15 @@ class ChannelPosix : public Channel, read_watcher_.reset( new base::MessagePumpForIO::FdWatchController(FROM_HERE)); base::MessageLoopCurrent::Get()->AddDestructionObserver(this); - if (handle_.get().needs_connection) { + if (server_.is_valid()) { base::MessageLoopCurrentForIO::Get()->WatchFileDescriptor( - handle_.get().handle, false /* persistent */, + server_.platform_handle().GetFD().get(), false /* persistent */, base::MessagePumpForIO::WATCH_READ, read_watcher_.get(), this); } else { write_watcher_.reset( new base::MessagePumpForIO::FdWatchController(FROM_HERE)); base::MessageLoopCurrentForIO::Get()->WatchFileDescriptor( - handle_.get().handle, true /* persistent */, + socket_.get(), true /* persistent */, base::MessagePumpForIO::WATCH_READ, read_watcher_.get(), this); base::AutoLock lock(write_lock_); FlushOutgoingMessagesNoLock(); @@ -241,7 +323,7 @@ class ChannelPosix : public Channel, if (io_task_runner_->RunsTasksInCurrentSequence()) { pending_write_ = true; base::MessageLoopCurrentForIO::Get()->WatchFileDescriptor( - handle_.get().handle, false /* persistent */, + socket_.get(), false /* persistent */, base::MessagePumpForIO::WATCH_WRITE, write_watcher_.get(), this); } else { io_task_runner_->PostTask( @@ -255,17 +337,81 @@ class ChannelPosix : public Channel, read_watcher_.reset(); write_watcher_.reset(); - if (leak_handle_) - ignore_result(handle_.release()); - handle_.reset(); + if (leak_handle_) { + ignore_result(socket_.release()); + server_.TakePlatformHandle().release(); + } else { + socket_.reset(); + ignore_result(server_.TakePlatformHandle()); + } #if defined(OS_MACOSX) - handles_to_close_.clear(); + fds_to_close_.clear(); +#endif + +#if defined(OS_MACOSX) && !defined(OS_IOS) + auto* relay = Core::Get()->GetMachPortRelay(); + if (relay) + relay->RemoveObserver(this); #endif // May destroy the |this| if it was the last reference. self_ = nullptr; } +#if defined(OS_MACOSX) && !defined(OS_IOS) + // MachPortRelay::Observer: + void OnProcessReady(base::ProcessHandle process) override { + if (process != remote_process().get()) + return; + + io_task_runner_->PostTask( + FROM_HERE, + base::BindOnce( + &ChannelPosix::FlushPendingMessagesWithMachPortsOnIOThread, this)); + } + + void FlushPendingMessagesWithMachPortsOnIOThread() { + // We have a task port for the remote process. Now we can send or accept + // any pending messages with Mach ports. + std::vector<RawIncomingMessage> incoming; + std::vector<MessagePtr> outgoing; + { + base::AutoLock lock(task_port_wait_lock_); + if (reject_incoming_messages_with_mach_ports_) + return; + std::swap(pending_incoming_with_mach_ports_, incoming); + std::swap(pending_outgoing_with_mach_ports_, outgoing); + } + + DCHECK(remote_process().is_valid()); + base::ProcessHandle process = remote_process().get(); + MachPortRelay* relay = Core::Get()->GetMachPortRelay(); + DCHECK(relay); + for (auto& message : incoming) { + Channel::Delegate* d = delegate(); + if (!d) + break; + std::vector<PlatformHandle> handles(message.handles.size()); + for (size_t i = 0; i < message.handles.size(); ++i) { + if (message.handles[i].is_mach_port_name()) { + handles[i] = PlatformHandle( + relay->ExtractPort(message.handles[i].mach_port_name(), process)); + } else { + DCHECK(!message.handles[i].owning_process().is_valid()); + handles[i] = message.handles[i].TakeHandle(); + } + } + d->OnChannelMessage(message.data.data(), message.data.size(), + std::move(handles)); + } + + for (auto& message : outgoing) { + relay->SendPortsToProcess(message.get(), process); + Write(std::move(message)); + } + } +#endif + // base::MessageLoopCurrent::DestructionObserver: void WillDestroyCurrentMessageLoop() override { DCHECK(io_task_runner_->RunsTasksInCurrentSequence()); @@ -275,25 +421,25 @@ class ChannelPosix : public Channel, // base::MessagePumpForIO::FdWatcher: void OnFileCanReadWithoutBlocking(int fd) override { - CHECK_EQ(fd, handle_.get().handle); - if (handle_.get().needs_connection) { + if (server_.is_valid()) { + CHECK_EQ(fd, server_.platform_handle().GetFD().get()); #if !defined(OS_NACL) read_watcher_.reset(); base::MessageLoopCurrent::Get()->RemoveDestructionObserver(this); - ScopedInternalPlatformHandle accept_fd; - ServerAcceptConnection(handle_, &accept_fd); - if (!accept_fd.is_valid()) { + AcceptSocketConnection(server_.platform_handle().GetFD().get(), &socket_); + ignore_result(server_.TakePlatformHandle()); + if (!socket_.is_valid()) { OnError(Error::kConnectionFailed); return; } - handle_ = std::move(accept_fd); StartOnIOThread(); #else NOTREACHED(); #endif return; } + CHECK_EQ(fd, socket_.get()); bool validation_error = false; bool read_error = false; @@ -306,8 +452,11 @@ class ChannelPosix : public Channel, char* buffer = GetReadBuffer(&buffer_capacity); DCHECK_GT(buffer_capacity, 0u); - ssize_t read_result = PlatformChannelRecvmsg( - handle_, buffer, buffer_capacity, &incoming_platform_handles_); + std::vector<base::ScopedFD> incoming_fds; + ssize_t read_result = + SocketRecvmsg(socket_.get(), buffer, buffer_capacity, &incoming_fds); + for (auto& fd : incoming_fds) + incoming_fds_.emplace_back(std::move(fd)); if (read_result > 0) { bytes_read = static_cast<size_t>(read_result); @@ -350,7 +499,7 @@ class ChannelPosix : public Channel, // 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) { + if (server_.is_valid()) { outgoing_messages_.emplace_front(std::move(message_view)); return true; } @@ -359,13 +508,15 @@ class ChannelPosix : public Channel, message_view.advance_data_offset(bytes_written); ssize_t result; - std::vector<ScopedInternalPlatformHandle> handles = - message_view.TakeHandles(); + std::vector<PlatformHandleInTransit> handles = message_view.TakeHandles(); if (!handles.empty()) { iovec iov = {const_cast<void*>(message_view.data()), message_view.data_num_bytes()}; + std::vector<base::ScopedFD> fds(handles.size()); + for (size_t i = 0; i < handles.size(); ++i) + fds[i] = handles[i].TakeHandle().TakeFD(); // TODO: Handle lots of handles. - result = PlatformChannelSendmsgWithHandles(handle_, &iov, 1, handles); + result = SendmsgWithHandles(socket_.get(), &iov, 1, fds); if (result >= 0) { #if defined(OS_MACOSX) // There is a bug on OSX which makes it dangerous to close @@ -376,25 +527,29 @@ class ChannelPosix : public Channel, // 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.get().handle); - { - base::AutoLock l(handles_to_close_lock_); - for (auto& handle : handles) - handles_to_close_.emplace_back(std::move(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); + { + base::AutoLock l(fds_to_close_lock_); + for (auto& fd : fds) + fds_to_close_.emplace_back(std::move(fd)); + } #endif // defined(OS_MACOSX) + } else { + // Message transmission failed, so pull the FDs back into |handles| + // so they can be held by the Message again. + for (size_t i = 0; i < fds.size(); ++i) { + handles[i] = + PlatformHandleInTransit(PlatformHandle(std::move(fds[i]))); + } } } else { - result = PlatformChannelWrite(handle_, message_view.data(), - message_view.data_num_bytes()); + result = SocketWrite(socket_.get(), message_view.data(), + message_view.data_num_bytes()); } if (result < 0) { @@ -459,11 +614,10 @@ class ChannelPosix : public Channel, } #if defined(OS_MACOSX) - bool OnControlMessage( - Message::MessageType message_type, - const void* payload, - size_t payload_size, - std::vector<ScopedInternalPlatformHandle> handles) override { + bool OnControlMessage(Message::MessageType message_type, + const void* payload, + size_t payload_size, + std::vector<PlatformHandle> handles) override { switch (message_type) { case Message::MessageType::HANDLES_SENT: { if (payload_size == 0) @@ -494,37 +648,35 @@ class ChannelPosix : public Channel, } // 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_|. + // |fds| does not match a sequence of handles in |fds_to_close_|. bool CloseHandles(const int* fds, size_t num_fds) { - base::AutoLock l(handles_to_close_lock_); + base::AutoLock l(fds_to_close_lock_); if (!num_fds) return false; - auto start = - std::find_if(handles_to_close_.begin(), handles_to_close_.end(), - [&fds](const ScopedInternalPlatformHandle& handle) { - return handle.get().handle == fds[0]; - }); - if (start == handles_to_close_.end()) + auto start = std::find_if( + fds_to_close_.begin(), fds_to_close_.end(), + [&fds](const base::ScopedFD& fd) { return fd.get() == fds[0]; }); + if (start == fds_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_|. - // TODO(wez): Consider making handles_to_close_ a circular_deque<> + // |fds_to_close_|. + // TODO(wez): Consider making |fds_to_close_| a circular_deque<> // for greater efficiency? Or assign a unique Id to each FD-containing // message, and map that to a vector of FDs to close, to avoid the // need for this traversal? Id could even be the first FD in the message. - for (; i < num_fds && it != handles_to_close_.end(); i++, ++it) { - if (it->get().handle != fds[i]) + for (; i < num_fds && it != fds_to_close_.end(); i++, ++it) { + if (it->get() != fds[i]) return false; } if (i != num_fds) return false; - // Close the FDs by erase()ing their ScopedInternalPlatformHandles. - handles_to_close_.erase(start, it); + // Close the FDs by erase()ing their ScopedFDs. + fds_to_close_.erase(start, it); return true; } #endif // defined(OS_MACOSX) @@ -549,14 +701,21 @@ class ChannelPosix : public Channel, // Keeps the Channel alive at least until explicit shutdown on the IO thread. scoped_refptr<Channel> self_; - ScopedInternalPlatformHandle handle_; + // We may be initialized with a server socket, in which case this will be + // valid until it accepts an incoming connection. + PlatformChannelServerEndpoint server_; + + // The socket over which to communicate. May be passed in at construction time + // or accepted over |server_|. + base::ScopedFD socket_; + scoped_refptr<base::TaskRunner> io_task_runner_; // These watchers must only be accessed on the IO thread. std::unique_ptr<base::MessagePumpForIO::FdWatchController> read_watcher_; std::unique_ptr<base::MessagePumpForIO::FdWatchController> write_watcher_; - base::circular_deque<ScopedInternalPlatformHandle> incoming_platform_handles_; + base::circular_deque<base::ScopedFD> incoming_fds_; // Protects |pending_write_| and |outgoing_messages_|. base::Lock write_lock_; @@ -567,9 +726,29 @@ class ChannelPosix : public Channel, bool leak_handle_ = false; #if defined(OS_MACOSX) - base::Lock handles_to_close_lock_; - std::vector<ScopedInternalPlatformHandle> handles_to_close_; -#endif + base::Lock fds_to_close_lock_; + std::vector<base::ScopedFD> fds_to_close_; +#if !defined(OS_IOS) + // Guards access to the send/receive queues below. These are messages that + // can't be fully accepted from or dispatched to the Channel user yet because + // we're still waiting on a task port for the remote process. + struct RawIncomingMessage { + RawIncomingMessage(std::vector<uint8_t> data, + std::vector<PlatformHandleInTransit> handles) + : data(std::move(data)), handles(std::move(handles)) {} + RawIncomingMessage(RawIncomingMessage&&) = default; + ~RawIncomingMessage() = default; + + std::vector<uint8_t> data; + std::vector<PlatformHandleInTransit> handles; + }; + + base::Lock task_port_wait_lock_; + bool reject_incoming_messages_with_mach_ports_ = false; + std::vector<MessagePtr> pending_outgoing_with_mach_ports_; + std::vector<RawIncomingMessage> pending_incoming_with_mach_ports_; +#endif // !defined(OS_IOS) +#endif // defined(OS_MACOSX) DISALLOW_COPY_AND_ASSIGN(ChannelPosix); }; @@ -585,5 +764,5 @@ scoped_refptr<Channel> Channel::Create( io_task_runner); } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/channel_unittest.cc b/chromium/mojo/core/channel_unittest.cc index 90ff9b3728f..e53cc0ffbc1 100644 --- a/chromium/mojo/edk/system/channel_unittest.cc +++ b/chromium/mojo/core/channel_unittest.cc @@ -2,18 +2,19 @@ // 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 "mojo/core/channel.h" #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/threading/thread.h" -#include "mojo/edk/embedder/platform_channel_pair.h" +#include "mojo/core/platform_handle_utils.h" +#include "mojo/public/cpp/platform/platform_channel.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo { -namespace edk { +namespace core { namespace { class TestChannel : public Channel { @@ -28,11 +29,14 @@ class TestChannel : public Channel { return OnReadComplete(bytes_read, next_read_size_hint); } - MOCK_METHOD4(GetReadInternalPlatformHandles, - bool(size_t num_handles, + MOCK_METHOD7(GetReadPlatformHandles, + bool(const void* payload, + size_t payload_size, + size_t num_handles, const void* extra_header, size_t extra_header_size, - std::vector<ScopedInternalPlatformHandle>* handles)); + std::vector<PlatformHandle>* handles, + bool* deferred)); MOCK_METHOD0(Start, void()); MOCK_METHOD0(ShutDownImpl, void()); MOCK_METHOD0(LeakHandle, void()); @@ -53,10 +57,9 @@ class MockChannelDelegate : public Channel::Delegate { const void* GetReceivedPayload() const { return payload_.get(); } protected: - void OnChannelMessage( - const void* payload, - size_t payload_size, - std::vector<ScopedInternalPlatformHandle> handles) override { + void OnChannelMessage(const void* payload, + size_t payload_size, + std::vector<PlatformHandle> handles) override { payload_.reset(new char[payload_size]); memcpy(payload_.get(), payload, payload_size); payload_size_ = payload_size; @@ -181,7 +184,7 @@ TEST(ChannelTest, OnReadNonLegacyMessage) { class ChannelTestShutdownAndWriteDelegate : public Channel::Delegate { public: ChannelTestShutdownAndWriteDelegate( - ScopedInternalPlatformHandle handle, + PlatformChannelEndpoint endpoint, scoped_refptr<base::TaskRunner> task_runner, scoped_refptr<Channel> client_channel, std::unique_ptr<base::Thread> client_thread, @@ -189,18 +192,16 @@ class ChannelTestShutdownAndWriteDelegate : public Channel::Delegate { : quit_closure_(std::move(quit_closure)), client_channel_(std::move(client_channel)), client_thread_(std::move(client_thread)) { - channel_ = Channel::Create( - this, ConnectionParams(TransportProtocol::kLegacy, std::move(handle)), - std::move(task_runner)); + channel_ = Channel::Create(this, ConnectionParams(std::move(endpoint)), + std::move(task_runner)); channel_->Start(); } ~ChannelTestShutdownAndWriteDelegate() override { channel_->ShutDown(); } // Channel::Delegate implementation - void OnChannelMessage( - const void* payload, - size_t payload_size, - std::vector<ScopedInternalPlatformHandle> handles) override { + void OnChannelMessage(const void* payload, + size_t payload_size, + std::vector<PlatformHandle> handles) override { ++message_count_; // If |client_channel_| exists then close it and its thread. @@ -240,7 +241,7 @@ class ChannelTestShutdownAndWriteDelegate : public Channel::Delegate { TEST(ChannelTest, PeerShutdownDuringRead) { base::MessageLoop message_loop(base::MessageLoop::TYPE_IO); - PlatformChannelPair channel_pair; + PlatformChannel channel; // Create a "client" Channel with one end of the pipe, and Start() it. std::unique_ptr<base::Thread> client_thread = @@ -249,9 +250,7 @@ TEST(ChannelTest, PeerShutdownDuringRead) { base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); scoped_refptr<Channel> client_channel = - Channel::Create(nullptr, - ConnectionParams(TransportProtocol::kLegacy, - channel_pair.PassClientHandle()), + Channel::Create(nullptr, ConnectionParams(channel.TakeRemoteEndpoint()), client_thread->task_runner()); client_channel->Start(); @@ -267,7 +266,7 @@ TEST(ChannelTest, PeerShutdownDuringRead) { // is received. base::RunLoop run_loop; ChannelTestShutdownAndWriteDelegate server_delegate( - channel_pair.PassServerHandle(), message_loop.task_runner(), + channel.TakeLocalEndpoint(), message_loop.task_runner(), std::move(client_channel), std::move(client_thread), run_loop.QuitClosure()); @@ -275,5 +274,5 @@ TEST(ChannelTest, PeerShutdownDuringRead) { } } // namespace -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/channel_win.cc b/chromium/mojo/core/channel_win.cc index 123e295f9db..30a14867beb 100644 --- a/chromium/mojo/edk/system/channel_win.cc +++ b/chromium/mojo/core/channel_win.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/channel.h" #include <stdint.h> #include <windows.h> @@ -18,13 +18,14 @@ #include "base/memory/ref_counted.h" #include "base/message_loop/message_loop_current.h" #include "base/message_loop/message_pump_for_io.h" +#include "base/process/process_handle.h" #include "base/synchronization/lock.h" #include "base/task_runner.h" +#include "base/win/scoped_handle.h" #include "base/win/win_util.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" namespace mojo { -namespace edk { +namespace core { namespace { @@ -33,13 +34,20 @@ class ChannelWin : public Channel, public base::MessagePumpForIO::IOHandler { public: ChannelWin(Delegate* delegate, - ScopedInternalPlatformHandle handle, + ConnectionParams connection_params, 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()); + : Channel(delegate), self_(this), io_task_runner_(io_task_runner) { + if (connection_params.server_endpoint().is_valid()) { + handle_ = connection_params.TakeServerEndpoint() + .TakePlatformHandle() + .TakeHandle(); + needs_connection_ = true; + } else { + handle_ = + connection_params.TakeEndpoint().TakePlatformHandle().TakeHandle(); + } + + CHECK(handle_.IsValid()); } void Start() override { @@ -54,6 +62,17 @@ class ChannelWin : public Channel, } void Write(MessagePtr message) override { + if (remote_process().is_valid()) { + // If we know the remote process handle, we transfer all outgoing handles + // to the process now rewriting them in the message. + std::vector<PlatformHandleInTransit> handles = message->TakeHandles(); + for (auto& handle : handles) { + if (handle.handle().is_valid()) + handle.TransferToProcess(remote_process().Clone()); + } + message->SetHandles(std::move(handles)); + } + bool write_error = false; { base::AutoLock lock(write_lock_); @@ -79,11 +98,13 @@ class ChannelWin : public Channel, leak_handle_ = true; } - bool GetReadInternalPlatformHandles( - size_t num_handles, - const void* extra_header, - size_t extra_header_size, - std::vector<ScopedInternalPlatformHandle>* handles) override { + bool GetReadPlatformHandles(const void* payload, + size_t payload_size, + size_t num_handles, + const void* extra_header, + size_t extra_header_size, + std::vector<PlatformHandle>* handles, + bool* deferred) override { DCHECK(extra_header); if (num_handles > std::numeric_limits<uint16_t>::max()) return false; @@ -95,8 +116,17 @@ class ChannelWin : public Channel, const HandleEntry* extra_header_handles = reinterpret_cast<const HandleEntry*>(extra_header); for (size_t i = 0; i < num_handles; i++) { - handles->emplace_back(ScopedInternalPlatformHandle(InternalPlatformHandle( - base::win::Uint32ToHandle(extra_header_handles[i].handle)))); + HANDLE handle_value = + base::win::Uint32ToHandle(extra_header_handles[i].handle); + if (remote_process().is_valid()) { + // If we know the remote process's handle, we assume it doesn't know + // ours; that means any handle values still belong to that process, and + // we need to transfer them to this process. + handle_value = PlatformHandleInTransit::TakeIncomingRemoteHandle( + handle_value, remote_process().get()) + .ReleaseHandle(); + } + handles->emplace_back(base::win::ScopedHandle(std::move(handle_value))); } return true; } @@ -107,12 +137,11 @@ class ChannelWin : public Channel, void StartOnIOThread() { base::MessageLoopCurrent::Get()->AddDestructionObserver(this); - base::MessageLoopCurrentForIO::Get()->RegisterIOHandler( - handle_.get().handle, this); + base::MessageLoopCurrentForIO::Get()->RegisterIOHandler(handle_.Get(), + this); - if (handle_.get().needs_connection) { - BOOL ok = ConnectNamedPipe(handle_.get().handle, - &connect_context_.overlapped); + if (needs_connection_) { + BOOL ok = ::ConnectNamedPipe(handle_.Get(), &connect_context_.overlapped); if (ok) { PLOG(ERROR) << "Unexpected success while waiting for pipe connection"; OnError(Error::kConnectionFailed); @@ -152,13 +181,14 @@ class ChannelWin : public Channel, void ShutDownOnIOThread() { base::MessageLoopCurrent::Get()->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); + // TODO(https://crbug.com/583525): This function is expected to be called + // once, and |handle_| should be valid at this point. + CHECK(handle_.IsValid()); + CancelIo(handle_.Get()); if (leak_handle_) - ignore_result(handle_.release()); - handle_.reset(); + ignore_result(handle_.Take()); + else + handle_.Close(); // Allow |this| to be destroyed as soon as no IO is pending. self_ = nullptr; @@ -236,13 +266,9 @@ class ChannelWin : public Channel, outgoing_messages_.pop_front(); // Invalidate all the scoped handles so we don't attempt to close them. - // Note that we don't simply release these objects because they also own - // an internal process handle (in |owning_process|) which *does* need to - // be closed. - std::vector<ScopedInternalPlatformHandle> handles = - message->TakeHandles(); + std::vector<PlatformHandleInTransit> handles = message->TakeHandles(); for (auto& handle : handles) - handle.get().handle = INVALID_HANDLE_VALUE; + handle.CompleteTransit(); // Overlapped WriteFile() to a pipe should always fully complete. if (message->data_num_bytes() != bytes_written) @@ -261,12 +287,9 @@ class ChannelWin : public Channel, 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); - + BOOL ok = + ::ReadFile(handle_.Get(), buffer, static_cast<DWORD>(buffer_capacity), + NULL, &read_context_.overlapped); if (ok || GetLastError() == ERROR_IO_PENDING) { is_read_pending_ = true; AddRef(); @@ -279,10 +302,9 @@ class ChannelWin : public Channel, // cannot be written, it's queued and a wait is initiated to write the message // ASAP on the I/O thread. bool WriteNoLock(const Channel::MessagePtr& message) { - BOOL ok = WriteFile(handle_.get().handle, message->data(), + BOOL ok = WriteFile(handle_.Get(), message->data(), static_cast<DWORD>(message->data_num_bytes()), NULL, &write_context_.overlapped); - if (ok || GetLastError() == ERROR_IO_PENDING) { is_write_pending_ = true; AddRef(); @@ -315,7 +337,12 @@ class ChannelWin : public Channel, // Keeps the Channel alive at least until explicit shutdown on the IO thread. scoped_refptr<Channel> self_; - ScopedInternalPlatformHandle handle_; + // The pipe handle this Channel uses for communication. + base::win::ScopedHandle handle_; + + // Indicates whether |handle_| must wait for a connection. + bool needs_connection_ = false; + const scoped_refptr<base::TaskRunner> io_task_runner_; base::MessagePumpForIO::IOContext connect_context_; @@ -343,9 +370,8 @@ 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); + return new ChannelWin(delegate, std::move(connection_params), io_task_runner); } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/configuration.cc b/chromium/mojo/core/configuration.cc index f9a5d106ae8..b65612cea4b 100644 --- a/chromium/mojo/edk/system/configuration.cc +++ b/chromium/mojo/core/configuration.cc @@ -2,14 +2,14 @@ // 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" +#include "mojo/core/configuration.h" namespace mojo { -namespace edk { +namespace core { namespace internal { Configuration g_configuration; } // namespace internal -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/configuration.h b/chromium/mojo/core/configuration.h index 489d20ba2ff..4ba3eeb3cb3 100644 --- a/chromium/mojo/edk/system/configuration.h +++ b/chromium/mojo/core/configuration.h @@ -2,14 +2,14 @@ // 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_ +#ifndef MOJO_CORE_CONFIGURATION_H_ +#define MOJO_CORE_CONFIGURATION_H_ -#include "mojo/edk/embedder/configuration.h" -#include "mojo/edk/system/system_impl_export.h" +#include "mojo/core/embedder/configuration.h" +#include "mojo/core/system_impl_export.h" namespace mojo { -namespace edk { +namespace core { namespace internal { MOJO_SYSTEM_IMPL_EXPORT extern Configuration g_configuration; @@ -19,7 +19,7 @@ MOJO_SYSTEM_IMPL_EXPORT inline const Configuration& GetConfiguration() { return internal::g_configuration; } -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_CONFIGURATION_H_ +#endif // MOJO_CORE_CONFIGURATION_H_ diff --git a/chromium/mojo/core/connection_params.cc b/chromium/mojo/core/connection_params.cc new file mode 100644 index 00000000000..444c1a1f3b5 --- /dev/null +++ b/chromium/mojo/core/connection_params.cc @@ -0,0 +1,31 @@ +// 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/core/connection_params.h" + +#include <utility> + +#include "base/logging.h" + +namespace mojo { +namespace core { + +ConnectionParams::ConnectionParams() = default; + +ConnectionParams::ConnectionParams(PlatformChannelEndpoint endpoint) + : endpoint_(std::move(endpoint)) {} + +ConnectionParams::ConnectionParams( + PlatformChannelServerEndpoint server_endpoint) + : server_endpoint_(std::move(server_endpoint)) {} + +ConnectionParams::ConnectionParams(ConnectionParams&&) = default; + +ConnectionParams::~ConnectionParams() = default; + +ConnectionParams& ConnectionParams::operator=(ConnectionParams&& params) = + default; + +} // namespace core +} // namespace mojo diff --git a/chromium/mojo/core/connection_params.h b/chromium/mojo/core/connection_params.h new file mode 100644 index 00000000000..77b41905633 --- /dev/null +++ b/chromium/mojo/core/connection_params.h @@ -0,0 +1,49 @@ +// 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_CORE_CONNECTION_PARAMS_H_ +#define MOJO_CORE_CONNECTION_PARAMS_H_ + +#include "base/macros.h" +#include "build/build_config.h" +#include "mojo/core/system_impl_export.h" +#include "mojo/public/cpp/platform/platform_channel_endpoint.h" +#include "mojo/public/cpp/platform/platform_channel_server_endpoint.h" + +namespace mojo { +namespace core { + +// A set of parameters used when establishing a connection to another process. +class MOJO_SYSTEM_IMPL_EXPORT ConnectionParams { + public: + ConnectionParams(); + explicit ConnectionParams(PlatformChannelEndpoint endpoint); + explicit ConnectionParams(PlatformChannelServerEndpoint server_endpoint); + ConnectionParams(ConnectionParams&&); + ~ConnectionParams(); + + ConnectionParams& operator=(ConnectionParams&&); + + const PlatformChannelEndpoint& endpoint() const { return endpoint_; } + const PlatformChannelServerEndpoint& server_endpoint() const { + return server_endpoint_; + } + + PlatformChannelEndpoint TakeEndpoint() { return std::move(endpoint_); } + + PlatformChannelServerEndpoint TakeServerEndpoint() { + return std::move(server_endpoint_); + } + + private: + PlatformChannelEndpoint endpoint_; + PlatformChannelServerEndpoint server_endpoint_; + + DISALLOW_COPY_AND_ASSIGN(ConnectionParams); +}; + +} // namespace core +} // namespace mojo + +#endif // MOJO_CORE_CONNECTION_PARAMS_H_ diff --git a/chromium/mojo/edk/system/core.cc b/chromium/mojo/core/core.cc index 58bc403cd50..8422ec247a4 100644 --- a/chromium/mojo/edk/system/core.cc +++ b/chromium/mojo/core/core.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/core.h" #include <string.h> @@ -23,27 +23,27 @@ #include "base/time/time.h" #include "base/trace_event/memory_dump_manager.h" #include "build/build_config.h" -#include "mojo/edk/embedder/platform_handle_utils.h" -#include "mojo/edk/embedder/process_error_callback.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/invitation_dispatcher.h" -#include "mojo/edk/system/message_pipe_dispatcher.h" -#include "mojo/edk/system/platform_handle_dispatcher.h" -#include "mojo/edk/system/platform_shared_memory_mapping.h" -#include "mojo/edk/system/ports/event.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/user_message_impl.h" -#include "mojo/edk/system/watcher_dispatcher.h" +#include "mojo/core/channel.h" +#include "mojo/core/configuration.h" +#include "mojo/core/data_pipe_consumer_dispatcher.h" +#include "mojo/core/data_pipe_producer_dispatcher.h" +#include "mojo/core/embedder/process_error_callback.h" +#include "mojo/core/handle_signals_state.h" +#include "mojo/core/invitation_dispatcher.h" +#include "mojo/core/message_pipe_dispatcher.h" +#include "mojo/core/platform_handle_dispatcher.h" +#include "mojo/core/platform_handle_utils.h" +#include "mojo/core/platform_shared_memory_mapping.h" +#include "mojo/core/ports/event.h" +#include "mojo/core/ports/name.h" +#include "mojo/core/ports/node.h" +#include "mojo/core/request_context.h" +#include "mojo/core/shared_buffer_dispatcher.h" +#include "mojo/core/user_message_impl.h" +#include "mojo/core/watcher_dispatcher.h" namespace mojo { -namespace edk { +namespace core { namespace { @@ -54,6 +54,10 @@ const uint32_t kMaxHandlesPerMessage = 1024 * 1024; // pipes too; for now we just use a constant. This only affects bootstrap pipes. const uint64_t kUnknownPipeIdForDebug = 0x7f7f7f7f7f7f7f7fUL; +// The pipe name which must be used for the sole pipe attachment on any isolated +// invitation. +constexpr base::StringPiece kIsolatedInvitationPipeName = {"\0\0\0\0", 4}; + void InvokeProcessErrorCallbackOnTaskRunner( scoped_refptr<base::TaskRunner> task_runner, MojoProcessErrorHandler handler, @@ -80,13 +84,13 @@ void InvokeProcessErrorCallbackOnTaskRunner( handler, context, error, flags)); } -// Helper class which is bound to the lifetime of a ProcessErrorCallback -// generated by the |MojoSendInvitation()| API. When the last reference to the -// error callback is lost within the EDK, which will happen shortly after a -// connection to the process is lost, that obviously guarantees that no more -// invocations of the callback will occur. At that point, the corresponding -// instance of this object (owned by the callback -- see Core::SendInvitation) -// will be destroyed. +// Helper class which is bound to the lifetime of a +// ProcessErrorCallback generated by the |MojoSendInvitation()| +// API. When the last reference to the error callback is lost within the EDK, +// which will happen shortly after a connection to the process is lost, that +// obviously guarantees that no more invocations of the callback will occur. At +// that point, the corresponding instance of this object (owned by the callback +// -- see Core::SendInvitation) will be destroyed. class ProcessDisconnectHandler { public: ProcessDisconnectHandler(scoped_refptr<base::TaskRunner> task_runner, @@ -135,8 +139,8 @@ Core::~Core() { 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_))); + base::BindOnce(&Core::PassNodeControllerToIOThread, + base::Passed(&node_controller_))); } base::trace_event::MemoryDumpManager::GetInstance() ->UnregisterAndDeleteDumpProviderSoon(std::move(handles_)); @@ -201,15 +205,12 @@ void Core::AcceptBrokerClientInvitation(ConnectionParams connection_params) { std::move(connection_params)); } -uint64_t Core::ConnectToPeer(ConnectionParams connection_params, - const ports::PortRef& port) { - RequestContext request_context; - return GetNodeController()->ConnectToPeer(std::move(connection_params), port); -} - -void Core::ClosePeerConnection(uint64_t peer_connection_id) { +void Core::ConnectIsolated(ConnectionParams connection_params, + const ports::PortRef& port, + base::StringPiece connection_name) { RequestContext request_context; - GetNodeController()->ClosePeerConnection(peer_connection_id); + GetNodeController()->ConnectIsolated(std::move(connection_params), port, + connection_name); } void Core::SetMachPortProvider(base::PortProvider* port_provider) { @@ -218,6 +219,12 @@ void Core::SetMachPortProvider(base::PortProvider* port_provider) { #endif } +#if defined(OS_MACOSX) && !defined(OS_IOS) +MachPortRelay* Core::GetMachPortRelay() { + return GetNodeController()->GetMachPortRelay(); +} +#endif + MojoHandle Core::AddDispatcher(scoped_refptr<Dispatcher> dispatcher) { base::AutoLock lock(handles_->GetLock()); return handles_->AddDispatcher(dispatcher); @@ -263,36 +270,6 @@ void Core::ReleaseDispatchersForTransit( handles_->CancelTransit(dispatchers); } -MojoResult Core::CreateInternalPlatformHandleWrapper( - ScopedInternalPlatformHandle 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::PassWrappedInternalPlatformHandle( - MojoHandle wrapper_handle, - ScopedInternalPlatformHandle* platform_handle) { - base::AutoLock lock(handles_->GetLock()); - 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->PassInternalPlatformHandle(); - } else { - result = MOJO_RESULT_INVALID_ARGUMENT; - } - d->Close(); - return result; -} - void Core::RequestShutdown(const base::Closure& callback) { GetNodeController()->RequestShutdown(callback); } @@ -338,7 +315,7 @@ MojoResult Core::QueryHandleSignalsState( MojoResult Core::CreateTrap(MojoTrapEventHandler handler, const MojoCreateTrapOptions* options, MojoHandle* trap_handle) { - if (options && options->struct_size != sizeof(*options)) + if (options && options->struct_size < sizeof(*options)) return MOJO_RESULT_INVALID_ARGUMENT; RequestContext request_context; @@ -356,7 +333,7 @@ MojoResult Core::AddTrigger(MojoHandle trap_handle, MojoTriggerCondition condition, uintptr_t context, const MojoAddTriggerOptions* options) { - if (options && options->struct_size != sizeof(*options)) + if (options && options->struct_size < sizeof(*options)) return MOJO_RESULT_INVALID_ARGUMENT; RequestContext request_context; @@ -375,7 +352,7 @@ MojoResult Core::AddTrigger(MojoHandle trap_handle, MojoResult Core::RemoveTrigger(MojoHandle trap_handle, uintptr_t context, const MojoRemoveTriggerOptions* options) { - if (options && options->struct_size != sizeof(*options)) + if (options && options->struct_size < sizeof(*options)) return MOJO_RESULT_INVALID_ARGUMENT; RequestContext request_context; @@ -387,26 +364,23 @@ MojoResult Core::RemoveTrigger(MojoHandle trap_handle, MojoResult Core::ArmTrap(MojoHandle trap_handle, const MojoArmTrapOptions* options, - uint32_t* num_ready_triggers, - uintptr_t* ready_triggers, - MojoResult* ready_results, - MojoHandleSignalsState* ready_signals_states) { - if (options && options->struct_size != sizeof(*options)) + uint32_t* num_blocking_events, + MojoTrapEvent* blocking_events) { + if (options && options->struct_size < sizeof(*options)) return MOJO_RESULT_INVALID_ARGUMENT; RequestContext request_context; scoped_refptr<Dispatcher> watcher = GetDispatcher(trap_handle); if (!watcher || watcher->GetType() != Dispatcher::Type::WATCHER) return MOJO_RESULT_INVALID_ARGUMENT; - return watcher->Arm(num_ready_triggers, ready_triggers, ready_results, - ready_signals_states); + return watcher->Arm(num_blocking_events, blocking_events); } MojoResult Core::CreateMessage(const MojoCreateMessageOptions* options, MojoMessageHandle* message_handle) { if (!message_handle) return MOJO_RESULT_INVALID_ARGUMENT; - if (options && options->struct_size != sizeof(*options)) + if (options && options->struct_size < sizeof(*options)) return MOJO_RESULT_INVALID_ARGUMENT; *message_handle = reinterpret_cast<MojoMessageHandle>( UserMessageImpl::CreateEventForNewMessage().release()); @@ -426,7 +400,7 @@ MojoResult Core::SerializeMessage(MojoMessageHandle message_handle, const MojoSerializeMessageOptions* options) { if (!message_handle) return MOJO_RESULT_INVALID_ARGUMENT; - if (options && options->struct_size != sizeof(*options)) + if (options && options->struct_size < sizeof(*options)) return MOJO_RESULT_INVALID_ARGUMENT; RequestContext request_context; return reinterpret_cast<ports::UserMessageEvent*>(message_handle) @@ -443,7 +417,7 @@ MojoResult Core::AppendMessageData(MojoMessageHandle message_handle, uint32_t* buffer_size) { if (!message_handle || (num_handles && !handles)) return MOJO_RESULT_INVALID_ARGUMENT; - if (options && options->struct_size != sizeof(*options)) + if (options && options->struct_size < sizeof(*options)) return MOJO_RESULT_INVALID_ARGUMENT; RequestContext request_context; @@ -476,7 +450,7 @@ MojoResult Core::GetMessageData(MojoMessageHandle message_handle, uint32_t* num_handles) { if (!message_handle || (num_handles && *num_handles && !handles)) return MOJO_RESULT_INVALID_ARGUMENT; - if (options && options->struct_size != sizeof(*options)) + if (options && options->struct_size < sizeof(*options)) return MOJO_RESULT_INVALID_ARGUMENT; auto* message = reinterpret_cast<ports::UserMessageEvent*>(message_handle) @@ -525,7 +499,7 @@ MojoResult Core::SetMessageContext( const MojoSetMessageContextOptions* options) { if (!message_handle) return MOJO_RESULT_INVALID_ARGUMENT; - if (options && options->struct_size != sizeof(*options)) + if (options && options->struct_size < sizeof(*options)) return MOJO_RESULT_INVALID_ARGUMENT; auto* message = reinterpret_cast<ports::UserMessageEvent*>(message_handle) ->GetMessage<UserMessageImpl>(); @@ -537,7 +511,7 @@ MojoResult Core::GetMessageContext(MojoMessageHandle message_handle, uintptr_t* context) { if (!message_handle) return MOJO_RESULT_INVALID_ARGUMENT; - if (options && options->struct_size != sizeof(*options)) + if (options && options->struct_size < sizeof(*options)) return MOJO_RESULT_INVALID_ARGUMENT; auto* message = reinterpret_cast<ports::UserMessageEvent*>(message_handle) @@ -681,7 +655,7 @@ 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)) + if (options && options->struct_size < sizeof(MojoCreateDataPipeOptions)) return MOJO_RESULT_INVALID_ARGUMENT; MojoCreateDataPipeOptions create_options; @@ -708,7 +682,7 @@ MojoResult Core::CreateDataPipe(const MojoCreateDataPipeOptions* options, // consumer of this pipe, and it would be impossible to support such access // control on Android anyway. auto writable_region_handle = ring_buffer_region.PassPlatformHandle(); -#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA) && \ +#if defined(OS_POSIX) && !defined(OS_ANDROID) && \ (!defined(OS_MACOSX) || defined(OS_IOS)) // This isn't strictly necessary, but it does make the handle configuration // consistent with regular UnsafeSharedMemoryRegions. @@ -1011,30 +985,54 @@ MojoResult Core::GetBufferInfo(MojoHandle buffer_handle, return dispatcher->GetBufferInfo(info); } -MojoResult Core::WrapInternalPlatformHandle( +MojoResult Core::WrapPlatformHandle( const MojoPlatformHandle* platform_handle, const MojoWrapPlatformHandleOptions* options, MojoHandle* mojo_handle) { - ScopedInternalPlatformHandle handle; - MojoResult result = MojoPlatformHandleToScopedInternalPlatformHandle( - platform_handle, &handle); - if (result != MOJO_RESULT_OK) - return result; + if (!platform_handle || + platform_handle->struct_size < sizeof(*platform_handle)) { + return MOJO_RESULT_INVALID_ARGUMENT; + } - return CreateInternalPlatformHandleWrapper(std::move(handle), mojo_handle); + auto handle = PlatformHandle::FromMojoPlatformHandle(platform_handle); + MojoHandle h = + AddDispatcher(PlatformHandleDispatcher::Create(std::move(handle))); + if (h == MOJO_HANDLE_INVALID) + return MOJO_RESULT_RESOURCE_EXHAUSTED; + + *mojo_handle = h; + return MOJO_RESULT_OK; } -MojoResult Core::UnwrapInternalPlatformHandle( +MojoResult Core::UnwrapPlatformHandle( MojoHandle mojo_handle, const MojoUnwrapPlatformHandleOptions* options, MojoPlatformHandle* platform_handle) { - ScopedInternalPlatformHandle handle; - MojoResult result = PassWrappedInternalPlatformHandle(mojo_handle, &handle); - if (result != MOJO_RESULT_OK) - return result; + if (!platform_handle || + platform_handle->struct_size < sizeof(*platform_handle)) { + return MOJO_RESULT_INVALID_ARGUMENT; + } - return ScopedInternalPlatformHandleToMojoPlatformHandle(std::move(handle), - platform_handle); + scoped_refptr<Dispatcher> dispatcher; + { + base::AutoLock lock(handles_->GetLock()); + dispatcher = handles_->GetDispatcher(mojo_handle); + if (dispatcher->GetType() != Dispatcher::Type::PLATFORM_HANDLE) + return MOJO_RESULT_INVALID_ARGUMENT; + + MojoResult result = + handles_->GetAndRemoveDispatcher(mojo_handle, &dispatcher); + if (result != MOJO_RESULT_OK) + return result; + } + + PlatformHandleDispatcher* phd = + static_cast<PlatformHandleDispatcher*>(dispatcher.get()); + PlatformHandle handle = phd->TakePlatformHandle(); + phd->Close(); + + PlatformHandle::ToMojoPlatformHandle(std::move(handle), platform_handle); + return MOJO_RESULT_OK; } MojoResult Core::WrapPlatformSharedMemoryRegion( @@ -1047,7 +1045,7 @@ MojoResult Core::WrapPlatformSharedMemoryRegion( MojoHandle* mojo_handle) { DCHECK(size); -#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA) && \ +#if defined(OS_POSIX) && !defined(OS_ANDROID) && \ (!defined(OS_MACOSX) || defined(OS_IOS)) if (access_mode == MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE) { if (num_platform_handles != 2) @@ -1058,12 +1056,11 @@ MojoResult Core::WrapPlatformSharedMemoryRegion( return MOJO_RESULT_INVALID_ARGUMENT; #endif - ScopedInternalPlatformHandle handles[2]; + PlatformHandle handles[2]; bool handles_ok = true; for (size_t i = 0; i < num_platform_handles; ++i) { - MojoResult result = MojoPlatformHandleToScopedInternalPlatformHandle( - &platform_handles[i], &handles[i]); - if (result != MOJO_RESULT_OK) + handles[i] = PlatformHandle::FromMojoPlatformHandle(&platform_handles[i]); + if (!handles[i].is_valid()) handles_ok = false; } if (!handles_ok) @@ -1089,7 +1086,7 @@ MojoResult Core::WrapPlatformSharedMemoryRegion( base::subtle::PlatformSharedMemoryRegion region = base::subtle::PlatformSharedMemoryRegion::Take( - CreateSharedMemoryRegionHandleFromInternalPlatformHandles( + CreateSharedMemoryRegionHandleFromPlatformHandles( std::move(handles[0]), std::move(handles[1])), mode, size, token); if (!region.IsValid()) @@ -1161,34 +1158,32 @@ MojoResult Core::UnwrapPlatformSharedMemoryRegion( return MOJO_RESULT_INVALID_ARGUMENT; } - ScopedInternalPlatformHandle handle; - ScopedInternalPlatformHandle read_only_handle; - ExtractInternalPlatformHandlesFromSharedMemoryRegionHandle( + PlatformHandle handle; + PlatformHandle read_only_handle; + ExtractPlatformHandlesFromSharedMemoryRegionHandle( region.PassPlatformHandle(), &handle, &read_only_handle); const uint32_t available_handle_storage_slots = *num_platform_handles; if (available_handle_storage_slots < 1) return MOJO_RESULT_RESOURCE_EXHAUSTED; *num_platform_handles = 1; -#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA) && \ +#if defined(OS_POSIX) && !defined(OS_ANDROID) && \ (!defined(OS_MACOSX) || defined(OS_IOS)) if (region.GetMode() == base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) { if (available_handle_storage_slots < 2) return MOJO_RESULT_INVALID_ARGUMENT; - if (ScopedInternalPlatformHandleToMojoPlatformHandle( - std::move(read_only_handle), &platform_handles[1]) != - MOJO_RESULT_OK) { + PlatformHandle::ToMojoPlatformHandle(std::move(read_only_handle), + &platform_handles[1]); + if (platform_handles[1].type == MOJO_PLATFORM_HANDLE_TYPE_INVALID) return MOJO_RESULT_INVALID_ARGUMENT; - } *num_platform_handles = 2; } #endif - if (ScopedInternalPlatformHandleToMojoPlatformHandle( - std::move(handle), &platform_handles[0]) != MOJO_RESULT_OK) { + PlatformHandle::ToMojoPlatformHandle(std::move(handle), &platform_handles[0]); + if (platform_handles[0].type == MOJO_PLATFORM_HANDLE_TYPE_INVALID) return MOJO_RESULT_INVALID_ARGUMENT; - } return MOJO_RESULT_OK; } @@ -1267,7 +1262,8 @@ MojoResult Core::ExtractMessagePipeFromInvitation( auto* invitation_dispatcher = static_cast<InvitationDispatcher*>(dispatcher.get()); // First attempt to extract from the invitation object itself. This is for - // cases where this creation was created in-process. + // cases where this invitation was created in-process or is an accepted + // isolated invitation. MojoResult extract_result = invitation_dispatcher->ExtractMessagePipe( name_string, message_pipe_handle); if (extract_result == MOJO_RESULT_OK || @@ -1336,16 +1332,23 @@ MojoResult Core::SendInvitation( auto* invitation_dispatcher = static_cast<InvitationDispatcher*>(dispatcher.get()); - ScopedInternalPlatformHandle endpoint_handle; - MojoResult result = MojoPlatformHandleToScopedInternalPlatformHandle( - &transport_endpoint->platform_handles[0], &endpoint_handle); - if (result != MOJO_RESULT_OK || !endpoint_handle.is_valid()) + auto endpoint = PlatformHandle::FromMojoPlatformHandle( + &transport_endpoint->platform_handles[0]); + if (!endpoint.is_valid()) return MOJO_RESULT_INVALID_ARGUMENT; -#if defined(OS_WIN) || (defined(OS_POSIX) && !defined(OS_FUCHSIA)) - if (transport_endpoint->type == MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER) - endpoint_handle.get().needs_connection = true; + ConnectionParams connection_params; +#if defined(OS_WIN) || defined(OS_POSIX) + if (transport_endpoint->type == + MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER) { + connection_params = + ConnectionParams(PlatformChannelServerEndpoint(std::move(endpoint))); + } #endif + if (!connection_params.server_endpoint().is_valid()) { + connection_params = + ConnectionParams(PlatformChannelEndpoint(std::move(endpoint))); + } // At this point everything else has been validated, so we can take ownership // of the dispatcher. @@ -1357,15 +1360,13 @@ MojoResult Core::SendInvitation( if (result != MOJO_RESULT_OK) { // Release ownership of the endpoint platform handle, per the API // contract. The caller retains ownership on failure. - ignore_result(endpoint_handle.release()); + connection_params.TakeEndpoint().TakePlatformHandle().release(); + connection_params.TakeServerEndpoint().TakePlatformHandle().release(); return result; } DCHECK_EQ(removed_dispatcher.get(), invitation_dispatcher); } - ConnectionParams connection_params(TransportProtocol::kLegacy, - std::move(endpoint_handle)); - std::vector<std::pair<std::string, ports::PortRef>> attached_ports; InvitationDispatcher::PortMapping attached_port_map = invitation_dispatcher->TakeAttachedPorts(); @@ -1373,10 +1374,22 @@ MojoResult Core::SendInvitation( for (auto& entry : attached_port_map) attached_ports.emplace_back(entry.first, std::move(entry.second)); + bool is_isolated = + options && (options->flags & MOJO_SEND_INVITATION_FLAG_ISOLATED); RequestContext request_context; - GetNodeController()->SendBrokerClientInvitation( - target_process, std::move(connection_params), attached_ports, - process_error_callback); + if (is_isolated) { + DCHECK_EQ(attached_ports.size(), 1u); + DCHECK_EQ(attached_ports[0].first, kIsolatedInvitationPipeName); + base::StringPiece connection_name(options->isolated_connection_name, + options->isolated_connection_name_length); + GetNodeController()->ConnectIsolated(std::move(connection_params), + attached_ports[0].second, + connection_name); + } else { + GetNodeController()->SendBrokerClientInvitation( + target_process, std::move(connection_params), attached_ports, + process_error_callback); + } return MOJO_RESULT_OK; } @@ -1404,32 +1417,83 @@ MojoResult Core::AcceptInvitation( if (!invitation_handle) return MOJO_RESULT_INVALID_ARGUMENT; - *invitation_handle = AddDispatcher(new InvitationDispatcher); + auto dispatcher = base::MakeRefCounted<InvitationDispatcher>(); + *invitation_handle = AddDispatcher(dispatcher); if (*invitation_handle == MOJO_HANDLE_INVALID) return MOJO_RESULT_RESOURCE_EXHAUSTED; - ScopedInternalPlatformHandle endpoint_handle; - MojoResult result = MojoPlatformHandleToScopedInternalPlatformHandle( - &transport_endpoint->platform_handles[0], &endpoint_handle); - if (result != MOJO_RESULT_OK) { + auto endpoint = PlatformHandle::FromMojoPlatformHandle( + &transport_endpoint->platform_handles[0]); + if (!endpoint.is_valid()) { Close(*invitation_handle); *invitation_handle = MOJO_HANDLE_INVALID; return MOJO_RESULT_INVALID_ARGUMENT; } -#if defined(OS_WIN) || (defined(OS_POSIX) && !defined(OS_FUCHSIA)) - if (transport_endpoint->type == MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER) - endpoint_handle.get().needs_connection = true; + ConnectionParams connection_params; +#if defined(OS_WIN) || defined(OS_POSIX) + if (transport_endpoint->type == + MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER) { + connection_params = + ConnectionParams(PlatformChannelServerEndpoint(std::move(endpoint))); + } #endif + if (!connection_params.server_endpoint().is_valid()) { + connection_params = + ConnectionParams(PlatformChannelEndpoint(std::move(endpoint))); + } + bool is_isolated = + options && (options->flags & MOJO_ACCEPT_INVITATION_FLAG_ISOLATED); + NodeController* const node_controller = GetNodeController(); RequestContext request_context; - ConnectionParams connection_params(TransportProtocol::kLegacy, - std::move(endpoint_handle)); - GetNodeController()->AcceptBrokerClientInvitation( - std::move(connection_params)); + if (is_isolated) { + // For an isolated invitation, we simply mint a new port pair here and send + // one name to the remote endpoint while stashing the other in the accepted + // invitation object for later extraction. + ports::PortRef local_port; + ports::PortRef remote_port; + node_controller->node()->CreatePortPair(&local_port, &remote_port); + node_controller->ConnectIsolated(std::move(connection_params), remote_port, + base::StringPiece()); + MojoResult result = + dispatcher->AttachMessagePipe(kIsolatedInvitationPipeName, local_port); + DCHECK_EQ(MOJO_RESULT_OK, result); + } else { + node_controller->AcceptBrokerClientInvitation(std::move(connection_params)); + } + return MOJO_RESULT_OK; } +MojoResult Core::SetQuota(MojoHandle handle, + MojoQuotaType type, + uint64_t limit, + const MojoSetQuotaOptions* options) { + RequestContext request_context; + if (options && options->struct_size < sizeof(*options)) + return MOJO_RESULT_INVALID_ARGUMENT; + auto dispatcher = GetDispatcher(handle); + if (!dispatcher) + return MOJO_RESULT_INVALID_ARGUMENT; + + return dispatcher->SetQuota(type, limit); +} + +MojoResult Core::QueryQuota(MojoHandle handle, + MojoQuotaType type, + const MojoQueryQuotaOptions* options, + uint64_t* limit, + uint64_t* usage) { + RequestContext request_context; + if (options && options->struct_size < sizeof(*options)) + return MOJO_RESULT_INVALID_ARGUMENT; + auto dispatcher = GetDispatcher(handle); + if (!dispatcher) + return MOJO_RESULT_INVALID_ARGUMENT; + return dispatcher->QueryQuota(type, limit, usage); +} + void Core::GetActiveHandlesForTest(std::vector<MojoHandle>* handles) { base::AutoLock lock(handles_->GetLock()); handles_->GetActiveHandlesForTest(handles); @@ -1445,5 +1509,5 @@ void Core::PassNodeControllerToIOThread( node_controller.release()->DestroyOnIOThreadShutdown(); } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/core.h b/chromium/mojo/core/core.h index 9401189c809..2840bc5e680 100644 --- a/chromium/mojo/edk/system/core.h +++ b/chromium/mojo/core/core.h @@ -2,8 +2,8 @@ // 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_ +#ifndef MOJO_CORE_CORE_H_ +#define MOJO_CORE_CORE_H_ #include <memory> #include <string> @@ -15,17 +15,18 @@ #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/node_controller.h" -#include "mojo/edk/system/system_impl_export.h" +#include "build/build_config.h" +#include "mojo/core/dispatcher.h" +#include "mojo/core/handle_signals_state.h" +#include "mojo/core/handle_table.h" +#include "mojo/core/node_controller.h" +#include "mojo/core/system_impl_export.h" #include "mojo/public/c/system/buffer.h" #include "mojo/public/c/system/data_pipe.h" #include "mojo/public/c/system/invitation.h" #include "mojo/public/c/system/message_pipe.h" #include "mojo/public/c/system/platform_handle.h" +#include "mojo/public/c/system/quota.h" #include "mojo/public/c/system/trap.h" #include "mojo/public/c/system/types.h" @@ -34,8 +35,9 @@ class PortProvider; } namespace mojo { -namespace edk { +namespace core { +class MachPortRelay; class PlatformSharedMemoryMapping; // |Core| is an object that implements the Mojo system calls. All public methods @@ -86,9 +88,9 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { const std::vector<std::pair<std::string, ports::PortRef>>& attached_ports, const ProcessErrorCallback& process_error_callback); - // Accepts a broker client invitation via |connection_params|. The other end - // of the connection medium in |connection_params| must have been used by some - // other process to send an OutgoingBrokerClientInvitation. + // Accepts an invitation via |connection_params|. The other end of the + // connection medium in |connection_params| must have been used by some other + // process to send an invitation. void AcceptBrokerClientInvitation(ConnectionParams connection_params); // Extracts a named message pipe endpoint from the broker client invitation @@ -102,15 +104,21 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { // channel. |port| is a port to be merged with the remote peer's port, which // it will provide via the same API. // - // Returns an ID which can be later used to close the connection via - // ClosePeerConnection(). - uint64_t ConnectToPeer(ConnectionParams connection_params, - const ports::PortRef& port); - void ClosePeerConnection(uint64_t peer_connection_id); + // |connection_name| if non-empty guarantees that no other isolated + // connections exist in the calling process using the same name. This is + // useful for invitation endpoints that use a named server accepting multiple + // connections. + void ConnectIsolated(ConnectionParams connection_params, + const ports::PortRef& port, + base::StringPiece connection_name); // Sets the mach port provider for this process. void SetMachPortProvider(base::PortProvider* port_provider); +#if defined(OS_MACOSX) && !defined(OS_IOS) + MachPortRelay* GetMachPortRelay(); +#endif + MojoHandle AddDispatcher(scoped_refptr<Dispatcher> dispatcher); // Adds new dispatchers for non-message-pipe handles received in a message. @@ -139,15 +147,6 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { const std::vector<Dispatcher::DispatcherInTransit>& dispatchers, bool in_transit); - // See "mojo/edk/embedder/embedder.h" for more information on these functions. - MojoResult CreateInternalPlatformHandleWrapper( - ScopedInternalPlatformHandle platform_handle, - MojoHandle* wrapper_handle); - - MojoResult PassWrappedInternalPlatformHandle( - MojoHandle wrapper_handle, - ScopedInternalPlatformHandle* platform_handle); - // 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 @@ -160,9 +159,9 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { // 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. + // "mojo/core/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": @@ -184,10 +183,8 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { const MojoRemoveTriggerOptions* options); MojoResult ArmTrap(MojoHandle trap_handle, const MojoArmTrapOptions* options, - uint32_t* num_ready_triggers, - uintptr_t* ready_triggers, - MojoResult* ready_results, - MojoHandleSignalsState* ready_signals_states); + uint32_t* num_blocking_events, + MojoTrapEvent* blocking_events); MojoResult CreateMessage(const MojoCreateMessageOptions* options, MojoMessageHandle* message_handle); MojoResult DestroyMessage(MojoMessageHandle message_handle); @@ -283,11 +280,10 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { // These methods correspond to the API functions defined in // "mojo/public/c/system/platform_handle.h". - MojoResult WrapInternalPlatformHandle( - const MojoPlatformHandle* platform_handle, - const MojoWrapPlatformHandleOptions* options, - MojoHandle* mojo_handle); - MojoResult UnwrapInternalPlatformHandle( + MojoResult WrapPlatformHandle(const MojoPlatformHandle* platform_handle, + const MojoWrapPlatformHandleOptions* options, + MojoHandle* mojo_handle); + MojoResult UnwrapPlatformHandle( MojoHandle mojo_handle, const MojoUnwrapPlatformHandleOptions* options, MojoPlatformHandle* platform_handle); @@ -335,6 +331,17 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { const MojoAcceptInvitationOptions* options, MojoHandle* invitation_handle); + // Quota API. + MojoResult SetQuota(MojoHandle handle, + MojoQuotaType type, + uint64_t limit, + const MojoSetQuotaOptions* options); + MojoResult QueryQuota(MojoHandle handle, + MojoQuotaType type, + const MojoQueryQuotaOptions* options, + uint64_t* limit, + uint64_t* usage); + void GetActiveHandlesForTest(std::vector<MojoHandle>* handles); private: @@ -372,7 +379,7 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { DISALLOW_COPY_AND_ASSIGN(Core); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_CORE_H_ +#endif // MOJO_CORE_CORE_H_ diff --git a/chromium/mojo/edk/system/core_test_base.cc b/chromium/mojo/core/core_test_base.cc index a025a9bbf3b..3be60c5b73f 100644 --- a/chromium/mojo/edk/system/core_test_base.cc +++ b/chromium/mojo/core/core_test_base.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/core_test_base.h" #include <stddef.h> #include <stdint.h> @@ -12,13 +12,13 @@ #include "base/logging.h" #include "base/macros.h" #include "base/memory/ref_counted.h" -#include "mojo/edk/system/configuration.h" -#include "mojo/edk/system/core.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/user_message_impl.h" +#include "mojo/core/configuration.h" +#include "mojo/core/core.h" +#include "mojo/core/dispatcher.h" +#include "mojo/core/user_message_impl.h" namespace mojo { -namespace edk { +namespace core { namespace test { namespace { @@ -105,11 +105,9 @@ class MockDispatcher : public Dispatcher { // CoreTestBase ---------------------------------------------------------------- -CoreTestBase::CoreTestBase() { -} +CoreTestBase::CoreTestBase() {} -CoreTestBase::~CoreTestBase() { -} +CoreTestBase::~CoreTestBase() {} MojoHandle CoreTestBase::CreateMockHandle(CoreTestBase::MockHandleInfo* info) { scoped_refptr<MockDispatcher> dispatcher = MockDispatcher::Create(info); @@ -135,8 +133,7 @@ CoreTestBase_MockHandleInfo::CoreTestBase_MockHandleInfo() begin_read_data_call_count_(0), end_read_data_call_count_(0) {} -CoreTestBase_MockHandleInfo::~CoreTestBase_MockHandleInfo() { -} +CoreTestBase_MockHandleInfo::~CoreTestBase_MockHandleInfo() {} unsigned CoreTestBase_MockHandleInfo::GetCtorCallCount() const { base::AutoLock locker(lock_); @@ -249,5 +246,5 @@ void CoreTestBase_MockHandleInfo::IncrementEndReadDataCallCount() { } } // namespace test -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/core_test_base.h b/chromium/mojo/core/core_test_base.h index 84718be2944..5d37d252c2e 100644 --- a/chromium/mojo/edk/system/core_test_base.h +++ b/chromium/mojo/core/core_test_base.h @@ -2,19 +2,19 @@ // 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_ +#ifndef MOJO_CORE_CORE_TEST_BASE_H_ +#define MOJO_CORE_CORE_TEST_BASE_H_ #include <stddef.h> #include "base/macros.h" #include "base/synchronization/lock.h" -#include "mojo/edk/system/test_utils.h" +#include "mojo/core/test_utils.h" #include "mojo/public/c/system/types.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo { -namespace edk { +namespace core { class Core; @@ -87,7 +87,7 @@ class CoreTestBase_MockHandleInfo { }; } // namespace test -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_CORE_TEST_BASE_H_ +#endif // MOJO_CORE_CORE_TEST_BASE_H_ diff --git a/chromium/mojo/edk/system/core_unittest.cc b/chromium/mojo/core/core_unittest.cc index a35208b7a46..8846051ad03 100644 --- a/chromium/mojo/edk/system/core_unittest.cc +++ b/chromium/mojo/core/core_unittest.cc @@ -2,15 +2,16 @@ // 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 "mojo/core/core.h" #include <stdint.h> #include <limits> #include "base/bind.h" -#include "mojo/edk/system/core_test_base.h" -#include "mojo/edk/system/test_utils.h" +#include "build/build_config.h" +#include "mojo/core/core_test_base.h" +#include "mojo/core/test_utils.h" #include "mojo/public/cpp/system/wait.h" #if defined(OS_WIN) @@ -18,14 +19,15 @@ #endif namespace mojo { -namespace edk { +namespace core { 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 | MOJO_HANDLE_SIGNAL_PEER_REMOTE; + MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_PEER_REMOTE | + MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED; using CoreTest = test::CoreTestBase; @@ -208,14 +210,12 @@ TEST_F(CoreTest, MessagePipe) { // 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); + EXPECT_FALSE(hss[1].satisfiable_signals & MOJO_HANDLE_SIGNAL_WRITABLE); // 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); + EXPECT_TRUE(hss[1].satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE); // Discard a message from |h[1]|. ASSERT_EQ(MOJO_RESULT_OK, core()->ReadMessage(h[1], nullptr, &message)); @@ -225,7 +225,7 @@ TEST_F(CoreTest, MessagePipe) { 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); + EXPECT_FALSE(hss[1].satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE); // Try writing to |h[1]|. ASSERT_EQ(MOJO_RESULT_OK, core()->CreateMessage(nullptr, &message)); @@ -455,5 +455,5 @@ TEST_F(CoreTest, DataPipe) { } } // namespace -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/data_pipe_consumer_dispatcher.cc b/chromium/mojo/core/data_pipe_consumer_dispatcher.cc index 78d6d129e86..68e2f7e24d6 100644 --- a/chromium/mojo/edk/system/data_pipe_consumer_dispatcher.cc +++ b/chromium/mojo/core/data_pipe_consumer_dispatcher.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/data_pipe_consumer_dispatcher.h" #include <stddef.h> #include <stdint.h> @@ -14,16 +14,16 @@ #include "base/bind.h" #include "base/logging.h" #include "base/memory/ref_counted.h" -#include "mojo/edk/embedder/platform_handle_utils.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/request_context.h" -#include "mojo/edk/system/user_message_impl.h" +#include "mojo/core/core.h" +#include "mojo/core/data_pipe_control_message.h" +#include "mojo/core/node_controller.h" +#include "mojo/core/platform_handle_utils.h" +#include "mojo/core/request_context.h" +#include "mojo/core/user_message_impl.h" #include "mojo/public/c/system/data_pipe.h" namespace mojo { -namespace edk { +namespace core { namespace { @@ -299,7 +299,7 @@ void DataPipeConsumerDispatcher::StartSerialize(uint32_t* num_bytes, bool DataPipeConsumerDispatcher::EndSerialize( void* destination, ports::PortName* ports, - ScopedInternalPlatformHandle* platform_handles) { + PlatformHandle* platform_handles) { SerializedState* state = static_cast<SerializedState*>(destination); memcpy(&state->options, &options_, sizeof(MojoCreateDataPipeOptions)); memset(state->padding, 0, sizeof(state->padding)); @@ -320,13 +320,14 @@ bool DataPipeConsumerDispatcher::EndSerialize( ports[0] = control_port_.name(); - ScopedInternalPlatformHandle ignored_handle; - ExtractInternalPlatformHandlesFromSharedMemoryRegionHandle( - region_handle.PassPlatformHandle(), &platform_handles[0], - &ignored_handle); - if (!platform_handles[0].is_valid() || ignored_handle.is_valid()) + PlatformHandle handle; + PlatformHandle ignored_handle; + ExtractPlatformHandlesFromSharedMemoryRegionHandle( + region_handle.PassPlatformHandle(), &handle, &ignored_handle); + if (!handle.is_valid() || ignored_handle.is_valid()) return false; + platform_handles[0] = std::move(handle); return true; } @@ -361,7 +362,7 @@ DataPipeConsumerDispatcher::Deserialize(const void* data, size_t num_bytes, const ports::PortName* ports, size_t num_ports, - ScopedInternalPlatformHandle* handles, + PlatformHandle* handles, size_t num_handles) { if (num_ports != 1 || num_handles != 1 || num_bytes != sizeof(SerializedState)) { @@ -379,11 +380,8 @@ DataPipeConsumerDispatcher::Deserialize(const void* data, if (node_controller->node()->GetPort(ports[0], &port) != ports::OK) return nullptr; - ScopedInternalPlatformHandle buffer_handle; - std::swap(buffer_handle, handles[0]); - auto region_handle = - CreateSharedMemoryRegionHandleFromInternalPlatformHandles( - std::move(buffer_handle), ScopedInternalPlatformHandle()); + auto region_handle = CreateSharedMemoryRegionHandleFromPlatformHandles( + std::move(handles[0]), PlatformHandle()); auto region = base::subtle::PlatformSharedMemoryRegion::Take( std::move(region_handle), base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe, @@ -592,5 +590,5 @@ void DataPipeConsumerDispatcher::UpdateSignalsStateNoLock() { } } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/data_pipe_consumer_dispatcher.h b/chromium/mojo/core/data_pipe_consumer_dispatcher.h index 48a63f1ba4a..982d3f055a0 100644 --- a/chromium/mojo/edk/system/data_pipe_consumer_dispatcher.h +++ b/chromium/mojo/core/data_pipe_consumer_dispatcher.h @@ -2,8 +2,8 @@ // 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_ +#ifndef MOJO_CORE_DATA_PIPE_CONSUMER_DISPATCHER_H_ +#define MOJO_CORE_DATA_PIPE_CONSUMER_DISPATCHER_H_ #include <stddef.h> #include <stdint.h> @@ -15,14 +15,13 @@ #include "base/memory/shared_memory_mapping.h" #include "base/memory/unsafe_shared_memory_region.h" #include "base/synchronization/lock.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" +#include "mojo/core/dispatcher.h" +#include "mojo/core/ports/port_ref.h" +#include "mojo/core/system_impl_export.h" +#include "mojo/core/watcher_set.h" namespace mojo { -namespace edk { +namespace core { class NodeController; @@ -58,7 +57,7 @@ class MOJO_SYSTEM_IMPL_EXPORT DataPipeConsumerDispatcher final uint32_t* num_handles) override; bool EndSerialize(void* destination, ports::PortName* ports, - ScopedInternalPlatformHandle* handles) override; + PlatformHandle* handles) override; bool BeginTransit() override; void CompleteTransitAndClose() override; void CancelTransit() override; @@ -68,7 +67,7 @@ class MOJO_SYSTEM_IMPL_EXPORT DataPipeConsumerDispatcher final size_t num_bytes, const ports::PortName* ports, size_t num_ports, - ScopedInternalPlatformHandle* handles, + PlatformHandle* handles, size_t num_handles); private: @@ -123,7 +122,7 @@ class MOJO_SYSTEM_IMPL_EXPORT DataPipeConsumerDispatcher final DISALLOW_COPY_AND_ASSIGN(DataPipeConsumerDispatcher); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_DATA_PIPE_CONSUMER_DISPATCHER_H_ +#endif // MOJO_CORE_DATA_PIPE_CONSUMER_DISPATCHER_H_ diff --git a/chromium/mojo/edk/system/data_pipe_control_message.cc b/chromium/mojo/core/data_pipe_control_message.cc index 6bb0785d71e..cd782e5bc12 100644 --- a/chromium/mojo/edk/system/data_pipe_control_message.cc +++ b/chromium/mojo/core/data_pipe_control_message.cc @@ -2,15 +2,14 @@ // 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/core/data_pipe_control_message.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/node_controller.h" -#include "mojo/edk/system/ports/event.h" -#include "mojo/edk/system/user_message_impl.h" +#include "mojo/core/node_controller.h" +#include "mojo/core/ports/event.h" +#include "mojo/core/user_message_impl.h" namespace mojo { -namespace edk { +namespace core { void SendDataPipeControlMessage(NodeController* node_controller, const ports::PortRef& port, @@ -34,5 +33,5 @@ void SendDataPipeControlMessage(NodeController* node_controller, } } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/data_pipe_control_message.h b/chromium/mojo/core/data_pipe_control_message.h index ec84ea3c559..3493e2b8b7b 100644 --- a/chromium/mojo/edk/system/data_pipe_control_message.h +++ b/chromium/mojo/core/data_pipe_control_message.h @@ -2,19 +2,18 @@ // 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_ +#ifndef MOJO_CORE_DATA_PIPE_CONTROL_MESSAGE_H_ +#define MOJO_CORE_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/core/ports/port_ref.h" #include "mojo/public/c/system/macros.h" namespace mojo { -namespace edk { +namespace core { class NodeController; @@ -37,7 +36,7 @@ void SendDataPipeControlMessage(NodeController* node_controller, DataPipeCommand command, uint32_t num_bytes); -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_DATA_PIPE_CONTROL_MESSAGE_H_ +#endif // MOJO_CORE_DATA_PIPE_CONTROL_MESSAGE_H_ diff --git a/chromium/mojo/edk/system/data_pipe_producer_dispatcher.cc b/chromium/mojo/core/data_pipe_producer_dispatcher.cc index caa76d959ef..e6256f8f0d9 100644 --- a/chromium/mojo/edk/system/data_pipe_producer_dispatcher.cc +++ b/chromium/mojo/core/data_pipe_producer_dispatcher.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/data_pipe_producer_dispatcher.h" #include <stddef.h> #include <stdint.h> @@ -12,17 +12,17 @@ #include "base/bind.h" #include "base/logging.h" #include "base/memory/ref_counted.h" -#include "mojo/edk/embedder/platform_handle_utils.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/request_context.h" -#include "mojo/edk/system/user_message_impl.h" +#include "mojo/core/configuration.h" +#include "mojo/core/core.h" +#include "mojo/core/data_pipe_control_message.h" +#include "mojo/core/node_controller.h" +#include "mojo/core/platform_handle_utils.h" +#include "mojo/core/request_context.h" +#include "mojo/core/user_message_impl.h" #include "mojo/public/c/system/data_pipe.h" namespace mojo { -namespace edk { +namespace core { namespace { @@ -258,7 +258,7 @@ void DataPipeProducerDispatcher::StartSerialize(uint32_t* num_bytes, bool DataPipeProducerDispatcher::EndSerialize( void* destination, ports::PortName* ports, - ScopedInternalPlatformHandle* platform_handles) { + PlatformHandle* platform_handles) { SerializedState* state = static_cast<SerializedState*>(destination); memcpy(&state->options, &options_, sizeof(MojoCreateDataPipeOptions)); memset(state->padding, 0, sizeof(state->padding)); @@ -279,13 +279,14 @@ bool DataPipeProducerDispatcher::EndSerialize( ports[0] = control_port_.name(); - ScopedInternalPlatformHandle ignored_handle; - ExtractInternalPlatformHandlesFromSharedMemoryRegionHandle( - region_handle.PassPlatformHandle(), &platform_handles[0], - &ignored_handle); - if (!platform_handles[0].is_valid() || ignored_handle.is_valid()) + PlatformHandle handle; + PlatformHandle ignored_handle; + ExtractPlatformHandlesFromSharedMemoryRegionHandle( + region_handle.PassPlatformHandle(), &handle, &ignored_handle); + if (!handle.is_valid() || ignored_handle.is_valid()) return false; + platform_handles[0] = std::move(handle); return true; } @@ -322,7 +323,7 @@ DataPipeProducerDispatcher::Deserialize(const void* data, size_t num_bytes, const ports::PortName* ports, size_t num_ports, - ScopedInternalPlatformHandle* handles, + PlatformHandle* handles, size_t num_handles) { if (num_ports != 1 || num_handles != 1 || num_bytes != sizeof(SerializedState)) { @@ -340,11 +341,8 @@ DataPipeProducerDispatcher::Deserialize(const void* data, if (node_controller->node()->GetPort(ports[0], &port) != ports::OK) return nullptr; - ScopedInternalPlatformHandle buffer_handle; - std::swap(buffer_handle, handles[0]); - auto region_handle = - CreateSharedMemoryRegionHandleFromInternalPlatformHandles( - std::move(buffer_handle), ScopedInternalPlatformHandle()); + auto region_handle = CreateSharedMemoryRegionHandleFromPlatformHandles( + std::move(handles[0]), PlatformHandle()); auto region = base::subtle::PlatformSharedMemoryRegion::Take( std::move(region_handle), base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe, @@ -536,5 +534,5 @@ void DataPipeProducerDispatcher::UpdateSignalsStateNoLock() { } } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/data_pipe_producer_dispatcher.h b/chromium/mojo/core/data_pipe_producer_dispatcher.h index 4ee72c8e426..15cd1c9b0de 100644 --- a/chromium/mojo/edk/system/data_pipe_producer_dispatcher.h +++ b/chromium/mojo/core/data_pipe_producer_dispatcher.h @@ -2,8 +2,8 @@ // 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_ +#ifndef MOJO_CORE_DATA_PIPE_PRODUCER_DISPATCHER_H_ +#define MOJO_CORE_DATA_PIPE_PRODUCER_DISPATCHER_H_ #include <stddef.h> #include <stdint.h> @@ -15,14 +15,13 @@ #include "base/memory/shared_memory_mapping.h" #include "base/memory/unsafe_shared_memory_region.h" #include "base/synchronization/lock.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" +#include "mojo/core/dispatcher.h" +#include "mojo/core/ports/port_ref.h" +#include "mojo/core/system_impl_export.h" +#include "mojo/core/watcher_set.h" namespace mojo { -namespace edk { +namespace core { class NodeController; @@ -57,7 +56,7 @@ class MOJO_SYSTEM_IMPL_EXPORT DataPipeProducerDispatcher final uint32_t* num_handles) override; bool EndSerialize(void* destination, ports::PortName* ports, - ScopedInternalPlatformHandle* handles) override; + PlatformHandle* handles) override; bool BeginTransit() override; void CompleteTransitAndClose() override; void CancelTransit() override; @@ -67,7 +66,7 @@ class MOJO_SYSTEM_IMPL_EXPORT DataPipeProducerDispatcher final size_t num_bytes, const ports::PortName* ports, size_t num_ports, - ScopedInternalPlatformHandle* handles, + PlatformHandle* handles, size_t num_handles); private: @@ -114,7 +113,7 @@ class MOJO_SYSTEM_IMPL_EXPORT DataPipeProducerDispatcher final DISALLOW_COPY_AND_ASSIGN(DataPipeProducerDispatcher); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_DATA_PIPE_PRODUCER_DISPATCHER_H_ +#endif // MOJO_CORE_DATA_PIPE_PRODUCER_DISPATCHER_H_ diff --git a/chromium/mojo/edk/system/data_pipe_unittest.cc b/chromium/mojo/core/data_pipe_unittest.cc index dbee3ed8b45..b2f32a4c29a 100644 --- a/chromium/mojo/edk/system/data_pipe_unittest.cc +++ b/chromium/mojo/core/data_pipe_unittest.cc @@ -13,10 +13,10 @@ #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 "build/build_config.h" +#include "mojo/core/embedder/embedder.h" +#include "mojo/core/test/mojo_test_base.h" +#include "mojo/core/test_utils.h" #include "mojo/public/c/system/data_pipe.h" #include "mojo/public/c/system/functions.h" #include "mojo/public/c/system/message_pipe.h" @@ -25,7 +25,7 @@ #include "testing/gtest/include/gtest/gtest.h" namespace mojo { -namespace edk { +namespace core { namespace { const uint32_t kSizeOfOptions = @@ -2043,5 +2043,5 @@ TEST_F(DataPipeTest, StatusChangeInTransit) { #endif // !defined(OS_IOS) } // namespace -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/dispatcher.cc b/chromium/mojo/core/dispatcher.cc index 6d9f875a1c0..a110dbdc8e4 100644 --- a/chromium/mojo/edk/system/dispatcher.cc +++ b/chromium/mojo/core/dispatcher.cc @@ -2,19 +2,19 @@ // 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 "mojo/core/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/ports/event.h" -#include "mojo/edk/system/shared_buffer_dispatcher.h" +#include "mojo/core/configuration.h" +#include "mojo/core/data_pipe_consumer_dispatcher.h" +#include "mojo/core/data_pipe_producer_dispatcher.h" +#include "mojo/core/message_pipe_dispatcher.h" +#include "mojo/core/platform_handle_dispatcher.h" +#include "mojo/core/ports/event.h" +#include "mojo/core/shared_buffer_dispatcher.h" namespace mojo { -namespace edk { +namespace core { Dispatcher::DispatcherInTransit::DispatcherInTransit() {} @@ -34,10 +34,8 @@ 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) { +MojoResult Dispatcher::Arm(uint32_t* num_blocking_events, + MojoTrapEvent* blocking_events) { return MOJO_RESULT_INVALID_ARGUMENT; } @@ -108,6 +106,16 @@ MojoResult Dispatcher::ExtractMessagePipe(base::StringPiece name, return MOJO_RESULT_INVALID_ARGUMENT; } +MojoResult Dispatcher::SetQuota(MojoQuotaType type, uint64_t limit) { + return MOJO_RESULT_INVALID_ARGUMENT; +} + +MojoResult Dispatcher::QueryQuota(MojoQuotaType type, + uint64_t* limit, + uint64_t* usage) { + return MOJO_RESULT_INVALID_ARGUMENT; +} + HandleSignalsState Dispatcher::GetHandleSignalsState() const { return HandleSignalsState(); } @@ -133,7 +141,7 @@ void Dispatcher::StartSerialize(uint32_t* num_bytes, bool Dispatcher::EndSerialize(void* destination, ports::PortName* ports, - ScopedInternalPlatformHandle* handles) { + PlatformHandle* handles) { LOG(ERROR) << "Attempting to serialize a non-transferrable dispatcher."; return true; } @@ -153,7 +161,7 @@ scoped_refptr<Dispatcher> Dispatcher::Deserialize( size_t num_bytes, const ports::PortName* ports, size_t num_ports, - ScopedInternalPlatformHandle* platform_handles, + PlatformHandle* platform_handles, size_t num_platform_handles) { switch (type) { case Type::MESSAGE_PIPE: @@ -186,5 +194,5 @@ Dispatcher::Dispatcher() {} Dispatcher::~Dispatcher() {} -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/dispatcher.h b/chromium/mojo/core/dispatcher.h index 3776be2e812..58cd7a7a39d 100644 --- a/chromium/mojo/edk/system/dispatcher.h +++ b/chromium/mojo/core/dispatcher.h @@ -2,8 +2,8 @@ // 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_ +#ifndef MOJO_CORE_DISPATCHER_H_ +#define MOJO_CORE_DISPATCHER_H_ #include <stddef.h> #include <stdint.h> @@ -16,21 +16,21 @@ #include "base/memory/ref_counted.h" #include "base/strings/string_piece.h" #include "base/synchronization/lock.h" -#include "mojo/edk/embedder/platform_handle.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/handle_signals_state.h" -#include "mojo/edk/system/ports/name.h" -#include "mojo/edk/system/ports/port_ref.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/edk/system/watch.h" +#include "mojo/core/handle_signals_state.h" +#include "mojo/core/ports/name.h" +#include "mojo/core/ports/port_ref.h" +#include "mojo/core/system_impl_export.h" +#include "mojo/core/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/quota.h" #include "mojo/public/c/system/trap.h" #include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/platform/platform_handle.h" namespace mojo { -namespace edk { +namespace core { namespace ports { class UserMessageEvent; @@ -80,10 +80,8 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher MojoTriggerCondition condition, 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); + virtual MojoResult Arm(uint32_t* num_blocking_events, + MojoTrapEvent* blocking_events); ///////////// Message pipe API ///////////// @@ -136,6 +134,12 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher virtual MojoResult ExtractMessagePipe(base::StringPiece name, MojoHandle* message_pipe_handle); + // Quota API. + virtual MojoResult SetQuota(MojoQuotaType type, uint64_t limit); + virtual MojoResult QueryQuota(MojoQuotaType type, + uint64_t* limit, + uint64_t* usage); + ///////////// General-purpose API for all handle types ///////// // Gets the current handle signals state. (The default implementation simply @@ -177,13 +181,13 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher // will close. // // NOTE: Transit MAY still fail after this call returns. Implementations - // should not assume InternalPlatformHandle ownership has transferred until + // should not assume PlatformHandle ownership has transferred until // CompleteTransitAndClose() is called. In other words, if CancelTransit() is - // called, the implementation should retain its InternalPlatformHandles in - // working condition. + // called, the implementation should retain its PlatformHandles in working + // condition. virtual bool EndSerialize(void* destination, ports::PortName* ports, - ScopedInternalPlatformHandle* handles); + 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 @@ -200,14 +204,13 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher 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, - ScopedInternalPlatformHandle* platform_handles, - size_t platform_handle_count); + 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 platform_handle_count); protected: friend class base::RefCountedThreadSafe<Dispatcher>; @@ -224,7 +227,7 @@ MOJO_SYSTEM_IMPL_EXPORT inline std::ostream& operator<<(std::ostream& out, return out << static_cast<int>(type); } -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_DISPATCHER_H_ +#endif // MOJO_CORE_DISPATCHER_H_ diff --git a/chromium/mojo/core/embedder/BUILD.gn b/chromium/mojo/core/embedder/BUILD.gn new file mode 100644 index 00000000000..47f1c390166 --- /dev/null +++ b/chromium/mojo/core/embedder/BUILD.gn @@ -0,0 +1,29 @@ +# Copyright 2018 The Chromium 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("embedder") { + output_name = "mojo_core_embedder" + + public = [ + "configuration.h", + "embedder.h", + "scoped_ipc_support.h", + ] + + sources = [ + "embedder.cc", + "scoped_ipc_support.cc", + ] + + defines = [ "IS_MOJO_CORE_EMBEDDER_IMPL" ] + + public_deps = [ + "//base", + ] + + deps = [ + "//mojo/core:embedder_internal", + "//mojo/public/c/system", + ] +} diff --git a/chromium/mojo/core/embedder/README.md b/chromium/mojo/core/embedder/README.md new file mode 100644 index 00000000000..7183a6374db --- /dev/null +++ b/chromium/mojo/core/embedder/README.md @@ -0,0 +1,86 @@ +# Mojo Core Embedder API +This document is a subset of the [Mojo documentation](/mojo/README.md). + +[TOC] + +## Overview + +The Mojo Core Embedder API enables process to initialize and use Mojo for IPC, +using an implementation of Mojo Core that is statically linked into the +application. See the note about dynamic linking +[here](/mojo/README.md#Mojo-Core) for more information about an alternative +approach to Mojo Core initialization. + +**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 Embedder API. Most processes defined in the +Chrome repo today already fully initialize Mojo Core so that all other public +Mojo APIs just work out of the box. + +## Basic Initialization + +As an embedder, initializing Mojo Core requires a single call to +`mojo::core::Init`: + +``` +#include "mojo/core/embedder/embedder.h" + +int main(int argc, char** argv) { + mojo::core::Init(); + + // Now you can create message pipes, write messages, etc + + return 0; +} +``` + +This enables local API calls to work, so message pipes *etc* can be created and +used. In some cases (particuarly many unit testing scenarios) this is +sufficient, but to support any actual multiprocess communication (e.g. sending +or accepting Mojo invitations), a second IPC initialization step is required. + +## IPC Initialization + +Internal Mojo IPC implementation requires a background `TaskRunner` on which it +can watch for inbound I/O from other processes. This is configured using a +`ScopedIPCSupport` object, which keeps IPC support alive through the extent of +its lifetime. + +Typically an application will create a dedicated background thread and give its +`TaskRunner` to Mojo. Note that in Chromium, we use the existing "IO thread" in +the browser process and content child processes. In general, any thread used +for Mojo IPC support must be running a `base::MessageLoop::TYPE_IO` loop. + +``` +#include "base/threading/thread.h" +#include "mojo/core/embedder/embedder.h" +#include "mojo/core/embedder/scoped_ipc_support.h" + +int main(int argc, char** argv) { + mojo::core::Init(); + + base::Thread ipc_thread("ipc!"); + ipc_thread.StartWithOptions( + base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); + + // As long as this object is alive, all Mojo API surface relevant to IPC + // connections is usable, and message pipes which span a process boundary will + // continue to function. + mojo::core::ScopedIPCSupport ipc_support( + ipc_thread.task_runner(), + mojo::core::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 + +Once IPC is initialized, you can bootstrap connections to other processes by +using the public +[Invitations API](/mojo/public/cpp/system/README.md#Invitations). diff --git a/chromium/mojo/edk/embedder/configuration.h b/chromium/mojo/core/embedder/configuration.h index d02b9995e60..f4621dcd174 100644 --- a/chromium/mojo/edk/embedder/configuration.h +++ b/chromium/mojo/core/embedder/configuration.h @@ -2,18 +2,18 @@ // 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_ +#ifndef MOJO_CORE_EMBEDDER_CONFIGURATION_H_ +#define MOJO_CORE_EMBEDDER_CONFIGURATION_H_ #include <stddef.h> #include <stdint.h> namespace mojo { -namespace edk { +namespace core { // A set of configuration parameters that the Mojo system uses internally. The // configuration used can be overridden from the default by passing a -// Configuration into |mojo::edk::Init()|. See embedder.h. +// Configuration into |mojo::core::Init()|. See embedder.h. // // NOTE: Please ensure that this type remains a simple aggregate of POD fields. struct Configuration { @@ -41,7 +41,7 @@ struct Configuration { size_t max_shared_memory_num_bytes = 1024 * 1024 * 1024; }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_EMBEDDER_CONFIGURATION_H_ +#endif // MOJO_CORE_EMBEDDER_CONFIGURATION_H_ diff --git a/chromium/mojo/core/embedder/embedder.cc b/chromium/mojo/core/embedder/embedder.cc new file mode 100644 index 00000000000..4044de083ee --- /dev/null +++ b/chromium/mojo/core/embedder/embedder.cc @@ -0,0 +1,50 @@ +// 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/core/embedder/embedder.h" + +#include <stdint.h> +#include <utility> + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/task_runner.h" +#include "build/build_config.h" +#include "mojo/core/configuration.h" +#include "mojo/core/core.h" +#include "mojo/core/entrypoints.h" +#include "mojo/core/node_controller.h" +#include "mojo/public/c/system/thunks.h" + +namespace mojo { +namespace core { + +void Init(const Configuration& configuration) { + internal::g_configuration = configuration; + InitializeCore(); + MojoEmbedderSetSystemThunks(&GetSystemThunks()); +} + +void Init() { + Init(Configuration()); +} + +void SetDefaultProcessErrorCallback(const ProcessErrorCallback& callback) { + Core::Get()->SetDefaultProcessErrorCallback(callback); +} + +scoped_refptr<base::TaskRunner> GetIOTaskRunner() { + return Core::Get()->GetNodeController()->io_task_runner(); +} + +#if defined(OS_MACOSX) && !defined(OS_IOS) +void SetMachPortProvider(base::PortProvider* port_provider) { + DCHECK(port_provider); + Core::Get()->SetMachPortProvider(port_provider); +} +#endif + +} // namespace core +} // namespace mojo diff --git a/chromium/mojo/core/embedder/embedder.h b/chromium/mojo/core/embedder/embedder.h new file mode 100644 index 00000000000..feb7ca1be4b --- /dev/null +++ b/chromium/mojo/core/embedder/embedder.h @@ -0,0 +1,65 @@ +// 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_CORE_EMBEDDER_EMBEDDER_H_ +#define MOJO_CORE_EMBEDDER_EMBEDDER_H_ + +#include <stddef.h> + +#include <string> + +#include "base/callback.h" +#include "base/component_export.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 "build/build_config.h" +#include "mojo/core/embedder/configuration.h" + +namespace base { +class PortProvider; +} + +namespace mojo { +namespace core { + +using ProcessErrorCallback = base::Callback<void(const std::string& error)>; + +// Basic configuration/initialization ------------------------------------------ + +// Must be called first, or just after setting configuration parameters, to +// initialize the (global, singleton) system state. There is no corresponding +// shutdown operation: once the embedder is initialized, public Mojo C API calls +// remain available for the remainder of the process's lifetime. +COMPONENT_EXPORT(MOJO_CORE_EMBEDDER) +void Init(const Configuration& configuration); + +// Like above but uses a default Configuration. +COMPONENT_EXPORT(MOJO_CORE_EMBEDDER) void Init(); + +// Sets a default callback to invoke when an internal error is reported but +// cannot be associated with a specific child process. Calling this is optional. +COMPONENT_EXPORT(MOJO_CORE_EMBEDDER) +void SetDefaultProcessErrorCallback(const ProcessErrorCallback& callback); + +// Initialialization/shutdown for interprocess communication (IPC) ------------- + +// Retrieves the TaskRunner used for IPC I/O, as set by ScopedIPCSupport. +COMPONENT_EXPORT(MOJO_CORE_EMBEDDER) +scoped_refptr<base::TaskRunner> GetIOTaskRunner(); + +#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. +// +// If called at all, this must be called while a ScopedIPCSupport exists. +COMPONENT_EXPORT(MOJO_CORE_EMBEDDER) +void SetMachPortProvider(base::PortProvider* port_provider); +#endif + +} // namespace core +} // namespace mojo + +#endif // MOJO_CORE_EMBEDDER_EMBEDDER_H_ diff --git a/chromium/mojo/edk/embedder/process_error_callback.h b/chromium/mojo/core/embedder/process_error_callback.h index 53234def1f4..f60ce899906 100644 --- a/chromium/mojo/edk/embedder/process_error_callback.h +++ b/chromium/mojo/core/embedder/process_error_callback.h @@ -2,20 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_EDK_EMBEDDER_PROCESS_ERROR_CALLBACK_H_ -#define MOJO_EDK_EMBEDDER_PROCESS_ERROR_CALLBACK_H_ +#ifndef MOJO_CORE_EMBEDDER_PROCESS_ERROR_CALLBACK_H_ +#define MOJO_CORE_EMBEDDER_PROCESS_ERROR_CALLBACK_H_ #include <string> #include "base/callback.h" namespace mojo { -namespace edk { +namespace core { using ProcessErrorCallback = base::RepeatingCallback<void(const std::string& error)>; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_EMBEDDER_PROCESS_ERROR_CALLBACK_H_ +#endif // MOJO_CORE_EMBEDDER_PROCESS_ERROR_CALLBACK_H_ diff --git a/chromium/mojo/edk/embedder/scoped_ipc_support.cc b/chromium/mojo/core/embedder/scoped_ipc_support.cc index a0231726d8f..71174f535ea 100644 --- a/chromium/mojo/edk/embedder/scoped_ipc_support.cc +++ b/chromium/mojo/core/embedder/scoped_ipc_support.cc @@ -2,16 +2,16 @@ // 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 "mojo/core/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/system/core.h" +#include "mojo/core/core.h" namespace mojo { -namespace edk { +namespace core { namespace { @@ -23,7 +23,8 @@ void ShutdownIPCSupport(const base::Closure& callback) { ScopedIPCSupport::ScopedIPCSupport( scoped_refptr<base::TaskRunner> io_thread_task_runner, - ShutdownPolicy shutdown_policy) : shutdown_policy_(shutdown_policy) { + ShutdownPolicy shutdown_policy) + : shutdown_policy_(shutdown_policy) { Core::Get()->SetIOTaskRunner(io_thread_task_runner); } @@ -39,9 +40,9 @@ ScopedIPCSupport::~ScopedIPCSupport() { ShutdownIPCSupport(base::Bind(&base::WaitableEvent::Signal, base::Unretained(&shutdown_event))); - base::ThreadRestrictions::ScopedAllowWait allow_io; + base::ScopedAllowBaseSyncPrimitives allow_io; shutdown_event.Wait(); } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/embedder/scoped_ipc_support.h b/chromium/mojo/core/embedder/scoped_ipc_support.h index 83611b76b51..8afcd00d541 100644 --- a/chromium/mojo/edk/embedder/scoped_ipc_support.h +++ b/chromium/mojo/core/embedder/scoped_ipc_support.h @@ -2,23 +2,23 @@ // 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_ +#ifndef MOJO_CORE_EMBEDDER_SCOPED_IPC_SUPPORT_H_ +#define MOJO_CORE_EMBEDDER_SCOPED_IPC_SUPPORT_H_ +#include "base/component_export.h" #include "base/memory/ref_counted.h" -#include "mojo/edk/system/system_impl_export.h" namespace base { class TaskRunner; } namespace mojo { -namespace edk { +namespace core { // A simple class that initialized Mojo IPC support on construction and shuts // down IPC support on destruction, optionally blocking the destructor on clean // IPC shutdown completion. -class MOJO_SYSTEM_IMPL_EXPORT ScopedIPCSupport { +class COMPONENT_EXPORT(MOJO_CORE_EMBEDDER) ScopedIPCSupport { public: // ShutdownPolicy is a type for specifying the desired Mojo IPC support // shutdown behavior used during ScopedIPCSupport destruction. @@ -32,7 +32,7 @@ class MOJO_SYSTEM_IMPL_EXPORT ScopedIPCSupport { // 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. + // //mojo/core/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: @@ -112,7 +112,7 @@ class MOJO_SYSTEM_IMPL_EXPORT ScopedIPCSupport { DISALLOW_COPY_AND_ASSIGN(ScopedIPCSupport); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_EMBEDDER_SCOPED_IPC_SUPPORT_H_ +#endif // MOJO_CORE_EMBEDDER_SCOPED_IPC_SUPPORT_H_ diff --git a/chromium/mojo/edk/embedder/embedder_unittest.cc b/chromium/mojo/core/embedder_unittest.cc index e0e3c194fd8..f336f4c8f70 100644 --- a/chromium/mojo/edk/embedder/embedder_unittest.cc +++ b/chromium/mojo/core/embedder_unittest.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/embedder/embedder.h" #include <stddef.h> #include <stdint.h> @@ -22,29 +22,26 @@ #include "base/memory/writable_shared_memory_region.h" #include "base/message_loop/message_loop.h" #include "base/path_service.h" -#include "base/process/process_handle.h" +#include "base/rand_util.h" #include "base/run_loop.h" +#include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" #include "base/synchronization/waitable_event.h" #include "base/test/test_timeouts.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/outgoing_broker_client_invitation.h" -#include "mojo/edk/embedder/peer_connection.h" -#include "mojo/edk/embedder/platform_channel_pair.h" -#include "mojo/edk/system/core.h" -#include "mojo/edk/system/shared_buffer_dispatcher.h" -#include "mojo/edk/system/test_utils.h" -#include "mojo/edk/test/mojo_test_base.h" +#include "mojo/core/core.h" +#include "mojo/core/shared_buffer_dispatcher.h" +#include "mojo/core/test/mojo_test_base.h" +#include "mojo/core/test_utils.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/platform_handle.h" #include "mojo/public/cpp/system/wait.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo { -namespace edk { +namespace core { namespace { template <typename T> @@ -170,37 +167,6 @@ TEST_F(EmbedderTest, ChannelsHandlePassing) { ASSERT_EQ(MOJO_RESULT_OK, MojoClose(h1)); } -TEST_F(EmbedderTest, PipeSetup_LaunchDeath) { - PlatformChannelPair pair; - - OutgoingBrokerClientInvitation invitation; - ScopedMessagePipeHandle parent_mp = invitation.AttachMessagePipe("unused"); - invitation.Send( - base::GetCurrentProcessHandle(), - ConnectionParams(TransportProtocol::kLegacy, pair.PassServerHandle())); - - // Close the remote end, simulating child death before the child extracts the - // attached message pipe. - 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 invitation = std::make_unique<OutgoingBrokerClientInvitation>(); - ScopedMessagePipeHandle parent_mp = invitation->AttachMessagePipe("unused"); - - // Ensure that if an OutgoingBrokerClientInvitation goes away before Send() is - // called, any message pipes attachde to it detect peer closure. - invitation.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" @@ -255,7 +221,8 @@ TEST_F(EmbedderTest, MultiprocessChannels) { 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_FALSE(state.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE); + ASSERT_FALSE(state.satisfiable_signals & MOJO_HANDLE_SIGNAL_WRITABLE); ASSERT_EQ(MOJO_RESULT_OK, MojoClose(mp2)); }); @@ -298,7 +265,8 @@ DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MultiprocessChannelsClient, 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_FALSE(state.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE); + ASSERT_FALSE(state.satisfiable_signals & MOJO_HANDLE_SIGNAL_WRITABLE); ASSERT_EQ(MOJO_RESULT_OK, MojoClose(mp1)); } @@ -375,17 +343,15 @@ DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MultiprocessSharedMemoryClient, EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(sb1)); } -#if defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_MACOSX) enum class HandleType { POSIX, MACH, - MACH_NULL, }; const HandleType kTestHandleTypes[] = { - HandleType::MACH, HandleType::MACH_NULL, HandleType::POSIX, - HandleType::POSIX, HandleType::MACH, + HandleType::MACH, HandleType::POSIX, HandleType::POSIX, HandleType::MACH, }; // Test that we can mix file descriptors and mach port handles. @@ -393,22 +359,17 @@ TEST_F(EmbedderTest, MultiprocessMixMachAndFds) { const size_t kShmSize = 1234; RunTestClient("MultiprocessMixMachAndFdsClient", [&](MojoHandle 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++) { + MojoHandle platform_handles[base::size(kTestHandleTypes)]; + for (size_t i = 0; i < base::size(kTestHandleTypes); i++) { const auto type = kTestHandleTypes[i]; - ScopedInternalPlatformHandle scoped_handle; + PlatformHandle 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(InternalPlatformHandle(file.TakePlatformFile())); - EXPECT_EQ(InternalPlatformHandle::Type::POSIX, - scoped_handle.get().type); - } else if (type == HandleType::MACH_NULL) { - scoped_handle.reset( - InternalPlatformHandle(static_cast<mach_port_t>(MACH_PORT_NULL))); - EXPECT_EQ(InternalPlatformHandle::Type::MACH, scoped_handle.get().type); + scoped_handle = PlatformHandle(base::ScopedFD(file.TakePlatformFile())); + ASSERT_TRUE(scoped_handle.is_valid_fd()); } else { auto shared_memory = base::UnsafeSharedMemoryRegion::Create(kShmSize); ASSERT_TRUE(shared_memory.IsValid()); @@ -416,17 +377,16 @@ TEST_F(EmbedderTest, MultiprocessMixMachAndFds) { base::UnsafeSharedMemoryRegion::TakeHandleForSerialization( std::move(shared_memory)) .PassPlatformHandle(); - scoped_handle.reset(InternalPlatformHandle(shm_handle.release())); - EXPECT_EQ(InternalPlatformHandle::Type::MACH, scoped_handle.get().type); + scoped_handle = PlatformHandle(std::move(shm_handle)); + ASSERT_TRUE(scoped_handle.is_valid_mach_port()); } - ASSERT_EQ(MOJO_RESULT_OK, - CreateInternalPlatformHandleWrapper(std::move(scoped_handle), - platform_handles + i)); + platform_handles[i] = + WrapPlatformHandle(std::move(scoped_handle)).release().value(); } // 2. Send all the handles to the child. WriteMessageWithHandles(server_mp, "hello", platform_handles, - arraysize(kTestHandleTypes)); + base::size(kTestHandleTypes)); // 3. Read a message from |server_mp|. EXPECT_EQ("bye", ReadMessage(server_mp)); @@ -436,7 +396,7 @@ TEST_F(EmbedderTest, MultiprocessMixMachAndFds) { DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MultiprocessMixMachAndFdsClient, EmbedderTest, client_mp) { - const int kNumHandles = arraysize(kTestHandleTypes); + const int kNumHandles = base::size(kTestHandleTypes); MojoHandle platform_handles[kNumHandles]; // 1. Read from |client_mp|, which should have a message containing @@ -447,20 +407,12 @@ DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MultiprocessMixMachAndFdsClient, // 2. Extract each handle, and verify the type. for (int i = 0; i < kNumHandles; i++) { const auto type = kTestHandleTypes[i]; - ScopedInternalPlatformHandle scoped_handle; - ASSERT_EQ(MOJO_RESULT_OK, PassWrappedInternalPlatformHandle( - platform_handles[i], &scoped_handle)); + PlatformHandle scoped_handle = + UnwrapPlatformHandle(ScopedHandle(Handle(platform_handles[i]))); if (type == HandleType::POSIX) { - EXPECT_NE(0, scoped_handle.get().handle); - EXPECT_EQ(InternalPlatformHandle::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(InternalPlatformHandle::Type::MACH, scoped_handle.get().type); + EXPECT_TRUE(scoped_handle.is_valid_fd()); } else { - EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL), - scoped_handle.get().port); - EXPECT_EQ(InternalPlatformHandle::Type::MACH, scoped_handle.get().type); + EXPECT_TRUE(scoped_handle.is_valid_mach_port()); } } @@ -468,115 +420,10 @@ DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MultiprocessMixMachAndFdsClient, 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) - -#if !defined(OS_FUCHSIA) -// TODO(fuchsia): Implement NamedPlatformHandles (crbug.com/754038). - -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, - ScopedInternalPlatformHandle* output) { - *output = CreateClientHandle(named_handle); -} - -TEST_F(EmbedderTest, ClosePendingPeerConnection) { - NamedPlatformHandle named_handle = GenerateChannelName(); - std::string peer_token = GenerateRandomToken(); - - auto peer_connection = std::make_unique<PeerConnection>(); - ScopedMessagePipeHandle server_pipe = - peer_connection->Connect(ConnectionParams( - TransportProtocol::kLegacy, CreateServerHandle(named_handle))); - peer_connection.reset(); - EXPECT_EQ(MOJO_RESULT_OK, - Wait(server_pipe.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED)); - base::MessageLoop message_loop; - base::RunLoop run_loop; - ScopedInternalPlatformHandle 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()); -} - -#endif // !defined(OS_FUCHSIA) - -#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_MACOSX) #endif // !defined(OS_IOS) } // namespace -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/embedder/entrypoints.cc b/chromium/mojo/core/entrypoints.cc index c57a02341ec..42e74171922 100644 --- a/chromium/mojo/edk/embedder/entrypoints.cc +++ b/chromium/mojo/core/entrypoints.cc @@ -2,20 +2,21 @@ // 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 "mojo/core/entrypoints.h" #include <stdint.h> -#include "mojo/edk/system/core.h" +#include "mojo/core/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" +#include "mojo/public/c/system/quota.h" namespace { -mojo::edk::Core* g_core; +mojo::core::Core* g_core; extern "C" { @@ -237,28 +238,24 @@ MojoResult MojoRemoveTriggerImpl(MojoHandle trap_handle, MojoResult MojoArmTrapImpl(MojoHandle trap_handle, const MojoArmTrapOptions* options, - uint32_t* num_ready_triggers, - uintptr_t* ready_triggers, - MojoResult* ready_results, - MojoHandleSignalsState* ready_signals_states) { - return g_core->ArmTrap(trap_handle, options, num_ready_triggers, - ready_triggers, ready_results, ready_signals_states); + uint32_t* num_blocking_events, + MojoTrapEvent* blocking_events) { + return g_core->ArmTrap(trap_handle, options, num_blocking_events, + blocking_events); } MojoResult MojoWrapPlatformHandleImpl( const MojoPlatformHandle* platform_handle, const MojoWrapPlatformHandleOptions* options, MojoHandle* mojo_handle) { - return g_core->WrapInternalPlatformHandle(platform_handle, options, - mojo_handle); + return g_core->WrapPlatformHandle(platform_handle, options, mojo_handle); } MojoResult MojoUnwrapPlatformHandleImpl( MojoHandle mojo_handle, const MojoUnwrapPlatformHandleOptions* options, MojoPlatformHandle* platform_handle) { - return g_core->UnwrapInternalPlatformHandle(mojo_handle, options, - platform_handle); + return g_core->UnwrapPlatformHandle(mojo_handle, options, platform_handle); } MojoResult MojoWrapPlatformSharedMemoryRegionImpl( @@ -332,6 +329,22 @@ MojoResult MojoAcceptInvitationImpl( invitation_handle); } +MojoResult MojoSetQuotaImpl(MojoHandle handle, + MojoQuotaType type, + uint64_t limit, + const MojoSetQuotaOptions* options) { + return g_core->SetQuota(handle, type, limit, options); +} + +MojoResult MojoQueryQuotaImpl(MojoHandle handle, + MojoQuotaType type, + const MojoQueryQuotaOptions* options, + uint64_t* current_limit, + uint64_t* current_usage) { + return g_core->QueryQuota(handle, type, options, current_limit, + current_usage); +} + } // extern "C" MojoSystemThunks g_thunks = {sizeof(MojoSystemThunks), @@ -375,12 +388,14 @@ MojoSystemThunks g_thunks = {sizeof(MojoSystemThunks), MojoAttachMessagePipeToInvitationImpl, MojoExtractMessagePipeFromInvitationImpl, MojoSendInvitationImpl, - MojoAcceptInvitationImpl}; + MojoAcceptInvitationImpl, + MojoSetQuotaImpl, + MojoQueryQuotaImpl}; } // namespace namespace mojo { -namespace edk { +namespace core { // static Core* Core::Get() { @@ -395,5 +410,5 @@ const MojoSystemThunks& GetSystemThunks() { return g_thunks; } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/embedder/entrypoints.h b/chromium/mojo/core/entrypoints.h index 63299a627ce..e18366b74dd 100644 --- a/chromium/mojo/edk/embedder/entrypoints.h +++ b/chromium/mojo/core/entrypoints.h @@ -2,14 +2,14 @@ // 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_ +#ifndef MOJO_CORE_ENTRYPOINTS_H_ +#define MOJO_CORE_ENTRYPOINTS_H_ -#include "mojo/edk/system/system_impl_export.h" +#include "mojo/core/system_impl_export.h" #include "mojo/public/c/system/thunks.h" namespace mojo { -namespace edk { +namespace core { // Initializes the global Core object. MOJO_SYSTEM_IMPL_EXPORT void InitializeCore(); @@ -19,7 +19,7 @@ MOJO_SYSTEM_IMPL_EXPORT void InitializeCore(); // application loading. MOJO_SYSTEM_IMPL_EXPORT const MojoSystemThunks& GetSystemThunks(); -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_EMBEDDER_ENTRYPOINTS_H_ +#endif // MOJO_CORE_ENTRYPOINTS_H_ diff --git a/chromium/mojo/edk/export_only_thunks_api.lst b/chromium/mojo/core/export_only_thunks_api.lst index 11e436bd3a3..11e436bd3a3 100644 --- a/chromium/mojo/edk/export_only_thunks_api.lst +++ b/chromium/mojo/core/export_only_thunks_api.lst diff --git a/chromium/mojo/edk/system/handle_signals_state.h b/chromium/mojo/core/handle_signals_state.h index c8c5b5ea4a0..7cd7d8db692 100644 --- a/chromium/mojo/edk/system/handle_signals_state.h +++ b/chromium/mojo/core/handle_signals_state.h @@ -2,14 +2,14 @@ // 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_ +#ifndef MOJO_CORE_HANDLE_SIGNALS_STATE_H_ +#define MOJO_CORE_HANDLE_SIGNALS_STATE_H_ -#include "mojo/edk/system/system_impl_export.h" +#include "mojo/core/system_impl_export.h" #include "mojo/public/c/system/types.h" namespace mojo { -namespace edk { +namespace core { // A convenience wrapper around the MojoHandleSignalsState struct. // @@ -71,6 +71,11 @@ struct MOJO_SYSTEM_IMPL_EXPORT HandleSignalsState final return satisfies_any(MOJO_HANDLE_SIGNAL_PEER_REMOTE); } + // Indicates whether the handle has exceeded some quota limit. + bool quota_exceeded() const { + return satisfies_any(MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); + } + // The handle will never be |readable()| again. bool never_readable() const { return !can_satisfy_any(MOJO_HANDLE_SIGNAL_READABLE); @@ -100,7 +105,7 @@ struct MOJO_SYSTEM_IMPL_EXPORT HandleSignalsState final static_assert(sizeof(HandleSignalsState) == sizeof(MojoHandleSignalsState), "HandleSignalsState should add no overhead"); -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_HANDLE_SIGNALS_STATE_H_ +#endif // MOJO_CORE_HANDLE_SIGNALS_STATE_H_ diff --git a/chromium/mojo/edk/system/handle_table.cc b/chromium/mojo/core/handle_table.cc index a9fcc651414..62419a92363 100644 --- a/chromium/mojo/edk/system/handle_table.cc +++ b/chromium/mojo/core/handle_table.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/handle_table.h" #include <stdint.h> @@ -11,7 +11,7 @@ #include "base/trace_event/memory_dump_manager.h" namespace mojo { -namespace edk { +namespace core { namespace { @@ -40,11 +40,9 @@ const char* GetNameForDispatcherType(Dispatcher::Type type) { } // namespace -HandleTable::HandleTable() { -} +HandleTable::HandleTable() {} -HandleTable::~HandleTable() { -} +HandleTable::~HandleTable() {} base::Lock& HandleTable::GetLock() { return lock_; @@ -171,6 +169,7 @@ bool HandleTable::OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, handle_count[Dispatcher::Type::SHARED_BUFFER]; handle_count[Dispatcher::Type::WATCHER]; handle_count[Dispatcher::Type::PLATFORM_HANDLE]; + handle_count[Dispatcher::Type::INVITATION]; // Count the number of each dispatcher type. { @@ -201,5 +200,5 @@ HandleTable::Entry::Entry(const Entry& other) = default; HandleTable::Entry::~Entry() {} -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/handle_table.h b/chromium/mojo/core/handle_table.h index f8206cfa21c..234bdac239e 100644 --- a/chromium/mojo/edk/system/handle_table.h +++ b/chromium/mojo/core/handle_table.h @@ -2,8 +2,8 @@ // 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_ +#ifndef MOJO_CORE_HANDLE_TABLE_H_ +#define MOJO_CORE_HANDLE_TABLE_H_ #include <stdint.h> @@ -14,12 +14,12 @@ #include "base/macros.h" #include "base/synchronization/lock.h" #include "base/trace_event/memory_dump_provider.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/system_impl_export.h" +#include "mojo/core/dispatcher.h" +#include "mojo/core/system_impl_export.h" #include "mojo/public/c/system/types.h" namespace mojo { -namespace edk { +namespace core { class MOJO_SYSTEM_IMPL_EXPORT HandleTable : public base::trace_event::MemoryDumpProvider { @@ -55,7 +55,7 @@ class MOJO_SYSTEM_IMPL_EXPORT HandleTable void CancelTransit( const std::vector<Dispatcher::DispatcherInTransit>& dispatchers); - void GetActiveHandlesForTest(std::vector<MojoHandle> *handles); + void GetActiveHandlesForTest(std::vector<MojoHandle>* handles); private: FRIEND_TEST_ALL_PREFIXES(HandleTableTest, OnMemoryDump); @@ -65,13 +65,13 @@ class MOJO_SYSTEM_IMPL_EXPORT HandleTable base::trace_event::ProcessMemoryDump* pmd) override; struct Entry { - Entry(); - explicit Entry(scoped_refptr<Dispatcher> dispatcher); - Entry(const Entry& other); - ~Entry(); + Entry(); + explicit Entry(scoped_refptr<Dispatcher> dispatcher); + Entry(const Entry& other); + ~Entry(); - scoped_refptr<Dispatcher> dispatcher; - bool busy = false; + scoped_refptr<Dispatcher> dispatcher; + bool busy = false; }; using HandleMap = base::hash_map<MojoHandle, Entry>; @@ -84,7 +84,7 @@ class MOJO_SYSTEM_IMPL_EXPORT HandleTable DISALLOW_COPY_AND_ASSIGN(HandleTable); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_HANDLE_TABLE_H_ +#endif // MOJO_CORE_HANDLE_TABLE_H_ diff --git a/chromium/mojo/edk/system/handle_table_unittest.cc b/chromium/mojo/core/handle_table_unittest.cc index 32f6425586b..493f71e021d 100644 --- a/chromium/mojo/edk/system/handle_table_unittest.cc +++ b/chromium/mojo/core/handle_table_unittest.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/handle_table.h" #include <memory> @@ -11,7 +11,7 @@ #include "base/trace_event/memory_dump_request_args.h" #include "base/trace_event/process_memory_dump.h" #include "base/trace_event/trace_event_argument.h" -#include "mojo/edk/system/dispatcher.h" +#include "mojo/core/dispatcher.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -22,7 +22,7 @@ using testing::Contains; using testing::ByRef; namespace mojo { -namespace edk { +namespace core { namespace { class FakeMessagePipeDispatcher : public Dispatcher { @@ -56,19 +56,18 @@ TEST(HandleTableTest, OnMemoryDump) { { base::AutoLock auto_lock(ht.GetLock()); - scoped_refptr<mojo::edk::Dispatcher> dispatcher( - new FakeMessagePipeDispatcher); + scoped_refptr<Dispatcher> dispatcher(new FakeMessagePipeDispatcher); ht.AddDispatcher(dispatcher); } base::trace_event::MemoryDumpArgs args = { base::trace_event::MemoryDumpLevelOfDetail::DETAILED}; - base::trace_event::ProcessMemoryDump pmd(nullptr, args); + base::trace_event::ProcessMemoryDump pmd(args); ht.OnMemoryDump(args, &pmd); CheckNameAndValue(&pmd, "mojo/message_pipe", 1); CheckNameAndValue(&pmd, "mojo/data_pipe_consumer", 0); } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/invitation_dispatcher.cc b/chromium/mojo/core/invitation_dispatcher.cc index aa684bc0409..82d33871025 100644 --- a/chromium/mojo/edk/system/invitation_dispatcher.cc +++ b/chromium/mojo/core/invitation_dispatcher.cc @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/edk/system/invitation_dispatcher.h" +#include "mojo/core/invitation_dispatcher.h" -#include "mojo/edk/system/core.h" +#include "mojo/core/core.h" namespace mojo { -namespace edk { +namespace core { InvitationDispatcher::InvitationDispatcher() = default; @@ -74,5 +74,5 @@ InvitationDispatcher::~InvitationDispatcher() { DCHECK(is_closed_); } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/invitation_dispatcher.h b/chromium/mojo/core/invitation_dispatcher.h index 21c530be02a..c856e1f7da2 100644 --- a/chromium/mojo/edk/system/invitation_dispatcher.h +++ b/chromium/mojo/core/invitation_dispatcher.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_EDK_SYSTEM_INVITATION_DISPATCHER_H_ -#define MOJO_EDK_SYSTEM_INVITATION_DISPATCHER_H_ +#ifndef MOJO_CORE_INVITATION_DISPATCHER_H_ +#define MOJO_CORE_INVITATION_DISPATCHER_H_ #include <stdint.h> @@ -11,13 +11,12 @@ #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/ports/port_ref.h" -#include "mojo/edk/system/system_impl_export.h" +#include "mojo/core/dispatcher.h" +#include "mojo/core/ports/port_ref.h" +#include "mojo/core/system_impl_export.h" namespace mojo { -namespace edk { +namespace core { class MOJO_SYSTEM_IMPL_EXPORT InvitationDispatcher : public Dispatcher { public: @@ -44,7 +43,7 @@ class MOJO_SYSTEM_IMPL_EXPORT InvitationDispatcher : public Dispatcher { DISALLOW_COPY_AND_ASSIGN(InvitationDispatcher); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_INVITATION_DISPATCHER_H +#endif // MOJO_CORE_INVITATION_DISPATCHER_H diff --git a/chromium/mojo/edk/system/invitation_unittest.cc b/chromium/mojo/core/invitation_unittest.cc index ddb86ae0659..791bb085539 100644 --- a/chromium/mojo/edk/system/invitation_unittest.cc +++ b/chromium/mojo/core/invitation_unittest.cc @@ -20,14 +20,14 @@ #include "base/test/scoped_task_environment.h" #include "base/threading/sequenced_task_runner_handle.h" #include "build/build_config.h" -#include "mojo/edk/test/mojo_test_base.h" +#include "mojo/core/test/mojo_test_base.h" #include "mojo/public/c/system/invitation.h" #include "mojo/public/cpp/platform/named_platform_channel.h" #include "mojo/public/cpp/platform/platform_channel.h" #include "mojo/public/cpp/system/platform_handle.h" namespace mojo { -namespace edk { +namespace core { namespace { enum class TransportType { @@ -35,6 +35,8 @@ enum class TransportType { kChannelServer, }; +const char kSecondaryChannelHandleSwitch[] = "test-secondary-channel-handle"; + class InvitationTest : public test::MojoTestBase { public: InvitationTest() = default; @@ -46,8 +48,22 @@ class InvitationTest : public test::MojoTestBase { MojoHandle* primordial_pipes, size_t num_primordial_pipes, TransportType transport_type, + MojoSendInvitationFlags send_flags, MojoProcessErrorHandler error_handler = nullptr, - uintptr_t error_handler_context = 0); + uintptr_t error_handler_context = 0, + base::CommandLine* custom_command_line = nullptr, + base::LaunchOptions* custom_launch_options = nullptr); + + static void SendInvitationToClient( + PlatformHandle endpoint_handle, + base::ProcessHandle process, + MojoHandle* primordial_pipes, + size_t num_primordial_pipes, + TransportType transport_type, + MojoSendInvitationFlags flags, + MojoProcessErrorHandler error_handler, + uintptr_t error_handler_context, + base::StringPiece isolated_invitation_name); private: base::test::ScopedTaskEnvironment task_environment_; @@ -55,6 +71,26 @@ class InvitationTest : public test::MojoTestBase { DISALLOW_COPY_AND_ASSIGN(InvitationTest); }; +void PrepareToPassRemoteEndpoint(PlatformChannel* channel, + base::LaunchOptions* options, + base::CommandLine* command_line, + base::StringPiece switch_name = {}) { + std::string value; +#if defined(OS_FUCHSIA) + channel->PrepareToPassRemoteEndpoint(&options->handles_to_transfer, &value); +#elif defined(OS_POSIX) + channel->PrepareToPassRemoteEndpoint(&options->fds_to_remap, &value); +#elif defined(OS_WIN) + channel->PrepareToPassRemoteEndpoint(&options->handles_to_inherit, &value); +#else +#error "Platform not yet supported." +#endif + + if (switch_name.empty()) + switch_name = PlatformChannel::kHandleSwitch; + command_line->AppendSwitchASCII(switch_name.as_string(), value); +} + TEST_F(InvitationTest, Create) { MojoHandle invitation; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateInvitation(nullptr, &invitation)); @@ -108,7 +144,7 @@ TEST_F(InvitationTest, InvalidArguments) { PlatformChannel channel; MojoPlatformHandle endpoint_handle; endpoint_handle.struct_size = sizeof(endpoint_handle); - PlatformHandleToMojoPlatformHandle( + PlatformHandle::ToMojoPlatformHandle( channel.TakeLocalEndpoint().TakePlatformHandle(), &endpoint_handle); ASSERT_NE(endpoint_handle.type, MOJO_PLATFORM_HANDLE_TYPE_INVALID); @@ -246,30 +282,30 @@ base::Process InvitationTest::LaunchChildTestClient( MojoHandle* primordial_pipes, size_t num_primordial_pipes, TransportType transport_type, + MojoSendInvitationFlags send_flags, MojoProcessErrorHandler error_handler, - uintptr_t error_handler_context) { - base::CommandLine command_line( - base::GetMultiProcessTestChildBaseCommandLine()); + uintptr_t error_handler_context, + base::CommandLine* custom_command_line, + base::LaunchOptions* custom_launch_options) { + base::CommandLine default_command_line = + base::GetMultiProcessTestChildBaseCommandLine(); + base::CommandLine& command_line = + custom_command_line ? *custom_command_line : default_command_line; + + base::LaunchOptions default_launch_options; + base::LaunchOptions& launch_options = + custom_launch_options ? *custom_launch_options : default_launch_options; +#if defined(OS_WIN) + launch_options.start_hidden = true; +#endif - base::LaunchOptions launch_options; base::Optional<PlatformChannel> channel; base::Optional<NamedPlatformChannel> named_channel; PlatformHandle local_endpoint_handle; if (transport_type == TransportType::kChannel) { channel.emplace(); -#if defined(OS_FUCHSIA) - channel->PrepareToPassRemoteEndpoint(&launch_options.handles_to_transfer, - &command_line); -#elif defined(OS_POSIX) - channel->PrepareToPassRemoteEndpoint(&launch_options.fds_to_remap, - &command_line); -#elif defined(OS_WIN) - launch_options.start_hidden = true; - channel->PrepareToPassRemoteEndpoint(&launch_options.handles_to_inherit, - &command_line); -#else -#error "Platform not yet supported." -#endif + PrepareToPassRemoteEndpoint(&channel.value(), &launch_options, + &command_line); local_endpoint_handle = channel->TakeLocalEndpoint().TakePlatformHandle(); } else { #if defined(OS_FUCHSIA) @@ -287,10 +323,33 @@ base::Process InvitationTest::LaunchChildTestClient( #endif } - MojoPlatformHandle endpoint_handle; - PlatformHandleToMojoPlatformHandle(std::move(local_endpoint_handle), - &endpoint_handle); - CHECK_NE(endpoint_handle.type, MOJO_PLATFORM_HANDLE_TYPE_INVALID); + base::Process child_process = base::SpawnMultiProcessTestChild( + test_client_name, command_line, launch_options); + if (channel) + channel->RemoteProcessLaunchAttempted(); + + SendInvitationToClient(std::move(local_endpoint_handle), + child_process.Handle(), primordial_pipes, + num_primordial_pipes, transport_type, send_flags, + error_handler, error_handler_context, ""); + + return child_process; +} + +// static +void InvitationTest::SendInvitationToClient( + PlatformHandle endpoint_handle, + base::ProcessHandle process, + MojoHandle* primordial_pipes, + size_t num_primordial_pipes, + TransportType transport_type, + MojoSendInvitationFlags flags, + MojoProcessErrorHandler error_handler, + uintptr_t error_handler_context, + base::StringPiece isolated_invitation_name) { + MojoPlatformHandle handle; + PlatformHandle::ToMojoPlatformHandle(std::move(endpoint_handle), &handle); + CHECK_NE(handle.type, MOJO_PLATFORM_HANDLE_TYPE_INVALID); MojoHandle invitation; CHECK_EQ(MOJO_RESULT_OK, MojoCreateInvitation(nullptr, &invitation)); @@ -300,18 +359,13 @@ base::Process InvitationTest::LaunchChildTestClient( &primordial_pipes[name])); } - base::Process child_process = base::SpawnMultiProcessTestChild( - test_client_name, command_line, launch_options); - if (channel) - channel->RemoteProcessLaunched(); - MojoPlatformProcessHandle process_handle; process_handle.struct_size = sizeof(process_handle); #if defined(OS_WIN) - process_handle.value = static_cast<uint64_t>( - reinterpret_cast<uintptr_t>(child_process.Handle())); + process_handle.value = + static_cast<uint64_t>(reinterpret_cast<uintptr_t>(process)); #else - process_handle.value = static_cast<uint64_t>(child_process.Handle()); + process_handle.value = static_cast<uint64_t>(process); #endif MojoInvitationTransportEndpoint transport_endpoint; @@ -321,26 +375,40 @@ base::Process InvitationTest::LaunchChildTestClient( else transport_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER; transport_endpoint.num_platform_handles = 1; - transport_endpoint.platform_handles = &endpoint_handle; + transport_endpoint.platform_handles = &handle; + + MojoSendInvitationOptions options; + options.struct_size = sizeof(options); + options.flags = flags; + if (flags & MOJO_SEND_INVITATION_FLAG_ISOLATED) { + options.isolated_connection_name = isolated_invitation_name.data(); + options.isolated_connection_name_length = + static_cast<uint32_t>(isolated_invitation_name.size()); + } CHECK_EQ(MOJO_RESULT_OK, MojoSendInvitation(invitation, &process_handle, &transport_endpoint, - error_handler, error_handler_context, nullptr)); - return child_process; + error_handler, error_handler_context, &options)); } class TestClientBase : public InvitationTest { public: - static MojoHandle AcceptInvitation() { + static MojoHandle AcceptInvitation(MojoAcceptInvitationFlags flags, + base::StringPiece switch_name = {}) { const auto& command_line = *base::CommandLine::ForCurrentProcess(); PlatformChannelEndpoint channel_endpoint = NamedPlatformChannel::ConnectToServer(command_line); if (!channel_endpoint.is_valid()) { - channel_endpoint = - PlatformChannel::RecoverPassedEndpointFromCommandLine(command_line); + if (switch_name.empty()) { + channel_endpoint = + PlatformChannel::RecoverPassedEndpointFromCommandLine(command_line); + } else { + channel_endpoint = PlatformChannel::RecoverPassedEndpointFromString( + command_line.GetSwitchValueASCII(switch_name)); + } } MojoPlatformHandle endpoint_handle; - PlatformHandleToMojoPlatformHandle(channel_endpoint.TakePlatformHandle(), - &endpoint_handle); + PlatformHandle::ToMojoPlatformHandle(channel_endpoint.TakePlatformHandle(), + &endpoint_handle); CHECK_NE(endpoint_handle.type, MOJO_PLATFORM_HANDLE_TYPE_INVALID); MojoInvitationTransportEndpoint transport_endpoint; @@ -349,9 +417,12 @@ class TestClientBase : public InvitationTest { transport_endpoint.num_platform_handles = 1; transport_endpoint.platform_handles = &endpoint_handle; + MojoAcceptInvitationOptions options; + options.struct_size = sizeof(options); + options.flags = flags; MojoHandle invitation; CHECK_EQ(MOJO_RESULT_OK, - MojoAcceptInvitation(&transport_endpoint, nullptr, &invitation)); + MojoAcceptInvitation(&transport_endpoint, &options, &invitation)); return invitation; } @@ -378,7 +449,8 @@ const std::string kTestMessage4 = "i shove the messages down the pipe"; TEST_F(InvitationTest, SendInvitation) { MojoHandle primordial_pipe; base::Process child_process = LaunchChildTestClient( - "SendInvitationClient", &primordial_pipe, 1, TransportType::kChannel); + "SendInvitationClient", &primordial_pipe, 1, TransportType::kChannel, + MOJO_SEND_INVITATION_FLAG_NONE); WriteMessage(primordial_pipe, kTestMessage1); EXPECT_EQ(MOJO_RESULT_OK, @@ -395,7 +467,7 @@ TEST_F(InvitationTest, SendInvitation) { DEFINE_TEST_CLIENT(SendInvitationClient) { MojoHandle primordial_pipe; - MojoHandle invitation = AcceptInvitation(); + MojoHandle invitation = AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_NONE); const uint32_t pipe_name = 0; ASSERT_EQ(MOJO_RESULT_OK, MojoExtractMessagePipeFromInvitation(invitation, &pipe_name, 4, @@ -413,7 +485,8 @@ DEFINE_TEST_CLIENT(SendInvitationClient) { TEST_F(InvitationTest, SendInvitationMultiplePipes) { MojoHandle pipes[2]; base::Process child_process = LaunchChildTestClient( - "SendInvitationMultiplePipesClient", pipes, 2, TransportType::kChannel); + "SendInvitationMultiplePipesClient", pipes, 2, TransportType::kChannel, + MOJO_SEND_INVITATION_FLAG_NONE); WriteMessage(pipes[0], kTestMessage1); WriteMessage(pipes[1], kTestMessage2); @@ -436,7 +509,7 @@ TEST_F(InvitationTest, SendInvitationMultiplePipes) { DEFINE_TEST_CLIENT(SendInvitationMultiplePipesClient) { MojoHandle pipes[2]; - MojoHandle invitation = AcceptInvitation(); + MojoHandle invitation = AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_NONE); const uint32_t pipe_names[] = {0, 1}; ASSERT_EQ(MOJO_RESULT_OK, MojoExtractMessagePipeFromInvitation(invitation, &pipe_names[0], 4, @@ -458,9 +531,9 @@ DEFINE_TEST_CLIENT(SendInvitationMultiplePipesClient) { #if !defined(OS_FUCHSIA) TEST_F(InvitationTest, SendInvitationWithServer) { MojoHandle primordial_pipe; - base::Process child_process = - LaunchChildTestClient("SendInvitationWithServerClient", &primordial_pipe, - 1, TransportType::kChannelServer); + base::Process child_process = LaunchChildTestClient( + "SendInvitationWithServerClient", &primordial_pipe, 1, + TransportType::kChannelServer, MOJO_SEND_INVITATION_FLAG_NONE); WriteMessage(primordial_pipe, kTestMessage1); EXPECT_EQ(MOJO_RESULT_OK, @@ -477,7 +550,7 @@ TEST_F(InvitationTest, SendInvitationWithServer) { DEFINE_TEST_CLIENT(SendInvitationWithServerClient) { MojoHandle primordial_pipe; - MojoHandle invitation = AcceptInvitation(); + MojoHandle invitation = AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_NONE); const uint32_t pipe_name = 0; ASSERT_EQ(MOJO_RESULT_OK, MojoExtractMessagePipeFromInvitation(invitation, &pipe_name, 4, @@ -554,7 +627,8 @@ TEST_F(InvitationTest, ProcessErrors) { MojoHandle pipe; base::Process child_process = LaunchChildTestClient( "ProcessErrorsClient", &pipe, 1, TransportType::kChannel, - &TestProcessErrorHandler, reinterpret_cast<uintptr_t>(&process_state)); + MOJO_SEND_INVITATION_FLAG_NONE, &TestProcessErrorHandler, + reinterpret_cast<uintptr_t>(&process_state)); MojoMessageHandle message; WaitForSignals(pipe, MOJO_HANDLE_SIGNAL_READABLE); @@ -590,7 +664,7 @@ TEST_F(InvitationTest, ProcessErrors) { DEFINE_TEST_CLIENT(ProcessErrorsClient) { MojoHandle pipe; - MojoHandle invitation = AcceptInvitation(); + MojoHandle invitation = AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_NONE); const uint32_t pipe_name = 0; ASSERT_EQ(MOJO_RESULT_OK, MojoExtractMessagePipeFromInvitation( invitation, &pipe_name, 4, nullptr, &pipe)); @@ -605,6 +679,196 @@ DEFINE_TEST_CLIENT(ProcessErrorsClient) { EXPECT_EQ(kDisconnectMessage, ReadMessage(pipe)); } +TEST_F(InvitationTest, SendIsolatedInvitation) { + MojoHandle primordial_pipe; + base::Process child_process = LaunchChildTestClient( + "SendIsolatedInvitationClient", &primordial_pipe, 1, + TransportType::kChannel, MOJO_SEND_INVITATION_FLAG_ISOLATED); + + WriteMessage(primordial_pipe, kTestMessage1); + EXPECT_EQ(MOJO_RESULT_OK, + WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE)); + EXPECT_EQ(kTestMessage3, ReadMessage(primordial_pipe)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe)); + + int wait_result = -1; + base::WaitForMultiprocessTestChildExit( + child_process, TestTimeouts::action_timeout(), &wait_result); + child_process.Close(); + EXPECT_EQ(0, wait_result); +} + +DEFINE_TEST_CLIENT(SendIsolatedInvitationClient) { + MojoHandle primordial_pipe; + MojoHandle invitation = + AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_ISOLATED); + const uint32_t pipe_name = 0; + ASSERT_EQ(MOJO_RESULT_OK, + MojoExtractMessagePipeFromInvitation(invitation, &pipe_name, 4, + nullptr, &primordial_pipe)); + ASSERT_EQ(MOJO_RESULT_OK, MojoClose(invitation)); + + WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE); + ASSERT_EQ(kTestMessage1, ReadMessage(primordial_pipe)); + WriteMessage(primordial_pipe, kTestMessage3); + WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED); + + ASSERT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe)); +} + +TEST_F(InvitationTest, SendMultipleIsolatedInvitations) { + // We send a secondary transport to the client process so we can send a second + // isolated invitation. + base::CommandLine command_line = + base::GetMultiProcessTestChildBaseCommandLine(); + PlatformChannel secondary_transport; + base::LaunchOptions options; + PrepareToPassRemoteEndpoint(&secondary_transport, &options, &command_line, + kSecondaryChannelHandleSwitch); + + MojoHandle primordial_pipe; + base::Process child_process = LaunchChildTestClient( + "SendMultipleIsolatedInvitationsClient", &primordial_pipe, 1, + TransportType::kChannel, MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, + &command_line, &options); + secondary_transport.RemoteProcessLaunchAttempted(); + + WriteMessage(primordial_pipe, kTestMessage1); + EXPECT_EQ(MOJO_RESULT_OK, + WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE)); + EXPECT_EQ(kTestMessage3, ReadMessage(primordial_pipe)); + + // Send another invitation over our seconary pipe. This should trample the + // original connection, breaking the first pipe. + MojoHandle new_pipe; + SendInvitationToClient( + secondary_transport.TakeLocalEndpoint().TakePlatformHandle(), + child_process.Handle(), &new_pipe, 1, TransportType::kChannel, + MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, ""); + WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe)); + + // And the new pipe should be working. + WriteMessage(new_pipe, kTestMessage1); + EXPECT_EQ(MOJO_RESULT_OK, + WaitForSignals(new_pipe, MOJO_HANDLE_SIGNAL_READABLE)); + EXPECT_EQ(kTestMessage3, ReadMessage(new_pipe)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(new_pipe)); + + int wait_result = -1; + base::WaitForMultiprocessTestChildExit( + child_process, TestTimeouts::action_timeout(), &wait_result); + child_process.Close(); + EXPECT_EQ(0, wait_result); +} + +DEFINE_TEST_CLIENT(SendMultipleIsolatedInvitationsClient) { + MojoHandle primordial_pipe; + MojoHandle invitation = + AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_ISOLATED); + const uint32_t pipe_name = 0; + ASSERT_EQ(MOJO_RESULT_OK, + MojoExtractMessagePipeFromInvitation(invitation, &pipe_name, 4, + nullptr, &primordial_pipe)); + ASSERT_EQ(MOJO_RESULT_OK, MojoClose(invitation)); + + WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE); + ASSERT_EQ(kTestMessage1, ReadMessage(primordial_pipe)); + WriteMessage(primordial_pipe, kTestMessage3); + + // The above pipe should get closed once we accept a new invitation. + invitation = AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_ISOLATED, + kSecondaryChannelHandleSwitch); + WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED); + primordial_pipe = MOJO_HANDLE_INVALID; + ASSERT_EQ(MOJO_RESULT_OK, + MojoExtractMessagePipeFromInvitation(invitation, &pipe_name, 4, + nullptr, &primordial_pipe)); + ASSERT_EQ(MOJO_RESULT_OK, MojoClose(invitation)); + WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE); + ASSERT_EQ(kTestMessage1, ReadMessage(primordial_pipe)); + WriteMessage(primordial_pipe, kTestMessage3); + WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED); + + ASSERT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe)); +} + +TEST_F(InvitationTest, SendIsolatedInvitationWithDuplicateName) { + PlatformChannel channel1; + PlatformChannel channel2; + MojoHandle pipe0, pipe1; + const char kConnectionName[] = "there can be only one!"; + SendInvitationToClient( + channel1.TakeLocalEndpoint().TakePlatformHandle(), + base::kNullProcessHandle, &pipe0, 1, TransportType::kChannel, + MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, kConnectionName); + + // Send another invitation with the same connection name. |pipe0| should be + // disconnected as the first invitation's connection is torn down. + SendInvitationToClient( + channel2.TakeLocalEndpoint().TakePlatformHandle(), + base::kNullProcessHandle, &pipe1, 1, TransportType::kChannel, + MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, kConnectionName); + + WaitForSignals(pipe0, MOJO_HANDLE_SIGNAL_PEER_CLOSED); +} + +TEST_F(InvitationTest, SendIsolatedInvitationToSelf) { + PlatformChannel channel; + MojoHandle pipe0, pipe1; + SendInvitationToClient(channel.TakeLocalEndpoint().TakePlatformHandle(), + base::kNullProcessHandle, &pipe0, 1, + TransportType::kChannel, + MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, ""); + SendInvitationToClient(channel.TakeRemoteEndpoint().TakePlatformHandle(), + base::kNullProcessHandle, &pipe1, 1, + TransportType::kChannel, + MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, ""); + + WriteMessage(pipe0, kTestMessage1); + EXPECT_EQ(kTestMessage1, ReadMessage(pipe1)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(pipe0)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(pipe1)); +} + +TEST_F(InvitationTest, BrokenInvitationTransportBreaksAttachedPipe) { + MojoHandle primordial_pipe; + base::Process child_process = LaunchChildTestClient( + "BrokenTransportClient", &primordial_pipe, 1, TransportType::kChannel, + MOJO_SEND_INVITATION_FLAG_NONE); + + EXPECT_EQ(MOJO_RESULT_OK, + WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe)); + + int wait_result = -1; + base::WaitForMultiprocessTestChildExit( + child_process, TestTimeouts::action_timeout(), &wait_result); + child_process.Close(); + EXPECT_EQ(0, wait_result); +} + +TEST_F(InvitationTest, BrokenIsolatedInvitationTransportBreaksAttachedPipe) { + MojoHandle primordial_pipe; + base::Process child_process = LaunchChildTestClient( + "BrokenTransportClient", &primordial_pipe, 1, TransportType::kChannel, + MOJO_SEND_INVITATION_FLAG_ISOLATED); + + EXPECT_EQ(MOJO_RESULT_OK, + WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe)); + + int wait_result = -1; + base::WaitForMultiprocessTestChildExit( + child_process, TestTimeouts::action_timeout(), &wait_result); + child_process.Close(); + EXPECT_EQ(0, wait_result); +} + +DEFINE_TEST_CLIENT(BrokenTransportClient) { + // No-op. Exit immediately without accepting any invitation. +} + } // namespace -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/mach_port_relay.cc b/chromium/mojo/core/mach_port_relay.cc index b3380a86d29..7d7a42510e4 100644 --- a/chromium/mojo/edk/system/mach_port_relay.cc +++ b/chromium/mojo/core/mach_port_relay.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/mach_port_relay.h" #include <mach/mach.h> @@ -13,10 +13,9 @@ #include "base/mac/scoped_mach_port.h" #include "base/metrics/histogram_macros.h" #include "base/process/process.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" namespace mojo { -namespace edk { +namespace core { namespace { @@ -65,37 +64,24 @@ void ReportChildError(ChildUMAError error) { } // namespace // static -void MachPortRelay::ReceivePorts( - std::vector<ScopedInternalPlatformHandle>* handles) { - DCHECK(handles); - - for (auto& handle : *handles) { - DCHECK(handle.get().type != InternalPlatformHandle::Type::MACH); - if (handle.get().type != InternalPlatformHandle::Type::MACH_NAME) - continue; - - handle.get().type = InternalPlatformHandle::Type::MACH; - - // MACH_PORT_NULL doesn't need translation. - if (handle.get().port == MACH_PORT_NULL) - continue; - - // TODO(wez): Wrapping handle.get().port in this way causes it to be - // Free()d via mach_port_mod_refs() - should InternalPlatformHandle also do - // that if the handle never reaches here, or should this code not be - // wrapping it? - base::mac::ScopedMachReceiveRight message_port(handle.get().port); - base::mac::ScopedMachSendRight received_port( - base::ReceiveMachPort(message_port.get())); - handle.get().port = received_port.release(); - if (!handle.is_valid()) { - ReportChildError(ChildUMAError::ERROR_RECEIVE_MACH_MESSAGE); - DLOG(ERROR) << "Error receiving mach port"; - continue; - } - - ReportChildError(ChildUMAError::SUCCESS); +base::mac::ScopedMachSendRight MachPortRelay::ReceiveSendRight( + base::mac::ScopedMachReceiveRight port) { + // MACH_PORT_NULL doesn't need translation. + if (!port.is_valid()) + return base::mac::ScopedMachSendRight(); + + // Take ownership of the receive right. We only need it to read this single + // send right, then it can be closed. + base::mac::ScopedMachSendRight received_port( + base::ReceiveMachPort(port.get())); + if (!received_port.is_valid()) { + ReportChildError(ChildUMAError::ERROR_RECEIVE_MACH_MESSAGE); + DLOG(ERROR) << "Error receiving mach port"; + return base::mac::ScopedMachSendRight(); } + + ReportChildError(ChildUMAError::SUCCESS); + return received_port; } MachPortRelay::MachPortRelay(base::PortProvider* port_provider) @@ -113,40 +99,29 @@ void MachPortRelay::SendPortsToProcess(Channel::Message* message, DCHECK(message); mach_port_t task_port = port_provider_->TaskForPid(process); - std::vector<ScopedInternalPlatformHandle> handles = message->TakeHandles(); + std::vector<PlatformHandleInTransit> handles = message->TakeHandles(); // Message should have handles, otherwise there's no point in calling this // function. DCHECK(!handles.empty()); for (auto& handle : handles) { - DCHECK(handle.get().type != InternalPlatformHandle::Type::MACH_NAME); - if (handle.get().type != InternalPlatformHandle::Type::MACH) + if (!handle.handle().is_valid_mach_port()) continue; - if (!handle.is_valid()) { - handle.get().type = InternalPlatformHandle::Type::MACH_NAME; - continue; - } - 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); - - // For MACH_PORT_NULL, use Type::MACH to indicate that no extraction is - // necessary. - // TODO(wez): But we're not setting Type::Mach... is the comment above - // out of date? - handle.get().port = MACH_PORT_NULL; + handle = PlatformHandleInTransit( + PlatformHandle(base::mac::ScopedMachSendRight())); continue; } mach_port_name_t intermediate_port; base::MachCreateError error_code; intermediate_port = base::CreateIntermediateMachPort( - task_port, base::mac::ScopedMachSendRight(handle.get().port), - &error_code); + task_port, handle.TakeHandle().TakeMachPort(), &error_code); if (intermediate_port == MACH_PORT_NULL) { BrokerUMAError uma_error; switch (error_code) { @@ -164,48 +139,44 @@ void MachPortRelay::SendPortsToProcess(Channel::Message* message, break; } ReportBrokerError(uma_error); - handle.get().port = MACH_PORT_NULL; + handle = PlatformHandleInTransit( + PlatformHandle(base::mac::ScopedMachSendRight())); continue; } + handle = PlatformHandleInTransit::CreateForMachPortName(intermediate_port); ReportBrokerError(BrokerUMAError::SUCCESS); - handle.get().port = intermediate_port; - handle.get().type = InternalPlatformHandle::Type::MACH_NAME; } message->SetHandles(std::move(handles)); } -void MachPortRelay::ExtractPort(ScopedInternalPlatformHandle* handle, - base::ProcessHandle process) { - DCHECK_EQ(handle->get().type, InternalPlatformHandle::Type::MACH_NAME); - handle->get().type = InternalPlatformHandle::Type::MACH; - +base::mac::ScopedMachSendRight MachPortRelay::ExtractPort( + mach_port_t port_name, + base::ProcessHandle process) { // No extraction necessary for MACH_PORT_NULL. - if (!handle->is_valid()) - return; + if (port_name == MACH_PORT_NULL) + return base::mac::ScopedMachSendRight(); mach_port_t task_port = port_provider_->TaskForPid(process); if (task_port == MACH_PORT_NULL) { ReportBrokerError(BrokerUMAError::ERROR_TASK_FOR_PID); - handle->get().port = MACH_PORT_NULL; - return; + return base::mac::ScopedMachSendRight(); } 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->get().port, MACH_MSG_TYPE_MOVE_SEND, &extracted_right, - &extracted_right_type); + kern_return_t kr = + mach_port_extract_right(task_port, port_name, MACH_MSG_TYPE_MOVE_SEND, + &extracted_right, &extracted_right_type); if (kr != KERN_SUCCESS) { ReportBrokerError(BrokerUMAError::ERROR_EXTRACT_SOURCE_RIGHT); - handle->get().port = MACH_PORT_NULL; - return; + return base::mac::ScopedMachSendRight(); } ReportBrokerError(BrokerUMAError::SUCCESS); DCHECK_EQ(static_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND), extracted_right_type); - handle->get().port = extracted_right; + return base::mac::ScopedMachSendRight(extracted_right); } void MachPortRelay::AddObserver(Observer* observer) { @@ -225,5 +196,5 @@ void MachPortRelay::OnReceivedTaskPort(base::ProcessHandle process) { observer->OnProcessReady(process); } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/mach_port_relay.h b/chromium/mojo/core/mach_port_relay.h index b8d232f20c3..4cb6de4bd64 100644 --- a/chromium/mojo/edk/system/mach_port_relay.h +++ b/chromium/mojo/core/mach_port_relay.h @@ -2,8 +2,8 @@ // 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_ +#ifndef MOJO_CORE_MACH_PORT_RELAY_H_ +#define MOJO_CORE_MACH_PORT_RELAY_H_ #include <set> #include <vector> @@ -11,11 +11,10 @@ #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" +#include "mojo/core/channel.h" namespace mojo { -namespace edk { +namespace core { // 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 @@ -34,17 +33,17 @@ class MachPortRelay : public base::PortProvider::Observer { }; // 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 + // process. The Mach port in |port| 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. - // On failure, the Mach port is replaced with MACH_PORT_NULL. + // port and gives ownership of the final Mach port to the caller. + // + // On failure, returns a null send right. // // See SendPortsToProcess() for the definition of intermediate and final Mach // ports. - static void ReceivePorts(std::vector<ScopedInternalPlatformHandle>* handles); + static base::mac::ScopedMachSendRight ReceiveSendRight( + base::mac::ScopedMachReceiveRight port); explicit MachPortRelay(base::PortProvider* port_provider); ~MachPortRelay() override; @@ -55,16 +54,17 @@ class MachPortRelay : public base::PortProvider::Observer { // 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. - // Ports that cannot be brokered are replaced with MACH_PORT_NULL. + // + // All ports in |message|'s set of handles are reset by this call, and all + // port names in the message's header are replaced with the new receive right + // ports. void SendPortsToProcess(Channel::Message* message, base::ProcessHandle process); - // Given a InternalPlatformHandle of Type::MACH_NAME, extracts the Mach port, - // and updates the contents of the InternalPlatformHandle to have Type::MACH - // and have the actual Mach port. On failure, replaces the contents with - // Type::MACH and MACH_PORT_NULL. - void ExtractPort(ScopedInternalPlatformHandle* handle, - base::ProcessHandle process); + // Given the name of a Mach send right within |process|, extracts an owned + // send right ref and returns it. May return a null port on failure. + base::mac::ScopedMachSendRight ExtractPort(mach_port_t port_name, + base::ProcessHandle process); // Observer interface. void AddObserver(Observer* observer); @@ -84,7 +84,7 @@ class MachPortRelay : public base::PortProvider::Observer { DISALLOW_COPY_AND_ASSIGN(MachPortRelay); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_MACH_PORT_RELAY_H_ +#endif // MOJO_CORE_MACH_PORT_RELAY_H_ diff --git a/chromium/mojo/edk/system/message_pipe_dispatcher.cc b/chromium/mojo/core/message_pipe_dispatcher.cc index 219ad4ccab8..00fc12e2aa0 100644 --- a/chromium/mojo/edk/system/message_pipe_dispatcher.cc +++ b/chromium/mojo/core/message_pipe_dispatcher.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/message_pipe_dispatcher.h" #include <limits> #include <memory> @@ -10,15 +10,15 @@ #include "base/logging.h" #include "base/macros.h" #include "base/memory/ref_counted.h" -#include "mojo/edk/system/core.h" -#include "mojo/edk/system/node_controller.h" -#include "mojo/edk/system/ports/event.h" -#include "mojo/edk/system/ports/message_filter.h" -#include "mojo/edk/system/request_context.h" -#include "mojo/edk/system/user_message_impl.h" +#include "mojo/core/core.h" +#include "mojo/core/node_controller.h" +#include "mojo/core/ports/event.h" +#include "mojo/core/ports/message_filter.h" +#include "mojo/core/request_context.h" +#include "mojo/core/user_message_impl.h" namespace mojo { -namespace edk { +namespace core { namespace { @@ -193,6 +193,56 @@ MojoResult MessagePipeDispatcher::ReadMessage( return MOJO_RESULT_OK; } +MojoResult MessagePipeDispatcher::SetQuota(MojoQuotaType type, uint64_t limit) { + switch (type) { + case MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH: + if (limit == MOJO_QUOTA_LIMIT_NONE) + receive_queue_length_limit_.reset(); + else + receive_queue_length_limit_ = limit; + break; + + case MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE: + if (limit == MOJO_QUOTA_LIMIT_NONE) + receive_queue_memory_size_limit_.reset(); + else + receive_queue_memory_size_limit_ = limit; + break; + + default: + return MOJO_RESULT_INVALID_ARGUMENT; + } + + return MOJO_RESULT_OK; +} + +MojoResult MessagePipeDispatcher::QueryQuota(MojoQuotaType type, + uint64_t* limit, + uint64_t* usage) { + ports::PortStatus port_status; + if (node_controller_->node()->GetStatus(port_, &port_status) != ports::OK) { + CHECK(in_transit_ || port_transferred_ || port_closed_); + return MOJO_RESULT_INVALID_ARGUMENT; + } + + switch (type) { + case MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH: + *limit = receive_queue_length_limit_.value_or(MOJO_QUOTA_LIMIT_NONE); + *usage = port_status.queued_message_count; + break; + + case MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE: + *limit = receive_queue_memory_size_limit_.value_or(MOJO_QUOTA_LIMIT_NONE); + *usage = port_status.queued_num_bytes; + break; + + default: + return MOJO_RESULT_INVALID_ARGUMENT; + } + + return MOJO_RESULT_OK; +} + HandleSignalsState MessagePipeDispatcher::GetHandleSignalsState() const { base::AutoLock lock(signal_lock_); return GetHandleSignalsStateNoLock(); @@ -223,10 +273,9 @@ void MessagePipeDispatcher::StartSerialize(uint32_t* num_bytes, *num_handles = 0; } -bool MessagePipeDispatcher::EndSerialize( - void* destination, - ports::PortName* ports, - ScopedInternalPlatformHandle* handles) { +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_); @@ -266,7 +315,7 @@ scoped_refptr<Dispatcher> MessagePipeDispatcher::Deserialize( size_t num_bytes, const ports::PortName* ports, size_t num_ports, - ScopedInternalPlatformHandle* handles, + PlatformHandle* handles, size_t num_handles) { if (num_ports != 1 || num_handles || num_bytes != sizeof(SerializedState)) return nullptr; @@ -329,7 +378,15 @@ HandleSignalsState MessagePipeDispatcher::GetHandleSignalsStateNoLock() const { } else { rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED; } - rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED; + if (receive_queue_length_limit_ && + port_status.queued_message_count > *receive_queue_length_limit_) { + rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED; + } else if (receive_queue_memory_size_limit_ && + port_status.queued_num_bytes > *receive_queue_memory_size_limit_) { + rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED; + } + rv.satisfiable_signals |= + MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED; return rv; } @@ -365,5 +422,5 @@ void MessagePipeDispatcher::OnPortStatusChanged() { watchers_.NotifyState(GetHandleSignalsStateNoLock()); } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/message_pipe_dispatcher.h b/chromium/mojo/core/message_pipe_dispatcher.h index 64ad3d70a05..4fef7080997 100644 --- a/chromium/mojo/edk/system/message_pipe_dispatcher.h +++ b/chromium/mojo/core/message_pipe_dispatcher.h @@ -2,8 +2,8 @@ // 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_ +#ifndef MOJO_CORE_MESSAGE_PIPE_DISPATCHER_H_ +#define MOJO_CORE_MESSAGE_PIPE_DISPATCHER_H_ #include <stdint.h> @@ -11,13 +11,14 @@ #include <queue> #include "base/macros.h" -#include "mojo/edk/system/atomic_flag.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/ports/port_ref.h" -#include "mojo/edk/system/watcher_set.h" +#include "base/optional.h" +#include "mojo/core/atomic_flag.h" +#include "mojo/core/dispatcher.h" +#include "mojo/core/ports/port_ref.h" +#include "mojo/core/watcher_set.h" namespace mojo { -namespace edk { +namespace core { class NodeController; @@ -51,6 +52,10 @@ class MessagePipeDispatcher : public Dispatcher { std::unique_ptr<ports::UserMessageEvent> message) override; MojoResult ReadMessage( std::unique_ptr<ports::UserMessageEvent>* message) override; + MojoResult SetQuota(MojoQuotaType type, uint64_t limit) override; + MojoResult QueryQuota(MojoQuotaType type, + uint64_t* limit, + uint64_t* usage) override; HandleSignalsState GetHandleSignalsState() const override; MojoResult AddWatcherRef(const scoped_refptr<WatcherDispatcher>& watcher, uintptr_t context) override; @@ -61,18 +66,17 @@ class MessagePipeDispatcher : public Dispatcher { uint32_t* num_handles) override; bool EndSerialize(void* destination, ports::PortName* ports, - ScopedInternalPlatformHandle* handles) override; + 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, - ScopedInternalPlatformHandle* handles, - size_t num_handles); + 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; @@ -100,11 +104,13 @@ class MessagePipeDispatcher : public Dispatcher { bool port_transferred_ = false; AtomicFlag port_closed_; WatcherSet watchers_; + base::Optional<uint64_t> receive_queue_length_limit_; + base::Optional<uint64_t> receive_queue_memory_size_limit_; DISALLOW_COPY_AND_ASSIGN(MessagePipeDispatcher); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_MESSAGE_PIPE_DISPATCHER_H_ +#endif // MOJO_CORE_MESSAGE_PIPE_DISPATCHER_H_ diff --git a/chromium/mojo/edk/system/message_pipe_perftest.cc b/chromium/mojo/core/message_pipe_perftest.cc index 70c00920f44..93c833a559b 100644 --- a/chromium/mojo/edk/system/message_pipe_perftest.cc +++ b/chromium/mojo/core/message_pipe_perftest.cc @@ -15,18 +15,17 @@ #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/core/embedder/embedder.h" +#include "mojo/core/handle_signals_state.h" +#include "mojo/core/test/mojo_test_base.h" +#include "mojo/core/test/test_utils.h" +#include "mojo/core/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 core { namespace { class MessagePipePerfTest : public test::MojoTestBase { @@ -158,5 +157,5 @@ TEST_F(MessagePipePerfTest, MultiprocessPingPong) { } } // namespace -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/message_pipe_unittest.cc b/chromium/mojo/core/message_pipe_unittest.cc index 2cdc58edd3a..20c71ff4549 100644 --- a/chromium/mojo/edk/system/message_pipe_unittest.cc +++ b/chromium/mojo/core/message_pipe_unittest.cc @@ -12,19 +12,21 @@ #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" -#include "mojo/edk/system/test_utils.h" -#include "mojo/edk/test/mojo_test_base.h" +#include "build/build_config.h" +#include "mojo/core/test/mojo_test_base.h" +#include "mojo/core/test_utils.h" #include "mojo/public/c/system/core.h" #include "mojo/public/c/system/types.h" #include "mojo/public/cpp/system/message_pipe.h" namespace mojo { -namespace edk { +namespace core { namespace { const MojoHandleSignals kAllSignals = MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_PEER_REMOTE; + MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_PEER_REMOTE | + MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED; static const char kHelloWorld[] = "hello world"; @@ -278,29 +280,23 @@ TEST_F(MessagePipeTest, BasicWaiting) { 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); + ASSERT_TRUE(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_PEER_CLOSED); + ASSERT_TRUE(hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_PEER_CLOSED); - // Port 1 should not be writable. + // Port 1 should not be writable now or ever again. 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); + ASSERT_FALSE(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); + ASSERT_FALSE(hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_WRITABLE); // 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); + ASSERT_TRUE(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE); + ASSERT_TRUE(hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE); // Read from port 1. buffer[0] = 0; @@ -313,7 +309,8 @@ TEST_F(MessagePipeTest, BasicWaiting) { 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); + ASSERT_FALSE(hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE); + ASSERT_FALSE(hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_WRITABLE); } #if !defined(OS_IOS) @@ -555,5 +552,5 @@ TEST_F(MessagePipeTest, ClosePipesStressTest) { } } // namespace -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/message_unittest.cc b/chromium/mojo/core/message_unittest.cc index b493bad3166..ce44f221441 100644 --- a/chromium/mojo/edk/system/message_unittest.cc +++ b/chromium/mojo/core/message_unittest.cc @@ -11,16 +11,15 @@ #include "base/numerics/safe_math.h" #include "base/rand_util.h" #include "build/build_config.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/embedder/platform_channel_pair.h" -#include "mojo/edk/system/user_message_impl.h" -#include "mojo/edk/test/mojo_test_base.h" +#include "mojo/core/test/mojo_test_base.h" +#include "mojo/core/user_message_impl.h" +#include "mojo/public/cpp/platform/platform_channel.h" #include "mojo/public/cpp/system/buffer.h" #include "mojo/public/cpp/system/message_pipe.h" #include "mojo/public/cpp/system/platform_handle.h" namespace mojo { -namespace edk { +namespace core { namespace { using MessageTest = test::MojoTestBase; @@ -815,11 +814,15 @@ TEST_F(MessageTest, ExtendPayloadWithHandlesAttached) { MojoHandle handles[5]; CreateMessagePipe(&handles[0], &handles[1]); - PlatformChannelPair channel; - ASSERT_EQ(MOJO_RESULT_OK, CreateInternalPlatformHandleWrapper( - channel.PassServerHandle(), &handles[2])); - ASSERT_EQ(MOJO_RESULT_OK, CreateInternalPlatformHandleWrapper( - channel.PassClientHandle(), &handles[3])); + PlatformChannel channel; + handles[2] = + WrapPlatformHandle(channel.TakeLocalEndpoint().TakePlatformHandle()) + .release() + .value(); + handles[3] = + WrapPlatformHandle(channel.TakeRemoteEndpoint().TakePlatformHandle()) + .release() + .value(); handles[4] = SharedBufferHandle::Create(64).release().value(); MojoMessageHandle message; @@ -864,11 +867,15 @@ DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReadAndIgnoreMessage, MessageTest, h) { TEST_F(MessageTest, ExtendPayloadWithHandlesAttachedViaExtension) { MojoHandle handles[5]; CreateMessagePipe(&handles[0], &handles[4]); - PlatformChannelPair channel; - ASSERT_EQ(MOJO_RESULT_OK, CreateInternalPlatformHandleWrapper( - channel.PassServerHandle(), &handles[1])); - ASSERT_EQ(MOJO_RESULT_OK, CreateInternalPlatformHandleWrapper( - channel.PassClientHandle(), &handles[2])); + PlatformChannel channel; + handles[1] = + WrapPlatformHandle(channel.TakeLocalEndpoint().TakePlatformHandle()) + .release() + .value(); + handles[2] = + WrapPlatformHandle(channel.TakeRemoteEndpoint().TakePlatformHandle()) + .release() + .value(); handles[3] = SharedBufferHandle::Create(64).release().value(); MojoMessageHandle message; @@ -945,5 +952,5 @@ TEST_F(MessageTest, PartiallySerializedMessagesDontLeakHandles) { } } // namespace -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/mojo_core.cc b/chromium/mojo/core/mojo_core.cc index b0c652750bf..9823b201709 100644 --- a/chromium/mojo/edk/mojo_core.cc +++ b/chromium/mojo/core/mojo_core.cc @@ -3,8 +3,8 @@ // found in the LICENSE file. #include "base/time/time.h" -#include "mojo/edk/embedder/entrypoints.h" -#include "mojo/edk/system/core.h" +#include "mojo/core/core.h" +#include "mojo/core/entrypoints.h" #include "mojo/public/c/system/core.h" #include "mojo/public/c/system/thunks.h" @@ -13,7 +13,7 @@ extern "C" { namespace { MojoResult InitializeImpl(const struct MojoInitializeOptions* options) { - mojo::edk::InitializeCore(); + mojo::core::InitializeCore(); return MOJO_RESULT_OK; } @@ -29,7 +29,7 @@ MojoSystemThunks g_thunks = {0}; EXPORT_FROM_MOJO_CORE void MojoGetSystemThunks(MojoSystemThunks* thunks) { if (!g_thunks.size) { - g_thunks = mojo::edk::GetSystemThunks(); + g_thunks = mojo::core::GetSystemThunks(); g_thunks.Initialize = InitializeImpl; } diff --git a/chromium/mojo/core/mojo_core.def b/chromium/mojo/core/mojo_core.def new file mode 100644 index 00000000000..59c73515990 --- /dev/null +++ b/chromium/mojo/core/mojo_core.def @@ -0,0 +1,10 @@ +; Copyright 2018 The Chromium Authors. All rights reserved. +; Use of this source code is governed by a BSD-style license that can be +; found in the LICENSE file. + +; Ensure that we export only MojoGetSystemThunks from the mojo_core library. + +LIBRARY "mojo_core.dll" + +EXPORTS + MojoGetSystemThunks diff --git a/chromium/mojo/edk/mojo_core_unittest.cc b/chromium/mojo/core/mojo_core_unittest.cc index 8428e09859b..8428e09859b 100644 --- a/chromium/mojo/edk/mojo_core_unittest.cc +++ b/chromium/mojo/core/mojo_core_unittest.cc diff --git a/chromium/mojo/edk/system/multiprocess_message_pipe_unittest.cc b/chromium/mojo/core/multiprocess_message_pipe_unittest.cc index 946a21a53b2..b69af363535 100644 --- a/chromium/mojo/edk/system/multiprocess_message_pipe_unittest.cc +++ b/chromium/mojo/core/multiprocess_message_pipe_unittest.cc @@ -22,22 +22,21 @@ #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/core/handle_signals_state.h" +#include "mojo/core/test/mojo_test_base.h" +#include "mojo/core/test/test_utils.h" +#include "mojo/core/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/message_pipe.h" +#include "mojo/public/cpp/system/platform_handle.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 core { namespace { // Temporary helpers to avoid tons of churn as old APIs are removed. These @@ -249,8 +248,8 @@ TEST_P(MultiprocessMessagePipeTestWithPeerSupport, QueueMessages) { 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); + ASSERT_FALSE(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE); + ASSERT_FALSE(hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE); }); EXPECT_EQ(static_cast<int>(kNumMessages % 100), exit_code); } @@ -266,9 +265,6 @@ DEFINE_TEST_CLIENT_WITH_PIPE(CheckSharedBuffer, // pipe before we do. CHECK(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE); CHECK(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); - CHECK_EQ(hss.satisfiable_signals, - MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_PEER_REMOTE); // It should have a shared buffer. std::string read_buffer(100, '\0'); @@ -305,9 +301,6 @@ DEFINE_TEST_CLIENT_WITH_PIPE(CheckSharedBuffer, MOJO_RESULT_OK); CHECK(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE); CHECK(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); - CHECK_EQ(hss.satisfiable_signals, - MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_PEER_REMOTE); read_buffer = std::string(100, '\0'); num_bytes = static_cast<uint32_t>(read_buffer.size()); @@ -397,12 +390,12 @@ TEST_F(MultiprocessMessagePipeTest, SharedBufferPassing) { 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); + ASSERT_FALSE(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE); + ASSERT_FALSE(hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE); }); } -DEFINE_TEST_CLIENT_WITH_PIPE(CheckInternalPlatformHandleFile, +DEFINE_TEST_CLIENT_WITH_PIPE(CheckPlatformHandleFile, MultiprocessMessagePipeTest, h) { HandleSignalsState hss; @@ -410,9 +403,6 @@ DEFINE_TEST_CLIENT_WITH_PIPE(CheckInternalPlatformHandleFile, MOJO_RESULT_OK); CHECK(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE); CHECK(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); - CHECK_EQ(hss.satisfiable_signals, - MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_PEER_REMOTE); std::string read_buffer(100, '\0'); uint32_t num_bytes = static_cast<uint32_t>(read_buffer.size()); @@ -431,13 +421,10 @@ DEFINE_TEST_CLIENT_WITH_PIPE(CheckInternalPlatformHandleFile, CHECK_GT(num_handles, 0); for (int i = 0; i < num_handles; ++i) { - ScopedInternalPlatformHandle h; - CHECK_EQ(PassWrappedInternalPlatformHandle(handles[i], &h), MOJO_RESULT_OK); + PlatformHandle h = UnwrapPlatformHandle(ScopedHandle(Handle(handles[i]))); CHECK(h.is_valid()); - MojoClose(handles[i]); - base::ScopedFILE fp( - test::FILEFromInternalPlatformHandle(std::move(h), "r")); + base::ScopedFILE fp = test::FILEFromPlatformHandle(std::move(h), "r"); CHECK(fp); std::string fread_buffer(100, '\0'); size_t bytes_read = @@ -453,12 +440,11 @@ class MultiprocessMessagePipeTestWithPipeCount : public MultiprocessMessagePipeTest, public testing::WithParamInterface<size_t> {}; -TEST_P(MultiprocessMessagePipeTestWithPipeCount, - InternalPlatformHandlePassing) { +TEST_P(MultiprocessMessagePipeTestWithPipeCount, PlatformHandlePassing) { base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - RunTestClient("CheckInternalPlatformHandleFile", [&](MojoHandle h) { + RunTestClient("CheckPlatformHandleFile", [&](MojoHandle h) { std::vector<MojoHandle> handles; size_t pipe_count = GetParam(); @@ -470,13 +456,10 @@ TEST_P(MultiprocessMessagePipeTestWithPipeCount, CHECK_EQ(fwrite(&world[0], 1, world.size(), fp.get()), world.size()); fflush(fp.get()); rewind(fp.get()); - MojoHandle handle; - ASSERT_EQ(CreateInternalPlatformHandleWrapper( - ScopedInternalPlatformHandle( - test::InternalPlatformHandleFromFILE(std::move(fp))), - &handle), - MOJO_RESULT_OK); - handles.push_back(handle); + ScopedHandle handle = + WrapPlatformHandle(test::PlatformHandleFromFILE(std::move(fp))); + ASSERT_TRUE(handle.is_valid()); + handles.push_back(handle.release().value()); } char message[128]; @@ -492,8 +475,8 @@ TEST_P(MultiprocessMessagePipeTestWithPipeCount, 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); + ASSERT_FALSE(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE); + ASSERT_FALSE(hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE); }); } @@ -517,9 +500,6 @@ DEFINE_TEST_CLIENT_WITH_PIPE(CheckMessagePipe, MultiprocessMessagePipeTest, h) { // pipe before we do. CHECK(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE); CHECK(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); - CHECK_EQ(hss.satisfiable_signals, - MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_PEER_REMOTE); // It should have a message pipe. MojoHandle handles[10]; @@ -534,9 +514,6 @@ DEFINE_TEST_CLIENT_WITH_PIPE(CheckMessagePipe, MultiprocessMessagePipeTest, h) { MOJO_RESULT_OK); CHECK(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE); CHECK(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); - CHECK_EQ(hss.satisfiable_signals, - MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_PEER_REMOTE); std::string read_buffer(100, '\0'); uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer.size()); @@ -637,7 +614,8 @@ DEFINE_TEST_CLIENT_WITH_PIPE(DataPipeConsumer, MultiprocessMessagePipeTest, h) { CHECK(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); CHECK_EQ(hss.satisfiable_signals, MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_PEER_REMOTE); + MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_PEER_REMOTE | + MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); // It should have a message pipe. MojoHandle handles[10]; @@ -654,7 +632,8 @@ DEFINE_TEST_CLIENT_WITH_PIPE(DataPipeConsumer, MultiprocessMessagePipeTest, h) { CHECK(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); CHECK_EQ(hss.satisfiable_signals, MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | - MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_PEER_REMOTE); + MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_PEER_REMOTE | + MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); std::string read_buffer(100, '\0'); uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer.size()); @@ -1348,58 +1327,6 @@ DEFINE_TEST_CLIENT_TEST_WITH_PIPE(BadMessageClient, 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)); - RunTestClient("BadMessageClient", [&](MojoHandle child1) { - set_process_error_callback( - base::Bind(&OnProcessError, &second_process_error)); - RunTestClient("BadMessageClient", [&](MojoHandle 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)); - MojoMessageHandle message; - ASSERT_EQ(MOJO_RESULT_OK, ::MojoReadMessage(a, nullptr, &message)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoNotifyBadMessage( - message, kFirstErrorMessage.data(), - static_cast<uint32_t>(kFirstErrorMessage.size()), nullptr)); - EXPECT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(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, ::MojoReadMessage(c, nullptr, &message)); - EXPECT_EQ( - MOJO_RESULT_OK, - MojoNotifyBadMessage( - message, kSecondErrorMessage.data(), - static_cast<uint32_t>(kSecondErrorMessage.size()), nullptr)); - EXPECT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(message)); - - WriteMessage(child2, "bye"); - }); - - WriteMessage(child1, "bye"); - }); - - // 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, @@ -1408,5 +1335,5 @@ INSTANTIATE_TEST_CASE_P( test::MojoTestBase::LaunchType::NAMED_CHILD, test::MojoTestBase::LaunchType::NAMED_PEER)); } // namespace -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/node_channel.cc b/chromium/mojo/core/node_channel.cc index f2e4ade4298..ebcb8812e1e 100644 --- a/chromium/mojo/edk/system/node_channel.cc +++ b/chromium/mojo/core/node_channel.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/node_channel.h" #include <cstring> #include <limits> @@ -12,16 +12,13 @@ #include "base/location.h" #include "base/logging.h" #include "base/memory/ptr_util.h" -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/configuration.h" -#include "mojo/edk/system/request_context.h" - -#if defined(OS_MACOSX) && !defined(OS_IOS) -#include "mojo/edk/system/mach_port_relay.h" -#endif +#include "mojo/core/channel.h" +#include "mojo/core/configuration.h" +#include "mojo/core/core.h" +#include "mojo/core/request_context.h" namespace mojo { -namespace edk { +namespace core { namespace { @@ -194,12 +191,6 @@ void NodeChannel::GetEventMessageData(Channel::Message* message, } 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_) @@ -207,12 +198,6 @@ void NodeChannel::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(); @@ -234,6 +219,11 @@ void NodeChannel::NotifyBadMessage(const std::string& error) { void NodeChannel::SetRemoteProcessHandle(ScopedProcessHandle process_handle) { DCHECK(io_task_runner_->RunsTasksInCurrentSequence()); + { + base::AutoLock lock(channel_lock_); + if (channel_) + channel_->set_remote_process(process_handle.Clone()); + } base::AutoLock lock(remote_process_handle_lock_); DCHECK(!remote_process_handle_.is_valid()); CHECK_NE(remote_process_handle_.get(), base::GetCurrentProcessHandle()); @@ -292,10 +282,9 @@ void NodeChannel::AcceptPeer(const ports::NodeName& sender_name, void NodeChannel::AddBrokerClient(const ports::NodeName& client_name, ScopedProcessHandle process_handle) { AddBrokerClientData* data; - std::vector<ScopedInternalPlatformHandle> handles; + std::vector<PlatformHandle> handles; #if defined(OS_WIN) - handles.emplace_back(ScopedInternalPlatformHandle( - InternalPlatformHandle(process_handle.release()))); + handles.emplace_back(base::win::ScopedHandle(process_handle.release())); #endif Channel::MessagePtr message = CreateMessage(MessageType::ADD_BROKER_CLIENT, sizeof(AddBrokerClientData), @@ -309,13 +298,12 @@ void NodeChannel::AddBrokerClient(const ports::NodeName& client_name, WriteChannelMessage(std::move(message)); } -void NodeChannel::BrokerClientAdded( - const ports::NodeName& client_name, - ScopedInternalPlatformHandle broker_channel) { +void NodeChannel::BrokerClientAdded(const ports::NodeName& client_name, + PlatformHandle broker_channel) { BrokerClientAddedData* data; - std::vector<ScopedInternalPlatformHandle> handles; + std::vector<PlatformHandle> handles; if (broker_channel.is_valid()) - handles.push_back(std::move(broker_channel)); + handles.emplace_back(std::move(broker_channel)); Channel::MessagePtr message = CreateMessage(MessageType::BROKER_CLIENT_ADDED, sizeof(BrokerClientAddedData), handles.size(), &data); @@ -324,13 +312,12 @@ void NodeChannel::BrokerClientAdded( WriteChannelMessage(std::move(message)); } -void NodeChannel::AcceptBrokerClient( - const ports::NodeName& broker_name, - ScopedInternalPlatformHandle broker_channel) { +void NodeChannel::AcceptBrokerClient(const ports::NodeName& broker_name, + PlatformHandle broker_channel) { AcceptBrokerClientData* data; - std::vector<ScopedInternalPlatformHandle> handles; + std::vector<PlatformHandle> handles; if (broker_channel.is_valid()) - handles.push_back(std::move(broker_channel)); + handles.emplace_back(std::move(broker_channel)); Channel::MessagePtr message = CreateMessage(MessageType::ACCEPT_BROKER_CLIENT, sizeof(AcceptBrokerClientData), handles.size(), &data); @@ -342,9 +329,9 @@ void NodeChannel::AcceptBrokerClient( 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); + 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)); @@ -359,11 +346,11 @@ void NodeChannel::RequestIntroduction(const ports::NodeName& name) { } void NodeChannel::Introduce(const ports::NodeName& name, - ScopedInternalPlatformHandle channel_handle) { + PlatformHandle channel_handle) { IntroductionData* data; - std::vector<ScopedInternalPlatformHandle> handles; + std::vector<PlatformHandle> handles; if (channel_handle.is_valid()) - handles.push_back(std::move(channel_handle)); + handles.emplace_back(std::move(channel_handle)); Channel::MessagePtr message = CreateMessage( MessageType::INTRODUCE, sizeof(IntroductionData), handles.size(), &data); message->SetHandles(std::move(handles)); @@ -409,9 +396,9 @@ void NodeChannel::RelayEventMessage(const ports::NodeName& destination, // above stated assumption. We should not leak handles in cases where we // outlive the broker, as we may continue existing and eventually accept a new // broker invitation. - std::vector<ScopedInternalPlatformHandle> handles = message->TakeHandles(); + std::vector<PlatformHandleInTransit> handles = message->TakeHandles(); for (auto& handle : handles) - ignore_result(handle.release()); + handle.TakeHandle().release(); #else DCHECK(message->has_mach_ports()); @@ -421,7 +408,7 @@ void NodeChannel::RelayEventMessage(const ports::NodeName& destination, // 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. - std::vector<ScopedInternalPlatformHandle> handles = message->TakeHandles(); + std::vector<PlatformHandleInTransit> handles = message->TakeHandles(); size_t num_bytes = sizeof(RelayEventMessageData) + message->data_num_bytes(); RelayEventMessageData* data; Channel::MessagePtr relay_message = CreateMessage( @@ -469,10 +456,9 @@ NodeChannel::~NodeChannel() { ShutDown(); } -void NodeChannel::OnChannelMessage( - const void* payload, - size_t payload_size, - std::vector<ScopedInternalPlatformHandle> handles) { +void NodeChannel::OnChannelMessage(const void* payload, + size_t payload_size, + std::vector<PlatformHandle> handles) { DCHECK(io_task_runner_->RunsTasksInCurrentSequence()); RequestContext request_context(RequestContext::Source::SYSTEM); @@ -482,41 +468,6 @@ void NodeChannel::OnChannelMessage( // 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.empty() && remote_process_handle_.is_valid()) { - // 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.get().owning_process = remote_process_handle_.get(); - if (!Channel::Message::RewriteHandles(remote_process_handle_.get(), - base::GetCurrentProcessHandle(), - &handles)) { - DLOG(ERROR) << "Received one or more invalid handles."; - } - } else if (!handles.empty()) { - // Handles received by an unknown process must already be owned by us. - for (auto& handle : handles) - handle.get().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_EVENT_MESSAGE. - { - MachPortRelay* relay = delegate_->GetMachPortRelay(); - if (!handles.empty() && !relay) - MachPortRelay::ReceivePorts(&handles); - } -#endif // defined(OS_WIN) - - if (payload_size <= sizeof(Header)) { delegate_->OnChannelError(remote_node_name_, this); return; @@ -553,7 +504,7 @@ void NodeChannel::OnChannelMessage( break; } delegate_->OnAddBrokerClient(remote_node_name_, data->client_name, - handles.at(0).release().handle); + handles[0].ReleaseHandle()); #else if (!handles.empty()) { DLOG(ERROR) << "Dropping invalid AddBrokerClient message."; @@ -575,7 +526,7 @@ void NodeChannel::OnChannelMessage( break; } delegate_->OnBrokerClientAdded(remote_node_name_, data->client_name, - std::move(handles.at(0))); + std::move(handles[0])); return; } break; @@ -584,14 +535,14 @@ void NodeChannel::OnChannelMessage( case MessageType::ACCEPT_BROKER_CLIENT: { const AcceptBrokerClientData* data; if (GetMessagePayload(payload, payload_size, &data)) { - ScopedInternalPlatformHandle broker_channel; + PlatformHandle broker_channel; if (handles.size() > 1) { DLOG(ERROR) << "Dropping invalid AcceptBrokerClient message."; break; } - if (handles.size() == 1) { - broker_channel = std::move(handles.at(0)); - } + if (handles.size() == 1) + broker_channel = std::move(handles[0]); + delegate_->OnAcceptBrokerClient(remote_node_name_, data->broker_name, std::move(broker_channel)); return; @@ -639,10 +590,10 @@ void NodeChannel::OnChannelMessage( DLOG(ERROR) << "Dropping invalid introduction message."; break; } - ScopedInternalPlatformHandle channel_handle; - if (handles.size() == 1) { - channel_handle = std::move(handles.at(0)); - } + PlatformHandle channel_handle; + if (handles.size() == 1) + channel_handle = std::move(handles[0]); + delegate_->OnIntroduce(remote_node_name_, data->name, std::move(channel_handle)); return; @@ -668,29 +619,15 @@ void NodeChannel::OnChannelMessage( const void* message_start = data + 1; Channel::MessagePtr message = Channel::Message::Deserialize( - message_start, payload_size - sizeof(Header) - sizeof(*data)); + message_start, payload_size - sizeof(Header) - sizeof(*data), + from_process); if (!message) { DLOG(ERROR) << "Dropping invalid relay message."; break; } - #if defined(OS_MACOSX) && !defined(OS_IOS) +#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))); - return; - } - } - #endif +#endif delegate_->OnRelayEventMessage(remote_node_name_, from_process, data->destination, std::move(message)); return; @@ -776,105 +713,7 @@ void NodeChannel::OnChannelError(Channel::Error error) { 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_.get(); - } - 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(); - relay->SendPortsToProcess(message.get(), remote_process_handle); - - 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_->OnRelayEventMessage(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 RelayEventMessage which - // stashes the handles in the message in such a way that they go undetected - // here (they'll be unpacked and duplicated by the broker). - - if (message->has_handles()) { - base::AutoLock lock(remote_process_handle_lock_); - - // Rewrite outgoing handles if we have a handle to the destination process. - if (remote_process_handle_.is_valid()) { - std::vector<ScopedInternalPlatformHandle> handles = - message->TakeHandles(); - if (!Channel::Message::RewriteHandles(base::GetCurrentProcessHandle(), - remote_process_handle_.get(), - &handles)) { - 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::AutoLock lock(remote_process_handle_lock_); - DCHECK(remote_process_handle_.is_valid()); - { - base::AutoLock lock(pending_mach_messages_lock_); - if (relay->port_provider()->TaskForPid(remote_process_handle_.get()) == - 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; - } - } - - relay->SendPortsToProcess(message.get(), remote_process_handle_.get()); - } - } -#endif - // Force a crash if this process attempts to send a message larger than the // maximum allowed size. This is more useful than killing a Channel when we // *receive* an oversized message, as we should consider oversized message @@ -888,5 +727,5 @@ void NodeChannel::WriteChannelMessage(Channel::MessagePtr message) { channel_->Write(std::move(message)); } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/node_channel.h b/chromium/mojo/core/node_channel.h index 95fd6ed6410..5573305013f 100644 --- a/chromium/mojo/edk/system/node_channel.h +++ b/chromium/mojo/core/node_channel.h @@ -2,8 +2,8 @@ // 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_ +#ifndef MOJO_CORE_NODE_CHANNEL_H_ +#define MOJO_CORE_NODE_CHANNEL_H_ #include <utility> #include <vector> @@ -16,27 +16,18 @@ #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/process_error_callback.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/ports/name.h" -#include "mojo/edk/system/scoped_process_handle.h" - -#if defined(OS_MACOSX) && !defined(OS_IOS) -#include "mojo/edk/system/mach_port_relay.h" -#endif +#include "mojo/core/channel.h" +#include "mojo/core/connection_params.h" +#include "mojo/core/embedder/process_error_callback.h" +#include "mojo/core/ports/name.h" +#include "mojo/core/scoped_process_handle.h" namespace mojo { -namespace edk { +namespace core { // 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 Channel::Delegate { public: class Delegate { public: @@ -50,14 +41,12 @@ class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>, 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, - ScopedInternalPlatformHandle broker_channel) = 0; - virtual void OnAcceptBrokerClient( - const ports::NodeName& from_node, - const ports::NodeName& broker_name, - ScopedInternalPlatformHandle broker_channel) = 0; + virtual void OnBrokerClientAdded(const ports::NodeName& from_node, + const ports::NodeName& client_name, + PlatformHandle broker_channel) = 0; + virtual void OnAcceptBrokerClient(const ports::NodeName& from_node, + const ports::NodeName& broker_name, + PlatformHandle broker_channel) = 0; virtual void OnEventMessage(const ports::NodeName& from_node, Channel::MessagePtr message) = 0; virtual void OnRequestPortMerge(const ports::NodeName& from_node, @@ -67,7 +56,7 @@ class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>, const ports::NodeName& name) = 0; virtual void OnIntroduce(const ports::NodeName& from_node, const ports::NodeName& name, - ScopedInternalPlatformHandle channel_handle) = 0; + PlatformHandle 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)) @@ -85,10 +74,6 @@ class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>, 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( @@ -135,14 +120,13 @@ class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>, void AddBrokerClient(const ports::NodeName& client_name, ScopedProcessHandle process_handle); void BrokerClientAdded(const ports::NodeName& client_name, - ScopedInternalPlatformHandle broker_channel); + PlatformHandle broker_channel); void AcceptBrokerClient(const ports::NodeName& broker_name, - ScopedInternalPlatformHandle broker_channel); + PlatformHandle broker_channel); void RequestPortMerge(const ports::PortName& connector_port_name, const std::string& token); void RequestIntroduction(const ports::NodeName& name); - void Introduce(const ports::NodeName& name, - ScopedInternalPlatformHandle channel_handle); + void Introduce(const ports::NodeName& name, PlatformHandle channel_handle); void SendChannelMessage(Channel::MessagePtr message); void Broadcast(Channel::MessagePtr message); @@ -175,19 +159,11 @@ class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>, ~NodeChannel() override; // Channel::Delegate: - void OnChannelMessage( - const void* payload, - size_t payload_size, - std::vector<ScopedInternalPlatformHandle> handles) override; + void OnChannelMessage(const void* payload, + size_t payload_size, + std::vector<PlatformHandle> handles) override; void OnChannelError(Channel::Error error) 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_; @@ -203,16 +179,10 @@ class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>, base::Lock remote_process_handle_lock_; ScopedProcessHandle remote_process_handle_; -#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 core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_NODE_CHANNEL_H_ +#endif // MOJO_CORE_NODE_CHANNEL_H_ diff --git a/chromium/mojo/edk/system/node_controller.cc b/chromium/mojo/core/node_controller.cc index ee602c46340..fc65c4d5b6d 100644 --- a/chromium/mojo/edk/system/node_controller.cc +++ b/chromium/mojo/core/node_controller.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/node_controller.h" #include <algorithm> #include <limits> @@ -19,18 +19,21 @@ #include "base/rand_util.h" #include "base/time/time.h" #include "base/timer/elapsed_timer.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/configuration.h" -#include "mojo/edk/system/core.h" -#include "mojo/edk/system/request_context.h" -#include "mojo/edk/system/user_message_impl.h" +#include "mojo/core/broker.h" +#include "mojo/core/broker_host.h" +#include "mojo/core/configuration.h" +#include "mojo/core/core.h" +#include "mojo/core/request_context.h" +#include "mojo/core/user_message_impl.h" +#include "mojo/public/cpp/platform/named_platform_channel.h" +#include "mojo/public/cpp/platform/platform_channel.h" + +#if defined(OS_WIN) +#include <windows.h> +#endif #if defined(OS_MACOSX) && !defined(OS_IOS) -#include "mojo/edk/system/mach_port_relay.h" +#include "mojo/core/mach_port_relay.h" #endif #if !defined(OS_NACL) @@ -38,16 +41,20 @@ #endif namespace mojo { -namespace edk { +namespace core { namespace { #if defined(OS_NACL) template <typename T> -void GenerateRandomName(T* out) { base::RandBytes(out, sizeof(T)); } +void GenerateRandomName(T* out) { + base::RandBytes(out, sizeof(T)); +} #else template <typename T> -void GenerateRandomName(T* out) { crypto::RandBytes(out, sizeof(T)); } +void GenerateRandomName(T* out) { + crypto::RandBytes(out, sizeof(T)); +} #endif ports::NodeName GetRandomNodeName() { @@ -150,8 +157,7 @@ NodeController::NodeController(Core* core) } #if defined(OS_MACOSX) && !defined(OS_IOS) -void NodeController::CreateMachPortRelay( - base::PortProvider* port_provider) { +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)); @@ -201,12 +207,13 @@ void NodeController::AcceptBrokerClientInvitation( #if !defined(OS_MACOSX) && !defined(OS_NACL_SFI) && !defined(OS_FUCHSIA) // Use the bootstrap channel for the broker and receive the node's channel // synchronously as the first message from the broker. + DCHECK(connection_params.endpoint().is_valid()); base::ElapsedTimer timer; - broker_.reset(new Broker(connection_params.TakeChannelHandle())); - ScopedInternalPlatformHandle platform_handle = - broker_->GetInviterInternalPlatformHandle(); + broker_ = std::make_unique<Broker>( + connection_params.TakeEndpoint().TakePlatformHandle()); + PlatformChannelEndpoint endpoint = broker_->GetInviterEndpoint(); - if (!platform_handle.is_valid()) { + if (!endpoint.is_valid()) { // Most likely the inviter's side of the channel has already been closed and // the broker was unable to negotiate a NodeChannel pipe. In this case we // can cancel our connection to our inviter. @@ -214,8 +221,7 @@ void NodeController::AcceptBrokerClientInvitation( CancelPendingPortMerges(); return; } - connection_params = ConnectionParams(connection_params.protocol(), - std::move(platform_handle)); + connection_params = ConnectionParams(std::move(endpoint)); #endif io_task_runner_->PostTask( @@ -224,24 +230,14 @@ void NodeController::AcceptBrokerClientInvitation( base::Unretained(this), std::move(connection_params))); } -uint64_t NodeController::ConnectToPeer(ConnectionParams connection_params, - const ports::PortRef& port) { - uint64_t id = 0; - { - base::AutoLock lock(peers_lock_); - id = next_peer_connection_id_++; - } - io_task_runner_->PostTask(FROM_HERE, - base::Bind(&NodeController::ConnectToPeerOnIOThread, - base::Unretained(this), id, - base::Passed(&connection_params), port)); - return id; -} - -void NodeController::ClosePeerConnection(uint64_t peer_connection_id) { +void NodeController::ConnectIsolated(ConnectionParams connection_params, + const ports::PortRef& port, + base::StringPiece connection_name) { io_task_runner_->PostTask( - FROM_HERE, base::Bind(&NodeController::ClosePeerConnectionOnIOThread, - base::Unretained(this), peer_connection_id)); + FROM_HERE, + base::BindOnce(&NodeController::ConnectIsolatedOnIOThread, + base::Unretained(this), base::Passed(&connection_params), + port, connection_name.as_string())); } void NodeController::SetPortObserver(const ports::PortRef& port, @@ -330,32 +326,34 @@ void NodeController::SendBrokerClientInvitationOnIOThread( DCHECK(io_task_runner_->RunsTasksInCurrentSequence()); #if !defined(OS_MACOSX) && !defined(OS_NACL) && !defined(OS_FUCHSIA) - PlatformChannelPair node_channel; - ScopedInternalPlatformHandle server_handle = node_channel.PassServerHandle(); + PlatformChannel node_channel; + ConnectionParams node_connection_params(node_channel.TakeLocalEndpoint()); // BrokerHost owns itself. - BrokerHost* broker_host = new BrokerHost( - target_process.get(), connection_params.TakeChannelHandle(), - process_error_callback); - bool channel_ok = broker_host->SendChannel(node_channel.PassClientHandle()); + BrokerHost* broker_host = + new BrokerHost(target_process.get(), std::move(connection_params), + process_error_callback); + bool channel_ok = broker_host->SendChannel( + node_channel.TakeRemoteEndpoint().TakePlatformHandle()); #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); + NamedPlatformChannel::Options options; + NamedPlatformChannel named_channel(options); + node_connection_params = + ConnectionParams(named_channel.TakeServerEndpoint()); + broker_host->SendNamedChannel(named_channel.GetServerName()); } #else CHECK(channel_ok); #endif // defined(OS_WIN) - scoped_refptr<NodeChannel> channel = NodeChannel::Create( - this, - ConnectionParams(connection_params.protocol(), std::move(server_handle)), - io_task_runner_, process_error_callback); + scoped_refptr<NodeChannel> channel = + NodeChannel::Create(this, std::move(node_connection_params), + io_task_runner_, process_error_callback); -#else // !defined(OS_MACOSX) && !defined(OS_NACL) +#else // !defined(OS_MACOSX) && !defined(OS_NACL) scoped_refptr<NodeChannel> channel = NodeChannel::Create(this, std::move(connection_params), io_task_runner_, process_error_callback); @@ -397,19 +395,38 @@ void NodeController::AcceptBrokerClientInvitationOnIOThread( bootstrap_inviter_channel_->Start(); } -void NodeController::ConnectToPeerOnIOThread(uint64_t peer_connection_id, - ConnectionParams connection_params, - ports::PortRef port) { +void NodeController::ConnectIsolatedOnIOThread( + ConnectionParams connection_params, + ports::PortRef port, + const std::string& connection_name) { DCHECK(io_task_runner_->RunsTasksInCurrentSequence()); scoped_refptr<NodeChannel> channel = NodeChannel::Create( this, std::move(connection_params), io_task_runner_, {}); + RequestContext request_context; ports::NodeName token; GenerateRandomName(&token); - peer_connections_.emplace(token, - PeerConnection{channel, port, peer_connection_id}); - peer_connections_by_id_.emplace(peer_connection_id, token); + pending_isolated_connections_.emplace( + token, IsolatedConnection{channel, port, connection_name}); + if (!connection_name.empty()) { + // If a connection already exists with this name, drop it. + auto it = named_isolated_connections_.find(connection_name); + if (it != named_isolated_connections_.end()) { + ports::NodeName connection_node = it->second; + if (connection_node != name_) { + DropPeer(connection_node, nullptr); + } else { + auto pending_it = pending_isolated_connections_.find(connection_node); + if (pending_it != pending_isolated_connections_.end()) { + node_->ClosePort(pending_it->second.local_port); + pending_isolated_connections_.erase(pending_it); + } + named_isolated_connections_.erase(it); + } + } + named_isolated_connections_.emplace(connection_name, token); + } channel->SetRemoteNodeName(token); channel->Start(); @@ -417,19 +434,6 @@ void NodeController::ConnectToPeerOnIOThread(uint64_t peer_connection_id, channel->AcceptPeer(name_, token, port.name()); } -void NodeController::ClosePeerConnectionOnIOThread( - uint64_t peer_connection_id) { - RequestContext request_context(RequestContext::Source::SYSTEM); - auto peer = peer_connections_by_id_.find(peer_connection_id); - // The connection may already be closed. - if (peer == peer_connections_by_id_.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_); @@ -536,8 +540,8 @@ void NodeController::DropPeer(const ports::NodeName& name, bool is_inviter; { base::AutoLock lock(inviter_lock_); - is_inviter = - (name == inviter_name_ || channel == bootstrap_inviter_channel_); + is_inviter = (name == inviter_name_ || + (channel && channel == bootstrap_inviter_channel_)); } // If the error comes from the inviter channel, we also need to cancel any @@ -546,11 +550,13 @@ void NodeController::DropPeer(const ports::NodeName& name, if (is_inviter) CancelPendingPortMerges(); - auto peer = peer_connections_.find(name); - if (peer != peer_connections_.end()) { - peer_connections_by_id_.erase(peer->second.connection_id); - ports_to_close.push_back(peer->second.local_port); - peer_connections_.erase(peer); + auto connection_it = pending_isolated_connections_.find(name); + if (connection_it != pending_isolated_connections_.end()) { + IsolatedConnection& connection = connection_it->second; + ports_to_close.push_back(connection.local_port); + if (!connection.name.empty()) + named_isolated_connections_.erase(connection.name); + pending_isolated_connections_.erase(connection_it); } for (const auto& port : ports_to_close) @@ -668,7 +674,8 @@ void NodeController::DropAllPeers() { peers_.clear(); pending_invitations_.clear(); pending_peer_messages_.clear(); - peer_connections_.clear(); + pending_isolated_connections_.clear(); + named_isolated_connections_.clear(); } for (const auto& peer : all_peers) @@ -799,7 +806,7 @@ void NodeController::OnAcceptInvitation(const ports::NodeName& from_node, if (!inviter) { // Yes, we're the broker. We can initialize the client directly. - channel->AcceptBrokerClient(name_, ScopedInternalPlatformHandle()); + channel->AcceptBrokerClient(name_, PlatformHandle()); } else { // We aren't the broker, so wait for a broker connection. base::AutoLock lock(broker_lock_); @@ -825,9 +832,8 @@ void NodeController::OnAddBrokerClient(const ports::NodeName& from_node, return; } - PlatformChannelPair broker_channel; - ConnectionParams connection_params(TransportProtocol::kLegacy, - broker_channel.PassServerHandle()); + PlatformChannel broker_channel; + ConnectionParams connection_params(broker_channel.TakeLocalEndpoint()); scoped_refptr<NodeChannel> client = NodeChannel::Create(this, std::move(connection_params), io_task_runner_, ProcessErrorCallback()); @@ -847,13 +853,13 @@ void NodeController::OnAddBrokerClient(const ports::NodeName& from_node, DVLOG(1) << "Broker " << name_ << " accepting client " << client_name << " from peer " << from_node; - sender->BrokerClientAdded(client_name, broker_channel.PassClientHandle()); + sender->BrokerClientAdded( + client_name, broker_channel.TakeRemoteEndpoint().TakePlatformHandle()); } -void NodeController::OnBrokerClientAdded( - const ports::NodeName& from_node, - const ports::NodeName& client_name, - ScopedInternalPlatformHandle broker_channel) { +void NodeController::OnBrokerClientAdded(const ports::NodeName& from_node, + const ports::NodeName& client_name, + PlatformHandle broker_channel) { scoped_refptr<NodeChannel> client = GetPeerChannel(client_name); if (!client) { DLOG(ERROR) << "BrokerClientAdded for unknown client " << client_name; @@ -871,10 +877,9 @@ void NodeController::OnBrokerClientAdded( client->AcceptBrokerClient(from_node, std::move(broker_channel)); } -void NodeController::OnAcceptBrokerClient( - const ports::NodeName& from_node, - const ports::NodeName& broker_name, - ScopedInternalPlatformHandle broker_channel) { +void NodeController::OnAcceptBrokerClient(const ports::NodeName& from_node, + const ports::NodeName& broker_name, + PlatformHandle broker_channel) { DCHECK(!GetConfiguration().is_broker_process); // This node should already have an inviter in bootstrap mode. @@ -910,7 +915,7 @@ void NodeController::OnAcceptBrokerClient( DCHECK(broker_channel.is_valid()); broker = NodeChannel::Create( this, - ConnectionParams(TransportProtocol::kLegacy, std::move(broker_channel)), + ConnectionParams(PlatformChannelEndpoint(std::move(broker_channel))), io_task_runner_, ProcessErrorCallback()); AddPeer(broker_name, broker, true /* start_channel */); } @@ -1028,17 +1033,19 @@ void NodeController::OnRequestIntroduction(const ports::NodeName& from_node, scoped_refptr<NodeChannel> new_friend = GetPeerChannel(name); if (!new_friend) { // We don't know who they're talking about! - requestor->Introduce(name, ScopedInternalPlatformHandle()); + requestor->Introduce(name, PlatformHandle()); } else { - PlatformChannelPair new_channel; - requestor->Introduce(name, new_channel.PassServerHandle()); - new_friend->Introduce(from_node, new_channel.PassClientHandle()); + PlatformChannel new_channel; + requestor->Introduce(name, + new_channel.TakeLocalEndpoint().TakePlatformHandle()); + new_friend->Introduce( + from_node, new_channel.TakeRemoteEndpoint().TakePlatformHandle()); } } void NodeController::OnIntroduce(const ports::NodeName& from_node, const ports::NodeName& name, - ScopedInternalPlatformHandle channel_handle) { + PlatformHandle channel_handle) { DCHECK(io_task_runner_->RunsTasksInCurrentSequence()); if (!channel_handle.is_valid()) { @@ -1052,7 +1059,7 @@ void NodeController::OnIntroduce(const ports::NodeName& from_node, scoped_refptr<NodeChannel> channel = NodeChannel::Create( this, - ConnectionParams(TransportProtocol::kLegacy, std::move(channel_handle)), + ConnectionParams(PlatformChannelEndpoint(std::move(channel_handle))), io_task_runner_, ProcessErrorCallback()); DVLOG(1) << "Adding new peer " << name << " via broker introduction."; @@ -1093,6 +1100,8 @@ void NodeController::OnRelayEventMessage(const ports::NodeName& from_node, base::ProcessHandle from_process, const ports::NodeName& destination, Channel::MessagePtr message) { + // The broker should always know which process this came from. + DCHECK(from_process != base::kNullProcessHandle); DCHECK(io_task_runner_->RunsTasksInCurrentSequence()); if (GetBrokerChannel()) { @@ -1102,45 +1111,6 @@ void NodeController::OnRelayEventMessage(const ports::NodeName& from_node, return; } - // The broker should always know which process this came from. - DCHECK(from_process != base::kNullProcessHandle); - -#if defined(OS_WIN) - // Rewrite the handles to this (the broker) process. If the message is - // destined for another client 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. - std::vector<ScopedInternalPlatformHandle> handles = message->TakeHandles(); - for (auto& handle : handles) - handle.get().owning_process = from_process; - if (!Channel::Message::RewriteHandles( - from_process, base::GetCurrentProcessHandle(), &handles)) { - DLOG(ERROR) << "Failed to relay one or more handles."; - } - message->SetHandles(std::move(handles)); -#else - std::vector<ScopedInternalPlatformHandle> handles = message->TakeHandles(); - for (auto& handle : handles) { - if (handle.get().type == InternalPlatformHandle::Type::MACH_NAME) { - MachPortRelay* relay = GetMachPortRelay(); - if (!relay) { - handle.get().type = InternalPlatformHandle::Type::MACH; - handle.get().port = MACH_PORT_NULL; - DLOG(ERROR) << "Receiving Mach ports without a port relay from " - << from_node << "."; - continue; - } - relay->ExtractPort(&handle, from_process); - } - } - message->SetHandles(std::move(handles)); -#endif // defined(OS_WIN) - if (destination == name_) { // Great, we can deliver this message locally. OnEventMessage(from_node, std::move(message)); @@ -1173,30 +1143,29 @@ void NodeController::OnAcceptPeer(const ports::NodeName& from_node, const ports::PortName& port_name) { DCHECK(io_task_runner_->RunsTasksInCurrentSequence()); - auto it = peer_connections_.find(from_node); - if (it == peer_connections_.end()) { + auto it = pending_isolated_connections_.find(from_node); + if (it == pending_isolated_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; - uint64_t peer_connection_id = it->second.connection_id; - peer_connections_.erase(it); + IsolatedConnection& connection = it->second; + scoped_refptr<NodeChannel> channel = std::move(connection.channel); + ports::PortRef local_port = connection.local_port; + if (!connection.name.empty()) + named_isolated_connections_[connection.name] = peer_name; + pending_isolated_connections_.erase(it); DCHECK(channel); - if (name_ == peer_name) { - // 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. - peer_connections_by_id_.erase(peer_connection_id); - } else { - peer_connections_by_id_[peer_connection_id] = peer_name; - peer_connections_.emplace( - peer_name, PeerConnection{nullptr, local_port, peer_connection_id}); - DVLOG(1) << "Node " << name_ << " accepted peer " << peer_name; - + if (name_ != peer_name) { + // It's possible (e.g. in tests) that we may "connect" to ourself, in which + // case we skip this |AddPeer()| call and go straight to merging ports. + // Note that we explicitly drop any prior connection to the same peer so + // that new isolated connections can replace old ones. + DropPeer(peer_name, nullptr); AddPeer(peer_name, channel, false /* start_channel */); + DVLOG(1) << "Node " << name_ << " accepted peer " << peer_name; } // We need to choose one side to initiate the port merge. It doesn't matter @@ -1262,7 +1231,7 @@ void NodeController::AttemptShutdownIfRequested() { if (shutdown_callback_.is_null()) return; if (!node_->CanShutdownCleanly( - ports::Node::ShutdownPolicy::ALLOW_LOCAL_PORTS)) { + ports::Node::ShutdownPolicy::ALLOW_LOCAL_PORTS)) { DVLOG(2) << "Unable to cleanly shut down node " << name_; return; } @@ -1277,29 +1246,27 @@ void NodeController::AttemptShutdownIfRequested() { callback.Run(); } -NodeController::PeerConnection::PeerConnection() = default; +NodeController::IsolatedConnection::IsolatedConnection() = default; -NodeController::PeerConnection::PeerConnection( - const PeerConnection& other) = default; +NodeController::IsolatedConnection::IsolatedConnection( + const IsolatedConnection& other) = default; -NodeController::PeerConnection::PeerConnection( - PeerConnection&& other) = default; +NodeController::IsolatedConnection::IsolatedConnection( + IsolatedConnection&& other) = default; -NodeController::PeerConnection::PeerConnection( +NodeController::IsolatedConnection::IsolatedConnection( scoped_refptr<NodeChannel> channel, const ports::PortRef& local_port, - uint64_t connection_id) - : channel(std::move(channel)), - local_port(local_port), - connection_id(connection_id) {} + base::StringPiece name) + : channel(std::move(channel)), local_port(local_port), name(name) {} -NodeController::PeerConnection::~PeerConnection() = default; +NodeController::IsolatedConnection::~IsolatedConnection() = default; -NodeController::PeerConnection& NodeController::PeerConnection:: -operator=(const PeerConnection& other) = default; +NodeController::IsolatedConnection& NodeController::IsolatedConnection:: +operator=(const IsolatedConnection& other) = default; -NodeController::PeerConnection& NodeController::PeerConnection:: -operator=(PeerConnection&& other) = default; +NodeController::IsolatedConnection& NodeController::IsolatedConnection:: +operator=(IsolatedConnection&& other) = default; -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/node_controller.h b/chromium/mojo/core/node_controller.h index e6ccd428aa8..fac39df9e42 100644 --- a/chromium/mojo/edk/system/node_controller.h +++ b/chromium/mojo/core/node_controller.h @@ -2,8 +2,8 @@ // 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_ +#ifndef MOJO_CORE_NODE_CONTROLLER_H_ +#define MOJO_CORE_NODE_CONTROLLER_H_ #include <map> #include <memory> @@ -21,22 +21,22 @@ #include "base/memory/writable_shared_memory_region.h" #include "base/task_runner.h" #include "build/build_config.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/event.h" -#include "mojo/edk/system/ports/name.h" -#include "mojo/edk/system/ports/node.h" -#include "mojo/edk/system/ports/node_delegate.h" -#include "mojo/edk/system/scoped_process_handle.h" -#include "mojo/edk/system/system_impl_export.h" +#include "mojo/core/atomic_flag.h" +#include "mojo/core/node_channel.h" +#include "mojo/core/ports/event.h" +#include "mojo/core/ports/name.h" +#include "mojo/core/ports/node.h" +#include "mojo/core/ports/node_delegate.h" +#include "mojo/core/scoped_process_handle.h" +#include "mojo/core/system_impl_export.h" +#include "mojo/public/cpp/platform/platform_handle.h" namespace base { class PortProvider; } namespace mojo { -namespace edk { +namespace core { class Broker; class Core; @@ -87,13 +87,10 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate, void AcceptBrokerClientInvitation(ConnectionParams connection_params); // Connects this node to a peer node. On success, |port| will be merged with - // the corresponding port in the peer node. Returns an ID that can be used to - // later close the connection with a call to ClosePeerConnection(). - uint64_t ConnectToPeer(ConnectionParams connection_params, - const ports::PortRef& port); - - // Close a connection to a peer associated with |peer_connection_id|. - void ClosePeerConnection(uint64_t peer_connection_id); + // the corresponding port in the peer node. + void ConnectIsolated(ConnectionParams connection_params, + const ports::PortRef& port, + base::StringPiece connection_name); // Sets a port's observer. If |observer| is null the port's current observer // is removed. @@ -136,27 +133,27 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate, private: friend Core; - using NodeMap = std::unordered_map<ports::NodeName, - scoped_refptr<NodeChannel>>; + using NodeMap = + std::unordered_map<ports::NodeName, scoped_refptr<NodeChannel>>; using OutgoingMessageQueue = base::queue<Channel::MessagePtr>; using PortMap = std::map<std::string, ports::PortRef>; - struct PeerConnection { - PeerConnection(); - PeerConnection(const PeerConnection& other); - PeerConnection(PeerConnection&& other); - PeerConnection(scoped_refptr<NodeChannel> channel, - const ports::PortRef& local_port, - uint64_t connection_id); - ~PeerConnection(); + struct IsolatedConnection { + IsolatedConnection(); + IsolatedConnection(const IsolatedConnection& other); + IsolatedConnection(IsolatedConnection&& other); + IsolatedConnection(scoped_refptr<NodeChannel> channel, + const ports::PortRef& local_port, + base::StringPiece name); + ~IsolatedConnection(); - PeerConnection& operator=(const PeerConnection& other); - PeerConnection& operator=(PeerConnection&& other); + IsolatedConnection& operator=(const IsolatedConnection& other); + IsolatedConnection& operator=(IsolatedConnection&& other); // NOTE: |channel| is null once the connection is fully established. scoped_refptr<NodeChannel> channel; ports::PortRef local_port; - uint64_t connection_id; + std::string name; }; void SendBrokerClientInvitationOnIOThread( @@ -167,10 +164,9 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate, void AcceptBrokerClientInvitationOnIOThread( ConnectionParams connection_params); - void ConnectToPeerOnIOThread(uint64_t peer_connection_id, - ConnectionParams connection_params, - ports::PortRef port); - void ClosePeerConnectionOnIOThread(uint64_t peer_connection_id); + void ConnectIsolatedOnIOThread(ConnectionParams connection_params, + ports::PortRef port, + const std::string& connection_name); scoped_refptr<NodeChannel> GetPeerChannel(const ports::NodeName& name); scoped_refptr<NodeChannel> GetInviterChannel(); @@ -199,14 +195,12 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate, 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, - ScopedInternalPlatformHandle broker_channel) override; - void OnAcceptBrokerClient( - const ports::NodeName& from_node, - const ports::NodeName& broker_name, - ScopedInternalPlatformHandle broker_channel) override; + void OnBrokerClientAdded(const ports::NodeName& from_node, + const ports::NodeName& client_name, + PlatformHandle broker_channel) override; + void OnAcceptBrokerClient(const ports::NodeName& from_node, + const ports::NodeName& broker_name, + PlatformHandle broker_channel) override; void OnEventMessage(const ports::NodeName& from_node, Channel::MessagePtr message) override; void OnRequestPortMerge(const ports::NodeName& from_node, @@ -216,7 +210,7 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate, const ports::NodeName& name) override; void OnIntroduce(const ports::NodeName& from_node, const ports::NodeName& name, - ScopedInternalPlatformHandle channel_handle) override; + PlatformHandle channel_handle) override; void OnBroadcast(const ports::NodeName& from_node, Channel::MessagePtr message) override; #if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) @@ -234,8 +228,9 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate, 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; + MachPortRelay* GetMachPortRelay(); #endif // Cancels all pending port merges. These are merges which are supposed to @@ -259,15 +254,12 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate, const std::unique_ptr<ports::Node> node_; scoped_refptr<base::TaskRunner> io_task_runner_; - // Guards |peers_|, |pending_peer_messages_|, and |next_peer_connection_id_|. + // Guards |peers_| and |pending_peer_messages_|. base::Lock peers_lock_; // Channels to known peers, including inviter and invitees, if any. NodeMap peers_; - // A unique ID generator for peer connections. - uint64_t next_peer_connection_id_ = 1; - // Outgoing message queues for peers we've heard of but can't yet talk to. std::unordered_map<ports::NodeName, OutgoingMessageQueue> pending_peer_messages_; @@ -327,10 +319,8 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate, // Channels to invitees during handshake. NodeMap pending_invitations_; - std::map<ports::NodeName, PeerConnection> peer_connections_; - - // Maps from peer token to node name, pending or not. - std::unordered_map<uint64_t, ports::NodeName> peer_connections_by_id_; + std::map<ports::NodeName, IsolatedConnection> pending_isolated_connections_; + std::map<std::string, ports::NodeName> named_isolated_connections_; // Indicates whether this object should delete itself on IO thread shutdown. // Must only be accessed from the IO thread. @@ -350,7 +340,7 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate, DISALLOW_COPY_AND_ASSIGN(NodeController); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_NODE_CONTROLLER_H_ +#endif // MOJO_CORE_NODE_CONTROLLER_H_ diff --git a/chromium/mojo/edk/system/options_validation.h b/chromium/mojo/core/options_validation.h index e1b337d5f74..ae4120800ae 100644 --- a/chromium/mojo/edk/system/options_validation.h +++ b/chromium/mojo/core/options_validation.h @@ -8,8 +8,8 @@ // 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_ +#ifndef MOJO_CORE_OPTIONS_VALIDATION_H_ +#define MOJO_CORE_OPTIONS_VALIDATION_H_ #include <stddef.h> #include <stdint.h> @@ -18,11 +18,11 @@ #include "base/logging.h" #include "base/macros.h" -#include "mojo/edk/system/system_impl_export.h" +#include "mojo/core/system_impl_export.h" #include "mojo/public/c/system/types.h" namespace mojo { -namespace edk { +namespace core { template <class Options> class UserOptionsReader { @@ -91,7 +91,7 @@ class UserOptionsReader { #define OPTIONS_STRUCT_HAS_MEMBER(Options, member, reader) \ reader.HasMember(offsetof(Options, member), sizeof(reader.options().member)) -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_OPTIONS_VALIDATION_H_ +#endif // MOJO_CORE_OPTIONS_VALIDATION_H_ diff --git a/chromium/mojo/edk/system/options_validation_unittest.cc b/chromium/mojo/core/options_validation_unittest.cc index a01a92cfb10..52e0032a898 100644 --- a/chromium/mojo/edk/system/options_validation_unittest.cc +++ b/chromium/mojo/core/options_validation_unittest.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/options_validation.h" #include <stddef.h> #include <stdint.h> @@ -11,7 +11,7 @@ #include "testing/gtest/include/gtest/gtest.h" namespace mojo { -namespace edk { +namespace core { namespace { // Declare a test options struct just as we do in actual public headers. @@ -130,5 +130,5 @@ TEST(OptionsValidationTest, InvalidDeath) { } } // namespace -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/platform_handle_dispatcher.cc b/chromium/mojo/core/platform_handle_dispatcher.cc index 19b1ed3fb19..7029b965369 100644 --- a/chromium/mojo/edk/system/platform_handle_dispatcher.cc +++ b/chromium/mojo/core/platform_handle_dispatcher.cc @@ -2,22 +2,20 @@ // 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 "mojo/core/platform_handle_dispatcher.h" #include "base/synchronization/lock.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" namespace mojo { -namespace edk { +namespace core { // static scoped_refptr<PlatformHandleDispatcher> PlatformHandleDispatcher::Create( - ScopedInternalPlatformHandle platform_handle) { + PlatformHandle platform_handle) { return new PlatformHandleDispatcher(std::move(platform_handle)); } -ScopedInternalPlatformHandle -PlatformHandleDispatcher::PassInternalPlatformHandle() { +PlatformHandle PlatformHandleDispatcher::TakePlatformHandle() { return std::move(platform_handle_); } @@ -42,14 +40,13 @@ void PlatformHandleDispatcher::StartSerialize(uint32_t* num_bytes, *num_handles = 1; } -bool PlatformHandleDispatcher::EndSerialize( - void* destination, - ports::PortName* ports, - ScopedInternalPlatformHandle* handles) { +bool PlatformHandleDispatcher::EndSerialize(void* destination, + ports::PortName* ports, + PlatformHandle* handles) { base::AutoLock lock(lock_); if (is_closed_) return false; - handles[0] = ScopedInternalPlatformHandle(platform_handle_.get()); + handles[0] = std::move(platform_handle_); return true; } @@ -63,12 +60,8 @@ bool PlatformHandleDispatcher::BeginTransit() { 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() { @@ -82,7 +75,7 @@ scoped_refptr<PlatformHandleDispatcher> PlatformHandleDispatcher::Deserialize( size_t num_bytes, const ports::PortName* ports, size_t num_ports, - ScopedInternalPlatformHandle* handles, + PlatformHandle* handles, size_t num_handles) { if (num_bytes || num_ports || num_handles != 1) return nullptr; @@ -91,7 +84,7 @@ scoped_refptr<PlatformHandleDispatcher> PlatformHandleDispatcher::Deserialize( } PlatformHandleDispatcher::PlatformHandleDispatcher( - ScopedInternalPlatformHandle platform_handle) + PlatformHandle platform_handle) : platform_handle_(std::move(platform_handle)) {} PlatformHandleDispatcher::~PlatformHandleDispatcher() { @@ -99,5 +92,5 @@ PlatformHandleDispatcher::~PlatformHandleDispatcher() { DCHECK(!platform_handle_.is_valid()); } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/platform_handle_dispatcher.h b/chromium/mojo/core/platform_handle_dispatcher.h index 8a377e47fa7..8d9627c393e 100644 --- a/chromium/mojo/edk/system/platform_handle_dispatcher.h +++ b/chromium/mojo/core/platform_handle_dispatcher.h @@ -2,25 +2,25 @@ // 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_ +#ifndef MOJO_CORE_PLATFORM_HANDLE_DISPATCHER_H_ +#define MOJO_CORE_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" +#include "mojo/core/dispatcher.h" +#include "mojo/core/system_impl_export.h" +#include "mojo/public/cpp/platform/platform_handle.h" namespace mojo { -namespace edk { +namespace core { class MOJO_SYSTEM_IMPL_EXPORT PlatformHandleDispatcher : public Dispatcher { public: static scoped_refptr<PlatformHandleDispatcher> Create( - ScopedInternalPlatformHandle platform_handle); + PlatformHandle platform_handle); - ScopedInternalPlatformHandle PassInternalPlatformHandle(); + PlatformHandle TakePlatformHandle(); // Dispatcher: Type GetType() const override; @@ -30,7 +30,7 @@ class MOJO_SYSTEM_IMPL_EXPORT PlatformHandleDispatcher : public Dispatcher { uint32_t* num_handles) override; bool EndSerialize(void* destination, ports::PortName* ports, - ScopedInternalPlatformHandle* handles) override; + PlatformHandle* handles) override; bool BeginTransit() override; void CompleteTransitAndClose() override; void CancelTransit() override; @@ -40,22 +40,22 @@ class MOJO_SYSTEM_IMPL_EXPORT PlatformHandleDispatcher : public Dispatcher { size_t num_bytes, const ports::PortName* ports, size_t num_ports, - ScopedInternalPlatformHandle* handles, + PlatformHandle* handles, size_t num_handles); private: - PlatformHandleDispatcher(ScopedInternalPlatformHandle platform_handle); + PlatformHandleDispatcher(PlatformHandle platform_handle); ~PlatformHandleDispatcher() override; base::Lock lock_; bool in_transit_ = false; bool is_closed_ = false; - ScopedInternalPlatformHandle platform_handle_; + PlatformHandle platform_handle_; DISALLOW_COPY_AND_ASSIGN(PlatformHandleDispatcher); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_PLATFORM_HANDLE_DISPATCHER_H_ +#endif // MOJO_CORE_PLATFORM_HANDLE_DISPATCHER_H_ diff --git a/chromium/mojo/edk/system/platform_handle_dispatcher_unittest.cc b/chromium/mojo/core/platform_handle_dispatcher_unittest.cc index 0313b8fdccf..8e3ad81f3d8 100644 --- a/chromium/mojo/edk/system/platform_handle_dispatcher_unittest.cc +++ b/chromium/mojo/core/platform_handle_dispatcher_unittest.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/platform_handle_dispatcher.h" #include <stdio.h> #include <utility> @@ -12,12 +12,12 @@ #include "base/files/scoped_file.h" #include "base/files/scoped_temp_dir.h" #include "base/memory/ref_counted.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/test/test_utils.h" +#include "mojo/core/test/test_utils.h" +#include "mojo/public/cpp/system/platform_handle.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo { -namespace edk { +namespace core { namespace { TEST(PlatformHandleDispatcherTest, Basic) { @@ -33,8 +33,7 @@ TEST(PlatformHandleDispatcherTest, Basic) { EXPECT_EQ(sizeof(kHelloWorld), fwrite(kHelloWorld, 1, sizeof(kHelloWorld), fp.get())); - ScopedInternalPlatformHandle h( - test::InternalPlatformHandleFromFILE(std::move(fp))); + PlatformHandle h = test::PlatformHandleFromFILE(std::move(fp)); EXPECT_FALSE(fp); ASSERT_TRUE(h.is_valid()); @@ -43,10 +42,10 @@ TEST(PlatformHandleDispatcherTest, Basic) { EXPECT_FALSE(h.is_valid()); EXPECT_EQ(Dispatcher::Type::PLATFORM_HANDLE, dispatcher->GetType()); - h = dispatcher->PassInternalPlatformHandle(); + h = dispatcher->TakePlatformHandle(); EXPECT_TRUE(h.is_valid()); - fp = test::FILEFromInternalPlatformHandle(std::move(h), "rb"); + fp = test::FILEFromPlatformHandle(std::move(h), "rb"); EXPECT_FALSE(h.is_valid()); EXPECT_TRUE(fp); @@ -57,8 +56,8 @@ TEST(PlatformHandleDispatcherTest, Basic) { EXPECT_STREQ(kHelloWorld, read_buffer); // Try getting the handle again. (It should fail cleanly.) - h = dispatcher->PassInternalPlatformHandle(); - EXPECT_FALSE(h.is_valid()); + auto internal_handle = dispatcher->TakePlatformHandle(); + EXPECT_FALSE(internal_handle.is_valid()); EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close()); } @@ -76,7 +75,7 @@ TEST(PlatformHandleDispatcherTest, Serialization) { scoped_refptr<PlatformHandleDispatcher> dispatcher = PlatformHandleDispatcher::Create( - test::InternalPlatformHandleFromFILE(std::move(fp))); + test::PlatformHandleFromFILE(std::move(fp))); uint32_t num_bytes = 0; uint32_t num_ports = 0; @@ -88,15 +87,14 @@ TEST(PlatformHandleDispatcherTest, Serialization) { EXPECT_EQ(0u, num_ports); EXPECT_EQ(1u, num_handles); - ScopedInternalPlatformHandle received_handle; + PlatformHandle received_handle; EXPECT_TRUE(dispatcher->EndSerialize(nullptr, nullptr, &received_handle)); dispatcher->CompleteTransitAndClose(); EXPECT_TRUE(received_handle.is_valid()); - ScopedInternalPlatformHandle handle = - dispatcher->PassInternalPlatformHandle(); + PlatformHandle handle = dispatcher->TakePlatformHandle(); EXPECT_FALSE(handle.is_valid()); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, dispatcher->Close()); @@ -110,8 +108,7 @@ TEST(PlatformHandleDispatcherTest, Serialization) { EXPECT_FALSE(received_handle.is_valid()); EXPECT_TRUE(dispatcher->GetType() == Dispatcher::Type::PLATFORM_HANDLE); - fp = test::FILEFromInternalPlatformHandle( - dispatcher->PassInternalPlatformHandle(), "rb"); + fp = test::FILEFromPlatformHandle(dispatcher->TakePlatformHandle(), "rb"); EXPECT_TRUE(fp); rewind(fp.get()); @@ -124,5 +121,5 @@ TEST(PlatformHandleDispatcherTest, Serialization) { } } // namespace -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/core/platform_handle_in_transit.cc b/chromium/mojo/core/platform_handle_in_transit.cc new file mode 100644 index 00000000000..7b82f27c34b --- /dev/null +++ b/chromium/mojo/core/platform_handle_in_transit.cc @@ -0,0 +1,156 @@ +// Copyright 2018 The Chromium 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/core/platform_handle_in_transit.h" + +#include <utility> + +#include "base/logging.h" +#include "base/process/process_handle.h" +#include "build/build_config.h" + +#if defined(OS_WIN) +#include <windows.h> + +#include "base/win/scoped_handle.h" +#endif + +namespace mojo { +namespace core { + +namespace { + +#if defined(OS_WIN) +HANDLE TransferHandle(HANDLE handle, + base::ProcessHandle from_process, + base::ProcessHandle to_process) { + BOOL result = + ::DuplicateHandle(from_process, handle, to_process, &handle, 0, FALSE, + DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); + if (result) { + return handle; + } else { + DPLOG(ERROR) << "DuplicateHandle failed"; + return INVALID_HANDLE_VALUE; + } +} + +void CloseHandleInProcess(HANDLE handle, const ScopedProcessHandle& process) { + DCHECK_NE(handle, INVALID_HANDLE_VALUE); + DCHECK(process.is_valid()); + + // The handle lives in |process|, so we close it there using a special + // incantation of |DuplicateHandle()|. + // + // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724251 for + // this usage of |DuplicateHandle()|, particularly where it says "to close a + // handle from the source process...". Note that although the documentation + // says that the target *handle* address must be NULL, it seems that the + // target process handle being NULL is what really matters here. + BOOL result = ::DuplicateHandle(process.get(), handle, NULL, &handle, 0, + FALSE, DUPLICATE_CLOSE_SOURCE); + if (!result) { + DPLOG(ERROR) << "DuplicateHandle failed"; + } +} +#endif + +} // namespace + +PlatformHandleInTransit::PlatformHandleInTransit() = default; + +PlatformHandleInTransit::PlatformHandleInTransit(PlatformHandle handle) + : handle_(std::move(handle)) {} + +PlatformHandleInTransit::PlatformHandleInTransit( + PlatformHandleInTransit&& other) { + *this = std::move(other); +} + +PlatformHandleInTransit::~PlatformHandleInTransit() { +#if defined(OS_WIN) + if (!owning_process_.is_valid()) { + DCHECK_EQ(remote_handle_, INVALID_HANDLE_VALUE); + return; + } + + CloseHandleInProcess(remote_handle_, owning_process_); +#endif +} + +PlatformHandleInTransit& PlatformHandleInTransit::operator=( + PlatformHandleInTransit&& other) { +#if defined(OS_WIN) + if (owning_process_.is_valid()) { + DCHECK_NE(remote_handle_, INVALID_HANDLE_VALUE); + CloseHandleInProcess(remote_handle_, owning_process_); + } + + remote_handle_ = INVALID_HANDLE_VALUE; + std::swap(remote_handle_, other.remote_handle_); +#elif defined(OS_MACOSX) && !defined(OS_IOS) + mach_port_name_ = MACH_PORT_NULL; + std::swap(mach_port_name_, other.mach_port_name_); +#endif + handle_ = std::move(other.handle_); + owning_process_ = std::move(other.owning_process_); + return *this; +} + +PlatformHandle PlatformHandleInTransit::TakeHandle() { + DCHECK(!owning_process_.is_valid()); + return std::move(handle_); +} + +void PlatformHandleInTransit::CompleteTransit() { +#if defined(OS_WIN) + remote_handle_ = INVALID_HANDLE_VALUE; +#endif + handle_.release(); + owning_process_ = ScopedProcessHandle(); +} + +bool PlatformHandleInTransit::TransferToProcess( + ScopedProcessHandle target_process) { + DCHECK(target_process.is_valid()); + DCHECK(!owning_process_.is_valid()); + DCHECK(handle_.is_valid()); +#if defined(OS_WIN) + remote_handle_ = + TransferHandle(handle_.ReleaseHandle(), base::GetCurrentProcessHandle(), + target_process.get()); + if (remote_handle_ == INVALID_HANDLE_VALUE) + return false; +#endif + owning_process_ = std::move(target_process); + return true; +} + +#if defined(OS_WIN) +// static +PlatformHandle PlatformHandleInTransit::TakeIncomingRemoteHandle( + HANDLE handle, + base::ProcessHandle owning_process) { + return PlatformHandle(base::win::ScopedHandle( + TransferHandle(handle, owning_process, base::GetCurrentProcessHandle()))); +} +#endif + +#if defined(OS_MACOSX) && !defined(OS_IOS) +// static +PlatformHandleInTransit PlatformHandleInTransit::CreateForMachPortName( + mach_port_t name) { + if (name == MACH_PORT_NULL) { + return PlatformHandleInTransit( + PlatformHandle(base::mac::ScopedMachSendRight())); + } + + PlatformHandleInTransit handle; + handle.mach_port_name_ = name; + return handle; +} +#endif + +} // namespace core +} // namespace mojo diff --git a/chromium/mojo/core/platform_handle_in_transit.h b/chromium/mojo/core/platform_handle_in_transit.h new file mode 100644 index 00000000000..b75a01fbb01 --- /dev/null +++ b/chromium/mojo/core/platform_handle_in_transit.h @@ -0,0 +1,107 @@ +// Copyright 2018 The Chromium 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_CORE_PLATFORM_HANDLE_IN_TRANSIT_H_ +#define MOJO_CORE_PLATFORM_HANDLE_IN_TRANSIT_H_ + +#include "base/macros.h" +#include "build/build_config.h" +#include "mojo/core/scoped_process_handle.h" +#include "mojo/public/cpp/platform/platform_handle.h" + +#if defined(OS_MACOSX) && !defined(OS_IOS) +#include <mach/mach.h> +#endif + +#if defined(OS_WIN) +#include <windows.h> +#endif + +namespace mojo { +namespace core { + +// Owns a PlatformHandle which may actually belong to another process. On +// Windows and (sometimes) Mac, handles in a message object may take on values +// which only have meaning in the context of a remote process. +// +// This class provides a safe way of scoping the lifetime of such handles so +// that they don't leak when transmission can't be completed. +class PlatformHandleInTransit { + public: + PlatformHandleInTransit(); + explicit PlatformHandleInTransit(PlatformHandle handle); + PlatformHandleInTransit(PlatformHandleInTransit&&); + ~PlatformHandleInTransit(); + + PlatformHandleInTransit& operator=(PlatformHandleInTransit&&); + + // Accessor for the owned handle. Must be owned by the calling process. + const PlatformHandle& handle() const { + DCHECK(!owning_process_.is_valid()); + return handle_; + } + + // Returns the process which owns this handle. If this is invalid, the handle + // is owned by the current process. + const ScopedProcessHandle& owning_process() const { return owning_process_; } + + // Takes ownership of the held handle as-is. The handle must belong to the + // current process. + PlatformHandle TakeHandle(); + + // Discards the handle owned by this object. The implication is that its + // value has been successfully communicated to the owning process and the + // calling process is no longer responsible for managing the handle's + // lifetime. + void CompleteTransit(); + + // Transfers ownership of this (local) handle to |target_process|. + bool TransferToProcess(ScopedProcessHandle target_process); + +#if defined(OS_WIN) + HANDLE remote_handle() const { return remote_handle_; } + + // Returns a new local handle, with ownership of |handle| being transferred + // from |owning_process| to the caller. + static PlatformHandle TakeIncomingRemoteHandle( + HANDLE handle, + base::ProcessHandle owning_process); +#endif + +#if defined(OS_MACOSX) && !defined(OS_IOS) + // Creates a special wrapper holding an unowned Mach port name. This may refer + // to a send or receive right in a remote task (process), and is used for + // cases where message must retain such an object as one of its attached + // handles. We're OK for now with leaking in any scenario where a lack of + // strict ownership could cause leakage. See https://crbug.com/855930 for more + // details. + static PlatformHandleInTransit CreateForMachPortName(mach_port_t name); + + bool is_mach_port_name() const { return mach_port_name_ != MACH_PORT_NULL; } + mach_port_t mach_port_name() const { return mach_port_name_; } +#endif + + private: +#if defined(OS_WIN) + // We don't use a ScopedHandle (or, by extension, PlatformHandle) here because + // the handle verifier expects all handle values to be owned by this process. + // On Windows we use |handle_| for locally owned handles and |remote_handle_| + // otherwise. On all other platforms we use |handle_| regardless of ownership. + HANDLE remote_handle_ = INVALID_HANDLE_VALUE; +#endif + + PlatformHandle handle_; + ScopedProcessHandle owning_process_; + +#if defined(OS_MACOSX) && !defined(OS_IOS) + mach_port_t mach_port_name_ = MACH_PORT_NULL; +#endif + + DISALLOW_COPY_AND_ASSIGN(PlatformHandleInTransit); +}; + +} // namespace core +} // namespace mojo + +#endif // MOJO_CORE_PLATFORM_HANDLE_IN_TRANSIT_H_ diff --git a/chromium/mojo/core/platform_handle_utils.cc b/chromium/mojo/core/platform_handle_utils.cc new file mode 100644 index 00000000000..edd0cff0639 --- /dev/null +++ b/chromium/mojo/core/platform_handle_utils.cc @@ -0,0 +1,67 @@ +// Copyright 2018 The Chromium 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/core/platform_handle_utils.h" + +#include "build/build_config.h" + +#if defined(OS_FUCHSIA) +#include <lib/zx/vmo.h> +#elif defined(OS_POSIX) +#include "base/files/scoped_file.h" +#elif defined(OS_WIN) +#include "base/win/scoped_handle.h" +#endif + +#if defined(OS_MACOSX) && !defined(OS_IOS) +#include "base/mac/scoped_mach_port.h" +#endif + +namespace mojo { +namespace core { + +void ExtractPlatformHandlesFromSharedMemoryRegionHandle( + base::subtle::PlatformSharedMemoryRegion::ScopedPlatformHandle handle, + PlatformHandle* extracted_handle, + PlatformHandle* extracted_readonly_handle) { +#if defined(OS_WIN) + *extracted_handle = PlatformHandle(base::win::ScopedHandle(handle.Take())); +#elif defined(OS_FUCHSIA) + *extracted_handle = PlatformHandle(std::move(handle)); +#elif defined(OS_MACOSX) && !defined(OS_IOS) + // This is a Mach port. Same code as above and below, but separated for + // clarity. + *extracted_handle = PlatformHandle(std::move(handle)); +#elif defined(OS_ANDROID) + // This is a file descriptor. Same code as above, but separated for clarity. + *extracted_handle = PlatformHandle(std::move(handle)); +#else + *extracted_handle = PlatformHandle(std::move(handle.fd)); + *extracted_readonly_handle = PlatformHandle(std::move(handle.readonly_fd)); +#endif +} + +base::subtle::PlatformSharedMemoryRegion::ScopedPlatformHandle +CreateSharedMemoryRegionHandleFromPlatformHandles( + PlatformHandle handle, + PlatformHandle readonly_handle) { +#if defined(OS_WIN) + DCHECK(!readonly_handle.is_valid()); + return handle.TakeHandle(); +#elif defined(OS_FUCHSIA) + DCHECK(!readonly_handle.is_valid()); + return zx::vmo(handle.TakeHandle()); +#elif defined(OS_MACOSX) && !defined(OS_IOS) + DCHECK(!readonly_handle.is_valid()); + return handle.TakeMachPort(); +#elif defined(OS_ANDROID) + DCHECK(!readonly_handle.is_valid()); + return handle.TakeFD(); +#else + return base::subtle::ScopedFDPair(handle.TakeFD(), readonly_handle.TakeFD()); +#endif +} + +} // namespace core +} // namespace mojo diff --git a/chromium/mojo/core/platform_handle_utils.h b/chromium/mojo/core/platform_handle_utils.h new file mode 100644 index 00000000000..b18275b5a4d --- /dev/null +++ b/chromium/mojo/core/platform_handle_utils.h @@ -0,0 +1,35 @@ +// 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_CORE_PLATFORM_HANDLE_UTILS_H_ +#define MOJO_CORE_PLATFORM_HANDLE_UTILS_H_ + +#include "base/memory/platform_shared_memory_region.h" +#include "mojo/core/system_impl_export.h" +#include "mojo/public/c/system/platform_handle.h" +#include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/platform/platform_handle.h" + +namespace mojo { +namespace core { + +// Converts a base shared memory platform handle into one (maybe two on POSIX) +// PlatformHandle(s). +MOJO_SYSTEM_IMPL_EXPORT void ExtractPlatformHandlesFromSharedMemoryRegionHandle( + base::subtle::PlatformSharedMemoryRegion::ScopedPlatformHandle handle, + PlatformHandle* extracted_handle, + PlatformHandle* extracted_readonly_handle); + +// Converts one (maybe two on POSIX) PlatformHandle(s) to a base shared memory +// platform handle. +MOJO_SYSTEM_IMPL_EXPORT +base::subtle::PlatformSharedMemoryRegion::ScopedPlatformHandle +CreateSharedMemoryRegionHandleFromPlatformHandles( + PlatformHandle handle, + PlatformHandle readonly_handle); + +} // namespace core +} // namespace mojo + +#endif // MOJO_CORE_PLATFORM_HANDLE_UTILS_H_ diff --git a/chromium/mojo/edk/system/platform_shared_memory_mapping.cc b/chromium/mojo/core/platform_shared_memory_mapping.cc index 80f14f6546c..6b8d67201f3 100644 --- a/chromium/mojo/edk/system/platform_shared_memory_mapping.cc +++ b/chromium/mojo/core/platform_shared_memory_mapping.cc @@ -2,7 +2,7 @@ // 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_shared_memory_mapping.h" +#include "mojo/core/platform_shared_memory_mapping.h" #include <utility> @@ -19,7 +19,7 @@ #endif namespace mojo { -namespace edk { +namespace core { namespace { @@ -100,5 +100,5 @@ size_t PlatformSharedMemoryMapping::GetLength() const { return length_; } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/platform_shared_memory_mapping.h b/chromium/mojo/core/platform_shared_memory_mapping.h index 764fe4f4c1e..b7c43ace9e0 100644 --- a/chromium/mojo/edk/system/platform_shared_memory_mapping.h +++ b/chromium/mojo/core/platform_shared_memory_mapping.h @@ -2,8 +2,8 @@ // 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_SHARED_MEMORY_MAPPING_H_ -#define MOJO_EDK_SYSTEM_PLATFORM_SHARED_MEMORY_MAPPING_H_ +#ifndef MOJO_CORE_PLATFORM_SHARED_MEMORY_MAPPING_H_ +#define MOJO_CORE_PLATFORM_SHARED_MEMORY_MAPPING_H_ #include <stddef.h> @@ -13,10 +13,10 @@ #include "base/macros.h" #include "base/memory/platform_shared_memory_region.h" #include "base/memory/shared_memory_mapping.h" -#include "mojo/edk/system/system_impl_export.h" +#include "mojo/core/system_impl_export.h" namespace mojo { -namespace edk { +namespace core { // A mapping of a |base::subtle::PlatformSharedMemoryRegion|, created // exclusively by |SharedBufferDispatcher::MapBuffer()|. Automatically maps @@ -54,7 +54,7 @@ class MOJO_SYSTEM_IMPL_EXPORT PlatformSharedMemoryMapping { DISALLOW_COPY_AND_ASSIGN(PlatformSharedMemoryMapping); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_PLATFORM_SHARED_MEMORY_MAPPING_H_ +#endif // MOJO_CORE_PLATFORM_SHARED_MEMORY_MAPPING_H_ diff --git a/chromium/mojo/edk/system/platform_wrapper_unittest.cc b/chromium/mojo/core/platform_wrapper_unittest.cc index f7770ba711c..e795b949201 100644 --- a/chromium/mojo/edk/system/platform_wrapper_unittest.cc +++ b/chromium/mojo/core/platform_wrapper_unittest.cc @@ -14,7 +14,7 @@ #include "base/memory/shared_memory.h" #include "base/process/process_handle.h" #include "build/build_config.h" -#include "mojo/edk/test/mojo_test_base.h" +#include "mojo/core/test/mojo_test_base.h" #include "mojo/public/c/system/platform_handle.h" #include "mojo/public/cpp/system/message_pipe.h" #include "testing/gtest/include/gtest/gtest.h" @@ -38,7 +38,7 @@ #define SHARED_BUFFER_PLATFORM_HANDLE_TYPE SIMPLE_PLATFORM_HANDLE_TYPE #endif -uint64_t InternalPlatformHandleValueFromPlatformFile(base::PlatformFile file) { +uint64_t PlatformHandleValueFromPlatformFile(base::PlatformFile file) { #if defined(OS_WIN) return reinterpret_cast<uint64_t>(file); #elif defined(OS_POSIX) || defined(OS_FUCHSIA) @@ -46,7 +46,7 @@ uint64_t InternalPlatformHandleValueFromPlatformFile(base::PlatformFile file) { #endif } -base::PlatformFile PlatformFileFromInternalPlatformHandleValue(uint64_t value) { +base::PlatformFile PlatformFileFromPlatformHandleValue(uint64_t value) { #if defined(OS_WIN) return reinterpret_cast<base::PlatformFile>(value); #elif defined(OS_POSIX) || defined(OS_FUCHSIA) @@ -55,12 +55,12 @@ base::PlatformFile PlatformFileFromInternalPlatformHandleValue(uint64_t value) { } namespace mojo { -namespace edk { +namespace core { namespace { using PlatformWrapperTest = test::MojoTestBase; -TEST_F(PlatformWrapperTest, WrapInternalPlatformHandle) { +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)); @@ -81,7 +81,7 @@ TEST_F(PlatformWrapperTest, WrapInternalPlatformHandle) { os_file.struct_size = sizeof(MojoPlatformHandle); os_file.type = SIMPLE_PLATFORM_HANDLE_TYPE; os_file.value = - InternalPlatformHandleValueFromPlatformFile(file.TakePlatformFile()); + PlatformHandleValueFromPlatformFile(file.TakePlatformFile()); EXPECT_EQ(MOJO_RESULT_OK, MojoWrapPlatformHandle(&os_file, nullptr, &wrapped_handle)); @@ -101,8 +101,7 @@ DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReadPlatformFile, PlatformWrapperTest, h) { ASSERT_EQ(MOJO_RESULT_OK, MojoUnwrapPlatformHandle(wrapped_handle, nullptr, &platform_handle)); EXPECT_EQ(SIMPLE_PLATFORM_HANDLE_TYPE, platform_handle.type); - base::File file( - PlatformFileFromInternalPlatformHandleValue(platform_handle.value)); + base::File file(PlatformFileFromPlatformHandleValue(platform_handle.value)); // Expect to read the same message from the file. std::vector<char> data(message.size()); @@ -246,5 +245,5 @@ TEST_F(PlatformWrapperTest, InvalidArgument) { } } // namespace -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/ports/BUILD.gn b/chromium/mojo/core/ports/BUILD.gn index 1a5ba79279c..68dce3f8eb6 100644 --- a/chromium/mojo/edk/system/ports/BUILD.gn +++ b/chromium/mojo/core/ports/BUILD.gn @@ -5,7 +5,7 @@ import("//testing/test.gni") component("ports") { - output_name = "mojo_edk_ports" + output_name = "mojo_core_ports" sources = [ "event.cc", @@ -29,7 +29,7 @@ component("ports") { "user_message.h", ] - defines = [ "IS_MOJO_EDK_PORTS_IMPL" ] + defines = [ "IS_MOJO_CORE_PORTS_IMPL" ] public_deps = [ "//base", diff --git a/chromium/mojo/edk/system/ports/event.cc b/chromium/mojo/core/ports/event.cc index 1c5abaf8e3b..f3cf74edbdd 100644 --- a/chromium/mojo/edk/system/ports/event.cc +++ b/chromium/mojo/core/ports/event.cc @@ -2,16 +2,16 @@ // 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 "mojo/core/ports/event.h" #include <stdint.h> #include <string.h> #include "base/numerics/safe_math.h" -#include "mojo/edk/system/ports/user_message.h" +#include "mojo/core/ports/user_message.h" namespace mojo { -namespace edk { +namespace core { namespace ports { namespace { @@ -189,6 +189,12 @@ UserMessageEvent::UserMessageEvent(const PortName& port_name, uint64_t sequence_num) : Event(Type::kUserMessage, port_name), sequence_num_(sequence_num) {} +size_t UserMessageEvent::GetSizeIfSerialized() const { + if (!message_) + return 0; + return message_->GetSizeIfSerialized(); +} + size_t UserMessageEvent::GetSerializedDataSize() const { DCHECK_EQ(ports_.size(), port_descriptors_.size()); base::CheckedNumeric<size_t> size = sizeof(UserMessageEventData); @@ -373,5 +379,5 @@ void MergePortEvent::SerializeData(void* buffer) const { } } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/ports/event.h b/chromium/mojo/core/ports/event.h index ab8a4d80113..e278896b04f 100644 --- a/chromium/mojo/edk/system/ports/event.h +++ b/chromium/mojo/core/ports/event.h @@ -2,8 +2,8 @@ // 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_ +#ifndef MOJO_CORE_PORTS_EVENT_H_ +#define MOJO_CORE_PORTS_EVENT_H_ #include <stdint.h> @@ -12,11 +12,11 @@ #include "base/component_export.h" #include "base/macros.h" #include "base/memory/ptr_util.h" -#include "mojo/edk/system/ports/name.h" -#include "mojo/edk/system/ports/user_message.h" +#include "mojo/core/ports/name.h" +#include "mojo/core/ports/user_message.h" namespace mojo { -namespace edk { +namespace core { namespace ports { class Event; @@ -25,7 +25,7 @@ using ScopedEvent = std::unique_ptr<Event>; // A Event is the fundamental unit of operation and communication within and // between Nodes. -class COMPONENT_EXPORT(MOJO_EDK_PORTS) Event { +class COMPONENT_EXPORT(MOJO_CORE_PORTS) Event { public: enum Type : uint32_t { // A user message event contains arbitrary user-specified payload data @@ -103,7 +103,7 @@ class COMPONENT_EXPORT(MOJO_EDK_PORTS) Event { DISALLOW_COPY_AND_ASSIGN(Event); }; -class COMPONENT_EXPORT(MOJO_EDK_PORTS) UserMessageEvent : public Event { +class COMPONENT_EXPORT(MOJO_CORE_PORTS) UserMessageEvent : public Event { public: explicit UserMessageEvent(size_t num_ports); ~UserMessageEvent() override; @@ -139,6 +139,8 @@ class COMPONENT_EXPORT(MOJO_EDK_PORTS) UserMessageEvent : public Event { const void* buffer, size_t num_bytes); + size_t GetSizeIfSerialized() const; + private: UserMessageEvent(const PortName& port_name, uint64_t sequence_num); @@ -153,7 +155,7 @@ class COMPONENT_EXPORT(MOJO_EDK_PORTS) UserMessageEvent : public Event { DISALLOW_COPY_AND_ASSIGN(UserMessageEvent); }; -class COMPONENT_EXPORT(MOJO_EDK_PORTS) PortAcceptedEvent : public Event { +class COMPONENT_EXPORT(MOJO_CORE_PORTS) PortAcceptedEvent : public Event { public: explicit PortAcceptedEvent(const PortName& port_name); ~PortAcceptedEvent() override; @@ -169,7 +171,7 @@ class COMPONENT_EXPORT(MOJO_EDK_PORTS) PortAcceptedEvent : public Event { DISALLOW_COPY_AND_ASSIGN(PortAcceptedEvent); }; -class COMPONENT_EXPORT(MOJO_EDK_PORTS) ObserveProxyEvent : public Event { +class COMPONENT_EXPORT(MOJO_CORE_PORTS) ObserveProxyEvent : public Event { public: ObserveProxyEvent(const PortName& port_name, const NodeName& proxy_node_name, @@ -204,7 +206,7 @@ class COMPONENT_EXPORT(MOJO_EDK_PORTS) ObserveProxyEvent : public Event { DISALLOW_COPY_AND_ASSIGN(ObserveProxyEvent); }; -class COMPONENT_EXPORT(MOJO_EDK_PORTS) ObserveProxyAckEvent : public Event { +class COMPONENT_EXPORT(MOJO_CORE_PORTS) ObserveProxyAckEvent : public Event { public: ObserveProxyAckEvent(const PortName& port_name, uint64_t last_sequence_num); ~ObserveProxyAckEvent() override; @@ -225,7 +227,7 @@ class COMPONENT_EXPORT(MOJO_EDK_PORTS) ObserveProxyAckEvent : public Event { DISALLOW_COPY_AND_ASSIGN(ObserveProxyAckEvent); }; -class COMPONENT_EXPORT(MOJO_EDK_PORTS) ObserveClosureEvent : public Event { +class COMPONENT_EXPORT(MOJO_CORE_PORTS) ObserveClosureEvent : public Event { public: ObserveClosureEvent(const PortName& port_name, uint64_t last_sequence_num); ~ObserveClosureEvent() override; @@ -249,7 +251,7 @@ class COMPONENT_EXPORT(MOJO_EDK_PORTS) ObserveClosureEvent : public Event { DISALLOW_COPY_AND_ASSIGN(ObserveClosureEvent); }; -class COMPONENT_EXPORT(MOJO_EDK_PORTS) MergePortEvent : public Event { +class COMPONENT_EXPORT(MOJO_CORE_PORTS) MergePortEvent : public Event { public: MergePortEvent(const PortName& port_name, const PortName& new_port_name, @@ -276,7 +278,7 @@ class COMPONENT_EXPORT(MOJO_EDK_PORTS) MergePortEvent : public Event { }; } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_PORTS_EVENT_H_ +#endif // MOJO_CORE_PORTS_EVENT_H_ diff --git a/chromium/mojo/edk/system/ports/message_filter.h b/chromium/mojo/core/ports/message_filter.h index 1b1f2ce2167..f09903ffca4 100644 --- a/chromium/mojo/edk/system/ports/message_filter.h +++ b/chromium/mojo/core/ports/message_filter.h @@ -2,11 +2,11 @@ // 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_ +#ifndef MOJO_CORE_PORTS_MESSAGE_FILTER_H_ +#define MOJO_CORE_PORTS_MESSAGE_FILTER_H_ namespace mojo { -namespace edk { +namespace core { namespace ports { class UserMessageEvent; @@ -23,7 +23,7 @@ class MessageFilter { }; } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_PORTS_MESSAGE_FILTER_H_ +#endif // MOJO_CORE_PORTS_MESSAGE_FILTER_H_ diff --git a/chromium/mojo/edk/system/ports/message_queue.cc b/chromium/mojo/core/ports/message_queue.cc index 859636243d1..074b35a46da 100644 --- a/chromium/mojo/edk/system/ports/message_queue.cc +++ b/chromium/mojo/core/ports/message_queue.cc @@ -2,15 +2,15 @@ // 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 "mojo/core/ports/message_queue.h" #include <algorithm> #include "base/logging.h" -#include "mojo/edk/system/ports/message_filter.h" +#include "mojo/core/ports/message_filter.h" namespace mojo { -namespace edk { +namespace core { namespace ports { // Used by std::{push,pop}_heap functions @@ -50,6 +50,7 @@ void MessageQueue::GetNextMessage(std::unique_ptr<UserMessageEvent>* message, std::pop_heap(heap_.begin(), heap_.end()); *message = std::move(heap_.back()); + total_queued_bytes_ -= (*message)->GetSizeIfSerialized(); heap_.pop_back(); next_sequence_num_++; @@ -59,6 +60,7 @@ void MessageQueue::AcceptMessage(std::unique_ptr<UserMessageEvent> message, bool* has_next_message) { // TODO: Handle sequence number roll-over. + total_queued_bytes_ += message->GetSizeIfSerialized(); heap_.emplace_back(std::move(message)); std::push_heap(heap_.begin(), heap_.end()); @@ -72,8 +74,9 @@ void MessageQueue::AcceptMessage(std::unique_ptr<UserMessageEvent> message, void MessageQueue::TakeAllMessages( std::vector<std::unique_ptr<UserMessageEvent>>* messages) { *messages = std::move(heap_); + total_queued_bytes_ = 0; } } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/ports/message_queue.h b/chromium/mojo/core/ports/message_queue.h index 829d217213e..1d34222c5eb 100644 --- a/chromium/mojo/edk/system/ports/message_queue.h +++ b/chromium/mojo/core/ports/message_queue.h @@ -2,8 +2,8 @@ // 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_ +#ifndef MOJO_CORE_PORTS_MESSAGE_QUEUE_H_ +#define MOJO_CORE_PORTS_MESSAGE_QUEUE_H_ #include <stdint.h> @@ -13,10 +13,10 @@ #include "base/component_export.h" #include "base/macros.h" -#include "mojo/edk/system/ports/event.h" +#include "mojo/core/ports/event.h" namespace mojo { -namespace edk { +namespace core { namespace ports { constexpr uint64_t kInitialSequenceNum = 1; @@ -28,7 +28,7 @@ class MessageFilter; // 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 COMPONENT_EXPORT(MOJO_EDK_PORTS) MessageQueue { +class COMPONENT_EXPORT(MOJO_CORE_PORTS) MessageQueue { public: explicit MessageQueue(); explicit MessageQueue(uint64_t next_sequence_num); @@ -62,16 +62,25 @@ class COMPONENT_EXPORT(MOJO_EDK_PORTS) MessageQueue { void TakeAllMessages( std::vector<std::unique_ptr<UserMessageEvent>>* messages); + // The number of messages queued here, regardless of whether the next expected + // message has arrived yet. + size_t queued_message_count() const { return heap_.size(); } + + // The aggregate memory size in bytes of all messages queued here, regardless + // of whether the next expected message has arrived yet. + size_t queued_num_bytes() const { return total_queued_bytes_; } + private: std::vector<std::unique_ptr<UserMessageEvent>> heap_; uint64_t next_sequence_num_; bool signalable_ = true; + size_t total_queued_bytes_ = 0; DISALLOW_COPY_AND_ASSIGN(MessageQueue); }; } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_PORTS_MESSAGE_QUEUE_H_ +#endif // MOJO_CORE_PORTS_MESSAGE_QUEUE_H_ diff --git a/chromium/mojo/edk/system/ports/name.cc b/chromium/mojo/core/ports/name.cc index fe03b4ba704..332a0a0826d 100644 --- a/chromium/mojo/edk/system/ports/name.cc +++ b/chromium/mojo/core/ports/name.cc @@ -2,10 +2,10 @@ // 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" +#include "mojo/core/ports/name.h" namespace mojo { -namespace edk { +namespace core { namespace ports { const PortName kInvalidPortName = {0, 0}; @@ -22,5 +22,5 @@ std::ostream& operator<<(std::ostream& stream, const Name& name) { } } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/ports/name.h b/chromium/mojo/core/ports/name.h index 80d7ffc38d5..415ba65a958 100644 --- a/chromium/mojo/edk/system/ports/name.h +++ b/chromium/mojo/core/ports/name.h @@ -2,8 +2,8 @@ // 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_ +#ifndef MOJO_CORE_PORTS_NAME_H_ +#define MOJO_CORE_PORTS_NAME_H_ #include <stdint.h> @@ -14,10 +14,10 @@ #include "base/hash.h" namespace mojo { -namespace edk { +namespace core { namespace ports { -struct COMPONENT_EXPORT(MOJO_EDK_PORTS) Name { +struct COMPONENT_EXPORT(MOJO_CORE_PORTS) Name { Name(uint64_t v1, uint64_t v2) : v1(v1), v2(v2) {} uint64_t v1, v2; }; @@ -34,43 +34,43 @@ inline bool operator<(const Name& a, const Name& b) { return std::tie(a.v1, a.v2) < std::tie(b.v1, b.v2); } -COMPONENT_EXPORT(MOJO_EDK_PORTS) +COMPONENT_EXPORT(MOJO_CORE_PORTS) std::ostream& operator<<(std::ostream& stream, const Name& name); -struct COMPONENT_EXPORT(MOJO_EDK_PORTS) PortName : Name { +struct COMPONENT_EXPORT(MOJO_CORE_PORTS) PortName : Name { PortName() : Name(0, 0) {} PortName(uint64_t v1, uint64_t v2) : Name(v1, v2) {} }; -extern COMPONENT_EXPORT(MOJO_EDK_PORTS) const PortName kInvalidPortName; +extern COMPONENT_EXPORT(MOJO_CORE_PORTS) const PortName kInvalidPortName; -struct COMPONENT_EXPORT(MOJO_EDK_PORTS) NodeName : Name { +struct COMPONENT_EXPORT(MOJO_CORE_PORTS) NodeName : Name { NodeName() : Name(0, 0) {} NodeName(uint64_t v1, uint64_t v2) : Name(v1, v2) {} }; -extern COMPONENT_EXPORT(MOJO_EDK_PORTS) const NodeName kInvalidNodeName; +extern COMPONENT_EXPORT(MOJO_CORE_PORTS) const NodeName kInvalidNodeName; } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo namespace std { template <> -struct COMPONENT_EXPORT(MOJO_EDK_PORTS) hash<mojo::edk::ports::PortName> { - std::size_t operator()(const mojo::edk::ports::PortName& name) const { +struct COMPONENT_EXPORT(MOJO_CORE_PORTS) hash<mojo::core::ports::PortName> { + std::size_t operator()(const mojo::core::ports::PortName& name) const { return base::HashInts64(name.v1, name.v2); } }; template <> -struct COMPONENT_EXPORT(MOJO_EDK_PORTS) hash<mojo::edk::ports::NodeName> { - std::size_t operator()(const mojo::edk::ports::NodeName& name) const { +struct COMPONENT_EXPORT(MOJO_CORE_PORTS) hash<mojo::core::ports::NodeName> { + std::size_t operator()(const mojo::core::ports::NodeName& name) const { return base::HashInts64(name.v1, name.v2); } }; } // namespace std -#endif // MOJO_EDK_SYSTEM_PORTS_NAME_H_ +#endif // MOJO_CORE_PORTS_NAME_H_ diff --git a/chromium/mojo/edk/system/ports/name_unittest.cc b/chromium/mojo/core/ports/name_unittest.cc index 7e4d82b7651..520295c5807 100644 --- a/chromium/mojo/edk/system/ports/name_unittest.cc +++ b/chromium/mojo/core/ports/name_unittest.cc @@ -2,12 +2,12 @@ // 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" +#include "mojo/core/ports/name.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo { -namespace edk { +namespace core { namespace ports { namespace test { @@ -71,5 +71,5 @@ TEST(NameTest, NodeNameChecks) { } // namespace test } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/ports/node.cc b/chromium/mojo/core/ports/node.cc index 78365e4d856..7ed191c7886 100644 --- a/chromium/mojo/edk/system/ports/node.cc +++ b/chromium/mojo/core/ports/node.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/ports/node.h" #include <string.h> @@ -18,9 +18,9 @@ #include "base/synchronization/lock.h" #include "base/threading/thread_local.h" #include "build/build_config.h" -#include "mojo/edk/system/ports/event.h" -#include "mojo/edk/system/ports/node_delegate.h" -#include "mojo/edk/system/ports/port_locker.h" +#include "mojo/core/ports/event.h" +#include "mojo/core/ports/node_delegate.h" +#include "mojo/core/ports/port_locker.h" #if !defined(OS_NACL) #include "crypto/random.h" @@ -29,7 +29,7 @@ #endif namespace mojo { -namespace edk { +namespace core { namespace ports { namespace { @@ -305,6 +305,9 @@ int Node::GetStatus(const PortRef& port_ref, PortStatus* port_status) { port_status->receiving_messages = CanAcceptMoreMessages(port); port_status->peer_closed = port->peer_closed; port_status->peer_remote = port->peer_node_name != name_; + port_status->queued_message_count = + port->message_queue.queued_message_count(); + port_status->queued_num_bytes = port->message_queue.queued_num_bytes(); return OK; } @@ -1381,5 +1384,5 @@ void Node::DelegateHolder::EnsureSafeDelegateAccess() const { #endif } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/ports/node.h b/chromium/mojo/core/ports/node.h index ec4bbbcd439..a9576bf2660 100644 --- a/chromium/mojo/edk/system/ports/node.h +++ b/chromium/mojo/core/ports/node.h @@ -2,8 +2,8 @@ // 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_ +#ifndef MOJO_CORE_PORTS_NODE_H_ +#define MOJO_CORE_PORTS_NODE_H_ #include <stddef.h> #include <stdint.h> @@ -15,14 +15,14 @@ #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/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" +#include "mojo/core/ports/event.h" +#include "mojo/core/ports/name.h" +#include "mojo/core/ports/port.h" +#include "mojo/core/ports/port_ref.h" +#include "mojo/core/ports/user_data.h" namespace mojo { -namespace edk { +namespace core { namespace ports { enum : int { @@ -41,6 +41,8 @@ struct PortStatus { bool receiving_messages; bool peer_closed; bool peer_remote; + size_t queued_message_count; + size_t queued_num_bytes; }; class MessageFilter; @@ -62,7 +64,7 @@ class NodeDelegate; // by Nodes to coordinate Port behavior and lifetime within and across Nodes. // See Event documentation for description of different types of events used by // a Node to coordinate behavior. -class COMPONENT_EXPORT(MOJO_EDK_PORTS) Node { +class COMPONENT_EXPORT(MOJO_CORE_PORTS) Node { public: enum class ShutdownPolicy { DONT_ALLOW_LOCAL_PORTS, @@ -242,7 +244,7 @@ class COMPONENT_EXPORT(MOJO_EDK_PORTS) Node { }; } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_PORTS_NODE_H_ +#endif // MOJO_CORE_PORTS_NODE_H_ diff --git a/chromium/mojo/edk/system/ports/node_delegate.h b/chromium/mojo/core/ports/node_delegate.h index 3c7550a608e..afe1c4cd975 100644 --- a/chromium/mojo/edk/system/ports/node_delegate.h +++ b/chromium/mojo/core/ports/node_delegate.h @@ -2,17 +2,17 @@ // 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_ +#ifndef MOJO_CORE_PORTS_NODE_DELEGATE_H_ +#define MOJO_CORE_PORTS_NODE_DELEGATE_H_ #include <stddef.h> -#include "mojo/edk/system/ports/event.h" -#include "mojo/edk/system/ports/name.h" -#include "mojo/edk/system/ports/port_ref.h" +#include "mojo/core/ports/event.h" +#include "mojo/core/ports/name.h" +#include "mojo/core/ports/port_ref.h" namespace mojo { -namespace edk { +namespace core { namespace ports { class NodeDelegate { @@ -32,7 +32,7 @@ class NodeDelegate { }; } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_PORTS_NODE_DELEGATE_H_ +#endif // MOJO_CORE_PORTS_NODE_DELEGATE_H_ diff --git a/chromium/mojo/edk/system/ports/port.cc b/chromium/mojo/core/ports/port.cc index e4403aed788..71869790218 100644 --- a/chromium/mojo/edk/system/ports/port.cc +++ b/chromium/mojo/core/ports/port.cc @@ -2,10 +2,10 @@ // 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" +#include "mojo/core/ports/port.h" namespace mojo { -namespace edk { +namespace core { namespace ports { Port::Port(uint64_t next_sequence_num_to_send, @@ -20,5 +20,5 @@ Port::Port(uint64_t next_sequence_num_to_send, Port::~Port() {} } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/ports/port.h b/chromium/mojo/core/ports/port.h index e10bfebe41d..d1a825e2c62 100644 --- a/chromium/mojo/edk/system/ports/port.h +++ b/chromium/mojo/core/ports/port.h @@ -2,8 +2,8 @@ // 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_ +#ifndef MOJO_CORE_PORTS_PORT_H_ +#define MOJO_CORE_PORTS_PORT_H_ #include <memory> #include <queue> @@ -13,12 +13,12 @@ #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_queue.h" -#include "mojo/edk/system/ports/user_data.h" +#include "mojo/core/ports/event.h" +#include "mojo/core/ports/message_queue.h" +#include "mojo/core/ports/user_data.h" namespace mojo { -namespace edk { +namespace core { namespace ports { class PortLocker; @@ -169,7 +169,7 @@ class Port : public base::RefCountedThreadSafe<Port> { }; } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_PORTS_PORT_H_ +#endif // MOJO_CORE_PORTS_PORT_H_ diff --git a/chromium/mojo/edk/system/ports/port_locker.cc b/chromium/mojo/core/ports/port_locker.cc index e84d0d00b23..880492332dd 100644 --- a/chromium/mojo/edk/system/ports/port_locker.cc +++ b/chromium/mojo/core/ports/port_locker.cc @@ -2,18 +2,18 @@ // 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_locker.h" +#include "mojo/core/ports/port_locker.h" #include <algorithm> -#include "mojo/edk/system/ports/port.h" +#include "mojo/core/ports/port.h" #if DCHECK_IS_ON() #include "base/threading/thread_local.h" #endif namespace mojo { -namespace edk { +namespace core { namespace ports { namespace { @@ -70,5 +70,5 @@ SinglePortLocker::SinglePortLocker(const PortRef* port_ref) SinglePortLocker::~SinglePortLocker() = default; } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/ports/port_locker.h b/chromium/mojo/core/ports/port_locker.h index 38782068b0e..0da26545635 100644 --- a/chromium/mojo/edk/system/ports/port_locker.h +++ b/chromium/mojo/core/ports/port_locker.h @@ -2,16 +2,16 @@ // 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_LOCKER_H_ -#define MOJO_EDK_SYSTEM_PORTS_PORT_LOCKER_H_ +#ifndef MOJO_CORE_PORTS_PORT_LOCKER_H_ +#define MOJO_CORE_PORTS_PORT_LOCKER_H_ #include <stdint.h> #include "base/macros.h" -#include "mojo/edk/system/ports/port_ref.h" +#include "mojo/core/ports/port_ref.h" namespace mojo { -namespace edk { +namespace core { namespace ports { class Port; @@ -80,7 +80,7 @@ class SinglePortLocker { }; } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_PORTS_PORT_LOCKER_H_ +#endif // MOJO_CORE_PORTS_PORT_LOCKER_H_ diff --git a/chromium/mojo/edk/system/ports/port_ref.cc b/chromium/mojo/core/ports/port_ref.cc index 66e48c2ecbe..a3d312bc399 100644 --- a/chromium/mojo/edk/system/ports/port_ref.cc +++ b/chromium/mojo/core/ports/port_ref.cc @@ -2,19 +2,17 @@ // 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/core/ports/port_ref.h" -#include "mojo/edk/system/ports/port.h" +#include "mojo/core/ports/port.h" namespace mojo { -namespace edk { +namespace core { namespace ports { -PortRef::~PortRef() { -} +PortRef::~PortRef() {} -PortRef::PortRef() { -} +PortRef::PortRef() {} PortRef::PortRef(const PortName& name, scoped_refptr<Port> port) : name_(name), port_(std::move(port)) {} @@ -28,5 +26,5 @@ PortRef& PortRef::operator=(const PortRef& other) = default; PortRef& PortRef::operator=(PortRef&& other) = default; } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/ports/port_ref.h b/chromium/mojo/core/ports/port_ref.h index 9bafe38f474..b63d6cfcd00 100644 --- a/chromium/mojo/edk/system/ports/port_ref.h +++ b/chromium/mojo/core/ports/port_ref.h @@ -2,22 +2,22 @@ // 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_ +#ifndef MOJO_CORE_PORTS_PORT_REF_H_ +#define MOJO_CORE_PORTS_PORT_REF_H_ #include "base/component_export.h" #include "base/logging.h" #include "base/memory/ref_counted.h" -#include "mojo/edk/system/ports/name.h" +#include "mojo/core/ports/name.h" namespace mojo { -namespace edk { +namespace core { namespace ports { class Port; class PortLocker; -class COMPONENT_EXPORT(MOJO_EDK_PORTS) PortRef { +class COMPONENT_EXPORT(MOJO_CORE_PORTS) PortRef { public: ~PortRef(); PortRef(); @@ -43,7 +43,7 @@ class COMPONENT_EXPORT(MOJO_EDK_PORTS) PortRef { }; } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_PORTS_PORT_REF_H_ +#endif // MOJO_CORE_PORTS_PORT_REF_H_ diff --git a/chromium/mojo/edk/system/ports/ports_unittest.cc b/chromium/mojo/core/ports/ports_unittest.cc index f04ac49f609..3d18bb680fa 100644 --- a/chromium/mojo/edk/system/ports/ports_unittest.cc +++ b/chromium/mojo/core/ports/ports_unittest.cc @@ -22,14 +22,14 @@ #include "base/synchronization/waitable_event.h" #include "base/test/scoped_task_environment.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 "mojo/edk/system/ports/user_message.h" +#include "mojo/core/ports/event.h" +#include "mojo/core/ports/node.h" +#include "mojo/core/ports/node_delegate.h" +#include "mojo/core/ports/user_message.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo { -namespace edk { +namespace core { namespace ports { namespace test { @@ -1640,5 +1640,5 @@ TEST_F(PortsTest, RetransmitUserMessageEvents) { } // namespace test } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/ports/user_data.h b/chromium/mojo/core/ports/user_data.h index 73e7d17b328..e18d9b79d5f 100644 --- a/chromium/mojo/edk/system/ports/user_data.h +++ b/chromium/mojo/core/ports/user_data.h @@ -2,13 +2,13 @@ // 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_ +#ifndef MOJO_CORE_PORTS_USER_DATA_H_ +#define MOJO_CORE_PORTS_USER_DATA_H_ #include "base/memory/ref_counted.h" namespace mojo { -namespace edk { +namespace core { namespace ports { class UserData : public base::RefCountedThreadSafe<UserData> { @@ -19,7 +19,7 @@ class UserData : public base::RefCountedThreadSafe<UserData> { }; } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_PORTS_USER_DATA_H_ +#endif // MOJO_CORE_PORTS_USER_DATA_H_ diff --git a/chromium/mojo/edk/system/ports/user_message.cc b/chromium/mojo/core/ports/user_message.cc index 1976dcb8130..aa16376c9c5 100644 --- a/chromium/mojo/edk/system/ports/user_message.cc +++ b/chromium/mojo/core/ports/user_message.cc @@ -2,10 +2,10 @@ // 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/user_message.h" +#include "mojo/core/ports/user_message.h" namespace mojo { -namespace edk { +namespace core { namespace ports { UserMessage::UserMessage(const TypeInfo* type_info) : type_info_(type_info) {} @@ -16,6 +16,10 @@ bool UserMessage::WillBeRoutedExternally() { return true; } +size_t UserMessage::GetSizeIfSerialized() const { + return 0; +} + } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/ports/user_message.h b/chromium/mojo/core/ports/user_message.h index b4414b2de21..34d26887202 100644 --- a/chromium/mojo/edk/system/ports/user_message.h +++ b/chromium/mojo/core/ports/user_message.h @@ -2,14 +2,14 @@ // 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_MESSAGE_H_ -#define MOJO_EDK_SYSTEM_PORTS_USER_MESSAGE_H_ +#ifndef MOJO_CORE_PORTS_USER_MESSAGE_H_ +#define MOJO_CORE_PORTS_USER_MESSAGE_H_ #include "base/component_export.h" #include "base/macros.h" namespace mojo { -namespace edk { +namespace core { namespace ports { // Base type to use for any embedder-defined user message implementation. This @@ -22,7 +22,7 @@ namespace ports { // |kUserMessageTypeInfo| and pass its address down to the UserMessage // constructor. The type of a UserMessage can then be dynamically inspected by // comparing |type_info()| to any subclass's |&kUserMessageTypeInfo|. -class COMPONENT_EXPORT(MOJO_EDK_PORTS) UserMessage { +class COMPONENT_EXPORT(MOJO_CORE_PORTS) UserMessage { public: struct TypeInfo {}; @@ -39,6 +39,10 @@ class COMPONENT_EXPORT(MOJO_EDK_PORTS) UserMessage { // message event will be destroyed without further routing. virtual bool WillBeRoutedExternally(); + // Returns the size in bytes of this message iff it's serialized. Zero + // otherwise. + virtual size_t GetSizeIfSerialized() const; + private: const TypeInfo* const type_info_; @@ -46,7 +50,7 @@ class COMPONENT_EXPORT(MOJO_EDK_PORTS) UserMessage { }; } // namespace ports -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_PORTS_USER_MESSAGE_H_ +#endif // MOJO_CORE_PORTS_USER_MESSAGE_H_ diff --git a/chromium/mojo/core/quota_unittest.cc b/chromium/mojo/core/quota_unittest.cc new file mode 100644 index 00000000000..bc26baf151a --- /dev/null +++ b/chromium/mojo/core/quota_unittest.cc @@ -0,0 +1,314 @@ +// Copyright 2018 The Chromium 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> + +#include "mojo/core/test/mojo_test_base.h" +#include "mojo/public/c/system/quota.h" + +namespace mojo { +namespace core { +namespace { + +using QuotaTest = test::MojoTestBase; + +void QuotaExceededEventHandler(const MojoTrapEvent* event) { + // Always treat trigger context as the address of a bool to set to |true|. + if (event->result == MOJO_RESULT_OK) + *reinterpret_cast<bool*>(event->trigger_context) = true; +} + +TEST_F(QuotaTest, InvalidArguments) { + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoSetQuota(MOJO_HANDLE_INVALID, + MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, 2, nullptr)); + + const MojoQuotaType kInvalidQuotaType = 0xfffffffful; + MojoHandle message_pipe0, message_pipe1; + CreateMessagePipe(&message_pipe0, &message_pipe1); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoSetQuota(message_pipe0, kInvalidQuotaType, 0, nullptr)); + + const MojoSetQuotaOptions kInvalidSetQuotaOptions = {0}; + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoSetQuota(message_pipe0, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, 0, + &kInvalidSetQuotaOptions)); + + uint64_t limit = 0; + uint64_t usage = 0; + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoQueryQuota(message_pipe0, kInvalidQuotaType, nullptr, &limit, + &usage)); + + const MojoQueryQuotaOptions kInvalidQueryQuotaOptions = {0}; + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoQueryQuota(message_pipe0, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, + &kInvalidQueryQuotaOptions, &limit, &usage)); + + MojoClose(message_pipe0); + MojoClose(message_pipe1); + + MojoHandle producer, consumer; + CreateDataPipe(&producer, &consumer, 1); + EXPECT_EQ( + MOJO_RESULT_INVALID_ARGUMENT, + MojoSetQuota(producer, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, 0, nullptr)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoSetQuota(producer, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE, 0, + nullptr)); + EXPECT_EQ( + MOJO_RESULT_INVALID_ARGUMENT, + MojoSetQuota(consumer, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, 0, nullptr)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoSetQuota(consumer, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE, 0, + nullptr)); + MojoClose(producer); + MojoClose(consumer); +} + +TEST_F(QuotaTest, BasicReceiveQueueLength) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + uint64_t limit = 0; + uint64_t usage = 0; + EXPECT_EQ(MOJO_RESULT_OK, + MojoQueryQuota(a, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, nullptr, + &limit, &usage)); + EXPECT_EQ(MOJO_QUOTA_LIMIT_NONE, limit); + EXPECT_EQ(0u, usage); + + const uint64_t kTestLimit = 42; + EXPECT_EQ(MOJO_RESULT_OK, + MojoSetQuota(a, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, kTestLimit, + nullptr)); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoQueryQuota(a, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, nullptr, + &limit, &usage)); + EXPECT_EQ(kTestLimit, limit); + EXPECT_EQ(0u, usage); + + const std::string kTestMessage = "doot"; + WriteMessage(b, kTestMessage); + WaitForSignals(a, MOJO_HANDLE_SIGNAL_READABLE); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoQueryQuota(a, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, nullptr, + &limit, &usage)); + EXPECT_EQ(kTestLimit, limit); + EXPECT_EQ(1u, usage); +} + +TEST_F(QuotaTest, BasicReceiveQueueMemorySize) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + uint64_t limit = 0; + uint64_t usage = 0; + EXPECT_EQ(MOJO_RESULT_OK, + MojoQueryQuota(a, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE, + nullptr, &limit, &usage)); + EXPECT_EQ(MOJO_QUOTA_LIMIT_NONE, limit); + EXPECT_EQ(0u, usage); + + const uint64_t kTestLimit = 42; + EXPECT_EQ(MOJO_RESULT_OK, + MojoSetQuota(a, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE, + kTestLimit, nullptr)); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoQueryQuota(a, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE, + nullptr, &limit, &usage)); + EXPECT_EQ(kTestLimit, limit); + EXPECT_EQ(0u, usage); + + const std::string kTestMessage = "doot"; + WriteMessage(b, kTestMessage); + WaitForSignals(a, MOJO_HANDLE_SIGNAL_READABLE); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoQueryQuota(a, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE, + nullptr, &limit, &usage)); + EXPECT_EQ(kTestLimit, limit); + EXPECT_EQ(usage, kTestMessage.size()); + + MojoClose(a); + MojoClose(b); +} + +TEST_F(QuotaTest, ReceiveQueueLengthLimitExceeded) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + const uint64_t kMaxMessages = 1; + EXPECT_EQ(MOJO_RESULT_OK, + MojoSetQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, kMaxMessages, + nullptr)); + + MojoHandleSignalsState signals; + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals)); + EXPECT_FALSE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); + + const std::string kTestMessage = "this message is lit, fam"; + WriteMessage(a, kTestMessage); + WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE); + + uint64_t limit = 0; + uint64_t usage = 0; + EXPECT_EQ(MOJO_RESULT_OK, + MojoQueryQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, nullptr, + &limit, &usage)); + EXPECT_EQ(kMaxMessages, limit); + EXPECT_EQ(1u, usage); + + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals)); + EXPECT_FALSE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); + + // Push the endpoint over quota and ensure that it signals accordingly. + WriteMessage(a, kTestMessage); + WaitForSignals(b, MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); + + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals)); + EXPECT_TRUE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoQueryQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, nullptr, + &limit, &usage)); + EXPECT_EQ(kMaxMessages, limit); + EXPECT_EQ(2u, usage); + + // Read a message and wait for QUOTA_EXCEEDED to go back low. + EXPECT_EQ(kTestMessage, ReadMessage(b)); + WaitForSignals(b, MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED, + MOJO_TRIGGER_CONDITION_SIGNALS_UNSATISFIED); + + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals)); + EXPECT_FALSE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoQueryQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, nullptr, + &limit, &usage)); + EXPECT_EQ(kMaxMessages, limit); + EXPECT_EQ(1u, usage); + + MojoClose(a); + MojoClose(b); +} + +TEST_F(QuotaTest, ReceiveQueueMemorySizeLimitExceeded) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + const uint64_t kMaxMessageBytes = 6; + EXPECT_EQ(MOJO_RESULT_OK, + MojoSetQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE, + kMaxMessageBytes, nullptr)); + + MojoHandleSignalsState signals; + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals)); + EXPECT_FALSE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); + + const std::string kTestMessage = "four"; + WriteMessage(a, kTestMessage); + WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE); + + uint64_t limit = 0; + uint64_t usage = 0; + EXPECT_EQ(MOJO_RESULT_OK, + MojoQueryQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE, + nullptr, &limit, &usage)); + EXPECT_EQ(kMaxMessageBytes, limit); + EXPECT_EQ(kTestMessage.size(), usage); + + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals)); + EXPECT_FALSE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); + + // Push the endpoint over quota and ensure that it signals accordingly. + WriteMessage(a, kTestMessage); + WaitForSignals(b, MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); + + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals)); + EXPECT_TRUE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoQueryQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE, + nullptr, &limit, &usage)); + EXPECT_EQ(kMaxMessageBytes, limit); + EXPECT_EQ(kTestMessage.size() * 2, usage); + + // Read a message and wait for QUOTA_EXCEEDED to go back low. + EXPECT_EQ(kTestMessage, ReadMessage(b)); + WaitForSignals(b, MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED, + MOJO_TRIGGER_CONDITION_SIGNALS_UNSATISFIED); + + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals)); + EXPECT_FALSE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoQueryQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE, + nullptr, &limit, &usage)); + EXPECT_EQ(kMaxMessageBytes, limit); + EXPECT_EQ(kTestMessage.size(), usage); + + MojoClose(a); + MojoClose(b); +} + +TEST_F(QuotaTest, TrapQuotaExceeded) { + // Simple sanity check to verify that QUOTA_EXCEEDED signals can be trapped + // like any other signals. + + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + const uint64_t kMaxMessages = 42; + EXPECT_EQ(MOJO_RESULT_OK, + MojoSetQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, kMaxMessages, + nullptr)); + + bool signal_event_fired = false; + MojoHandle quota_trap; + EXPECT_EQ(MOJO_RESULT_OK, + MojoCreateTrap(&QuotaExceededEventHandler, nullptr, "a_trap)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(quota_trap, b, MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + reinterpret_cast<uintptr_t>(&signal_event_fired), + nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(quota_trap, nullptr, nullptr, nullptr)); + + const std::string kTestMessage("sup"); + for (uint64_t i = 0; i < kMaxMessages; ++i) + WriteMessage(a, kTestMessage); + + // We're at quota but not yet over. + MojoHandleSignalsState signals; + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals)); + EXPECT_FALSE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); + EXPECT_FALSE(signal_event_fired); + + // Push over quota. The event handler should be invoked before this returns. + WriteMessage(a, kTestMessage); + EXPECT_TRUE(signal_event_fired); + + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals)); + EXPECT_TRUE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); + + uint64_t limit = 0; + uint64_t usage = 0; + EXPECT_EQ(MOJO_RESULT_OK, + MojoQueryQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, nullptr, + &limit, &usage)); + EXPECT_EQ(kMaxMessages, limit); + EXPECT_EQ(kMaxMessages + 1, usage); + + MojoClose(quota_trap); + MojoClose(a); + MojoClose(b); +} + +} // namespace +} // namespace core +} // namespace mojo diff --git a/chromium/mojo/edk/system/request_context.cc b/chromium/mojo/core/request_context.cc index 4de4343fa2b..0a481af7291 100644 --- a/chromium/mojo/edk/system/request_context.cc +++ b/chromium/mojo/core/request_context.cc @@ -2,14 +2,14 @@ // 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 "mojo/core/request_context.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/threading/thread_local.h" namespace mojo { -namespace edk { +namespace core { namespace { @@ -55,8 +55,14 @@ RequestContext::~RequestContext() { 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_); + // triggered by the callback invocation. Note that while it would be safe + // to inherit |source_| from the perspective of Mojo core re-entrancy, + // upper application layers may use the flag as a signal to allow + // synchronous event dispatch and in turn shoot themselves in the foot + // with e.g. mutually recursive event handlers. We avoid that by + // treating all nested trap events as if they originated from a local API + // call even if this is a system RequestContext. + RequestContext inner_context(Source::LOCAL_API_CALL); watch->InvokeCallback(MOJO_RESULT_CANCELLED, closed_state, flags); } @@ -106,5 +112,5 @@ RequestContext::WatchNotifyFinalizer::WatchNotifyFinalizer( RequestContext::WatchNotifyFinalizer::~WatchNotifyFinalizer() {} -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/request_context.h b/chromium/mojo/core/request_context.h index d1f43bdfbd1..89988f27d30 100644 --- a/chromium/mojo/edk/system/request_context.h +++ b/chromium/mojo/core/request_context.h @@ -2,21 +2,22 @@ // 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_ +#ifndef MOJO_CORE_REQUEST_CONTEXT_H_ +#define MOJO_CORE_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" +#include "mojo/core/handle_signals_state.h" +#include "mojo/core/system_impl_export.h" +#include "mojo/core/watch.h" namespace base { -template<typename T> class ThreadLocalPointer; +template <typename T> +class ThreadLocalPointer; } namespace mojo { -namespace edk { +namespace core { // 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 @@ -101,7 +102,7 @@ class MOJO_SYSTEM_IMPL_EXPORT RequestContext { DISALLOW_COPY_AND_ASSIGN(RequestContext); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_REQUEST_CONTEXT_H_ +#endif // MOJO_CORE_REQUEST_CONTEXT_H_ diff --git a/chromium/mojo/edk/run_all_core_unittests.cc b/chromium/mojo/core/run_all_core_unittests.cc index 92dcafc9619..92dcafc9619 100644 --- a/chromium/mojo/edk/run_all_core_unittests.cc +++ b/chromium/mojo/core/run_all_core_unittests.cc diff --git a/chromium/mojo/core/scoped_process_handle.cc b/chromium/mojo/core/scoped_process_handle.cc new file mode 100644 index 00000000000..65dfcf78aa6 --- /dev/null +++ b/chromium/mojo/core/scoped_process_handle.cc @@ -0,0 +1,90 @@ +// Copyright 2018 The Chromium 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/core/scoped_process_handle.h" + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include <windows.h> +#endif + +namespace mojo { +namespace core { + +namespace { + +base::ProcessHandle GetCurrentProcessHandle() { +#if defined(OS_NACL_NONSFI) + // Doesn't really matter, it's not going to be used for anything interesting + // under NaCl. + return 1; +#else + return base::GetCurrentProcessHandle(); +#endif +} + +} // namespace + +ScopedProcessHandle::ScopedProcessHandle() = default; + +ScopedProcessHandle::ScopedProcessHandle(base::ProcessHandle handle) + : handle_(handle) { + DCHECK_NE(handle, GetCurrentProcessHandle()); +} + +ScopedProcessHandle::ScopedProcessHandle(ScopedProcessHandle&&) = default; + +ScopedProcessHandle::~ScopedProcessHandle() = default; + +// static +ScopedProcessHandle ScopedProcessHandle::CloneFrom(base::ProcessHandle handle) { + DCHECK_NE(handle, GetCurrentProcessHandle()); + if (handle == base::kNullProcessHandle) + return ScopedProcessHandle(); + +#if defined(OS_WIN) + BOOL ok = ::DuplicateHandle(GetCurrentProcessHandle(), handle, + GetCurrentProcessHandle(), &handle, 0, FALSE, + DUPLICATE_SAME_ACCESS); + DCHECK(ok); +#endif + return ScopedProcessHandle(handle); +} + +ScopedProcessHandle& ScopedProcessHandle::operator=(ScopedProcessHandle&&) = + default; + +bool ScopedProcessHandle::is_valid() const { +#if defined(OS_WIN) + return handle_.IsValid(); +#else + return handle_ != base::kNullProcessHandle; +#endif +} + +base::ProcessHandle ScopedProcessHandle::get() const { +#if defined(OS_WIN) + return handle_.Get(); +#else + return handle_; +#endif +} + +base::ProcessHandle ScopedProcessHandle::release() { +#if defined(OS_WIN) + return handle_.Take(); +#else + return handle_; +#endif +} + +ScopedProcessHandle ScopedProcessHandle::Clone() const { + if (is_valid()) + return CloneFrom(get()); + return ScopedProcessHandle(); +} + +} // namespace core +} // namespace mojo diff --git a/chromium/mojo/edk/system/scoped_process_handle.h b/chromium/mojo/core/scoped_process_handle.h index dbefdfe0593..4677145d9f9 100644 --- a/chromium/mojo/edk/system/scoped_process_handle.h +++ b/chromium/mojo/core/scoped_process_handle.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_EDK_SYSTEM_SCOPED_PROCESS_HANDLE_H_ -#define MOJO_EDK_SYSTEM_SCOPED_PROCESS_HANDLE_H_ +#ifndef MOJO_CORE_SCOPED_PROCESS_HANDLE_H_ +#define MOJO_CORE_SCOPED_PROCESS_HANDLE_H_ #include "base/macros.h" #include "base/process/process_handle.h" @@ -14,7 +14,7 @@ #endif namespace mojo { -namespace edk { +namespace core { // Wraps a |base::ProcessHandle| with additional scoped lifetime semantics on // applicable platforms. For platforms where process handles aren't ownable @@ -23,6 +23,10 @@ namespace edk { // This essentially exists to support passing around process handles internally // in a generic way while also supporting Windows process handle ownership // semantics. +// +// A ScopedProcessHandle will never refer to the current process, and +// constructing a ScopedProcessHandle over the current process's handle is +// considered an error. class ScopedProcessHandle { public: ScopedProcessHandle(); @@ -39,29 +43,9 @@ class ScopedProcessHandle { ScopedProcessHandle& operator=(ScopedProcessHandle&&); - bool is_valid() const { -#if defined(OS_WIN) - return handle_.IsValid(); -#else - return handle_ != base::kNullProcessHandle; -#endif - } - - base::ProcessHandle get() const { -#if defined(OS_WIN) - return handle_.Get(); -#else - return handle_; -#endif - } - - base::ProcessHandle release() { -#if defined(OS_WIN) - return handle_.Take(); -#else - return handle_; -#endif - } + bool is_valid() const; + base::ProcessHandle get() const; + base::ProcessHandle release(); ScopedProcessHandle Clone() const; @@ -75,7 +59,7 @@ class ScopedProcessHandle { DISALLOW_COPY_AND_ASSIGN(ScopedProcessHandle); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_SCOPED_PROCESS_HANDLE_H_ +#endif // MOJO_CORE_SCOPED_PROCESS_HANDLE_H_ diff --git a/chromium/mojo/edk/system/shared_buffer_dispatcher.cc b/chromium/mojo/core/shared_buffer_dispatcher.cc index 73b2c03d624..8a0026ab0e3 100644 --- a/chromium/mojo/edk/system/shared_buffer_dispatcher.cc +++ b/chromium/mojo/core/shared_buffer_dispatcher.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/shared_buffer_dispatcher.h" #include <stddef.h> #include <stdint.h> @@ -14,15 +14,15 @@ #include "base/logging.h" #include "base/memory/ptr_util.h" #include "build/build_config.h" -#include "mojo/edk/embedder/platform_handle_utils.h" -#include "mojo/edk/system/configuration.h" -#include "mojo/edk/system/node_controller.h" -#include "mojo/edk/system/options_validation.h" -#include "mojo/edk/system/platform_shared_memory_mapping.h" +#include "mojo/core/configuration.h" +#include "mojo/core/node_controller.h" +#include "mojo/core/options_validation.h" +#include "mojo/core/platform_handle_utils.h" +#include "mojo/core/platform_shared_memory_mapping.h" #include "mojo/public/c/system/platform_handle.h" namespace mojo { -namespace edk { +namespace core { namespace { @@ -122,7 +122,7 @@ scoped_refptr<SharedBufferDispatcher> SharedBufferDispatcher::Deserialize( size_t num_bytes, const ports::PortName* ports, size_t num_ports, - ScopedInternalPlatformHandle* platform_handles, + PlatformHandle* platform_handles, size_t num_platform_handles) { if (num_bytes != sizeof(SerializedState)) { LOG(ERROR) << "Invalid serialized shared buffer dispatcher (bad size)"; @@ -140,8 +140,8 @@ scoped_refptr<SharedBufferDispatcher> SharedBufferDispatcher::Deserialize( if (num_ports) return nullptr; - ScopedInternalPlatformHandle handles[2]; -#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA) && \ + PlatformHandle handles[2]; +#if defined(OS_POSIX) && !defined(OS_ANDROID) && \ (!defined(OS_MACOSX) || defined(OS_IOS)) if (serialized_state->access_mode == MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE) { @@ -176,9 +176,10 @@ scoped_refptr<SharedBufferDispatcher> SharedBufferDispatcher::Deserialize( LOG(ERROR) << "Invalid serialized shared buffer access mode."; return nullptr; } + auto region = base::subtle::PlatformSharedMemoryRegion::Take( - CreateSharedMemoryRegionHandleFromInternalPlatformHandles( - std::move(handles[0]), std::move(handles[1])), + CreateSharedMemoryRegionHandleFromPlatformHandles(std::move(handles[0]), + std::move(handles[1])), mode, static_cast<size_t>(serialized_state->num_bytes), guid); if (!region.IsValid()) { LOG(ERROR) @@ -250,7 +251,7 @@ MojoResult SharedBufferDispatcher::DuplicateBufferHandle( } else if (region_.GetMode() == base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) { auto handle = region_.PassPlatformHandle(); -#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA) && \ +#if defined(OS_POSIX) && !defined(OS_ANDROID) && \ (!defined(OS_MACOSX) || defined(OS_IOS)) // On POSIX systems excluding Android, Fuchsia, and OSX, we explicitly // wipe out the secondary (read-only) FD from the platform handle to @@ -311,7 +312,7 @@ void SharedBufferDispatcher::StartSerialize(uint32_t* num_bytes, *num_bytes = sizeof(SerializedState); *num_ports = 0; *num_platform_handles = 1; -#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA) && \ +#if defined(OS_POSIX) && !defined(OS_ANDROID) && \ (!defined(OS_MACOSX) || defined(OS_IOS)) if (region_.GetMode() == base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) { @@ -320,10 +321,9 @@ void SharedBufferDispatcher::StartSerialize(uint32_t* num_bytes, #endif } -bool SharedBufferDispatcher::EndSerialize( - void* destination, - ports::PortName* ports, - ScopedInternalPlatformHandle* handles) { +bool SharedBufferDispatcher::EndSerialize(void* destination, + ports::PortName* ports, + PlatformHandle* handles) { SerializedState* serialized_state = static_cast<SerializedState*>(destination); base::AutoLock lock(lock_); @@ -352,19 +352,25 @@ bool SharedBufferDispatcher::EndSerialize( serialized_state->padding = 0; auto region = std::move(region_); -#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA) && \ +#if defined(OS_POSIX) && !defined(OS_ANDROID) && \ (!defined(OS_MACOSX) || defined(OS_IOS)) if (region.GetMode() == base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) { - ExtractInternalPlatformHandlesFromSharedMemoryRegionHandle( - region.PassPlatformHandle(), &handles[0], &handles[1]); + PlatformHandle platform_handles[2]; + ExtractPlatformHandlesFromSharedMemoryRegionHandle( + region.PassPlatformHandle(), &platform_handles[0], + &platform_handles[1]); + handles[0] = std::move(platform_handles[0]); + handles[1] = std::move(platform_handles[1]); return true; } #endif - ScopedInternalPlatformHandle ignored_handle; - ExtractInternalPlatformHandlesFromSharedMemoryRegionHandle( - region.PassPlatformHandle(), &handles[0], &ignored_handle); + PlatformHandle platform_handle; + PlatformHandle ignored_handle; + ExtractPlatformHandlesFromSharedMemoryRegionHandle( + region.PassPlatformHandle(), &platform_handle, &ignored_handle); + handles[0] = std::move(platform_handle); return true; } @@ -435,5 +441,5 @@ MojoResult SharedBufferDispatcher::ValidateDuplicateOptions( return MOJO_RESULT_OK; } -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/shared_buffer_dispatcher.h b/chromium/mojo/core/shared_buffer_dispatcher.h index 30c0dd6f60e..cbdb3533f96 100644 --- a/chromium/mojo/edk/system/shared_buffer_dispatcher.h +++ b/chromium/mojo/core/shared_buffer_dispatcher.h @@ -2,8 +2,8 @@ // 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_ +#ifndef MOJO_CORE_SHARED_BUFFER_DISPATCHER_H_ +#define MOJO_CORE_SHARED_BUFFER_DISPATCHER_H_ #include <stddef.h> #include <stdint.h> @@ -12,13 +12,12 @@ #include "base/macros.h" #include "base/memory/platform_shared_memory_region.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/system_impl_export.h" +#include "mojo/core/dispatcher.h" +#include "mojo/core/system_impl_export.h" namespace mojo { -namespace edk { +namespace core { class NodeController; class PlatformSharedMemoryMapping; @@ -60,7 +59,7 @@ class MOJO_SYSTEM_IMPL_EXPORT SharedBufferDispatcher final : public Dispatcher { size_t num_bytes, const ports::PortName* ports, size_t num_ports, - ScopedInternalPlatformHandle* platform_handles, + PlatformHandle* platform_handles, size_t num_handles); // Passes the underlying PlatformSharedMemoryRegion. This dispatcher must be @@ -88,7 +87,7 @@ class MOJO_SYSTEM_IMPL_EXPORT SharedBufferDispatcher final : public Dispatcher { uint32_t* num_platform_handles) override; bool EndSerialize(void* destination, ports::PortName* ports, - ScopedInternalPlatformHandle* handles) override; + PlatformHandle* handles) override; bool BeginTransit() override; void CompleteTransitAndClose() override; void CancelTransit() override; @@ -119,7 +118,7 @@ class MOJO_SYSTEM_IMPL_EXPORT SharedBufferDispatcher final : public Dispatcher { DISALLOW_COPY_AND_ASSIGN(SharedBufferDispatcher); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_SHARED_BUFFER_DISPATCHER_H_ +#endif // MOJO_CORE_SHARED_BUFFER_DISPATCHER_H_ diff --git a/chromium/mojo/edk/system/shared_buffer_dispatcher_unittest.cc b/chromium/mojo/core/shared_buffer_dispatcher_unittest.cc index 768c7aa7475..22cbb44a4ab 100644 --- a/chromium/mojo/edk/system/shared_buffer_dispatcher_unittest.cc +++ b/chromium/mojo/core/shared_buffer_dispatcher_unittest.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/shared_buffer_dispatcher.h" #include <stddef.h> #include <stdint.h> @@ -13,12 +13,12 @@ #include "base/memory/platform_shared_memory_region.h" #include "base/memory/ref_counted.h" #include "base/memory/writable_shared_memory_region.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/platform_shared_memory_mapping.h" +#include "mojo/core/dispatcher.h" +#include "mojo/core/platform_shared_memory_mapping.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo { -namespace edk { +namespace core { namespace { // NOTE(vtl): There's currently not much to test for in @@ -37,9 +37,8 @@ void RevalidateCreateOptions( // Nothing to check for flags. MojoCreateSharedBufferOptions revalidated_options = {}; - EXPECT_EQ(MOJO_RESULT_OK, - SharedBufferDispatcher::ValidateCreateOptions( - &validated_options, &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); } @@ -76,9 +75,8 @@ TEST_F(SharedBufferDispatcherTest, ValidateCreateOptionsValid) { flags // |flags|. }; MojoCreateSharedBufferOptions validated_options = {}; - EXPECT_EQ(MOJO_RESULT_OK, - SharedBufferDispatcher::ValidateCreateOptions( - &options, &validated_options)) + EXPECT_EQ(MOJO_RESULT_OK, SharedBufferDispatcher::ValidateCreateOptions( + &options, &validated_options)) << capacity; RevalidateCreateOptions(validated_options); EXPECT_EQ(options.flags, validated_options.flags); @@ -95,8 +93,7 @@ TEST_F(SharedBufferDispatcherTest, ValidateCreateOptionsInvalid) { }; MojoCreateSharedBufferOptions unused; EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - SharedBufferDispatcher::ValidateCreateOptions( - &options, &unused)); + SharedBufferDispatcher::ValidateCreateOptions(&options, &unused)); } // Unknown |flags|. @@ -107,8 +104,7 @@ TEST_F(SharedBufferDispatcherTest, ValidateCreateOptionsInvalid) { }; MojoCreateSharedBufferOptions unused; EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED, - SharedBufferDispatcher::ValidateCreateOptions( - &options, &unused)); + SharedBufferDispatcher::ValidateCreateOptions(&options, &unused)); } } @@ -195,8 +191,8 @@ TEST_F(SharedBufferDispatcherTest, DuplicateBufferHandle) { // Duplicate |dispatcher1| and then close it. scoped_refptr<Dispatcher> dispatcher2; - EXPECT_EQ(MOJO_RESULT_OK, dispatcher1->DuplicateBufferHandle( - nullptr, &dispatcher2)); + EXPECT_EQ(MOJO_RESULT_OK, + dispatcher1->DuplicateBufferHandle(nullptr, &dispatcher2)); ASSERT_TRUE(dispatcher2); EXPECT_EQ(Dispatcher::Type::SHARED_BUFFER, dispatcher2->GetType()); @@ -340,5 +336,5 @@ TEST_F(SharedBufferDispatcherTest, MapBufferInvalidArguments) { } } // namespace -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/shared_buffer_unittest.cc b/chromium/mojo/core/shared_buffer_unittest.cc index 2e09b60059d..58a0b88376c 100644 --- a/chromium/mojo/edk/system/shared_buffer_unittest.cc +++ b/chromium/mojo/core/shared_buffer_unittest.cc @@ -10,14 +10,15 @@ #include "base/logging.h" #include "base/memory/shared_memory.h" #include "base/strings/string_piece.h" -#include "mojo/edk/system/core.h" -#include "mojo/edk/system/shared_buffer_dispatcher.h" -#include "mojo/edk/test/mojo_test_base.h" +#include "build/build_config.h" +#include "mojo/core/core.h" +#include "mojo/core/shared_buffer_dispatcher.h" +#include "mojo/core/test/mojo_test_base.h" #include "mojo/public/c/system/types.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo { -namespace edk { +namespace core { namespace { using SharedBufferTest = test::MojoTestBase; @@ -318,5 +319,5 @@ TEST_F(SharedBufferTest, MAYBE_CreateAndPassFromChildReadOnlyBuffer) { #endif // !defined(OS_IOS) } // namespace -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/signals_unittest.cc b/chromium/mojo/core/signals_unittest.cc index 34f2105ea94..2519763250c 100644 --- a/chromium/mojo/edk/system/signals_unittest.cc +++ b/chromium/mojo/core/signals_unittest.cc @@ -2,7 +2,8 @@ // 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 "build/build_config.h" +#include "mojo/core/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" @@ -11,7 +12,7 @@ #include "mojo/public/c/system/types.h" namespace mojo { -namespace edk { +namespace core { namespace { using SignalsTest = test::MojoTestBase; @@ -36,13 +37,17 @@ TEST_F(SignalsTest, QueryMessagePipeSignals) { 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 | MOJO_HANDLE_SIGNAL_PEER_REMOTE, + MOJO_HANDLE_SIGNAL_PEER_CLOSED | + MOJO_HANDLE_SIGNAL_PEER_REMOTE | + MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED, 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 | MOJO_HANDLE_SIGNAL_PEER_REMOTE, + MOJO_HANDLE_SIGNAL_PEER_CLOSED | + MOJO_HANDLE_SIGNAL_PEER_REMOTE | + MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED, state.satisfiable_signals); WriteMessage(a, "ok"); @@ -52,7 +57,9 @@ TEST_F(SignalsTest, QueryMessagePipeSignals) { 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 | MOJO_HANDLE_SIGNAL_PEER_REMOTE, + MOJO_HANDLE_SIGNAL_PEER_CLOSED | + MOJO_HANDLE_SIGNAL_PEER_REMOTE | + MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED, state.satisfiable_signals); EXPECT_EQ("ok", ReadMessage(b)); @@ -60,7 +67,9 @@ TEST_F(SignalsTest, QueryMessagePipeSignals) { 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 | MOJO_HANDLE_SIGNAL_PEER_REMOTE, + MOJO_HANDLE_SIGNAL_PEER_CLOSED | + MOJO_HANDLE_SIGNAL_PEER_REMOTE | + MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED, state.satisfiable_signals); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); @@ -69,7 +78,8 @@ TEST_F(SignalsTest, QueryMessagePipeSignals) { 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); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED, + state.satisfiable_signals); } TEST_F(SignalsTest, LocalPeers) { @@ -208,5 +218,5 @@ DEFINE_TEST_CLIENT_TEST_WITH_PIPE(RemotePeersClient, SignalsTest, h) { #endif // !defined(OS_IOS) } // namespace -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/system_impl_export.h b/chromium/mojo/core/system_impl_export.h index 5bbf0057b0d..b55acbc26cc 100644 --- a/chromium/mojo/edk/system/system_impl_export.h +++ b/chromium/mojo/core/system_impl_export.h @@ -2,9 +2,12 @@ // 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_ +#ifndef MOJO_CORE_SYSTEM_IMPL_EXPORT_H_ +#define MOJO_CORE_SYSTEM_IMPL_EXPORT_H_ +#if defined(MOJO_CORE_SHARED_LIBRARY) +#define MOJO_SYSTEM_IMPL_EXPORT +#else #if defined(COMPONENT_BUILD) #if defined(WIN32) @@ -25,5 +28,6 @@ #else // defined(COMPONENT_BUILD) #define MOJO_SYSTEM_IMPL_EXPORT #endif +#endif -#endif // MOJO_EDK_SYSTEM_SYSTEM_IMPL_EXPORT_H_ +#endif // MOJO_CORE_SYSTEM_IMPL_EXPORT_H_ diff --git a/chromium/mojo/edk/test/BUILD.gn b/chromium/mojo/core/test/BUILD.gn index 71491d304f8..6fad6fec711 100644 --- a/chromium/mojo/edk/test/BUILD.gn +++ b/chromium/mojo/core/test/BUILD.gn @@ -18,16 +18,14 @@ static_library("test_support") { ] } - if (is_fuchsia) { - sources += [ "test_utils_fuchsia.cc" ] - } else if (is_posix) { - sources += [ "test_utils_posix.cc" ] + if (is_fuchsia || is_posix) { + sources += [ "test_utils.cc" ] } public_deps = [ "//base", "//base/test:test_support", - "//mojo/edk", + "//mojo/core/embedder", "//mojo/public/cpp/system", "//testing/gtest", ] @@ -44,7 +42,7 @@ source_set("run_all_unittests") { ":test_support_impl", "//base", "//base/test:test_support", - "//mojo/edk", + "//mojo/core/embedder", "//mojo/public/c/test_support", "//testing/gtest", ] @@ -60,8 +58,8 @@ source_set("run_all_perftests") { ":test_support_impl", "//base", "//base/test:test_support", - "//mojo/edk", - "//mojo/edk/test:test_support", + "//mojo/core/embedder", + "//mojo/core/test:test_support", "//mojo/public/c/test_support", ] diff --git a/chromium/mojo/edk/system/test_utils.cc b/chromium/mojo/core/test_utils.cc index 4a39cf73dae..33ce9ea0155 100644 --- a/chromium/mojo/edk/system/test_utils.cc +++ b/chromium/mojo/core/test_utils.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/test_utils.h" #include <stdint.h> @@ -14,7 +14,7 @@ #include "build/build_config.h" namespace mojo { -namespace edk { +namespace core { namespace test { MojoDeadline DeadlineFromMilliseconds(unsigned milliseconds) { @@ -54,11 +54,9 @@ void Sleep(MojoDeadline deadline) { base::TimeDelta::FromMicroseconds(static_cast<int64_t>(deadline))); } -Stopwatch::Stopwatch() { -} +Stopwatch::Stopwatch() {} -Stopwatch::~Stopwatch() { -} +Stopwatch::~Stopwatch() {} void Stopwatch::Start() { start_time_ = base::TimeTicks::Now(); @@ -72,5 +70,5 @@ MojoDeadline Stopwatch::Elapsed() { } } // namespace test -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/test_utils.h b/chromium/mojo/core/test_utils.h index 1c90dc1717c..98ac51e182d 100644 --- a/chromium/mojo/edk/system/test_utils.h +++ b/chromium/mojo/core/test_utils.h @@ -2,8 +2,8 @@ // 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_ +#ifndef MOJO_CORE_TEST_UTILS_H_ +#define MOJO_CORE_TEST_UTILS_H_ #include "base/macros.h" #include "base/time/time.h" @@ -11,7 +11,7 @@ #include "testing/gtest/include/gtest/gtest.h" namespace mojo { -namespace edk { +namespace core { namespace test { MojoDeadline DeadlineFromMilliseconds(unsigned milliseconds); @@ -53,7 +53,7 @@ class Stopwatch { }; } // namespace test -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_TEST_UTILS_H_ +#endif // MOJO_CORE_TEST_UTILS_H_ diff --git a/chromium/mojo/core/trap_unittest.cc b/chromium/mojo/core/trap_unittest.cc new file mode 100644 index 00000000000..555726f4da6 --- /dev/null +++ b/chromium/mojo/core/trap_unittest.cc @@ -0,0 +1,1763 @@ +// 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/rand_util.h" +#include "base/synchronization/waitable_event.h" +#include "base/test/bind_test_util.h" +#include "base/threading/platform_thread.h" +#include "base/threading/simple_thread.h" +#include "base/time/time.h" +#include "mojo/core/test/mojo_test_base.h" +#include "mojo/public/c/system/data_pipe.h" +#include "mojo/public/c/system/trap.h" +#include "mojo/public/c/system/types.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace core { +namespace { + +using TrapTest = test::MojoTestBase; + +class TriggerHelper { + public: + using ContextCallback = base::RepeatingCallback<void(const MojoTrapEvent&)>; + + TriggerHelper() {} + ~TriggerHelper() {} + + MojoResult CreateTrap(MojoHandle* handle) { + return MojoCreateTrap(&Notify, nullptr, handle); + } + + template <typename Handler> + uintptr_t CreateContext(Handler handler) { + return CreateContextWithCancel(handler, [] {}); + } + + template <typename Handler, typename CancelHandler> + uintptr_t CreateContextWithCancel(Handler handler, + CancelHandler cancel_handler) { + auto* context = + new NotificationContext(base::BindLambdaForTesting(handler)); + context->SetCancelCallback( + base::BindOnce(base::BindLambdaForTesting([cancel_handler, context] { + cancel_handler(); + delete context; + }))); + return reinterpret_cast<uintptr_t>(context); + } + + private: + class NotificationContext { + public: + explicit NotificationContext(const ContextCallback& callback) + : callback_(callback) {} + + ~NotificationContext() {} + + void SetCancelCallback(base::OnceClosure cancel_callback) { + cancel_callback_ = std::move(cancel_callback); + } + + void Notify(const MojoTrapEvent& event) { + if (event.result == MOJO_RESULT_CANCELLED && cancel_callback_) + std::move(cancel_callback_).Run(); + else + callback_.Run(event); + } + + private: + const ContextCallback callback_; + base::OnceClosure cancel_callback_; + + DISALLOW_COPY_AND_ASSIGN(NotificationContext); + }; + + static void Notify(const MojoTrapEvent* event) { + reinterpret_cast<NotificationContext*>(event->trigger_context) + ->Notify(*event); + } + + DISALLOW_COPY_AND_ASSIGN(TriggerHelper); +}; + +class ThreadedRunner : public base::SimpleThread { + public: + explicit ThreadedRunner(base::OnceClosure callback) + : SimpleThread("ThreadedRunner"), callback_(std::move(callback)) {} + ~ThreadedRunner() override {} + + void Run() override { std::move(callback_).Run(); } + + private: + base::OnceClosure callback_; + + DISALLOW_COPY_AND_ASSIGN(ThreadedRunner); +}; + +void ExpectNoNotification(const MojoTrapEvent* event) { + NOTREACHED(); +} + +void ExpectOnlyCancel(const MojoTrapEvent* event) { + EXPECT_EQ(event->result, MOJO_RESULT_CANCELLED); +} + +TEST_F(TrapTest, InvalidArguments) { + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoCreateTrap(&ExpectNoNotification, nullptr, nullptr)); + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, MojoCreateTrap(&ExpectNoNotification, nullptr, &t)); + + // Try to add triggers for handles which don't raise trappable signals. + EXPECT_EQ( + MOJO_RESULT_INVALID_ARGUMENT, + MojoAddTrigger(t, t, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, 0, nullptr)); + MojoHandle buffer_handle = CreateBuffer(42); + EXPECT_EQ( + MOJO_RESULT_INVALID_ARGUMENT, + MojoAddTrigger(t, buffer_handle, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, 0, nullptr)); + + // Try to remove a trigger on a non-trap handle. + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoRemoveTrigger(buffer_handle, 0, nullptr)); + + // Try to arm an invalid or non-trap handle. + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoArmTrap(MOJO_HANDLE_INVALID, nullptr, nullptr, nullptr)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoArmTrap(buffer_handle, nullptr, nullptr, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(buffer_handle)); + + // Try to arm with a non-null count but a null output buffer. + uint32_t num_blocking_events = 1; + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoArmTrap(t, nullptr, &num_blocking_events, nullptr)); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); +} + +TEST_F(TrapTest, TrapMessagePipeReadable) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + TriggerHelper helper; + int num_expected_notifications = 1; + const uintptr_t readable_a_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + EXPECT_GT(num_expected_notifications, 0); + num_expected_notifications -= 1; + + EXPECT_EQ(MOJO_RESULT_OK, event.result); + wait.Signal(); + }); + + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_a_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, 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); + wait.Wait(); + + // This also shouldn't fire a notification; the trap is still disarmed. + WriteMessage(b, kMessage3); + + // Arming should fail with relevant information. + constexpr size_t kMaxBlockingEvents = 3; + uint32_t num_blocking_events = kMaxBlockingEvents; + MojoTrapEvent blocking_events[kMaxBlockingEvents] = { + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}}; + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0])); + EXPECT_EQ(1u, num_blocking_events); + EXPECT_EQ(readable_a_context, blocking_events[0].trigger_context); + EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result); + + // 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 trap. + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); +} + +TEST_F(TrapTest, CloseWatchedMessagePipeHandle) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + TriggerHelper helper; + const uintptr_t readable_a_context = helper.CreateContextWithCancel( + [](const MojoTrapEvent&) {}, [&] { wait.Signal(); }); + + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_a_context, nullptr)); + + // Test that closing a watched handle fires an appropriate notification, even + // when the trap is unarmed. + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); + wait.Wait(); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); +} + +TEST_F(TrapTest, CloseWatchedMessagePipeHandlePeer) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + TriggerHelper helper; + const uintptr_t readable_a_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, event.result); + wait.Signal(); + }); + + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_a_context, nullptr)); + + // Test that closing a watched handle's peer with an armed trap fires an + // appropriate notification. + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); + wait.Wait(); + + // And now arming should fail with correct information about |a|'s state. + constexpr size_t kMaxBlockingEvents = 3; + uint32_t num_blocking_events = kMaxBlockingEvents; + MojoTrapEvent blocking_events[kMaxBlockingEvents] = { + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}}; + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0])); + EXPECT_EQ(1u, num_blocking_events); + EXPECT_EQ(readable_a_context, blocking_events[0].trigger_context); + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, blocking_events[0].result); + EXPECT_TRUE(blocking_events[0].signals_state.satisfied_signals & + MOJO_HANDLE_SIGNAL_PEER_CLOSED); + EXPECT_FALSE(blocking_events[0].signals_state.satisfiable_signals & + MOJO_HANDLE_SIGNAL_READABLE); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); +} + +TEST_F(TrapTest, TrapDataPipeConsumerReadable) { + constexpr size_t kTestPipeCapacity = 64; + MojoHandle producer, consumer; + CreateDataPipe(&producer, &consumer, kTestPipeCapacity); + + base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + TriggerHelper helper; + int num_expected_notifications = 1; + const uintptr_t readable_consumer_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + EXPECT_GT(num_expected_notifications, 0); + num_expected_notifications -= 1; + + EXPECT_EQ(MOJO_RESULT_OK, event.result); + wait.Signal(); + }); + + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, consumer, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_consumer_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, 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); + wait.Wait(); + + // This also shouldn't fire a notification; the trap is still disarmed. + WriteData(producer, kMessage3); + + // Arming should fail with relevant information. + constexpr size_t kMaxBlockingEvents = 3; + uint32_t num_blocking_events = kMaxBlockingEvents; + MojoTrapEvent blocking_events[kMaxBlockingEvents] = { + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}}; + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0])); + EXPECT_EQ(1u, num_blocking_events); + EXPECT_EQ(readable_consumer_context, blocking_events[0].trigger_context); + EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result); + + // 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 trap. + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); +} + +TEST_F(TrapTest, TrapDataPipeConsumerNewDataReadable) { + constexpr size_t kTestPipeCapacity = 64; + MojoHandle producer, consumer; + CreateDataPipe(&producer, &consumer, kTestPipeCapacity); + + base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + TriggerHelper helper; + int num_new_data_notifications = 0; + const uintptr_t new_data_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + num_new_data_notifications += 1; + + EXPECT_EQ(MOJO_RESULT_OK, event.result); + wait.Signal(); + }); + + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, consumer, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + new_data_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, 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); + wait.Wait(); + + // This also shouldn't fire a notification; the trap is still disarmed. + WriteData(producer, kMessage3); + + // Arming should fail with relevant information. + constexpr size_t kMaxBlockingEvents = 3; + uint32_t num_blocking_events = kMaxBlockingEvents; + MojoTrapEvent blocking_events[kMaxBlockingEvents] = { + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}}; + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0])); + EXPECT_EQ(1u, num_blocking_events); + EXPECT_EQ(new_data_context, blocking_events[0].trigger_context); + EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result); + + // 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; + MojoReadDataOptions options; + options.struct_size = sizeof(options); + options.flags = MOJO_READ_DATA_FLAG_ALL_OR_NONE; + EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, + MojoReadData(consumer, &options, large_buffer, &large_read_size)); + + // Attempt to arm again. Should succeed. + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + // Write more data. Should notify. + wait.Reset(); + WriteData(producer, kMessage1); + wait.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, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + EXPECT_EQ(2, num_new_data_notifications); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); +} + +TEST_F(TrapTest, TrapDataPipeProducerWritable) { + 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 wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + TriggerHelper helper; + int num_expected_notifications = 1; + const uintptr_t writable_producer_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + EXPECT_GT(num_expected_notifications, 0); + num_expected_notifications -= 1; + + EXPECT_EQ(MOJO_RESULT_OK, event.result); + wait.Signal(); + }); + + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, producer, MOJO_HANDLE_SIGNAL_WRITABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + writable_producer_context, nullptr)); + + // The producer is already writable, so arming should fail with relevant + // information. + constexpr size_t kMaxBlockingEvents = 3; + uint32_t num_blocking_events = kMaxBlockingEvents; + MojoTrapEvent blocking_events[kMaxBlockingEvents] = { + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}}; + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0])); + EXPECT_EQ(1u, num_blocking_events); + EXPECT_EQ(writable_producer_context, blocking_events[0].trigger_context); + EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result); + EXPECT_TRUE(blocking_events[0].signals_state.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, + MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0])); + EXPECT_EQ(1u, num_blocking_events); + EXPECT_EQ(writable_producer_context, blocking_events[0].trigger_context); + EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result); + EXPECT_TRUE(blocking_events[0].signals_state.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, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + // Now read from the pipe, making the producer writable again. Should notify. + EXPECT_EQ(kTestData, ReadData(consumer, sizeof(kTestData) - 1)); + wait.Wait(); + + // Arming should fail again. + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0])); + EXPECT_EQ(1u, num_blocking_events); + EXPECT_EQ(writable_producer_context, blocking_events[0].trigger_context); + EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result); + EXPECT_TRUE(blocking_events[0].signals_state.satisfied_signals & + MOJO_HANDLE_SIGNAL_WRITABLE); + + // Fill the pipe once more and arm the trap. Should succeed. + WriteData(producer, kTestData); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); +}; + +TEST_F(TrapTest, CloseWatchedDataPipeConsumerHandle) { + constexpr size_t kTestPipeCapacity = 8; + MojoHandle producer, consumer; + CreateDataPipe(&producer, &consumer, kTestPipeCapacity); + + base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + TriggerHelper helper; + const uintptr_t readable_consumer_context = helper.CreateContextWithCancel( + [](const MojoTrapEvent&) {}, [&] { wait.Signal(); }); + + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, consumer, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_consumer_context, nullptr)); + + // Closing the consumer should fire a cancellation notification. + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); + wait.Wait(); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); +} + +TEST_F(TrapTest, CloseWatchedDataPipeConsumerHandlePeer) { + constexpr size_t kTestPipeCapacity = 8; + MojoHandle producer, consumer; + CreateDataPipe(&producer, &consumer, kTestPipeCapacity); + + base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + TriggerHelper helper; + const uintptr_t readable_consumer_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, event.result); + wait.Signal(); + }); + + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, consumer, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_consumer_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + // Closing the producer should fire a notification for an unsatisfiable + // condition. + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); + wait.Wait(); + + // Now attempt to rearm and expect appropriate error feedback. + constexpr size_t kMaxBlockingEvents = 3; + uint32_t num_blocking_events = kMaxBlockingEvents; + MojoTrapEvent blocking_events[kMaxBlockingEvents] = { + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}}; + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0])); + EXPECT_EQ(1u, num_blocking_events); + EXPECT_EQ(readable_consumer_context, blocking_events[0].trigger_context); + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, blocking_events[0].result); + EXPECT_FALSE(blocking_events[0].signals_state.satisfiable_signals & + MOJO_HANDLE_SIGNAL_READABLE); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); +} + +TEST_F(TrapTest, CloseWatchedDataPipeProducerHandle) { + constexpr size_t kTestPipeCapacity = 8; + MojoHandle producer, consumer; + CreateDataPipe(&producer, &consumer, kTestPipeCapacity); + + base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + TriggerHelper helper; + const uintptr_t writable_producer_context = helper.CreateContextWithCancel( + [](const MojoTrapEvent&) {}, [&] { wait.Signal(); }); + + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, producer, MOJO_HANDLE_SIGNAL_WRITABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + writable_producer_context, nullptr)); + + // Closing the consumer should fire a cancellation notification. + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); + wait.Wait(); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); +} + +TEST_F(TrapTest, 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 wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + TriggerHelper helper; + const uintptr_t writable_producer_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, event.result); + wait.Signal(); + }); + + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, producer, MOJO_HANDLE_SIGNAL_WRITABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + writable_producer_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + // Closing the consumer should fire a notification for an unsatisfiable + // condition, 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)); + wait.Wait(); + + // Now attempt to rearm and expect appropriate error feedback. + constexpr size_t kMaxBlockingEvents = 3; + uint32_t num_blocking_events = kMaxBlockingEvents; + MojoTrapEvent blocking_events[kMaxBlockingEvents] = { + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}}; + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0])); + EXPECT_EQ(1u, num_blocking_events); + EXPECT_EQ(writable_producer_context, blocking_events[0].trigger_context); + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, blocking_events[0].result); + EXPECT_FALSE(blocking_events[0].signals_state.satisfiable_signals & + MOJO_HANDLE_SIGNAL_WRITABLE); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); +} + +TEST_F(TrapTest, ArmWithNoTriggers) { + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, MojoCreateTrap(&ExpectNoNotification, nullptr, &t)); + EXPECT_EQ(MOJO_RESULT_NOT_FOUND, MojoArmTrap(t, nullptr, nullptr, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); +} + +TEST_F(TrapTest, DuplicateTriggerContext) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, MojoCreateTrap(&ExpectOnlyCancel, nullptr, &t)); + EXPECT_EQ( + MOJO_RESULT_OK, + MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, 0, nullptr)); + EXPECT_EQ( + MOJO_RESULT_ALREADY_EXISTS, + MojoAddTrigger(t, b, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, 0, nullptr)); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); +} + +TEST_F(TrapTest, RemoveUnknownTrigger) { + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, MojoCreateTrap(&ExpectNoNotification, nullptr, &t)); + EXPECT_EQ(MOJO_RESULT_NOT_FOUND, MojoRemoveTrigger(t, 1234, nullptr)); +} + +TEST_F(TrapTest, ArmWithTriggerConditionAlreadySatisfied) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, MojoCreateTrap(&ExpectOnlyCancel, nullptr, &t)); + EXPECT_EQ( + MOJO_RESULT_OK, + MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_WRITABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, 0, nullptr)); + + // |a| is always writable, so we can never arm this trap. + constexpr size_t kMaxBlockingEvents = 3; + uint32_t num_blocking_events = kMaxBlockingEvents; + MojoTrapEvent blocking_events[kMaxBlockingEvents] = { + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}}; + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0])); + EXPECT_EQ(1u, num_blocking_events); + EXPECT_EQ(0u, blocking_events[0].trigger_context); + EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result); + EXPECT_TRUE(blocking_events[0].signals_state.satisfied_signals & + MOJO_HANDLE_SIGNAL_WRITABLE); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); +} + +TEST_F(TrapTest, ArmWithTriggerConditionAlreadyUnsatisfiable) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, MojoCreateTrap(&ExpectOnlyCancel, nullptr, &t)); + EXPECT_EQ( + MOJO_RESULT_OK, + MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, 0, nullptr)); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); + + // |b| is closed and never wrote any messages, so |a| won't be readable again. + // MojoArmTrap() should fail, incidcating as much. + constexpr size_t kMaxBlockingEvents = 3; + uint32_t num_blocking_events = kMaxBlockingEvents; + MojoTrapEvent blocking_events[kMaxBlockingEvents] = { + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}}; + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0])); + EXPECT_EQ(1u, num_blocking_events); + EXPECT_EQ(0u, blocking_events[0].trigger_context); + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, blocking_events[0].result); + EXPECT_TRUE(blocking_events[0].signals_state.satisfied_signals & + MOJO_HANDLE_SIGNAL_PEER_CLOSED); + EXPECT_FALSE(blocking_events[0].signals_state.satisfiable_signals & + MOJO_HANDLE_SIGNAL_READABLE); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); +} + +TEST_F(TrapTest, MultipleTriggers) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + base::WaitableEvent a_wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + base::WaitableEvent b_wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + TriggerHelper helper; + int num_a_notifications = 0; + int num_b_notifications = 0; + uintptr_t readable_a_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + num_a_notifications += 1; + EXPECT_EQ(MOJO_RESULT_OK, event.result); + a_wait.Signal(); + }); + uintptr_t readable_b_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + num_b_notifications += 1; + EXPECT_EQ(MOJO_RESULT_OK, event.result); + b_wait.Signal(); + }); + + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + + // Add two independent triggers to trap |a| or |b| readability. + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_a_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, b, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_b_context, nullptr)); + + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, 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_wait.Wait(); + a_wait.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 + // trap 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 kMaxBlockingEvents = 3; + uint32_t num_blocking_events = 1; + MojoTrapEvent blocking_events[kMaxBlockingEvents] = { + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}, + {sizeof(blocking_events[0])}}; + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0])); + EXPECT_EQ(1u, num_blocking_events); + EXPECT_TRUE(blocking_events[0].trigger_context == readable_a_context || + blocking_events[0].trigger_context == readable_b_context); + EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result); + EXPECT_TRUE(blocking_events[0].signals_state.satisfied_signals & + MOJO_HANDLE_SIGNAL_WRITABLE); + + // Now try arming again, verifying that both contexts are returned. + num_blocking_events = kMaxBlockingEvents; + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0])); + EXPECT_EQ(2u, num_blocking_events); + EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result); + EXPECT_EQ(MOJO_RESULT_OK, blocking_events[1].result); + EXPECT_TRUE(blocking_events[0].signals_state.satisfied_signals & + MOJO_HANDLE_SIGNAL_WRITABLE); + EXPECT_TRUE(blocking_events[1].signals_state.satisfied_signals & + MOJO_HANDLE_SIGNAL_WRITABLE); + EXPECT_TRUE((blocking_events[0].trigger_context == readable_a_context && + blocking_events[1].trigger_context == readable_b_context) || + (blocking_events[0].trigger_context == readable_b_context && + blocking_events[1].trigger_context == 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 trigger whose condition 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([](const MojoTrapEvent&) { NOTREACHED(); }); + MojoHandle c, d; + CreateMessagePipe(&c, &d); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, c, MOJO_HANDLE_SIGNAL_WRITABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + writable_c_context, nullptr)); + num_blocking_events = kMaxBlockingEvents; + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0])); + EXPECT_EQ(1u, num_blocking_events); + EXPECT_EQ(writable_c_context, blocking_events[0].trigger_context); + EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result); + EXPECT_TRUE(blocking_events[0].signals_state.satisfied_signals & + MOJO_HANDLE_SIGNAL_WRITABLE); + + // Remove the new trigger and arming should succeed once again. + EXPECT_EQ(MOJO_RESULT_OK, MojoRemoveTrigger(t, writable_c_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); + 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(TrapTest, ActivateOtherTriggerFromEventHandler) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + static const char kTestMessageToA[] = "hello a"; + static const char kTestMessageToB[] = "hello b"; + + base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + + TriggerHelper helper; + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + + uintptr_t readable_a_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + EXPECT_EQ(MOJO_RESULT_OK, event.result); + EXPECT_EQ("hello a", ReadMessage(a)); + + // Re-arm the trap and signal |b|. + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + WriteMessage(a, kTestMessageToB); + }); + + uintptr_t readable_b_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + EXPECT_EQ(MOJO_RESULT_OK, event.result); + EXPECT_EQ(kTestMessageToB, ReadMessage(b)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + wait.Signal(); + }); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_a_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, b, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_b_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + // Send a message to |a|. The relevant trigger should be notified and the + // event handler should send a message to |b|, in turn notifying the other + // trigger. The second event handler will signal |wait|. + WriteMessage(b, kTestMessageToA); + wait.Wait(); +} + +TEST_F(TrapTest, ActivateSameTriggerFromEventHandler) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + static const char kTestMessageToA[] = "hello a"; + + base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + + TriggerHelper helper; + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + + int expected_notifications = 10; + uintptr_t readable_a_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + EXPECT_EQ(MOJO_RESULT_OK, event.result); + EXPECT_EQ("hello a", ReadMessage(a)); + + EXPECT_GT(expected_notifications, 0); + expected_notifications -= 1; + if (expected_notifications == 0) { + wait.Signal(); + return; + } else { + // Re-arm the trap and signal |a| again. + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + WriteMessage(b, kTestMessageToA); + } + }); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_a_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + // Send a message to |a|. When the trigger above is activated, the event + // handler will rearm the trap and send another message to |a|. This will + // happen until |expected_notifications| reaches 0. + WriteMessage(b, kTestMessageToA); + wait.Wait(); +} + +TEST_F(TrapTest, ImplicitRemoveOtherTriggerWithinEventHandler) { + 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 wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + + TriggerHelper helper; + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + + uintptr_t readable_a_context = helper.CreateContextWithCancel( + [](const MojoTrapEvent&) { NOTREACHED(); }, [&] { wait.Signal(); }); + + uintptr_t readable_c_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + EXPECT_EQ(MOJO_RESULT_OK, event.result); + EXPECT_EQ(kTestMessageToC, ReadMessage(c)); + + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + // Must result in exactly ONE notification from the above trigger, 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 trigger should already be + // implicitly removed (even though the notification will not have + // been invoked yet.) + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + // Nothing interesting should happen as a result of this. + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); + }); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_a_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, c, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_c_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + WriteMessage(d, kTestMessageToC); + wait.Wait(); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); +} + +TEST_F(TrapTest, ExplicitRemoveOtherTriggerWithinEventHandler) { + 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 wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + + TriggerHelper helper; + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + + uintptr_t readable_a_context = + helper.CreateContext([](const MojoTrapEvent&) { NOTREACHED(); }); + + uintptr_t readable_c_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + EXPECT_EQ(MOJO_RESULT_OK, event.result); + EXPECT_EQ(kTestMessageToC, ReadMessage(c)); + + // Now rearm the trap. + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + // Should result in no notifications from the above trigger, because the + // trigger will have been removed by the time the event handler can + // execute. + WriteMessage(b, kTestMessageToA); + WriteMessage(b, kTestMessageToA); + EXPECT_EQ(MOJO_RESULT_OK, + MojoRemoveTrigger(t, readable_a_context, nullptr)); + + // Rearming should be fine now. + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, 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)); + + wait.Signal(); + }); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_a_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, c, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_c_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + WriteMessage(d, kTestMessageToC); + wait.Wait(); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); +} + +TEST_F(TrapTest, 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 trigger on |b| using one trap and + // triggers on |c| and |d| using another trap. + // + // A message is written to |d| to activate |c|'s trigger, and the resuling + // event handler invocation does the folllowing: + // 1. Writes to |a| to eventually activate |b|'s trigger. + // 2. Rearms |c|'s trap. + // 3. Writes to |d| to eventually activate |c|'s trigger again. + // + // Meanwhile, |b|'s event handler removes |c|'s trigger altogether before + // writing to |c| to activate |d|'s trigger. + // + // The net result should be that |c|'s trigger only gets activated once (from + // the first write to |d| above) and everyone else gets notified as expected. + + MojoHandle b_trap; + MojoHandle cd_trap; + TriggerHelper helper; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&b_trap)); + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&cd_trap)); + + base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + uintptr_t readable_d_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + EXPECT_EQ(MOJO_RESULT_OK, event.result); + EXPECT_EQ(kTestMessageToD, ReadMessage(d)); + wait.Signal(); + }); + + int num_expected_c_notifications = 1; + uintptr_t readable_c_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + EXPECT_EQ(MOJO_RESULT_OK, event.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, + MojoArmTrap(cd_trap, nullptr, nullptr, nullptr)); + + // Trigger another eventual |readable_c_context| notification. + WriteMessage(d, kTestMessageToC); + }); + + uintptr_t readable_b_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + EXPECT_EQ(MOJO_RESULT_OK, + MojoRemoveTrigger(cd_trap, readable_c_context, nullptr)); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoArmTrap(cd_trap, nullptr, nullptr, nullptr)); + + WriteMessage(c, kTestMessageToD); + }); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(b_trap, b, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_b_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(cd_trap, c, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_c_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(cd_trap, d, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_d_context, nullptr)); + + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(b_trap, nullptr, nullptr, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(cd_trap, nullptr, nullptr, nullptr)); + + WriteMessage(d, kTestMessageToC); + wait.Wait(); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(cd_trap)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b_trap)); + 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(TrapTest, RemoveSelfWithinEventHandler) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + static const char kTestMessageToA[] = "hey a"; + + MojoHandle t; + TriggerHelper helper; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + + base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + + static uintptr_t readable_a_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + EXPECT_EQ(MOJO_RESULT_OK, event.result); + + // There should be no problem removing this trigger from its own + // notification invocation. + EXPECT_EQ(MOJO_RESULT_OK, + MojoRemoveTrigger(t, readable_a_context, nullptr)); + EXPECT_EQ(kTestMessageToA, ReadMessage(a)); + + // Arming should fail because there are no longer any registered + // triggers on the trap. + EXPECT_EQ(MOJO_RESULT_NOT_FOUND, + MojoArmTrap(t, 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)); + + wait.Signal(); + }); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_a_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + WriteMessage(b, kTestMessageToA); + wait.Wait(); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); +} + +TEST_F(TrapTest, CloseTrapWithinEventHandler) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + static const char kTestMessageToA1[] = "hey a"; + static const char kTestMessageToA2[] = "hey a again"; + + MojoHandle t; + TriggerHelper helper; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + + base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + + uintptr_t readable_a_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + EXPECT_EQ(MOJO_RESULT_OK, event.result); + EXPECT_EQ(kTestMessageToA1, ReadMessage(a)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + // There should be no problem closing this trap from its own + // notification callback. + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); + + // And these should not trigger more notifications, because |t| has been + // closed already. + WriteMessage(b, kTestMessageToA2); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); + + wait.Signal(); + }); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_a_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + WriteMessage(b, kTestMessageToA1); + wait.Wait(); +} + +TEST_F(TrapTest, CloseTrapAfterImplicitTriggerRemoval) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + static const char kTestMessageToA[] = "hey a"; + + MojoHandle t; + TriggerHelper helper; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + + base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + + uintptr_t readable_a_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + EXPECT_EQ(MOJO_RESULT_OK, event.result); + EXPECT_EQ(kTestMessageToA, ReadMessage(a)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, 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 trap here. + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); + + wait.Signal(); + }); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_a_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + WriteMessage(b, kTestMessageToA); + wait.Wait(); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); +} + +TEST_F(TrapTest, OtherThreadRemovesTriggerDuringEventHandler) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + static const char kTestMessageToA[] = "hey a"; + + MojoHandle t; + TriggerHelper helper; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + + 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( + [&](const MojoTrapEvent& event) { + EXPECT_EQ(MOJO_RESULT_OK, event.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; + }, + [&] { + EXPECT_TRUE(callback_done); + wait_for_cancellation.Signal(); + }); + + ThreadedRunner runner(base::BindOnce(base::BindLambdaForTesting([&] { + wait_for_notification.Wait(); + + // Cancel the watch while the notification is still running. + EXPECT_EQ(MOJO_RESULT_OK, + MojoRemoveTrigger(t, readable_a_context, nullptr)); + + wait_for_cancellation.Wait(); + + EXPECT_TRUE(callback_done); + }))); + runner.Start(); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_a_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + WriteMessage(b, kTestMessageToA); + runner.Join(); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); +} + +TEST_F(TrapTest, TriggersRemoveEachOtherWithinEventHandlers) { + 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_trap; + MojoHandle b_trap; + TriggerHelper helper; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&a_trap)); + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&b_trap)); + + // We set up two traps, one triggered on |a| readability and one triggered on + // |b| readability. Each removes the other's trigger from within its own event + // handler. This should be safe, i.e., it should not deadlock in spite of the + // fact that we also guarantee mutually exclusive event handler invocation + // (including cancellations) on any given trap. + bool a_cancelled = false; + bool b_cancelled = false; + static uintptr_t readable_b_context; + uintptr_t readable_a_context = helper.CreateContextWithCancel( + [&](const MojoTrapEvent& event) { + EXPECT_EQ(MOJO_RESULT_OK, event.result); + EXPECT_EQ(kTestMessageToA, ReadMessage(a)); + wait_for_a_to_notify.Signal(); + wait_for_b_to_notify.Wait(); + EXPECT_EQ(MOJO_RESULT_OK, + MojoRemoveTrigger(b_trap, readable_b_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b_trap)); + }, + [&] { + a_cancelled = true; + wait_for_a_to_cancel.Signal(); + wait_for_b_to_cancel.Wait(); + }); + + readable_b_context = helper.CreateContextWithCancel( + [&](const MojoTrapEvent& event) { + EXPECT_EQ(MOJO_RESULT_OK, event.result); + EXPECT_EQ(kTestMessageToB, ReadMessage(b)); + wait_for_b_to_notify.Signal(); + wait_for_a_to_notify.Wait(); + EXPECT_EQ(MOJO_RESULT_OK, + MojoRemoveTrigger(a_trap, readable_a_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a_trap)); + }, + [&] { + b_cancelled = true; + wait_for_b_to_cancel.Signal(); + wait_for_a_to_cancel.Wait(); + }); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(a_trap, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_a_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(a_trap, nullptr, nullptr, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(b_trap, b, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_b_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(b_trap, nullptr, nullptr, nullptr)); + + ThreadedRunner runner(base::BindOnce( + [](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(TrapTest, AlwaysCancel) { + // Basic sanity check to ensure that all possible ways to remove a trigger + // result in a final MOJO_RESULT_CANCELLED notification. + + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + MojoHandle t; + TriggerHelper helper; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + + base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + auto ignore_event = [](const MojoTrapEvent&) {}; + auto signal_wait = [&] { wait.Signal(); }; + + // Cancel via |MojoRemoveTrigger()|. + uintptr_t context = helper.CreateContextWithCancel(ignore_event, signal_wait); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, context, + nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoRemoveTrigger(t, context, nullptr)); + wait.Wait(); + wait.Reset(); + + // Cancel by closing the trigger's watched handle. + context = helper.CreateContextWithCancel(ignore_event, signal_wait); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, context, + nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); + wait.Wait(); + wait.Reset(); + + // Cancel by closing the trap handle. + context = helper.CreateContextWithCancel(ignore_event, signal_wait); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, b, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, context, + nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); + wait.Wait(); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); +} + +TEST_F(TrapTest, ArmFailureCirculation) { + // Sanity check to ensure that all ready trigger events will eventually be + // returned over a finite number of calls to MojoArmTrap(). + + 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 trap and watch all of them for readability. + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, MojoCreateTrap(&ExpectOnlyCancel, nullptr, &t)); + for (size_t i = 0; i < kNumTestHandles; ++i) { + EXPECT_EQ( + MOJO_RESULT_OK, + MojoAddTrigger(t, handles[i], MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, i, nullptr)); + } + + // Keep trying to arm |t| until every trigger gets an entry in + // |ready_contexts|. If MojoArmTrap() is well-behaved, this should terminate + // eventually. + std::set<uintptr_t> ready_contexts; + while (ready_contexts.size() < kNumTestHandles) { + uint32_t num_blocking_events = 1; + MojoTrapEvent blocking_event = {sizeof(blocking_event)}; + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_event)); + EXPECT_EQ(1u, num_blocking_events); + EXPECT_EQ(MOJO_RESULT_OK, blocking_event.result); + ready_contexts.insert(blocking_event.trigger_context); + } + + for (size_t i = 0; i < kNumTestHandles; ++i) + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handles[i])); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); +} + +TEST_F(TrapTest, TriggerOnUnsatisfiedSignals) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + TriggerHelper helper; + const uintptr_t readable_a_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + EXPECT_EQ(MOJO_RESULT_OK, event.result); + wait.Signal(); + }); + + MojoHandle t; + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + readable_a_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + const char kMessage[] = "this is not a message"; + + WriteMessage(b, kMessage); + wait.Wait(); + + // Now we know |a| is readable. Remove the trigger and add a new one to watch + // for a not-readable state. + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); + const uintptr_t not_readable_a_context = + helper.CreateContext([&](const MojoTrapEvent& event) { + EXPECT_EQ(MOJO_RESULT_OK, event.result); + wait.Signal(); + }); + EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t)); + EXPECT_EQ(MOJO_RESULT_OK, + MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_UNSATISFIED, + not_readable_a_context, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr)); + + // This should not block, because the event should be signaled by + // |not_readable_a_context| when we read the only available message off of + // |a|. + wait.Reset(); + EXPECT_EQ(kMessage, ReadMessage(a)); + wait.Wait(); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); +} + +base::Closure g_do_random_thing_callback; + +void ReadAllMessages(const MojoTrapEvent* event) { + if (event->result == MOJO_RESULT_OK) { + MojoHandle handle = static_cast<MojoHandle>(event->trigger_context); + MojoMessageHandle message; + while (MojoReadMessage(handle, nullptr, &message) == MOJO_RESULT_OK) + MojoDestroyMessage(message); + } + + constexpr size_t kNumRandomThingsToDoOnNotify = 5; + for (size_t i = 0; i < kNumRandomThingsToDoOnNotify; ++i) + g_do_random_thing_callback.Run(); +} + +MojoHandle RandomHandle(MojoHandle* handles, size_t size) { + return handles[base::RandInt(0, static_cast<int>(size) - 1)]; +} + +void DoRandomThing(MojoHandle* traps, + size_t num_traps, + MojoHandle* watched_handles, + size_t num_watched_handles) { + switch (base::RandInt(0, 10)) { + case 0: + MojoClose(RandomHandle(traps, num_traps)); + break; + case 1: + MojoClose(RandomHandle(watched_handles, num_watched_handles)); + break; + case 2: + case 3: + case 4: { + MojoMessageHandle message; + ASSERT_EQ(MOJO_RESULT_OK, MojoCreateMessage(nullptr, &message)); + ASSERT_EQ(MOJO_RESULT_OK, + MojoSetMessageContext(message, 1, nullptr, nullptr, nullptr)); + MojoWriteMessage(RandomHandle(watched_handles, num_watched_handles), + message, nullptr); + break; + } + case 5: + case 6: { + MojoHandle t = RandomHandle(traps, num_traps); + MojoHandle h = RandomHandle(watched_handles, num_watched_handles); + MojoAddTrigger(t, h, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + static_cast<uintptr_t>(h), nullptr); + break; + } + case 7: + case 8: { + uint32_t num_blocking_events = 1; + MojoTrapEvent blocking_event = {sizeof(blocking_event)}; + if (MojoArmTrap(RandomHandle(traps, num_traps), nullptr, + &num_blocking_events, + &blocking_event) == MOJO_RESULT_FAILED_PRECONDITION && + blocking_event.result == MOJO_RESULT_OK) { + ReadAllMessages(&blocking_event); + } + break; + } + case 9: + case 10: { + MojoHandle t = RandomHandle(traps, num_traps); + MojoHandle h = RandomHandle(watched_handles, num_watched_handles); + MojoRemoveTrigger(t, static_cast<uintptr_t>(h), nullptr); + break; + } + default: + NOTREACHED(); + break; + } +} + +TEST_F(TrapTest, ConcurrencyStressTest) { + // Regression test for https://crbug.com/740044. Exercises racy usage of the + // trap API to weed out potential crashes. + + constexpr size_t kNumTraps = 50; + constexpr size_t kNumWatchedHandles = 50; + static_assert(kNumWatchedHandles % 2 == 0, "Invalid number of test handles."); + + constexpr size_t kNumThreads = 10; + static constexpr size_t kNumOperationsPerThread = 400; + + MojoHandle traps[kNumTraps]; + MojoHandle watched_handles[kNumWatchedHandles]; + g_do_random_thing_callback = base::BindRepeating( + &DoRandomThing, traps, kNumTraps, watched_handles, kNumWatchedHandles); + + for (size_t i = 0; i < kNumTraps; ++i) + MojoCreateTrap(&ReadAllMessages, nullptr, &traps[i]); + for (size_t i = 0; i < kNumWatchedHandles; i += 2) + CreateMessagePipe(&watched_handles[i], &watched_handles[i + 1]); + + std::unique_ptr<ThreadedRunner> threads[kNumThreads]; + for (size_t i = 0; i < kNumThreads; ++i) { + threads[i] = std::make_unique<ThreadedRunner>(base::BindOnce([] { + for (size_t i = 0; i < kNumOperationsPerThread; ++i) + g_do_random_thing_callback.Run(); + })); + threads[i]->Start(); + } + for (size_t i = 0; i < kNumThreads; ++i) + threads[i]->Join(); + for (size_t i = 0; i < kNumTraps; ++i) + MojoClose(traps[i]); + for (size_t i = 0; i < kNumWatchedHandles; ++i) + MojoClose(watched_handles[i]); +} + +} // namespace +} // namespace core +} // namespace mojo diff --git a/chromium/mojo/edk/system/user_message_impl.cc b/chromium/mojo/core/user_message_impl.cc index dc36c1f99e7..d4a4da16a23 100644 --- a/chromium/mojo/edk/system/user_message_impl.cc +++ b/chromium/mojo/core/user_message_impl.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/edk/system/user_message_impl.h" +#include "mojo/core/user_message_impl.h" #include <algorithm> #include <vector> @@ -17,16 +17,16 @@ #include "base/trace_event/memory_dump_manager.h" #include "base/trace_event/memory_dump_provider.h" #include "base/trace_event/trace_event.h" -#include "mojo/edk/system/core.h" -#include "mojo/edk/system/node_channel.h" -#include "mojo/edk/system/node_controller.h" -#include "mojo/edk/system/ports/event.h" -#include "mojo/edk/system/ports/message_filter.h" -#include "mojo/edk/system/ports/node.h" +#include "mojo/core/core.h" +#include "mojo/core/node_channel.h" +#include "mojo/core/node_controller.h" +#include "mojo/core/ports/event.h" +#include "mojo/core/ports/message_filter.h" +#include "mojo/core/ports/node.h" #include "mojo/public/c/system/types.h" namespace mojo { -namespace edk { +namespace core { namespace { @@ -152,7 +152,7 @@ MojoResult CreateOrExtendSerializedEventMessage( DispatcherHeader* new_dispatcher_headers; char* new_dispatcher_data; size_t total_num_dispatchers = num_new_dispatchers; - std::vector<ScopedInternalPlatformHandle> handles; + std::vector<PlatformHandle> handles; if (original_message) { DCHECK(original_header); size_t original_dispatcher_headers_size = @@ -176,9 +176,12 @@ MojoResult CreateOrExtendSerializedEventMessage( memcpy(dispatcher_data, original_dispatcher_data, original_dispatcher_data_size); new_dispatcher_data = dispatcher_data + original_dispatcher_data_size; - handles = original_message->TakeHandles(); - if (!handles.empty()) + auto handles_in_transit = original_message->TakeHandles(); + if (!handles_in_transit.empty()) { handles.resize(num_handles); + for (size_t i = 0; i < handles_in_transit.size(); ++i) + handles[i] = handles_in_transit[i].TakeHandle(); + } memcpy(reinterpret_cast<char*>(header) + header_size, reinterpret_cast<char*>(original_header) + original_header_size, original_payload_size); @@ -190,9 +193,8 @@ MojoResult CreateOrExtendSerializedEventMessage( reinterpret_cast<char*>(new_dispatcher_headers + num_new_dispatchers); } - if (handles.empty() && num_new_handles) { + if (handles.empty() && num_new_handles) handles.resize(num_new_handles); - } header->num_dispatchers = base::CheckedNumeric<uint32_t>(total_num_dispatchers).ValueOrDie(); @@ -240,7 +242,7 @@ MojoResult CreateOrExtendSerializedEventMessage( // retain ownership when message creation fails, so these are not actually // leaking. for (auto& handle : handles) - ignore_result(handle.release()); + handle.release(); // Leave the original message in place on failure if applicable. if (original_message) @@ -582,8 +584,13 @@ MojoResult UserMessageImpl::ExtractSerializedHandles( dispatcher_headers + header->num_dispatchers); size_t port_index = 0; size_t platform_handle_index = 0; - std::vector<ScopedInternalPlatformHandle> msg_handles = + std::vector<PlatformHandleInTransit> handles_in_transit = channel_message_->TakeHandles(); + std::vector<PlatformHandle> msg_handles(handles_in_transit.size()); + for (size_t i = 0; i < handles_in_transit.size(); ++i) { + DCHECK(!handles_in_transit[i].owning_process().is_valid()); + msg_handles[i] = handles_in_transit[i].TakeHandle(); + } for (size_t i = 0; i < header->num_dispatchers; ++i) { const DispatcherHeader& dh = dispatcher_headers[i]; auto type = static_cast<Dispatcher::Type>(dh.type); @@ -610,7 +617,7 @@ MojoResult UserMessageImpl::ExtractSerializedHandles( return MOJO_RESULT_ABORTED; } - ScopedInternalPlatformHandle* out_handles = + PlatformHandle* out_handles = !msg_handles.empty() ? msg_handles.data() + platform_handle_index : nullptr; dispatchers[i].dispatcher = Dispatcher::Deserialize( @@ -669,5 +676,11 @@ bool UserMessageImpl::WillBeRoutedExternally() { return result == MOJO_RESULT_OK || result == MOJO_RESULT_FAILED_PRECONDITION; } -} // namespace edk +size_t UserMessageImpl::GetSizeIfSerialized() const { + if (!IsSerialized()) + return 0; + return user_payload_size_; +} + +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/user_message_impl.h b/chromium/mojo/core/user_message_impl.h index dc974c31077..c6a15f98c78 100644 --- a/chromium/mojo/edk/system/user_message_impl.h +++ b/chromium/mojo/core/user_message_impl.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_EDK_SYSTEM_USER_MESSAGE_IMPL_H_ -#define MOJO_EDK_SYSTEM_USER_MESSAGE_IMPL_H_ +#ifndef MOJO_CORE_USER_MESSAGE_IMPL_H_ +#define MOJO_CORE_USER_MESSAGE_IMPL_H_ #include <memory> #include <utility> @@ -11,19 +11,18 @@ #include "base/macros.h" #include "base/optional.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/channel.h" -#include "mojo/edk/system/dispatcher.h" -#include "mojo/edk/system/ports/event.h" -#include "mojo/edk/system/ports/name.h" -#include "mojo/edk/system/ports/port_ref.h" -#include "mojo/edk/system/ports/user_message.h" -#include "mojo/edk/system/system_impl_export.h" +#include "mojo/core/channel.h" +#include "mojo/core/dispatcher.h" +#include "mojo/core/ports/event.h" +#include "mojo/core/ports/name.h" +#include "mojo/core/ports/port_ref.h" +#include "mojo/core/ports/user_message.h" +#include "mojo/core/system_impl_export.h" #include "mojo/public/c/system/message_pipe.h" #include "mojo/public/c/system/types.h" namespace mojo { -namespace edk { +namespace core { // UserMessageImpl is the sole implementation of ports::UserMessage used to // attach message data to any ports::UserMessageEvent. @@ -170,6 +169,7 @@ class MOJO_SYSTEM_IMPL_EXPORT UserMessageImpl : public ports::UserMessage { // UserMessage: bool WillBeRoutedExternally() override; + size_t GetSizeIfSerialized() const override; // The event which owns this serialized message. Not owned. ports::UserMessageEvent* const message_event_; @@ -212,7 +212,7 @@ class MOJO_SYSTEM_IMPL_EXPORT UserMessageImpl : public ports::UserMessage { DISALLOW_COPY_AND_ASSIGN(UserMessageImpl); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_USER_MESSAGE_IMPL_H_ +#endif // MOJO_CORE_USER_MESSAGE_IMPL_H_ diff --git a/chromium/mojo/edk/system/watch.cc b/chromium/mojo/core/watch.cc index bc1d84337e4..996f0a7ca24 100644 --- a/chromium/mojo/edk/system/watch.cc +++ b/chromium/mojo/core/watch.cc @@ -2,13 +2,13 @@ // 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/core/watch.h" -#include "mojo/edk/system/request_context.h" -#include "mojo/edk/system/watcher_dispatcher.h" +#include "mojo/core/request_context.h" +#include "mojo/core/watcher_dispatcher.h" namespace mojo { -namespace edk { +namespace core { Watch::Watch(const scoped_refptr<WatcherDispatcher>& watcher, const scoped_refptr<Dispatcher>& dispatcher, @@ -86,5 +86,5 @@ void Watch::AssertWatcherLockAcquired() const { } #endif -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/watch.h b/chromium/mojo/core/watch.h index 07d6169379a..b2a81658332 100644 --- a/chromium/mojo/edk/system/watch.h +++ b/chromium/mojo/core/watch.h @@ -2,18 +2,18 @@ // 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_ +#ifndef MOJO_CORE_WATCH_H_ +#define MOJO_CORE_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" +#include "mojo/core/atomic_flag.h" +#include "mojo/core/handle_signals_state.h" #include "mojo/public/c/system/trap.h" namespace mojo { -namespace edk { +namespace core { class Dispatcher; class WatcherDispatcher; @@ -121,7 +121,7 @@ class Watch : public base::RefCountedThreadSafe<Watch> { DISALLOW_COPY_AND_ASSIGN(Watch); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_WATCH_H_ +#endif // MOJO_CORE_WATCH_H_ diff --git a/chromium/mojo/edk/system/watcher_dispatcher.cc b/chromium/mojo/core/watcher_dispatcher.cc index 6e8a44280ef..f2eb2e253d7 100644 --- a/chromium/mojo/edk/system/watcher_dispatcher.cc +++ b/chromium/mojo/core/watcher_dispatcher.cc @@ -2,7 +2,7 @@ // 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 "mojo/core/watcher_dispatcher.h" #include <algorithm> #include <limits> @@ -10,10 +10,10 @@ #include "base/debug/alias.h" #include "base/macros.h" #include "base/memory/ptr_util.h" -#include "mojo/edk/system/watch.h" +#include "mojo/core/watch.h" namespace mojo { -namespace edk { +namespace core { WatcherDispatcher::WatcherDispatcher(MojoTrapEventHandler handler) : handler_(handler) {} @@ -204,16 +204,11 @@ MojoResult WatcherDispatcher::CancelWatch(uintptr_t context) { return MOJO_RESULT_OK; } -MojoResult WatcherDispatcher::Arm( - uint32_t* num_ready_contexts, - uintptr_t* ready_contexts, - MojoResult* ready_results, - MojoHandleSignalsState* ready_signals_states) { +MojoResult WatcherDispatcher::Arm(uint32_t* num_blocking_events, + MojoTrapEvent* blocking_events) { base::AutoLock lock(lock_); - if (num_ready_contexts && - (!ready_contexts || !ready_results || !ready_signals_states)) { + if (num_blocking_events && !blocking_events) return MOJO_RESULT_INVALID_ARGUMENT; - } if (closed_) return MOJO_RESULT_INVALID_ARGUMENT; @@ -226,10 +221,10 @@ MojoResult WatcherDispatcher::Arm( return MOJO_RESULT_OK; } - if (num_ready_contexts) { + if (num_blocking_events) { 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())); + *num_blocking_events = std::min( + *num_blocking_events, static_cast<uint32_t>(ready_watches_.size())); WatchSet::const_iterator next_ready_iter = ready_watches_.begin(); if (last_watch_to_block_arming_) { @@ -242,11 +237,14 @@ MojoResult WatcherDispatcher::Arm( next_ready_iter = ready_watches_.begin(); } - for (size_t i = 0; i < *num_ready_contexts; ++i) { + for (size_t i = 0; i < *num_blocking_events; ++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(); + if (blocking_events[i].struct_size < sizeof(*blocking_events)) + return MOJO_RESULT_INVALID_ARGUMENT; + blocking_events[i].flags = MOJO_TRAP_EVENT_FLAG_WITHIN_API_CALL; + blocking_events[i].trigger_context = watch->context(); + blocking_events[i].result = watch->last_known_result(); + blocking_events[i].signals_state = watch->last_known_signals_state(); // Iterate and wrap around. last_watch_to_block_arming_ = watch; @@ -261,5 +259,5 @@ MojoResult WatcherDispatcher::Arm( WatcherDispatcher::~WatcherDispatcher() = default; -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/watcher_dispatcher.h b/chromium/mojo/core/watcher_dispatcher.h index 3048a58833e..6a8bceda9fd 100644 --- a/chromium/mojo/edk/system/watcher_dispatcher.h +++ b/chromium/mojo/core/watcher_dispatcher.h @@ -2,8 +2,8 @@ // 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_ +#ifndef MOJO_CORE_WATCHER_DISPATCHER_H_ +#define MOJO_CORE_WATCHER_DISPATCHER_H_ #include <stdint.h> @@ -13,13 +13,13 @@ #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/core/dispatcher.h" +#include "mojo/core/handle_signals_state.h" +#include "mojo/core/system_impl_export.h" #include "mojo/public/c/system/trap.h" namespace mojo { -namespace edk { +namespace core { class Watch; @@ -50,10 +50,8 @@ class WatcherDispatcher : public Dispatcher { MojoTriggerCondition condition, 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; + MojoResult Arm(uint32_t* num_blocking_events, + MojoTrapEvent* blocking_events) override; private: friend class Watch; @@ -98,7 +96,7 @@ class WatcherDispatcher : public Dispatcher { DISALLOW_COPY_AND_ASSIGN(WatcherDispatcher); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_WATCHER_DISPATCHER_H_ +#endif // MOJO_CORE_WATCHER_DISPATCHER_H_ diff --git a/chromium/mojo/edk/system/watcher_set.cc b/chromium/mojo/core/watcher_set.cc index 0355b58795f..98aa1afe7cb 100644 --- a/chromium/mojo/edk/system/watcher_set.cc +++ b/chromium/mojo/core/watcher_set.cc @@ -2,12 +2,12 @@ // 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 "mojo/core/watcher_set.h" #include <utility> namespace mojo { -namespace edk { +namespace core { WatcherSet::WatcherSet(Dispatcher* owner) : owner_(owner) {} @@ -78,5 +78,5 @@ WatcherSet::Entry::~Entry() = default; WatcherSet::Entry& WatcherSet::Entry::operator=(Entry&& other) = default; -} // namespace edk +} // namespace core } // namespace mojo diff --git a/chromium/mojo/edk/system/watcher_set.h b/chromium/mojo/core/watcher_set.h index 77ddd88e5ff..6abd43e0999 100644 --- a/chromium/mojo/edk/system/watcher_set.h +++ b/chromium/mojo/core/watcher_set.h @@ -2,18 +2,18 @@ // 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_ +#ifndef MOJO_CORE_WATCHER_SET_H_ +#define MOJO_CORE_WATCHER_SET_H_ #include "base/containers/flat_map.h" #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" +#include "mojo/core/handle_signals_state.h" +#include "mojo/core/watcher_dispatcher.h" namespace mojo { -namespace edk { +namespace core { // A WatcherSet maintains a set of references to WatcherDispatchers to be // notified when a handle changes state. @@ -64,7 +64,7 @@ class WatcherSet { DISALLOW_COPY_AND_ASSIGN(WatcherSet); }; -} // namespace edk +} // namespace core } // namespace mojo -#endif // MOJO_EDK_SYSTEM_WATCHER_SET_H_ +#endif // MOJO_CORE_WATCHER_SET_H_ diff --git a/chromium/mojo/edk/BUILD.gn b/chromium/mojo/edk/BUILD.gn deleted file mode 100644 index 0fbbe1588a3..00000000000 --- a/chromium/mojo/edk/BUILD.gn +++ /dev/null @@ -1,304 +0,0 @@ -# Copyright 2018 The Chromium 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") - -# Targets should depend on this if directly referencing the |mojo::edk| -# namespace. -component("edk") { - output_name = "mojo_edk" - - public = [ - "embedder/embedder.h", - "embedder/incoming_broker_client_invitation.h", - "embedder/outgoing_broker_client_invitation.h", - "embedder/peer_connection.h", - ] - - sources = [ - "embedder/embedder.cc", - "embedder/incoming_broker_client_invitation.cc", - "embedder/outgoing_broker_client_invitation.cc", - "embedder/peer_connection.cc", - ] - - defines = [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ] - - deps = [] - if (!is_nacl) { - deps += [ "//crypto" ] - } - - public_deps = [ - ":impl_for_edk", - "//mojo/public/cpp/platform", - "//mojo/public/cpp/system", - ] -} - -# Bits of the EDK library which do not depend on public API linkage. It is -# not allowed for this target or any of its transitive dependencies to depend -# on anything under //mojo/public beyond strict C type definitions. -# -# This is templated because it's consumed by both the ":edk" component library -# as well as the ":mojo_core" shared library. In the former case we want to -# export symbols, but in the latter case we don't. The template stamps out two -# nearly identical targets which differ only in what symbols they export. -template("core_impl_source_set") { - source_set(target_name) { - if (invoker.for_mojo_core) { - visibility = [ ":mojo_core" ] - } else { - visibility = [ ":edk" ] - } - - public = [ - "embedder/configuration.h", - "embedder/connection_params.h", - "embedder/entrypoints.h", - "embedder/named_platform_channel_pair.h", - "embedder/named_platform_handle.h", - "embedder/named_platform_handle_utils.h", - "embedder/platform_channel_pair.h", - "embedder/platform_handle.h", - "embedder/platform_handle_utils.h", - "embedder/process_error_callback.h", - "embedder/scoped_ipc_support.h", - "embedder/scoped_platform_handle.h", - "embedder/transport_protocol.h", - "system/channel.h", - "system/configuration.h", - "system/core.h", - "system/data_pipe_consumer_dispatcher.h", - "system/data_pipe_control_message.h", - "system/data_pipe_producer_dispatcher.h", - "system/dispatcher.h", - "system/handle_signals_state.h", - "system/handle_table.h", - "system/invitation_dispatcher.h", - "system/message_pipe_dispatcher.h", - "system/node_controller.h", - "system/options_validation.h", - "system/platform_handle_dispatcher.h", - "system/platform_shared_memory_mapping.h", - "system/request_context.h", - "system/scoped_process_handle.h", - "system/shared_buffer_dispatcher.h", - "system/user_message_impl.h", - ] - - if (is_win) { - public += [ "embedder/named_platform_handle_win.h" ] - } else if (is_posix || is_fuchsia) { - public += [ "embedder/named_platform_handle_posix.h" ] - } - - sources = [ - "embedder/connection_params.cc", - "embedder/entrypoints.cc", - "embedder/named_platform_handle_utils_win.cc", - "embedder/platform_channel_pair.cc", - "embedder/platform_channel_pair_win.cc", - "embedder/platform_handle.cc", - "embedder/platform_handle_utils.cc", - "embedder/platform_handle_utils_win.cc", - "embedder/scoped_ipc_support.cc", - "system/atomic_flag.h", - "system/broker.h", - "system/broker_win.cc", - "system/channel.cc", - "system/channel_win.cc", - "system/configuration.cc", - "system/core.cc", - "system/data_pipe_consumer_dispatcher.cc", - "system/data_pipe_control_message.cc", - "system/data_pipe_producer_dispatcher.cc", - "system/dispatcher.cc", - "system/handle_table.cc", - "system/invitation_dispatcher.cc", - "system/message_pipe_dispatcher.cc", - "system/node_channel.cc", - "system/node_channel.h", - "system/node_controller.cc", - "system/platform_handle_dispatcher.cc", - "system/platform_shared_memory_mapping.cc", - "system/request_context.cc", - "system/scoped_process_handle.cc", - "system/shared_buffer_dispatcher.cc", - "system/user_message_impl.cc", - "system/watch.cc", - "system/watch.h", - "system/watcher_dispatcher.cc", - "system/watcher_dispatcher.h", - "system/watcher_set.cc", - "system/watcher_set.h", - ] - - public_deps = [ - "//base", - "//mojo/edk/system/ports", - "//mojo/public/c/system:headers", - "//mojo/public/cpp/platform", - ] - - if (is_fuchsia) { - sources += [ - "embedder/named_platform_handle_utils_fuchsia.cc", - "embedder/platform_channel_pair_fuchsia.cc", - "embedder/platform_handle_utils_fuchsia.cc", - "system/channel_fuchsia.cc", - ] - - public_deps += [ "//third_party/fuchsia-sdk:fdio" ] - } - - if (is_posix) { - sources += [ - "embedder/platform_channel_pair_posix.cc", - "embedder/platform_handle_utils_posix.cc", - ] - - if (!is_nacl || is_nacl_nonsfi) { - public += [ "embedder/platform_channel_utils_posix.h" ] - sources += [ - "embedder/platform_channel_utils_posix.cc", - "system/broker_posix.cc", - "system/channel_posix.cc", - ] - } - - if (!is_nacl) { - sources += [ "embedder/named_platform_handle_utils_posix.cc" ] - } - } - - if (is_mac && !is_ios) { - sources += [ - "system/mach_port_relay.cc", - "system/mach_port_relay.h", - ] - } - - if (!is_nacl || is_nacl_nonsfi) { - sources += [ - "system/broker_host.cc", - "system/broker_host.h", - ] - } - - defines = [] - if (!invoker.for_mojo_core) { - defines += [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ] - } - - deps = [] - if (is_android) { - deps += [ "//third_party/ashmem" ] - } - if (!is_nacl) { - sources += [ "embedder/named_platform_channel_pair.cc" ] - deps += [ "//crypto" ] - } - - if (is_win) { - cflags = [ "/wd4324" ] # Structure was padded due to __declspec(align()), - # which is uninteresting. - } - - # 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" ] - } - } -} - -core_impl_source_set("impl_for_edk") { - for_mojo_core = false -} - -if (is_chromeos || is_linux || is_android) { - core_impl_source_set("impl_for_mojo_core") { - for_mojo_core = true - } - - shared_library("mojo_core") { - sources = [ - "mojo_core.cc", - ] - deps = [ - ":impl_for_mojo_core", - "//build/config:exe_and_shlib_deps", - "//mojo/public/c/system:headers", - ] - configs += [ ":export_only_thunks_api" ] - } - - if (is_chromeos) { - if (target_cpu == "arm" || target_cpu == "arm64") { - android32_toolchain = "android_clang_arm" - android64_toolchain = "android_clang_arm64" - } else { - android32_toolchain = "android_clang_x86" - android64_toolchain = "android_clang_x64" - } - - group("mojo_core_for_arc") { - deps = [ - ":mojo_core_arc32", - ":mojo_core_arc64", - ] - } - - copy("mojo_core_arc32") { - sources = [ - "${root_out_dir}/${android32_toolchain}/libmojo_core.so", - ] - outputs = [ - "${root_out_dir}/libmojo_core_arc32.so", - ] - deps = [ - ":mojo_core(//build/toolchain/android:${android32_toolchain})", - ] - } - - copy("mojo_core_arc64") { - sources = [ - "${root_out_dir}/${android64_toolchain}/libmojo_core.so", - ] - outputs = [ - "${root_out_dir}/libmojo_core_arc64.so", - ] - deps = [ - ":mojo_core(//build/toolchain/android:${android64_toolchain})", - ] - } - } - - config("export_only_thunks_api") { - ldflags = [ "-Wl,--version-script=" + - rebase_path("//mojo/edk/export_only_thunks_api.lst") ] - } - - if (is_chromeos || is_linux) { - test("mojo_core_unittests") { - sources = [ - "mojo_core_unittest.cc", - "run_all_core_unittests.cc", - ] - - deps = [ - "//base", - "//base/test:test_support", - "//mojo/public/c/system", - "//testing/gtest", - ] - - data_deps = [ - ":mojo_core", - ] - } - } -} diff --git a/chromium/mojo/edk/embedder/BUILD.gn b/chromium/mojo/edk/embedder/BUILD.gn deleted file mode 100644 index a33988eec4f..00000000000 --- a/chromium/mojo/edk/embedder/BUILD.gn +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2014 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -source_set("embedder_unittests") { - testonly = true - - # TODO: Figure out why this visibility check fails on Android. - # visibility = [ "//mojo:mojo_unittests" ] - - sources = [ - "embedder_unittest.cc", - ] - - if (is_posix) { - sources += [ "platform_channel_pair_posix_unittest.cc" ] - } - - deps = [ - "//base", - "//base/test:test_support", - "//mojo/edk", - "//mojo/edk/system:test_utils", - "//mojo/edk/test:test_support", - "//testing/gtest", - ] -} diff --git a/chromium/mojo/edk/embedder/README.md b/chromium/mojo/edk/embedder/README.md deleted file mode 100644 index a700f48abe9..00000000000 --- a/chromium/mojo/edk/embedder/README.md +++ /dev/null @@ -1,325 +0,0 @@ -# Mojo Embedder Development Kit (EDK) -This document is a subset of the [Mojo documentation](/mojo/README.md). - -[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 a direct -dependency on the GN `//mojo/edk` target. Headers in `mojo/edk/system` are -reserved for internal use by the EDK only. - -**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, 0)); - - // 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 -`OutgoingBrokerClientInvitation` - 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/outgoing_broker_client_invitation.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::ScopedInternalPlatformHandle 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, 0)); - - 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::OutgoingBrokerClientInvitation invitation; - - base::ProcessHandle child_handle = - LaunchCoolChildProcess(channel.PassClientHandle()); - - // At this point it's safe for |invitation| to go out of scope and nothing - // will break. - invitation.Send(child_handle, channel.PassServerHandle()); - - return 0; -} -``` - -The launched process code uses `IncomingBrokerClientInvitation` 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 ScopedInternalPlatformHandle that was passed by -// whomever launched this process (i.e. LaunchCoolChildProcess above). -mojo::edk::ScopedInternalPlatformHandle 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, 0)); - - mojo::edk::ScopedIPCSupport ipc_support( - ipc_thread.task_runner(), - mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN); - - mojo::edk::IncomingBrokerClientInvitation::Accept(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_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: `OutgoingBrokerClientInvitation` has an -`AttachMessagePipe` method which synthesizes a new solitary message pipe -endpoint for your immediate use, and attaches the other end to the invitation -such that it can later be extracted by name by the invitee from the -`IncomingBrokerClientInvitation`. - -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/outgoing_broker_client_invitation.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::ScopedInternalPlatformHandle 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, 0)); - - mojo::edk::ScopedIPCSupport ipc_support( - ipc_thread.task_runner(), - mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN); - - mojo::edk::PlatformChannelPair channel; - - mojo::edk::OutgoingBrokerClientInvitation invitation; - - // Create a new message pipe with one end being retrievable in the new - // process. Note that the name chosen for the attachment is arbitrary and - // scoped to this invitation. - mojo::ScopedMessagePipeHandle my_pipe = - invitation.AttachMessagePipe("pretty_cool_pipe"); - - base::ProcessHandle child_handle = - LaunchCoolChildProcess(channel.PassClientHandle()); - invitation.Send( - child_handle, - mojo::edk::ConnectionParams(mojo::edk::TransportProtocol::kLegacy, - 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/run_loop/run_loop.h" -#include "base/threading/thread.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/embedder/incoming_broker_client_invitation.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::ScopedInternalPlatformHandle 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) { - 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); - - auto invitation = mojo::edk::IncomingBrokerClientInvitation::Accept( - mojo::edk::ConnectionParams(mojo::edk::TransportProtocol::kLegacy, - GetChannelHandle())); - - mojo::ScopedMessagePipeHandle my_pipe = - invitation->ExtractMessagePipe("pretty_cool_pipe"); - - FooImpl impl(local::mojom::FooRequest(std::move(my_pipe))); - - // 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/README.md#High_Level-Bindings-APIs). - diff --git a/chromium/mojo/edk/embedder/connection_params.cc b/chromium/mojo/edk/embedder/connection_params.cc deleted file mode 100644 index 8e53d4f5644..00000000000 --- a/chromium/mojo/edk/embedder/connection_params.cc +++ /dev/null @@ -1,33 +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> - -#include "base/logging.h" - -namespace mojo { -namespace edk { - -ConnectionParams::ConnectionParams(TransportProtocol protocol, - ScopedInternalPlatformHandle channel) - : protocol_(protocol), channel_(std::move(channel)) { - // TODO(rockot): Support other protocols. - DCHECK_EQ(TransportProtocol::kLegacy, protocol); -} - -ConnectionParams::ConnectionParams(ConnectionParams&& params) { - *this = std::move(params); -} - -ConnectionParams& ConnectionParams::operator=(ConnectionParams&& params) = - default; - -ScopedInternalPlatformHandle ConnectionParams::TakeChannelHandle() { - return std::move(channel_); -} - -} // namespace edk -} // namespace mojo diff --git a/chromium/mojo/edk/embedder/connection_params.h b/chromium/mojo/edk/embedder/connection_params.h deleted file mode 100644 index 1a7c9793439..00000000000 --- a/chromium/mojo/edk/embedder/connection_params.h +++ /dev/null @@ -1,42 +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/embedder/transport_protocol.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace edk { - -// A set of parameters used when establishing a connection to another process. -class MOJO_SYSTEM_IMPL_EXPORT ConnectionParams { - public: - // Configures an OS pipe-based connection of type |type| to the remote process - // using the given transport |protocol|. - ConnectionParams(TransportProtocol protocol, - ScopedInternalPlatformHandle channel); - - ConnectionParams(ConnectionParams&& params); - ConnectionParams& operator=(ConnectionParams&& params); - - TransportProtocol protocol() const { return protocol_; } - - ScopedInternalPlatformHandle TakeChannelHandle(); - - private: - TransportProtocol protocol_; - ScopedInternalPlatformHandle channel_; - - DISALLOW_COPY_AND_ASSIGN(ConnectionParams); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_CONNECTION_PARAMS_H_ diff --git a/chromium/mojo/edk/embedder/embedder.cc b/chromium/mojo/edk/embedder/embedder.cc deleted file mode 100644 index a4a896cbe09..00000000000 --- a/chromium/mojo/edk/embedder/embedder.cc +++ /dev/null @@ -1,80 +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/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 "mojo/edk/embedder/entrypoints.h" -#include "mojo/edk/system/configuration.h" -#include "mojo/edk/system/core.h" -#include "mojo/edk/system/node_controller.h" -#include "mojo/public/c/system/thunks.h" - -#if !defined(OS_NACL) -#include "crypto/random.h" -#endif - -namespace mojo { -namespace edk { - -void Init(const Configuration& configuration) { - internal::g_configuration = configuration; - InitializeCore(); - MojoEmbedderSetSystemThunks(&GetSystemThunks()); -} - -void Init() { - Init(Configuration()); -} - -void SetDefaultProcessErrorCallback(const ProcessErrorCallback& callback) { - Core::Get()->SetDefaultProcessErrorCallback(callback); -} - -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 CreateInternalPlatformHandleWrapper( - ScopedInternalPlatformHandle platform_handle, - MojoHandle* platform_handle_wrapper_handle) { - return Core::Get()->CreateInternalPlatformHandleWrapper( - std::move(platform_handle), platform_handle_wrapper_handle); -} - -MojoResult PassWrappedInternalPlatformHandle( - MojoHandle platform_handle_wrapper_handle, - ScopedInternalPlatformHandle* platform_handle) { - return Core::Get()->PassWrappedInternalPlatformHandle( - platform_handle_wrapper_handle, platform_handle); -} - -scoped_refptr<base::TaskRunner> GetIOTaskRunner() { - return Core::Get()->GetNodeController()->io_task_runner(); -} - -#if defined(OS_MACOSX) && !defined(OS_IOS) -void SetMachPortProvider(base::PortProvider* port_provider) { - DCHECK(port_provider); - Core::Get()->SetMachPortProvider(port_provider); -} -#endif - -} // namespace edk -} // namespace mojo diff --git a/chromium/mojo/edk/embedder/embedder.h b/chromium/mojo/edk/embedder/embedder.h deleted file mode 100644 index 4e0766f3360..00000000000 --- a/chromium/mojo/edk/embedder/embedder.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_EDK_EMBEDDER_EMBEDDER_H_ -#define MOJO_EDK_EMBEDDER_EMBEDDER_H_ - -#include <stddef.h> - -#include <string> - -#include "base/callback.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/configuration.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/types.h" - -namespace base { -class PortProvider; -} - -namespace mojo { -namespace edk { - -using ProcessErrorCallback = base::Callback<void(const std::string& error)>; - -// Basic configuration/initialization ------------------------------------------ - -// Must be called first, or just after setting configuration parameters, to -// initialize the (global, singleton) system state. There is no corresponding -// shutdown operation: once the EDK is initialized, public Mojo C API calls -// remain available for the remainder of the process's lifetime. -MOJO_SYSTEM_IMPL_EXPORT void Init(const Configuration& configuration); - -// Like above but uses a default Configuration. -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. Calling this is optional. -MOJO_SYSTEM_IMPL_EXPORT void SetDefaultProcessErrorCallback( - const ProcessErrorCallback& callback); - -// Generates a random ASCII token string for use with various APIs that expect -// a globally unique token string. May be called at any time on any thread. -MOJO_SYSTEM_IMPL_EXPORT std::string GenerateRandomToken(); - -// Basic functions ------------------------------------------------------------- -// -// The functions in this section are available once |Init()| has been called and -// provide the embedder with some extra capabilities not exposed by public Mojo -// C APIs. - -// Creates a |MojoHandle| that wraps the given |InternalPlatformHandle| (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 CreateInternalPlatformHandleWrapper( - ScopedInternalPlatformHandle platform_handle, - MojoHandle* platform_handle_wrapper_handle); - -// Retrieves the |InternalPlatformHandle| that was wrapped into a |MojoHandle| -// (using |CreateInternalPlatformHandleWrapper()| above). Note that the -// |MojoHandle| is closed on success. -MOJO_SYSTEM_IMPL_EXPORT MojoResult PassWrappedInternalPlatformHandle( - MojoHandle platform_handle_wrapper_handle, - ScopedInternalPlatformHandle* platform_handle); - -// Initialialization/shutdown for interprocess communication (IPC) ------------- - -// Retrieves the TaskRunner used for IPC I/O, as set by ScopedIPCSupport. -MOJO_SYSTEM_IMPL_EXPORT scoped_refptr<base::TaskRunner> GetIOTaskRunner(); - -#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. -// -// If called at all, this must be called while a ScopedIPCSupport exists. -MOJO_SYSTEM_IMPL_EXPORT void SetMachPortProvider( - base::PortProvider* port_provider); -#endif - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_EMBEDDER_H_ diff --git a/chromium/mojo/edk/embedder/incoming_broker_client_invitation.cc b/chromium/mojo/edk/embedder/incoming_broker_client_invitation.cc deleted file mode 100644 index ed201a53656..00000000000 --- a/chromium/mojo/edk/embedder/incoming_broker_client_invitation.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/incoming_broker_client_invitation.h" - -#include "base/command_line.h" -#include "base/logging.h" -#include "base/memory/ptr_util.h" -#include "mojo/edk/embedder/platform_channel_pair.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/core.h" - -namespace mojo { -namespace edk { - -IncomingBrokerClientInvitation::~IncomingBrokerClientInvitation() = default; - -// static -std::unique_ptr<IncomingBrokerClientInvitation> -IncomingBrokerClientInvitation::Accept(ConnectionParams params) { - return base::WrapUnique( - new IncomingBrokerClientInvitation(std::move(params))); -} - -// static -std::unique_ptr<IncomingBrokerClientInvitation> -IncomingBrokerClientInvitation::AcceptFromCommandLine( - TransportProtocol protocol) { - ScopedInternalPlatformHandle platform_channel = - PlatformChannelPair::PassClientHandleFromParentProcess( - *base::CommandLine::ForCurrentProcess()); - DCHECK(platform_channel.is_valid()); - return base::WrapUnique(new IncomingBrokerClientInvitation( - ConnectionParams(protocol, std::move(platform_channel)))); -} - -ScopedMessagePipeHandle IncomingBrokerClientInvitation::ExtractMessagePipe( - const std::string& name) { - return ScopedMessagePipeHandle( - MessagePipeHandle(Core::Get()->ExtractMessagePipeFromInvitation(name))); -} - -IncomingBrokerClientInvitation::IncomingBrokerClientInvitation( - ConnectionParams params) { - Core::Get()->AcceptBrokerClientInvitation(std::move(params)); -} - -} // namespace edk -} // namespace mojo diff --git a/chromium/mojo/edk/embedder/incoming_broker_client_invitation.h b/chromium/mojo/edk/embedder/incoming_broker_client_invitation.h deleted file mode 100644 index 79193ea5564..00000000000 --- a/chromium/mojo/edk/embedder/incoming_broker_client_invitation.h +++ /dev/null @@ -1,58 +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_INCOMING_BROKER_CLIENT_INVITATION_H_ -#define MOJO_EDK_EMBEDDER_INCOMING_BROKER_CLIENT_INVITATION_H_ - -#include <memory> - -#include "base/macros.h" -#include "mojo/edk/embedder/connection_params.h" -#include "mojo/edk/embedder/transport_protocol.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/cpp/system/message_pipe.h" - -namespace mojo { -namespace edk { - -// Mojo embedders may use this to accept a broker client invitation from another -// embedder in the system. -class MOJO_SYSTEM_IMPL_EXPORT IncomingBrokerClientInvitation { - public: - ~IncomingBrokerClientInvitation(); - - // Accepts an incoming invitation received via the connection medium in - // |params|. - static std::unique_ptr<IncomingBrokerClientInvitation> Accept( - ConnectionParams params); - - // Accepts an incoming invitation from the command line. The command line is - // expected to have a |PlatformChannelPair::kMojoPlatformChannelHandleSwitch| - // switch whose value is an integer platform handle identifier (e.g. FD or - // HANDLE) in the calling process. The handle should correspond to one end of - // an OS pipe whose other end was used by another process to send an - // OutgoingBrokerClientInvitation. - static std::unique_ptr<IncomingBrokerClientInvitation> AcceptFromCommandLine( - TransportProtocol protocol); - - // Extracts a named message pipe from the accepted invitation. Must be called - // after Accept() or AcceptFromCommandLine(). - // - // Note that while this returns a usable pipe handle immediately, extraction - // and internal pipe connection is an asynchronous process. Therefore this - // method always returns a valid handle even if no such pipe was attached; in - // such cases where there was no attached pipe named |name|, the returned pipe - // handle will imminently signal peer closure. - ScopedMessagePipeHandle ExtractMessagePipe(const std::string& name); - - private: - explicit IncomingBrokerClientInvitation(ConnectionParams params); - - DISALLOW_COPY_AND_ASSIGN(IncomingBrokerClientInvitation); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_INCOMING_BROKER_CLIENT_INVITATION_H_ diff --git a/chromium/mojo/edk/embedder/named_platform_channel_pair.cc b/chromium/mojo/edk/embedder/named_platform_channel_pair.cc deleted file mode 100644 index 63c45b837d1..00000000000 --- a/chromium/mojo/edk/embedder/named_platform_channel_pair.cc +++ /dev/null @@ -1,101 +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 <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" - -#if defined(OS_WIN) -#include <windows.h> -#endif - -namespace mojo { -namespace edk { - -namespace { - -const char kMojoNamedPlatformChannelPipeSwitch[] = - "mojo-named-platform-channel-pipe"; - -#if defined(OS_WIN) -std::wstring GeneratePipeName( - const NamedPlatformChannelPair::Options& options) { - return base::StringPrintf(L"%u.%u.%I64u", GetCurrentProcessId(), - GetCurrentThreadId(), base::RandUint64()); -} -#else -std::string GeneratePipeName(const NamedPlatformChannelPair::Options& options) { - return options.socket_dir - .AppendASCII(base::NumberToString(base::RandUint64())) - .value(); -} -#endif - -} // namespace - -NamedPlatformChannelPair::NamedPlatformChannelPair( - const NamedPlatformChannelPair::Options& options) - : pipe_handle_(GeneratePipeName(options)) { - CreateServerHandleOptions server_handle_options; -#if defined(OS_WIN) - server_handle_options.security_descriptor = options.security_descriptor; - server_handle_options.enforce_uniqueness = true; -#endif - server_handle_ = CreateServerHandle(pipe_handle_, server_handle_options); - PCHECK(server_handle_.is_valid()); -} - -NamedPlatformChannelPair::~NamedPlatformChannelPair() {} - -ScopedInternalPlatformHandle NamedPlatformChannelPair::PassServerHandle() { - return std::move(server_handle_); -} - -// static -ScopedInternalPlatformHandle -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 ScopedInternalPlatformHandle(); - - 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/chromium/mojo/edk/embedder/named_platform_channel_pair.h b/chromium/mojo/edk/embedder/named_platform_channel_pair.h deleted file mode 100644 index f49400d2d56..00000000000 --- a/chromium/mojo/edk/embedder/named_platform_channel_pair.h +++ /dev/null @@ -1,78 +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/files/file_path.h" -#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; -#else - // On POSIX, every new NamedPlatformChannelPair creates a new server socket - // with a random name. This controls the directory where that happens. - base::FilePath socket_dir; -#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 OutgoingBrokerClientInvitation::Send() only. - ScopedInternalPlatformHandle 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 ScopedInternalPlatformHandle 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_; - ScopedInternalPlatformHandle server_handle_; - - DISALLOW_COPY_AND_ASSIGN(NamedPlatformChannelPair); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_CHANNEL_PAIR_H_ diff --git a/chromium/mojo/edk/embedder/named_platform_handle.h b/chromium/mojo/edk/embedder/named_platform_handle.h deleted file mode 100644 index e212792aef7..00000000000 --- a/chromium/mojo/edk/embedder/named_platform_handle.h +++ /dev/null @@ -1,16 +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 "build/build_config.h" - -#if defined(OS_WIN) -#include "mojo/edk/embedder/named_platform_handle_win.h" -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -#include "mojo/edk/embedder/named_platform_handle_posix.h" -#endif - -#endif // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_H_ diff --git a/chromium/mojo/edk/embedder/named_platform_handle_posix.h b/chromium/mojo/edk/embedder/named_platform_handle_posix.h deleted file mode 100644 index 3c7e0a1e5ec..00000000000 --- a/chromium/mojo/edk/embedder/named_platform_handle_posix.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2018 The Chromium 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_POSIX_H_ -#define MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_POSIX_H_ - -#include <string> - -#include "base/strings/string_piece.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace edk { - -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; -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_POSIX_H_ diff --git a/chromium/mojo/edk/embedder/named_platform_handle_utils.h b/chromium/mojo/edk/embedder/named_platform_handle_utils.h deleted file mode 100644 index 21f9a3d1886..00000000000 --- a/chromium/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 ScopedInternalPlatformHandle -CreateClientHandle(const NamedPlatformHandle& handle); - -// Creates a server platform handle from |handle|. -MOJO_SYSTEM_IMPL_EXPORT ScopedInternalPlatformHandle -CreateServerHandle(const NamedPlatformHandle& handle, - const CreateServerHandleOptions& options = {}); - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_UTILS_H_ diff --git a/chromium/mojo/edk/embedder/named_platform_handle_utils_fuchsia.cc b/chromium/mojo/edk/embedder/named_platform_handle_utils_fuchsia.cc deleted file mode 100644 index f9539e78d76..00000000000 --- a/chromium/mojo/edk/embedder/named_platform_handle_utils_fuchsia.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/named_platform_handle_utils.h" - -#include "mojo/edk/embedder/named_platform_handle.h" - -namespace mojo { -namespace edk { - -ScopedInternalPlatformHandle CreateClientHandle( - const NamedPlatformHandle& named_handle) { - // TODO(fuchsia): Implement, or remove dependencies (crbug.com/754038). - NOTREACHED(); - return ScopedInternalPlatformHandle(); -} - -ScopedInternalPlatformHandle CreateServerHandle( - const NamedPlatformHandle& named_handle, - const CreateServerHandleOptions& options) { - // TODO(fuchsia): Implement, or remove dependencies (crbug.com/754038). - NOTREACHED(); - return ScopedInternalPlatformHandle(); -} - -} // namespace edk -} // namespace mojo diff --git a/chromium/mojo/edk/embedder/named_platform_handle_utils_posix.cc b/chromium/mojo/edk/embedder/named_platform_handle_utils_posix.cc deleted file mode 100644 index 6a8c58b726a..00000000000 --- a/chromium/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 ScopedInternalPlatformHandle containing the -// socket. Otherwise, this returns an invalid ScopedInternalPlatformHandle. -ScopedInternalPlatformHandle CreateUnixDomainSocket(bool needs_connection) { - // Create the unix domain socket. - InternalPlatformHandle socket_handle(socket(AF_UNIX, SOCK_STREAM, 0)); - socket_handle.needs_connection = needs_connection; - ScopedInternalPlatformHandle handle(socket_handle); - if (!handle.is_valid()) { - PLOG(ERROR) << "Failed to create AF_UNIX socket."; - return ScopedInternalPlatformHandle(); - } - - // Now set it as non-blocking. - if (!base::SetNonBlocking(handle.get().handle)) { - PLOG(ERROR) << "base::SetNonBlocking() failed " << handle.get().handle; - return ScopedInternalPlatformHandle(); - } - return handle; -} - -} // namespace - -ScopedInternalPlatformHandle CreateClientHandle( - const NamedPlatformHandle& named_handle) { - if (!named_handle.is_valid()) - return ScopedInternalPlatformHandle(); - - struct sockaddr_un unix_addr; - size_t unix_addr_len; - if (!MakeUnixAddr(named_handle, &unix_addr, &unix_addr_len)) - return ScopedInternalPlatformHandle(); - - ScopedInternalPlatformHandle handle = CreateUnixDomainSocket(false); - if (!handle.is_valid()) - return ScopedInternalPlatformHandle(); - - if (HANDLE_EINTR(connect(handle.get().handle, - reinterpret_cast<sockaddr*>(&unix_addr), - unix_addr_len)) < 0) { - PLOG(ERROR) << "connect " << named_handle.name; - return ScopedInternalPlatformHandle(); - } - - return handle; -} - -ScopedInternalPlatformHandle CreateServerHandle( - const NamedPlatformHandle& named_handle, - const CreateServerHandleOptions& options) { - if (!named_handle.is_valid()) - return ScopedInternalPlatformHandle(); - - // 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 ScopedInternalPlatformHandle(); - } - - // Delete any old FS instances. - if (unlink(named_handle.name.c_str()) < 0 && errno != ENOENT) { - PLOG(ERROR) << "unlink " << named_handle.name; - return ScopedInternalPlatformHandle(); - } - - struct sockaddr_un unix_addr; - size_t unix_addr_len; - if (!MakeUnixAddr(named_handle, &unix_addr, &unix_addr_len)) - return ScopedInternalPlatformHandle(); - - ScopedInternalPlatformHandle handle = CreateUnixDomainSocket(true); - if (!handle.is_valid()) - return ScopedInternalPlatformHandle(); - - // 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 ScopedInternalPlatformHandle(); - } - - // 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 ScopedInternalPlatformHandle(); - } - return handle; -} - -} // namespace edk -} // namespace mojo diff --git a/chromium/mojo/edk/embedder/named_platform_handle_utils_win.cc b/chromium/mojo/edk/embedder/named_platform_handle_utils_win.cc deleted file mode 100644 index 21125a3c034..00000000000 --- a/chromium/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 - -ScopedInternalPlatformHandle CreateClientHandle( - const NamedPlatformHandle& named_handle) { - if (!named_handle.is_valid()) - return ScopedInternalPlatformHandle(); - - base::string16 pipe_name = named_handle.pipe_name(); - - // Note: This may block. - if (!WaitNamedPipeW(pipe_name.c_str(), NMPWAIT_USE_DEFAULT_WAIT)) - return ScopedInternalPlatformHandle(); - - 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; - ScopedInternalPlatformHandle handle( - InternalPlatformHandle(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; -} - -ScopedInternalPlatformHandle CreateServerHandle( - const NamedPlatformHandle& named_handle, - const CreateServerHandleOptions& options) { - if (!named_handle.is_valid()) - return ScopedInternalPlatformHandle(); - - 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; - InternalPlatformHandle 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 ScopedInternalPlatformHandle(handle); -} - -} // namespace edk -} // namespace mojo diff --git a/chromium/mojo/edk/embedder/named_platform_handle_win.h b/chromium/mojo/edk/embedder/named_platform_handle_win.h deleted file mode 100644 index febeb68989e..00000000000 --- a/chromium/mojo/edk/embedder/named_platform_handle_win.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018 The Chromium 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_WIN_H_ -#define MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_WIN_H_ - -#include "base/strings/string16.h" -#include "base/strings/string_piece.h" -#include "base/strings/utf_string_conversions.h" -#include "mojo/edk/system/system_impl_export.h" - -namespace mojo { -namespace edk { - -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; -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_WIN_H_ diff --git a/chromium/mojo/edk/embedder/outgoing_broker_client_invitation.cc b/chromium/mojo/edk/embedder/outgoing_broker_client_invitation.cc deleted file mode 100644 index 6c9ae1ed398..00000000000 --- a/chromium/mojo/edk/embedder/outgoing_broker_client_invitation.cc +++ /dev/null @@ -1,65 +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/outgoing_broker_client_invitation.h" - -#include "base/logging.h" -#include "mojo/edk/system/core.h" -#include "mojo/edk/system/node_controller.h" -#include "mojo/edk/system/ports/port_ref.h" -#include "mojo/edk/system/request_context.h" - -namespace mojo { -namespace edk { - -OutgoingBrokerClientInvitation::OutgoingBrokerClientInvitation() = default; - -OutgoingBrokerClientInvitation::~OutgoingBrokerClientInvitation() { - RequestContext request_context; - for (auto& entry : attached_ports_) - Core::Get()->GetNodeController()->ClosePort(entry.second); -} - -ScopedMessagePipeHandle OutgoingBrokerClientInvitation::AttachMessagePipe( - const std::string& name) { - DCHECK(!sent_); - ports::PortRef port; - ScopedMessagePipeHandle pipe = ScopedMessagePipeHandle( - MessagePipeHandle(Core::Get()->CreatePartialMessagePipe(&port))); - attached_ports_.emplace_back(name, port); - return pipe; -} - -ScopedMessagePipeHandle -OutgoingBrokerClientInvitation::ExtractInProcessMessagePipe( - const std::string& name) { - // NOTE: Efficiency is not really important here. This is not used in normal - // production code and is in practice only called when |attached_ports_| has - // a single entry. - for (auto it = attached_ports_.begin(); it != attached_ports_.end(); ++it) { - if (it->first == name) { - ScopedMessagePipeHandle pipe = ScopedMessagePipeHandle( - MessagePipeHandle(Core::Get()->CreatePartialMessagePipe(it->second))); - attached_ports_.erase(it); - return pipe; - } - } - - NOTREACHED(); - return ScopedMessagePipeHandle(); -} - -void OutgoingBrokerClientInvitation::Send( - base::ProcessHandle target_process, - ConnectionParams params, - const ProcessErrorCallback& error_callback) { - DCHECK(!sent_); - sent_ = true; - Core::Get()->SendBrokerClientInvitation(target_process, std::move(params), - attached_ports_, error_callback); - attached_ports_.clear(); -} - -} // namespace edk -} // namespace mojo diff --git a/chromium/mojo/edk/embedder/outgoing_broker_client_invitation.h b/chromium/mojo/edk/embedder/outgoing_broker_client_invitation.h deleted file mode 100644 index aadf3f8e632..00000000000 --- a/chromium/mojo/edk/embedder/outgoing_broker_client_invitation.h +++ /dev/null @@ -1,93 +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_OUTGOING_BROKER_CLIENT_INVITATION_H_ -#define MOJO_EDK_EMBEDDER_OUTGOING_BROKER_CLIENT_INVITATION_H_ - -#include <stdint.h> - -#include <string> -#include <utility> -#include <vector> - -#include "base/macros.h" -#include "base/process/process_handle.h" -#include "mojo/edk/embedder/connection_params.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/cpp/system/message_pipe.h" - -namespace mojo { -namespace edk { - -namespace ports { -class PortRef; -} - -// Any Mojo embedder which is either the broker process itself or an existing -// broker client can use a OutgoingBrokerClientInvitation to invite a new -// process into broker's group of connected processes. -// -// In order to use OutgoingBrokerClientInvitation, you must have a valid process -// handle to the target process, as well as a valid ConnectionParams -// corresponding to some ConnectionParams in the target process. -// -// It is the embedder's responsibility get a corresponding ConnectionParams into -// the target process somehow (for example, by using file descriptor inheritance -// at process launch to give it the other end of a socket pair) and ensure that -// the target process constructs a corresponding IncomingBrokerClientInvitation -// object for that ConnectionParams. -// -// New message pipes may be attached to a OutgoingBrokerClientInvitation by -// calling AttachMessagePipe(). -// -// The invitation is sent by calling Send(), and once the invitation is sent -// there is no further need to keep the OutgoingBrokerClientInvitation object -// alive. -class MOJO_SYSTEM_IMPL_EXPORT OutgoingBrokerClientInvitation { - public: - OutgoingBrokerClientInvitation(); - ~OutgoingBrokerClientInvitation(); - - // Attaches a new message pipe to this invitation. The returned message pipe - // handle can be used immediately in the calling process. The other end can be - // obtained by the eventual receiver of this invitation, i.e., the target of - // Send(). - // - // NOTE: This must not be called after Send(). - ScopedMessagePipeHandle AttachMessagePipe(const std::string& name); - - // Sends the invitation to the target process. |target_process| must be a - // valid handle and |params| must correspond to some other ConnectionParams - // (e.g. the other half of a socket pair) in the target process. - void Send( - base::ProcessHandle target_process, - ConnectionParams params, - const ProcessErrorCallback& error_callback = ProcessErrorCallback()); - - // Extracts an attached message pipe endpoint by name. For use only when this - // invitation will NOT be sent to a remote process (i.e. Send() will never be - // be called) after all, and the caller wishes to retrieve the message pipe - // endpoint that would have been received. - // - // TODO(rockot): Remove this. It's only here to support content single-process - // mode and the NaCl broker, both of which could be implemented without this - // after some refactoring. - ScopedMessagePipeHandle ExtractInProcessMessagePipe(const std::string& name); - - private: - // List of named ports attached to this invitation. Each port is the peer of - // some corresponding message pipe handle returned by AttachMessagePipe. - std::vector<std::pair<std::string, ports::PortRef>> attached_ports_; - - // Indicates whether the invitation has been sent yet. - bool sent_ = false; - - DISALLOW_COPY_AND_ASSIGN(OutgoingBrokerClientInvitation); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_OUTGOING_BROKER_CLIENT_INVITATION_H_ diff --git a/chromium/mojo/edk/embedder/peer_connection.cc b/chromium/mojo/edk/embedder/peer_connection.cc deleted file mode 100644 index 85344114e90..00000000000 --- a/chromium/mojo/edk/embedder/peer_connection.cc +++ /dev/null @@ -1,31 +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/peer_connection.h" - -#include "mojo/edk/system/core.h" - -namespace mojo { -namespace edk { - -PeerConnection::PeerConnection() = default; - -PeerConnection::~PeerConnection() { - if (is_connected_) - Core::Get()->ClosePeerConnection(connection_id_); -} - -ScopedMessagePipeHandle PeerConnection::Connect(ConnectionParams params) { - DCHECK(!is_connected_); - is_connected_ = true; - - ports::PortRef peer_port; - auto pipe = ScopedMessagePipeHandle( - MessagePipeHandle(Core::Get()->CreatePartialMessagePipe(&peer_port))); - connection_id_ = Core::Get()->ConnectToPeer(std::move(params), peer_port); - return pipe; -} - -} // namespace edk -} // namespace mojo diff --git a/chromium/mojo/edk/embedder/peer_connection.h b/chromium/mojo/edk/embedder/peer_connection.h deleted file mode 100644 index f17fc48f262..00000000000 --- a/chromium/mojo/edk/embedder/peer_connection.h +++ /dev/null @@ -1,54 +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_PEER_CONNECTION_H_ -#define MOJO_EDK_EMBEDDER_PEER_CONNECTION_H_ - -#include "base/macros.h" -#include "mojo/edk/embedder/connection_params.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/cpp/system/message_pipe.h" - -namespace mojo { -namespace edk { - -// Used to connect to a peer process. -// -// NOTE: This should ONLY be used if there is no common ancestor for the -// processes being connected. Peer connections have limited capabilities with -// respect to Mojo IPC when compared to standard broker client connections (see -// OutgoingBrokerClientInvitation and IncomingBrokerClientInvitation), and in -// particular it's undefined behavior to attempt to forward any resources -// (message pipes or other system handles) received from a peer process over to -// any other process to which you're connected. -// -// Both processes must construct a PeerConnection with each one corresponding to -// one end of some shared connection medium (e.g. a platform channel.) -// -// Each PeerConnection gets an implicit cross-process message pipe, the local -// endpoint of which may be acquired by a one-time call to TakeMessagePipe(). -// -// Once established, the connection to the remote peer will remain valid as long -// as each process keeps its respective PeerConnection object alive. -class MOJO_SYSTEM_IMPL_EXPORT PeerConnection { - public: - // Constructs a disconnected connection. - PeerConnection(); - ~PeerConnection(); - - // Connects to the peer and returns a primordial message pipe handle which - // will be connected to a corresponding peer pipe in the remote process. - ScopedMessagePipeHandle Connect(ConnectionParams params); - - private: - bool is_connected_ = false; - uint64_t connection_id_ = 0; - - DISALLOW_COPY_AND_ASSIGN(PeerConnection); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_PEER_CONNECTION_H_ diff --git a/chromium/mojo/edk/embedder/platform_channel_pair.cc b/chromium/mojo/edk/embedder/platform_channel_pair.cc deleted file mode 100644 index 2bb008cdb7b..00000000000 --- a/chromium/mojo/edk/embedder/platform_channel_pair.cc +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/embedder/platform_channel_pair.h" - -#include <utility> - -#include "base/logging.h" -#include "build/build_config.h" - -namespace mojo { -namespace edk { - -const char PlatformChannelPair::kMojoPlatformChannelHandleSwitch[] = - "mojo-platform-channel-handle"; - -PlatformChannelPair::~PlatformChannelPair() { -} - -ScopedInternalPlatformHandle PlatformChannelPair::PassServerHandle() { - return std::move(server_handle_); -} - -ScopedInternalPlatformHandle PlatformChannelPair::PassClientHandle() { - return std::move(client_handle_); -} - -void PlatformChannelPair::ChildProcessLaunched() { - DCHECK(client_handle_.is_valid()); -#if defined(OS_FUCHSIA) - // The |client_handle_| is transferred, not cloned, to the child. - ignore_result(client_handle_.release()); -#else - client_handle_.reset(); -#endif -} - -} // namespace edk -} // namespace mojo diff --git a/chromium/mojo/edk/embedder/platform_channel_pair.h b/chromium/mojo/edk/embedder/platform_channel_pair.h deleted file mode 100644 index 09f6d75afa5..00000000000 --- a/chromium/mojo/edk/embedder/platform_channel_pair.h +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#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_FUCHSIA) -using HandlePassingInformation = base::HandlesToTransferVector; -#elif defined(OS_POSIX) -using HandlePassingInformation = base::FileHandleMappingVector; -#else -#error "Unsupported." -#endif - -// This is used to create a pair of |InternalPlatformHandle|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(); - - ScopedInternalPlatformHandle PassServerHandle(); - - // For in-process use (e.g., in tests or to pass over another channel). - ScopedInternalPlatformHandle 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 ScopedInternalPlatformHandle PassClientHandleFromParentProcess( - const base::CommandLine& command_line); - - // Like above, but gets the handle from the passed in string. - static ScopedInternalPlatformHandle - 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; - -#if defined(OS_FUCHSIA) - // Like above, but accepts a caller-supplied client |handle|. - // TODO(wez): Consider incorporating this call into other platform - // implementations. - static void PrepareToPassHandleToChildProcess( - const InternalPlatformHandle& handle, - base::CommandLine* command_line, - HandlePassingInformation* handle_passing_info); -#endif - - // To be called once the child process has been successfully launched, to do - // any cleanup necessary. - void ChildProcessLaunched(); - - private: - ScopedInternalPlatformHandle server_handle_; - ScopedInternalPlatformHandle client_handle_; - - DISALLOW_COPY_AND_ASSIGN(PlatformChannelPair); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_PAIR_H_ diff --git a/chromium/mojo/edk/embedder/platform_channel_pair_fuchsia.cc b/chromium/mojo/edk/embedder/platform_channel_pair_fuchsia.cc deleted file mode 100644 index 17f6b1c6955..00000000000 --- a/chromium/mojo/edk/embedder/platform_channel_pair_fuchsia.cc +++ /dev/null @@ -1,96 +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 <zircon/process.h> -#include <zircon/processargs.h> -#include <zircon/syscalls.h> - -#include "base/command_line.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/strings/string_number_conversions.h" -#include "mojo/edk/embedder/platform_handle.h" - -namespace mojo { -namespace edk { - -namespace { - -std::string PrepareToPassHandleToChildProcessAsString( - const InternalPlatformHandle& handle, - HandlePassingInformation* handle_passing_info) { - DCHECK(handle.is_valid()); - - const uint32_t id = PA_HND(PA_USER0, 0); - handle_passing_info->push_back({id, handle.as_handle()}); - - return base::UintToString(id); -} - -} // namespace - -PlatformChannelPair::PlatformChannelPair(bool client_is_blocking) { - zx_handle_t handles[2] = {}; - zx_status_t result = zx_channel_create(0, &handles[0], &handles[1]); - CHECK_EQ(ZX_OK, result); - - server_handle_.reset(InternalPlatformHandle::ForHandle(handles[0])); - DCHECK(server_handle_.is_valid()); - client_handle_.reset(InternalPlatformHandle::ForHandle(handles[1])); - DCHECK(client_handle_.is_valid()); -} - -// static -ScopedInternalPlatformHandle -PlatformChannelPair::PassClientHandleFromParentProcess( - const base::CommandLine& command_line) { - std::string handle_string = - command_line.GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch); - return PassClientHandleFromParentProcessFromString(handle_string); -} - -ScopedInternalPlatformHandle -PlatformChannelPair::PassClientHandleFromParentProcessFromString( - const std::string& value) { - unsigned int id = 0; - if (value.empty() || !base::StringToUint(value, &id)) { - LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch; - return ScopedInternalPlatformHandle(); - } - return ScopedInternalPlatformHandle(InternalPlatformHandle::ForHandle( - zx_get_startup_handle(base::checked_cast<uint32_t>(id)))); -} - -void PlatformChannelPair::PrepareToPassClientHandleToChildProcess( - base::CommandLine* command_line, - HandlePassingInformation* handle_passing_info) const { - return PrepareToPassHandleToChildProcess(client_handle_.get(), command_line, - handle_passing_info); -} - -// static -void PlatformChannelPair::PrepareToPassHandleToChildProcess( - const InternalPlatformHandle& handle, - base::CommandLine* command_line, - HandlePassingInformation* handle_passing_info) { - 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, - PrepareToPassHandleToChildProcessAsString(handle, handle_passing_info)); -} - -} // namespace edk -} // namespace mojo diff --git a/chromium/mojo/edk/embedder/platform_channel_pair_posix.cc b/chromium/mojo/edk/embedder/platform_channel_pair_posix.cc deleted file mode 100644 index 5b206658626..00000000000 --- a/chromium/mojo/edk/embedder/platform_channel_pair_posix.cc +++ /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. - -#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) -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(InternalPlatformHandle(fds[0])); - DCHECK(server_handle_.is_valid()); - client_handle_.reset(InternalPlatformHandle(fds[1])); - DCHECK(client_handle_.is_valid()); -} - -// static -ScopedInternalPlatformHandle -PlatformChannelPair::PassClientHandleFromParentProcess( - const base::CommandLine& command_line) { - std::string client_fd_string = - command_line.GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch); - return PassClientHandleFromParentProcessFromString(client_fd_string); -} - -ScopedInternalPlatformHandle -PlatformChannelPair::PassClientHandleFromParentProcessFromString( - const std::string& value) { - int client_fd = -1; -#if defined(OS_ANDROID) - base::GlobalDescriptors::Key key = -1; - if (value.empty() || !base::StringToUint(value, &key)) { - LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch; - return ScopedInternalPlatformHandle(); - } - 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 ScopedInternalPlatformHandle(); - } -#endif - return ScopedInternalPlatformHandle(InternalPlatformHandle(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) - 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/chromium/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc b/chromium/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc deleted file mode 100644 index 9955b2103db..00000000000 --- a/chromium/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc +++ /dev/null @@ -1,257 +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 <utility> -#include <vector> - -#include "base/containers/circular_deque.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/macros.h" -#include "mojo/edk/embedder/platform_channel_utils_posix.h" -#include "mojo/edk/embedder/platform_handle.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(InternalPlatformHandle 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; - ScopedInternalPlatformHandle server_handle = channel_pair.PassServerHandle(); - ScopedInternalPlatformHandle 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, 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, iov, 2); - EXPECT_EQ(-1, result); - if (errno != EPIPE) - PLOG(WARNING) << "write (expected EPIPE)"; -} - -TEST_F(PlatformChannelPairPosixTest, SendReceiveData) { - PlatformChannelPair channel_pair; - ScopedInternalPlatformHandle server_handle = channel_pair.PassServerHandle(); - ScopedInternalPlatformHandle 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, send_string.data(), - send_string.size())); - - WaitReadable(client_handle.get()); - - char buf[10000] = {}; - base::circular_deque<ScopedInternalPlatformHandle> received_handles; - ssize_t result = PlatformChannelRecvmsg(client_handle, 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; - ScopedInternalPlatformHandle server_handle = channel_pair.PassServerHandle(); - ScopedInternalPlatformHandle 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); - std::vector<ScopedInternalPlatformHandle> platform_handles; - 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::InternalPlatformHandleFromFILE(std::move(fp))); - 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, &iov, 1, - std::move(platform_handles))); - - WaitReadable(client_handle.get()); - - char buf[10000] = {}; - base::circular_deque<ScopedInternalPlatformHandle> received_handles; - // We assume that the |recvmsg()| actually reads all the data. - EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)), - PlatformChannelRecvmsg(client_handle, buf, sizeof(buf), - &received_handles)); - EXPECT_STREQ(kHello, buf); - EXPECT_EQ(i, received_handles.size()); - - for (size_t j = 0; j < received_handles.size(); j++) { - base::ScopedFILE fp(test::FILEFromInternalPlatformHandle( - std::move(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; - ScopedInternalPlatformHandle server_handle = channel_pair.PassServerHandle(); - ScopedInternalPlatformHandle 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())); - std::vector<ScopedInternalPlatformHandle> platform_handles(1); - platform_handles[0] = test::InternalPlatformHandleFromFILE(std::move(fp)); - 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, &iov, 1, - std::move(platform_handles))); - } - - WaitReadable(client_handle.get()); - - // Start with an invalid handle in the vector. - base::circular_deque<ScopedInternalPlatformHandle> received_handles; - received_handles.push_back(ScopedInternalPlatformHandle()); - - char buf[100] = {}; - // We assume that the |recvmsg()| actually reads all the data. - EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)), - PlatformChannelRecvmsg(client_handle, 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::FILEFromInternalPlatformHandle( - std::move(received_handles[1]), "rb")); - 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/chromium/mojo/edk/embedder/platform_channel_pair_win.cc b/chromium/mojo/edk/embedder/platform_channel_pair_win.cc deleted file mode 100644 index 7981cc6c013..00000000000 --- a/chromium/mojo/edk/embedder/platform_channel_pair_win.cc +++ /dev/null @@ -1,122 +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 "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(InternalPlatformHandle( - 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(InternalPlatformHandle( - 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 -ScopedInternalPlatformHandle -PlatformChannelPair::PassClientHandleFromParentProcess( - const base::CommandLine& command_line) { - std::string client_handle_string = - command_line.GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch); - return PassClientHandleFromParentProcessFromString(client_handle_string); -} - -ScopedInternalPlatformHandle -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 ScopedInternalPlatformHandle(); - } - - return ScopedInternalPlatformHandle( - InternalPlatformHandle(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()); - - handle_passing_info->push_back(client_handle_.get().handle); - - return base::IntToString(HandleToLong(client_handle_.get().handle)); -} - -} // namespace edk -} // namespace mojo diff --git a/chromium/mojo/edk/embedder/platform_channel_utils_posix.cc b/chromium/mojo/edk/embedder/platform_channel_utils_posix.cc deleted file mode 100644 index 8ac67dc2bce..00000000000 --- a/chromium/mojo/edk/embedder/platform_channel_utils_posix.cc +++ /dev/null @@ -1,250 +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/containers/queue.h" -#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(InternalPlatformHandle 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(InternalPlatformHandle 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) || defined(OS_FUCHSIA) -const int kSendFlags = 0; -#else -const int kSendFlags = MSG_NOSIGNAL; -#endif - -ssize_t PlatformChannelWrite(const ScopedInternalPlatformHandle& 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.get().handle, bytes, num_bytes)); -#else - return send(h.get().handle, bytes, num_bytes, kSendFlags); -#endif -} - -ssize_t PlatformChannelWritev(const ScopedInternalPlatformHandle& 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.get().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.get().handle, &msg, kSendFlags)); -#endif -} - -ssize_t PlatformChannelSendmsgWithHandles( - const ScopedInternalPlatformHandle& h, - struct iovec* iov, - size_t num_iov, - const std::vector<ScopedInternalPlatformHandle>& platform_handles) { - DCHECK(iov); - DCHECK_GT(num_iov, 0u); - DCHECK(!platform_handles.empty()); - DCHECK_LE(platform_handles.size(), 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(platform_handles.size() * sizeof(int)); - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(platform_handles.size() * sizeof(int)); - for (size_t i = 0; i < platform_handles.size(); i++) { - DCHECK(platform_handles[i].is_valid()); - reinterpret_cast<int*>(CMSG_DATA(cmsg))[i] = - platform_handles[i].get().handle; - } - - return HANDLE_EINTR(sendmsg(h.get().handle, &msg, kSendFlags)); -} - -ssize_t PlatformChannelRecvmsg( - const ScopedInternalPlatformHandle& h, - void* buf, - size_t num_bytes, - base::circular_deque<ScopedInternalPlatformHandle>* 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.get().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( - ScopedInternalPlatformHandle(InternalPlatformHandle(fds[i]))); - DCHECK(platform_handles->back().is_valid()); - } - } - } - - return result; -} - -bool ServerAcceptConnection(const ScopedInternalPlatformHandle& server_handle, - ScopedInternalPlatformHandle* connection_handle, - bool check_peer_user) { - DCHECK(server_handle.is_valid()); - connection_handle->reset(); -#if defined(OS_NACL) - NOTREACHED(); - return false; -#else - ScopedInternalPlatformHandle accept_handle(InternalPlatformHandle( - HANDLE_EINTR(accept(server_handle.get().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/chromium/mojo/edk/embedder/platform_channel_utils_posix.h b/chromium/mojo/edk/embedder/platform_channel_utils_posix.h deleted file mode 100644 index e5a4b0096a7..00000000000 --- a/chromium/mojo/edk/embedder/platform_channel_utils_posix.h +++ /dev/null @@ -1,79 +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 <vector> - -#include "base/containers/circular_deque.h" -#include "mojo/edk/embedder/platform_handle.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/system_impl_export.h" - -struct iovec; // Declared in <sys/uio.h>. - -namespace mojo { -namespace edk { -class ScopedInternalPlatformHandle; - -// 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(const ScopedInternalPlatformHandle& h, - const void* bytes, - size_t num_bytes); -MOJO_SYSTEM_IMPL_EXPORT ssize_t -PlatformChannelWritev(const ScopedInternalPlatformHandle& h, - struct iovec* iov, - size_t num_iov); - -// Writes data, and the given set of |InternalPlatformHandle|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( - const ScopedInternalPlatformHandle& h, - struct iovec* iov, - size_t num_iov, - const std::vector<ScopedInternalPlatformHandle>& platform_handles); - -// Wrapper around |recvmsg()|, which will extract any attached file descriptors -// (in the control message) to |InternalPlatformHandle|s (and append them to -// |platform_handles|). (This also handles |EINTR|.) -MOJO_SYSTEM_IMPL_EXPORT ssize_t PlatformChannelRecvmsg( - const ScopedInternalPlatformHandle& h, - void* buf, - size_t num_bytes, - base::circular_deque<ScopedInternalPlatformHandle>* 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( - const ScopedInternalPlatformHandle& server_handle, - ScopedInternalPlatformHandle* connection_handle, - bool check_peer_user = true); - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_UTILS_POSIX_H_ diff --git a/chromium/mojo/edk/embedder/platform_handle.cc b/chromium/mojo/edk/embedder/platform_handle.cc deleted file mode 100644 index fce7177348a..00000000000 --- a/chromium/mojo/edk/embedder/platform_handle.cc +++ /dev/null @@ -1,109 +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_WIN) -#include <windows.h> -#elif defined(OS_FUCHSIA) -#include <unistd.h> -#include <zircon/status.h> -#include <zircon/syscalls.h> -#elif defined(OS_POSIX) -#include <unistd.h> -#endif - -#include "base/logging.h" - -#if defined(OS_WIN) -#include "base/optional.h" -#include "mojo/edk/system/scoped_process_handle.h" -#endif - -namespace mojo { -namespace edk { - -void InternalPlatformHandle::CloseIfNecessary() { -#if defined(OS_WIN) - // Take local ownership of the process handle in |owning_process| if it's - // a handle to a remote process. We do this before the generic handle validity - // test below because even if the platform handle has been taken by someone - // else, we may still own a remote process handle and it needs to be closed - // before we return. - base::Optional<ScopedProcessHandle> remote_process_handle; - if (owning_process != base::GetCurrentProcessHandle()) { - remote_process_handle.emplace(owning_process); - owning_process = base::GetCurrentProcessHandle(); - } -#endif - - if (!is_valid()) - return; - -#if defined(OS_WIN) - if (remote_process_handle) { - // 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 [sic] to NULL. (N.B.: This appears to be a - // documentation bug; what matters is that hTargetProcessHandle is - // 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(remote_process_handle->get(), handle, NULL, &handle, 0, - FALSE, DUPLICATE_CLOSE_SOURCE); - return; - } - - bool success = !!CloseHandle(handle); - DPCHECK(success); - handle = INVALID_HANDLE_VALUE; -#elif defined(OS_FUCHSIA) - if (handle != ZX_HANDLE_INVALID) { - zx_status_t result = zx_handle_close(handle); - DCHECK_EQ(ZX_OK, result) << "CloseIfNecessary(zx_handle_close): " - << zx_status_get_string(result); - handle = ZX_HANDLE_INVALID; - } - if (fd >= 0) { - bool success = (close(fd) == 0); - DPCHECK(success); - fd = -1; - } -#elif 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) -#else -#error "Platform not yet supported." -#endif // defined(OS_WIN) -} - -} // namespace edk -} // namespace mojo diff --git a/chromium/mojo/edk/embedder/platform_handle.h b/chromium/mojo/edk/embedder/platform_handle.h deleted file mode 100644 index 302412261ce..00000000000 --- a/chromium/mojo/edk/embedder/platform_handle.h +++ /dev/null @@ -1,126 +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_FUCHSIA) -#include <fdio/limits.h> -#include <zircon/syscalls.h> -#elif defined(OS_MACOSX) && !defined(OS_IOS) -#include <mach/mach.h> -#endif - -#include "base/logging.h" - -namespace mojo { -namespace edk { - -#if defined(OS_WIN) -struct MOJO_SYSTEM_IMPL_EXPORT InternalPlatformHandle { - InternalPlatformHandle() : InternalPlatformHandle(INVALID_HANDLE_VALUE) {} - explicit InternalPlatformHandle(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 and this process - // handle (if not null, i.e., the current process) is *owned* by this - // InternalPlatformHandle. - 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; -}; -#elif defined(OS_FUCHSIA) -// TODO(fuchsia): Find a clean way to share this with the POSIX version. -// |zx_handle_t| is a typedef of |int|, so we only allow InternalPlatformHandle -// to be created via explicit For<type>() creator functions. -struct MOJO_SYSTEM_IMPL_EXPORT InternalPlatformHandle { - public: - static InternalPlatformHandle ForHandle(zx_handle_t handle) { - InternalPlatformHandle platform_handle; - platform_handle.handle = handle; - return platform_handle; - } - static InternalPlatformHandle ForFd(int fd) { - InternalPlatformHandle platform_handle; - DCHECK_LT(fd, FDIO_MAX_FD); - platform_handle.fd = fd; - return platform_handle; - } - - void CloseIfNecessary(); - bool is_valid() const { return is_valid_fd() || is_valid_handle(); } - bool is_valid_handle() const { return handle != ZX_HANDLE_INVALID && fd < 0; } - zx_handle_t as_handle() const { return handle; } - bool is_valid_fd() const { return fd >= 0 && handle == ZX_HANDLE_INVALID; } - int as_fd() const { return fd; } - - private: - zx_handle_t handle = ZX_HANDLE_INVALID; - int fd = -1; -}; -#elif defined(OS_POSIX) -struct MOJO_SYSTEM_IMPL_EXPORT InternalPlatformHandle { - InternalPlatformHandle() {} - explicit InternalPlatformHandle(int handle) : handle(handle) {} -#if defined(OS_MACOSX) && !defined(OS_IOS) - explicit InternalPlatformHandle(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 -}; -#else -#error "Platform not yet supported." -#endif - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_PLATFORM_HANDLE_H_ diff --git a/chromium/mojo/edk/embedder/platform_handle_utils.cc b/chromium/mojo/edk/embedder/platform_handle_utils.cc deleted file mode 100644 index e3c0cfd6b6f..00000000000 --- a/chromium/mojo/edk/embedder/platform_handle_utils.cc +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2018 The Chromium 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 "build/build_config.h" - -namespace mojo { -namespace edk { - -MojoResult MojoPlatformHandleToScopedInternalPlatformHandle( - const MojoPlatformHandle* platform_handle, - ScopedInternalPlatformHandle* 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; - } - - InternalPlatformHandle handle; - switch (platform_handle->type) { -#if defined(OS_FUCHSIA) - case MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE: - handle = InternalPlatformHandle::ForHandle(platform_handle->value); - break; - case MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR: - handle = InternalPlatformHandle::ForFd(platform_handle->value); - break; - -#elif 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 = InternalPlatformHandle::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 ScopedInternalPlatformHandleToMojoPlatformHandle( - ScopedInternalPlatformHandle 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_FUCHSIA) - if (handle.get().is_valid_fd()) { - platform_handle->type = MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR; - platform_handle->value = handle.release().as_fd(); - } else { - platform_handle->type = MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE; - platform_handle->value = handle.release().as_handle(); - } -#elif defined(OS_POSIX) - switch (handle.get().type) { - case InternalPlatformHandle::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 InternalPlatformHandle::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; -} - -void ExtractInternalPlatformHandlesFromSharedMemoryRegionHandle( - base::subtle::PlatformSharedMemoryRegion::ScopedPlatformHandle handle, - ScopedInternalPlatformHandle* extracted_handle, - ScopedInternalPlatformHandle* extracted_readonly_handle) { -#if defined(OS_WIN) - extracted_handle->reset(InternalPlatformHandle(handle.Take())); -#elif defined(OS_FUCHSIA) - extracted_handle->reset(InternalPlatformHandle::ForHandle(handle.release())); -#elif defined(OS_MACOSX) && !defined(OS_IOS) - // This is a Mach port. Same code as below, but separated for clarity. - extracted_handle->reset(InternalPlatformHandle(handle.release())); -#elif defined(OS_ANDROID) - // This is a file descriptor. Same code as above, but separated for clarity. - extracted_handle->reset(InternalPlatformHandle(handle.release())); -#else - extracted_handle->reset(InternalPlatformHandle(handle.fd.release())); - extracted_readonly_handle->reset( - InternalPlatformHandle(handle.readonly_fd.release())); -#endif -} - -base::subtle::PlatformSharedMemoryRegion::ScopedPlatformHandle -CreateSharedMemoryRegionHandleFromInternalPlatformHandles( - ScopedInternalPlatformHandle handle, - ScopedInternalPlatformHandle readonly_handle) { -#if defined(OS_WIN) - DCHECK(!readonly_handle.is_valid()); - return base::win::ScopedHandle(handle.release().handle); -#elif defined(OS_FUCHSIA) - DCHECK(!readonly_handle.is_valid()); - return base::ScopedZxHandle(handle.release().as_handle()); -#elif defined(OS_MACOSX) && !defined(OS_IOS) - DCHECK(!readonly_handle.is_valid()); - return base::mac::ScopedMachSendRight(handle.release().port); -#elif defined(OS_ANDROID) - DCHECK(!readonly_handle.is_valid()); - return base::ScopedFD(handle.release().handle); -#else - return base::subtle::ScopedFDPair( - base::ScopedFD(handle.release().handle), - base::ScopedFD(readonly_handle.release().handle)); -#endif -} - -} // namespace edk -} // namespace mojo diff --git a/chromium/mojo/edk/embedder/platform_handle_utils.h b/chromium/mojo/edk/embedder/platform_handle_utils.h deleted file mode 100644 index 2bdfb3f1572..00000000000 --- a/chromium/mojo/edk/embedder/platform_handle_utils.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_PLATFORM_HANDLE_UTILS_H_ -#define MOJO_EDK_EMBEDDER_PLATFORM_HANDLE_UTILS_H_ - -#include "base/memory/platform_shared_memory_region.h" -#include "mojo/edk/embedder/platform_handle.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/platform_handle.h" -#include "mojo/public/c/system/types.h" -#include "mojo/public/cpp/platform/platform_handle.h" - -namespace mojo { -namespace edk { - -// Closes all the |InternalPlatformHandle|s in the given container. -template <typename InternalPlatformHandleContainer> -MOJO_SYSTEM_IMPL_EXPORT inline void CloseAllInternalPlatformHandles( - InternalPlatformHandleContainer* platform_handles) { - for (typename InternalPlatformHandleContainer::iterator it = - platform_handles->begin(); - it != platform_handles->end(); ++it) - it->CloseIfNecessary(); -} - -MOJO_SYSTEM_IMPL_EXPORT MojoResult -MojoPlatformHandleToScopedInternalPlatformHandle( - const MojoPlatformHandle* platform_handle, - ScopedInternalPlatformHandle* out_handle); - -MOJO_SYSTEM_IMPL_EXPORT MojoResult -ScopedInternalPlatformHandleToMojoPlatformHandle( - ScopedInternalPlatformHandle handle, - MojoPlatformHandle* platform_handle); - -// Duplicates the given |InternalPlatformHandle| (which must be valid). (Returns -// an invalid |ScopedInternalPlatformHandle| on failure.) -MOJO_SYSTEM_IMPL_EXPORT ScopedInternalPlatformHandle -DuplicatePlatformHandle(InternalPlatformHandle platform_handle); - -// Converts a base shared memory platform handle into one (maybe two on POSIX) -// EDK ScopedInternalPlatformHandles. -MOJO_SYSTEM_IMPL_EXPORT void -ExtractInternalPlatformHandlesFromSharedMemoryRegionHandle( - base::subtle::PlatformSharedMemoryRegion::ScopedPlatformHandle handle, - ScopedInternalPlatformHandle* extracted_handle, - ScopedInternalPlatformHandle* extracted_readonly_handle); - -// Converts one (maybe two on POSIX) EDK ScopedInternalPlatformHandles to a base -// shared memory platform handle. -MOJO_SYSTEM_IMPL_EXPORT -base::subtle::PlatformSharedMemoryRegion::ScopedPlatformHandle -CreateSharedMemoryRegionHandleFromInternalPlatformHandles( - ScopedInternalPlatformHandle handle, - ScopedInternalPlatformHandle readonly_handle); - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_PLATFORM_HANDLE_UTILS_H_ diff --git a/chromium/mojo/edk/embedder/platform_handle_utils_fuchsia.cc b/chromium/mojo/edk/embedder/platform_handle_utils_fuchsia.cc deleted file mode 100644 index a904a08b6c3..00000000000 --- a/chromium/mojo/edk/embedder/platform_handle_utils_fuchsia.cc +++ /dev/null @@ -1,24 +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/platform_handle_utils.h" - -#include "base/logging.h" - -namespace mojo { -namespace edk { - -ScopedInternalPlatformHandle DuplicatePlatformHandle( - InternalPlatformHandle platform_handle) { - DCHECK(platform_handle.is_valid()); - zx_handle_t duped; - // zx_handle_duplicate won't touch |duped| in case of failure. - zx_status_t result = zx_handle_duplicate(platform_handle.as_handle(), - ZX_RIGHT_SAME_RIGHTS, &duped); - DLOG_IF(ERROR, result != ZX_OK) << "zx_duplicate_handle failed: " << result; - return ScopedInternalPlatformHandle(InternalPlatformHandle::ForHandle(duped)); -} - -} // namespace edk -} // namespace mojo diff --git a/chromium/mojo/edk/embedder/platform_handle_utils_posix.cc b/chromium/mojo/edk/embedder/platform_handle_utils_posix.cc deleted file mode 100644 index a29b79f8713..00000000000 --- a/chromium/mojo/edk/embedder/platform_handle_utils_posix.cc +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/embedder/platform_handle_utils.h" - -#include <unistd.h> - -#include "base/logging.h" - -namespace mojo { -namespace edk { - -ScopedInternalPlatformHandle DuplicatePlatformHandle( - InternalPlatformHandle platform_handle) { - DCHECK(platform_handle.is_valid()); - // Note that |dup()| returns -1 on error (which is exactly the value we use - // for invalid |InternalPlatformHandle| FDs). - InternalPlatformHandle duped(dup(platform_handle.handle)); - duped.needs_connection = platform_handle.needs_connection; - return ScopedInternalPlatformHandle(duped); -} - -} // namespace edk -} // namespace mojo diff --git a/chromium/mojo/edk/embedder/platform_handle_utils_win.cc b/chromium/mojo/edk/embedder/platform_handle_utils_win.cc deleted file mode 100644 index 4622e925fb1..00000000000 --- a/chromium/mojo/edk/embedder/platform_handle_utils_win.cc +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/edk/embedder/platform_handle_utils.h" - -#include <windows.h> - -#include "base/logging.h" - -namespace mojo { -namespace edk { - -ScopedInternalPlatformHandle DuplicatePlatformHandle( - InternalPlatformHandle 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 ScopedInternalPlatformHandle(); - DCHECK_NE(new_handle, INVALID_HANDLE_VALUE); - return ScopedInternalPlatformHandle(InternalPlatformHandle(new_handle)); -} - -} // namespace edk -} // namespace mojo diff --git a/chromium/mojo/edk/embedder/scoped_platform_handle.h b/chromium/mojo/edk/embedder/scoped_platform_handle.h deleted file mode 100644 index 67ffdb07b88..00000000000 --- a/chromium/mojo/edk/embedder/scoped_platform_handle.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_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 ScopedInternalPlatformHandle { - public: - ScopedInternalPlatformHandle() {} - explicit ScopedInternalPlatformHandle(InternalPlatformHandle handle) - : handle_(handle) {} - ~ScopedInternalPlatformHandle() { handle_.CloseIfNecessary(); } - - // Move-only constructor and operator=. - ScopedInternalPlatformHandle(ScopedInternalPlatformHandle&& other) - : handle_(other.release()) {} - - ScopedInternalPlatformHandle& operator=( - ScopedInternalPlatformHandle&& other) { - reset(other.release()); - return *this; - } - - const InternalPlatformHandle& get() const { return handle_; } - InternalPlatformHandle& get() { return handle_; } - - void swap(ScopedInternalPlatformHandle& other) { - InternalPlatformHandle temp = handle_; - handle_ = other.handle_; - other.handle_ = temp; - } - - InternalPlatformHandle release() WARN_UNUSED_RESULT { - InternalPlatformHandle rv = handle_; - handle_ = InternalPlatformHandle(); - return rv; - } - - void reset(InternalPlatformHandle handle = InternalPlatformHandle()) { - handle_.CloseIfNecessary(); - handle_ = handle; - } - - bool is_valid() const { return handle_.is_valid(); } - - private: - InternalPlatformHandle handle_; - - DISALLOW_COPY_AND_ASSIGN(ScopedInternalPlatformHandle); -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_SCOPED_PLATFORM_HANDLE_H_ diff --git a/chromium/mojo/edk/embedder/transport_protocol.h b/chromium/mojo/edk/embedder/transport_protocol.h deleted file mode 100644 index 67d8ee94f92..00000000000 --- a/chromium/mojo/edk/embedder/transport_protocol.h +++ /dev/null @@ -1,47 +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_TRANSPORT_PROTOCOL_H_ -#define MOJO_EDK_EMBEDDER_TRANSPORT_PROTOCOL_H_ - -#include <stdint.h> - -namespace mojo { -namespace edk { - -// Selects the transport protocol to use when connecting to the remote -// process. -// -// New code should generally prefer to use |kAutomatic| to allow the protocol -// to be negotiated by the two processes involved in the connection. As noted -// below, this is not possible when either one of the two processes only -// supports the legacy protocol. In such cases both ends of the connection -// must be aware and explicitly use |kLegacy|, which is the default for now. -enum class TransportProtocol : int32_t { - // Legacy transport protocol. Should only be used when connecting to - // embedders which may not support at least |kVersion0|. Deprecated. - kLegacy = -2, - - // Automatically negotiate the transport protocol. This will ultimately - // select the highest protocol version supported by all of the calling - // process, the broker process (if any), and the target process. - // - // This may only be used when connecting to embedders which support at least - // |kVersion0|. Otherwise the deprecated |kLegacy| protocol must be used. - kAutomatic = -1, - - // Protocol version 0. This is NOT backwards compatible with the legacy - // protocol. If this is used to establish a connection, the remote embedder - // (and the broker, if applicable) must also support at least version 0. - kVersion0 = 0, - - // The maximum version supported by the current process. This must be - // updated if a new version is added. - kMaxSupportedVersion = kVersion0, -}; - -} // namespace edk -} // namespace mojo - -#endif // MOJO_EDK_EMBEDDER_TRANSPORT_PROTOCOL_H_ diff --git a/chromium/mojo/edk/system/BUILD.gn b/chromium/mojo/edk/system/BUILD.gn deleted file mode 100644 index 235f32e31e1..00000000000 --- a/chromium/mojo/edk/system/BUILD.gn +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright 2014 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//build/config/nacl/config.gni") -import("../../../mojo/public/tools/bindings/mojom.gni") - -if (is_android) { - import("//build/config/android/config.gni") - import("//build/config/android/rules.gni") -} - -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", - ] -} - -source_set("test_sources") { - testonly = true - sources = [ - "channel_unittest.cc", - "core_test_base.cc", - "core_test_base.h", - "core_unittest.cc", - "handle_table_unittest.cc", - "message_pipe_unittest.cc", - "message_unittest.cc", - "options_validation_unittest.cc", - "platform_handle_dispatcher_unittest.cc", - "shared_buffer_dispatcher_unittest.cc", - "shared_buffer_unittest.cc", - "signals_unittest.cc", - "trap_unittest.cc", - ] - - if (!is_ios) { - sources += [ - "data_pipe_unittest.cc", - "invitation_unittest.cc", - "multiprocess_message_pipe_unittest.cc", - "platform_wrapper_unittest.cc", - ] - } - - deps = [ - ":test_utils", - "//base", - "//base/test:test_support", - "//mojo/edk", - "//mojo/edk/embedder:embedder_unittests", - "//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" ] -} diff --git a/chromium/mojo/edk/system/scoped_process_handle.cc b/chromium/mojo/edk/system/scoped_process_handle.cc deleted file mode 100644 index 43d719df410..00000000000 --- a/chromium/mojo/edk/system/scoped_process_handle.cc +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2018 The Chromium 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/scoped_process_handle.h" - -#include "build/build_config.h" - -#if defined(OS_WIN) -#include <windows.h> -#endif - -namespace mojo { -namespace edk { - -ScopedProcessHandle::ScopedProcessHandle() = default; - -ScopedProcessHandle::ScopedProcessHandle(base::ProcessHandle handle) - : handle_(handle) {} - -ScopedProcessHandle::ScopedProcessHandle(ScopedProcessHandle&&) = default; - -ScopedProcessHandle::~ScopedProcessHandle() = default; - -// static -ScopedProcessHandle ScopedProcessHandle::CloneFrom(base::ProcessHandle handle) { -#if defined(OS_WIN) - BOOL ok = ::DuplicateHandle(base::GetCurrentProcessHandle(), handle, - base::GetCurrentProcessHandle(), &handle, 0, - FALSE, DUPLICATE_SAME_ACCESS); - DCHECK(ok); -#endif - return ScopedProcessHandle(handle); -} - -ScopedProcessHandle& ScopedProcessHandle::operator=(ScopedProcessHandle&&) = - default; - -ScopedProcessHandle ScopedProcessHandle::Clone() const { - DCHECK(is_valid()); - return CloneFrom(get()); -} - -} // namespace edk -} // namespace mojo diff --git a/chromium/mojo/edk/system/trap_unittest.cc b/chromium/mojo/edk/system/trap_unittest.cc deleted file mode 100644 index 011f988020e..00000000000 --- a/chromium/mojo/edk/system/trap_unittest.cc +++ /dev/null @@ -1,1868 +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/rand_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/trap.h" -#include "mojo/public/c/system/types.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace edk { -namespace { - -// TODO(https://crbug.com/819046): These are temporary wrappers to reduce -// changes necessary during the API rename. Remove them. - -MojoResult MojoWatch(MojoHandle trap_handle, - MojoHandle handle, - MojoHandleSignals signals, - MojoTriggerCondition condition, - uintptr_t context) { - return MojoAddTrigger(trap_handle, handle, signals, condition, context, - nullptr); -} - -MojoResult MojoCancelWatch(MojoHandle trap_handle, uintptr_t context) { - return MojoRemoveTrigger(trap_handle, context, nullptr); -} - -MojoResult MojoArmWatcher(MojoHandle trap_handle, - uint32_t* num_ready_contexts, - uintptr_t* ready_contexts, - MojoResult* ready_results, - MojoHandleSignalsState* ready_signals) { - return MojoArmTrap(trap_handle, nullptr, num_ready_contexts, ready_contexts, - ready_results, ready_signals); -} - -using WatcherTest = test::MojoTestBase; - -class WatchHelper { - public: - using ContextCallback = - base::Callback<void(MojoResult, MojoHandleSignalsState)>; - - WatchHelper() {} - ~WatchHelper() {} - - MojoResult CreateWatcher(MojoHandle* handle) { - return MojoCreateTrap(&Notify, nullptr, 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 = std::make_unique<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(const MojoTrapEvent* event) { - reinterpret_cast<NotificationContext*>(event->trigger_context) - ->Notify(event->result, event->signals_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(const MojoTrapEvent* event) { - NOTREACHED(); -} - -void ExpectOnlyCancel(const MojoTrapEvent* event) { - EXPECT_EQ(event->result, MOJO_RESULT_CANCELLED); -} - -TEST_F(WatcherTest, InvalidArguments) { - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoCreateTrap(&ExpectNoNotification, nullptr, nullptr)); - MojoHandle w; - EXPECT_EQ(MOJO_RESULT_OK, MojoCreateTrap(&ExpectNoNotification, nullptr, &w)); - - // Try to watch unwatchable handles. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoWatch(w, w, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_WATCH_CONDITION_SATISFIED, 0)); - MojoHandle buffer_handle = CreateBuffer(42); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoWatch(w, buffer_handle, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, - 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, - MOJO_WATCH_CONDITION_SATISFIED, 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; - MojoReadDataOptions options; - options.struct_size = sizeof(options); - options.flags = MOJO_READ_DATA_FLAG_ALL_OR_NONE; - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - MojoReadData(consumer, &options, large_buffer, &large_read_size)); - - // 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, - MOJO_WATCH_CONDITION_SATISFIED, - 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, - MOJO_WATCH_CONDITION_SATISFIED, - 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, - MOJO_WATCH_CONDITION_SATISFIED, - 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, - MOJO_WATCH_CONDITION_SATISFIED, - 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, - MOJO_WATCH_CONDITION_SATISFIED, - 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, MojoCreateTrap(&ExpectNoNotification, nullptr, &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, MojoCreateTrap(&ExpectOnlyCancel, nullptr, &w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_WATCH_CONDITION_SATISFIED, 0)); - EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, - MojoWatch(w, b, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_WATCH_CONDITION_SATISFIED, 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, MojoCreateTrap(&ExpectNoNotification, nullptr, &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, MojoCreateTrap(&ExpectOnlyCancel, nullptr, &w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_WRITABLE, - MOJO_WATCH_CONDITION_SATISFIED, 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, MojoCreateTrap(&ExpectOnlyCancel, nullptr, &w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, readable_a_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, b, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, readable_a_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, b, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, readable_a_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, c, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, readable_a_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, c, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, readable_b_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(cd_watcher, c, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_WATCH_CONDITION_SATISFIED, readable_c_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(cd_watcher, d, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, 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, - MOJO_WATCH_CONDITION_SATISFIED, 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, MojoCreateTrap(&ExpectOnlyCancel, nullptr, &w)); - for (size_t i = 0; i < kNumTestHandles; ++i) { - EXPECT_EQ(MOJO_RESULT_OK, - MojoWatch(w, handles[i], MOJO_HANDLE_SIGNAL_READABLE, - MOJO_WATCH_CONDITION_SATISFIED, 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)); -} - -TEST_F(WatcherTest, WatchNotSatisfied) { - 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_OK, 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, - MOJO_WATCH_CONDITION_SATISFIED, readable_a_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - const char kMessage[] = "this is not a message"; - - WriteMessage(b, kMessage); - event.Wait(); - - // Now we know |a| is readable. Cancel the watch and watch for the - // not-readable state. - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); - const uintptr_t not_readable_a_context = helper.CreateContext(base::Bind( - [](base::WaitableEvent* event, MojoResult result, - MojoHandleSignalsState state) { - EXPECT_EQ(MOJO_RESULT_OK, result); - event->Signal(); - }, - &event)); - EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_WATCH_CONDITION_NOT_SATISFIED, - not_readable_a_context)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); - - // This should not block, because the event should be signaled by - // |not_readable_a_context| when we read the only available message off of - // |a|. - event.Reset(); - EXPECT_EQ(kMessage, ReadMessage(a)); - event.Wait(); - - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); -} - -base::Closure g_do_random_thing_callback; - -void ReadAllMessages(const MojoTrapEvent* event) { - if (event->result == MOJO_RESULT_OK) { - MojoHandle handle = static_cast<MojoHandle>(event->trigger_context); - MojoMessageHandle message; - while (MojoReadMessage(handle, nullptr, &message) == MOJO_RESULT_OK) - MojoDestroyMessage(message); - } - - constexpr size_t kNumRandomThingsToDoOnNotify = 5; - for (size_t i = 0; i < kNumRandomThingsToDoOnNotify; ++i) - g_do_random_thing_callback.Run(); -} - -MojoHandle RandomHandle(MojoHandle* handles, size_t size) { - return handles[base::RandInt(0, static_cast<int>(size) - 1)]; -} - -void DoRandomThing(MojoHandle* watchers, - size_t num_watchers, - MojoHandle* watched_handles, - size_t num_watched_handles) { - switch (base::RandInt(0, 10)) { - case 0: - MojoClose(RandomHandle(watchers, num_watchers)); - break; - case 1: - MojoClose(RandomHandle(watched_handles, num_watched_handles)); - break; - case 2: - case 3: - case 4: { - MojoMessageHandle message; - ASSERT_EQ(MOJO_RESULT_OK, MojoCreateMessage(nullptr, &message)); - ASSERT_EQ(MOJO_RESULT_OK, - MojoSetMessageContext(message, 1, nullptr, nullptr, nullptr)); - MojoWriteMessage(RandomHandle(watched_handles, num_watched_handles), - message, nullptr); - break; - } - case 5: - case 6: { - MojoHandle w = RandomHandle(watchers, num_watchers); - MojoHandle h = RandomHandle(watched_handles, num_watched_handles); - MojoWatch(w, h, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_WATCH_CONDITION_SATISFIED, static_cast<uintptr_t>(h)); - break; - } - case 7: - case 8: { - uint32_t num_ready_contexts = 1; - uintptr_t ready_context; - MojoResult ready_result; - MojoHandleSignalsState ready_state; - if (MojoArmWatcher(RandomHandle(watchers, num_watchers), - &num_ready_contexts, &ready_context, &ready_result, - &ready_state) == MOJO_RESULT_FAILED_PRECONDITION && - ready_result == MOJO_RESULT_OK) { - MojoTrapEvent event; - event.struct_size = sizeof(event); - event.trigger_context = ready_context; - event.result = ready_result; - event.signals_state = ready_state; - event.flags = MOJO_TRAP_EVENT_FLAG_NONE; - ReadAllMessages(&event); - } - break; - } - case 9: - case 10: { - MojoHandle w = RandomHandle(watchers, num_watchers); - MojoHandle h = RandomHandle(watched_handles, num_watched_handles); - MojoCancelWatch(w, static_cast<uintptr_t>(h)); - break; - } - default: - NOTREACHED(); - break; - } -} - -TEST_F(WatcherTest, ConcurrencyStressTest) { - // Regression test for https://crbug.com/740044. Exercises racy usage of the - // watcher API to weed out potential crashes. - - constexpr size_t kNumWatchers = 50; - constexpr size_t kNumWatchedHandles = 50; - static_assert(kNumWatchedHandles % 2 == 0, "Invalid number of test handles."); - - constexpr size_t kNumThreads = 10; - static constexpr size_t kNumOperationsPerThread = 400; - - MojoHandle watchers[kNumWatchers]; - MojoHandle watched_handles[kNumWatchedHandles]; - g_do_random_thing_callback = - base::Bind(&DoRandomThing, watchers, kNumWatchers, watched_handles, - kNumWatchedHandles); - - for (size_t i = 0; i < kNumWatchers; ++i) - MojoCreateTrap(&ReadAllMessages, nullptr, &watchers[i]); - for (size_t i = 0; i < kNumWatchedHandles; i += 2) - CreateMessagePipe(&watched_handles[i], &watched_handles[i + 1]); - - std::unique_ptr<ThreadedRunner> threads[kNumThreads]; - auto runner_callback = base::Bind([]() { - for (size_t i = 0; i < kNumOperationsPerThread; ++i) - g_do_random_thing_callback.Run(); - }); - for (size_t i = 0; i < kNumThreads; ++i) { - threads[i] = std::make_unique<ThreadedRunner>(runner_callback); - threads[i]->Start(); - } - for (size_t i = 0; i < kNumThreads; ++i) - threads[i]->Join(); - for (size_t i = 0; i < kNumWatchers; ++i) - MojoClose(watchers[i]); - for (size_t i = 0; i < kNumWatchedHandles; ++i) - MojoClose(watched_handles[i]); -} - -} // namespace -} // namespace edk -} // namespace mojo diff --git a/chromium/mojo/public/c/system/README.md b/chromium/mojo/public/c/system/README.md index f31375e903a..ec88e3e6674 100644 --- a/chromium/mojo/public/c/system/README.md +++ b/chromium/mojo/public/c/system/README.md @@ -4,15 +4,16 @@ This document is a subset of the [Mojo documentation](/mojo/README.md). [TOC] ## Overview -The Mojo C System API is a lightweight API (with an eventually-stable ABI) upon -which all higher layers of the Mojo system are built. +The Mojo C System API is a lightweight API (with an stable, forward-compatible +ABI) upon which all higher-level public Mojo APIs are built. This API exposes the fundamental capabilities to: create, read from, and write to **message pipes**; create, read from, and write to **data pipes**; create **shared buffers** and generate sharable handles to them; wrap platform-specific handle objects (such as **file descriptors**, **Windows handles**, and **Mach ports**) for seamless transit over message pipes; and efficiently watch -handles for various types of state transitions. +handles for various types of state transitions. Finally, there are also APIs to +bootstrap Mojo IPC between two processes. This document provides a brief guide to API usage with example code snippets. For a detailed API references please consult the headers in @@ -64,11 +65,14 @@ API calls, or by reading messages which contain attached handles. A `MojoHandle` can represent a message pipe endpoint, a data pipe consumer, a data pipe producer, a shared buffer reference, a wrapped native platform -handle such as a POSIX file descriptor or a Windows system handle, or a watcher -object (see [Signals & Watchers](#Signals-Watchers) below.) +handle such as a POSIX file descriptor or a Windows system handle, a trap object +(see [Signals & Traps](#Signals-Traps) below), or a process invitation (see +[Invitations](#Invitations) below). -All types of handles except for watchers (which are an inherently local concept) -can be attached to messages and sent over message pipes. +Message pipes, data pipes, shared buffers, and platform handles can all be +attached to messages and sent over message pipes. Traps are an inherently +process-local concept, and invitations are transmitted using special dedicated +APIs. Any `MojoHandle` may be closed by calling `MojoClose`: @@ -92,12 +96,11 @@ unstructured binary messages with zero or more `MojoHandle` attachments to be transferred from one end of a pipe to the other. Message pipes work seamlessly across process boundaries or within a single process. -The [Embedder Development Kit (EDK)](/mojo/edk/embedder/README.md) provides the -means to bootstrap one or more primordial cross-process message pipes, and it's -up to Mojo embedders to expose this capability in some useful way. Once such a -pipe is established, additional handles -- including other message pipe -handles -- may be sent to a remote process using that pipe (or in turn, over -other pipes sent over that pipe, or pipes sent over *that* pipe, and so on...) +[Invitations](#Invitations) provide the means to bootstrap one or more +primordial cross-process message pipes between two processes. Once such a pipe +is established, additional handles -- including other message pipe handles -- +may be sent to a remote process using that pipe (or in turn, over other pipes +sent over that pipe, or pipes sent over *that* pipe, and so on...) The public C System API exposes the ability to read and write messages on pipes and to create new message pipes. @@ -124,7 +127,7 @@ written to `b` are eventually readable from `a`. If `a` is closed at any point, will become aware of that. The state of these conditions can be queried and watched asynchronously as -described in the [Signals & Watchers](#Signals-Watchers) section below. +described in the [Signals & Traps](#Signals-Traps) section below. ### Creating Messages @@ -221,7 +224,7 @@ and not transferred. In this case since we know `b` is still open, we also know the message will eventually arrive at `b`. `b` can be queried or watched to become aware of when the message arrives, but we'll ignore that complexity for now. See -[Signals & Watchers](#Signals-Watchers) below for more information. +[Signals & Traps](#Signals-Traps) below for more information. *** aside **NOTE**: Although this is an implementation detail and not strictly guaranteed @@ -528,7 +531,7 @@ over a message pipe first. Native platform handles to system objects can be wrapped as Mojo handles for seamless transit over message pipes. Mojo currently supports wrapping POSIX -file descriptors, Windows handles, and Mach ports. +file descriptors, Windows handles, Mach ports, and Fuchsia zx_handles. See [//mojo/public/c/system/platform_handle.h](https://cs.chromium.org/chromium/src/mojo/public/c/system/platform_handle.h) for detailed platform handle API documentation. @@ -579,7 +582,7 @@ On OS X, the wrapped platform handle must be a memory-object send right. On all other POSIX systems, the wrapped platform handle must be a file descriptor for a shared memory object. -## Signals & Watchers +## Signals & Traps Message pipe and data pipe (producer and consumer) handles can change state in ways that may be interesting to a Mojo API user. For example, you may wish to @@ -649,81 +652,72 @@ Finally if we read the last message from `a` its signaling state becomes: and we know definitively that `a` can never be read from again. -### Watching Signals +### Trapping Signals The ability to query a handle's signaling state can be useful, but it's not -sufficient to support robust and efficient pipe usage. Mojo watchers empower -users with the ability to **watch** a handle's signaling state for interesting -changes and automatically invoke a notification handler in response. +sufficient to support robust and efficient pipe usage. Mojo traps empower users +with the ability to **trap** changes in a handle's signaling state and +automatically invoke a notification handler in response. -When a watcher is created it must be bound to a function pointer matching +When a trap is created it must be bound to a function pointer matching the following signature, defined in -[//mojo/public/c/system/watcher.h](https://cs.chromium.org/chromium/src/mojo/public/c/system/watcher.h): +[//mojo/public/c/system/trap.h](https://cs.chromium.org/chromium/src/mojo/public/c/system/trap.h): ``` c -typedef void (*MojoWatcherNotificationCallback)( - uintptr_t context, - MojoResult result, - MojoHandleSignalsState signals_state, - MojoWatcherNotificationFlags flags); +typedef void (*MojoTrapEventHandler)(const struct MojoTrapEvent* event); ``` -The `context` argument corresponds to a specific handle being watched by the -watcher (read more below), and the remaining arguments provide details regarding -the specific reason for the notification. It's important to be aware that a -watcher's registered handler may be called **at any time** and -**on any thread**. +The `event` parameter conveys details about why the event handler is being +invoked. The handler may be called **at any time** and **from any thread**, so +it is critical that handler implementations account for this. It's also helpful to understand a bit about the mechanism by which the handler can be invoked. Essentially, any Mojo C System API call may elicit a handle state change of some kind. If such a change is relevant to conditions watched by -a watcher, and that watcher is in a state which allows it raise a corresponding +a trap, and that trap is in a state which allows it raise a corresponding notification, its notification handler will be invoked synchronously some time -before the outermost System API call on the current thread's stack returns. +before the stack unwinds beyond the outermost System API call on the current +thread. Handle state changes can also occur as a result of incoming IPC from an external process. If a pipe in the current process is connected to an endpoint in another process and the internal Mojo system receives an incoming message bound for the -local endpoint, the arrival of that message will trigger a state change on the -receiving handle and may thus invoke one or more watchers' notification handlers -as a result. +local endpoint, the arrival of that message may trigger a state change on the +receiving handle and may therefore invoke one or more traps' notification +handlers as a result. -The `MOJO_WATCHER_NOTIFICATION_FLAG_FROM_SYSTEM` flag on the notification -handler's `flags` argument is used to indicate whether the handler was invoked -due to such an internal system IPC event (if the flag is set), or if it was -invoked synchronously due to some local API call (if the flag is unset.) -This distinction can be useful to make in certain cases to *e.g.* avoid -accidental reentrancy in user code. +The `MOJO_TRAP_EVENT_FLAG_WITHIN_API_CALL` flag on the `flags` field of `event` +is used to indicate whether the handler was invoked due to such an internal +system IPC event (if the flag is unset), or if it was invoked synchronously due +to some local API call (if the flag is set.) This distinction can be useful to +make in certain cases to *e.g.* avoid accidental reentrancy in user code. -### Creating a Watcher +### Creating a Trap -Creating a watcher is simple: +Creating a trap is simple: ``` c -void OnNotification(uintptr_t context, - MojoResult result, - MojoHandleSignalsState signals_state, - MojoWatcherNotificationFlags flags) { +void OnNotification(const struct MojoTrapEvent* event) { // ... } -MojoHandle w; -MojoResult result = MojoCreateWatcher(&OnNotification, &w); +MojoHandle t; +MojoResult result = MojoCreateTrap(&OnNotification, NULL, &t); ``` -Like all other `MojoHandle` types, watchers may be destroyed by closing them -with `MojoClose`. Unlike other `MojoHandle` types, watcher handles are **not** +Like all other `MojoHandle` types, traps may be destroyed by closing them with +`MojoClose`. Unlike most other `MojoHandle` types, trap handles are **not** transferrable across message pipes. -In order for a watcher to be useful, it has to watch at least one handle. +In order for a trap to be useful, it has have at least one **trigger** attached +to it. -### Adding a Handle to a Watcher +### Adding a Trigger to a Trap -Any given watcher can watch any given (message or data pipe) handle for some set +Any given trap can watch any given (message or data pipe) handle for some set of signaling conditions. A handle may be watched simultaneously by multiple -watchers, and a single watcher can watch multiple different handles -simultaneously. +traps, and a single trap can watch multiple different handles simultaneously. ``` c MojoHandle a, b; @@ -731,25 +725,27 @@ MojoCreateMessagePipe(NULL, &a, &b); // Watch handle |a| for readability. const uintptr_t context = 1234; -MojoResult result = MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, context); +MojoResult result = MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + context, NULL); ``` -We've successfully instructed watcher `w` to begin watching pipe handle `a` for -readability. However, our recently created watcher is still in a **disarmed** -state, meaning that it will never fire a notification pertaining to this watched -signaling condition. It must be **armed** before that can happen. +We've successfully instructed trap `t` to begin watching pipe handle `a` for +readability. However, our recently created trap is still in a **disarmed** +state, meaning that it will never fire a notification pertaining to this +trigger. It must be **armed** before that can happen. -### Arming a Watcher +### Arming a Trap -In order for a watcher to invoke its notification handler in response to a -relevant signaling state change on a watched handle, it must first be armed. A -watcher may only be armed if none of its watched handles would elicit a -notification immediately once armed. +In order for a trap to invoke its notification handler in response to a relevant +signaling state change on a watched handle, it must first be armed. A trap may +only be armed if none of its attached triggers would elicit a notification +immediately once armed. In this case `a` is clearly not yet readable, so arming should succeed: ``` c -MojoResult result = MojoArmWatcher(w, NULL, NULL, NULL, NULL); +MojoResult result = MojoArmTrap(t, NULL, NULL, NULL); ``` Now we can write to `b` to make `a` readable: @@ -762,56 +758,58 @@ MojoWriteMessage(b, m, nullptr); Eventually -- and in practice possibly before `MojoWriteMessage` even returns -- this will cause `OnNotification` to be invoked on the calling thread -with the `context` value (*i.e.* 1234) that was given when the handle was added -to the watcher. +with the `context` value (*i.e.* 1234) that was given when the trigger was added +to the trap. -The `result` parameter will be `MOJO_RESULT_OK` to indicate that the watched -signaling condition has been *satisfied*. If the watched condition had instead -become permanently *unsatisfiable* (*e.g.*, if `b` were instead closed), `result` -would instead indicate `MOJO_RESULT_FAILED_PRECONDITION`. +The `result` field of the event will be `MOJO_RESULT_OK` to indicate that the +trigger's condition has been met. If the handle's state had instead changed in +such a way that the trigger's condition could never be met again (*e.g.* if `b` +were instead closed), `result` would instead indicate +`MOJO_RESULT_FAILED_PRECONDITION`. -**NOTE:** Immediately before a watcher decides to invoke its notification -handler, it automatically disarms itself to prevent another state change from -eliciting another notification. Therefore a watcher must be repeatedly rearmed -in order to continue dispatching signaling notifications. +**NOTE:** Immediately before a trigger decides to invoke its event handler, it +automatically disarms itself to prevent another state change from eliciting +another notification. Therefore a trap must be repeatedly rearmed in order to +continue dispatching events. -As noted above, arming a watcher may fail if any of the watched conditions for -a handle are already partially satisfied or fully unsatisfiable. In that case -the caller may provide buffers for `MojoArmWatcher` to store information about -a subset of the relevant watches which caused it to fail: +As noted above, arming a watcher may fail if any of its triggers would be +activated immediately. In that case, the caller may provide buffers to +`MojoArmTrap` to receive information about a subset of the triggers which caused +it to fail: ``` c -// Provide some storage for information about watches that are already ready. -uint32_t num_ready_contexts = 4; -uintptr_t ready_contexts[4]; -MojoResult ready_results[4]; -struct MojoHandleSignalsStates ready_states[4]; -MojoResult result = MojoArmWatcher(w, &num_ready_contexts, ready_contexts, - ready_results, ready_states); +// Provide some storage for information about triggers that would have been +// activated immediately. +uint32_t num_blocking_events = 2; +MojoTrapEvent blocking_events[2] = {{sizeof(MojoTrapEvent)}, + {sizeof(MojoTrapEvent)}}; +MojoResult result = MojoArmTrap(t, NULL, &num_blocking_events, + &blocking_events); ``` -Because `a` is still readable this operation will fail with -`MOJO_RESULT_FAILED_PRECONDITION`. The input value of `num_ready_contexts` -informs `MojoArmWatcher` that it may store information regarding up to 4 watches -which currently prevent arming. In this case of course there is only one active -watch, so upon return we will see: +Because `a` is still readable this operation will now fail with +`MOJO_RESULT_FAILED_PRECONDITION`. The input value of `num_blocking_events` +informs `MojoArmTrap` that it may store information regarding up to 2 triggers +which have prevented arming. In this case of course there is only one active +trigger, so upon return we will see: -* `num_ready_contexts` is `1`. -* `ready_contexts[0]` is `1234`. -* `ready_results[0]` is `MOJO_RESULT_OK` -* `ready_states[0]` is the last known signaling state of handle `a`. +* `num_blocking_events` is `1`. +* `blocking_events[0].trigger_context` is `1234`. +* `blocking_events[0].result` is `MOJO_RESULT_OK` +* `blocking_events[0].signals_state` is the last known signaling state of handle + `a`. -In other words the stored information mirrors what would have been the -notification handler's arguments if the watcher were allowed to arm and thus -notify immediately. +In other words the stored information mirrors what would have been the resulting +event structure if the trap were allowed to arm and then notify immediately. -### Cancelling a Watch +### Removing a Trigger -There are three ways a watch can be cancelled: +There are three ways a trigger can be removed: -* The watched handle is closed -* The watcher handle is closed (in which case all of its watches are cancelled.) -* `MojoCancelWatch` is explicitly called for a given `context`. +* The handle being watched by the trigger is closed +* The trap handle is closed, in which case all of its attached triggers are + implicitly removed. +* `MojoRemoveTrigger` is called for a given `context`. In the above example this means any of the following operations will cancel the watch on `a`: @@ -820,25 +818,24 @@ watch on `a`: // Close the watched handle... MojoClose(a); -// OR close the watcher handle... -MojoClose(w); +// OR close the trap handle... +MojoClose(t); -// OR explicitly cancel. -MojoResult result = MojoCancelWatch(w, 1234); +// OR explicitly remove it. +MojoResult result = MojoRemoveTrigger(t, 1234, NULL); ``` -In every case the watcher's notification handler is invoked for the cancelled -watch(es) regardless of whether or not the watcher is or was armed at the time. -The notification handler receives a `result` of `MOJO_RESULT_CANCELLED` for -these notifications, and this is guaranteed to be the final notification for any -given watch context. +In every case the trap's event handler is invoked for the cancelled trigger(es) +regardless of whether or not the trap was armed at the time. The event handler +receives a `result` of `MOJO_RESULT_CANCELLED` for each of these invocations, +and this is guaranteed to be the final event for any given trigger context. -### Practical Watch Context Usage +### Practical Trigger Context Usage -It is common and probably wise to treat a watch's `context` value as an opaque +It is common and probably wise to treat a trigger's `context` value as an opaque pointer to some thread-safe state associated in some way with the handle being -watched. Here's a small example which uses a single watcher to watch both ends -of a message pipe and accumulate a count of messages received at each end. +watched. Here's a small example which uses a single trap to watch both ends of a +message pipe and accumulate a count of messages received at each end. ``` c // NOTE: For the sake of simplicity this example code is not in fact @@ -846,45 +843,43 @@ of a message pipe and accumulate a count of messages received at each end. // no external process connections, this is fine. struct WatchedHandleState { - MojoHandle watcher; + MojoHandle trap; MojoHandle handle; int message_count; }; -void OnNotification(uintptr_t context, - MojoResult result, - MojoHandleSignalsState signals_state, - MojoWatcherNotificationFlags flags) { - struct WatchedHandleState* state = (struct WatchedHandleState*)(context); +void OnNotification(const struct MojoTrapEvent* event) { + struct WatchedHandleState* state = + (struct WatchedHandleState*)(event->trigger_context); MojoResult rv; - if (result == MOJO_RESULT_CANCELLED) { - // Cancellation is always the last notification and is guaranteed to - // eventually happen for every context, assuming no handles are leaked. We - // treat this as an opportunity to free the WatchedHandleState. + if (event->result == MOJO_RESULT_CANCELLED) { + // Cancellation is always the last event and is guaranteed to happen for + // every context, assuming no handles are leaked. We treat this as an + // opportunity to free the WatchedHandleState. free(state); return; } if (result == MOJO_RESULT_FAILED_PRECONDITION) { // No longer readable, i.e. the other handle must have been closed. Better - // cancel. Note that we could also just call MojoClose(state->watcher) here - // since we know |context| is its only registered watch. - MojoCancelWatch(state->watcher, context); + // cancel. Note that we could also just call MojoClose(state->trap) here + // since we know there's only one attached trigger. + MojoRemoveTrigger(state->trap, event->trigger_context, NULL); return; } - // This is the only handle watched by the watcher, so as long as we can't arm + // This is the only handle watched by the trap, so as long as we can't arm // the watcher we know something's up with this handle. Try to read messages // until we can successfully arm again or something goes terribly wrong. - while (MojoArmWatcher(state->watcher, NULL, NULL, NULL, NULL) == + while (MojoArmTrap(state->trap, NULL NULL, NULL) == MOJO_RESULT_FAILED_PRECONDITION) { rv = MojoReadMessageNew(state->handle, NULL, NULL, NULL, MOJO_READ_MESSAGE_FLAG_MAY_DISCARD); if (rv == MOJO_RESULT_OK) { state->message_count++; } else if (rv == MOJO_RESULT_FAILED_PRECONDITION) { - MojoCancelWatch(state->watcher, context); + MojoRemoveTrigger(state->trap, event->trigger_context, NULL); return; } } @@ -893,25 +888,29 @@ void OnNotification(uintptr_t context, MojoHandle a, b; MojoCreateMessagePipe(NULL, &a, &b); -MojoHandle a_watcher, b_watcher; -MojoCreateWatcher(&OnNotification, &a_watcher); -MojoCreateWatcher(&OnNotification, &b_watcher) +MojoHandle a_trap, b_trap; +MojoCreateTrap(&OnNotification, NULL, &a_trap); +MojoCreateTrap(&OnNotification, NULL, &b_trap) struct WatchedHandleState* a_state = malloc(sizeof(struct WatchedHandleState)); -a_state->watcher = a_watcher; +a_state->trap = a_trap; a_state->handle = a; a_state->message_count = 0; struct WatchedHandleState* b_state = malloc(sizeof(struct WatchedHandleState)); -b_state->watcher = b_watcher; +b_state->trap = b_trap; b_state->handle = b; b_state->message_count = 0; -MojoWatch(a_watcher, a, MOJO_HANDLE_SIGNAL_READABLE, (uintptr_t)a_state); -MojoWatch(b_watcher, b, MOJO_HANDLE_SIGNAL_READABLE, (uintptr_t)b_state); +MojoAddTrigger(a_trap, a, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, (uintptr_t)a_state, + NULL); +MojoAddTrigger(b_trap, b, MOJO_HANDLE_SIGNAL_READABLE, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, (uintptr_t)b_state, + NULL); -MojoArmWatcher(a_watcher, NULL, NULL, NULL, NULL); -MojoArmWatcher(b_watcher, NULL, NULL, NULL, NULL); +MojoArmTrap(a_trap, NULL, NULL, NULL); +MojoArmTrap(b_trap, NULL, NULL, NULL); ``` Now any writes to `a` will increment `message_count` in `b_state`, and any @@ -920,3 +919,12 @@ writes to `b` will increment `message_count` in `a_state`. If either `a` or `b` is closed, both watches will be cancelled - one because watch cancellation is implicit in handle closure, and the other because its watcher will eventually detect that the handle is no longer readable. + +## Invitations + +TODO. + +For now see the +[C header](https://cs.chromium.org/src/mojo/public/c/system/invitation.h) and +the documentation for the equivalent +[C++ API](/mojo/public/cpp/system/README.md#Invitations). diff --git a/chromium/mojo/public/c/system/core.h b/chromium/mojo/public/c/system/core.h index 5d29a0f485b..2e8fd39d3fa 100644 --- a/chromium/mojo/public/c/system/core.h +++ b/chromium/mojo/public/c/system/core.h @@ -16,6 +16,7 @@ #include "mojo/public/c/system/macros.h" #include "mojo/public/c/system/message_pipe.h" #include "mojo/public/c/system/platform_handle.h" +#include "mojo/public/c/system/quota.h" #include "mojo/public/c/system/system_export.h" #include "mojo/public/c/system/trap.h" #include "mojo/public/c/system/types.h" diff --git a/chromium/mojo/public/c/system/functions.h b/chromium/mojo/public/c/system/functions.h index f59ee577342..9f90c38962e 100644 --- a/chromium/mojo/public/c/system/functions.h +++ b/chromium/mojo/public/c/system/functions.h @@ -21,8 +21,8 @@ extern "C" { // Initializes Mojo in the calling application. // -// With the exception of EDK embedders, applications using Mojo APIs must call -// this function before any others. +// With the exception of Mojo Core embedders, applications using Mojo APIs must +// call this function before any others. // // |options| may be null. // @@ -30,8 +30,8 @@ extern "C" { // |MOJO_RESULT_OK| if Mojo intiailization was successful. // |MOJO_RESULT_INVALID_ARGUMENT| if |options| was null or invalid. // |MOJO_RESULT_FAILED_PRECONDITION| if |MojoInitialize()| was already called -// once or if the application already explicitly initialized a Mojo EDK -// environment. +// once or if the application already explicitly initialized a Mojo Core +// environment as an embedder. MOJO_SYSTEM_EXPORT MojoResult MojoInitialize(const struct MojoInitializeOptions* options); diff --git a/chromium/mojo/public/c/system/invitation.h b/chromium/mojo/public/c/system/invitation.h index 233b93740fa..eb1a4a80e8e 100644 --- a/chromium/mojo/public/c/system/invitation.h +++ b/chromium/mojo/public/c/system/invitation.h @@ -43,15 +43,13 @@ struct MOJO_ALIGNAS(8) MojoProcessErrorDetails { // An error message corresponding to the reason why the connection was // terminated. This is an information message which may be useful to // developers. - const char* error_message; + MOJO_POINTER_FIELD(const char*, error_message); // See |MojoProcessErrorFlags|. MojoProcessErrorFlags flags; }; -MOJO_STATIC_ASSERT_FOR_32_BIT(sizeof(MojoProcessErrorDetails) == 16, - "MojoProcessErrorDetails has wrong size."); -MOJO_STATIC_ASSERT_FOR_64_BIT(sizeof(MojoProcessErrorDetails) == 24, - "MojoProcessErrorDetails has wrong size."); +MOJO_STATIC_ASSERT(sizeof(MojoProcessErrorDetails) == 24, + "MojoProcessErrorDetails has wrong size."); // An opaque process handle value which must be provided when sending an // invitation to another process via a platform transport. See @@ -110,14 +108,10 @@ struct MOJO_ALIGNAS(8) MojoInvitationTransportEndpoint { // - On Fuchsua, this is a single channel Fuchsia handle // - On other POSIX systems, this is a single Unix domain socket file // descriptor. - const struct MojoPlatformHandle* platform_handles; + MOJO_POINTER_FIELD(const struct MojoPlatformHandle*, platform_handles); }; -MOJO_STATIC_ASSERT_FOR_32_BIT( - sizeof(MojoInvitationTransportEndpoint) == 16, - "MojoInvitationTransportEndpoint has wrong size."); -MOJO_STATIC_ASSERT_FOR_64_BIT( - sizeof(MojoInvitationTransportEndpoint) == 24, - "MojoInvitationTransportEndpoint has wrong size."); +MOJO_STATIC_ASSERT(sizeof(MojoInvitationTransportEndpoint) == 24, + "MojoInvitationTransportEndpoint has wrong size."); // Flags passed to |MojoCreateInvitation()| via |MojoCreateInvitationOptions|. typedef uint32_t MojoCreateInvitationFlags; @@ -181,6 +175,17 @@ typedef uint32_t MojoSendInvitationFlags; // No flags. Default behavior. #define MOJO_SEND_INVITATION_FLAG_NONE ((MojoSendInvitationFlags)0) +// Send an isolated invitation to the receiver. Isolated invitations only +// establish communication between the sender and the receiver. Accepting an +// isolated invitation does not make IPC possible between the sender and any +// other members of the receiver's process graph, nor does it make IPC possible +// between the receiver and any other members of the sender's process graph. +// +// Invitations sent with this flag set must be accepted with the corresponding +// |MOJO_ACCEPT_INVITATION_FLAG_ISOLATED| flag set, and may only have a single +// message pipe attached with a name of exactly four zero-bytes ("\0\0\0\0"). +#define MOJO_SEND_INVITATION_FLAG_ISOLATED ((MojoSendInvitationFlags)1) + // Options passed to |MojoSendInvitation()|. struct MOJO_ALIGNAS(8) MojoSendInvitationOptions { // The size of this structure, used for versioning. @@ -188,8 +193,16 @@ struct MOJO_ALIGNAS(8) MojoSendInvitationOptions { // See |MojoSendInvitationFlags|. MojoSendInvitationFlags flags; + + // If |flags| includes |MOJO_SEND_INVITATION_FLAG_ISOLATED| then these fields + // specify a name identifying the established isolated connection. There are + // no restrictions on the value given. If |isolated_connection_name_length| is + // non-zero, the system ensures that only one isolated process connection can + // exist for the given name at any time. + MOJO_POINTER_FIELD(const char*, isolated_connection_name); + uint32_t isolated_connection_name_length; }; -MOJO_STATIC_ASSERT(sizeof(MojoSendInvitationOptions) == 8, +MOJO_STATIC_ASSERT(sizeof(MojoSendInvitationOptions) == 24, "MojoSendInvitationOptions has wrong size"); // Flags passed to |MojoAcceptInvitation()| via |MojoAcceptInvitationOptions|. @@ -198,6 +211,10 @@ typedef uint32_t MojoAcceptInvitationFlags; // No flags. Default behavior. #define MOJO_ACCEPT_INVITATION_FLAG_NONE ((MojoAcceptInvitationFlags)0) +// Accept an isoalted invitation from the sender. See +// |MOJO_SEND_INVITATION_FLAG_ISOLATED| for details. +#define MOJO_ACCEPT_INVITATION_FLAG_ISOLATED ((MojoAcceptInvitationFlags)1) + // Options passed to |MojoAcceptInvitation()|. struct MOJO_ALIGNAS(8) MojoAcceptInvitationOptions { // The size of this structure, used for versioning. @@ -229,7 +246,7 @@ typedef void (*MojoProcessErrorHandler)( // An invitation is used to invite another process to join this process's // IPC network. The caller must already be a member of a Mojo network, either // either by itself having been previously invited, or by being the Mojo broker -// process initialized via private Mojo APIs (i.e. the EDK). +// process initialized via the Mojo Core Embedder API. // // Invitations can have message pipes attached to them, and these message pipes // are used to bootstrap Mojo IPC between the inviter and the invitee. See diff --git a/chromium/mojo/public/c/system/macros.h b/chromium/mojo/public/c/system/macros.h index 2c297bb76ff..6de4a913de7 100644 --- a/chromium/mojo/public/c/system/macros.h +++ b/chromium/mojo/public/c/system/macros.h @@ -13,16 +13,19 @@ // MOJO_STATIC_ASSERT(sizeof(Foo) == 12, "Foo has invalid size"); #if defined(__cplusplus) #define MOJO_STATIC_ASSERT(expr, msg) static_assert(expr, msg) -#define MOJO_STATIC_ASSERT_FOR_32_BIT(expr, msg) \ - static_assert(sizeof(void*) != 4 || (expr), msg) -#define MOJO_STATIC_ASSERT_FOR_64_BIT(expr, msg) \ - static_assert(sizeof(void*) != 8 || (expr), msg) #else #define MOJO_STATIC_ASSERT(expr, msg) -#define MOJO_STATIC_ASSERT_FOR_32_BIT(expr, msg) -#define MOJO_STATIC_ASSERT_FOR_64_BIT(expr, msg) #endif +// Defines a pointer-sized struct field of the given type. This ensures that the +// field has an 8-byte footprint on both 32-bit and 64-bit systems, using an +// anonymous bitfield of either 32 or 0 bits, depending on pointer size. Weird +// formatting here courtesy of clang-format. +#define MOJO_POINTER_FIELD(type, name) \ + type name; \ + uint32_t: \ + (sizeof(void*) == 4 ? 32 : 0) + // Like the C++11 |alignof| operator. #if __cplusplus >= 201103L #define MOJO_ALIGNOF(type) alignof(type) diff --git a/chromium/mojo/public/c/system/quota.h b/chromium/mojo/public/c/system/quota.h new file mode 100644 index 00000000000..0e4ad3cb722 --- /dev/null +++ b/chromium/mojo/public/c/system/quota.h @@ -0,0 +1,126 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_C_SYSTEM_QUOTA_H_ +#define MOJO_PUBLIC_C_SYSTEM_QUOTA_H_ + +#include <stdint.h> + +#include "mojo/public/c/system/macros.h" +#include "mojo/public/c/system/system_export.h" +#include "mojo/public/c/system/types.h" + +// Flags passed to |MojoSetQuota| via |MojoSetQuotaOptions|. +typedef uint32_t MojoSetQuotaFlags; + +// No flags. +#define MOJO_SET_QUOTA_FLAG_NONE ((MojoSetQuotaFlags)0) + +// Options passed to |MojoSetQuota()|. +struct MOJO_ALIGNAS(8) MojoSetQuotaOptions { + // The size of this structure, used for versioning. + uint32_t struct_size; + + // See |MojoSetQuotaFlags| above. + MojoSetQuotaFlags flags; +}; +MOJO_STATIC_ASSERT(sizeof(MojoSetQuotaOptions) == 8, + "MojoSetQuotaOptions has wrong size."); + +// Flags passed to |MojoQueryQuota| via |MojoQueryQuotaOptions|. +typedef uint32_t MojoQueryQuotaFlags; + +// No flags. +#define MOJO_QUERY_QUOTA_FLAG_NONE ((MojoQueryQuotaFlags)0) + +// Options passed to |MojoQueryQuota()|. +struct MOJO_ALIGNAS(8) MojoQueryQuotaOptions { + // The size of this structure, used for versioning. + uint32_t struct_size; + + // See |MojoQueryQuotaFlags| above. + MojoQueryQuotaFlags flags; +}; +MOJO_STATIC_ASSERT(sizeof(MojoQueryQuotaOptions) == 8, + "MojoQueryQuotaOptions has wrong size."); + +// The maximum value any quota can be set to. Effectively means "no quota". +#define MOJO_QUOTA_LIMIT_NONE ((uint64_t)0xffffffffffffffff) + +// An enumeration of different types of quotas that can be set on a handle. +typedef uint32_t MojoQuotaType; + +// Limits the number of unread messages which can be queued on a message pipe +// endpoint before raising a |MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED| signal on that +// endpoint. May only be set on message pipe handles. +#define MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH ((MojoQuotaType)0) + +// Limits the total size (in bytes) of unread messages which can be queued on a +// message pipe endpoint before raising a |MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED| +// signal on that endpoint. May only be set on message pipe handles. +#define MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE ((MojoQuotaType)1) + +#ifdef __cplusplus +extern "C" { +#endif + +// Sets a quota on a given handle which will cause that handle to raise the +// |MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED| signal if the quota is exceeded. Signals +// can be trapped using |MojoCreateTrap()| and related APIs (see trap.h). +// +// All quota limits on a handle default to |MOJO_QUOTA_LIMIT_NONE|, meaning that +// the resource is unlimited. +// +// NOTE: A handle's quota is only enforced as long as the handle remains within +// the process which set the quota. +// +// Parameters: +// |handle|: The handle on which a quota should be set. +// |type|: The type of quota to set. Certain types of quotas may only be set +// on certain types of handles. See notes on individual quota type +// definitions above for meaning and restrictions. +// |limit|: The limiting value of the quota. The meaning of this is determined +// by |type|. See notes on individual quota type definitions above. +// |options|: Additional options; may be null. +// +// Returns: +// |MOJO_RESULT_OK| if the quota was successfully set. +// |MOJO_RESULT_INVALID_ARGUMENT| if |handle| is not a valid handle value, +// |type| is not a known quota type, |options| is non-null but +// |*options| is malformed, or the quota |type| cannot be set on |handle| +// because the quota does not apply to that type of handle. +MOJO_SYSTEM_EXPORT MojoResult +MojoSetQuota(MojoHandle handle, + MojoQuotaType type, + uint64_t limit, + const struct MojoSetQuotaOptions* options); + +// Queries a handle for information about a specific quota. +// +// Parameters: +// |handle|: The handle to query. +// |type|: The type of quota to query. +// |limit|: Receives the quota's currently set limit if non-null. +// |usage|: Receives the quota's current usage if non-null. +// +// Returns: +// |MOJO_RESULT_OK| if the quota was successfully queried on |handle|. Upon +// return, |*limit| contains the quota's current limit if |limit| is +// non-null, and |*usage| contains the quota's current usage if |usage| is +// non-null. +// |MOJO_RESULT_INVALID_ARGUMENT| if |handle| is not a valid handle value or +// quota |type| does not apply to the type of object referenced by +// |handle|. +MOJO_SYSTEM_EXPORT MojoResult +MojoQueryQuota(MojoHandle handle, + MojoQuotaType type, + const struct MojoQueryQuotaOptions* options, + uint64_t* limit, + uint64_t* usage); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // MOJO_PUBLIC_C_SYSTEM_QUOTA_H_ diff --git a/chromium/mojo/public/c/system/thunks.cc b/chromium/mojo/public/c/system/thunks.cc index 38fc96688dc..cffef2cc50c 100644 --- a/chromium/mojo/public/c/system/thunks.cc +++ b/chromium/mojo/public/c/system/thunks.cc @@ -10,11 +10,13 @@ #include "base/logging.h" #include "base/macros.h" +#include "base/memory/protected_memory.h" +#include "base/memory/protected_memory_cfi.h" #include "base/no_destructor.h" #include "build/build_config.h" #include "mojo/public/c/system/core.h" -#if defined(OS_CHROMEOS) || defined(OS_LINUX) +#if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_WIN) #include "base/environment.h" #include "base/files/file_path.h" #include "base/optional.h" @@ -24,10 +26,35 @@ namespace { -MojoSystemThunks g_thunks = {0}; +typedef void (*MojoGetSystemThunksFunction)(MojoSystemThunks* thunks); + +#if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_WIN) +PROTECTED_MEMORY_SECTION +base::ProtectedMemory<MojoGetSystemThunksFunction> g_get_thunks; +#endif + +PROTECTED_MEMORY_SECTION base::ProtectedMemory<MojoSystemThunks> g_thunks; + +MojoResult NotImplemented(const char* name) { + DLOG(ERROR) << "Function 'Mojo" << name + << "()' not supported in this version of Mojo Core."; + return MOJO_RESULT_UNIMPLEMENTED; +} } // namespace +// Macro to verify that the thunk symbol |name| is actually present in the +// runtime version of Mojo Core that is currently in use. +#define FUNCTION_IS_IMPLEMENTED(name) \ + (reinterpret_cast<uintptr_t>(static_cast<const void*>(&g_thunks->name)) - \ + reinterpret_cast<uintptr_t>(static_cast<const void*>(&g_thunks)) < \ + g_thunks->size) + +#define INVOKE_THUNK(name, ...) \ + FUNCTION_IS_IMPLEMENTED(name) \ + ? base::UnsanitizedCfiCall(g_thunks, &MojoSystemThunks::name)(__VA_ARGS__) \ + : NotImplemented(#name) + namespace mojo { // NOTE: This is defined within the global mojo namespace so that it can be @@ -35,42 +62,67 @@ namespace mojo { // enabled. class CoreLibraryInitializer { public: - typedef void (*MojoGetSystemThunksFunction)(MojoSystemThunks* thunks); - - CoreLibraryInitializer() { -#if defined(OS_CHROMEOS) || defined(OS_LINUX) - auto environment = base::Environment::Create(); - - base::FilePath library_path; - std::string library_path_value; - const char kLibraryPathEnvironmentVar[] = "MOJO_CORE_LIBRARY_PATH"; - if (environment->GetVar(kLibraryPathEnvironmentVar, &library_path_value)) { - library_path = base::FilePath::FromUTF8Unsafe(library_path_value); + CoreLibraryInitializer(const MojoInitializeOptions* options) { +#if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_WIN) + bool application_provided_path = false; + base::Optional<base::FilePath> library_path; + if (options && options->struct_size >= sizeof(*options) && + options->mojo_core_path) { + base::StringPiece utf8_path(options->mojo_core_path, + options->mojo_core_path_length); + library_path.emplace(base::FilePath::FromUTF8Unsafe(utf8_path)); + application_provided_path = true; } else { + auto environment = base::Environment::Create(); + std::string library_path_value; + const char kLibraryPathEnvironmentVar[] = "MOJO_CORE_LIBRARY_PATH"; + if (environment->GetVar(kLibraryPathEnvironmentVar, &library_path_value)) + library_path = base::FilePath::FromUTF8Unsafe(library_path_value); + } + + if (!library_path) { // Default to looking for the library in the current working directory. +#if defined(OS_CHROMEOS) || defined(OS_LINUX) const base::FilePath::CharType kDefaultLibraryPathValue[] = FILE_PATH_LITERAL("./libmojo_core.so"); - library_path = base::FilePath(kDefaultLibraryPathValue); +#elif defined(OS_WIN) + const base::FilePath::CharType kDefaultLibraryPathValue[] = + FILE_PATH_LITERAL("mojo_core.dll"); +#endif + library_path.emplace(kDefaultLibraryPathValue); } base::ScopedAllowBlocking allow_blocking; - library_.emplace(library_path); - CHECK(library_->is_valid()) - << "Unable to load the mojo_core library. Make sure the library is in " - << "the working directory or is correctly pointed to by the " - << "MOJO_CORE_LIBRARY_PATH environment variable."; + library_.emplace(*library_path); + if (!application_provided_path) { + CHECK(library_->is_valid()) + << "Unable to load the mojo_core library. Make sure the library is " + << "in the working directory or is correctly pointed to by the " + << "MOJO_CORE_LIBRARY_PATH environment variable."; + } else { + CHECK(library_->is_valid()) + << "Unable to locate mojo_core library. This application expects to " + << "find it at " << library_path->value(); + } const char kGetThunksFunctionName[] = "MojoGetSystemThunks"; - MojoGetSystemThunksFunction get_thunks = - reinterpret_cast<MojoGetSystemThunksFunction>( - library_->GetFunctionPointer(kGetThunksFunctionName)); - CHECK(get_thunks) << "Invalid mojo_core library"; - - DCHECK_EQ(g_thunks.size, 0u); - g_thunks.size = sizeof(g_thunks); - get_thunks(&g_thunks); + { + auto writer = base::AutoWritableMemory::Create(g_get_thunks); + *g_get_thunks = reinterpret_cast<MojoGetSystemThunksFunction>( + library_->GetFunctionPointer(kGetThunksFunctionName)); + } + CHECK(*g_get_thunks) << "Invalid mojo_core library: " + << library_path->value(); + + DCHECK_EQ(g_thunks->size, 0u); + { + auto writer = base::AutoWritableMemory::Create(g_thunks); + g_thunks->size = sizeof(*g_thunks); + base::UnsanitizedCfiCall(g_get_thunks)(&*g_thunks); + } - CHECK_GT(g_thunks.size, 0u) << "Invalid mojo_core library"; + CHECK_GT(g_thunks->size, 0u) + << "Invalid mojo_core library: " << library_path->value(); #else // defined(OS_CHROMEOS) || defined(OS_LINUX) NOTREACHED() << "Dynamic mojo_core loading is not supported on this platform."; @@ -80,7 +132,7 @@ class CoreLibraryInitializer { ~CoreLibraryInitializer() = default; private: -#if defined(OS_CHROMEOS) || defined(OS_LINUX) +#if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_WIN) base::Optional<base::ScopedNativeLibrary> library_; #endif @@ -92,117 +144,120 @@ class CoreLibraryInitializer { extern "C" { MojoResult MojoInitialize(const struct MojoInitializeOptions* options) { - static base::NoDestructor<mojo::CoreLibraryInitializer> initializer; + static base::NoDestructor<mojo::CoreLibraryInitializer> initializer(options); ALLOW_UNUSED_LOCAL(initializer); - DCHECK(g_thunks.Initialize); + DCHECK(g_thunks->Initialize); - return g_thunks.Initialize(options); + return INVOKE_THUNK(Initialize, options); } MojoTimeTicks MojoGetTimeTicksNow() { - return g_thunks.GetTimeTicksNow(); + return INVOKE_THUNK(GetTimeTicksNow); } MojoResult MojoClose(MojoHandle handle) { - return g_thunks.Close(handle); + return INVOKE_THUNK(Close, handle); } MojoResult MojoQueryHandleSignalsState( MojoHandle handle, struct MojoHandleSignalsState* signals_state) { - return g_thunks.QueryHandleSignalsState(handle, signals_state); + return INVOKE_THUNK(QueryHandleSignalsState, handle, signals_state); } MojoResult MojoCreateMessagePipe(const MojoCreateMessagePipeOptions* options, MojoHandle* message_pipe_handle0, MojoHandle* message_pipe_handle1) { - return g_thunks.CreateMessagePipe(options, message_pipe_handle0, - message_pipe_handle1); + return INVOKE_THUNK(CreateMessagePipe, options, message_pipe_handle0, + message_pipe_handle1); } MojoResult MojoWriteMessage(MojoHandle message_pipe_handle, MojoMessageHandle message_handle, const MojoWriteMessageOptions* options) { - return g_thunks.WriteMessage(message_pipe_handle, message_handle, options); + return INVOKE_THUNK(WriteMessage, message_pipe_handle, message_handle, + options); } MojoResult MojoReadMessage(MojoHandle message_pipe_handle, const MojoReadMessageOptions* options, MojoMessageHandle* message_handle) { - return g_thunks.ReadMessage(message_pipe_handle, options, message_handle); + return INVOKE_THUNK(ReadMessage, message_pipe_handle, options, + message_handle); } MojoResult MojoFuseMessagePipes(MojoHandle handle0, MojoHandle handle1, const MojoFuseMessagePipesOptions* options) { - return g_thunks.FuseMessagePipes(handle0, handle1, options); + return INVOKE_THUNK(FuseMessagePipes, handle0, handle1, options); } MojoResult MojoCreateDataPipe(const MojoCreateDataPipeOptions* options, MojoHandle* data_pipe_producer_handle, MojoHandle* data_pipe_consumer_handle) { - return g_thunks.CreateDataPipe(options, data_pipe_producer_handle, - data_pipe_consumer_handle); + return INVOKE_THUNK(CreateDataPipe, options, data_pipe_producer_handle, + data_pipe_consumer_handle); } MojoResult MojoWriteData(MojoHandle data_pipe_producer_handle, const void* elements, uint32_t* num_elements, const MojoWriteDataOptions* options) { - return g_thunks.WriteData(data_pipe_producer_handle, elements, num_elements, - options); + return INVOKE_THUNK(WriteData, data_pipe_producer_handle, elements, + num_elements, options); } MojoResult MojoBeginWriteData(MojoHandle data_pipe_producer_handle, const MojoBeginWriteDataOptions* options, void** buffer, uint32_t* buffer_num_elements) { - return g_thunks.BeginWriteData(data_pipe_producer_handle, options, buffer, - buffer_num_elements); + return INVOKE_THUNK(BeginWriteData, data_pipe_producer_handle, options, + buffer, buffer_num_elements); } MojoResult MojoEndWriteData(MojoHandle data_pipe_producer_handle, uint32_t num_elements_written, const MojoEndWriteDataOptions* options) { - return g_thunks.EndWriteData(data_pipe_producer_handle, num_elements_written, - options); + return INVOKE_THUNK(EndWriteData, data_pipe_producer_handle, + num_elements_written, options); } MojoResult MojoReadData(MojoHandle data_pipe_consumer_handle, const MojoReadDataOptions* options, void* elements, uint32_t* num_elements) { - return g_thunks.ReadData(data_pipe_consumer_handle, options, elements, - num_elements); + return INVOKE_THUNK(ReadData, data_pipe_consumer_handle, options, elements, + num_elements); } MojoResult MojoBeginReadData(MojoHandle data_pipe_consumer_handle, const MojoBeginReadDataOptions* options, const void** buffer, uint32_t* buffer_num_elements) { - return g_thunks.BeginReadData(data_pipe_consumer_handle, options, buffer, - buffer_num_elements); + return INVOKE_THUNK(BeginReadData, data_pipe_consumer_handle, options, buffer, + buffer_num_elements); } MojoResult MojoEndReadData(MojoHandle data_pipe_consumer_handle, uint32_t num_elements_read, const MojoEndReadDataOptions* options) { - return g_thunks.EndReadData(data_pipe_consumer_handle, num_elements_read, - options); + return INVOKE_THUNK(EndReadData, data_pipe_consumer_handle, num_elements_read, + options); } MojoResult MojoCreateSharedBuffer(uint64_t num_bytes, const MojoCreateSharedBufferOptions* options, MojoHandle* shared_buffer_handle) { - return g_thunks.CreateSharedBuffer(num_bytes, options, shared_buffer_handle); + return INVOKE_THUNK(CreateSharedBuffer, num_bytes, options, + shared_buffer_handle); } MojoResult MojoDuplicateBufferHandle( MojoHandle buffer_handle, const MojoDuplicateBufferHandleOptions* options, MojoHandle* new_buffer_handle) { - return g_thunks.DuplicateBufferHandle(buffer_handle, options, - new_buffer_handle); + return INVOKE_THUNK(DuplicateBufferHandle, buffer_handle, options, + new_buffer_handle); } MojoResult MojoMapBuffer(MojoHandle buffer_handle, @@ -210,23 +265,24 @@ MojoResult MojoMapBuffer(MojoHandle buffer_handle, uint64_t num_bytes, const MojoMapBufferOptions* options, void** buffer) { - return g_thunks.MapBuffer(buffer_handle, offset, num_bytes, options, buffer); + return INVOKE_THUNK(MapBuffer, buffer_handle, offset, num_bytes, options, + buffer); } MojoResult MojoUnmapBuffer(void* buffer) { - return g_thunks.UnmapBuffer(buffer); + return INVOKE_THUNK(UnmapBuffer, buffer); } MojoResult MojoGetBufferInfo(MojoHandle buffer_handle, const MojoGetBufferInfoOptions* options, MojoSharedBufferInfo* info) { - return g_thunks.GetBufferInfo(buffer_handle, options, info); + return INVOKE_THUNK(GetBufferInfo, buffer_handle, options, info); } MojoResult MojoCreateTrap(MojoTrapEventHandler handler, const MojoCreateTrapOptions* options, MojoHandle* trap_handle) { - return g_thunks.CreateTrap(handler, options, trap_handle); + return INVOKE_THUNK(CreateTrap, handler, options, trap_handle); } MojoResult MojoAddTrigger(MojoHandle trap_handle, @@ -235,38 +291,36 @@ MojoResult MojoAddTrigger(MojoHandle trap_handle, MojoTriggerCondition condition, uintptr_t context, const MojoAddTriggerOptions* options) { - return g_thunks.AddTrigger(trap_handle, handle, signals, condition, context, - options); + return INVOKE_THUNK(AddTrigger, trap_handle, handle, signals, condition, + context, options); } MojoResult MojoRemoveTrigger(MojoHandle trap_handle, uintptr_t context, const MojoRemoveTriggerOptions* options) { - return g_thunks.RemoveTrigger(trap_handle, context, options); + return INVOKE_THUNK(RemoveTrigger, trap_handle, context, options); } MojoResult MojoArmTrap(MojoHandle trap_handle, const MojoArmTrapOptions* options, - uint32_t* num_ready_triggers, - uintptr_t* ready_triggers, - MojoResult* ready_results, - MojoHandleSignalsState* ready_signals_states) { - return g_thunks.ArmTrap(trap_handle, options, num_ready_triggers, - ready_triggers, ready_results, ready_signals_states); + uint32_t* num_blocking_events, + MojoTrapEvent* blocking_events) { + return INVOKE_THUNK(ArmTrap, trap_handle, options, num_blocking_events, + blocking_events); } MojoResult MojoCreateMessage(const MojoCreateMessageOptions* options, MojoMessageHandle* message) { - return g_thunks.CreateMessage(options, message); + return INVOKE_THUNK(CreateMessage, options, message); } MojoResult MojoDestroyMessage(MojoMessageHandle message) { - return g_thunks.DestroyMessage(message); + return INVOKE_THUNK(DestroyMessage, message); } MojoResult MojoSerializeMessage(MojoMessageHandle message, const MojoSerializeMessageOptions* options) { - return g_thunks.SerializeMessage(message, options); + return INVOKE_THUNK(SerializeMessage, message, options); } MojoResult MojoAppendMessageData(MojoMessageHandle message, @@ -276,8 +330,8 @@ MojoResult MojoAppendMessageData(MojoMessageHandle message, const MojoAppendMessageDataOptions* options, void** buffer, uint32_t* buffer_size) { - return g_thunks.AppendMessageData(message, payload_size, handles, num_handles, - options, buffer, buffer_size); + return INVOKE_THUNK(AppendMessageData, message, payload_size, handles, + num_handles, options, buffer, buffer_size); } MojoResult MojoGetMessageData(MojoMessageHandle message, @@ -286,8 +340,8 @@ MojoResult MojoGetMessageData(MojoMessageHandle message, uint32_t* num_bytes, MojoHandle* handles, uint32_t* num_handles) { - return g_thunks.GetMessageData(message, options, buffer, num_bytes, handles, - num_handles); + return INVOKE_THUNK(GetMessageData, message, options, buffer, num_bytes, + handles, num_handles); } MojoResult MojoSetMessageContext(MojoMessageHandle message, @@ -295,34 +349,37 @@ MojoResult MojoSetMessageContext(MojoMessageHandle message, MojoMessageContextSerializer serializer, MojoMessageContextDestructor destructor, const MojoSetMessageContextOptions* options) { - return g_thunks.SetMessageContext(message, context, serializer, destructor, - options); + return INVOKE_THUNK(SetMessageContext, message, context, serializer, + destructor, options); } MojoResult MojoGetMessageContext(MojoMessageHandle message, const MojoGetMessageContextOptions* options, uintptr_t* context) { - return g_thunks.GetMessageContext(message, options, context); + return INVOKE_THUNK(GetMessageContext, message, options, context); } MojoResult MojoNotifyBadMessage(MojoMessageHandle message, const char* error, uint32_t error_num_bytes, const MojoNotifyBadMessageOptions* options) { - return g_thunks.NotifyBadMessage(message, error, error_num_bytes, options); + return INVOKE_THUNK(NotifyBadMessage, message, error, error_num_bytes, + options); } MojoResult MojoWrapPlatformHandle(const MojoPlatformHandle* platform_handle, const MojoWrapPlatformHandleOptions* options, MojoHandle* mojo_handle) { - return g_thunks.WrapPlatformHandle(platform_handle, options, mojo_handle); + return INVOKE_THUNK(WrapPlatformHandle, platform_handle, options, + mojo_handle); } MojoResult MojoUnwrapPlatformHandle( MojoHandle mojo_handle, const MojoUnwrapPlatformHandleOptions* options, MojoPlatformHandle* platform_handle) { - return g_thunks.UnwrapPlatformHandle(mojo_handle, options, platform_handle); + return INVOKE_THUNK(UnwrapPlatformHandle, mojo_handle, options, + platform_handle); } MojoResult MojoWrapPlatformSharedMemoryRegion( @@ -333,9 +390,9 @@ MojoResult MojoWrapPlatformSharedMemoryRegion( MojoPlatformSharedMemoryRegionAccessMode access_mode, const MojoWrapPlatformSharedMemoryRegionOptions* options, MojoHandle* mojo_handle) { - return g_thunks.WrapPlatformSharedMemoryRegion( - platform_handles, num_platform_handles, num_bytes, guid, access_mode, - options, mojo_handle); + return INVOKE_THUNK(WrapPlatformSharedMemoryRegion, platform_handles, + num_platform_handles, num_bytes, guid, access_mode, + options, mojo_handle); } MojoResult MojoUnwrapPlatformSharedMemoryRegion( @@ -346,14 +403,14 @@ MojoResult MojoUnwrapPlatformSharedMemoryRegion( uint64_t* num_bytes, struct MojoSharedBufferGuid* guid, MojoPlatformSharedMemoryRegionAccessMode* access_mode) { - return g_thunks.UnwrapPlatformSharedMemoryRegion( - mojo_handle, options, platform_handles, num_platform_handles, num_bytes, - guid, access_mode); + return INVOKE_THUNK(UnwrapPlatformSharedMemoryRegion, mojo_handle, options, + platform_handles, num_platform_handles, num_bytes, guid, + access_mode); } MojoResult MojoCreateInvitation(const MojoCreateInvitationOptions* options, MojoHandle* invitation_handle) { - return g_thunks.CreateInvitation(options, invitation_handle); + return INVOKE_THUNK(CreateInvitation, options, invitation_handle); } MojoResult MojoAttachMessagePipeToInvitation( @@ -362,8 +419,8 @@ MojoResult MojoAttachMessagePipeToInvitation( uint32_t name_num_bytes, const MojoAttachMessagePipeToInvitationOptions* options, MojoHandle* message_pipe_handle) { - return g_thunks.AttachMessagePipeToInvitation( - invitation_handle, name, name_num_bytes, options, message_pipe_handle); + return INVOKE_THUNK(AttachMessagePipeToInvitation, invitation_handle, name, + name_num_bytes, options, message_pipe_handle); } MojoResult MojoExtractMessagePipeFromInvitation( @@ -372,8 +429,8 @@ MojoResult MojoExtractMessagePipeFromInvitation( uint32_t name_num_bytes, const MojoExtractMessagePipeFromInvitationOptions* options, MojoHandle* message_pipe_handle) { - return g_thunks.ExtractMessagePipeFromInvitation( - invitation_handle, name, name_num_bytes, options, message_pipe_handle); + return INVOKE_THUNK(ExtractMessagePipeFromInvitation, invitation_handle, name, + name_num_bytes, options, message_pipe_handle); } MojoResult MojoSendInvitation( @@ -383,31 +440,47 @@ MojoResult MojoSendInvitation( MojoProcessErrorHandler error_handler, uintptr_t error_handler_context, const MojoSendInvitationOptions* options) { - return g_thunks.SendInvitation(invitation_handle, process_handle, - transport_endpoint, error_handler, - error_handler_context, options); + return INVOKE_THUNK(SendInvitation, invitation_handle, process_handle, + transport_endpoint, error_handler, error_handler_context, + options); } MojoResult MojoAcceptInvitation( const MojoInvitationTransportEndpoint* transport_endpoint, const MojoAcceptInvitationOptions* options, MojoHandle* invitation_handle) { - return g_thunks.AcceptInvitation(transport_endpoint, options, - invitation_handle); + return INVOKE_THUNK(AcceptInvitation, transport_endpoint, options, + invitation_handle); +} + +MojoResult MojoSetQuota(MojoHandle handle, + MojoQuotaType type, + uint64_t limit, + const MojoSetQuotaOptions* options) { + return INVOKE_THUNK(SetQuota, handle, type, limit, options); +} + +MojoResult MojoQueryQuota(MojoHandle handle, + MojoQuotaType type, + const MojoQueryQuotaOptions* options, + uint64_t* limit, + uint64_t* usage) { + return INVOKE_THUNK(QueryQuota, handle, type, options, limit, usage); } } // extern "C" void MojoEmbedderSetSystemThunks(const MojoSystemThunks* thunks) { - // Assume embedders will always use matching versions of the EDK and public - // APIs. - DCHECK_EQ(thunks->size, sizeof(g_thunks)); - - // This should only have to check that the |g_thunks.size| is zero, but we - // have multiple EDK initializations in some test suites still. For now we - // allow double calls as long as they're the same thunks as before. - DCHECK(g_thunks.size == 0 || !memcmp(&g_thunks, thunks, sizeof(g_thunks))) + // Assume embedders will always use matching versions of the Mojo Core and + // public APIs. + DCHECK_EQ(thunks->size, sizeof(*g_thunks)); + + // This should only have to check that the |g_thunks->size| is zero, but we + // have multiple Mojo Core initializations in some test suites still. For now + // we allow double calls as long as they're the same thunks as before. + DCHECK(g_thunks->size == 0 || !memcmp(&*g_thunks, thunks, sizeof(*g_thunks))) << "Cannot set embedder thunks after Mojo API calls have been made."; - g_thunks = *thunks; + auto writer = base::AutoWritableMemory::Create(g_thunks); + *g_thunks = *thunks; } diff --git a/chromium/mojo/public/c/system/thunks.h b/chromium/mojo/public/c/system/thunks.h index b1cc6b15702..1e5e2493c7e 100644 --- a/chromium/mojo/public/c/system/thunks.h +++ b/chromium/mojo/public/c/system/thunks.h @@ -156,10 +156,8 @@ struct MojoSystemThunks { const struct MojoRemoveTriggerOptions* options); MojoResult (*ArmTrap)(MojoHandle trap_handle, const struct MojoArmTrapOptions* options, - uint32_t* num_ready_triggers, - uintptr_t* ready_triggers, - MojoResult* ready_results, - MojoHandleSignalsState* ready_signals_states); + uint32_t* num_blocking_events, + struct MojoTrapEvent* blocking_events); // Platform handle API. MojoResult (*WrapPlatformHandle)( @@ -214,6 +212,15 @@ struct MojoSystemThunks { const struct MojoInvitationTransportEndpoint* transport_endpoint, const struct MojoAcceptInvitationOptions* options, MojoHandle* invitation_handle); + MojoResult (*SetQuota)(MojoHandle handle, + MojoQuotaType type, + uint64_t limit, + const struct MojoSetQuotaOptions* options); + MojoResult (*QueryQuota)(MojoHandle handle, + MojoQuotaType type, + const struct MojoQueryQuotaOptions* options, + uint64_t* limit, + uint64_t* usage); }; #pragma pack(pop) diff --git a/chromium/mojo/public/c/system/trap.h b/chromium/mojo/public/c/system/trap.h index 7c65af31fa2..c5e718ec131 100644 --- a/chromium/mojo/public/c/system/trap.h +++ b/chromium/mojo/public/c/system/trap.h @@ -40,7 +40,7 @@ struct MOJO_ALIGNAS(8) MojoTrapEvent { MojoTrapEventFlags flags; // The context for the trigger which tripped the trap. - uintptr_t trigger_context; + MOJO_POINTER_FIELD(uintptr_t, trigger_context); // A result code indicating the cause of the event. May take on any of the // following values: @@ -63,6 +63,8 @@ struct MOJO_ALIGNAS(8) MojoTrapEvent { // time the trap was tripped. struct MojoHandleSignalsState signals_state; }; +MOJO_STATIC_ASSERT(sizeof(MojoTrapEvent) == 32, + "MojoTrapEvent has wrong size."); // Value given to |MojoAddTrigger| to configure what condition should cause it // to trip its trap. May be one of the following values: @@ -101,6 +103,8 @@ struct MOJO_ALIGNAS(8) MojoCreateTrapOptions { // Flags. Currently unused. MojoCreateTrapFlags flags; }; +MOJO_STATIC_ASSERT(sizeof(MojoCreateTrapOptions) == 8, + "MojoCreateTrapOptions has wrong size."); // Flags passed to |MojoAddTrigger()| via |MojoAddTriggerOptions|. typedef uint32_t MojoAddTriggerFlags; @@ -119,6 +123,8 @@ struct MOJO_ALIGNAS(8) MojoAddTriggerOptions { // Flags. Currently unused. MojoAddTriggerFlags flags; }; +MOJO_STATIC_ASSERT(sizeof(MojoAddTriggerOptions) == 8, + "MojoAddTriggerOptions has wrong size."); // Flags passed to |MojoRemoveTrigger()| via |MojoRemoveTriggerOptions|. typedef uint32_t MojoRemoveTriggerFlags; @@ -137,6 +143,8 @@ struct MOJO_ALIGNAS(8) MojoRemoveTriggerOptions { // Flags. Currently unused. MojoRemoveTriggerFlags flags; }; +MOJO_STATIC_ASSERT(sizeof(MojoRemoveTriggerOptions) == 8, + "MojoRemoveTriggerOptions has wrong size."); // Flags passed to |MojoArmTrap()| via |MojoArmTrapOptions|. typedef uint32_t MojoArmTrapFlags; @@ -155,6 +163,8 @@ struct MOJO_ALIGNAS(8) MojoArmTrapOptions { // Flags. Currently unused. MojoArmTrapFlags flags; }; +MOJO_STATIC_ASSERT(sizeof(MojoArmTrapOptions) == 8, + "MojoArmTrapOptions has wrong size."); #ifdef __cplusplus extern "C" { @@ -267,51 +277,43 @@ MojoRemoveTrigger(MojoHandle trap_handle, // // Parameters: // |trap_handle|: The handle of the trap to be armed. -// |num_ready_triggers|: An address pointing to the number of elements -// available for storage in the following output buffer parameters. +// |num_blocking_events|: An address pointing to the number of elements +// available for storage at the address given by |blocking_events|. // Optional and only used when |MOJO_RESULT_FAILED_PRECONDITION| is // returned. See below. -// |ready_triggers|: An output buffer for contexts of triggers that would have -// tripped the trap immediately if it were armed. Optional and used only -// when |MOJO_RESULT_FAILED_PRECONDITION| is returned. See below. -// |ready_results|: An output buffer for |MojoResult| values corresponding to -// each trigger in |ready_triggers|. Optional and only used when -// |MOJO_RESULT_FAILED_PRECONDITION| is returned. See below. -// |ready_signals_states|: An output buffer for |MojoHandleSignalsState| -// structures corresponding to the handle observed by each respective -// trigger in |ready_triggers|. Optional and only used when +// |blocking_events|: An output buffer for |MojoTrapEvent| structures to be +// filled in if one or more triggers would have tripped the trap +// immediately if it were armed. Optional and used only when // |MOJO_RESULT_FAILED_PRECONDITION| is returned. See below. // // Returns: -// |MOJO_RESULT_OK| if the trap has been successfully armed. All arguments -// other than |trap_handle| are ignored in this case. -// |MOJO_RESULT_NOT_FOUND| if the trap does not have any triggers. All -// arguments other than |trap_handle| are ignored. +// |MOJO_RESULT_OK| if the trap has been successfully armed. +// |num_blocking_events| and |blocking_events| are ignored. +// |MOJO_RESULT_NOT_FOUND| if the trap does not have any triggers. +// |num_blocking_events| and |blocking_events| are ignored. // |MOJO_RESULT_INVALID_ARGUMENT| if |trap_handle| is not a valid trap handle, -// or if |num_ready_triggers| is non-null and any of the output buffer -// paramters is null. +// or if |num_blocking_events| is non-null but |blocking_events| is +// not. // |MOJO_RESULT_FAILED_PRECONDITION| if one or more triggers would have -// tripped the trap immediately upon arming. If |num_handles| is non-null, -// this assumes there is enough space for |*num_handles| entries in each -// of the subsequent output buffer parameters. +// tripped the trap immediately upon arming. If |num_blocking_events| is +// non-null, this assumes there is enough space for |*num_blocking_events| +// entries at the non-null address in |blocking_events|. // -// At most |*num_handles| entries are populated in the output buffers, -// with each entry corresponding to one of the triggers which would have -// tripped the trap. The actual number of entries populated is written to -// |*num_handles| before returning. +// At most |*num_blocking_events| entries are populated there, with each +// entry corresponding to one of the triggers which would have tripped the +// trap. The actual number of entries populated is written to +// |*num_blocking_events| before returning. // // If there are more ready triggers than available provided storage, the -// subset presented to thecaller is arbitrary. The runtime makes an effort -// to circulate triggers returned by consecutive failed |MojoArmTrap()| -// calls so that callers may avoid handle starvation when observing a -// large number of active handles with a single trap. +// subset presented to the caller is arbitrary. The runtime makes an +// effort to circulate triggers returned by consecutive failed +// |MojoArmTrap()| calls so that callers may avoid handle starvation when +// observing a large number of active handles with a single trap. MOJO_SYSTEM_EXPORT MojoResult MojoArmTrap(MojoHandle trap_handle, const struct MojoArmTrapOptions* options, - uint32_t* num_ready_triggers, - uintptr_t* ready_triggers, - MojoResult* ready_results, - struct MojoHandleSignalsState* ready_signals_states); + uint32_t* num_blocking_events, + struct MojoTrapEvent* blocking_events); #ifdef __cplusplus } // extern "C" diff --git a/chromium/mojo/public/c/system/types.h b/chromium/mojo/public/c/system/types.h index 79562634e82..4cf566a5699 100644 --- a/chromium/mojo/public/c/system/types.h +++ b/chromium/mojo/public/c/system/types.h @@ -149,7 +149,16 @@ struct MOJO_ALIGNAS(8) MojoInitializeOptions { // See |MojoInitializeFlags|. MojoInitializeFlags flags; + + // Address and length of the UTF8-encoded path of a mojo_core shared library + // to load. If the |mojo_core_path| is null then |mojo_core_path_length| is + // ignored and Mojo will fall back first onto the |MOJO_CORE_LIBRARY_PATH| + // environment variable, and then onto the current working directory. + MOJO_POINTER_FIELD(const char*, mojo_core_path); + uint32_t mojo_core_path_length; }; +MOJO_STATIC_ASSERT(sizeof(MojoInitializeOptions) == 24, + "MojoInitializeOptions has wrong size"); // |MojoHandleSignals|: Used to specify signals that can be watched for on a // handle (and which can be triggered), e.g., the ability to read or write to @@ -168,6 +177,8 @@ struct MOJO_ALIGNAS(8) MojoInitializeOptions { // execution context (e.g. in another process.) Note that this signal is // maintained with best effort but may at any time be slightly out of sync // with the actual location of the peer handle. +// |MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED| - One or more quotas set on the handle +// is currently exceeded. typedef uint32_t MojoHandleSignals; @@ -178,6 +189,7 @@ const MojoHandleSignals MOJO_HANDLE_SIGNAL_WRITABLE = 1 << 1; const MojoHandleSignals MOJO_HANDLE_SIGNAL_PEER_CLOSED = 1 << 2; const MojoHandleSignals MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE = 1 << 3; const MojoHandleSignals MOJO_HANDLE_SIGNAL_PEER_REMOTE = 1 << 4; +const MojoHandleSignals MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED = 1 << 5; #else #define MOJO_HANDLE_SIGNAL_NONE ((MojoHandleSignals)0) #define MOJO_HANDLE_SIGNAL_READABLE ((MojoHandleSignals)1 << 0) @@ -185,6 +197,7 @@ const MojoHandleSignals MOJO_HANDLE_SIGNAL_PEER_REMOTE = 1 << 4; #define MOJO_HANDLE_SIGNAL_PEER_CLOSED ((MojoHandleSignals)1 << 2) #define MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE ((MojoHandleSignals)1 << 3); #define MOJO_HANDLE_SIGNAL_PEER_REMOTE ((MojoHandleSignals)1 << 4); +#define MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED ((MojoHandleSignals)1 << 5); #endif // |MojoHandleSignalsState|: Returned by watch notification callbacks and diff --git a/chromium/mojo/public/cpp/base/BUILD.gn b/chromium/mojo/public/cpp/base/BUILD.gn index e97ccdab2a4..c57ac0bf1d9 100644 --- a/chromium/mojo/public/cpp/base/BUILD.gn +++ b/chromium/mojo/public/cpp/base/BUILD.gn @@ -30,10 +30,14 @@ component("shared_typemap_traits") { sources = [ "big_buffer_mojom_traits.cc", "big_buffer_mojom_traits.h", + "file_info_mojom_traits.cc", + "file_info_mojom_traits.h", "file_path_mojom_traits.cc", "file_path_mojom_traits.h", "shared_memory_mojom_traits.cc", "shared_memory_mojom_traits.h", + "time_mojom_traits.cc", + "time_mojom_traits.h", "values_mojom_traits.cc", "values_mojom_traits.h", ] diff --git a/chromium/mojo/public/cpp/base/file_info.typemap b/chromium/mojo/public/cpp/base/file_info.typemap index f6ef16a586c..4faac8f1c4c 100644 --- a/chromium/mojo/public/cpp/base/file_info.typemap +++ b/chromium/mojo/public/cpp/base/file_info.typemap @@ -4,9 +4,10 @@ mojom = "//mojo/public/mojom/base/file_info.mojom" public_headers = [ "//base/files/file.h" ] -traits_headers = [ "//ipc/ipc_message_utils.h" ] +traits_headers = [ "//mojo/public/cpp/base/file_info_mojom_traits.h" ] public_deps = [ - "//ipc", + "//base", + "//mojo/public/cpp/base:shared_typemap_traits", ] type_mappings = [ "mojo_base.mojom.FileInfo=base::File::Info" ] diff --git a/chromium/mojo/public/cpp/base/file_info_mojom_traits.cc b/chromium/mojo/public/cpp/base/file_info_mojom_traits.cc new file mode 100644 index 00000000000..64a08029573 --- /dev/null +++ b/chromium/mojo/public/cpp/base/file_info_mojom_traits.cc @@ -0,0 +1,28 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/public/cpp/base/file_info_mojom_traits.h" + +#include "base/logging.h" +#include "mojo/public/cpp/base/time_mojom_traits.h" + +namespace mojo { + +// static +bool StructTraits<mojo_base::mojom::FileInfoDataView, base::File::Info>::Read( + mojo_base::mojom::FileInfoDataView data, + base::File::Info* out) { + if (!data.ReadLastModified(&out->last_modified)) + return false; + if (!data.ReadLastAccessed(&out->last_accessed)) + return false; + if (!data.ReadCreationTime(&out->creation_time)) + return false; + out->size = data.size(); + out->is_directory = data.is_directory(); + out->is_symbolic_link = data.is_symbolic_link(); + return true; +} + +} // namespace mojo diff --git a/chromium/mojo/public/cpp/base/file_info_mojom_traits.h b/chromium/mojo/public/cpp/base/file_info_mojom_traits.h new file mode 100644 index 00000000000..4049e038d72 --- /dev/null +++ b/chromium/mojo/public/cpp/base/file_info_mojom_traits.h @@ -0,0 +1,49 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BASE_FILE_INFO_MOJOM_TRAITS_H_ +#define MOJO_PUBLIC_CPP_BASE_FILE_INFO_MOJOM_TRAITS_H_ + +#include <cstdint> + +#include "base/component_export.h" +#include "base/files/file.h" +#include "base/macros.h" +#include "mojo/public/cpp/bindings/struct_traits.h" +#include "mojo/public/mojom/base/file_info.mojom-shared.h" + +namespace mojo { + +template <> +struct COMPONENT_EXPORT(MOJO_BASE_SHARED_TRAITS) + StructTraits<mojo_base::mojom::FileInfoDataView, base::File::Info> { + static int64_t size(const base::File::Info& info) { return info.size; } + + static bool is_directory(const base::File::Info& info) { + return info.is_directory; + } + + static bool is_symbolic_link(const base::File::Info& info) { + return info.is_symbolic_link; + } + + static base::Time last_modified(const base::File::Info& info) { + return info.last_modified; + } + + static base::Time last_accessed(const base::File::Info& info) { + return info.last_accessed; + } + + static base::Time creation_time(const base::File::Info& info) { + return info.creation_time; + } + + static bool Read(mojo_base::mojom::FileInfoDataView data, + base::File::Info* out); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BASE_FILE_INFO_MOJOM_TRAITS_H_ diff --git a/chromium/mojo/public/cpp/base/file_mojom_traits.cc b/chromium/mojo/public/cpp/base/file_mojom_traits.cc index e7fd60590ea..5799b79dd12 100644 --- a/chromium/mojo/public/cpp/base/file_mojom_traits.cc +++ b/chromium/mojo/public/cpp/base/file_mojom_traits.cc @@ -23,7 +23,7 @@ bool StructTraits<mojo_base::mojom::FileDataView, base::File>::Read( MOJO_RESULT_OK) { return false; } - *file = base::File(platform_handle); + *file = base::File(platform_handle, data.async()); return true; } diff --git a/chromium/mojo/public/cpp/base/file_mojom_traits.h b/chromium/mojo/public/cpp/base/file_mojom_traits.h index ac2d0d294a9..e4dd2f98759 100644 --- a/chromium/mojo/public/cpp/base/file_mojom_traits.h +++ b/chromium/mojo/public/cpp/base/file_mojom_traits.h @@ -19,6 +19,7 @@ struct COMPONENT_EXPORT(MOJO_BASE_MOJOM) static void SetToNull(base::File* file) { *file = base::File(); } static mojo::ScopedHandle fd(base::File& file); + static bool async(base::File& file) { return file.async(); } static bool Read(mojo_base::mojom::FileDataView data, base::File* file); }; diff --git a/chromium/mojo/public/cpp/base/file_unittest.cc b/chromium/mojo/public/cpp/base/file_unittest.cc index fb3cff405e1..e1db729c752 100644 --- a/chromium/mojo/public/cpp/base/file_unittest.cc +++ b/chromium/mojo/public/cpp/base/file_unittest.cc @@ -28,6 +28,7 @@ TEST(FileTest, File) { mojo::test::SerializeAndDeserialize<mojom::File>(&file, &file_out)); std::vector<char> content(test_content.size()); ASSERT_TRUE(file_out.IsValid()); + ASSERT_FALSE(file_out.async()); ASSERT_EQ(static_cast<int>(test_content.size()), file_out.Read(0, content.data(), base::checked_cast<int>(test_content.size()))); @@ -35,6 +36,25 @@ TEST(FileTest, File) { base::StringPiece(content.data(), test_content.size())); } +TEST(FileTest, AsyncFile) { + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + base::FilePath path = temp_dir.GetPath().AppendASCII("async_test_file.txt"); + + base::File write_file(path, base::File::FLAG_CREATE | base::File::FLAG_WRITE); + const base::StringPiece test_content = "test string"; + write_file.WriteAtCurrentPos(test_content.data(), + base::checked_cast<int>(test_content.size())); + write_file.Close(); + + base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ | + base::File::FLAG_ASYNC); + base::File file_out; + ASSERT_TRUE( + mojo::test::SerializeAndDeserialize<mojom::File>(&file, &file_out)); + ASSERT_TRUE(file_out.async()); +} + TEST(FileTest, InvalidFile) { base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); diff --git a/chromium/mojo/public/cpp/base/logfont_win.typemap b/chromium/mojo/public/cpp/base/logfont_win.typemap index 43cdc1e2c1d..daaf6fcca6f 100644 --- a/chromium/mojo/public/cpp/base/logfont_win.typemap +++ b/chromium/mojo/public/cpp/base/logfont_win.typemap @@ -3,10 +3,16 @@ # found in the LICENSE file. mojom = "//mojo/public/mojom/base/logfont_win.mojom" -os_whitelist = [ "win" ] -public_headers = [ "//base/win/windows_full.h" ] -traits_headers = [ "//ipc/ipc_message_utils.h" ] +public_headers = [ + "//base/win/windows_full.h", + "//base/win/windows_types.h", +] +traits_headers = [ "//mojo/public/cpp/base/logfont_win_mojom_traits.h" ] public_deps = [ - "//ipc", + "//base", +] +sources = [ + "//mojo/public/cpp/base/logfont_win_mojom_traits.cc", + "//mojo/public/cpp/base/logfont_win_mojom_traits.h", ] type_mappings = [ "mojo_base.mojom.LOGFONT=::LOGFONT" ] diff --git a/chromium/mojo/public/cpp/base/logfont_win_mojom_traits.cc b/chromium/mojo/public/cpp/base/logfont_win_mojom_traits.cc new file mode 100644 index 00000000000..3c73701ce99 --- /dev/null +++ b/chromium/mojo/public/cpp/base/logfont_win_mojom_traits.cc @@ -0,0 +1,39 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/public/cpp/base/logfont_win_mojom_traits.h" + +#include <tchar.h> + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" + +namespace mojo { + +// static +base::span<const uint8_t> +StructTraits<mojo_base::mojom::LOGFONTDataView, ::LOGFONT>::bytes( + const ::LOGFONT& input) { + return base::make_span(reinterpret_cast<const uint8_t*>(&input), + sizeof(::LOGFONT)); +} + +// static +bool StructTraits<mojo_base::mojom::LOGFONTDataView, ::LOGFONT>::Read( + mojo_base::mojom::LOGFONTDataView data, + ::LOGFONT* out) { + ArrayDataView<uint8_t> bytes_view; + data.GetBytesDataView(&bytes_view); + if (bytes_view.size() != sizeof(::LOGFONT)) + return false; + + const ::LOGFONT* font = reinterpret_cast<const ::LOGFONT*>(bytes_view.data()); + if (_tcsnlen(font->lfFaceName, LF_FACESIZE) >= LF_FACESIZE) + return false; + + memcpy(out, font, sizeof(::LOGFONT)); + return true; +} + +} // namespace mojo diff --git a/chromium/mojo/public/cpp/base/logfont_win_mojom_traits.h b/chromium/mojo/public/cpp/base/logfont_win_mojom_traits.h new file mode 100644 index 00000000000..09a9fbbeeec --- /dev/null +++ b/chromium/mojo/public/cpp/base/logfont_win_mojom_traits.h @@ -0,0 +1,30 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BASE_LOGFONT_WIN_MOJOM_TRAITS_H_ +#define MOJO_PUBLIC_CPP_BASE_LOGFONT_WIN_MOJOM_TRAITS_H_ + +#include <windows.h> + +#include <cstdint> + +#include "base/component_export.h" +#include "base/containers/span.h" +#include "base/macros.h" +#include "base/win/windows_types.h" +#include "mojo/public/cpp/bindings/struct_traits.h" +#include "mojo/public/mojom/base/logfont_win.mojom-shared.h" + +namespace mojo { + +template <> +struct COMPONENT_EXPORT(MOJO_BASE_MOJOM) + StructTraits<mojo_base::mojom::LOGFONTDataView, ::LOGFONT> { + static base::span<const uint8_t> bytes(const ::LOGFONT& input); + static bool Read(mojo_base::mojom::LOGFONTDataView data, ::LOGFONT* out); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BASE_LOGFONT_WIN_MOJOM_TRAITS_H_ diff --git a/chromium/mojo/public/cpp/base/time.typemap b/chromium/mojo/public/cpp/base/time.typemap index b3b2e8b0658..ecd6a27fc7b 100644 --- a/chromium/mojo/public/cpp/base/time.typemap +++ b/chromium/mojo/public/cpp/base/time.typemap @@ -5,12 +5,9 @@ mojom = "//mojo/public/mojom/base/time.mojom" public_headers = [ "//base/time/time.h" ] traits_headers = [ "//mojo/public/cpp/base/time_mojom_traits.h" ] -sources = [ - "//mojo/public/cpp/base/time_mojom_traits.cc", - "//mojo/public/cpp/base/time_mojom_traits.h", -] public_deps = [ "//base", + "//mojo/public/cpp/base:shared_typemap_traits", ] type_mappings = [ diff --git a/chromium/mojo/public/cpp/base/time_mojom_traits.h b/chromium/mojo/public/cpp/base/time_mojom_traits.h index db54476861f..213c6e21696 100644 --- a/chromium/mojo/public/cpp/base/time_mojom_traits.h +++ b/chromium/mojo/public/cpp/base/time_mojom_traits.h @@ -12,7 +12,7 @@ namespace mojo { template <> -struct COMPONENT_EXPORT(MOJO_BASE_MOJOM) +struct COMPONENT_EXPORT(MOJO_BASE_SHARED_TRAITS) StructTraits<mojo_base::mojom::TimeDataView, base::Time> { static int64_t internal_value(const base::Time& time); @@ -20,7 +20,7 @@ struct COMPONENT_EXPORT(MOJO_BASE_MOJOM) }; template <> -struct COMPONENT_EXPORT(MOJO_BASE_MOJOM) +struct COMPONENT_EXPORT(MOJO_BASE_SHARED_TRAITS) StructTraits<mojo_base::mojom::TimeDeltaDataView, base::TimeDelta> { static int64_t microseconds(const base::TimeDelta& delta); @@ -29,7 +29,7 @@ struct COMPONENT_EXPORT(MOJO_BASE_MOJOM) }; template <> -struct COMPONENT_EXPORT(MOJO_BASE_MOJOM) +struct COMPONENT_EXPORT(MOJO_BASE_SHARED_TRAITS) StructTraits<mojo_base::mojom::TimeTicksDataView, base::TimeTicks> { static int64_t internal_value(const base::TimeTicks& time); diff --git a/chromium/mojo/public/cpp/bindings/BUILD.gn b/chromium/mojo/public/cpp/bindings/BUILD.gn index f268dcc8442..27152835ace 100644 --- a/chromium/mojo/public/cpp/bindings/BUILD.gn +++ b/chromium/mojo/public/cpp/bindings/BUILD.gn @@ -3,6 +3,9 @@ # found in the LICENSE file. import("//build/buildflag_header.gni") +import("//build/config/nacl/config.gni") +import("//tools/ipc_fuzzer/ipc_fuzzer.gni") + declare_args() { enable_mojo_tracing = false } @@ -91,6 +94,10 @@ component("bindings_base") { "//base", "//mojo/public/cpp/system", ] + + if (enable_ipc_fuzzer) { + all_dependent_configs = [ "//tools/ipc_fuzzer:ipc_fuzzer_config" ] + } } component("bindings") { @@ -161,6 +168,13 @@ component("bindings") { "unique_ptr_impl_ref_traits.h", ] + if (enable_ipc_fuzzer && !is_nacl_nonsfi) { + sources += [ + "lib/message_dumper.cc", + "message_dumper.h", + ] + } + public_deps = [ ":bindings_base", ":struct_traits", diff --git a/chromium/mojo/public/cpp/bindings/README.md b/chromium/mojo/public/cpp/bindings/README.md index 3c85a69f77c..89e5d95b157 100644 --- a/chromium/mojo/public/cpp/bindings/README.md +++ b/chromium/mojo/public/cpp/bindings/README.md @@ -16,6 +16,9 @@ This document provides a detailed guide to bindings API usage with example code snippets. For a detailed API references please consult the headers in [//mojo/public/cpp/bindings](https://cs.chromium.org/chromium/src/mojo/public/cpp/bindings/README.md). +For a simplified guide targeted at Chromium developers, see [this +link](/docs/mojo_guide.md). + ## Getting Started When a Mojom IDL file is processed by the bindings generator, C++ code is @@ -998,9 +1001,182 @@ class TableImpl : public db::mojom::Table { ## Associated Interfaces -See [this document](https://www.chromium.org/developers/design-documents/mojo/associated-interfaces). +Associated interfaces are interfaces which: -TODO: Move the above doc into the repository markdown docs. +* enable running multiple interfaces over a single message pipe while + preserving message ordering. +* make it possible for the bindings to access a single message pipe from + multiple sequences. + +### Mojom + +A new keyword `associated` is introduced for interface pointer/request +fields. For example: + +``` cpp +interface Bar {}; + +struct Qux { + associated Bar bar3; +}; + +interface Foo { + // Uses associated interface pointer. + SetBar(associated Bar bar1); + // Uses associated interface request. + GetBar(associated Bar& bar2); + // Passes a struct with associated interface pointer. + PassQux(Qux qux); + // Uses associated interface pointer in callback. + AsyncGetBar() => (associated Bar bar4); +}; +``` + +It means the interface impl/client will communicate using the same +message pipe over which the associated interface pointer/request is +passed. + +### Using associated interfaces in C++ + +When generating C++ bindings, the associated interface pointer of `Bar` is +mapped to `BarAssociatedPtrInfo` (which is an alias of +`mojo::AssociatedInterfacePtrInfo<Bar>`); associated interface request to +`BarAssociatedRequest` (which is an alias of +`mojo::AssociatedInterfaceRequest<Bar>`). + +``` cpp +// In mojom: +interface Foo { + ... + SetBar(associated Bar bar1); + GetBar(associated Bar& bar2); + ... +}; + +// In C++: +class Foo { + ... + virtual void SetBar(BarAssociatedPtrInfo bar1) = 0; + virtual void GetBar(BarAssociatedRequest bar2) = 0; + ... +}; +``` + +#### Passing associated interface requests + +Assume you have already got an `InterfacePtr<Foo> foo_ptr`, and you would like +to call `GetBar()` on it. You can do: + +``` cpp +BarAssociatedPtrInfo bar_ptr_info; +BarAssociatedRequest bar_request = MakeRequest(&bar_ptr_info); +foo_ptr->GetBar(std::move(bar_request)); + +// BarAssociatedPtr is an alias of AssociatedInterfacePtr<Bar>. +BarAssociatedPtr bar_ptr; +bar_ptr.Bind(std::move(bar_ptr_info)); +bar_ptr->DoSomething(); +``` + +First, the code creates an associated interface of type `Bar`. It looks very +similar to what you would do to setup a non-associated interface. An +important difference is that one of the two associated endpoints (either +`bar_request` or `bar_ptr_info`) must be sent over another interface. That is +how the interface is associated with an existing message pipe. + +It should be noted that you cannot call `bar_ptr->DoSomething()` before passing +`bar_request`. This is required by the FIFO-ness guarantee: at the receiver +side, when the message of `DoSomething` call arrives, we want to dispatch it to +the corresponding `AssociatedBinding<Bar>` before processing any subsequent +messages. If `bar_request` is in a subsequent message, message dispatching gets +into a deadlock. On the other hand, as soon as `bar_request` is sent, `bar_ptr` +is usable. There is no need to wait until `bar_request` is bound to an +implementation at the remote side. + +A `MakeRequest` overload which takes an `AssociatedInterfacePtr` pointer +(instead of an `AssociatedInterfacePtrInfo` pointer) is provided to make the +code a little shorter. The following code achieves the same purpose: + +``` cpp +BarAssociatedPtr bar_ptr; +foo_ptr->GetBar(MakeRequest(&bar_ptr)); +bar_ptr->DoSomething(); +``` + +The implementation of `Foo` looks like this: + +``` cpp +class FooImpl : public Foo { + ... + void GetBar(BarAssociatedRequest bar2) override { + bar_binding_.Bind(std::move(bar2)); + ... + } + ... + + Binding<Foo> foo_binding_; + AssociatedBinding<Bar> bar_binding_; +}; +``` + +In this example, `bar_binding_`'s lifespan is tied to that of `FooImpl`. But you +don't have to do that. You can, for example, pass `bar2` to another sequence to +bind to an `AssociatedBinding<Bar>` there. + +When the underlying message pipe is disconnected (e.g., `foo_ptr` or +`foo_binding_` is destroyed), all associated interface endpoints (e.g., +`bar_ptr` and `bar_binding_`) will receive a connection error. + +#### Passing associated interface pointers + +Similarly, assume you have already got an `InterfacePtr<Foo> foo_ptr`, and you +would like to call `SetBar()` on it. You can do: + +``` cpp +AssociatedBind<Bar> bar_binding(some_bar_impl); +BarAssociatedPtrInfo bar_ptr_info; +BarAssociatedRequest bar_request = MakeRequest(&bar_ptr_info); +foo_ptr->SetBar(std::move(bar_ptr_info)); +bar_binding.Bind(std::move(bar_request)); +``` + +The following code achieves the same purpose: + +``` cpp +AssociatedBind<Bar> bar_binding(some_bar_impl); +BarAssociatedPtrInfo bar_ptr_info; +bar_binding.Bind(&bar_ptr_info); +foo_ptr->SetBar(std::move(bar_ptr_info)); +``` + +### Performance considerations + +When using associated interfaces on different sequences than the master sequence +(where the master interface lives): + +* Sending messages: send happens directly on the calling sequence. So there + isn't sequence hopping. +* Receiving messages: associated interfaces bound on a different sequence from + the master interface incur an extra sequence hop during dispatch. + +Therefore, performance-wise associated interfaces are better suited for +scenarios where message receiving happens on the master sequence. + +### Testing + +Associated interfaces need to be associated with a master interface before +they can be used. This means one end of the associated interface must be sent +over one end of the master interface, or over one end of another associated +interface which itself already has a master interface. + +If you want to test an associated interface endpoint without first +associating it, you can use `mojo::MakeIsolatedRequest()`. This will create +working associated interface endpoints which are not actually associated with +anything else. + +### Read more + +* [Design: Mojo Associated Interfaces](https://docs.google.com/document/d/1nq3J_HbS-gvVfIoEhcVyxm1uY-9G_7lhD-4Kyxb1WIY/edit) ## Synchronous Calls diff --git a/chromium/mojo/public/cpp/bindings/connector.h b/chromium/mojo/public/cpp/bindings/connector.h index b08985b4c1d..fa71604b1b3 100644 --- a/chromium/mojo/public/cpp/bindings/connector.h +++ b/chromium/mojo/public/cpp/bindings/connector.h @@ -5,6 +5,7 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_CONNECTOR_H_ #define MOJO_PUBLIC_CPP_BINDINGS_CONNECTOR_H_ +#include <atomic> #include <memory> #include <utility> @@ -234,7 +235,7 @@ class MOJO_CPP_BINDINGS_EXPORT Connector : public MessageReceiver { std::unique_ptr<SimpleWatcher> handle_watcher_; base::Optional<HandleSignalTracker> peer_remoteness_tracker_; - bool error_ = false; + std::atomic<bool> error_; bool drop_writes_ = false; bool enforce_errors_from_incoming_receiver_ = true; @@ -270,6 +271,10 @@ class MOJO_CPP_BINDINGS_EXPORT Connector : public MessageReceiver { // nested dispatch operations. bool is_dispatching_ = false; +#if defined(ENABLE_IPC_FUZZER) + std::unique_ptr<MessageReceiver> message_dumper_; +#endif + // Create a single weak ptr and use it everywhere, to avoid the malloc/free // cost of creating a new weak ptr whenever it is needed. // NOTE: This weak pointer is invalidated when the message pipe is closed or diff --git a/chromium/mojo/public/cpp/bindings/interface_endpoint_client.h b/chromium/mojo/public/cpp/bindings/interface_endpoint_client.h index 6842c7c3221..581c91d842b 100644 --- a/chromium/mojo/public/cpp/bindings/interface_endpoint_client.h +++ b/chromium/mojo/public/cpp/bindings/interface_endpoint_client.h @@ -113,6 +113,7 @@ class MOJO_CPP_BINDINGS_EXPORT InterfaceEndpointClient void QueryVersion(const base::Callback<void(uint32_t)>& callback); void RequireVersion(uint32_t version); void FlushForTesting(); + void FlushAsyncForTesting(base::OnceClosure callback); private: // Maps from the id of a response to the MessageReceiver that handles the diff --git a/chromium/mojo/public/cpp/bindings/interface_ptr.h b/chromium/mojo/public/cpp/bindings/interface_ptr.h index 1de84b7830b..0d7d6ff3811 100644 --- a/chromium/mojo/public/cpp/bindings/interface_ptr.h +++ b/chromium/mojo/public/cpp/bindings/interface_ptr.h @@ -48,15 +48,15 @@ class InterfacePtr { InterfacePtr(decltype(nullptr)) {} // Takes over the binding of another InterfacePtr. - InterfacePtr(InterfacePtr&& other) { + InterfacePtr(InterfacePtr&& other) noexcept { internal_state_.Swap(&other.internal_state_); } - explicit InterfacePtr(PtrInfoType&& info) { Bind(std::move(info)); } + explicit InterfacePtr(PtrInfoType&& info) noexcept { Bind(std::move(info)); } // Takes over the binding of another InterfacePtr, and closes any message pipe // already bound to this pointer. - InterfacePtr& operator=(InterfacePtr&& other) { + InterfacePtr& operator=(InterfacePtr&& other) noexcept { reset(); internal_state_.Swap(&other.internal_state_); return *this; @@ -127,6 +127,12 @@ class InterfacePtr { // stimulus. void FlushForTesting() { internal_state_.FlushForTesting(); } + // Same as |FlushForTesting()| but will call |callback| when the flush is + // complete. + void FlushAsyncForTesting(base::OnceClosure callback) { + internal_state_.FlushAsyncForTesting(std::move(callback)); + } + // Closes the bound message pipe, if any. void reset() { State doomed; diff --git a/chromium/mojo/public/cpp/bindings/lib/connector.cc b/chromium/mojo/public/cpp/bindings/lib/connector.cc index 27b471f16cf..352c51815fc 100644 --- a/chromium/mojo/public/cpp/bindings/lib/connector.cc +++ b/chromium/mojo/public/cpp/bindings/lib/connector.cc @@ -22,6 +22,10 @@ #include "mojo/public/cpp/bindings/sync_handle_watcher.h" #include "mojo/public/cpp/system/wait.h" +#if defined(ENABLE_IPC_FUZZER) +#include "mojo/public/cpp/bindings/message_dumper.h" +#endif + namespace mojo { namespace { @@ -140,6 +144,7 @@ Connector::Connector(ScopedMessagePipeHandle message_pipe, scoped_refptr<base::SequencedTaskRunner> runner) : message_pipe_(std::move(message_pipe)), task_runner_(std::move(runner)), + error_(false), outgoing_serialization_mode_(g_default_outgoing_serialization_mode), incoming_serialization_mode_(g_default_incoming_serialization_mode), nesting_observer_(RunLoopNestingObserver::GetForThread()), @@ -147,6 +152,11 @@ Connector::Connector(ScopedMessagePipeHandle message_pipe, if (config == MULTI_THREADED_SEND) lock_.emplace(); +#if defined(ENABLE_IPC_FUZZER) + if (!MessageDumper::GetMessageDumpDirectory().empty()) + message_dumper_ = std::make_unique<MessageDumper>(); +#endif + weak_self_ = weak_factory_.GetWeakPtr(); // Even though we don't have an incoming receiver, we still want to monitor // the message pipe to know if is closed or encounters an error. @@ -263,9 +273,6 @@ bool Connector::Accept(Message* message) { if (!lock_) DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // It shouldn't hurt even if |error_| may be changed by a different sequence - // at the same time. The outcome is that we may write into |message_pipe_| - // after encountering an error, which should be fine. if (error_) return false; @@ -274,6 +281,13 @@ bool Connector::Accept(Message* message) { if (!message_pipe_.is_valid() || drop_writes_) return true; +#if defined(ENABLE_IPC_FUZZER) + if (message_dumper_ && message->is_serialized()) { + bool dump_result = message_dumper_->Accept(message); + DCHECK(dump_result); + } +#endif + MojoResult rv = WriteMessageNew(message_pipe_.get(), message->TakeMojoMessage(), MOJO_WRITE_MESSAGE_FLAG_NONE); diff --git a/chromium/mojo/public/cpp/bindings/lib/control_message_proxy.cc b/chromium/mojo/public/cpp/bindings/lib/control_message_proxy.cc index 9fd7bf41735..5e6c3808e8e 100644 --- a/chromium/mojo/public/cpp/bindings/lib/control_message_proxy.cc +++ b/chromium/mojo/public/cpp/bindings/lib/control_message_proxy.cc @@ -12,6 +12,7 @@ #include "base/callback_helpers.h" #include "base/macros.h" #include "base/run_loop.h" +#include "base/threading/sequenced_task_runner_handle.h" #include "mojo/public/cpp/bindings/lib/serialization.h" #include "mojo/public/cpp/bindings/lib/validation_util.h" #include "mojo/public/cpp/bindings/message.h" @@ -39,12 +40,12 @@ bool ValidateControlResponse(Message* message) { } using RunCallback = - base::Callback<void(interface_control::RunResponseMessageParamsPtr)>; + base::OnceCallback<void(interface_control::RunResponseMessageParamsPtr)>; class RunResponseForwardToCallback : public MessageReceiver { public: - explicit RunResponseForwardToCallback(const RunCallback& callback) - : callback_(callback) {} + explicit RunResponseForwardToCallback(RunCallback callback) + : callback_(std::move(callback)) {} bool Accept(Message* message) override; private: @@ -65,13 +66,13 @@ bool RunResponseForwardToCallback::Accept(Message* message) { Deserialize<interface_control::RunResponseMessageParamsDataView>( params, ¶ms_ptr, &context); - callback_.Run(std::move(params_ptr)); + std::move(callback_).Run(std::move(params_ptr)); return true; } void SendRunMessage(MessageReceiverWithResponder* receiver, interface_control::RunInputPtr input_ptr, - const RunCallback& callback) { + RunCallback callback) { auto params_ptr = interface_control::RunMessageParams::New(); params_ptr->input = std::move(input_ptr); Message message(interface_control::kRunMessageId, @@ -81,7 +82,7 @@ void SendRunMessage(MessageReceiverWithResponder* receiver, Serialize<interface_control::RunMessageParamsDataView>( params_ptr, message.payload_buffer(), ¶ms, &context); std::unique_ptr<MessageReceiver> responder = - std::make_unique<RunResponseForwardToCallback>(callback); + std::make_unique<RunResponseForwardToCallback>(std::move(callback)); ignore_result(receiver->AcceptWithResponder(&message, std::move(responder))); } @@ -115,9 +116,9 @@ void RunVersionCallback( callback.Run(version); } -void RunClosure(const base::Closure& callback, +void RunClosure(base::OnceClosure callback, interface_control::RunResponseMessageParamsPtr run_response) { - callback.Run(); + std::move(callback).Run(); } } // namespace @@ -133,7 +134,7 @@ void ControlMessageProxy::QueryVersion( auto input_ptr = interface_control::RunInput::New(); input_ptr->set_query_version(interface_control::QueryVersion::New()); SendRunMessage(receiver_, std::move(input_ptr), - base::Bind(&RunVersionCallback, callback)); + base::BindOnce(&RunVersionCallback, callback)); } void ControlMessageProxy::RequireVersion(uint32_t version) { @@ -145,29 +146,38 @@ void ControlMessageProxy::RequireVersion(uint32_t version) { } void ControlMessageProxy::FlushForTesting() { - if (encountered_error_) + base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); + FlushAsyncForTesting(run_loop.QuitClosure()); + run_loop.Run(); +} + +void ControlMessageProxy::FlushAsyncForTesting(base::OnceClosure callback) { + if (encountered_error_) { + base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE, + std::move(callback)); return; + } auto input_ptr = interface_control::RunInput::New(); input_ptr->set_flush_for_testing(interface_control::FlushForTesting::New()); - base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); - run_loop_quit_closure_ = run_loop.QuitClosure(); + DCHECK(!pending_flush_callback_); + pending_flush_callback_ = std::move(callback); SendRunMessage( receiver_, std::move(input_ptr), - base::Bind(&RunClosure, - base::Bind(&ControlMessageProxy::RunFlushForTestingClosure, - base::Unretained(this)))); - run_loop.Run(); + base::BindOnce( + &RunClosure, + base::BindOnce(&ControlMessageProxy::RunFlushForTestingClosure, + base::Unretained(this)))); } void ControlMessageProxy::RunFlushForTestingClosure() { - DCHECK(!run_loop_quit_closure_.is_null()); - base::ResetAndReturn(&run_loop_quit_closure_).Run(); + DCHECK(!pending_flush_callback_.is_null()); + std::move(pending_flush_callback_).Run(); } void ControlMessageProxy::OnConnectionError() { encountered_error_ = true; - if (!run_loop_quit_closure_.is_null()) + if (!pending_flush_callback_.is_null()) RunFlushForTestingClosure(); } diff --git a/chromium/mojo/public/cpp/bindings/lib/control_message_proxy.h b/chromium/mojo/public/cpp/bindings/lib/control_message_proxy.h index 2f9314ebf00..f15ba246874 100644 --- a/chromium/mojo/public/cpp/bindings/lib/control_message_proxy.h +++ b/chromium/mojo/public/cpp/bindings/lib/control_message_proxy.h @@ -29,6 +29,7 @@ class MOJO_CPP_BINDINGS_EXPORT ControlMessageProxy { void RequireVersion(uint32_t version); void FlushForTesting(); + void FlushAsyncForTesting(base::OnceClosure callback); void OnConnectionError(); private: @@ -38,7 +39,7 @@ class MOJO_CPP_BINDINGS_EXPORT ControlMessageProxy { MessageReceiverWithResponder* receiver_; bool encountered_error_ = false; - base::Closure run_loop_quit_closure_; + base::OnceClosure pending_flush_callback_; DISALLOW_COPY_AND_ASSIGN(ControlMessageProxy); }; diff --git a/chromium/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc b/chromium/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc index 6f119e4c1d6..4e6108d3373 100644 --- a/chromium/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc +++ b/chromium/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc @@ -347,6 +347,10 @@ void InterfaceEndpointClient::FlushForTesting() { control_message_proxy_.FlushForTesting(); } +void InterfaceEndpointClient::FlushAsyncForTesting(base::OnceClosure callback) { + control_message_proxy_.FlushAsyncForTesting(std::move(callback)); +} + void InterfaceEndpointClient::InitControllerIfNecessary() { if (controller_ || handle_.pending_association()) return; diff --git a/chromium/mojo/public/cpp/bindings/lib/interface_ptr_state.h b/chromium/mojo/public/cpp/bindings/lib/interface_ptr_state.h index 2e73564a80c..dcea8661f4f 100644 --- a/chromium/mojo/public/cpp/bindings/lib/interface_ptr_state.h +++ b/chromium/mojo/public/cpp/bindings/lib/interface_ptr_state.h @@ -131,6 +131,11 @@ class InterfacePtrState : public InterfacePtrStateBase { endpoint_client()->FlushForTesting(); } + void FlushAsyncForTesting(base::OnceClosure callback) { + ConfigureProxyIfNecessary(); + endpoint_client()->FlushAsyncForTesting(std::move(callback)); + } + void CloseWithReason(uint32_t custom_reason, const std::string& description) { ConfigureProxyIfNecessary(); endpoint_client()->CloseWithReason(custom_reason, description); diff --git a/chromium/mojo/public/cpp/bindings/lib/message.cc b/chromium/mojo/public/cpp/bindings/lib/message.cc index 44ebf653b26..8972d9efd1d 100644 --- a/chromium/mojo/public/cpp/bindings/lib/message.cc +++ b/chromium/mojo/public/cpp/bindings/lib/message.cc @@ -185,6 +185,10 @@ Message::Message(Message&& other) serialized_(other.serialized_) { other.transferable_ = false; other.serialized_ = false; +#if defined(ENABLE_IPC_FUZZER) + interface_name_ = other.interface_name_; + method_name_ = other.method_name_; +#endif } Message::Message(std::unique_ptr<internal::UnserializedMessageContext> context) @@ -262,6 +266,10 @@ Message& Message::operator=(Message&& other) { other.transferable_ = false; serialized_ = other.serialized_; other.serialized_ = false; +#if defined(ENABLE_IPC_FUZZER) + interface_name_ = other.interface_name_; + method_name_ = other.method_name_; +#endif return *this; } diff --git a/chromium/mojo/public/cpp/bindings/lib/message_dumper.cc b/chromium/mojo/public/cpp/bindings/lib/message_dumper.cc new file mode 100644 index 00000000000..f187e4569b8 --- /dev/null +++ b/chromium/mojo/public/cpp/bindings/lib/message_dumper.cc @@ -0,0 +1,96 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/public/cpp/bindings/message_dumper.h" + +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/no_destructor.h" +#include "base/process/process.h" +#include "base/rand_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/task_scheduler/post_task.h" +#include "mojo/public/cpp/bindings/message.h" + +namespace { + +base::FilePath& DumpDirectory() { + static base::NoDestructor<base::FilePath> dump_directory; + return *dump_directory; +} + +void WriteMessage(uint32_t identifier, + const mojo::MessageDumper::MessageEntry& entry) { + static uint64_t num = 0; + + if (!entry.interface_name) + return; + + base::FilePath message_directory = + DumpDirectory() + .AppendASCII(entry.interface_name) + .AppendASCII(base::NumberToString(identifier)); + + if (!base::DirectoryExists(message_directory) && + !base::CreateDirectory(message_directory)) { + LOG(ERROR) << "Failed to create" << message_directory.value(); + return; + } + + std::string filename = + base::NumberToString(num++) + "." + entry.method_name + ".mojomsg"; + base::FilePath path = message_directory.AppendASCII(filename); + base::File file(path, + base::File::FLAG_WRITE | base::File::FLAG_CREATE_ALWAYS); + + file.WriteAtCurrentPos(reinterpret_cast<const char*>(entry.data_bytes.data()), + static_cast<int>(entry.data_bytes.size())); +} + +} // namespace + +namespace mojo { + +MessageDumper::MessageEntry::MessageEntry(const uint8_t* data, + uint32_t data_size, + const char* interface_name, + const char* method_name) + : interface_name(interface_name), + method_name(method_name), + data_bytes(data, data + data_size) {} + +MessageDumper::MessageEntry::MessageEntry(const MessageEntry& entry) = default; + +MessageDumper::MessageEntry::~MessageEntry() {} + +MessageDumper::MessageDumper() : identifier_(base::RandUint64()) {} + +MessageDumper::~MessageDumper() {} + +bool MessageDumper::Accept(mojo::Message* message) { + MessageEntry entry(message->data(), message->data_num_bytes(), + message->interface_name(), message->method_name()); + + static base::NoDestructor<scoped_refptr<base::TaskRunner>> task_runner( + base::CreateSequencedTaskRunnerWithTraits( + {base::MayBlock(), base::TaskPriority::USER_BLOCKING, + base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})); + + (*task_runner) + ->PostTask(FROM_HERE, + base::BindOnce(&WriteMessage, identifier_, std::move(entry))); + return true; +} + +void MessageDumper::SetMessageDumpDirectory(const base::FilePath& directory) { + DumpDirectory() = directory; +} + +const base::FilePath& MessageDumper::GetMessageDumpDirectory() { + return DumpDirectory(); +} + +} // namespace mojo diff --git a/chromium/mojo/public/cpp/bindings/lib/multiplex_router.cc b/chromium/mojo/public/cpp/bindings/lib/multiplex_router.cc index 61833097ef3..6a1c58bce6c 100644 --- a/chromium/mojo/public/cpp/bindings/lib/multiplex_router.cc +++ b/chromium/mojo/public/cpp/bindings/lib/multiplex_router.cc @@ -338,8 +338,9 @@ MultiplexRouter::MultiplexRouter( connector_.AllowWokenUpBySyncWatchOnSameThread(); } connector_.set_incoming_receiver(&filters_); - connector_.set_connection_error_handler(base::Bind( - &MultiplexRouter::OnPipeConnectionError, base::Unretained(this))); + connector_.set_connection_error_handler( + base::BindOnce(&MultiplexRouter::OnPipeConnectionError, + base::Unretained(this), false /* force_async_dispatch */)); std::unique_ptr<MessageHeaderValidator> header_validator = std::make_unique<MessageHeaderValidator>(); @@ -491,7 +492,7 @@ void MultiplexRouter::RaiseError() { connector_.RaiseError(); } else { task_runner_->PostTask(FROM_HERE, - base::Bind(&MultiplexRouter::RaiseError, this)); + base::BindOnce(&MultiplexRouter::RaiseError, this)); } } @@ -506,7 +507,7 @@ void MultiplexRouter::CloseMessagePipe() { // CloseMessagePipe() above won't trigger connection error handler. // Explicitly call OnPipeConnectionError() so that associated endpoints will // get notified. - OnPipeConnectionError(); + OnPipeConnectionError(true /* force_async_dispatch */); } void MultiplexRouter::PauseIncomingMethodCallProcessing() { @@ -641,7 +642,7 @@ bool MultiplexRouter::OnPeerAssociatedEndpointClosed( return true; } -void MultiplexRouter::OnPipeConnectionError() { +void MultiplexRouter::OnPipeConnectionError(bool force_async_dispatch) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); scoped_refptr<MultiplexRouter> protector(this); @@ -664,10 +665,13 @@ void MultiplexRouter::OnPipeConnectionError() { UpdateEndpointStateMayRemove(endpoint.get(), PEER_ENDPOINT_CLOSED); } - ProcessTasks(connector_.during_sync_handle_watcher_callback() - ? ALLOW_DIRECT_CLIENT_CALLS_FOR_SYNC_MESSAGES - : ALLOW_DIRECT_CLIENT_CALLS, - connector_.task_runner()); + ClientCallBehavior call_behavior = ALLOW_DIRECT_CLIENT_CALLS; + if (force_async_dispatch) + call_behavior = NO_DIRECT_CLIENT_CALLS; + else if (connector_.during_sync_handle_watcher_callback()) + call_behavior = ALLOW_DIRECT_CLIENT_CALLS_FOR_SYNC_MESSAGES; + + ProcessTasks(call_behavior, connector_.task_runner()); } void MultiplexRouter::ProcessTasks( @@ -879,7 +883,8 @@ void MultiplexRouter::MaybePostToProcessTasks( posted_to_process_tasks_ = true; posted_to_task_runner_ = task_runner; task_runner->PostTask( - FROM_HERE, base::Bind(&MultiplexRouter::LockAndCallProcessTasks, this)); + FROM_HERE, + base::BindOnce(&MultiplexRouter::LockAndCallProcessTasks, this)); } void MultiplexRouter::LockAndCallProcessTasks() { diff --git a/chromium/mojo/public/cpp/bindings/lib/multiplex_router.h b/chromium/mojo/public/cpp/bindings/lib/multiplex_router.h index 8c2e7c8b0fc..155e1fe39ab 100644 --- a/chromium/mojo/public/cpp/bindings/lib/multiplex_router.h +++ b/chromium/mojo/public/cpp/bindings/lib/multiplex_router.h @@ -173,7 +173,7 @@ class MOJO_CPP_BINDINGS_EXPORT MultiplexRouter InterfaceId id, const base::Optional<DisconnectReason>& reason) override; - void OnPipeConnectionError(); + void OnPipeConnectionError(bool force_async_dispatch); // Specifies whether we are allowed to directly call into // InterfaceEndpointClient (given that we are already on the same sequence as diff --git a/chromium/mojo/public/cpp/bindings/lib/unserialized_message_context.cc b/chromium/mojo/public/cpp/bindings/lib/unserialized_message_context.cc index 855db4c3cb3..b029f4ef006 100644 --- a/chromium/mojo/public/cpp/bindings/lib/unserialized_message_context.cc +++ b/chromium/mojo/public/cpp/bindings/lib/unserialized_message_context.cc @@ -15,6 +15,7 @@ UnserializedMessageContext::UnserializedMessageContext(const Tag* tag, header_.version = 1; header_.name = message_name; header_.flags = message_flags; + header_.num_bytes = sizeof(header_); } UnserializedMessageContext::~UnserializedMessageContext() = default; diff --git a/chromium/mojo/public/cpp/bindings/message.h b/chromium/mojo/public/cpp/bindings/message.h index 4ad1c04c187..7f6e3ea436c 100644 --- a/chromium/mojo/public/cpp/bindings/message.h +++ b/chromium/mojo/public/cpp/bindings/message.h @@ -211,6 +211,16 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) Message { generic_context.release()->template SafeCast<MessageType>()); } +#if defined(ENABLE_IPC_FUZZER) + const char* interface_name() const { return interface_name_; } + void set_interface_name(const char* interface_name) { + interface_name_ = interface_name; + } + + const char* method_name() const { return method_name_; } + void set_method_name(const char* method_name) { method_name_ = method_name; } +#endif + private: ScopedMessageHandle handle_; @@ -230,6 +240,11 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) Message { // Indicates whether this Message object is serialized. bool serialized_ = false; +#if defined(ENABLE_IPC_FUZZER) + const char* interface_name_ = nullptr; + const char* method_name_ = nullptr; +#endif + DISALLOW_COPY_AND_ASSIGN(Message); }; diff --git a/chromium/mojo/public/cpp/bindings/message_dumper.h b/chromium/mojo/public/cpp/bindings/message_dumper.h new file mode 100644 index 00000000000..44cf384ab0d --- /dev/null +++ b/chromium/mojo/public/cpp/bindings/message_dumper.h @@ -0,0 +1,43 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_DUMPER_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_DUMPER_H_ + +#include "base/files/file_path.h" +#include "mojo/public/cpp/bindings/message.h" +#include "mojo/public/cpp/bindings/message_dumper.h" + +namespace mojo { + +class MessageDumper : public mojo::MessageReceiver { + public: + MessageDumper(); + ~MessageDumper() override; + + bool Accept(mojo::Message* message) override; + + struct MessageEntry { + MessageEntry(const uint8_t* data, + uint32_t data_size, + const char* interface_name, + const char* method_name); + MessageEntry(const MessageEntry& entry); + ~MessageEntry(); + + const char* interface_name; + const char* method_name; + std::vector<uint8_t> data_bytes; + }; + + static void SetMessageDumpDirectory(const base::FilePath& directory); + static const base::FilePath& GetMessageDumpDirectory(); + + private: + uint32_t identifier_; +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_DUMPER_H_ diff --git a/chromium/mojo/public/cpp/bindings/sync_call_restrictions.h b/chromium/mojo/public/cpp/bindings/sync_call_restrictions.h index 225782d96d7..e72cb1d3cb1 100644 --- a/chromium/mojo/public/cpp/bindings/sync_call_restrictions.h +++ b/chromium/mojo/public/cpp/bindings/sync_call_restrictions.h @@ -37,9 +37,10 @@ namespace prefs { class PersistentPrefStoreClient; } -namespace views { -class ClipboardMus; -} +namespace ui { +class ClipboardClient; +class HostContextFactoryPrivate; +} // namespace ui namespace viz { class HostFrameSinkManager; @@ -94,18 +95,19 @@ class MOJO_CPP_BINDINGS_EXPORT SyncCallRestrictions { friend class mojo::ScopedAllowSyncCallForTesting; // For file open and save dialogs created synchronously. friend class ::ChromeSelectFileDialogFactory; + // For synchronous system clipboard access. + friend class ui::ClipboardClient; // For destroying the GL context/surface that draw to a platform window before // the platform window is destroyed. friend class viz::HostFrameSinkManager; // Allow for layout test pixel dumps. friend class content::BlinkTestController; + // For preventing frame swaps of wrong size during resize on Windows. + // (https://crbug.com/811945) + friend class ui::HostContextFactoryPrivate; // END ALLOWED USAGE. // BEGIN USAGE THAT NEEDS TO BE FIXED. - // In the non-mus case, we called blocking OS functions in the ui::Clipboard - // implementation which weren't caught by sync call restrictions. Our blocking - // calls to mus, however, are. - friend class views::ClipboardMus; // In ash::Shell::Init() it assumes that NativeDisplayDelegate will be // synchronous at first. In mushrome ForwardingDisplayDelegate uses a // synchronous call to get the display snapshots as a workaround. diff --git a/chromium/mojo/public/cpp/bindings/tests/BUILD.gn b/chromium/mojo/public/cpp/bindings/tests/BUILD.gn index b54000d3212..82038a186d9 100644 --- a/chromium/mojo/public/cpp/bindings/tests/BUILD.gn +++ b/chromium/mojo/public/cpp/bindings/tests/BUILD.gn @@ -50,7 +50,7 @@ source_set("tests") { deps = [ ":mojo_public_bindings_test_utils", "//base/test:test_support", - "//mojo/edk", + "//mojo/core/embedder", "//mojo/public/cpp/bindings", "//mojo/public/cpp/system", "//mojo/public/cpp/test_support:test_utils", @@ -134,8 +134,8 @@ source_set("perftests") { deps = [ "//base/test:test_support", - "//mojo/edk", - "//mojo/edk/test:test_support", + "//mojo/core/embedder", + "//mojo/core/test:test_support", "//mojo/public/cpp/bindings", "//mojo/public/cpp/system", "//mojo/public/cpp/test_support:test_utils", diff --git a/chromium/mojo/public/cpp/platform/BUILD.gn b/chromium/mojo/public/cpp/platform/BUILD.gn index 3aea9dea5e7..b0aa90ef371 100644 --- a/chromium/mojo/public/cpp/platform/BUILD.gn +++ b/chromium/mojo/public/cpp/platform/BUILD.gn @@ -2,6 +2,8 @@ # 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") + component("platform") { output_name = "mojo_cpp_platform" @@ -22,8 +24,14 @@ component("platform") { "platform_handle.cc", ] + if (is_posix && (!is_nacl || is_nacl_nonsfi)) { + public += [ "socket_utils_posix.h" ] + sources += [ "socket_utils_posix.cc" ] + } + public_deps = [ "//base", + "//mojo/public/c/system:headers", ] if (is_posix && (!is_nacl && !is_fuchsia)) { @@ -32,7 +40,10 @@ component("platform") { if (is_fuchsia) { sources += [ "named_platform_channel_fuchsia.cc" ] - public_deps += [ "//third_party/fuchsia-sdk:fdio" ] + public_deps += [ + "//third_party/fuchsia-sdk:fdio", + "//third_party/fuchsia-sdk:zx", + ] } defines = [ "IS_MOJO_CPP_PLATFORM_IMPL" ] diff --git a/chromium/mojo/public/cpp/platform/DEPS b/chromium/mojo/public/cpp/platform/DEPS index aa2ee21388d..375d67bbe6e 100644 --- a/chromium/mojo/public/cpp/platform/DEPS +++ b/chromium/mojo/public/cpp/platform/DEPS @@ -2,6 +2,7 @@ include_rules = [ # Mojo platform support must not depend on any other part of the Mojo public # library. "-mojo", + "+mojo/public/c/system", "+mojo/public/cpp/platform", # For some syscalls when building in NaCl toolchains. diff --git a/chromium/mojo/public/cpp/platform/README.md b/chromium/mojo/public/cpp/platform/README.md new file mode 100644 index 00000000000..c91e85fa7fa --- /dev/null +++ b/chromium/mojo/public/cpp/platform/README.md @@ -0,0 +1,75 @@ +# Mojo C++ Platform API +This document is a subset of the [Mojo documentation](/mojo/README.md). + +[TOC] + +## Overview +The Mojo C++ Platform API provides a lightweight set of abstractions around +stable platform primitive APIs like UNIX domain sockets and Windows named pipes. +This API is primarily useful in conjunction with Mojo +[Invitations](/mojo/public/cpp/system/README.md#Invitations) to bootstrap Mojo +IPC between two processes. + +## Platform Handles +The `PlatformHandle` type provides a move-only wrapper around an owned, +platform-specific primitive handle types. The type of primitive it holds can be +any of the following: + + * Windows HANDLE (Windows only) + * Fuchsia zx_handle_t (Fuchsia only) + * Mach send right (OSX only) + * POSIX file descriptor (POSIX systems only) + +See the +[header](https://cs.chromium.org/src/mojo/public/cpp/platform/platform_handle.h) +for more details. + +## Platform Channels +The `PlatformChannel` type abstracts a platform-specific IPC FIFO primitive +primarily for use with the Mojo +[Invitations](/mojo/public/cpp/system/README.md#Invitations) API. Constructing +a `PlatformChannel` instance creates the underlying system primitive with two +transferrable `PlatformHandle` instances, each thinly wrapped as a +`PlatformChannelEndpoint` for additional type-safety. One endpoint is designated +as **local** and the other **remote**, the intention being that the remote +endpoint will be transferred to another process in the system. + +See the +[header](https://cs.chromium.org/src/mojo/public/cpp/platform/platform_channel.h) +for more details. See the +[Invitations](/mojo/public/cpp/system/README.md#Invitations) documentation for +an example of using `PlatformChannel` with an invitation to bootstrap IPC +between a process and one of its newly launched child processes. + +## Named Platform Channels +For cases where it is not feasible to transfer a `PlatformHandle` from one +running process to another, the Platform API also provides +`NamedPlatformChannel`, which abstracts a named system resource that can +facilitate communication similarly to `PlatformChannel`. + +A `NamedPlatformChannel` upon construction will begin listening on +platform-specific primitive (a named pipe server on Windows, a domain socket +server on POSIX, *etc.*). The globally reachable name of the server (*e.g.* the +socket path) can be specified at construction time via +`NamedPlatformChannel::Options::server_name`, but if no name is given, a +suitably random one is generated and used. + +``` cpp +// In one process +mojo::NamedPlatformChannel::Options options; +mojo::NamedPlatformChannel named_channel(options); +OutgoingInvitation::Send(std::move(invitation), + named_channel.TakeServerEndpoint()); +SendServerNameToRemoteProcessSomehow(named_channel.GetServerName()); + +// In the other process +void OnGotServerName(const mojo::NamedPlatformChannel::ServerName& name) { + // Connect to the server. + mojo::PlatformChannelEndpoint endpoint = + mojo::NamedPlatformChannel::ConnectToServer(name); + + // Proceed normally with invitation acceptance. + auto invitation = mojo::IncomingInvitation::Accept(std::move(endpoint)); + // ... +} +``` diff --git a/chromium/mojo/public/cpp/platform/named_platform_channel.cc b/chromium/mojo/public/cpp/platform/named_platform_channel.cc index e0d12178128..4439710df97 100644 --- a/chromium/mojo/public/cpp/platform/named_platform_channel.cc +++ b/chromium/mojo/public/cpp/platform/named_platform_channel.cc @@ -5,6 +5,7 @@ #include "mojo/public/cpp/platform/named_platform_channel.h" #include "base/logging.h" +#include "base/strings/utf_string_conversions.h" namespace mojo { @@ -16,8 +17,24 @@ NamedPlatformChannel::NamedPlatformChannel(const Options& options) { CreateServerEndpoint(options, &server_name_)); } +NamedPlatformChannel::NamedPlatformChannel(NamedPlatformChannel&& other) = + default; + NamedPlatformChannel::~NamedPlatformChannel() = default; +NamedPlatformChannel& NamedPlatformChannel::operator=( + NamedPlatformChannel&& other) = default; + +// static +NamedPlatformChannel::ServerName NamedPlatformChannel::ServerNameFromUTF8( + base::StringPiece name) { +#if defined(OS_WIN) + return base::UTF8ToUTF16(name); +#else + return name.as_string(); +#endif +} + void NamedPlatformChannel::PassServerNameOnCommandLine( base::CommandLine* command_line) { command_line->AppendSwitchNative(kNamedHandleSwitch, server_name_); diff --git a/chromium/mojo/public/cpp/platform/named_platform_channel.h b/chromium/mojo/public/cpp/platform/named_platform_channel.h index 4cc32a7d9c0..177136e4e5d 100644 --- a/chromium/mojo/public/cpp/platform/named_platform_channel.h +++ b/chromium/mojo/public/cpp/platform/named_platform_channel.h @@ -64,12 +64,18 @@ class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) NamedPlatformChannel { }; NamedPlatformChannel(const Options& options); + NamedPlatformChannel(NamedPlatformChannel&& other); ~NamedPlatformChannel(); + NamedPlatformChannel& operator=(NamedPlatformChannel&& other); + const PlatformChannelServerEndpoint& server_endpoint() const { return server_endpoint_; } + // Helper to create a ServerName from a UTF8 string regardless of platform. + static ServerName ServerNameFromUTF8(base::StringPiece name); + // Passes the local server endpoint for the channel. On Windows, this is a // named pipe server; on POSIX it's a bound, listening domain socket. In each // case it should accept a single new connection. diff --git a/chromium/mojo/public/cpp/platform/named_platform_channel_win.cc b/chromium/mojo/public/cpp/platform/named_platform_channel_win.cc index 0507efe5abb..9c329bd6d81 100644 --- a/chromium/mojo/public/cpp/platform/named_platform_channel_win.cc +++ b/chromium/mojo/public/cpp/platform/named_platform_channel_win.cc @@ -5,6 +5,7 @@ #include "mojo/public/cpp/platform/named_platform_channel.h" #include <windows.h> +#include <memory> // NOTE: This needs to be included *after* windows.h. #include <sddl.h> diff --git a/chromium/mojo/public/cpp/platform/platform_channel.cc b/chromium/mojo/public/cpp/platform/platform_channel.cc index f707b7c2195..a82910d06a0 100644 --- a/chromium/mojo/public/cpp/platform/platform_channel.cc +++ b/chromium/mojo/public/cpp/platform/platform_channel.cc @@ -21,11 +21,11 @@ #include "base/win/scoped_handle.h" #elif defined(OS_FUCHSIA) +#include <lib/zx/channel.h> #include <zircon/process.h> #include <zircon/processargs.h> -#include <zircon/syscalls.h> -#include "base/fuchsia/scoped_zx_handle.h" +#include "base/fuchsia/fuchsia_logging.h" #elif defined(OS_POSIX) #include <fcntl.h> #include <sys/types.h> @@ -66,7 +66,8 @@ void CreateChannel(PlatformHandle* local_endpoint, 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; + DWORD kFlags = + SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS | FILE_FLAG_OVERLAPPED; // Allow the handle to be inherited by child processes. SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES), nullptr, TRUE}; @@ -83,12 +84,12 @@ void CreateChannel(PlatformHandle* local_endpoint, #elif defined(OS_FUCHSIA) void CreateChannel(PlatformHandle* local_endpoint, PlatformHandle* remote_endpoint) { - zx_handle_t handles[2] = {}; - zx_status_t result = zx_channel_create(0, &handles[0], &handles[1]); - CHECK_EQ(ZX_OK, result); + zx::channel handles[2]; + zx_status_t result = zx::channel::create(0, &handles[0], &handles[1]); + ZX_CHECK(result == ZX_OK, result); - *local_endpoint = PlatformHandle(base::ScopedZxHandle(handles[0])); - *remote_endpoint = PlatformHandle(base::ScopedZxHandle(handles[1])); + *local_endpoint = PlatformHandle(std::move(handles[0])); + *remote_endpoint = PlatformHandle(std::move(handles[1])); DCHECK(local_endpoint->is_valid()); DCHECK(remote_endpoint->is_valid()); } @@ -173,13 +174,14 @@ void PlatformChannel::PrepareToPassRemoteEndpoint(HandlePassingInfo* info, *value = base::NumberToString( HandleToLong(remote_endpoint_.platform_handle().GetHandle().Get())); #elif defined(OS_FUCHSIA) - const uint32_t id = PA_HND(PA_USER0, 0); + const uint32_t id = PA_HND(PA_USER0, info->size()); info->push_back({id, remote_endpoint_.platform_handle().GetHandle().get()}); *value = base::NumberToString(id); #elif defined(OS_ANDROID) int fd = remote_endpoint_.platform_handle().GetFD().get(); - info->emplace_back(fd, kAndroidClientHandleDescriptor); - *value = base::NumberToString(kAndroidClientHandleDescriptor); + int mapped_fd = kAndroidClientHandleDescriptor + info->size(); + info->emplace_back(fd, mapped_fd); + *value = base::NumberToString(mapped_fd); #elif defined(OS_POSIX) // Arbitrary sanity check to ensure the loop below terminates reasonably // quickly. @@ -206,10 +208,25 @@ void PlatformChannel::PrepareToPassRemoteEndpoint( command_line->AppendSwitchASCII(kHandleSwitch, value); } -void PlatformChannel::RemoteProcessLaunched() { +void PlatformChannel::PrepareToPassRemoteEndpoint( + base::LaunchOptions* options, + base::CommandLine* command_line) { +#if defined(OS_WIN) + PrepareToPassRemoteEndpoint(&options->handles_to_inherit, command_line); +#elif defined(OS_FUCHSIA) + PrepareToPassRemoteEndpoint(&options->handles_to_transfer, command_line); +#elif defined(OS_POSIX) + PrepareToPassRemoteEndpoint(&options->fds_to_remap, command_line); +#else +#error "Platform not supported." +#endif +} + +void PlatformChannel::RemoteProcessLaunchAttempted() { #if defined(OS_FUCHSIA) - // Unlike other platforms, Fuchsia just transfers handle ownership to the - // launcher process rather than duplicating it. + // Unlike other platforms, Fuchsia transfers handle ownership to the new + // process, rather than duplicating it. For consistency the process-launch + // call will have consumed the handle regardless of whether launch succeeded. DCHECK(remote_endpoint_.platform_handle().is_valid_handle()); ignore_result(remote_endpoint_.TakePlatformHandle().ReleaseHandle()); #else @@ -234,8 +251,8 @@ PlatformChannelEndpoint PlatformChannel::RecoverPassedEndpointFromString( DLOG(ERROR) << "Invalid PlatformChannel endpoint string."; return PlatformChannelEndpoint(); } - return PlatformChannelEndpoint(PlatformHandle(base::ScopedZxHandle( - zx_get_startup_handle(base::checked_cast<uint32_t>(handle_value))))); + return PlatformChannelEndpoint(PlatformHandle(zx::handle( + zx_take_startup_handle(base::checked_cast<uint32_t>(handle_value))))); #elif defined(OS_ANDROID) base::GlobalDescriptors::Key key = -1; if (value.empty() || !base::StringToUint(value, &key)) { diff --git a/chromium/mojo/public/cpp/platform/platform_channel.h b/chromium/mojo/public/cpp/platform/platform_channel.h index 149c6b10a44..534c6bbfdc2 100644 --- a/chromium/mojo/public/cpp/platform/platform_channel.h +++ b/chromium/mojo/public/cpp/platform/platform_channel.h @@ -74,8 +74,9 @@ class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) PlatformChannel { // passed on the new process's command line. // // **NOTE**: If this method is called it is important to also call - // |RemoteProcessLaunched()| on this PlatformChanenl *after* the process has - // launched. Failing to do so can result in leaked handles. + // |RemoteProcessLaunchAttempted()| on this PlatformChannel *after* attempting + // to launch the new process, regardless of whether the attempt succeeded. + // Failing to do so can result in leaked handles. void PrepareToPassRemoteEndpoint(HandlePassingInfo* info, std::string* value); // Like above but modifies |*command_line| to include the endpoint string @@ -83,11 +84,17 @@ class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) PlatformChannel { void PrepareToPassRemoteEndpoint(HandlePassingInfo* info, base::CommandLine* command_line); - // Must be called after the corresponding process launch if + // Like above but adds handle-passing information directly to + // |*launch_options|, eliminating the potential need for callers to write + // platform-specific code to do the same. + void PrepareToPassRemoteEndpoint(base::LaunchOptions* options, + base::CommandLine* command_line); + + // Must be called after the corresponding process launch attempt if // |PrepareToPassRemoteEndpoint()| was used. - void RemoteProcessLaunched(); + void RemoteProcessLaunchAttempted(); - // Recovers and endpoint handle which was passed to the calling process by + // Recovers an endpoint handle which was passed to the calling process by // its creator. |value| is a string returned by // |PrepareToPassRemoteEndpoint()| in the creator's process. static PlatformChannelEndpoint RecoverPassedEndpointFromString( diff --git a/chromium/mojo/public/cpp/platform/platform_handle.cc b/chromium/mojo/public/cpp/platform/platform_handle.cc index 14140717143..106fe48e4cd 100644 --- a/chromium/mojo/public/cpp/platform/platform_handle.cc +++ b/chromium/mojo/public/cpp/platform/platform_handle.cc @@ -9,19 +9,25 @@ #if defined(OS_WIN) #include <windows.h> + +#include "base/win/scoped_handle.h" #elif defined(OS_FUCHSIA) -#include <fdio/limits.h> +#include <lib/fdio/limits.h> #include <unistd.h> #include <zircon/status.h> -#include <zircon/syscalls.h> + +#include "base/fuchsia/fuchsia_logging.h" #elif defined(OS_MACOSX) && !defined(OS_IOS) #include <mach/mach_vm.h> #include "base/mac/mach_logging.h" +#include "base/mac/scoped_mach_port.h" #endif #if defined(OS_POSIX) #include <unistd.h> + +#include "base/files/scoped_file.h" #endif namespace mojo { @@ -42,14 +48,14 @@ base::win::ScopedHandle CloneHandle(const base::win::ScopedHandle& handle) { return base::win::ScopedHandle(dupe); } #elif defined(OS_FUCHSIA) -base::ScopedZxHandle CloneHandle(const base::ScopedZxHandle& handle) { +zx::handle CloneHandle(const zx::handle& handle) { DCHECK(handle.is_valid()); - zx_handle_t dupe; - zx_status_t result = - zx_handle_duplicate(handle.get(), ZX_RIGHT_SAME_RIGHTS, &dupe); - DLOG_IF(ERROR, result != ZX_OK) << "zx_duplicate_handle failed: " << result; - return base::ScopedZxHandle(dupe); + zx::handle dupe; + zx_status_t result = handle.duplicate(ZX_RIGHT_SAME_RIGHTS, &dupe); + if (result != ZX_OK) + ZX_DLOG(ERROR, result) << "zx_duplicate_handle"; + return std::move(dupe); } #elif defined(OS_MACOSX) && !defined(OS_IOS) base::mac::ScopedMachSendRight CloneMachPort( @@ -77,21 +83,24 @@ base::ScopedFD CloneFD(const base::ScopedFD& fd) { PlatformHandle::PlatformHandle() = default; -PlatformHandle::PlatformHandle(PlatformHandle&& other) = default; +PlatformHandle::PlatformHandle(PlatformHandle&& other) { + *this = std::move(other); +} #if defined(OS_WIN) PlatformHandle::PlatformHandle(base::win::ScopedHandle handle) - : handle_(std::move(handle)) {} + : type_(Type::kHandle), handle_(std::move(handle)) {} #elif defined(OS_FUCHSIA) -PlatformHandle::PlatformHandle(base::ScopedZxHandle handle) - : handle_(std::move(handle)) {} +PlatformHandle::PlatformHandle(zx::handle handle) + : type_(Type::kHandle), handle_(std::move(handle)) {} #elif defined(OS_MACOSX) && !defined(OS_IOS) PlatformHandle::PlatformHandle(base::mac::ScopedMachSendRight mach_port) - : mach_port_(std::move(mach_port)) {} + : type_(Type::kMachPort), mach_port_(std::move(mach_port)) {} #endif #if defined(OS_POSIX) || defined(OS_FUCHSIA) -PlatformHandle::PlatformHandle(base::ScopedFD fd) : fd_(std::move(fd)) { +PlatformHandle::PlatformHandle(base::ScopedFD fd) + : type_(Type::kFd), fd_(std::move(fd)) { #if defined(OS_FUCHSIA) DCHECK_LT(fd_.get(), FDIO_MAX_FD); #endif @@ -100,9 +109,101 @@ PlatformHandle::PlatformHandle(base::ScopedFD fd) : fd_(std::move(fd)) { PlatformHandle::~PlatformHandle() = default; -PlatformHandle& PlatformHandle::operator=(PlatformHandle&& other) = default; +PlatformHandle& PlatformHandle::operator=(PlatformHandle&& other) { + type_ = other.type_; + other.type_ = Type::kNone; + +#if defined(OS_WIN) + handle_ = std::move(other.handle_); +#elif defined(OS_FUCHSIA) + handle_ = std::move(other.handle_); +#elif defined(OS_MACOSX) && !defined(OS_IOS) + mach_port_ = std::move(other.mach_port_); +#endif + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) + fd_ = std::move(other.fd_); +#endif + + return *this; +} + +// static +void PlatformHandle::ToMojoPlatformHandle(PlatformHandle handle, + MojoPlatformHandle* out_handle) { + DCHECK(out_handle); + out_handle->struct_size = sizeof(MojoPlatformHandle); + if (handle.type_ == Type::kNone) { + out_handle->type = MOJO_PLATFORM_HANDLE_TYPE_INVALID; + out_handle->value = 0; + return; + } + + do { +#if defined(OS_WIN) + out_handle->type = MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE; + out_handle->value = + static_cast<uint64_t>(HandleToLong(handle.TakeHandle().Take())); + break; +#elif defined(OS_FUCHSIA) + if (handle.is_handle()) { + out_handle->type = MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE; + out_handle->value = handle.TakeHandle().release(); + break; + } +#elif defined(OS_MACOSX) && !defined(OS_IOS) + if (handle.is_mach_port()) { + out_handle->type = MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT; + out_handle->value = + static_cast<uint64_t>(handle.TakeMachPort().release()); + break; + } +#endif + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) + DCHECK(handle.is_fd()); + out_handle->type = MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR; + out_handle->value = static_cast<uint64_t>(handle.TakeFD().release()); +#endif + } while (false); + + // One of the above cases must take ownership of |handle|. + DCHECK(!handle.is_valid()); +} + +// static +PlatformHandle PlatformHandle::FromMojoPlatformHandle( + const MojoPlatformHandle* handle) { + if (handle->struct_size < sizeof(*handle) || + handle->type == MOJO_PLATFORM_HANDLE_TYPE_INVALID) { + return PlatformHandle(); + } + +#if defined(OS_WIN) + if (handle->type != MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE) + return PlatformHandle(); + return PlatformHandle( + base::win::ScopedHandle(LongToHandle(static_cast<long>(handle->value)))); +#elif defined(OS_FUCHSIA) + if (handle->type == MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE) + return PlatformHandle(zx::handle(handle->value)); +#elif defined(OS_MACOSX) && !defined(OS_IOS) + if (handle->type == MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT) { + return PlatformHandle(base::mac::ScopedMachSendRight( + static_cast<mach_port_t>(handle->value))); + } +#endif + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) + if (handle->type != MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR) + return PlatformHandle(); + return PlatformHandle(base::ScopedFD(static_cast<int>(handle->value))); +#endif +} void PlatformHandle::reset() { + type_ = Type::kNone; + #if defined(OS_WIN) handle_.Close(); #elif defined(OS_FUCHSIA) @@ -116,6 +217,22 @@ void PlatformHandle::reset() { #endif } +void PlatformHandle::release() { + type_ = Type::kNone; + +#if defined(OS_WIN) + ignore_result(handle_.Take()); +#elif defined(OS_FUCHSIA) + ignore_result(handle_.release()); +#elif defined(OS_MACOSX) && !defined(OS_IOS) + ignore_result(mach_port_.release()); +#endif + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) + ignore_result(fd_.release()); +#endif +} + PlatformHandle PlatformHandle::Clone() const { #if defined(OS_WIN) return PlatformHandle(CloneHandle(handle_)); diff --git a/chromium/mojo/public/cpp/platform/platform_handle.h b/chromium/mojo/public/cpp/platform/platform_handle.h index c9608fab333..22ea774f56a 100644 --- a/chromium/mojo/public/cpp/platform/platform_handle.h +++ b/chromium/mojo/public/cpp/platform/platform_handle.h @@ -9,11 +9,12 @@ #include "base/logging.h" #include "base/macros.h" #include "build/build_config.h" +#include "mojo/public/c/system/platform_handle.h" #if defined(OS_WIN) #include "base/win/scoped_handle.h" #elif defined(OS_FUCHSIA) -#include "base/fuchsia/scoped_zx_handle.h" +#include <lib/zx/handle.h> #elif defined(OS_MACOSX) && !defined(OS_IOS) #include "base/mac/scoped_mach_port.h" #endif @@ -40,13 +41,25 @@ namespace mojo { // NOTE: This assumes ownership if the handle it represents. class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) PlatformHandle { public: + enum class Type { + kNone, +#if defined(OS_WIN) || defined(OS_FUCHSIA) + kHandle, +#elif defined(OS_MACOSX) && !defined(OS_IOS) + kMachPort, +#endif +#if defined(OS_POSIX) || defined(OS_FUCHSIA) + kFd, +#endif + }; + PlatformHandle(); PlatformHandle(PlatformHandle&& other); #if defined(OS_WIN) explicit PlatformHandle(base::win::ScopedHandle handle); #elif defined(OS_FUCHSIA) - explicit PlatformHandle(base::ScopedZxHandle handle); + explicit PlatformHandle(zx::handle handle); #elif defined(OS_MACOSX) && !defined(OS_IOS) explicit PlatformHandle(base::mac::ScopedMachSendRight mach_port); #endif @@ -59,36 +72,76 @@ class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) PlatformHandle { PlatformHandle& operator=(PlatformHandle&& other); + // Takes ownership of |handle|'s underlying platform handle and fills in + // |mojo_handle| with a representation of it. The caller assumes ownership of + // the platform handle. + static void ToMojoPlatformHandle(PlatformHandle handle, + MojoPlatformHandle* mojo_handle); + // Closes the underlying platform handle. + // Assumes ownership of the platform handle described by |handle|, and returns + // it as a new PlatformHandle. + static PlatformHandle FromMojoPlatformHandle( + const MojoPlatformHandle* handle); + + Type type() const { return type_; } + void reset(); + // Relinquishes ownership of the underlying handle, regardless of type, and + // discards its value. To release and obtain the underlying handle value, use + // one of the specific |Release*()| methods below. + void release(); + // Duplicates the underlying platform handle, returning a new PlatformHandle // which owns it. PlatformHandle Clone() const; #if defined(OS_WIN) - bool is_valid() const { return handle_.IsValid(); } + bool is_valid() const { return is_valid_handle(); } + bool is_valid_handle() const { return handle_.IsValid(); } + bool is_handle() const { return type_ == Type::kHandle; } const base::win::ScopedHandle& GetHandle() const { return handle_; } - base::win::ScopedHandle TakeHandle() { return std::move(handle_); } - HANDLE ReleaseHandle() WARN_UNUSED_RESULT { return handle_.Take(); } + base::win::ScopedHandle TakeHandle() { + DCHECK_EQ(type_, Type::kHandle); + type_ = Type::kNone; + return std::move(handle_); + } + HANDLE ReleaseHandle() WARN_UNUSED_RESULT { + DCHECK_EQ(type_, Type::kHandle); + type_ = Type::kNone; + return handle_.Take(); + } #elif defined(OS_FUCHSIA) bool is_valid() const { return is_valid_fd() || is_valid_handle(); } - bool is_valid_handle() const { return handle_.is_valid(); } - const base::ScopedZxHandle& GetHandle() const { return handle_; } - base::ScopedZxHandle TakeHandle() { return std::move(handle_); } - zx_handle_t ReleaseHandle() WARN_UNUSED_RESULT { return handle_.release(); } + bool is_handle() const { return type_ == Type::kHandle; } + const zx::handle& GetHandle() const { return handle_; } + zx::handle TakeHandle() { + if (type_ == Type::kHandle) + type_ = Type::kNone; + return std::move(handle_); + } + zx_handle_t ReleaseHandle() WARN_UNUSED_RESULT { + if (type_ == Type::kHandle) + type_ = Type::kNone; + return handle_.release(); + } #elif defined(OS_MACOSX) && !defined(OS_IOS) bool is_valid() const { return is_valid_fd() || is_valid_mach_port(); } - bool is_valid_mach_port() const { return mach_port_.is_valid(); } + bool is_mach_port() const { return type_ == Type::kMachPort; } const base::mac::ScopedMachSendRight& GetMachPort() const { return mach_port_; } base::mac::ScopedMachSendRight TakeMachPort() { + if (type_ == Type::kMachPort) + type_ = Type::kNone; return std::move(mach_port_); } mach_port_t ReleaseMachPort() WARN_UNUSED_RESULT { + if (type_ == Type::kMachPort) + type_ = Type::kNone; return mach_port_.release(); } #elif defined(OS_POSIX) @@ -99,16 +152,27 @@ class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) PlatformHandle { #if defined(OS_POSIX) || defined(OS_FUCHSIA) bool is_valid_fd() const { return fd_.is_valid(); } + bool is_fd() const { return type_ == Type::kFd; } const base::ScopedFD& GetFD() const { return fd_; } - base::ScopedFD TakeFD() { return std::move(fd_); } - int ReleaseFD() WARN_UNUSED_RESULT { return fd_.release(); } + base::ScopedFD TakeFD() { + if (type_ == Type::kFd) + type_ = Type::kNone; + return std::move(fd_); + } + int ReleaseFD() WARN_UNUSED_RESULT { + if (type_ == Type::kFd) + type_ = Type::kNone; + return fd_.release(); + } #endif private: + Type type_ = Type::kNone; + #if defined(OS_WIN) base::win::ScopedHandle handle_; #elif defined(OS_FUCHSIA) - base::ScopedZxHandle handle_; + zx::handle handle_; #elif defined(OS_MACOSX) && !defined(OS_IOS) base::mac::ScopedMachSendRight mach_port_; #endif diff --git a/chromium/mojo/public/cpp/platform/socket_utils_posix.cc b/chromium/mojo/public/cpp/platform/socket_utils_posix.cc new file mode 100644 index 00000000000..4bbdcb754bb --- /dev/null +++ b/chromium/mojo/public/cpp/platform/socket_utils_posix.cc @@ -0,0 +1,194 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/public/cpp/platform/socket_utils_posix.h" + +#include <stddef.h> +#include <sys/socket.h> +#include <unistd.h> + +#if !defined(OS_NACL) +#include <sys/uio.h> +#endif + +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "build/build_config.h" + +namespace mojo { + +namespace { + +#if !defined(OS_NACL) +bool IsRecoverableError() { + return errno == ECONNABORTED || errno == EMFILE || errno == ENFILE || + errno == ENOMEM || errno == ENOBUFS; +} + +bool GetPeerEuid(base::PlatformFile fd, uid_t* peer_euid) { +#if defined(OS_MACOSX) || defined(OS_OPENBSD) || defined(OS_FREEBSD) + uid_t socket_euid; + gid_t socket_gid; + if (getpeereid(fd, &socket_euid, &socket_gid) < 0) { + PLOG(ERROR) << "getpeereid " << fd; + return false; + } + *peer_euid = socket_euid; + return true; +#else + struct ucred cred; + socklen_t cred_len = sizeof(cred); + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) < 0) { + PLOG(ERROR) << "getsockopt " << fd; + 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(base::PlatformFile fd) { + uid_t peer_euid; + if (!GetPeerEuid(fd, &peer_euid)) + return false; + if (peer_euid != geteuid()) { + DLOG(ERROR) << "Client euid is not authorized"; + return false; + } + return true; +} +#endif // !defined(OS_NACL) + +// NOTE: On Linux |SIGPIPE| is suppressed by passing |MSG_NOSIGNAL| to +// |sendmsg()|. On Mac we instead set |SO_NOSIGPIPE| on the socket itself. +#if defined(OS_MACOSX) +constexpr int kSendmsgFlags = 0; +#else +constexpr int kSendmsgFlags = MSG_NOSIGNAL; +#endif + +constexpr size_t kMaxSendmsgHandles = 128; + +} // namespace + +ssize_t SocketWrite(base::PlatformFile socket, + const void* bytes, + size_t num_bytes) { +#if defined(OS_MACOSX) || defined(OS_NACL_NONSFI) + return HANDLE_EINTR(write(socket, bytes, num_bytes)); +#else + return send(socket, bytes, num_bytes, kSendmsgFlags); +#endif +} + +ssize_t SocketWritev(base::PlatformFile socket, + struct iovec* iov, + size_t num_iov) { +#if defined(OS_MACOSX) + return HANDLE_EINTR(writev(socket, iov, static_cast<int>(num_iov))); +#else + struct msghdr msg = {}; + msg.msg_iov = iov; + msg.msg_iovlen = num_iov; + return HANDLE_EINTR(sendmsg(socket, &msg, kSendmsgFlags)); +#endif +} + +ssize_t SendmsgWithHandles(base::PlatformFile socket, + struct iovec* iov, + size_t num_iov, + const std::vector<base::ScopedFD>& descriptors) { + DCHECK(iov); + DCHECK_GT(num_iov, 0u); + DCHECK(!descriptors.empty()); + DCHECK_LE(descriptors.size(), kMaxSendmsgHandles); + + char cmsg_buf[CMSG_SPACE(kMaxSendmsgHandles * sizeof(int))]; + struct msghdr msg = {}; + msg.msg_iov = iov; + msg.msg_iovlen = num_iov; + msg.msg_control = cmsg_buf; + msg.msg_controllen = CMSG_LEN(descriptors.size() * sizeof(int)); + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(descriptors.size() * sizeof(int)); + for (size_t i = 0; i < descriptors.size(); ++i) { + DCHECK_GE(descriptors[i].get(), 0); + reinterpret_cast<int*>(CMSG_DATA(cmsg))[i] = descriptors[i].get(); + } + return HANDLE_EINTR(sendmsg(socket, &msg, kSendmsgFlags)); +} + +ssize_t SocketRecvmsg(base::PlatformFile socket, + void* buf, + size_t num_bytes, + std::vector<base::ScopedFD>* descriptors, + bool block) { + struct iovec iov = {buf, num_bytes}; + char cmsg_buf[CMSG_SPACE(kMaxSendmsgHandles * 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(socket, &msg, block ? 0 : MSG_DONTWAIT)); + if (result < 0) + return result; + + if (msg.msg_controllen == 0) + return result; + + DCHECK(!(msg.msg_flags & MSG_CTRUNC)); + + descriptors->clear(); + 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) { + base::ScopedFD fd(fds[i]); + DCHECK(fd.is_valid()); + descriptors->emplace_back(std::move(fd)); + } + } + } + + return result; +} + +bool AcceptSocketConnection(base::PlatformFile server_fd, + base::ScopedFD* connection_fd, + bool check_peer_user) { + DCHECK_GE(server_fd, 0); + connection_fd->reset(); +#if defined(OS_NACL) + NOTREACHED(); + return false; +#else + base::ScopedFD accepted_handle(HANDLE_EINTR(accept(server_fd, nullptr, 0))); + if (!accepted_handle.is_valid()) + return IsRecoverableError(); + if (check_peer_user && !IsPeerAuthorized(accepted_handle.get())) + return true; + if (!base::SetNonBlocking(accepted_handle.get())) { + PLOG(ERROR) << "base::SetNonBlocking() failed " << accepted_handle.get(); + return true; + } + + *connection_fd = std::move(accepted_handle); + return true; +#endif // defined(OS_NACL) +} + +} // namespace mojo diff --git a/chromium/mojo/public/cpp/platform/socket_utils_posix.h b/chromium/mojo/public/cpp/platform/socket_utils_posix.h new file mode 100644 index 00000000000..e512f1bc807 --- /dev/null +++ b/chromium/mojo/public/cpp/platform/socket_utils_posix.h @@ -0,0 +1,80 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_PLATFORM_SOCKET_UTILS_POSIX_H_ +#define MOJO_PUBLIC_CPP_PLATFORM_SOCKET_UTILS_POSIX_H_ + +#include <stddef.h> +#include <sys/types.h> + +#include "base/component_export.h" +#include "base/files/platform_file.h" +#include "base/files/scoped_file.h" +#include "base/logging.h" +#include "base/macros.h" + +struct iovec; // Declared in <sys/uio.h> + +namespace mojo { + +// NOTE: Functions declared here really don't belong in Mojo, but they exist to +// support code which used to rely on internal parts of the Mojo implementation +// and there wasn't a much better home for them. Consider moving them to +// src/base or something. + +// Like |write()| but handles |EINTR| and never raises |SIGPIPE|. +COMPONENT_EXPORT(MOJO_CPP_PLATFORM) +ssize_t SocketWrite(base::PlatformFile socket, + const void* bytes, + size_t num_bytes); + +// Like |writev()| but handles |EINTR| and never raises |SIGPIPE|. +COMPONENT_EXPORT(MOJO_CPP_PLATFORM) +ssize_t SocketWritev(base::PlatformFile socket, + struct iovec* iov, + size_t num_iov); + +// Wrapper around |sendmsg()| which makes it convenient to send attached file +// descriptors. All entries in |descriptors| must be valid and |descriptors| +// must be non-empty. +// +// Returns the same value as |sendmsg()|, i.e. -1 on error and otherwise the +// number of bytes sent. Note that the number of bytes sent may be smaller +// than the total data in |iov|. +// +// Note that regardless of success or failure, descriptors in |descriptors| are +// not closed. +COMPONENT_EXPORT(MOJO_CPP_PLATFORM) +ssize_t SendmsgWithHandles(base::PlatformFile socket, + struct iovec* iov, + size_t num_iov, + const std::vector<base::ScopedFD>& descriptors); + +// Like |recvmsg()|, but handles |EINTR|. +COMPONENT_EXPORT(MOJO_CPP_PLATFORM) +ssize_t SocketRecvmsg(base::PlatformFile socket, + void* buf, + size_t num_bytes, + std::vector<base::ScopedFD>* descriptors, + bool block = false); + +// Treats |server_fd| as a socket listening for new connections. Returns |false| +// if it encounters an unrecoverable error. +// +// If a connection wasn't established but the server is still OK, this returns +// |true| and leaves |*connection_fd| unchanged. +// +// If a connection was accepted, this returns |true| and |*connection_fd| is +// updated with a file descriptor for the new connection. +// +// Iff |check_peer_user| is |true|, connecting clients running as a different +// user from the server (i.e. the calling process) will be rejected. +COMPONENT_EXPORT(MOJO_CPP_PLATFORM) +bool AcceptSocketConnection(base::PlatformFile server_fd, + base::ScopedFD* connection_fd, + bool check_peer_user = true); + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_PLATFORM_SOCKET_UTILS_POSIX_H_ diff --git a/chromium/mojo/public/cpp/system/BUILD.gn b/chromium/mojo/public/cpp/system/BUILD.gn index bf9cecb9b25..155ac14ab3a 100644 --- a/chromium/mojo/public/cpp/system/BUILD.gn +++ b/chromium/mojo/public/cpp/system/BUILD.gn @@ -38,6 +38,10 @@ component("system") { "handle_signal_tracker.cc", "handle_signal_tracker.h", "handle_signals_state.h", + "invitation.cc", + "invitation.h", + "isolated_connection.cc", + "isolated_connection.h", "message.h", "message_pipe.cc", "message_pipe.h", diff --git a/chromium/mojo/public/cpp/system/README.md b/chromium/mojo/public/cpp/system/README.md index bb61bae931b..0e6d72df97f 100644 --- a/chromium/mojo/public/cpp/system/README.md +++ b/chromium/mojo/public/cpp/system/README.md @@ -23,7 +23,8 @@ top-level `mojo` namespace. All types of Mojo handles in the C API are simply opaque, integral `MojoHandle` values. The C++ API has more strongly typed wrappers defined for different handle types: `MessagePipeHandle`, `SharedBufferHandle`, -`DataPipeConsumerHandle`, `DataPipeProducerHandle`, and `WatcherHandle`. +`DataPipeConsumerHandle`, `DataPipeProducerHandle`, `TrapHandle`, and +`InvitationHandle`. Each of these also has a corresponding, move-only, scoped type for safer usage: `ScopedMessagePipeHandle`, `ScopedSharedBufferHandle`, and so on. When a scoped @@ -160,15 +161,17 @@ for detailed C++ shared buffer API documentation. The C++ library provides several helpers for wrapping system handle types. These are specifically useful when working with a few `//base` types, namely -`base::PlatformFile` and `base::SharedMemoryHandle`. See +`base::PlatformFile`, `base::SharedMemoryHandle` (deprecated), and various +strongly-typed shared memory region types like +`base::ReadOnlySharedMemoryRegion`. See [platform_handle.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/platform_handle.h) for detailed C++ platform handle API documentation. -## Signals & Watchers +## Signals & Traps -For an introduction to the concepts of handle signals and watchers, check out +For an introduction to the concepts of handle signals and traps, check out the C API's documentation on -[Signals & Watchers](/mojo/public/c/system/README.md#Signals-Watchers). +[Signals & Traps](/mojo/public/c/system/README.md#Signals-Traps). ### Querying Signals @@ -206,7 +209,7 @@ if (message_pipe.handle0->QuerySignalsState().readable()) { The [`mojo::SimpleWatcher`](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/simple_watcher.h) class serves as a convenient helper for using the -[low-level watcher API](/mojo/public/c/system/README.md#Signals-Watchers) +[low-level traps API](/mojo/public/c/system/README.md#Signals-Traps) to watch a handle for signaling state changes. A `SimpleWatcher` is bound to a single sequence and always dispatches its notifications on a `base::SequencedTaskRunner`. @@ -218,7 +221,7 @@ time by the `mojo::SimpleWatcher::ArmingPolicy` enum: before any notifications will fire regarding the state of the watched handle. Every time the notification callback is run, the `SimpleWatcher` must be rearmed again before the next one can fire. See - [Arming a Watcher](/mojo/public/c/system/README.md#Arming-a-Watcher) and the + [Arming a Trap](/mojo/public/c/system/README.md#Arming-a-Trap) and the documentation in `SimpleWatcher`'s header. * `AUTOMATIC` mode ensures that the `SimpleWatcher` always either is armed or @@ -274,7 +277,7 @@ WriteABunchOfStuff(pipe.handle1.get()); The C++ System API defines some utilities to block a calling sequence while waiting for one or more handles to change signaling state in an interesting way. These threads combine usage of the -[low-level Watcher API](/mojo/public/c/system/README.md#Signals-Watchers) +[low-level traps API](/mojo/public/c/system/README.md#Signals-Traps) with common synchronization primitives (namely `base::WaitableEvent`.) While these API features should be used sparingly, they are sometimes necessary. @@ -388,3 +391,150 @@ if (num_ready_handles > 0) { // signaling races with timeout, both things might be true. } ``` + +## Invitations +Invitations are the means by which two processes can have Mojo IPC bootstrapped +between them. An invitation must be transmitted over some platform-specific IPC +primitive (*e.g.* a Windows named pipe or UNIX domain socket), and the public +[platform support library](/mojo/public/cpp/platform/README.md) provides some +lightweight, cross-platform abstractions for those primitives. + +For any two processes looking to be connected, one must send an +`OutgoingInvitation` and the other must accept an `IncomingInvitation`. The +sender can attach named message pipe handles to the `OutgoingInvitation`, and +the receiver can extract them from its `IncomingInvitation`. + +Basic usage might look something like this in the case where one process is +responsible for launching the other. + +``` cpp +#include "base/command_line.h" +#include "base/process/launch.h" +#include "mojo/public/cpp/platform/platform_channel.h" +#include "mojo/public/cpp/system/invitation.h" +#include "mojo/public/cpp/system/message_pipe.h" + +mojo::ScopedMessagePipeHandle LaunchAndConnectSomething() { + // Under the hood, this is essentially always an OS pipe (domain socket pair, + // Windows named pipe, Fuchsia channel, etc). + mojo::PlatformChannel channel; + + mojo::OutgoingInvitation invitation; + + // Attach a message pipe to be extracted by the receiver. The other end of the + // pipe is returned for us to use locally. + mojo::ScopedMessagePipeHandle pipe = + invitation->AttachMessagePipe("arbitrary pipe name"); + + base::LaunchOptions options; + base::CommandLine command_line("some_executable") + channel.PrepareToPassRemoteEndpoint(&options, &command_line); + base::Process child_process = base::LaunchProcess(command_line, options); + channel.RemoteProcessLaunchAttempted(); + + OutgoingInvitation::Send(std::move(invitation), child_process.Handle(), + channel.TakeLocalEndpoint()); + return pipe; +} +``` + +The launched process can in turn accept an `IncomingInvitation`: + +``` cpp +#include "base/command_line.h" +#include "base/threading/thread.h" +#include "mojo/core/embedder/embedder.h" +#include "mojo/core/embedder/scoped_ipc_support.h" +#include "mojo/public/cpp/platform/platform_channel.h" +#include "mojo/public/cpp/system/invitation.h" +#include "mojo/public/cpp/system/message_pipe.h" + +int main(int argc, char** argv) { + // Basic Mojo initialization for a new process. + mojo::core::Init(); + base::Thread ipc_thread("ipc!"); + ipc_thread.StartWithOptions( + base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); + mojo::core::ScopedIPCSupport ipc_support( + ipc_thread.task_runner(), + mojo::core::ScopedIPCSupport::ShutdownPolicy::CLEAN); + + // Accept an invitation. + mojo::IncomingInvitation invitation = mojo::IncomingInvitation::Accept( + mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine( + *base::CommandLine::ForCurrentProcess())); + mojo::ScopedMessagePipeHandle pipe = + invitation->ExtractMessagePipe("arbitrary pipe name"); + + // etc... + return GoListenForMessagesAndRunForever(std::move(pipe)); +} +``` + +Now we have IPC initialized between the two processes. + +Also keep in mind that bindings interfaces are just message pipes with some +semantic and syntactic sugar wrapping them, so you can use these primordial +message pipe handles as mojom interfaces. For example: + +``` cpp +// Process A +mojo::OutgoingInvitation invitation; +auto pipe = invitation->AttachMessagePipe("x"); +mojo::Binding<foo::mojom::Bar> binding( + &bar_impl, + foo::mojom::BarRequest(std::move(pipe))); + +// Process B +auto invitation = mojo::IncomingInvitation::Accept(...); +auto pipe = invitation->ExtractMessagePipe("x"); +foo::mojom::BarPtr bar(foo::mojom::BarPtrInfo(std::move(pipe), 0)); + +// Will asynchronously invoke bar_impl.DoSomething() in process A. +bar->DoSomething(); +``` + +And just to be sure, the usage here could be reversed: the invitation sender +could just as well treat its pipe endpoint as a `BarPtr` while the receiver +treats theirs as a `BarRequest` to be bound. + +### Process Networks +Accepting an invitation admits the accepting process into the sender's connected +network of processes. Once this is done, it's possible for the newly admitted +process to establish communication with any other process in that network via +normal message pipe passing. + +This does not mean that the invited process can proactively locate and connect +to other processes without assistance; rather it means that Mojo handles created +by the process can safely be transferred to any other process in the network +over established message pipes, and similarly that Mojo handles created by any +other process in the network can be safely passed to the newly admitted process. + +### Invitation Restrictions +A process may only belong to a single network at a time. + +Additionally, once a process has joined a network, it cannot join another for +the remainder of its lifetime even if it has lost the connection to its original +network. This restriction will soon be lifted, but for now developers must be +mindful of it when authoring any long-running daemon process that will accept an +incoming invitation. + +### Isolated Invitations +It is possible to have two independent networks of Mojo-connected processes; for +example, a long-running system daemon which uses Mojo to talk to child processes +of its own, as well as the Chrome browser process running with no common +ancestor, talking to its own child processes. + +In this scenario it may be desirable to have a process in one network talk to a +process in the other network. Normal invitations cannot be used here since both +processes already belong to a network. In this case, an **isolated** invitation +can be used. These work just like regular invitations, except the sender must +call `OutgoingInvitation::SendIsolated` and the receiver must call +`IncomingInvitation::AcceptIsolated`. + +Once a connection is established via isolated invitation, Mojo IPC can be used +normally, with the exception that transitive process connections are not +supported; that is, if process A sends a message pipe handle to process B via +an isolated connection, process B cannot reliably send that pipe handle onward +to another process in its own network. Isolated invitations therefore may only +be used to facilitate direct 1:1 communication between two processes. diff --git a/chromium/mojo/public/cpp/system/handle_signals_state.h b/chromium/mojo/public/cpp/system/handle_signals_state.h index 4c658f3d858..3eb295beccf 100644 --- a/chromium/mojo/public/cpp/system/handle_signals_state.h +++ b/chromium/mojo/public/cpp/system/handle_signals_state.h @@ -67,6 +67,11 @@ struct MOJO_CPP_SYSTEM_EXPORT HandleSignalsState final return satisfies_any(MOJO_HANDLE_SIGNAL_PEER_REMOTE); } + // Indicates whether the handle has exceeded some quota limit. + bool quota_exceeded() const { + return satisfies_any(MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); + } + // The handle will never be |readable()| again. bool never_readable() const { return !can_satisfy_any(MOJO_HANDLE_SIGNAL_READABLE); diff --git a/chromium/mojo/public/cpp/system/invitation.cc b/chromium/mojo/public/cpp/system/invitation.cc new file mode 100644 index 00000000000..f70c435e955 --- /dev/null +++ b/chromium/mojo/public/cpp/system/invitation.cc @@ -0,0 +1,286 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/public/cpp/system/invitation.h" + +#include "base/numerics/safe_conversions.h" +#include "build/build_config.h" +#include "mojo/public/c/system/invitation.h" +#include "mojo/public/c/system/platform_handle.h" +#include "mojo/public/cpp/system/platform_handle.h" + +namespace mojo { + +namespace { + +static constexpr base::StringPiece kIsolatedPipeName = {"\0\0\0\0", 4}; + +void ProcessHandleToMojoProcessHandle(base::ProcessHandle target_process, + MojoPlatformProcessHandle* handle) { + handle->struct_size = sizeof(*handle); +#if defined(OS_WIN) + handle->value = + static_cast<uint64_t>(reinterpret_cast<uintptr_t>(target_process)); +#else + handle->value = static_cast<uint64_t>(target_process); +#endif +} + +void PlatformHandleToTransportEndpoint( + PlatformHandle platform_handle, + MojoPlatformHandle* endpoint_handle, + MojoInvitationTransportEndpoint* endpoint) { + PlatformHandle::ToMojoPlatformHandle(std::move(platform_handle), + endpoint_handle); + CHECK_NE(endpoint_handle->type, MOJO_PLATFORM_HANDLE_TYPE_INVALID); + + endpoint->struct_size = sizeof(*endpoint); + endpoint->num_platform_handles = 1; + endpoint->platform_handles = endpoint_handle; +} + +void RunErrorCallback(uintptr_t context, + const MojoProcessErrorDetails* details) { + auto* callback = reinterpret_cast<ProcessErrorCallback*>(context); + std::string error_message; + if (details->error_message) { + error_message = + std::string(details->error_message, details->error_message_length - 1); + callback->Run(error_message); + } else if (details->flags & MOJO_PROCESS_ERROR_FLAG_DISCONNECTED) { + delete callback; + } +} + +void SendInvitation(ScopedInvitationHandle invitation, + base::ProcessHandle target_process, + PlatformHandle endpoint_handle, + MojoInvitationTransportType transport_type, + MojoSendInvitationFlags flags, + const ProcessErrorCallback& error_callback, + base::StringPiece isolated_connection_name) { + MojoPlatformProcessHandle process_handle; + ProcessHandleToMojoProcessHandle(target_process, &process_handle); + + MojoPlatformHandle platform_handle; + MojoInvitationTransportEndpoint endpoint; + PlatformHandleToTransportEndpoint(std::move(endpoint_handle), + &platform_handle, &endpoint); + endpoint.type = transport_type; + + MojoProcessErrorHandler error_handler = nullptr; + uintptr_t error_handler_context = 0; + if (error_callback) { + error_handler = &RunErrorCallback; + + // NOTE: The allocated callback is effectively owned by the error handler, + // which will delete it on the final invocation for this context (i.e. + // process disconnection). + error_handler_context = + reinterpret_cast<uintptr_t>(new ProcessErrorCallback(error_callback)); + } + + MojoSendInvitationOptions options; + options.struct_size = sizeof(options); + options.flags = flags; + if (flags & MOJO_SEND_INVITATION_FLAG_ISOLATED) { + options.isolated_connection_name = isolated_connection_name.data(); + options.isolated_connection_name_length = + static_cast<uint32_t>(isolated_connection_name.size()); + } + MojoResult result = + MojoSendInvitation(invitation.get().value(), &process_handle, &endpoint, + error_handler, error_handler_context, &options); + // If successful, the invitation handle is already closed for us. + if (result == MOJO_RESULT_OK) + ignore_result(invitation.release()); +} + +} // namespace + +OutgoingInvitation::OutgoingInvitation() { + MojoHandle invitation_handle; + MojoResult result = MojoCreateInvitation(nullptr, &invitation_handle); + DCHECK_EQ(result, MOJO_RESULT_OK); + + handle_.reset(InvitationHandle(invitation_handle)); +} + +OutgoingInvitation::OutgoingInvitation(OutgoingInvitation&& other) = default; + +OutgoingInvitation::~OutgoingInvitation() = default; + +OutgoingInvitation& OutgoingInvitation::operator=(OutgoingInvitation&& other) = + default; + +ScopedMessagePipeHandle OutgoingInvitation::AttachMessagePipe( + base::StringPiece name) { + DCHECK(!name.empty()); + DCHECK(base::IsValueInRangeForNumericType<uint32_t>(name.size())); + MojoHandle message_pipe_handle; + MojoResult result = MojoAttachMessagePipeToInvitation( + handle_.get().value(), name.data(), static_cast<uint32_t>(name.size()), + nullptr, &message_pipe_handle); + DCHECK_EQ(MOJO_RESULT_OK, result); + return ScopedMessagePipeHandle(MessagePipeHandle(message_pipe_handle)); +} + +ScopedMessagePipeHandle OutgoingInvitation::AttachMessagePipe(uint64_t name) { + return AttachMessagePipe( + base::StringPiece(reinterpret_cast<const char*>(&name), sizeof(name))); +} + +ScopedMessagePipeHandle OutgoingInvitation::ExtractMessagePipe( + base::StringPiece name) { + DCHECK(!name.empty()); + DCHECK(base::IsValueInRangeForNumericType<uint32_t>(name.size())); + MojoHandle message_pipe_handle; + MojoResult result = MojoExtractMessagePipeFromInvitation( + handle_.get().value(), name.data(), static_cast<uint32_t>(name.size()), + nullptr, &message_pipe_handle); + DCHECK_EQ(MOJO_RESULT_OK, result); + return ScopedMessagePipeHandle(MessagePipeHandle(message_pipe_handle)); +} + +ScopedMessagePipeHandle OutgoingInvitation::ExtractMessagePipe(uint64_t name) { + return ExtractMessagePipe( + base::StringPiece(reinterpret_cast<const char*>(&name), sizeof(name))); +} + +// static +void OutgoingInvitation::Send(OutgoingInvitation invitation, + base::ProcessHandle target_process, + PlatformChannelEndpoint channel_endpoint, + const ProcessErrorCallback& error_callback) { + SendInvitation(std::move(invitation.handle_), target_process, + channel_endpoint.TakePlatformHandle(), + MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL, + MOJO_SEND_INVITATION_FLAG_NONE, error_callback, ""); +} + +// static +void OutgoingInvitation::Send(OutgoingInvitation invitation, + base::ProcessHandle target_process, + PlatformChannelServerEndpoint server_endpoint, + const ProcessErrorCallback& error_callback) { + SendInvitation(std::move(invitation.handle_), target_process, + server_endpoint.TakePlatformHandle(), + MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER, + MOJO_SEND_INVITATION_FLAG_NONE, error_callback, ""); +} + +// static +ScopedMessagePipeHandle OutgoingInvitation::SendIsolated( + PlatformChannelEndpoint channel_endpoint, + base::StringPiece connection_name) { + mojo::OutgoingInvitation invitation; + ScopedMessagePipeHandle pipe = + invitation.AttachMessagePipe(kIsolatedPipeName); + SendInvitation(std::move(invitation.handle_), base::kNullProcessHandle, + channel_endpoint.TakePlatformHandle(), + MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL, + MOJO_SEND_INVITATION_FLAG_ISOLATED, ProcessErrorCallback(), + connection_name); + return pipe; +} + +// static +ScopedMessagePipeHandle OutgoingInvitation::SendIsolated( + PlatformChannelServerEndpoint server_endpoint, + base::StringPiece connection_name) { + mojo::OutgoingInvitation invitation; + ScopedMessagePipeHandle pipe = + invitation.AttachMessagePipe(kIsolatedPipeName); + SendInvitation(std::move(invitation.handle_), base::kNullProcessHandle, + server_endpoint.TakePlatformHandle(), + MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER, + MOJO_SEND_INVITATION_FLAG_ISOLATED, ProcessErrorCallback(), + connection_name); + return pipe; +} + +IncomingInvitation::IncomingInvitation() = default; + +IncomingInvitation::IncomingInvitation(IncomingInvitation&& other) = default; + +IncomingInvitation::IncomingInvitation(ScopedInvitationHandle handle) + : handle_(std::move(handle)) {} + +IncomingInvitation::~IncomingInvitation() = default; + +IncomingInvitation& IncomingInvitation::operator=(IncomingInvitation&& other) = + default; + +// static +IncomingInvitation IncomingInvitation::Accept( + PlatformChannelEndpoint channel_endpoint) { + MojoPlatformHandle endpoint_handle; + PlatformHandle::ToMojoPlatformHandle(channel_endpoint.TakePlatformHandle(), + &endpoint_handle); + CHECK_NE(endpoint_handle.type, MOJO_PLATFORM_HANDLE_TYPE_INVALID); + + MojoInvitationTransportEndpoint transport_endpoint; + transport_endpoint.struct_size = sizeof(transport_endpoint); + transport_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL; + transport_endpoint.num_platform_handles = 1; + transport_endpoint.platform_handles = &endpoint_handle; + + MojoHandle invitation_handle; + MojoResult result = + MojoAcceptInvitation(&transport_endpoint, nullptr, &invitation_handle); + if (result != MOJO_RESULT_OK) + return IncomingInvitation(); + + return IncomingInvitation( + ScopedInvitationHandle(InvitationHandle(invitation_handle))); +} + +// static +ScopedMessagePipeHandle IncomingInvitation::AcceptIsolated( + PlatformChannelEndpoint channel_endpoint) { + MojoPlatformHandle endpoint_handle; + PlatformHandle::ToMojoPlatformHandle(channel_endpoint.TakePlatformHandle(), + &endpoint_handle); + CHECK_NE(endpoint_handle.type, MOJO_PLATFORM_HANDLE_TYPE_INVALID); + + MojoInvitationTransportEndpoint transport_endpoint; + transport_endpoint.struct_size = sizeof(transport_endpoint); + transport_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL; + transport_endpoint.num_platform_handles = 1; + transport_endpoint.platform_handles = &endpoint_handle; + + MojoAcceptInvitationOptions options; + options.struct_size = sizeof(options); + options.flags = MOJO_ACCEPT_INVITATION_FLAG_ISOLATED; + + MojoHandle invitation_handle; + MojoResult result = + MojoAcceptInvitation(&transport_endpoint, &options, &invitation_handle); + if (result != MOJO_RESULT_OK) + return ScopedMessagePipeHandle(); + + IncomingInvitation invitation{ + ScopedInvitationHandle(InvitationHandle(invitation_handle))}; + return invitation.ExtractMessagePipe(kIsolatedPipeName); +} + +ScopedMessagePipeHandle IncomingInvitation::ExtractMessagePipe( + base::StringPiece name) { + DCHECK(!name.empty()); + DCHECK(base::IsValueInRangeForNumericType<uint32_t>(name.size())); + DCHECK(handle_.is_valid()); + MojoHandle message_pipe_handle; + MojoResult result = MojoExtractMessagePipeFromInvitation( + handle_.get().value(), name.data(), static_cast<uint32_t>(name.size()), + nullptr, &message_pipe_handle); + DCHECK_EQ(MOJO_RESULT_OK, result); + return ScopedMessagePipeHandle(MessagePipeHandle(message_pipe_handle)); +} + +ScopedMessagePipeHandle IncomingInvitation::ExtractMessagePipe(uint64_t name) { + return ExtractMessagePipe( + base::StringPiece(reinterpret_cast<const char*>(&name), sizeof(name))); +} + +} // namespace mojo diff --git a/chromium/mojo/public/cpp/system/invitation.h b/chromium/mojo/public/cpp/system/invitation.h new file mode 100644 index 00000000000..475a6735822 --- /dev/null +++ b/chromium/mojo/public/cpp/system/invitation.h @@ -0,0 +1,186 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_SYSTEM_INVITATION_H_ +#define MOJO_PUBLIC_CPP_SYSTEM_INVITATION_H_ + +#include <cstdint> +#include <string> + +#include "base/callback.h" +#include "base/component_export.h" +#include "base/macros.h" +#include "base/process/process_handle.h" +#include "base/strings/string_piece.h" +#include "mojo/public/cpp/platform/platform_channel_endpoint.h" +#include "mojo/public/cpp/platform/platform_channel_server_endpoint.h" +#include "mojo/public/cpp/system/handle.h" +#include "mojo/public/cpp/system/message_pipe.h" +#include "mojo/public/cpp/system/system_export.h" + +namespace mojo { + +// A callback which may be provided when sending an invitation to another +// process. In the event of any validation errors regarding messages from that +// process (reported via MojoNotifyBadMessage etc and related helpers), the +// callback will be invoked. +using ProcessErrorCallback = base::RepeatingCallback<void(const std::string&)>; + +// A strongly-typed representation of a |MojoHandle| for an invitation. +class InvitationHandle : public Handle { + public: + InvitationHandle() {} + explicit InvitationHandle(MojoHandle value) : Handle(value) {} + + // Copying and assignment allowed. +}; + +static_assert(sizeof(InvitationHandle) == sizeof(Handle), + "Bad size for C++ InvitationHandle"); + +using ScopedInvitationHandle = ScopedHandleBase<InvitationHandle>; +static_assert(sizeof(ScopedInvitationHandle) == sizeof(InvitationHandle), + "Bad size for C++ ScopedInvitationHandle"); + +// An OutgoingInvitation is used to invite another process to join the calling +// process's IPC network. +// +// Typical use involves constructing a |PlatformChannel| and using one end to +// send the invitation (see |Send()| below) while passing the other to a child +// process. +// +// This may also be used with the server endpoint of a |NamedPlatformChannel|. +class MOJO_CPP_SYSTEM_EXPORT OutgoingInvitation { + public: + OutgoingInvitation(); + OutgoingInvitation(OutgoingInvitation&& other); + ~OutgoingInvitation(); + + OutgoingInvitation& operator=(OutgoingInvitation&& other); + + // Creates a new message pipe, attaching one end to this invitation and + // returning the other end to the caller. The invitee can extract the + // attached endpoint (see |IncomingInvitation|) thus establishing end-to-end + // Mojo communication. + // + // |name| is an arbitrary value that must be used by the invitee to extract + // the corresponding attached endpoint. + ScopedMessagePipeHandle AttachMessagePipe(base::StringPiece name); + + // Same as above but allows use of an integer name for convenience. + ScopedMessagePipeHandle AttachMessagePipe(uint64_t name); + + // Extracts an attached pipe. Note that this is not typically useful, but it + // is potentially necessary in cases where a caller wants to, e.g., abort + // launching another process and recover a pipe endpoint they had previously + // attached. + ScopedMessagePipeHandle ExtractMessagePipe(base::StringPiece name); + + // Same as above but allows use of an integer name for convenience. + ScopedMessagePipeHandle ExtractMessagePipe(uint64_t name); + + // Sends |invitation| to another process via |channel_endpoint|, which should + // correspond to the local endpoint taken from a |PlatformChannel|. + // + // |process_handle| is a handle to the destination process if known. If not + // provided, IPC may be limited on some platforms (namely Mac and Windows) due + // to an inability to transfer system handles across the boundary. + static void Send(OutgoingInvitation invitation, + base::ProcessHandle target_process, + PlatformChannelEndpoint channel_endpoint, + const ProcessErrorCallback& error_callback = {}); + + // Similar to above, but sends |invitation| via |server_endpoint|, which + // should correspond to a |PlatformChannelServerEndpoint| taken from a + // |NamedPlatformChannel|. + static void Send(OutgoingInvitation invitation, + base::ProcessHandle target_process, + PlatformChannelServerEndpoint server_endpoint, + const ProcessErrorCallback& error_callback = {}); + + // Sends an isolated invitation over |endpoint|. The process at the other + // endpoint must use |IncomingInvitation::AcceptIsolated()| to accept the + // invitation. + // + // Isolated invitations must be used in lieu of regular invitations in cases + // where both of the processes being connected already belong to independent + // multiprocess graphs. + // + // Such connections are limited in functionality: + // + // * Platform handles may not be transferrable between the processes + // + // * Pipes sent between the processes may not be subsequently transferred to + // other processes in each others' process graph. + // + // Only one concurrent isolated connection is supported between any two + // processes. + // + // Unlike |Send()| above, isolated invitations automatically have a single + // message pipe attached and this is the only attachment allowed. The local + // end of the attached pipe is returned here. + // + // If |connection_name| is non-empty, any previously established isolated + // connection using the same name will be disconnected. + static ScopedMessagePipeHandle SendIsolated( + PlatformChannelEndpoint channel_endpoint, + base::StringPiece connection_name = {}); + + // Similar to above but sends |invitation| via |server_endpoint|, which should + // correspond to a |PlatformChannelServerEndpoint| taken from a + // |NamedPlatformChannel|. + // + // If |connection_name| is non-empty, any previously established isolated + // connection using the same name will be disconnected. + static ScopedMessagePipeHandle SendIsolated( + PlatformChannelServerEndpoint server_endpoint, + base::StringPiece connection_name = {}); + + private: + ScopedInvitationHandle handle_; + + DISALLOW_COPY_AND_ASSIGN(OutgoingInvitation); +}; + +// An IncomingInvitation can be accepted by an invited process by calling +// |IncomingInvitation::Accept()|. Once accepted, the invitation can be used +// to extract attached message pipes by name. +class MOJO_CPP_SYSTEM_EXPORT IncomingInvitation { + public: + IncomingInvitation(); + IncomingInvitation(IncomingInvitation&& other); + explicit IncomingInvitation(ScopedInvitationHandle handle); + ~IncomingInvitation(); + + IncomingInvitation& operator=(IncomingInvitation&& other); + + // Accepts an incoming invitation from |channel_endpoint|. If the invitation + // was sent using one end of a |PlatformChannel|, |channel_endpoint| should be + // the other end of that channel. If the invitation was sent using a + // |PlatformChannelServerEndpoint|, then |channel_endpoint| should be created + // by |NamedPlatformChannel::ConnectToServer|. + static IncomingInvitation Accept(PlatformChannelEndpoint channel_endpoint); + + // Accepts an incoming isolated invitation from |channel_endpoint|. See + // notes on |OutgoingInvitation::SendIsolated()|. + static ScopedMessagePipeHandle AcceptIsolated( + PlatformChannelEndpoint channel_endpoint); + + // Extracts an attached message pipe from this invitation. This may succeed + // even if no such pipe was attached, though the extracted pipe will + // eventually observe peer closure. + ScopedMessagePipeHandle ExtractMessagePipe(base::StringPiece name); + + // Same as above but allows use of an integer name for convenience. + ScopedMessagePipeHandle ExtractMessagePipe(uint64_t name); + + private: + ScopedInvitationHandle handle_; + + DISALLOW_COPY_AND_ASSIGN(IncomingInvitation); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_SYSTEM_INVITATION_H_ diff --git a/chromium/mojo/public/cpp/system/isolated_connection.cc b/chromium/mojo/public/cpp/system/isolated_connection.cc new file mode 100644 index 00000000000..f85be4e3d41 --- /dev/null +++ b/chromium/mojo/public/cpp/system/isolated_connection.cc @@ -0,0 +1,41 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/public/cpp/system/isolated_connection.h" + +#include "mojo/public/cpp/platform/platform_channel.h" +#include "mojo/public/cpp/system/invitation.h" + +namespace mojo { + +IsolatedConnection::IsolatedConnection() + : token_(base::UnguessableToken::Create()) {} + +IsolatedConnection::~IsolatedConnection() { + // We send a dummy invitation over a temporary channel, re-using |token_| as + // the name. This ensures that the connection set up by Connect(), if any, + // will be replaced with a short-lived, self-terminating connection. + // + // This is a bit of a hack since Mojo does not provide any API for explicitly + // terminating isolated connections, but this is a decision made to minimize + // the API surface dedicated to isolated connections in anticipation of the + // concept being deprecated eventually. + PlatformChannel channel; + OutgoingInvitation::SendIsolated(channel.TakeLocalEndpoint(), + token_.ToString()); +} + +ScopedMessagePipeHandle IsolatedConnection::Connect( + PlatformChannelEndpoint endpoint) { + return OutgoingInvitation::SendIsolated(std::move(endpoint), + token_.ToString()); +} + +ScopedMessagePipeHandle IsolatedConnection::Connect( + PlatformChannelServerEndpoint endpoint) { + return OutgoingInvitation::SendIsolated(std::move(endpoint), + token_.ToString()); +} + +} // namespace mojo diff --git a/chromium/mojo/public/cpp/system/isolated_connection.h b/chromium/mojo/public/cpp/system/isolated_connection.h new file mode 100644 index 00000000000..42c90532ad8 --- /dev/null +++ b/chromium/mojo/public/cpp/system/isolated_connection.h @@ -0,0 +1,60 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_SYSTEM_ISOLATED_CONNECTION_H_ +#define MOJO_PUBLIC_CPP_SYSTEM_ISOLATED_CONNECTION_H_ + +#include "base/macros.h" +#include "base/unguessable_token.h" +#include "mojo/public/cpp/platform/platform_channel_endpoint.h" +#include "mojo/public/cpp/platform/platform_channel_server_endpoint.h" +#include "mojo/public/cpp/system/message_pipe.h" +#include "mojo/public/cpp/system/system_export.h" + +namespace mojo { + +// IsolatedConnection establishes a one-off Mojo IPC connection between two +// processes. Unlike more common connections established by invitation +// (see OutgoingInvitation and IncomingInvitation), isolated connections +// do not result in the two processes becoming part of the same connected +// graph of processes. As such, any message pipe established over this +// connection can only be used for direct IPC between the two processes in +// question. +// +// This means that if one of the processes sends a Mojo handle (e.g. another +// message pipe endpoint) to the other process, the receiving process cannot +// pass that handle to yet another process in its own graph. This limitation is +// subtle and can be difficult to work around, so use of IsolatedConnection +// should be rare. +// +// This is primarily useful when you already have two established Mojo process +// graphs isolated form each other, and you want to do some IPC between two +// processes, one in each graph. +// +// A connection established via |Connect()|, and any opened message pipes +// spanning that connection, will remain valid and connected as long as this +// object remains alive. +class MOJO_CPP_SYSTEM_EXPORT IsolatedConnection { + public: + IsolatedConnection(); + ~IsolatedConnection(); + + // Connects to a process at the other end of the channel. Returns a primordial + // message pipe that can be used for Mojo IPC. The connection + // will be connected to a corresponding peer pipe in the remote process. + ScopedMessagePipeHandle Connect(PlatformChannelEndpoint endpoint); + + // Same as above but works with a server endpoint. The corresponding client + // could use the above signature with NamedPlatformChannel::ConnectToServer. + ScopedMessagePipeHandle Connect(PlatformChannelServerEndpoint endpoint); + + private: + const base::UnguessableToken token_; + + DISALLOW_COPY_AND_ASSIGN(IsolatedConnection); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_SYSTEM_ISOLATED_CONNECTION_H_ diff --git a/chromium/mojo/public/cpp/system/platform_handle.cc b/chromium/mojo/public/cpp/system/platform_handle.cc index 0ed2c15ca23..9e55f550b2f 100644 --- a/chromium/mojo/public/cpp/system/platform_handle.cc +++ b/chromium/mojo/public/cpp/system/platform_handle.cc @@ -179,75 +179,26 @@ base::subtle::PlatformSharedMemoryRegion UnwrapPlatformSharedMemoryRegion( } // namespace -void PlatformHandleToMojoPlatformHandle(PlatformHandle handle, - MojoPlatformHandle* out_handle) { - DCHECK(out_handle); - out_handle->struct_size = sizeof(MojoPlatformHandle); - if (!handle.is_valid()) { - out_handle->type = MOJO_PLATFORM_HANDLE_TYPE_INVALID; - out_handle->value = 0; - return; - } - - do { -#if defined(OS_WIN) - out_handle->type = MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE; - out_handle->value = - static_cast<uint64_t>(HandleToLong(handle.TakeHandle().Take())); - break; -#elif defined(OS_FUCHSIA) - if (handle.is_valid_handle()) { - out_handle->type = MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE; - out_handle->value = handle.TakeHandle().release(); - break; - } -#elif defined(OS_MACOSX) && !defined(OS_IOS) - if (handle.is_valid_mach_port()) { - out_handle->type = MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT; - out_handle->value = - static_cast<uint64_t>(handle.TakeMachPort().release()); - break; - } -#endif - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) - DCHECK(handle.is_valid_fd()); - out_handle->type = MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR; - out_handle->value = static_cast<uint64_t>(handle.TakeFD().release()); -#endif - } while (false); +ScopedHandle WrapPlatformHandle(PlatformHandle handle) { + MojoPlatformHandle platform_handle; + PlatformHandle::ToMojoPlatformHandle(std::move(handle), &platform_handle); - // One of the above cases must take ownership of |handle|. - DCHECK(!handle.is_valid()); + MojoHandle wrapped_handle; + MojoResult result = + MojoWrapPlatformHandle(&platform_handle, nullptr, &wrapped_handle); + if (result != MOJO_RESULT_OK) + return ScopedHandle(); + return ScopedHandle(Handle(wrapped_handle)); } -PlatformHandle MojoPlatformHandleToPlatformHandle( - const MojoPlatformHandle* handle) { - if (handle->struct_size < sizeof(*handle) || - handle->type == MOJO_PLATFORM_HANDLE_TYPE_INVALID) { - return PlatformHandle(); - } - -#if defined(OS_WIN) - if (handle->type != MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE) - return PlatformHandle(); - return PlatformHandle( - base::win::ScopedHandle(LongToHandle(static_cast<long>(handle->value)))); -#elif defined(OS_FUCHSIA) - if (handle->type == MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE) - return PlatformHandle(base::ScopedZxHandle(handle->value)); -#elif defined(OS_MACOSX) && !defined(OS_IOS) - if (handle->type == MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT) { - return PlatformHandle(base::mac::ScopedMachSendRight( - static_cast<mach_port_t>(handle->value))); - } -#endif - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) - if (handle->type != MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR) +PlatformHandle UnwrapPlatformHandle(ScopedHandle handle) { + MojoPlatformHandle platform_handle; + platform_handle.struct_size = sizeof(platform_handle); + MojoResult result = MojoUnwrapPlatformHandle(handle.release().value(), + nullptr, &platform_handle); + if (result != MOJO_RESULT_OK) return PlatformHandle(); - return PlatformHandle(base::ScopedFD(static_cast<int>(handle->value))); -#endif + return PlatformHandle::FromMojoPlatformHandle(&platform_handle); } // Wraps a PlatformFile as a Mojo handle. Takes ownership of the file object. diff --git a/chromium/mojo/public/cpp/system/platform_handle.h b/chromium/mojo/public/cpp/system/platform_handle.h index 5fbd569dc86..b18ceafacd9 100644 --- a/chromium/mojo/public/cpp/system/platform_handle.h +++ b/chromium/mojo/public/cpp/system/platform_handle.h @@ -76,18 +76,13 @@ enum class UnwrappedSharedMemoryHandleProtection { kReadOnly, }; -// Converts a PlatformHandle to the MojoPlatformHandle C struct for use with -// Mojo APIs. Note that although MojoPlatformHandle has weak ownership, this -// relinquishes ownership from |handle|. -void MOJO_CPP_SYSTEM_EXPORT -PlatformHandleToMojoPlatformHandle(PlatformHandle handle, - MojoPlatformHandle* out_handle); - -// Converts a MojoPlatformHandle C struct to a PlatformHandle for use with -// various C++ APIs. Note that although MojoPlatformHandle has weak ownership, -// the new handle assumes ownership of the represented platform handle. -PlatformHandle MOJO_CPP_SYSTEM_EXPORT -MojoPlatformHandleToPlatformHandle(const MojoPlatformHandle* handle); +// Wraps a PlatformHandle from the C++ platform support library as a Mojo +// handle. +MOJO_CPP_SYSTEM_EXPORT ScopedHandle WrapPlatformHandle(PlatformHandle handle); + +// Unwraps a Mojo handle to a PlatformHandle object from the C++ platform +// support library. +MOJO_CPP_SYSTEM_EXPORT PlatformHandle UnwrapPlatformHandle(ScopedHandle handle); // Wraps a PlatformFile as a Mojo handle. Takes ownership of the file object. // If |platform_file| is valid, this will return a valid handle. diff --git a/chromium/mojo/public/cpp/system/simple_watcher.cc b/chromium/mojo/public/cpp/system/simple_watcher.cc index 9c5017b4c9e..6384c1abed2 100644 --- a/chromium/mojo/public/cpp/system/simple_watcher.cc +++ b/chromium/mojo/public/cpp/system/simple_watcher.cc @@ -100,8 +100,8 @@ class SimpleWatcher::Context : public base::RefCountedThreadSafe<Context> { task_runner_->RunsTasksInCurrentSequence() && weak_watcher_ && weak_watcher_->is_default_task_runner_) { // System notifications will trigger from the task runner passed to - // mojo::edk::ScopedIPCSupport. In Chrome this happens to always be the - // default task runner for the IO thread. + // mojo::core::ScopedIPCSupport. In Chrome this happens to always be + // the default task runner for the IO thread. weak_watcher_->OnHandleReady(watch_id_, result, state); } else { task_runner_->PostTask( @@ -206,22 +206,21 @@ void SimpleWatcher::Cancel() { MojoResult SimpleWatcher::Arm(MojoResult* ready_result, HandleSignalsState* ready_state) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - uint32_t num_ready_contexts = 1; - uintptr_t ready_context; - MojoResult local_ready_result; - HandleSignalsState local_ready_state; - if (!ready_state) - ready_state = &local_ready_state; - MojoResult rv = - MojoArmTrap(trap_handle_.get().value(), nullptr, &num_ready_contexts, - &ready_context, &local_ready_result, - reinterpret_cast<MojoHandleSignalsState*>(ready_state)); + uint32_t num_blocking_events = 1; + MojoTrapEvent blocking_event = {sizeof(blocking_event)}; + MojoResult rv = MojoArmTrap(trap_handle_.get().value(), nullptr, + &num_blocking_events, &blocking_event); if (rv == MOJO_RESULT_FAILED_PRECONDITION) { DCHECK(context_); - DCHECK_EQ(1u, num_ready_contexts); - DCHECK_EQ(context_->value(), ready_context); + DCHECK_EQ(1u, num_blocking_events); + DCHECK_EQ(context_->value(), blocking_event.trigger_context); if (ready_result) - *ready_result = local_ready_result; + *ready_result = blocking_event.result; + if (ready_state) { + *ready_state = + HandleSignalsState(blocking_event.signals_state.satisfied_signals, + blocking_event.signals_state.satisfiable_signals); + } } return rv; diff --git a/chromium/mojo/public/cpp/system/tests/BUILD.gn b/chromium/mojo/public/cpp/system/tests/BUILD.gn index e29b5c03f30..c08f3c1099e 100644 --- a/chromium/mojo/public/cpp/system/tests/BUILD.gn +++ b/chromium/mojo/public/cpp/system/tests/BUILD.gn @@ -18,11 +18,16 @@ source_set("tests") { "wait_unittest.cc", ] + if (!is_ios) { + sources += [ "invitation_unittest.cc" ] + } + deps = [ "//base", "//base/test:test_support", - "//mojo/edk/test:test_support", + "//mojo/core/test:test_support", "//mojo/public/c/system/tests", + "//mojo/public/cpp/platform", "//mojo/public/cpp/system", "//mojo/public/cpp/test_support:test_utils", "//testing/gtest", diff --git a/chromium/mojo/public/cpp/system/wait.cc b/chromium/mojo/public/cpp/system/wait.cc index a1aa4bc16c0..8defafe4d8f 100644 --- a/chromium/mojo/public/cpp/system/wait.cc +++ b/chromium/mojo/public/cpp/system/wait.cc @@ -87,23 +87,21 @@ MojoResult Wait(Handle handle, } DCHECK_EQ(MOJO_RESULT_OK, rv); - uint32_t num_ready_contexts = 1; - uintptr_t ready_context; - MojoResult ready_result; - MojoHandleSignalsState ready_state; - rv = MojoArmTrap(trap.get().value(), nullptr, &num_ready_contexts, - &ready_context, &ready_result, &ready_state); + uint32_t num_blocking_events = 1; + MojoTrapEvent blocking_event = {sizeof(blocking_event)}; + rv = MojoArmTrap(trap.get().value(), nullptr, &num_blocking_events, + &blocking_event); if (rv == MOJO_RESULT_FAILED_PRECONDITION) { - DCHECK_EQ(1u, num_ready_contexts); + DCHECK_EQ(1u, num_blocking_events); if (signals_state) - *signals_state = ready_state; - return ready_result; + *signals_state = blocking_event.signals_state; + return blocking_event.result; } // Wait for the first notification only. context->event().Wait(); - ready_result = context->wait_result(); + MojoResult ready_result = context->wait_result(); DCHECK_NE(MOJO_RESULT_UNKNOWN, ready_result); if (signals_state) @@ -150,22 +148,24 @@ MojoResult WaitMany(const Handle* handles, events[i] = &contexts[i]->event(); } - uint32_t num_ready_contexts = 1; - uintptr_t ready_context = 0; - MojoResult ready_result = MOJO_RESULT_UNKNOWN; - MojoHandleSignalsState ready_state{0, 0}; - rv = MojoArmTrap(trap.get().value(), nullptr, &num_ready_contexts, - &ready_context, &ready_result, &ready_state); + uint32_t num_blocking_events = 1; + MojoTrapEvent blocking_event = {sizeof(blocking_event)}; + rv = MojoArmTrap(trap.get().value(), nullptr, &num_blocking_events, + &blocking_event); size_t index = num_handles; + MojoResult ready_result = MOJO_RESULT_UNKNOWN; + MojoHandleSignalsState ready_state = {}; if (rv == MOJO_RESULT_FAILED_PRECONDITION) { - DCHECK_EQ(1u, num_ready_contexts); + DCHECK_EQ(1u, num_blocking_events); // Most commonly we only watch a small number of handles. Just scan for // the right index. for (size_t i = 0; i < num_handles; ++i) { - if (contexts[i]->context_value() == ready_context) { + if (contexts[i]->context_value() == blocking_event.trigger_context) { index = i; + ready_result = blocking_event.result; + ready_state = blocking_event.signals_state; break; } } diff --git a/chromium/mojo/public/cpp/system/wait_set.cc b/chromium/mojo/public/cpp/system/wait_set.cc index 3ad98409160..cd8b859dc9c 100644 --- a/chromium/mojo/public/cpp/system/wait_set.cc +++ b/chromium/mojo/public/cpp/system/wait_set.cc @@ -145,22 +145,18 @@ class WaitSet::State : public base::RefCountedThreadSafe<State> { handle_event_.Reset(); DCHECK_LE(*num_ready_handles, std::numeric_limits<uint32_t>::max()); - uint32_t num_ready_contexts = static_cast<uint32_t>(*num_ready_handles); - - base::StackVector<uintptr_t, 4> ready_contexts; - ready_contexts.container().resize(num_ready_contexts); - base::StackVector<MojoHandleSignalsState, 4> ready_states; - MojoHandleSignalsState* out_states = signals_states; - if (!out_states) { - // If the caller didn't provide a buffer for signal states, we provide - // our own locally. MojoArmTrap() requires one if we want to handle - // arming failure properly. - ready_states.container().resize(num_ready_contexts); - out_states = ready_states.container().data(); + uint32_t num_blocking_events = + static_cast<uint32_t>(*num_ready_handles); + + base::StackVector<MojoTrapEvent, 4> blocking_events; + blocking_events.container().resize(num_blocking_events); + for (size_t i = 0; i < num_blocking_events; ++i) { + blocking_events.container()[i].struct_size = + sizeof(blocking_events.container()[i]); } - MojoResult rv = MojoArmTrap( - trap_handle_.get().value(), nullptr, &num_ready_contexts, - ready_contexts.container().data(), ready_results, out_states); + MojoResult rv = MojoArmTrap(trap_handle_.get().value(), nullptr, + &num_blocking_events, + blocking_events.container().data()); if (rv == MOJO_RESULT_FAILED_PRECONDITION) { // Simulate the handles becoming ready. We do this in lieu of @@ -168,11 +164,12 @@ class WaitSet::State : public base::RefCountedThreadSafe<State> { // starving user events. i.e., we always want to call WaitMany() // below. handle_event_.Signal(); - for (size_t i = 0; i < num_ready_contexts; ++i) { - auto it = contexts_.find(ready_contexts.container()[i]); + for (size_t i = 0; i < num_blocking_events; ++i) { + const auto& event = blocking_events.container()[i]; + auto it = contexts_.find(event.trigger_context); DCHECK(it != contexts_.end()); - ready_handles_[it->second->handle()] = {ready_results[i], - out_states[i]}; + ready_handles_[it->second->handle()] = {event.result, + event.signals_state}; } } else if (rv == MOJO_RESULT_NOT_FOUND) { // Nothing to watch. If there are no user events, always signal to diff --git a/chromium/mojo/public/java/BUILD.gn b/chromium/mojo/public/java/BUILD.gn index 14951f4f095..c6159ab2e80 100644 --- a/chromium/mojo/public/java/BUILD.gn +++ b/chromium/mojo/public/java/BUILD.gn @@ -41,6 +41,7 @@ android_library("bindings_java") { "bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java", "bindings/src/org/chromium/mojo/bindings/DeserializationException.java", "bindings/src/org/chromium/mojo/bindings/Encoder.java", + "bindings/src/org/chromium/mojo/bindings/ExceptionHandler.java", "bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java", "bindings/src/org/chromium/mojo/bindings/HandleOwner.java", "bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java", @@ -72,7 +73,7 @@ android_library("base_java") { deps = [ ":system_java", - "//mojo/android:system_java", + "//mojo/public/java/system:system_impl_java", "//mojo/public/mojom/base:base_java", ] } diff --git a/chromium/mojo/android/BUILD.gn b/chromium/mojo/public/java/system/BUILD.gn index 223f859aae4..0025f5588d3 100644 --- a/chromium/mojo/android/BUILD.gn +++ b/chromium/mojo/public/java/system/BUILD.gn @@ -4,12 +4,12 @@ import("//build/config/android/rules.gni") -group("android") { +group("system") { testonly = true deps = [ ":mojo_javatests", ":mojo_test_apk", - ":system_java", + ":system_impl_java", ] } @@ -19,48 +19,48 @@ generate_jni("jni_headers") { "javatests/src/org/chromium/mojo/bindings/ValidationTestUtil.java", ] public_deps = [ - ":system_java_jni_headers", + ":system_impl_java_jni_headers", ] jni_package = "mojo" } -generate_jni("system_java_jni_headers") { +generate_jni("system_impl_java_jni_headers") { sources = [ - "system/src/org/chromium/mojo/system/impl/BaseRunLoop.java", - "system/src/org/chromium/mojo/system/impl/CoreImpl.java", - "system/src/org/chromium/mojo/system/impl/WatcherImpl.java", + "src/org/chromium/mojo/system/impl/BaseRunLoop.java", + "src/org/chromium/mojo/system/impl/CoreImpl.java", + "src/org/chromium/mojo/system/impl/WatcherImpl.java", ] jni_package = "mojo" } -source_set("libsystem_java") { +source_set("native_support") { sources = [ - "system/base_run_loop.cc", - "system/core_impl.cc", - "system/watcher_impl.cc", + "base_run_loop.cc", + "core_impl.cc", + "watcher_impl.cc", ] deps = [ - ":system_java_jni_headers", + ":system_impl_java_jni_headers", "//base", "//mojo/public/c/system", "//mojo/public/cpp/system", ] } -android_library("system_java") { +android_library("system_impl_java") { java_files = [ - "system/src/org/chromium/mojo/system/impl/BaseRunLoop.java", - "system/src/org/chromium/mojo/system/impl/CoreImpl.java", - "system/src/org/chromium/mojo/system/impl/DataPipeConsumerHandleImpl.java", - "system/src/org/chromium/mojo/system/impl/DataPipeProducerHandleImpl.java", - "system/src/org/chromium/mojo/system/impl/HandleBase.java", - "system/src/org/chromium/mojo/system/impl/MessagePipeHandleImpl.java", - "system/src/org/chromium/mojo/system/impl/SharedBufferHandleImpl.java", - "system/src/org/chromium/mojo/system/impl/UntypedHandleImpl.java", - "system/src/org/chromium/mojo/system/impl/WatcherImpl.java", + "src/org/chromium/mojo/system/impl/BaseRunLoop.java", + "src/org/chromium/mojo/system/impl/CoreImpl.java", + "src/org/chromium/mojo/system/impl/DataPipeConsumerHandleImpl.java", + "src/org/chromium/mojo/system/impl/DataPipeProducerHandleImpl.java", + "src/org/chromium/mojo/system/impl/HandleBase.java", + "src/org/chromium/mojo/system/impl/MessagePipeHandleImpl.java", + "src/org/chromium/mojo/system/impl/SharedBufferHandleImpl.java", + "src/org/chromium/mojo/system/impl/UntypedHandleImpl.java", + "src/org/chromium/mojo/system/impl/WatcherImpl.java", ] deps = [ @@ -69,11 +69,34 @@ android_library("system_java") { ] } +# Targets should also depend on :test_support for the native side. +android_library("test_support_java") { + testonly = true + java_files = [ "javatests/src/org/chromium/mojo/MojoTestRule.java" ] + deps = [ + "//base:base_java", + "//third_party/junit", + ] +} + +source_set("test_support") { + testonly = true + sources = [ + "javatests/mojo_test_rule.cc", + ] + deps = [ + ":jni_headers", + "//base", + "//base/test:test_support", + "//mojo/core/embedder", + ] + defines = [ "UNIT_TEST" ] +} + android_library("mojo_javatests") { testonly = true java_files = [ "javatests/src/org/chromium/mojo/HandleMock.java", - "javatests/src/org/chromium/mojo/MojoTestRule.java", "javatests/src/org/chromium/mojo/TestUtils.java", "javatests/src/org/chromium/mojo/bindings/BindingsHelperTest.java", "javatests/src/org/chromium/mojo/bindings/BindingsTest.java", @@ -97,7 +120,8 @@ android_library("mojo_javatests") { ] deps = [ - ":system_java", + ":system_impl_java", + ":test_support_java", "//base:base_java", "//base:base_java_test_support", "//mojo/public/interfaces/bindings/tests:test_interfaces_java", @@ -119,18 +143,17 @@ shared_library("mojo_java_unittests") { sources = [ "javatests/init_library.cc", - "javatests/mojo_test_rule.cc", "javatests/validation_test_util.cc", ] deps = [ ":jni_headers", - ":libsystem_java", - ":system_java_jni_headers", + ":native_support", + ":system_impl_java_jni_headers", + ":test_support", "//base", "//base/test:test_support", - "//build/config:exe_and_shlib_deps", - "//mojo/edk", + "//mojo/core/embedder", "//mojo/public/cpp/bindings/tests:mojo_public_bindings_test_utils", "//mojo/public/cpp/test_support:test_utils", ] @@ -142,7 +165,7 @@ shared_library("mojo_java_unittests") { instrumentation_test_apk("mojo_test_apk") { deps = [ ":mojo_javatests", - ":system_java", + ":system_impl_java", "//base:base_java", "//mojo/public/interfaces/bindings/tests:test_interfaces", "//mojo/public/java:bindings_java", diff --git a/chromium/mojo/android/DEPS b/chromium/mojo/public/java/system/DEPS index c80012b5621..c80012b5621 100644 --- a/chromium/mojo/android/DEPS +++ b/chromium/mojo/public/java/system/DEPS diff --git a/chromium/mojo/android/system/base_run_loop.cc b/chromium/mojo/public/java/system/base_run_loop.cc index bfc170007cf..5b14852b130 100644 --- a/chromium/mojo/android/system/base_run_loop.cc +++ b/chromium/mojo/public/java/system/base_run_loop.cc @@ -59,7 +59,8 @@ static void JNI_BaseRunLoop_PostDelayedTask( runnable_ref.Reset(env, runnable); reinterpret_cast<base::MessageLoop*>(runLoopID) ->task_runner() - ->PostDelayedTask(FROM_HERE, base::Bind(&RunJavaRunnable, runnable_ref), + ->PostDelayedTask(FROM_HERE, + base::BindOnce(&RunJavaRunnable, runnable_ref), base::TimeDelta::FromMicroseconds(delay)); } diff --git a/chromium/mojo/android/system/core_impl.cc b/chromium/mojo/public/java/system/core_impl.cc index 33f0576a4c4..33f0576a4c4 100644 --- a/chromium/mojo/android/system/core_impl.cc +++ b/chromium/mojo/public/java/system/core_impl.cc diff --git a/chromium/mojo/android/javatests/AndroidManifest.xml b/chromium/mojo/public/java/system/javatests/AndroidManifest.xml index 37fc407e071..37fc407e071 100644 --- a/chromium/mojo/android/javatests/AndroidManifest.xml +++ b/chromium/mojo/public/java/system/javatests/AndroidManifest.xml diff --git a/chromium/mojo/public/java/system/javatests/DEPS b/chromium/mojo/public/java/system/javatests/DEPS new file mode 100644 index 00000000000..9243dcd6900 --- /dev/null +++ b/chromium/mojo/public/java/system/javatests/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+mojo/core/embedder", +] diff --git a/chromium/mojo/android/javatests/apk/.empty b/chromium/mojo/public/java/system/javatests/apk/.empty index e69de29bb2d..e69de29bb2d 100644 --- a/chromium/mojo/android/javatests/apk/.empty +++ b/chromium/mojo/public/java/system/javatests/apk/.empty diff --git a/chromium/mojo/android/javatests/init_library.cc b/chromium/mojo/public/java/system/javatests/init_library.cc index 2b2e81a5d19..2826d29b9d6 100644 --- a/chromium/mojo/android/javatests/init_library.cc +++ b/chromium/mojo/public/java/system/javatests/init_library.cc @@ -4,7 +4,7 @@ #include "base/android/base_jni_onload.h" #include "base/android/jni_android.h" -#include "mojo/edk/embedder/embedder.h" +#include "mojo/core/embedder/embedder.h" JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { base::android::InitVM(vm); @@ -12,6 +12,6 @@ JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { if (!base::android::OnJNIOnLoadInit()) return -1; - mojo::edk::Init(); + mojo::core::Init(); return JNI_VERSION_1_4; } diff --git a/chromium/mojo/android/javatests/mojo_test_rule.cc b/chromium/mojo/public/java/system/javatests/mojo_test_rule.cc index 6a7503cab08..67576bbfac1 100644 --- a/chromium/mojo/android/javatests/mojo_test_rule.cc +++ b/chromium/mojo/public/java/system/javatests/mojo_test_rule.cc @@ -14,6 +14,7 @@ #include "base/test/test_support_android.h" #include "base/threading/thread_task_runner_handle.h" #include "jni/MojoTestRule_jni.h" +#include "mojo/core/embedder/embedder.h" using base::android::JavaParamRef; @@ -31,6 +32,11 @@ struct TestEnvironment { namespace mojo { namespace android { +static void JNI_MojoTestRule_InitCore(JNIEnv* env, + const JavaParamRef<jclass>& clazz) { + mojo::core::Init(); +} + static void JNI_MojoTestRule_Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller) { base::InitAndroidTestMessageLoop(); diff --git a/chromium/mojo/android/javatests/validation_test_util.cc b/chromium/mojo/public/java/system/javatests/validation_test_util.cc index 83ba6796791..277a2d5b634 100644 --- a/chromium/mojo/android/javatests/validation_test_util.cc +++ b/chromium/mojo/public/java/system/javatests/validation_test_util.cc @@ -27,8 +27,8 @@ ScopedJavaLocalRef<jobject> JNI_ValidationTestUtil_ParseData( std::vector<uint8_t> data; size_t num_handles; std::string error_message; - if (!test::ParseValidationTestInput( - input, &data, &num_handles, &error_message)) { + if (!test::ParseValidationTestInput(input, &data, &num_handles, + &error_message)) { ScopedJavaLocalRef<jstring> j_error_message = base::android::ConvertUTF8ToJavaString(env, error_message); return Java_ValidationTestUtil_buildData(env, nullptr, 0, j_error_message); diff --git a/chromium/mojo/android/system/watcher_impl.cc b/chromium/mojo/public/java/system/watcher_impl.cc index b744416ff7b..36196487ebc 100644 --- a/chromium/mojo/android/system/watcher_impl.cc +++ b/chromium/mojo/public/java/system/watcher_impl.cc @@ -34,8 +34,8 @@ class WatcherImpl { jint signals) { java_watcher_.Reset(env, jcaller); - auto ready_callback = - base::Bind(&WatcherImpl::OnHandleReady, base::Unretained(this)); + auto ready_callback = base::BindRepeating(&WatcherImpl::OnHandleReady, + base::Unretained(this)); MojoResult result = watcher_.Watch(mojo::Handle(static_cast<MojoHandle>(mojo_handle)), diff --git a/chromium/mojo/public/js/lib/unicode.js b/chromium/mojo/public/js/lib/unicode.js index 6ed8839c3fb..67976ae4e6d 100644 --- a/chromium/mojo/public/js/lib/unicode.js +++ b/chromium/mojo/public/js/lib/unicode.js @@ -9,6 +9,8 @@ */ (function() { var internal = mojo.internal; + var textDecoder = new TextDecoder('utf-8'); + var textEncoder = new TextEncoder('utf-8'); /** * Decodes the UTF8 string from the given buffer. @@ -16,7 +18,7 @@ * @return {string} The corresponding JavaScript string. */ function decodeUtf8String(buffer) { - return decodeURIComponent(escape(String.fromCharCode.apply(null, buffer))); + return textDecoder.decode(buffer); } /** @@ -28,12 +30,11 @@ * @return {number} The number of bytes written to |outputBuffer|. */ function encodeUtf8String(str, outputBuffer) { - var utf8String = unescape(encodeURIComponent(str)); - if (outputBuffer.length < utf8String.length) + const utf8Buffer = textEncoder.encode(str); + if (outputBuffer.length < utf8Buffer.length) throw new Error("Buffer too small for encodeUtf8String"); - for (var i = 0; i < outputBuffer.length && i < utf8String.length; i++) - outputBuffer[i] = utf8String.charCodeAt(i); - return i; + outputBuffer.set(utf8Buffer); + return utf8Buffer.length; } /** @@ -41,8 +42,7 @@ * |str| would occupy. */ function utf8Length(str) { - var utf8String = unescape(encodeURIComponent(str)); - return utf8String.length; + return textEncoder.encode(str).length; } internal.decodeUtf8String = decodeUtf8String; diff --git a/chromium/mojo/public/mojom/base/file.mojom b/chromium/mojo/public/mojom/base/file.mojom index 824ebb1f133..9f08b26bea5 100644 --- a/chromium/mojo/public/mojom/base/file.mojom +++ b/chromium/mojo/public/mojom/base/file.mojom @@ -7,4 +7,5 @@ module mojo_base.mojom; // Corresponds to |base::File| in base/files/file.h struct File { handle fd; + bool async; }; diff --git a/chromium/mojo/public/mojom/base/file_info.mojom b/chromium/mojo/public/mojom/base/file_info.mojom index 544f2914387..5dff3ca38a6 100644 --- a/chromium/mojo/public/mojom/base/file_info.mojom +++ b/chromium/mojo/public/mojom/base/file_info.mojom @@ -4,5 +4,14 @@ module mojo_base.mojom; -[Native] -struct FileInfo; +import "mojo/public/mojom/base/time.mojom"; + +// Corresponds to base::File::Info. +struct FileInfo { + int64 size; + bool is_directory; + bool is_symbolic_link; + Time last_modified; + Time last_accessed; + Time creation_time; +}; diff --git a/chromium/mojo/public/mojom/base/logfont_win.mojom b/chromium/mojo/public/mojom/base/logfont_win.mojom index 81beefad726..27922b84782 100644 --- a/chromium/mojo/public/mojom/base/logfont_win.mojom +++ b/chromium/mojo/public/mojom/base/logfont_win.mojom @@ -5,5 +5,7 @@ module mojo_base.mojom; // Native Windows struct. Its typemap only exists for windows builds. -[Native] -struct LOGFONT; +[EnableIf=is_win] +struct LOGFONT { + array<uint8> bytes; +}; diff --git a/chromium/mojo/public/tools/bindings/blink_bindings_configuration.gni b/chromium/mojo/public/tools/bindings/blink_bindings_configuration.gni index 178394599ac..990d1337deb 100644 --- a/chromium/mojo/public/tools/bindings/blink_bindings_configuration.gni +++ b/chromium/mojo/public/tools/bindings/blink_bindings_configuration.gni @@ -7,6 +7,7 @@ variant = "blink" for_blink = true _typemap_imports = [ + "//device/gamepad/public/cpp/typemaps.gni", "//mojo/public/cpp/bindings/tests/blink_typemaps.gni", "//third_party/blink/renderer/platform/mojo/blink_typemaps.gni", "//third_party/blink/public/blink_typemaps.gni", diff --git a/chromium/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/chromium/mojo/public/tools/bindings/chromium_bindings_configuration.gni index 0f508ca2e5b..1893db1ac95 100644 --- a/chromium/mojo/public/tools/bindings/chromium_bindings_configuration.gni +++ b/chromium/mojo/public/tools/bindings/chromium_bindings_configuration.gni @@ -8,7 +8,10 @@ _typemap_imports = [ "//chrome/common/importer/typemaps.gni", "//chrome/common/media_router/mojo/typemaps.gni", "//chrome/typemaps.gni", + "//chromecast/common/mojom/typemaps.gni", + "//chromeos/typemaps.gni", "//chromeos/services/device_sync/public/mojom/typemaps.gni", + "//chromeos/services/secure_channel/public/mojom/typemaps.gni", "//components/arc/common/typemaps.gni", "//components/metrics/public/cpp/typemaps.gni", "//components/sync/mojo/typemaps.gni", @@ -20,8 +23,9 @@ _typemap_imports = [ "//content/public/common/typemaps.gni", "//device/bluetooth/public/mojom/typemaps.gni", "//device/bluetooth/public/mojom/test/typemaps.gni", - "//device/gamepad/public/mojom/typemaps.gni", + "//device/gamepad/public/cpp/typemaps.gni", "//gpu/ipc/common/typemaps.gni", + "//ipc/typemaps.gni", "//media/capture/mojom/typemaps.gni", "//media/mojo/interfaces/typemaps.gni", "//mojo/public/cpp/base/typemaps.gni", diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl index f0ea917a265..72c3101c142 100644 --- a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl @@ -142,6 +142,11 @@ bool {{proxy_name}}::{{method.name}}( "message")}} {%- endif %} +#if defined(ENABLE_IPC_FUZZER) + message.set_interface_name({{class_name}}::Name_); + message.set_method_name("{{method.name}}"); +#endif + bool result = false; std::unique_ptr<mojo::MessageReceiver> responder( new {{class_name}}_{{method.name}}_HandleSyncResponse( @@ -180,6 +185,11 @@ void {{proxy_name}}::{{method.name}}( "message")}} {%- endif %} +#if defined(ENABLE_IPC_FUZZER) + message.set_interface_name({{class_name}}::Name_); + message.set_method_name("{{method.name}}"); +#endif + {%- if method.response_parameters != None %} std::unique_ptr<mojo::MessageReceiver> responder( new {{class_name}}_{{method.name}}_ForwardToCallback( @@ -329,6 +339,12 @@ void {{class_name}}_{{method.name}}_ProxyToResponder::Run( TRACE_EVENT1("mojom", "(Impl){{namespace_as_string}}::{{class_name}}::{{method.name}}Callback", "message", message.name()); #endif + +#if defined(ENABLE_IPC_FUZZER) + message.set_interface_name({{class_name}}::Name_); + message.set_method_name("{{method.name}}"); +#endif + message.set_request_id(request_id_); ignore_result(responder_->Accept(&message)); // TODO(darin): Accept() returning false indicates a malformed message, and diff --git a/chromium/mojo/public/tools/bindings/generators/js_templates/externs/interface_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/js_templates/externs/interface_definition.tmpl index 9f9b9f1ebd3..672b5d5d893 100644 --- a/chromium/mojo/public/tools/bindings/generators/js_templates/externs/interface_definition.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/js_templates/externs/interface_definition.tmpl @@ -1,6 +1,6 @@ -/** @implements {mojo.InterfacePtr} */ -{{module.namespace}}.{{interface.name}}Ptr = class { -{% for method in interface.methods %} +{% macro generateInterfaceClass() -%} +class { +{%- for method in interface.methods %} /** {%- for parameter in method.parameters %} * @param { {{parameter.kind|closure_type_with_nullability}} } {{parameter.name|sanitize_identifier}} @@ -9,16 +9,26 @@ * @return {Promise} {%- endif %} */ - {{method.name}}( + {{method.name}}( {%- for parameter in method.parameters -%} {{parameter.name|sanitize_identifier}}{% if not loop.last %}, {% endif %} {%- endfor -%} ) {} {%- endfor %} }; +{%- endmacro %} /** * @const - * @type { mojo.Interface }; + * @type { !mojo.Interface }; */ {{module.namespace}}.{{interface.name}}; + +/** @interface */ +{{module.namespace}}.{{interface.name}}Impl = {{ generateInterfaceClass() }} + +/** + * @implements { mojo.InterfacePtr } + * @implements { {{module.namespace}}.{{interface.name}}Impl } + */ +{{module.namespace}}.{{interface.name}}Ptr = {{ generateInterfaceClass() }} diff --git a/chromium/mojo/public/tools/bindings/generators/js_templates/externs/module.externs.tmpl b/chromium/mojo/public/tools/bindings/generators/js_templates/externs/module.externs.tmpl index 8eccefbfecf..fd711068f26 100644 --- a/chromium/mojo/public/tools/bindings/generators/js_templates/externs/module.externs.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/js_templates/externs/module.externs.tmpl @@ -5,12 +5,27 @@ {% for declaration in module.namespace|namespace_declarations -%} /** @const */ {%- if loop.first %} -let {{declaration}} = {}; +var {{declaration}} = {}; {% else %} {{declaration}} = {}; {% endif -%} {%- endfor -%} +{#--- Constant definitions #} +{%- for constant in module.constants %} +/** @type { {{constant.kind|closure_type_with_nullability }} } */ +{{module.namespace}}.{{constant.name}}; +{%- endfor %} + +{#--- Enum definitions #} +{% for enum in enums %} +/** @enum {number} */ +{{module.namespace}}.{{enum.name}} = {}; +{%- for field in enum.fields %} +{{module.namespace}}.{{enum.name}}.{{field.name}}; +{%- endfor %} +{%- endfor %} + {#--- Interface definitions #} {%- for interface in interfaces -%} {%- include "externs/interface_definition.tmpl" %} diff --git a/chromium/mojo/public/tools/bindings/generators/js_templates/fuzzing.tmpl b/chromium/mojo/public/tools/bindings/generators/js_templates/fuzzing.tmpl index fb535fe03af..a91260ed9ac 100644 --- a/chromium/mojo/public/tools/bindings/generators/js_templates/fuzzing.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/js_templates/fuzzing.tmpl @@ -114,9 +114,9 @@ idx = {{name}}.setHandlesInternal_(handles, idx) {%- elif kind|is_enum_kind -%} {{generate_or_mutate_enum(obj, operation, kind, name)}} {%- elif kind|is_struct_kind -%} -{{build_call(obj, operation, 'Struct', kind.module.namespace ~ '.' ~ kind.name, kind.is_nullable|to_js_boolean)}} +{{build_call(obj, operation, 'Struct', name, kind.module.namespace ~ '.' ~ kind.name, kind.is_nullable|to_js_boolean)}} {%- elif kind|is_union_kind -%} -{{build_call(obj, operation, 'Union', kind.module.namespace ~ '.' ~ kind.name, kind.is_nullable|to_js_boolean)}} +{{build_call(obj, operation, 'Union', name, kind.module.namespace ~ '.' ~ kind.name, kind.is_nullable|to_js_boolean)}} {%- elif kind|is_array_kind -%} {{generate_or_mutate_array(obj, operation, kind, name)}} {%- elif kind|is_map_kind -%} diff --git a/chromium/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl index 499c786e2e1..ad857b2c25c 100644 --- a/chromium/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl @@ -64,13 +64,13 @@ function {{union.name}}(value) { { field: "{{field.name}}", - mutator: function() { return {{generate_or_mutate('mutator_', 'mutate', field.kind, 'this.' ~ field.name)|indent(6)}}; }, + mutator: function(val) { return {{generate_or_mutate('mutator_', 'mutate', field.kind, 'val.' ~ field.name)|indent(6)}}; }, }, {%- endfor %} ]; var result = mutator_.mutateUnionField(this, mutators); - generated[result.field] = result.value; + this[result.field] = result.value; return this; } diff --git a/chromium/mojo/public/tools/bindings/mojom.gni b/chromium/mojo/public/tools/bindings/mojom.gni index a38262322e6..3329631470f 100644 --- a/chromium/mojo/public/tools/bindings/mojom.gni +++ b/chromium/mojo/public/tools/bindings/mojom.gni @@ -389,7 +389,7 @@ template("mojom") { } else if (is_chromeos) { enabled_features += [ "is_chromeos" ] } else if (is_fuchsia) { - enabled_features += [ "is_fuschia" ] + enabled_features += [ "is_fuchsia" ] } else if (is_ios) { enabled_features += [ "is_ios" ] } else if (is_linux) { diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py index 66f89540126..653a2dfc74d 100644 --- a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py +++ b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py @@ -4,27 +4,14 @@ # Based on third_party/WebKit/Source/build/scripts/template_expander.py. -import imp import os.path import sys -# Disable lint check for finding modules: -# pylint: disable=F0401 - -def _GetDirAbove(dirname): - """Returns the directory "above" this file containing |dirname| (which must - also be "above" this file).""" - path = os.path.abspath(__file__) - while True: - path, tail = os.path.split(path) - assert tail - if tail == dirname: - return path - -try: - imp.find_module("jinja2") -except ImportError: - sys.path.append(os.path.join(_GetDirAbove("mojo"), "third_party")) +_current_dir = os.path.dirname(os.path.realpath(__file__)) +# jinja2 is in chromium's third_party directory +# Insert at front to override system libraries, and after path[0] == script dir +sys.path.insert( + 1, os.path.join(_current_dir, *([os.pardir] * 7 + ['third_party']))) import jinja2 diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/parse/parser.py b/chromium/mojo/public/tools/bindings/pylib/mojom/parse/parser.py index 916d69d2242..b9f10dce9e3 100644 --- a/chromium/mojo/public/tools/bindings/pylib/mojom/parse/parser.py +++ b/chromium/mojo/public/tools/bindings/pylib/mojom/parse/parser.py @@ -4,24 +4,12 @@ """Generates a syntax tree from a Mojo IDL file.""" -import imp import os.path import sys -def _GetDirAbove(dirname): - """Returns the directory "above" this file containing |dirname| (which must - also be "above" this file).""" - path = os.path.abspath(__file__) - while True: - path, tail = os.path.split(path) - assert tail - if tail == dirname: - return path - -try: - imp.find_module("ply") -except ImportError: - sys.path.append(os.path.join(_GetDirAbove("mojo"), "third_party")) +_current_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.insert( + 1, os.path.join(_current_dir, *([os.pardir] * 7 + ['third_party']))) from ply import lex from ply import yacc diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py index 6822cbc8d0d..36d8c4abfe8 100644 --- a/chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py +++ b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py @@ -17,10 +17,7 @@ def _GetDirAbove(dirname): if tail == dirname: return path -try: - imp.find_module("ply") -except ImportError: - sys.path.append(os.path.join(_GetDirAbove("mojo"), "third_party")) +sys.path.insert(1, os.path.join(_GetDirAbove("mojo"), "third_party")) from ply import lex try: diff --git a/chromium/mojo/public/tools/fuzzers/BUILD.gn b/chromium/mojo/public/tools/fuzzers/BUILD.gn index 00d09f1969e..69531552db6 100644 --- a/chromium/mojo/public/tools/fuzzers/BUILD.gn +++ b/chromium/mojo/public/tools/fuzzers/BUILD.gn @@ -26,7 +26,7 @@ fuzzer_test("mojo_parse_message_fuzzer") { ] deps = [ ":fuzz_mojom", - "//mojo/edk", + "//mojo/core/embedder", ] seed_corpus = "//mojo/public/tools/fuzzers/message_corpus" } @@ -41,8 +41,7 @@ if (!is_win) { deps = [ ":fuzz_mojom", "//base", - "//build/config:exe_and_shlib_deps", - "//mojo/edk", + "//mojo/core/embedder", ] } } @@ -55,7 +54,7 @@ fuzzer_test("mojo_parse_message_proto_fuzzer") { deps = [ ":fuzz_mojom", ":mojo_fuzzer_proto", - "//mojo/edk", + "//mojo/core/embedder", "//third_party/libprotobuf-mutator", ] seed_corpus = "//mojo/public/tools/fuzzers/mojo_parse_message_proto_corpus" diff --git a/chromium/mojo/public/tools/fuzzers/DEPS b/chromium/mojo/public/tools/fuzzers/DEPS index 909a8338dae..9243dcd6900 100644 --- a/chromium/mojo/public/tools/fuzzers/DEPS +++ b/chromium/mojo/public/tools/fuzzers/DEPS @@ -1,3 +1,3 @@ include_rules = [ - "+mojo/edk", + "+mojo/core/embedder", ] diff --git a/chromium/mojo/public/tools/fuzzers/mojo_fuzzer_message_dump.cc b/chromium/mojo/public/tools/fuzzers/mojo_fuzzer_message_dump.cc index b1bdb95a6c2..62d8d87b29d 100644 --- a/chromium/mojo/public/tools/fuzzers/mojo_fuzzer_message_dump.cc +++ b/chromium/mojo/public/tools/fuzzers/mojo_fuzzer_message_dump.cc @@ -10,7 +10,7 @@ #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "base/task_scheduler/task_scheduler.h" -#include "mojo/edk/embedder/embedder.h" +#include "mojo/core/embedder/embedder.h" #include "mojo/public/tools/fuzzers/fuzz.mojom.h" #include "mojo/public/tools/fuzzers/fuzz_impl.h" @@ -21,7 +21,7 @@ struct Environment { Environment() : message_loop() { base::TaskScheduler::CreateAndStartWithDefaultParams( "MojoFuzzerMessageDumpProcess"); - mojo::edk::Init(); + mojo::core::Init(); } /* Message loop to send messages on. */ diff --git a/chromium/mojo/public/tools/fuzzers/mojo_parse_message_fuzzer.cc b/chromium/mojo/public/tools/fuzzers/mojo_parse_message_fuzzer.cc index 0adb6844462..ba0f15015f5 100644 --- a/chromium/mojo/public/tools/fuzzers/mojo_parse_message_fuzzer.cc +++ b/chromium/mojo/public/tools/fuzzers/mojo_parse_message_fuzzer.cc @@ -5,7 +5,7 @@ #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/task_scheduler/task_scheduler.h" -#include "mojo/edk/embedder/embedder.h" +#include "mojo/core/embedder/embedder.h" #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/tools/fuzzers/fuzz_impl.h" @@ -38,7 +38,7 @@ struct Environment { Environment() : message_loop(base::MessageLoop::TYPE_UI) { base::TaskScheduler::CreateAndStartWithDefaultParams( "MojoParseMessageFuzzerProcess"); - mojo::edk::Init(); + mojo::core::Init(); } /* Message loop to send and handle messages on. */ diff --git a/chromium/mojo/public/tools/fuzzers/mojo_parse_message_proto_fuzzer.cc b/chromium/mojo/public/tools/fuzzers/mojo_parse_message_proto_fuzzer.cc index 6772541db7e..69c70bea32c 100644 --- a/chromium/mojo/public/tools/fuzzers/mojo_parse_message_proto_fuzzer.cc +++ b/chromium/mojo/public/tools/fuzzers/mojo_parse_message_proto_fuzzer.cc @@ -8,7 +8,7 @@ #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/task_scheduler/task_scheduler.h" -#include "mojo/edk/embedder/embedder.h" +#include "mojo/core/embedder/embedder.h" #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/tools/fuzzers/fuzz_impl.h" #include "mojo/public/tools/fuzzers/mojo_fuzzer.pb.h" @@ -49,7 +49,7 @@ struct Environment { Environment() : message_loop(base::MessageLoop::TYPE_UI) { base::TaskScheduler::CreateAndStartWithDefaultParams( "MojoParseMessageFuzzerProcess"); - mojo::edk::Init(); + mojo::core::Init(); } // Message loop to send and handle messages on. |