diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2022-11-28 16:14:41 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2022-12-13 15:19:41 +0000 |
commit | 61d9742824d54be5693191fe502325a909feca59 (patch) | |
tree | cbf28e779b11338fe52eb75b915684cd8955542c /chromium/third_party/ipcz | |
parent | 45f9ded08bb7526984b24ccb5a5327aaf6821676 (diff) | |
download | qtwebengine-chromium-61d9742824d54be5693191fe502325a909feca59.tar.gz |
BASELINE: Update Chromium to 108.0.5359.70
Change-Id: I77334ff232b819600f275bd3cfe41fbaa3619230
Reviewed-on: https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/445904
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/third_party/ipcz')
165 files changed, 3027 insertions, 1336 deletions
diff --git a/chromium/third_party/ipcz/BUILD.gn b/chromium/third_party/ipcz/BUILD.gn index 00ee98279b3..fc14e13e69d 100644 --- a/chromium/third_party/ipcz/BUILD.gn +++ b/chromium/third_party/ipcz/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2022 The Chromium Authors. All rights reserved. +# Copyright 2022 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/chromium/third_party/ipcz/DIR_METADATA b/chromium/third_party/ipcz/DIR_METADATA new file mode 100644 index 00000000000..dc2b868ccd8 --- /dev/null +++ b/chromium/third_party/ipcz/DIR_METADATA @@ -0,0 +1,11 @@ +# Metadata information for this directory. +# +# For more information on DIR_METADATA files, see: +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md +# +# For the schema of this file, see Metadata message: +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto + +monorail { + component: "Internals>Mojo>Core" +} diff --git a/chromium/third_party/ipcz/LICENSE b/chromium/third_party/ipcz/LICENSE index 794df6b5591..57c22a518fc 100644 --- a/chromium/third_party/ipcz/LICENSE +++ b/chromium/third_party/ipcz/LICENSE @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. -// * Neither the name of Google Inc. nor the names of its +// * Neither the name of Google LLC nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // diff --git a/chromium/third_party/ipcz/build_overrides/build.gni b/chromium/third_party/ipcz/build_overrides/build.gni index c406776d3e2..be40719702e 100644 --- a/chromium/third_party/ipcz/build_overrides/build.gni +++ b/chromium/third_party/ipcz/build_overrides/build.gni @@ -1,4 +1,4 @@ -# Copyright 2022 The Chromium Authors. All rights reserved. +# Copyright 2022 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/chromium/third_party/ipcz/build_overrides/gtest.gni b/chromium/third_party/ipcz/build_overrides/gtest.gni index 5c2ab79cede..e0d128ff301 100644 --- a/chromium/third_party/ipcz/build_overrides/gtest.gni +++ b/chromium/third_party/ipcz/build_overrides/gtest.gni @@ -1,4 +1,4 @@ -# Copyright 2022 The Chromium Authors. All rights reserved. +# Copyright 2022 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/chromium/third_party/ipcz/build_overrides/ipcz.gni b/chromium/third_party/ipcz/build_overrides/ipcz.gni index 3c630c5bf4c..c4055c317f6 100644 --- a/chromium/third_party/ipcz/build_overrides/ipcz.gni +++ b/chromium/third_party/ipcz/build_overrides/ipcz.gni @@ -1,4 +1,4 @@ -# Copyright 2022 The Chromium Authors. All rights reserved. +# Copyright 2022 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/chromium/third_party/ipcz/include/ipcz/ipcz.h b/chromium/third_party/ipcz/include/ipcz/ipcz.h index c8c3b532ca6..cffbc11f784 100644 --- a/chromium/third_party/ipcz/include/ipcz/ipcz.h +++ b/chromium/third_party/ipcz/include/ipcz/ipcz.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -437,6 +437,18 @@ struct IPCZ_ALIGN(8) IpczDriver { uint32_t flags, // in const void* options); // in + // The ipcz Reject() API can be used by an application to reject a specific + // parcel received from a portal. If the parcel in question came from a + // remote node, ipcz invokes ReportBadTransportActivity() to notify the driver + // about the `transport` which delivered the rejected parcel. + // + // `context` is an opaque value passed by the application to the Reject() call + // which elicited this invocation. + IpczResult(IPCZ_API* ReportBadTransportActivity)(IpczDriverHandle transport, + uintptr_t context, + uint32_t flags, + const void* options); + // Allocates a shared memory region and returns a driver handle in // `driver_memory` which can be used to reference it in other calls to the // driver. @@ -729,6 +741,18 @@ typedef uint32_t IpczTrapConditionFlags; // by any amount. Edge-triggered. #define IPCZ_TRAP_CONSUMED_REMOTE_PARCEL IPCZ_FLAG_BIT(8) +// Indicates that the trap event is being fired from within the extent of an +// ipcz API call (i.e., as opposed to being fired from within the extent of an +// incoming driver transport notification.) For example if a trap is monitoring +// a portal for incoming parcels, and the application puts a parcel into the +// portal's peer on the same node, the trap event will be fired within the +// extent of the corresponding Put() call, and this flag will be set on the +// event. +// +// This flag is ignored when specifying conditions to watch for Trap(), and it +// may be set on any event dispatched to an IpczTrapEventHandler. +#define IPCZ_TRAP_WITHIN_API_CALL IPCZ_FLAG_BIT(9) + // A structure describing portal conditions necessary to trigger a trap and // invoke its event handler. struct IPCZ_ALIGN(8) IpczTrapConditions { @@ -1225,6 +1249,11 @@ struct IPCZ_ALIGN(8) IpczAPI { // (`num_bytes` and `num_handles`) are still updated as specified in the // IPCZ_RESULT_OK details below. // + // If this call succeeds and `validator` is non-null, it's populated with a + // new validator handle which the application can use to report + // application-level validation failures regarding this specific transaction. + // See Reject(). + // // `options` is ignored and must be null. // // Returns: @@ -1258,13 +1287,14 @@ struct IPCZ_ALIGN(8) IpczAPI { // // IPCZ_RESULT_ALREADY_EXISTS if there is a two-phase get operation in // progress on `portal`. - IpczResult(IPCZ_API* Get)(IpczHandle portal, // in - IpczGetFlags flags, // in - const void* options, // in - void* data, // out - size_t* num_bytes, // in/out - IpczHandle* handles, // out - size_t* num_handles); // in/out + IpczResult(IPCZ_API* Get)(IpczHandle portal, // in + IpczGetFlags flags, // in + const void* options, // in + void* data, // out + size_t* num_bytes, // in/out + IpczHandle* handles, // out + size_t* num_handles, // in/out + IpczHandle* validator); // out // Begins a two-phase get operation on `portal` to retrieve data and handles. // While a two-phase get operation is in progress on a portal, all other get @@ -1337,6 +1367,11 @@ struct IPCZ_ALIGN(8) IpczAPI { // operation in progress on `portal`, all other arguments are ignored and the // pending operation is cancelled without consuming any data from the portal. // + // If this call succeeds (without IPCZ_END_GET_ABORT specified) and + // `validator` is non-null, it's populated with a new validator handle which + // the application can use to report application-level validation failures + // regarding this specific transaction. See Reject(). + // // `options` is unused and must be null. // // Returns: @@ -1360,7 +1395,8 @@ struct IPCZ_ALIGN(8) IpczAPI { size_t num_handles, // in IpczEndGetFlags flags, // in const void* options, // in - IpczHandle* handles); // out + IpczHandle* handles, // out + IpczHandle* validator); // out // Attempts to install a trap to catch interesting changes to a portal's // state. The condition(s) to observe are specified in `conditions`. @@ -1418,6 +1454,34 @@ struct IPCZ_ALIGN(8) IpczAPI { IpczTrapConditionFlags* satisfied_condition_flags, // out struct IpczPortalStatus* status); // out + // Reports an application-level validation failure to ipcz, in reference to + // a specific `validator` returned by a previous call to Get() or EndGet(). + // ipcz propagates this rejection to the driver via + // ReportBadTransportActivity(), if and only if the associated parcel did in + // fact come from a remote node. + // + // `context` is an opaque handle which, on success, is passed to the driver + // when issuing a corresponding ReportBadTransportActivity() invocation. + // + // `flags` is ignored and must be 0. + // + // `options` is ignored and must be null. + // + // Returns: + // + // IPCZ_RESULT_OK if the driver was successfully notified about this + // rejection via ReportBadTransportActivity(). + // + // IPCZ_RESULT_INVALID_ARGUMENT if `validator` is not a valid validator + // handle previously returned by Get() or EndGet(). + // + // IPCZ_RESULT_FAILED_PRECONDITION if `validator` is associated with a + // parcel that did not come from another node. + IpczResult(IPCZ_API* Reject)(IpczHandle validator, + uintptr_t context, + uint32_t flags, + const void* options); + // Boxes an object managed by a node's driver and returns a new IpczHandle to // reference the box. If the driver is able to serialize the boxed object, the // box can be placed into a portal for transmission to the other side. diff --git a/chromium/third_party/ipcz/src/BUILD.gn b/chromium/third_party/ipcz/src/BUILD.gn index 4fa9f4fd751..8b98d75aaa8 100644 --- a/chromium/third_party/ipcz/src/BUILD.gn +++ b/chromium/third_party/ipcz/src/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2022 The Chromium Authors. All rights reserved. +# Copyright 2022 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -138,19 +138,17 @@ ipcz_source_set("reference_drivers") { public = [ "reference_drivers/async_reference_driver.h", - "reference_drivers/blob.h", + "reference_drivers/single_process_reference_driver_base.h", "reference_drivers/sync_reference_driver.h", ] sources = [ "reference_drivers/async_reference_driver.cc", - "reference_drivers/blob.cc", "reference_drivers/object.cc", "reference_drivers/object.h", "reference_drivers/random.cc", "reference_drivers/random.h", "reference_drivers/single_process_reference_driver_base.cc", - "reference_drivers/single_process_reference_driver_base.h", "reference_drivers/sync_reference_driver.cc", ] @@ -230,6 +228,8 @@ ipcz_source_set("impl") { "ipcz/node_link_memory.h", "ipcz/node_messages.h", "ipcz/node_name.h", + "ipcz/node_type.h", + "ipcz/operation_context.h", "ipcz/parcel.h", "ipcz/parcel_queue.h", "ipcz/portal.h", @@ -243,9 +243,12 @@ ipcz_source_set("impl") { "ipcz/sequenced_queue.h", "ipcz/sublink_id.h", "ipcz/test_messages.h", + "ipcz/validator.h", ] sources = [ "ipcz/api_object.cc", + "ipcz/atomic_queue_state.cc", + "ipcz/atomic_queue_state.h", "ipcz/block_allocator.cc", "ipcz/block_allocator_pool.cc", "ipcz/block_allocator_pool.h", @@ -272,6 +275,7 @@ ipcz_source_set("impl") { "ipcz/message_macros/message_params_declaration_macros.h", "ipcz/message_macros/message_params_declaration_macros.h", "ipcz/message_macros/undef_message_macros.h", + "ipcz/monitored_atomic.h", "ipcz/node.cc", "ipcz/node_connector.cc", "ipcz/node_link.cc", @@ -295,6 +299,7 @@ ipcz_source_set("impl") { "ipcz/trap_event_dispatcher.h", "ipcz/trap_set.cc", "ipcz/trap_set.h", + "ipcz/validator.cc", ] public_deps = [ ":ipcz_header", @@ -387,6 +392,7 @@ ipcz_source_set("ipcz_tests_sources") { "remote_portal_test.cc", "trap_test.cc", "util/ref_counted_test.cc", + "util/safe_math_test.cc", "util/stack_trace_test.cc", ] diff --git a/chromium/third_party/ipcz/src/api.cc b/chromium/third_party/ipcz/src/api.cc index c398f25da7c..74cf5b1134c 100644 --- a/chromium/third_party/ipcz/src/api.cc +++ b/chromium/third_party/ipcz/src/api.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -13,6 +13,7 @@ #include "ipcz/node_link_memory.h" #include "ipcz/portal.h" #include "ipcz/router.h" +#include "ipcz/validator.h" #include "util/ref_counted.h" extern "C" { @@ -215,12 +216,13 @@ IpczResult Get(IpczHandle portal_handle, void* data, size_t* num_bytes, IpczHandle* handles, - size_t* num_handles) { + size_t* num_handles, + IpczHandle* validator) { ipcz::Portal* portal = ipcz::Portal::FromHandle(portal_handle); if (!portal) { return IPCZ_RESULT_INVALID_ARGUMENT; } - return portal->Get(flags, data, num_bytes, handles, num_handles); + return portal->Get(flags, data, num_bytes, handles, num_handles, validator); } IpczResult BeginGet(IpczHandle portal_handle, @@ -242,7 +244,8 @@ IpczResult EndGet(IpczHandle portal_handle, size_t num_handles, IpczEndGetFlags flags, const void* options, - IpczHandle* handles) { + IpczHandle* handles, + IpczHandle* validator) { ipcz::Portal* portal = ipcz::Portal::FromHandle(portal_handle); if (!portal) { return IPCZ_RESULT_INVALID_ARGUMENT; @@ -256,7 +259,7 @@ IpczResult EndGet(IpczHandle portal_handle, } return portal->CommitGet(num_bytes_consumed, - absl::MakeSpan(handles, num_handles)); + absl::MakeSpan(handles, num_handles), validator); } IpczResult Trap(IpczHandle portal_handle, @@ -281,6 +284,18 @@ IpczResult Trap(IpczHandle portal_handle, satisfied_condition_flags, status); } +IpczResult Reject(IpczHandle validator_handle, + uintptr_t context, + uint32_t flags, + const void* options) { + ipcz::Validator* validator = ipcz::Validator::FromHandle(validator_handle); + if (!validator) { + return IPCZ_RESULT_INVALID_ARGUMENT; + } + + return validator->Reject(context); +} + IpczResult Box(IpczHandle node_handle, IpczDriverHandle driver_handle, uint32_t flags, @@ -334,6 +349,7 @@ constexpr IpczAPI kCurrentAPI = { BeginGet, EndGet, Trap, + Reject, Box, Unbox, }; diff --git a/chromium/third_party/ipcz/src/api.h b/chromium/third_party/ipcz/src/api.h index 6a6bee21dae..3a02c431630 100644 --- a/chromium/third_party/ipcz/src/api.h +++ b/chromium/third_party/ipcz/src/api.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/api_test.cc b/chromium/third_party/ipcz/src/api_test.cc index 4bf73ab35f2..88275cfe773 100644 --- a/chromium/third_party/ipcz/src/api_test.cc +++ b/chromium/third_party/ipcz/src/api_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,6 +6,7 @@ #include <string> #include "ipcz/ipcz.h" +#include "reference_drivers/single_process_reference_driver_base.h" #include "reference_drivers/sync_reference_driver.h" #include "test/test.h" #include "testing/gtest/include/gtest/gtest.h" @@ -233,8 +234,9 @@ TEST_F(APITest, PutGet) { // Get from an empty portal. char data[4]; size_t num_bytes = 4; - EXPECT_EQ(IPCZ_RESULT_UNAVAILABLE, ipcz().Get(b, IPCZ_NO_FLAGS, nullptr, data, - &num_bytes, nullptr, nullptr)); + EXPECT_EQ(IPCZ_RESULT_UNAVAILABLE, + ipcz().Get(b, IPCZ_NO_FLAGS, nullptr, data, &num_bytes, nullptr, + nullptr, nullptr)); // A portal can't transfer itself or its peer. EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT, @@ -264,18 +266,18 @@ TEST_F(APITest, PutGet) { num_bytes = 0; EXPECT_EQ(IPCZ_RESULT_RESOURCE_EXHAUSTED, ipcz().Get(b, IPCZ_NO_FLAGS, nullptr, data, &num_bytes, nullptr, - nullptr)); + nullptr, nullptr)); EXPECT_EQ(2u, num_bytes); num_bytes = 4; EXPECT_EQ(IPCZ_RESULT_OK, ipcz().Get(b, IPCZ_NO_FLAGS, nullptr, data, - &num_bytes, nullptr, nullptr)); + &num_bytes, nullptr, nullptr, nullptr)); EXPECT_EQ(2u, num_bytes); EXPECT_EQ("hi", std::string(data, 2)); num_bytes = 4; EXPECT_EQ(IPCZ_RESULT_OK, ipcz().Get(b, IPCZ_NO_FLAGS, nullptr, data, - &num_bytes, nullptr, nullptr)); + &num_bytes, nullptr, nullptr, nullptr)); EXPECT_EQ(3u, num_bytes); EXPECT_EQ("bye", std::string(data, 3)); @@ -286,7 +288,7 @@ TEST_F(APITest, PutGet) { // Getting an empty parcel requires no storage. EXPECT_EQ(IPCZ_RESULT_OK, ipcz().Get(b, IPCZ_NO_FLAGS, nullptr, nullptr, - nullptr, nullptr, nullptr)); + nullptr, nullptr, nullptr, nullptr)); EXPECT_EQ(IPCZ_RESULT_OK, ipcz().QueryPortalStatus(b, IPCZ_NO_FLAGS, nullptr, &status)); EXPECT_EQ(1u, status.num_local_parcels); @@ -295,11 +297,11 @@ TEST_F(APITest, PutGet) { // Insufficient handle storage. EXPECT_EQ(IPCZ_RESULT_RESOURCE_EXHAUSTED, ipcz().Get(b, IPCZ_NO_FLAGS, nullptr, nullptr, nullptr, nullptr, - nullptr)); + nullptr, nullptr)); size_t num_handles = 1; EXPECT_EQ(IPCZ_RESULT_OK, ipcz().Get(b, IPCZ_NO_FLAGS, nullptr, nullptr, - nullptr, &d, &num_handles)); + nullptr, &d, &num_handles, nullptr)); EXPECT_EQ(1u, num_handles); Close(d); @@ -403,22 +405,23 @@ TEST_F(APITest, BeginEndGetFailure) { // Invalid handle. EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT, ipcz().EndGet(IPCZ_INVALID_HANDLE, 0, 0, IPCZ_NO_FLAGS, nullptr, - nullptr)); + nullptr, nullptr)); // Non-zero handle count with null handle buffer. EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT, - ipcz().EndGet(a, 0, 1, IPCZ_NO_FLAGS, nullptr, nullptr)); + ipcz().EndGet(a, 0, 1, IPCZ_NO_FLAGS, nullptr, nullptr, nullptr)); // Data size out of range. - EXPECT_EQ( - IPCZ_RESULT_OUT_OF_RANGE, - ipcz().EndGet(a, num_bytes + 1, 0, IPCZ_NO_FLAGS, nullptr, nullptr)); + EXPECT_EQ(IPCZ_RESULT_OUT_OF_RANGE, + ipcz().EndGet(a, num_bytes + 1, 0, IPCZ_NO_FLAGS, nullptr, nullptr, + nullptr)); // Two-phase Get not in progress. - EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().EndGet(a, num_bytes, 0, IPCZ_NO_FLAGS, nullptr, nullptr)); - EXPECT_EQ(IPCZ_RESULT_FAILED_PRECONDITION, - ipcz().EndGet(a, num_bytes, 0, IPCZ_NO_FLAGS, nullptr, nullptr)); + EXPECT_EQ(IPCZ_RESULT_OK, ipcz().EndGet(a, num_bytes, 0, IPCZ_NO_FLAGS, + nullptr, nullptr, nullptr)); + EXPECT_EQ( + IPCZ_RESULT_FAILED_PRECONDITION, + ipcz().EndGet(a, num_bytes, 0, IPCZ_NO_FLAGS, nullptr, nullptr, nullptr)); CloseAll({a, b, node}); } @@ -443,15 +446,15 @@ TEST_F(APITest, TwoPhasePutGet) { EXPECT_EQ(kMessage[0], *reinterpret_cast<const char*>(in_data)); EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().EndGet(b, 1, 0, IPCZ_NO_FLAGS, nullptr, nullptr)); + ipcz().EndGet(b, 1, 0, IPCZ_NO_FLAGS, nullptr, nullptr, nullptr)); EXPECT_EQ(IPCZ_RESULT_OK, ipcz().BeginGet(b, IPCZ_NO_FLAGS, nullptr, &in_data, &num_bytes, nullptr)); EXPECT_EQ( kMessage.substr(1), std::string_view(reinterpret_cast<const char*>(in_data), num_bytes)); - EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().EndGet(b, num_bytes, 0, IPCZ_NO_FLAGS, nullptr, nullptr)); + EXPECT_EQ(IPCZ_RESULT_OK, ipcz().EndGet(b, num_bytes, 0, IPCZ_NO_FLAGS, + nullptr, nullptr, nullptr)); EXPECT_EQ( IPCZ_RESULT_UNAVAILABLE, @@ -500,6 +503,80 @@ TEST_F(APITest, TrapInvalid) { CloseAll({a, b, node}); } +TEST_F(APITest, RejectInvalid) { + EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT, + ipcz().Reject(IPCZ_INVALID_HANDLE, 0, IPCZ_NO_FLAGS, nullptr)); + + const IpczHandle node = CreateNode(kDefaultDriver); + auto [a, b] = OpenPortals(node); + EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT, + ipcz().Reject(a, 0, IPCZ_NO_FLAGS, nullptr)); + CloseAll({a, b, node}); +} + +TEST_F(APITest, RejectLocal) { + const IpczHandle node = CreateNode(kDefaultDriver); + auto [a, b] = OpenPortals(node); + Put(a, "!"); + + char byte; + size_t num_bytes = 1; + IpczHandle validator; + EXPECT_EQ(IPCZ_RESULT_OK, + ipcz().Get(b, IPCZ_NO_FLAGS, nullptr, &byte, &num_bytes, nullptr, + nullptr, &validator)); + EXPECT_EQ('!', byte); + + EXPECT_EQ(IPCZ_RESULT_FAILED_PRECONDITION, + ipcz().Reject(validator, 0, IPCZ_NO_FLAGS, nullptr)); + + CloseAll({a, b, node, validator}); +} + +TEST_F(APITest, RejectRemote) { + const IpczHandle node_a = + CreateNode(kDefaultDriver, IPCZ_CREATE_NODE_AS_BROKER); + const IpczHandle node_b = CreateNode(kDefaultDriver); + IpczDriverHandle transport0, transport1; + ASSERT_EQ(IPCZ_RESULT_OK, + kDefaultDriver.CreateTransports( + IPCZ_INVALID_DRIVER_HANDLE, IPCZ_INVALID_DRIVER_HANDLE, + IPCZ_NO_FLAGS, nullptr, &transport0, &transport1)); + + IpczHandle a; + ASSERT_EQ(IPCZ_RESULT_OK, ipcz().ConnectNode(node_a, transport0, 1, + IPCZ_NO_FLAGS, nullptr, &a)); + IpczHandle b; + ASSERT_EQ(IPCZ_RESULT_OK, + ipcz().ConnectNode(node_b, transport1, 1, + IPCZ_CONNECT_NODE_TO_BROKER, nullptr, &b)); + + Put(a, "!"); + char byte; + size_t num_bytes = 1; + IpczHandle validator; + EXPECT_EQ(IPCZ_RESULT_OK, + ipcz().Get(b, IPCZ_NO_FLAGS, nullptr, &byte, &num_bytes, nullptr, + nullptr, &validator)); + EXPECT_EQ('!', byte); + + constexpr uintptr_t kTestContext = 42; + IpczDriverHandle error_transport = IPCZ_INVALID_DRIVER_HANDLE; + uintptr_t error_context = 0; + reference_drivers::SetBadTransportActivityCallback( + [&](IpczDriverHandle transport, uintptr_t context) { + error_transport = transport; + error_context = context; + }); + EXPECT_EQ(IPCZ_RESULT_OK, + ipcz().Reject(validator, kTestContext, IPCZ_NO_FLAGS, nullptr)); + EXPECT_EQ(transport1, error_transport); + EXPECT_EQ(kTestContext, error_context); + reference_drivers::SetBadTransportActivityCallback(nullptr); + + CloseAll({a, b, node_b, node_a, validator}); +} + TEST_F(APITest, BoxInvalid) { IpczDriverHandle transport0, transport1; ASSERT_EQ(IPCZ_RESULT_OK, diff --git a/chromium/third_party/ipcz/src/box_test.cc b/chromium/third_party/ipcz/src/box_test.cc index f13203abaad..726081ecd74 100644 --- a/chromium/third_party/ipcz/src/box_test.cc +++ b/chromium/third_party/ipcz/src/box_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,7 +6,6 @@ #include <string_view> #include "ipcz/ipcz.h" -#include "reference_drivers/blob.h" #include "test/multinode_test.h" #include "testing/gtest/include/gtest/gtest.h" #include "util/ref_counted.h" @@ -14,75 +13,50 @@ namespace ipcz { namespace { -using Blob = reference_drivers::Blob; - using BoxTestNode = test::TestNode; using BoxTest = test::MultinodeTest<BoxTestNode>; -IpczDriverHandle CreateTestBlob(std::string_view message) { - return Blob::ReleaseAsHandle(MakeRefCounted<Blob>(message)); -} - -std::string GetBlobContents(IpczDriverHandle handle) { - Ref<Blob> blob = Blob::TakeFromHandle(handle); - return std::string(blob->message()); -} - -TEST_P(BoxTest, BoxAndUnbox) { +MULTINODE_TEST(BoxTest, BoxAndUnbox) { constexpr const char kMessage[] = "Hello, world?"; - IpczDriverHandle blob_handle = CreateTestBlob(kMessage); - - IpczHandle box; - EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().Box(node(), blob_handle, IPCZ_NO_FLAGS, nullptr, &box)); - - blob_handle = IPCZ_INVALID_DRIVER_HANDLE; - EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().Unbox(box, IPCZ_NO_FLAGS, nullptr, &blob_handle)); - EXPECT_EQ(kMessage, GetBlobContents(blob_handle)); + EXPECT_EQ(kMessage, UnboxBlob(BoxBlob(kMessage))); } -TEST_P(BoxTest, CloseBox) { - Ref<Blob> blob = MakeRefCounted<Blob>("meh"); - Ref<Blob::RefCountedFlag> destroyed = blob->destruction_flag_for_testing(); - IpczDriverHandle blob_handle = Blob::ReleaseAsHandle(std::move(blob)); - - IpczHandle box; +MULTINODE_TEST(BoxTest, CloseBox) { + // Verifies that box closure releases its underlying driver object. This test + // does not explicitly observe side effects of that release, but LSan will + // fail if something's off. EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().Box(node(), blob_handle, IPCZ_NO_FLAGS, nullptr, &box)); - - EXPECT_FALSE(destroyed->get()); - EXPECT_EQ(IPCZ_RESULT_OK, ipcz().Close(box, IPCZ_NO_FLAGS, nullptr)); - EXPECT_TRUE(destroyed->get()); + ipcz().Close(BoxBlob("meh"), IPCZ_NO_FLAGS, nullptr)); } -TEST_P(BoxTest, Peek) { - constexpr const char kMessage[] = "Hello, world?"; - IpczDriverHandle blob_handle = CreateTestBlob(kMessage); - IpczHandle box; - EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().Box(node(), blob_handle, IPCZ_NO_FLAGS, nullptr, &box)); +MULTINODE_TEST(BoxTest, Peek) { + constexpr std::string_view kMessage = "Hello, world?"; + IpczHandle box = BoxBlob(kMessage); - blob_handle = IPCZ_INVALID_DRIVER_HANDLE; + IpczDriverHandle memory = IPCZ_INVALID_DRIVER_HANDLE; EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().Unbox(box, IPCZ_UNBOX_PEEK, nullptr, &blob_handle)); + ipcz().Unbox(box, IPCZ_UNBOX_PEEK, nullptr, &memory)); EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().Unbox(box, IPCZ_UNBOX_PEEK, nullptr, &blob_handle)); + ipcz().Unbox(box, IPCZ_UNBOX_PEEK, nullptr, &memory)); EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().Unbox(box, IPCZ_UNBOX_PEEK, nullptr, &blob_handle)); - - Blob* blob = Blob::FromHandle(blob_handle); - EXPECT_EQ(kMessage, blob->message()); + ipcz().Unbox(box, IPCZ_UNBOX_PEEK, nullptr, &memory)); + EXPECT_NE(IPCZ_INVALID_DRIVER_HANDLE, memory); + IpczDriverHandle mapping; + void* base; EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().Unbox(box, IPCZ_NO_FLAGS, nullptr, &blob_handle)); + GetDriver().MapSharedMemory(memory, IPCZ_NO_FLAGS, nullptr, &base, + &mapping)); + std::string contents(static_cast<const char*>(base), kMessage.size()); + EXPECT_EQ(kMessage, contents); + EXPECT_EQ(IPCZ_RESULT_OK, GetDriver().Close(mapping, IPCZ_NO_FLAGS, nullptr)); - Ref<Blob> released_blob = Blob::TakeFromHandle(blob_handle); - EXPECT_EQ(blob, released_blob.get()); + EXPECT_EQ(kMessage, UnboxBlob(box)); } constexpr const char kMessage1[] = "Hello, world?"; constexpr const char kMessage2[] = "Hello, world!"; +constexpr const char kMessage3[] = "Hello. World."; MULTINODE_TEST_NODE(BoxTestNode, TransferBoxClient) { IpczHandle b = ConnectToBroker(); @@ -91,26 +65,41 @@ MULTINODE_TEST_NODE(BoxTestNode, TransferBoxClient) { IpczHandle box; EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(b, &message, {&box, 1})); EXPECT_EQ(kMessage2, message); - - IpczDriverHandle blob_handle; - EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().Unbox(box, IPCZ_NO_FLAGS, nullptr, &blob_handle)); - EXPECT_EQ(kMessage1, GetBlobContents(blob_handle)); - + EXPECT_EQ(kMessage1, UnboxBlob(box)); Close(b); } -TEST_P(BoxTest, TransferBox) { +MULTINODE_TEST(BoxTest, TransferBox) { IpczHandle c = SpawnTestNode<TransferBoxClient>(); + IpczHandle box = BoxBlob(kMessage1); + EXPECT_EQ(IPCZ_RESULT_OK, Put(c, kMessage2, {&box, 1})); + Close(c); +} - IpczDriverHandle blob_handle = CreateTestBlob(kMessage1); - IpczHandle box; - EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().Box(node(), blob_handle, IPCZ_NO_FLAGS, nullptr, &box)); +MULTINODE_TEST_NODE(BoxTestNode, TransferBoxAndPortalClient) { + IpczHandle b = ConnectToBroker(); - EXPECT_EQ(IPCZ_RESULT_OK, Put(c, kMessage2, {&box, 1})); + IpczHandle handles[2]; + std::string message; + EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(b, &message, handles)); + EXPECT_EQ(kMessage2, message); + EXPECT_EQ(IPCZ_RESULT_OK, Put(handles[1], kMessage3)); + EXPECT_EQ(kMessage1, UnboxBlob(handles[0])); + CloseAll({b, handles[1]}); +} - Close(c); +MULTINODE_TEST(BoxTest, TransferBoxAndPortal) { + IpczHandle c = SpawnTestNode<TransferBoxAndPortalClient>(); + + auto [q, p] = OpenPortals(); + IpczHandle box = BoxBlob(kMessage1); + IpczHandle handles[] = {box, p}; + EXPECT_EQ(IPCZ_RESULT_OK, Put(c, kMessage2, absl::MakeSpan(handles))); + + std::string message; + EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(q, &message)); + EXPECT_EQ(kMessage3, message); + CloseAll({c, q}); } constexpr size_t TransferBoxBetweenNonBrokersNumIterations = 50; @@ -121,19 +110,14 @@ MULTINODE_TEST_NODE(BoxTestNode, TransferBoxBetweenNonBrokersClient1) { EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(b, nullptr, {&q, 1})); for (size_t i = 0; i < TransferBoxBetweenNonBrokersNumIterations; ++i) { - IpczDriverHandle blob_handle = CreateTestBlob(kMessage1); - IpczHandle box; - EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().Box(node(), blob_handle, IPCZ_NO_FLAGS, nullptr, &box)); + IpczHandle box = BoxBlob(kMessage1); EXPECT_EQ(IPCZ_RESULT_OK, Put(q, kMessage2, {&box, 1})); box = IPCZ_INVALID_DRIVER_HANDLE; std::string message; EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(q, &message, {&box, 1})); EXPECT_EQ(kMessage1, message); - EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().Unbox(box, IPCZ_NO_FLAGS, nullptr, &blob_handle)); - EXPECT_EQ(kMessage2, GetBlobContents(blob_handle)); + EXPECT_EQ(kMessage2, UnboxBlob(box)); } WaitForDirectRemoteLink(q); @@ -147,17 +131,12 @@ MULTINODE_TEST_NODE(BoxTestNode, TransferBoxBetweenNonBrokersClient2) { for (size_t i = 0; i < TransferBoxBetweenNonBrokersNumIterations; ++i) { IpczHandle box; - IpczDriverHandle blob_handle; std::string message; EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(p, &message, {&box, 1})); EXPECT_EQ(kMessage2, message); - EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().Unbox(box, IPCZ_NO_FLAGS, nullptr, &blob_handle)); - EXPECT_EQ(kMessage1, GetBlobContents(blob_handle)); + EXPECT_EQ(kMessage1, UnboxBlob(box)); - blob_handle = CreateTestBlob(kMessage2); - EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().Box(node(), blob_handle, IPCZ_NO_FLAGS, nullptr, &box)); + box = BoxBlob(kMessage2); EXPECT_EQ(IPCZ_RESULT_OK, Put(p, kMessage1, {&box, 1})); } @@ -165,7 +144,7 @@ MULTINODE_TEST_NODE(BoxTestNode, TransferBoxBetweenNonBrokersClient2) { CloseAll({p, b}); } -TEST_P(BoxTest, TransferBoxBetweenNonBrokers) { +MULTINODE_TEST(BoxTest, TransferBoxBetweenNonBrokers) { IpczHandle c1 = SpawnTestNode<TransferBoxBetweenNonBrokersClient1>(); IpczHandle c2 = SpawnTestNode<TransferBoxBetweenNonBrokersClient2>(); @@ -181,7 +160,5 @@ TEST_P(BoxTest, TransferBoxBetweenNonBrokers) { CloseAll({c1, c2}); } -INSTANTIATE_MULTINODE_TEST_SUITE_P(BoxTest); - } // namespace } // namespace ipcz diff --git a/chromium/third_party/ipcz/src/connect_test.cc b/chromium/third_party/ipcz/src/connect_test.cc index 10d4df50722..1e6cc9aefbc 100644 --- a/chromium/third_party/ipcz/src/connect_test.cc +++ b/chromium/third_party/ipcz/src/connect_test.cc @@ -1,12 +1,14 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <string> +#include "build/build_config.h" #include "ipcz/ipcz.h" #include "ipcz/node_messages.h" -#include "reference_drivers/blob.h" +#include "reference_drivers/async_reference_driver.h" +#include "reference_drivers/sync_reference_driver.h" #include "test/multinode_test.h" #include "test/test_transport_listener.h" #include "testing/gtest/include/gtest/gtest.h" @@ -16,7 +18,16 @@ namespace ipcz { namespace { -using ConnectTestNode = test::TestNode; +class ConnectTestNode : public test::TestNode { + public: + void ActivateAndClose(IpczDriverHandle transport) { + // Registering any listener callback activates the transport, and + // listener destruction closes it. + test::TestTransportListener listener(node(), transport); + listener.OnError([] {}); + } +}; + using ConnectTest = test::MultinodeTest<ConnectTestNode>; MULTINODE_TEST_NODE(ConnectTestNode, BrokerToNonBrokerClient) { @@ -25,7 +36,7 @@ MULTINODE_TEST_NODE(ConnectTestNode, BrokerToNonBrokerClient) { Close(b); } -TEST_P(ConnectTest, BrokerToNonBroker) { +MULTINODE_TEST(ConnectTest, BrokerToNonBroker) { IpczHandle c = SpawnTestNode<BrokerToNonBrokerClient>(); Close(c); } @@ -47,7 +58,7 @@ MULTINODE_TEST_NODE(ConnectTestNode, SurplusPortalsClient) { CloseAll(portals); } -TEST_P(ConnectTest, SurplusPortals) { +MULTINODE_TEST(ConnectTest, SurplusPortals) { IpczHandle portals[kNumBrokerPortals]; SpawnTestNode<SurplusPortalsClient>(portals); CloseAll(portals); @@ -59,12 +70,11 @@ MULTINODE_TEST_NODE(ConnectTestNode, ExpectDisconnectFromBroker) { Close(b); } -TEST_P(ConnectTest, DisconnectWithoutBrokerHandshake) { - TransportPair transports = CreateTransports(); +MULTINODE_TEST(ConnectTest, DisconnectWithoutBrokerHandshake) { + IpczDriverHandle our_transport; auto controller = - SpawnTestNodeWithTransport<ExpectDisconnectFromBroker>(transports.theirs); - EXPECT_EQ(IPCZ_RESULT_OK, - GetDriver().Close(transports.ours, IPCZ_NO_FLAGS, nullptr)); + SpawnTestNodeNoConnect<ExpectDisconnectFromBroker>(our_transport); + ActivateAndClose(our_transport); controller->WaitForShutdown(); } @@ -74,25 +84,24 @@ MULTINODE_TEST_NODE(ConnectTestNode, // we never call ConnectToBroker(). No action required. } -TEST_P(ConnectTest, DisconnectWithoutNonBrokerHandshake) { +MULTINODE_TEST(ConnectTest, DisconnectWithoutNonBrokerHandshake) { IpczHandle c = SpawnTestNode<DisconnectWithoutNonBrokerHandshakeClient>(); EXPECT_EQ(IPCZ_RESULT_OK, WaitForConditionFlags(c, IPCZ_TRAP_PEER_CLOSED)); Close(c); } -TEST_P(ConnectTest, DisconnectOnBadBrokerMessage) { - TransportPair transports = CreateTransports(); +MULTINODE_TEST(ConnectTest, DisconnectOnBadBrokerMessage) { + IpczDriverHandle our_transport; auto controller = - SpawnTestNodeWithTransport<ExpectDisconnectFromBroker>(transports.theirs); + SpawnTestNodeNoConnect<ExpectDisconnectFromBroker>(our_transport); // Send some garbage to the other node. const char kBadMessage[] = "this will never be a valid handshake message!"; EXPECT_EQ( IPCZ_RESULT_OK, - GetDriver().Transmit(transports.ours, kBadMessage, std::size(kBadMessage), + GetDriver().Transmit(our_transport, kBadMessage, std::size(kBadMessage), nullptr, 0, IPCZ_NO_FLAGS, nullptr)); - EXPECT_EQ(IPCZ_RESULT_OK, - GetDriver().Close(transports.ours, IPCZ_NO_FLAGS, nullptr)); + ActivateAndClose(our_transport); // The other node will only shut down once it's observed peer closure on its // portal to us; which it should, because we just sent it some garbage. @@ -115,7 +124,7 @@ MULTINODE_TEST_NODE(ConnectTestNode, TransmitSomeGarbage) { listener.StopListening(); } -TEST_P(ConnectTest, DisconnectOnBadNonBrokerMessage) { +MULTINODE_TEST(ConnectTest, DisconnectOnBadNonBrokerMessage) { IpczHandle c; auto controller = SpawnTestNode<TransmitSomeGarbage>({&c, 1}); @@ -160,7 +169,17 @@ MULTINODE_TEST_NODE(ConnectTestNode, NonBrokerToNonBrokerClient) { CloseAll({c, b}); } -TEST_P(ConnectTest, NonBrokerToNonBroker) { +MULTINODE_TEST(ConnectTest, NonBrokerToNonBroker) { +#if BUILDFLAG(IS_ANDROID) + // Client nodes launching other client nodes doesn't work for Chromium's + // custom test driver on Android. Limit this test to the reference test + // drivers there. + if (&GetDriver() != &reference_drivers::kSyncReferenceDriver && + &GetDriver() != &reference_drivers::kAsyncReferenceDriver) { + return; + } +#endif + IpczHandle c1 = SpawnTestNode<NonBrokerToNonBrokerClient>(); IpczHandle c2 = SpawnTestNode<NonBrokerToNonBrokerClient>(); @@ -212,7 +231,7 @@ MULTINODE_TEST_NODE(ConnectTestNode, BadNonBrokerReferralClient) { GetDriver().Close(transports.theirs, IPCZ_NO_FLAGS, nullptr)); } -TEST_P(ConnectTest, BadNonBrokerReferral) { +MULTINODE_TEST(ConnectTest, BadNonBrokerReferral) { IpczHandle c = SpawnTestNode<BadNonBrokerReferralClient>(); EXPECT_EQ(IPCZ_RESULT_OK, WaitForConditionFlags(c, IPCZ_TRAP_PEER_CLOSED)); Close(c); @@ -230,27 +249,109 @@ MULTINODE_TEST_NODE(ConnectTestNode, FailedNonBrokerReferralReferredClient) { MULTINODE_TEST_NODE(ConnectTestNode, FailedNonBrokerReferralClient) { IpczHandle b = ConnectToBroker(); - TransportPair transports = CreateTransports(); + IpczDriverHandle our_transport; auto controller = - SpawnTestNodeWithTransport<FailedNonBrokerReferralReferredClient>( - transports.theirs); + SpawnTestNodeNoConnect<FailedNonBrokerReferralReferredClient>( + our_transport); - // Disconnect the transport instead of passing to our broker with - // ConnectNode(). The referred client should observe disconnection of its - // initial portals and terminate itself. - EXPECT_EQ(IPCZ_RESULT_OK, - GetDriver().Close(transports.ours, IPCZ_NO_FLAGS, nullptr)); + // Activate and immediately disconnect the transport instead of passing to our + // broker with ConnectNode(). The referred client should observe disconnection + // of its initial portals and terminate itself. + ActivateAndClose(our_transport); controller->WaitForShutdown(); Close(b); } -TEST_P(ConnectTest, FailedNonBrokerReferral) { +MULTINODE_TEST(ConnectTest, FailedNonBrokerReferral) { IpczHandle c = SpawnTestNode<FailedNonBrokerReferralClient>(); EXPECT_EQ(IPCZ_RESULT_OK, WaitForConditionFlags(c, IPCZ_TRAP_PEER_CLOSED)); Close(c); } -INSTANTIATE_MULTINODE_TEST_SUITE_P(ConnectTest); +MULTINODE_TEST_BROKER_NODE(ConnectTestNode, AnotherBroker) { + IpczHandle b = ConnectToBroker(); + PingPong(b); + Close(b); +} + +MULTINODE_TEST(ConnectTest, BrokerToBroker) { + IpczHandle b = SpawnTestNode<AnotherBroker>(); + + PingPong(b); + EXPECT_EQ(IPCZ_RESULT_OK, WaitForConditionFlags(b, IPCZ_TRAP_PEER_CLOSED)); + Close(b); +} + +MULTINODE_TEST_NODE(ConnectTestNode, BrokerClient) { + IpczHandle b = ConnectToBroker(); + IpczHandle other_client; + EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(b, nullptr, {&other_client, 1})); + + // Ensure that we end up with a direct connection to the other client, which + // implies the two non-broker nodes have been properly introduced across the + // boundary of their respective node networks. + PingPong(other_client); + WaitForDirectRemoteLink(other_client); + + // Synchronize against the main test node. See synchronization comment there. + PingPong(b); + CloseAll({b, other_client}); +} + +MULTINODE_TEST_BROKER_NODE(ConnectTestNode, BrokerWithClientNode) { + IpczHandle b = ConnectToBroker(); + IpczHandle client = SpawnTestNode<BrokerClient>(); + + IpczHandle other_client; + EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(b, nullptr, {&other_client, 1})); + Put(client, "", {&other_client, 1}); + + // Synchronize against the launched client to ensure that it's done before we + // join it and terminate. + PingPong(client); + EXPECT_EQ(IPCZ_RESULT_OK, + WaitForConditionFlags(client, IPCZ_TRAP_PEER_CLOSED)); + + // Synchronize against the main test node. See synchronization comment there. + PingPong(b); + CloseAll({b, client}); +} + +// TODOD(crbug.com/1374114): Fix flakiness and re-enable +MULTINODE_TEST(ConnectTest, DISABLED_MultiBrokerIntroductions) { + // This test covers introductions in a multi-broker network. There are four + // test nodes involved here: the main node (this one, call it A), a secondary + // broker B launched with the BrokerWithClientNode body defined above; and + // two client nodes (running BrokerClient above) we will call C and D, with + // C launched by A and D launched by B. + // + // A portal pair is created on A and its portals are passed to node B (our + // secondary broker) and node C (the singular non-broker client node in A's + // local network) respectively. + // + // Node B in turn passes its end to its own launched non-broker client D. This + // ultimately elicits a need for node C to be introduced to node D. The test + // succeeds only once the portal on node C appears to be directly connected to + // the portal on node D -- and vice versa -- implying successful introduction. + + IpczHandle other_broker = SpawnTestNode<BrokerWithClientNode>(); + IpczHandle client = SpawnTestNode<BrokerClient>(); + + auto [q, p] = OpenPortals(); + Put(other_broker, "", {&q, 1}); + Put(client, "", {&p, 1}); + + // Synchronize against both the launched broker and the launched client node + // to ensure that they're done before we join them and terminate. + PingPong(other_broker); + PingPong(client); + EXPECT_EQ(IPCZ_RESULT_OK, + WaitForConditionFlags(other_broker, IPCZ_TRAP_PEER_CLOSED)); + EXPECT_EQ(IPCZ_RESULT_OK, + WaitForConditionFlags(client, IPCZ_TRAP_PEER_CLOSED)); + + CloseAll({other_broker, client}); +} } // namespace } // namespace ipcz diff --git a/chromium/third_party/ipcz/src/ipcz/api_object.cc b/chromium/third_party/ipcz/src/ipcz/api_object.cc index 08b0474939e..0a6784b9754 100644 --- a/chromium/third_party/ipcz/src/ipcz/api_object.cc +++ b/chromium/third_party/ipcz/src/ipcz/api_object.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/api_object.h b/chromium/third_party/ipcz/src/ipcz/api_object.h index aa5f19d9263..0477ccd04a3 100644 --- a/chromium/third_party/ipcz/src/ipcz/api_object.h +++ b/chromium/third_party/ipcz/src/ipcz/api_object.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -24,6 +24,7 @@ class APIObject : public RefCounted { kPortal, kBox, kTransport, + kValidator, }; explicit APIObject(ObjectType type); diff --git a/chromium/third_party/ipcz/src/ipcz/atomic_queue_state.cc b/chromium/third_party/ipcz/src/ipcz/atomic_queue_state.cc new file mode 100644 index 00000000000..d2f2dc94fd1 --- /dev/null +++ b/chromium/third_party/ipcz/src/ipcz/atomic_queue_state.cc @@ -0,0 +1,38 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ipcz/atomic_queue_state.h" + +#include <cstdint> + +#include "ipcz/monitored_atomic.h" +#include "third_party/abseil-cpp/absl/base/macros.h" + +namespace ipcz { + +AtomicQueueState::AtomicQueueState() noexcept = default; + +AtomicQueueState::QueryResult AtomicQueueState::Query( + const MonitorSelection& monitors) { + return { + .num_parcels_consumed = + num_parcels_consumed_.Query({.monitor = monitors.monitor_parcels}), + .num_bytes_consumed = + num_bytes_consumed_.Query({.monitor = monitors.monitor_bytes}), + }; +} + +bool AtomicQueueState::Update(const UpdateValue& value) { + ABSL_ASSERT(value.num_parcels_consumed <= + MonitoredAtomic<uint64_t>::kMaxValue); + ABSL_ASSERT(value.num_bytes_consumed <= MonitoredAtomic<uint64_t>::kMaxValue); + const bool parcels_were_monitored = + num_parcels_consumed_.UpdateValueAndResetMonitor( + value.num_parcels_consumed); + const bool bytes_were_monitored = + num_bytes_consumed_.UpdateValueAndResetMonitor(value.num_bytes_consumed); + return parcels_were_monitored || bytes_were_monitored; +} + +} // namespace ipcz diff --git a/chromium/third_party/ipcz/src/ipcz/atomic_queue_state.h b/chromium/third_party/ipcz/src/ipcz/atomic_queue_state.h new file mode 100644 index 00000000000..bb7401feb8e --- /dev/null +++ b/chromium/third_party/ipcz/src/ipcz/atomic_queue_state.h @@ -0,0 +1,75 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPCZ_SRC_IPCZ_ATOMIC_QUEUE_STATE_ +#define IPCZ_SRC_IPCZ_ATOMIC_QUEUE_STATE_ + +#include <cstdint> +#include <type_traits> + +#include "ipcz/monitored_atomic.h" + +namespace ipcz { + +// AtomicQueueState holds some trivial data about how much of a router's inbound +// parcel sequence has been consumed so far. +// +// Note that the fields herein are not strictly synchronized. If a queue +// accumulates a 4k parcel and an 8k parcel which are both then consumed by the +// application, the remote sender may observe `num_parcels_consumed` at 0, then +// 1, then 2; and they may observe `num_bytes_consumed` at 0, then 4k, and then +// 12k; the ordering of those individual progressions is guaranteed, but there's +// no guarantee that an observer will see `num_parcels_consumed` as 1 at the +// same time they see `num_bytes_consumed` as 4k. +class alignas(8) AtomicQueueState { + public: + AtomicQueueState() noexcept; + + // Performs a best-effort query of the most recently visible value on both + // fields and returns them as a QueryResult. `monitors` determines whether + // each field will be atomically marked for monitoring at the same time its + // value is retrieved. + struct QueryResult { + MonitoredAtomic<uint64_t>::State num_parcels_consumed; + MonitoredAtomic<uint64_t>::State num_bytes_consumed; + }; + struct MonitorSelection { + bool monitor_parcels; + bool monitor_bytes; + }; + QueryResult Query(const MonitorSelection& monitors); + + // Updates both fields with new values, resetting any monitor bit that may + // have been set on either one. If either field had a monitor bit set prior to + // this update, this returns true. Otherwise it returns false. + struct UpdateValue { + uint64_t num_parcels_consumed; + uint64_t num_bytes_consumed; + }; + bool Update(const UpdateValue& value); + + private: + // The number of parcels consumed from the router's inbound parcel queue, + // either by the application reading from its portal, or by ipcz proxying them + // onward to another router. + MonitoredAtomic<uint64_t> num_parcels_consumed_{0}; + + // The total number of bytes of data consumed from the router's inbound parcel + // queue. This is the sum of the data size of all parcels covered by + // `consumed_sequence_length`, plus any bytes already consumed from the + // next parcel in sequence if it's been partially consumed.. + MonitoredAtomic<uint64_t> num_bytes_consumed_{0}; +}; + +// This must remain stable at 16 bytes in size, as it's part of shared memory +// layouts. Trivial copyability is also required as a proxy condition to prevent +// changes which might break that usage (e.g. introduction of a non-trivial +// destructor.) +static_assert(sizeof(AtomicQueueState) == 16, "Invalid AtomicQueueState size"); +static_assert(std::is_trivially_copyable_v<AtomicQueueState>, + "AtomicQueueState must be trivially copyable"); + +} // namespace ipcz + +#endif // IPCZ_SRC_IPCZ_ATOMIC_QUEUE_STATE_ diff --git a/chromium/third_party/ipcz/src/ipcz/block_allocator.cc b/chromium/third_party/ipcz/src/ipcz/block_allocator.cc index 8b0cdb518d4..a826bd79d3f 100644 --- a/chromium/third_party/ipcz/src/ipcz/block_allocator.cc +++ b/chromium/third_party/ipcz/src/ipcz/block_allocator.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/block_allocator.h b/chromium/third_party/ipcz/src/ipcz/block_allocator.h index 8f6781bf2ab..ff9b29318eb 100644 --- a/chromium/third_party/ipcz/src/ipcz/block_allocator.h +++ b/chromium/third_party/ipcz/src/ipcz/block_allocator.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/block_allocator_pool.cc b/chromium/third_party/ipcz/src/ipcz/block_allocator_pool.cc index 8e32b5404e6..bd464f897d1 100644 --- a/chromium/third_party/ipcz/src/ipcz/block_allocator_pool.cc +++ b/chromium/third_party/ipcz/src/ipcz/block_allocator_pool.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/block_allocator_pool.h b/chromium/third_party/ipcz/src/ipcz/block_allocator_pool.h index bbaed157671..103e2949014 100644 --- a/chromium/third_party/ipcz/src/ipcz/block_allocator_pool.h +++ b/chromium/third_party/ipcz/src/ipcz/block_allocator_pool.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/block_allocator_test.cc b/chromium/third_party/ipcz/src/ipcz/block_allocator_test.cc index 01327939375..dcd339de1a9 100644 --- a/chromium/third_party/ipcz/src/ipcz/block_allocator_test.cc +++ b/chromium/third_party/ipcz/src/ipcz/block_allocator_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/box.cc b/chromium/third_party/ipcz/src/ipcz/box.cc index ca4d19e9457..f6e2285ddc8 100644 --- a/chromium/third_party/ipcz/src/ipcz/box.cc +++ b/chromium/third_party/ipcz/src/ipcz/box.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/box.h b/chromium/third_party/ipcz/src/ipcz/box.h index a861405e6b3..9a13d17cb53 100644 --- a/chromium/third_party/ipcz/src/ipcz/box.h +++ b/chromium/third_party/ipcz/src/ipcz/box.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/buffer_id.h b/chromium/third_party/ipcz/src/ipcz/buffer_id.h index 763a436d1de..99635050362 100644 --- a/chromium/third_party/ipcz/src/ipcz/buffer_id.h +++ b/chromium/third_party/ipcz/src/ipcz/buffer_id.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/buffer_pool.cc b/chromium/third_party/ipcz/src/ipcz/buffer_pool.cc index 945e33eda15..6881346d8f8 100644 --- a/chromium/third_party/ipcz/src/ipcz/buffer_pool.cc +++ b/chromium/third_party/ipcz/src/ipcz/buffer_pool.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/buffer_pool.h b/chromium/third_party/ipcz/src/ipcz/buffer_pool.h index 4244375ba68..9a73aef4634 100644 --- a/chromium/third_party/ipcz/src/ipcz/buffer_pool.h +++ b/chromium/third_party/ipcz/src/ipcz/buffer_pool.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/buffer_pool_test.cc b/chromium/third_party/ipcz/src/ipcz/buffer_pool_test.cc index 428b61cb376..a009ffe1c20 100644 --- a/chromium/third_party/ipcz/src/ipcz/buffer_pool_test.cc +++ b/chromium/third_party/ipcz/src/ipcz/buffer_pool_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/driver_memory.cc b/chromium/third_party/ipcz/src/ipcz/driver_memory.cc index bc28fcdc6c6..6dcf5c4dcc0 100644 --- a/chromium/third_party/ipcz/src/ipcz/driver_memory.cc +++ b/chromium/third_party/ipcz/src/ipcz/driver_memory.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/driver_memory.h b/chromium/third_party/ipcz/src/ipcz/driver_memory.h index 3545418d1f4..2a5569967bb 100644 --- a/chromium/third_party/ipcz/src/ipcz/driver_memory.h +++ b/chromium/third_party/ipcz/src/ipcz/driver_memory.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/driver_memory_mapping.cc b/chromium/third_party/ipcz/src/ipcz/driver_memory_mapping.cc index d2c04f1b3e9..9d0b8c0833b 100644 --- a/chromium/third_party/ipcz/src/ipcz/driver_memory_mapping.cc +++ b/chromium/third_party/ipcz/src/ipcz/driver_memory_mapping.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/driver_memory_mapping.h b/chromium/third_party/ipcz/src/ipcz/driver_memory_mapping.h index 525f3508400..946518783ef 100644 --- a/chromium/third_party/ipcz/src/ipcz/driver_memory_mapping.h +++ b/chromium/third_party/ipcz/src/ipcz/driver_memory_mapping.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/driver_memory_test.cc b/chromium/third_party/ipcz/src/ipcz/driver_memory_test.cc index bd6ab4693e6..cb16efe94e5 100644 --- a/chromium/third_party/ipcz/src/ipcz/driver_memory_test.cc +++ b/chromium/third_party/ipcz/src/ipcz/driver_memory_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/driver_object.cc b/chromium/third_party/ipcz/src/ipcz/driver_object.cc index 5f429c7082a..967b3b41b64 100644 --- a/chromium/third_party/ipcz/src/ipcz/driver_object.cc +++ b/chromium/third_party/ipcz/src/ipcz/driver_object.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/driver_object.h b/chromium/third_party/ipcz/src/ipcz/driver_object.h index 2a5ecac8fff..44b0c688956 100644 --- a/chromium/third_party/ipcz/src/ipcz/driver_object.h +++ b/chromium/third_party/ipcz/src/ipcz/driver_object.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/driver_object_test.cc b/chromium/third_party/ipcz/src/ipcz/driver_object_test.cc index 35a7279f3c4..78ce1da9245 100644 --- a/chromium/third_party/ipcz/src/ipcz/driver_object_test.cc +++ b/chromium/third_party/ipcz/src/ipcz/driver_object_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/driver_transport.cc b/chromium/third_party/ipcz/src/ipcz/driver_transport.cc index f78971c640c..0103284c7bd 100644 --- a/chromium/third_party/ipcz/src/ipcz/driver_transport.cc +++ b/chromium/third_party/ipcz/src/ipcz/driver_transport.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/driver_transport.h b/chromium/third_party/ipcz/src/ipcz/driver_transport.h index a9f79f53733..e95bffffcef 100644 --- a/chromium/third_party/ipcz/src/ipcz/driver_transport.h +++ b/chromium/third_party/ipcz/src/ipcz/driver_transport.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/driver_transport_test.cc b/chromium/third_party/ipcz/src/ipcz/driver_transport_test.cc index 0af63717c6b..e64dab378cb 100644 --- a/chromium/third_party/ipcz/src/ipcz/driver_transport_test.cc +++ b/chromium/third_party/ipcz/src/ipcz/driver_transport_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/fragment.cc b/chromium/third_party/ipcz/src/ipcz/fragment.cc index 2cb2e8753a3..651d1c2fca5 100644 --- a/chromium/third_party/ipcz/src/ipcz/fragment.cc +++ b/chromium/third_party/ipcz/src/ipcz/fragment.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/fragment.h b/chromium/third_party/ipcz/src/ipcz/fragment.h index e1a87d2122d..c0151fdcf4b 100644 --- a/chromium/third_party/ipcz/src/ipcz/fragment.h +++ b/chromium/third_party/ipcz/src/ipcz/fragment.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/fragment_descriptor.cc b/chromium/third_party/ipcz/src/ipcz/fragment_descriptor.cc index 1e65f2832f2..6ef132c6a57 100644 --- a/chromium/third_party/ipcz/src/ipcz/fragment_descriptor.cc +++ b/chromium/third_party/ipcz/src/ipcz/fragment_descriptor.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/fragment_descriptor.h b/chromium/third_party/ipcz/src/ipcz/fragment_descriptor.h index 578acca45e4..b247215fd5e 100644 --- a/chromium/third_party/ipcz/src/ipcz/fragment_descriptor.h +++ b/chromium/third_party/ipcz/src/ipcz/fragment_descriptor.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/fragment_ref.cc b/chromium/third_party/ipcz/src/ipcz/fragment_ref.cc index b294e1cadb3..30ea949d8d1 100644 --- a/chromium/third_party/ipcz/src/ipcz/fragment_ref.cc +++ b/chromium/third_party/ipcz/src/ipcz/fragment_ref.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/fragment_ref.h b/chromium/third_party/ipcz/src/ipcz/fragment_ref.h index e54135b01aa..6f6ef49df67 100644 --- a/chromium/third_party/ipcz/src/ipcz/fragment_ref.h +++ b/chromium/third_party/ipcz/src/ipcz/fragment_ref.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/handle_type.h b/chromium/third_party/ipcz/src/ipcz/handle_type.h index df08a27a45d..26a15f13abe 100644 --- a/chromium/third_party/ipcz/src/ipcz/handle_type.h +++ b/chromium/third_party/ipcz/src/ipcz/handle_type.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/link_side.cc b/chromium/third_party/ipcz/src/ipcz/link_side.cc index d6bb1836f1e..40f029ef9a1 100644 --- a/chromium/third_party/ipcz/src/ipcz/link_side.cc +++ b/chromium/third_party/ipcz/src/ipcz/link_side.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/link_side.h b/chromium/third_party/ipcz/src/ipcz/link_side.h index 7032e9a0e0a..5d95a1995c0 100644 --- a/chromium/third_party/ipcz/src/ipcz/link_side.h +++ b/chromium/third_party/ipcz/src/ipcz/link_side.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/link_type.cc b/chromium/third_party/ipcz/src/ipcz/link_type.cc index d69a4514229..c98306cc525 100644 --- a/chromium/third_party/ipcz/src/ipcz/link_type.cc +++ b/chromium/third_party/ipcz/src/ipcz/link_type.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/link_type.h b/chromium/third_party/ipcz/src/ipcz/link_type.h index 28b52c5115e..1c4eacccf6a 100644 --- a/chromium/third_party/ipcz/src/ipcz/link_type.h +++ b/chromium/third_party/ipcz/src/ipcz/link_type.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/local_router_link.cc b/chromium/third_party/ipcz/src/ipcz/local_router_link.cc index e675a579fab..6091424a5e1 100644 --- a/chromium/third_party/ipcz/src/ipcz/local_router_link.cc +++ b/chromium/third_party/ipcz/src/ipcz/local_router_link.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -94,6 +94,10 @@ RouterLinkState* LocalRouterLink::GetLinkState() const { return &state_->link_state(); } +void LocalRouterLink::WaitForLinkStateAsync(std::function<void()> callback) { + callback(); +} + Ref<Router> LocalRouterLink::GetLocalPeer() { return state_->GetRouter(side_.opposite()); } @@ -108,47 +112,42 @@ void LocalRouterLink::AllocateParcelData(size_t num_bytes, parcel.AllocateData(num_bytes, allow_partial, /*memory=*/nullptr); } -void LocalRouterLink::AcceptParcel(Parcel& parcel) { +void LocalRouterLink::AcceptParcel(const OperationContext& context, + Parcel& parcel) { if (Ref<Router> receiver = state_->GetRouter(side_.opposite())) { if (state_->type() == LinkType::kCentral) { - receiver->AcceptInboundParcel(parcel); + receiver->AcceptInboundParcel(context, parcel); } else { ABSL_ASSERT(state_->type() == LinkType::kBridge); - receiver->AcceptOutboundParcel(parcel); + receiver->AcceptOutboundParcel(context, parcel); } } } -void LocalRouterLink::AcceptRouteClosure(SequenceNumber sequence_length) { +void LocalRouterLink::AcceptRouteClosure(const OperationContext& context, + SequenceNumber sequence_length) { if (Ref<Router> receiver = state_->GetRouter(side_.opposite())) { - receiver->AcceptRouteClosureFrom(state_->type(), sequence_length); + receiver->AcceptRouteClosureFrom(context, state_->type(), sequence_length); } } -size_t LocalRouterLink::GetParcelCapacityInBytes(const IpczPutLimits& limits) { - return state_->GetRouter(side_.opposite())->GetInboundCapacityInBytes(limits); -} - -RouterLinkState::QueueState LocalRouterLink::GetPeerQueueState() { - return state_->link_state().GetQueueState(side_.opposite()); +AtomicQueueState* LocalRouterLink::GetPeerQueueState() { + return &state_->link_state().GetQueueState(side_.opposite()); } -bool LocalRouterLink::UpdateInboundQueueState(size_t num_parcels, - size_t num_bytes) { - return state_->link_state().UpdateQueueState(side_, num_parcels, num_bytes); +AtomicQueueState* LocalRouterLink::GetLocalQueueState() { + return &state_->link_state().GetQueueState(side_); } -void LocalRouterLink::NotifyDataConsumed() { - state_->GetRouter(side_.opposite())->NotifyPeerConsumedData(); -} - -bool LocalRouterLink::EnablePeerMonitoring(bool enable) { - return state_->link_state().SetSideIsMonitoringPeer(side_, enable); +void LocalRouterLink::SnapshotPeerQueueState(const OperationContext& context) { + if (Ref<Router> receiver = state_->GetRouter(side_.opposite())) { + receiver->SnapshotPeerQueueState(context); + } } -void LocalRouterLink::AcceptRouteDisconnected() { +void LocalRouterLink::AcceptRouteDisconnected(const OperationContext& context) { if (Ref<Router> receiver = state_->GetRouter(side_.opposite())) { - receiver->AcceptRouteDisconnectedFrom(state_->type()); + receiver->AcceptRouteDisconnectedFrom(context, state_->type()); } } @@ -174,10 +173,11 @@ void LocalRouterLink::Unlock() { state_->link_state().Unlock(side_); } -bool LocalRouterLink::FlushOtherSideIfWaiting() { +bool LocalRouterLink::FlushOtherSideIfWaiting(const OperationContext& context) { const LinkSide other_side = side_.opposite(); if (state_->link_state().ResetWaitingBit(other_side)) { - state_->GetRouter(other_side)->Flush(Router::kForceProxyBypassAttempt); + state_->GetRouter(other_side) + ->Flush(context, Router::kForceProxyBypassAttempt); return true; } return false; @@ -192,24 +192,28 @@ bool LocalRouterLink::CanNodeRequestBypass( allowed_source == bypass_request_source; } -void LocalRouterLink::BypassPeer(const NodeName& bypass_target_node, +void LocalRouterLink::BypassPeer(const OperationContext& context, + const NodeName& bypass_target_node, SublinkId bypass_target_sublink) { // Not implemented, and never called on local links. ABSL_ASSERT(false); } -void LocalRouterLink::StopProxying(SequenceNumber inbound_sequence_length, +void LocalRouterLink::StopProxying(const OperationContext& context, + SequenceNumber inbound_sequence_length, SequenceNumber outbound_sequence_length) { // Not implemented, and never called on local links. ABSL_ASSERT(false); } -void LocalRouterLink::ProxyWillStop(SequenceNumber inbound_sequence_length) { +void LocalRouterLink::ProxyWillStop(const OperationContext& context, + SequenceNumber inbound_sequence_length) { // Not implemented, and never called on local links. ABSL_ASSERT(false); } void LocalRouterLink::BypassPeerWithLink( + const OperationContext& context, SublinkId new_sublink, FragmentRef<RouterLinkState> new_link_state, SequenceNumber inbound_sequence_length) { @@ -218,6 +222,7 @@ void LocalRouterLink::BypassPeerWithLink( } void LocalRouterLink::StopProxyingToLocalPeer( + const OperationContext& context, SequenceNumber outbound_sequence_length) { // Not implemented, and never called on local links. ABSL_ASSERT(false); diff --git a/chromium/third_party/ipcz/src/ipcz/local_router_link.h b/chromium/third_party/ipcz/src/ipcz/local_router_link.h index 512230877ae..ccf33f2e7e7 100644 --- a/chromium/third_party/ipcz/src/ipcz/local_router_link.h +++ b/chromium/third_party/ipcz/src/ipcz/local_router_link.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -35,34 +35,39 @@ class LocalRouterLink : public RouterLink { // RouterLink: LinkType GetType() const override; RouterLinkState* GetLinkState() const override; + void WaitForLinkStateAsync(std::function<void()> callback) override; Ref<Router> GetLocalPeer() override; RemoteRouterLink* AsRemoteRouterLink() override; void AllocateParcelData(size_t num_bytes, bool allow_partial, Parcel& parcel) override; - void AcceptParcel(Parcel& parcel) override; - void AcceptRouteClosure(SequenceNumber sequence_length) override; - void AcceptRouteDisconnected() override; - size_t GetParcelCapacityInBytes(const IpczPutLimits& limits) override; - RouterLinkState::QueueState GetPeerQueueState() override; - bool UpdateInboundQueueState(size_t num_parcels, size_t num_bytes) override; - void NotifyDataConsumed() override; - bool EnablePeerMonitoring(bool enable) override; + void AcceptParcel(const OperationContext& context, Parcel& parcel) override; + void AcceptRouteClosure(const OperationContext& context, + SequenceNumber sequence_length) override; + void AcceptRouteDisconnected(const OperationContext& context) override; + AtomicQueueState* GetPeerQueueState() override; + AtomicQueueState* GetLocalQueueState() override; + void SnapshotPeerQueueState(const OperationContext& context) override; void MarkSideStable() override; bool TryLockForBypass(const NodeName& bypass_request_source) override; bool TryLockForClosure() override; void Unlock() override; - bool FlushOtherSideIfWaiting() override; + bool FlushOtherSideIfWaiting(const OperationContext& context) override; bool CanNodeRequestBypass(const NodeName& bypass_request_source) override; - void BypassPeer(const NodeName& bypass_target_node, + void BypassPeer(const OperationContext& context, + const NodeName& bypass_target_node, SublinkId bypass_target_sublink) override; - void StopProxying(SequenceNumber inbound_sequence_length, + void StopProxying(const OperationContext& context, + SequenceNumber inbound_sequence_length, SequenceNumber outbound_sequence_length) override; - void ProxyWillStop(SequenceNumber inbound_sequence_length) override; - void BypassPeerWithLink(SublinkId new_sublink, + void ProxyWillStop(const OperationContext& context, + SequenceNumber inbound_sequence_length) override; + void BypassPeerWithLink(const OperationContext& context, + SublinkId new_sublink, FragmentRef<RouterLinkState> new_link_state, SequenceNumber inbound_sequence_length) override; void StopProxyingToLocalPeer( + const OperationContext& context, SequenceNumber outbound_sequence_length) override; void Deactivate() override; std::string Describe() const override; diff --git a/chromium/third_party/ipcz/src/ipcz/message.cc b/chromium/third_party/ipcz/src/ipcz/message.cc index 1b686ed649f..fea6a45fbd5 100644 --- a/chromium/third_party/ipcz/src/ipcz/message.cc +++ b/chromium/third_party/ipcz/src/ipcz/message.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/message.h b/chromium/third_party/ipcz/src/ipcz/message.h index 180f207d49a..879afcd66c0 100644 --- a/chromium/third_party/ipcz/src/ipcz/message.h +++ b/chromium/third_party/ipcz/src/ipcz/message.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/message_macros/message_declaration_macros.h b/chromium/third_party/ipcz/src/ipcz/message_macros/message_declaration_macros.h index ecfc7243285..89b5e68dbe9 100644 --- a/chromium/third_party/ipcz/src/ipcz/message_macros/message_declaration_macros.h +++ b/chromium/third_party/ipcz/src/ipcz/message_macros/message_declaration_macros.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/message_macros/message_definition_macros.h b/chromium/third_party/ipcz/src/ipcz/message_macros/message_definition_macros.h index a373ca652ab..f4149ae8e41 100644 --- a/chromium/third_party/ipcz/src/ipcz/message_macros/message_definition_macros.h +++ b/chromium/third_party/ipcz/src/ipcz/message_macros/message_definition_macros.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/message_macros/message_listener_declaration_macros.h b/chromium/third_party/ipcz/src/ipcz/message_macros/message_listener_declaration_macros.h index 05e00a17634..a60eada1c7e 100644 --- a/chromium/third_party/ipcz/src/ipcz/message_macros/message_listener_declaration_macros.h +++ b/chromium/third_party/ipcz/src/ipcz/message_macros/message_listener_declaration_macros.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/message_macros/message_listener_definition_macros.h b/chromium/third_party/ipcz/src/ipcz/message_macros/message_listener_definition_macros.h index 1525d24f135..72b31e45195 100644 --- a/chromium/third_party/ipcz/src/ipcz/message_macros/message_listener_definition_macros.h +++ b/chromium/third_party/ipcz/src/ipcz/message_macros/message_listener_definition_macros.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/message_macros/message_listener_dispatch_macros.h b/chromium/third_party/ipcz/src/ipcz/message_macros/message_listener_dispatch_macros.h index 719944bbf52..5e8738420c5 100644 --- a/chromium/third_party/ipcz/src/ipcz/message_macros/message_listener_dispatch_macros.h +++ b/chromium/third_party/ipcz/src/ipcz/message_macros/message_listener_dispatch_macros.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/message_macros/message_params_declaration_macros.h b/chromium/third_party/ipcz/src/ipcz/message_macros/message_params_declaration_macros.h index b50ed1e391d..aea9cd54b01 100644 --- a/chromium/third_party/ipcz/src/ipcz/message_macros/message_params_declaration_macros.h +++ b/chromium/third_party/ipcz/src/ipcz/message_macros/message_params_declaration_macros.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/message_macros/message_params_definition_macros.h b/chromium/third_party/ipcz/src/ipcz/message_macros/message_params_definition_macros.h index b4bfa11bc90..04789edffa3 100644 --- a/chromium/third_party/ipcz/src/ipcz/message_macros/message_params_definition_macros.h +++ b/chromium/third_party/ipcz/src/ipcz/message_macros/message_params_definition_macros.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/message_macros/undef_message_macros.h b/chromium/third_party/ipcz/src/ipcz/message_macros/undef_message_macros.h index bc07bf5e66e..a5b260468aa 100644 --- a/chromium/third_party/ipcz/src/ipcz/message_macros/undef_message_macros.h +++ b/chromium/third_party/ipcz/src/ipcz/message_macros/undef_message_macros.h @@ -1,9 +1,9 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // no-include-guard-because-multiply-included -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/message_test.cc b/chromium/third_party/ipcz/src/ipcz/message_test.cc index 9b21738f73a..ee85d11103e 100644 --- a/chromium/third_party/ipcz/src/ipcz/message_test.cc +++ b/chromium/third_party/ipcz/src/ipcz/message_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/monitored_atomic.h b/chromium/third_party/ipcz/src/ipcz/monitored_atomic.h new file mode 100644 index 00000000000..b8ec5c4f316 --- /dev/null +++ b/chromium/third_party/ipcz/src/ipcz/monitored_atomic.h @@ -0,0 +1,77 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPCZ_SRC_IPCZ_MONITORED_VALUE_H_ +#define IPCZ_SRC_IPCZ_MONITORED_VALUE_H_ + +#include <atomic> +#include <limits> +#include <type_traits> + +namespace ipcz { + +// MonitoredAtomic is a trivial wrapper around around an atomic unsigned +// integral value, with the high bit reserved for primitive communication +// between one producer and any number of concurrent consumers of the value. +// +// Consumers can atomically query the value while simultaneously signaling that +// they want to be notified about the next time the value changes. Producers can +// atomically update the value while simulataneously querying (and resetting) +// the consumer's interest in being notified about the change. +template <typename T> +class MonitoredAtomic { + static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>, + "MonitoredAtomic requires an unsigned integral type"); + + public: + struct State { + T value; + bool monitored; + }; + + static constexpr T kMaxValue = std::numeric_limits<T>::max() >> 1; + static constexpr T kMonitorBit = kMaxValue + 1; + + MonitoredAtomic() noexcept = default; + explicit MonitoredAtomic(T value) noexcept : value_(value) {} + + // Returns a best-effort snapshot of the most recent underlying value. If + // `monitor` is true in `options`, then the stored value is also atomically + // flagged for monitoring. + struct QueryOptions { + bool monitor; + }; + State Query(const QueryOptions& options) { + T value = value_.load(std::memory_order_relaxed); + while (options.monitor && !IsMonitored(value) && + !value_.compare_exchange_weak(value, Monitored(value), + std::memory_order_release, + std::memory_order_relaxed)) { + } + return {.value = Unmonitored(value), .monitored = IsMonitored(value)}; + } + + // Stores a new underlying value, resetting the monitor bit if it was set. + // Returns a boolean indicating whether the monitor bit was set. + [[nodiscard]] bool UpdateValueAndResetMonitor(T value) { + T old_value = value_.load(std::memory_order_relaxed); + while (value != old_value && + !value_.compare_exchange_weak(old_value, value, + std::memory_order_release, + std::memory_order_relaxed)) { + } + return IsMonitored(old_value); + } + + private: + static bool IsMonitored(T value) { return (value & kMonitorBit) != 0; } + static T Monitored(T value) { return value | kMonitorBit; } + static T Unmonitored(T value) { return value & kMaxValue; } + + std::atomic<T> value_{0}; +}; + +} // namespace ipcz + +#endif // IPCZ_SRC_IPCZ_MONITORED_VALUE_H_ diff --git a/chromium/third_party/ipcz/src/ipcz/node.cc b/chromium/third_party/ipcz/src/ipcz/node.cc index 19e584a4362..f2fc29e5454 100644 --- a/chromium/third_party/ipcz/src/ipcz/node.cc +++ b/chromium/third_party/ipcz/src/ipcz/node.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -16,12 +16,60 @@ #include "ipcz/portal.h" #include "ipcz/router.h" #include "third_party/abseil-cpp/absl/base/macros.h" +#include "third_party/abseil-cpp/absl/container/inlined_vector.h" #include "third_party/abseil-cpp/absl/synchronization/mutex.h" +#include "third_party/abseil-cpp/absl/types/span.h" #include "util/log.h" #include "util/ref_counted.h" namespace ipcz { +// A pending introduction tracks progress of one or more outstanding +// introduction requests for a single node in the system. +class Node::PendingIntroduction { + public: + // Constructs a new object to track introduction a specific node. `broker` + // is the sequence of broker nodes queried for the introduction. + explicit PendingIntroduction(absl::Span<const Ref<NodeLink>> brokers) { + pending_replies_.reserve(brokers.size()); + for (const auto& broker : brokers) { + pending_replies_.insert(broker->remote_node_name()); + } + } + + // Indicates that all pending responses have come back indicating failure, + // and that the pending introduction itself has failed. + bool failed() const { return pending_replies_.empty(); } + + // Marks a specific broker as having responded with rejection. + void NotifyFailureFrom(NodeLink& rejecting_broker) { + pending_replies_.erase(rejecting_broker.remote_node_name()); + } + + // Registers a new callback to be invoked once the introduction process has + // completed, regardless of success or failure. + void AddCallback(Node::EstablishLinkCallback callback) { + callbacks_.push_back(std::move(callback)); + } + + // Runs all callbacks associated with this introduction. If the introduction + // failed, `result` will be null. Otherwise it's a link to the newly + // introduced remote node. + void Finish(NodeLink* result) { + for (auto& callback : callbacks_) { + callback(result); + } + } + + private: + // Set of brokers from whom we're still expecting a reply for this pending + // introduction request. + absl::flat_hash_set<NodeName> pending_replies_; + + // Callbacks to be invoked once the introduction is finished. + std::vector<EstablishLinkCallback> callbacks_; +}; + Node::Node(Type type, const IpczDriver& driver, IpczDriverHandle driver_node) : type_(type), driver_(driver), driver_node_(driver_node) { if (type_ == Type::kBroker) { @@ -80,46 +128,66 @@ Ref<NodeLink> Node::GetBrokerLink() { return broker_link_; } -void Node::SetBrokerLink(Ref<NodeLink> link) { - std::vector<BrokerLinkCallback> callbacks; - { - absl::MutexLock lock(&mutex_); - ABSL_ASSERT(!broker_link_); - broker_link_ = link; - broker_link_callbacks_.swap(callbacks); - } - - for (auto& callback : callbacks) { - callback(link); - } -} - void Node::SetAssignedName(const NodeName& name) { absl::MutexLock lock(&mutex_); ABSL_ASSERT(!assigned_name_.is_valid()); assigned_name_ = name; } -bool Node::AddLink(const NodeName& remote_node_name, Ref<NodeLink> link) { +bool Node::AddConnection(const NodeName& remote_node_name, + Connection connection) { + std::vector<BrokerLinkCallback> callbacks; { - absl::MutexLock lock(&mutex_); - auto [it, inserted] = node_links_.insert({remote_node_name, link}); - if (inserted) { - return true; + absl::ReleasableMutexLock lock(&mutex_); + auto [it, inserted] = connections_.insert({remote_node_name, connection}); + if (!inserted) { + lock.Release(); + + const OperationContext context{OperationContext::kTransportNotification}; + connection.link->Deactivate(context); + return false; + } + + const bool remote_is_broker = + connection.link->remote_node_type() == Type::kBroker; + const bool local_is_broker = type_ == Type::kBroker; + if (local_is_broker && remote_is_broker) { + // We're a broker, and this is a link to some other broker. We retain a + // separate mapping of other brokers so they can be consulted for + // introductions. + other_brokers_.insert({remote_node_name, connection.link}); + } else if (remote_is_broker) { + // The first connection accepted by a non-broker must be a connection to + // its own broker. + ABSL_ASSERT(connections_.size() == 1); + ABSL_ASSERT(!broker_link_); + broker_link_ = connection.link; + broker_link_callbacks_.swap(callbacks); } } - link->Deactivate(); - return false; + for (auto& callback : callbacks) { + callback(connection.link); + } + return true; +} + +absl::optional<Node::Connection> Node::GetConnection(const NodeName& name) { + absl::MutexLock lock(&mutex_); + auto it = connections_.find(name); + if (it == connections_.end()) { + return absl::nullopt; + } + return it->second; } Ref<NodeLink> Node::GetLink(const NodeName& name) { absl::MutexLock lock(&mutex_); - auto it = node_links_.find(name); - if (it == node_links_.end()) { + auto it = connections_.find(name); + if (it == connections_.end()) { return nullptr; } - return it->second; + return it->second.link; } NodeName Node::GenerateRandomName() const { @@ -152,30 +220,50 @@ void Node::AllocateSharedMemory(size_t size, } void Node::EstablishLink(const NodeName& name, EstablishLinkCallback callback) { - Ref<NodeLink> broker; - Ref<NodeLink> link; + Ref<NodeLink> existing_link; + absl::InlinedVector<Ref<NodeLink>, 2> brokers_to_query; { absl::MutexLock lock(&mutex_); - broker = broker_link_; - auto it = node_links_.find(name); - if (it != node_links_.end()) { - link = it->second; - } else if (type_ == Type::kNormal && broker) { - auto [pending_it, inserted] = pending_introductions_.insert({name, {}}); - pending_it->second.push_back(std::move(callback)); - if (!inserted) { - // There's already an introduction request out for this node, so there's - // nothing more we need to do. - return; + auto it = connections_.find(name); + if (it != connections_.end()) { + existing_link = it->second.link; + } else { + if (type_ == Type::kNormal && broker_link_) { + brokers_to_query.push_back(broker_link_); + } else if (!other_brokers_.empty()) { + ABSL_ASSERT(type_ == Type::kBroker); + brokers_to_query.reserve(other_brokers_.size()); + for (const auto& [broker_name, link] : other_brokers_) { + brokers_to_query.push_back(link); + } + } + + if (!brokers_to_query.empty()) { + auto [pending_it, inserted] = + pending_introductions_.insert({name, nullptr}); + auto& intro = pending_it->second; + if (!intro) { + intro = std::make_unique<PendingIntroduction>( + absl::MakeSpan(brokers_to_query)); + } + intro->AddCallback(std::move(callback)); + if (!inserted) { + // There was already a pending introduction we can wait for. + return; + } } } } - if (broker && !link) { - broker->RequestIntroduction(name); - } else { - callback(link.get()); + if (!brokers_to_query.empty()) { + for (const auto& broker : brokers_to_query) { + broker->RequestIntroduction(name); + } + return; } + + // NOTE: `existing_link` may be null here, implying that we have failed. + callback(existing_link.get()); } void Node::HandleIntroductionRequest(NodeLink& from_node_link, @@ -189,50 +277,62 @@ void Node::HandleIntroductionRequest(NodeLink& from_node_link, << " received introduction request for " << for_node.ToString() << " from " << requestor.ToString(); - // A key which uniquely identifies the pair of nodes being introduced - // regardless of who requested the introduction. - const auto key = (requestor < for_node) - ? IntroductionKey(requestor, for_node) - : IntroductionKey(for_node, requestor); - - Ref<NodeLink> target_link; - { - absl::MutexLock lock(&mutex_); - auto it = node_links_.find(for_node); - if (it != node_links_.end()) { - target_link = it->second; - - auto [intro_it, inserted] = in_progress_introductions_.insert(key); - if (!inserted) { - // We're already introducing the same two nodes, so drop this request. + const absl::optional<Connection> target_connection = GetConnection(for_node); + if (!target_connection) { + // We are not familiar with the requested node. Attempt to establish our own + // link to it first, then try again. + EstablishLink(for_node, [self = WrapRefCounted(this), + requestor = WrapRefCounted(&from_node_link), + name = for_node](NodeLink* link) { + if (!link) { + requestor->RejectIntroduction(name); return; } - } + + self->HandleIntroductionRequest(*requestor, name); + }); + return; } - if (!target_link) { - from_node_link.RejectIntroduction(for_node); + const bool is_target_in_network = !target_connection->broker; + const bool is_target_broker = + target_connection->link == target_connection->broker; + const bool is_requestor_broker = + from_node_link.remote_node_type() == Type::kBroker; + if (is_requestor_broker && is_target_broker) { + DLOG(ERROR) << "Invalid introduction request from broker " + << requestor.ToString() << " for broker " + << for_node.ToString(); return; } - DriverMemoryWithMapping buffer = NodeLinkMemory::AllocateMemory(driver_); - auto [transport_for_target, transport_for_requestor] = - DriverTransport::CreatePair(driver_, target_link->transport().get(), - from_node_link.transport().get()); - target_link->AcceptIntroduction( - requestor, LinkSide::kA, from_node_link.remote_protocol_version(), - std::move(transport_for_target), buffer.memory.Clone()); - from_node_link.AcceptIntroduction( - for_node, LinkSide::kB, target_link->remote_protocol_version(), - std::move(transport_for_requestor), std::move(buffer.memory)); + if (is_target_broker || is_requestor_broker || is_target_in_network || + target_connection->broker->link_side().is_side_a()) { + // If one of the two nodes being introduced is a broker, or if the target + // is in-network (which implies the requestor is too, if it's not a broker) + // then we are the only node that can introduce these two nodes. + // + // Otherwise if this is an introduction between two non-brokers in separate + // networks, by convention we can only perform the introduction if we're on + // side A of the link between the two relevant brokers. + IntroduceRemoteNodes(from_node_link, *target_connection->link); + return; + } - absl::MutexLock lock(&mutex_); - in_progress_introductions_.erase(key); + // This is an introduction between two non-brokers in separate networks, and + // we (one of the networks' brokers) are on side B of the link to the other + // network's broker. This introduction is therefore the other broker's + // responsibility. + msg::RequestIndirectIntroduction request; + request.params().source_node = from_node_link.remote_node_name(); + request.params().target_node = target_connection->link->remote_node_name(); + target_connection->broker->Transmit(request); } void Node::AcceptIntroduction(NodeLink& from_node_link, const NodeName& name, LinkSide side, + Node::Type remote_node_type, uint32_t remote_protocol_version, Ref<DriverTransport> transport, Ref<NodeLinkMemory> memory) { @@ -247,14 +347,25 @@ void Node::AcceptIntroduction(NodeLink& from_node_link, << from_node_link.remote_node_name().ToString(); Ref<NodeLink> new_link = NodeLink::CreateInactive( - WrapRefCounted(this), side, local_name, name, Type::kNormal, + WrapRefCounted(this), side, local_name, name, remote_node_type, remote_protocol_version, transport, memory); ABSL_ASSERT(new_link); - std::vector<EstablishLinkCallback> callbacks; + std::unique_ptr<PendingIntroduction> pending_introduction; { absl::MutexLock lock(&mutex_); - auto [link_it, inserted] = node_links_.insert({name, new_link}); + if (type_ == Type::kNormal && !broker_link_) { + // If we've lost our broker connection, we should ignore any further + // introductions that arrive. + return; + } + + auto [connection_it, inserted] = + connections_.insert({name, + { + .link = new_link, + .broker = WrapRefCounted(&from_node_link), + }}); if (!inserted) { // If both nodes race to request an introduction to each other, the // broker may send redundant introductions. It does however take care to @@ -269,34 +380,39 @@ void Node::AcceptIntroduction(NodeLink& from_node_link, // requested it. auto it = pending_introductions_.find(name); if (it != pending_introductions_.end()) { - callbacks = std::move(it->second); + pending_introduction = std::move(it->second); pending_introductions_.erase(it); } } new_link->Activate(); - for (auto& callback : callbacks) { - callback(new_link.get()); + if (pending_introduction) { + pending_introduction->Finish(new_link.get()); } } -bool Node::CancelIntroduction(const NodeName& name) { - std::vector<EstablishLinkCallback> callbacks; +void Node::NotifyIntroductionFailed(NodeLink& from_broker, + const NodeName& name) { + std::unique_ptr<PendingIntroduction> failed_introduction; { absl::MutexLock lock(&mutex_); auto it = pending_introductions_.find(name); if (it == pending_introductions_.end()) { - return false; + return; } - callbacks = std::move(it->second); - pending_introductions_.erase(it); - } - for (auto& callback : callbacks) { - callback(nullptr); + auto& intro = it->second; + intro->NotifyFailureFrom(from_broker); + if (!intro->failed()) { + // We're still waiting for replies from one or more other brokers. + return; + } + + failed_introduction = std::move(intro); + pending_introductions_.erase(it); } - return true; + failed_introduction->Finish(nullptr); } bool Node::RelayMessage(const NodeName& from_node, msg::RelayMessage& relay) { @@ -324,25 +440,29 @@ bool Node::AcceptRelayedMessage(msg::AcceptRelayedMessage& accept) { return true; } -void Node::DropLink(const NodeName& name) { +void Node::DropConnection(const OperationContext& context, + const NodeName& name) { Ref<NodeLink> link; + std::vector<NodeName> pending_introductions; bool lost_broker = false; { absl::MutexLock lock(&mutex_); - auto it = node_links_.find(name); - if (it == node_links_.end()) { + auto it = connections_.find(name); + if (it == connections_.end()) { return; } - link = std::move(it->second); - node_links_.erase(it); + link = std::move(it->second.link); + connections_.erase(it); const NodeName& local_name = link->local_node_name(); DVLOG(4) << "Node " << local_name.ToString() << " dropping " - << " link to " << link->remote_node_name().ToString(); + << "link to " << link->remote_node_name().ToString(); if (link == broker_link_) { DVLOG(4) << "Node " << local_name.ToString() << " lost its broker link"; broker_link_.reset(); lost_broker = true; + } else if (link->remote_node_type() == Type::kBroker) { + other_brokers_.erase(link->remote_node_name()); } if (link == allocation_delegate_link_) { @@ -350,12 +470,24 @@ void Node::DropLink(const NodeName& name) { << " lost its allocation delegate"; allocation_delegate_link_.reset(); } + + // Accumulate the set of currently pending introductions. If any of them are + // awaiting a response from the dropped link, their expectations will be + // updated accordingly by NotifyIntroductionFailed() below. + pending_introductions.reserve(pending_introductions_.size()); + for (auto& [target, intro] : pending_introductions_) { + pending_introductions.push_back(target); + } } - link->Deactivate(); + link->Deactivate(context); if (lost_broker) { CancelAllIntroductions(); + } else { + for (auto& target : pending_introductions) { + NotifyIntroductionFailed(*link, target); + } } } @@ -374,17 +506,65 @@ void Node::WaitForBrokerLinkAsync(BrokerLinkCallback callback) { callback(std::move(broker_link)); } +bool Node::HandleIndirectIntroductionRequest(NodeLink& from_node_link, + const NodeName& our_node, + const NodeName& their_node) { + // Enforced by NodeLink before dispatching to this Node. + ABSL_ASSERT(type_ == Type::kBroker); + ABSL_ASSERT(from_node_link.remote_node_type() == Type::kBroker); + ABSL_ASSERT(from_node_link.link_side().is_side_a()); + + absl::optional<Connection> connection_to_their_node = + GetConnection(their_node); + if (!connection_to_their_node) { + // We need to establish our own direct connection to `their_node` before we + // can help introduce it to `our_node`. + EstablishLink(their_node, [self = WrapRefCounted(this), + their_broker = WrapRefCounted(&from_node_link), + our_node, their_node](NodeLink* link) { + if (!link) { + // If we could not get our own link to the identified node, then we + // can't complete the introduction request. Notify our own node of the + // failure in case they were awaiting such an introduction. + absl::optional<Connection> connection_to_our_node = + self->GetConnection(our_node); + if (connection_to_our_node) { + connection_to_our_node->link->RejectIntroduction(their_node); + } + return; + } + self->HandleIndirectIntroductionRequest(*their_broker, our_node, + their_node); + }); + return true; + } + + absl::optional<Connection> connection_to_our_node = GetConnection(our_node); + if (!connection_to_our_node) { + // We may have lost this connection for any number of reasons, but in any + // case we can't fulfill the request. + connection_to_their_node->link->RejectIntroduction(our_node); + return true; + } + + IntroduceRemoteNodes(*connection_to_our_node->link, + *connection_to_their_node->link); + return true; +} + void Node::ShutDown() { - NodeLinkMap node_links; + ConnectionMap connections; { absl::MutexLock lock(&mutex_); - std::swap(node_links_, node_links); + connections_.swap(connections); broker_link_.reset(); allocation_delegate_link_.reset(); + other_brokers_.clear(); } - for (const auto& entry : node_links) { - entry.second->Deactivate(); + const OperationContext context{OperationContext::kAPICall}; + for (const auto& entry : connections) { + entry.second.link->Deactivate(context); } CancelAllIntroductions(); @@ -396,12 +576,41 @@ void Node::CancelAllIntroductions() { absl::MutexLock lock(&mutex_); introductions.swap(pending_introductions_); } + for (auto& [name, intro] : introductions) { + intro->Finish(nullptr); + } +} - for (auto& [name, callbacks] : introductions) { - for (auto& callback : callbacks) { - callback(nullptr); +void Node::IntroduceRemoteNodes(NodeLink& first, NodeLink& second) { + // Ensure that no other thread does the same introduction concurrently. + const NodeName& first_name = first.remote_node_name(); + const NodeName& second_name = second.remote_node_name(); + const auto key = (first_name < second_name) + ? IntroductionKey(first_name, second_name) + : IntroductionKey(second_name, first_name); + { + absl::MutexLock lock(&mutex_); + auto [it, inserted] = in_progress_introductions_.insert(key); + if (!inserted) { + return; } } + + DriverMemoryWithMapping buffer = NodeLinkMemory::AllocateMemory(driver_); + auto [transport_for_first_node, transport_for_second_node] = + DriverTransport::CreatePair(driver_, first.transport().get(), + second.transport().get()); + first.AcceptIntroduction(second_name, LinkSide::kA, second.remote_node_type(), + second.remote_protocol_version(), + std::move(transport_for_first_node), + buffer.memory.Clone()); + second.AcceptIntroduction(first_name, LinkSide::kB, first.remote_node_type(), + first.remote_protocol_version(), + std::move(transport_for_second_node), + std::move(buffer.memory)); + + absl::MutexLock lock(&mutex_); + in_progress_introductions_.erase(key); } } // namespace ipcz diff --git a/chromium/third_party/ipcz/src/ipcz/node.h b/chromium/third_party/ipcz/src/ipcz/node.h index 14bea6c7c52..ee9a223f62a 100644 --- a/chromium/third_party/ipcz/src/ipcz/node.h +++ b/chromium/third_party/ipcz/src/ipcz/node.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,6 +6,9 @@ #define IPCZ_SRC_IPCZ_NODE_H_ #include <functional> +#include <memory> +#include <utility> +#include <vector> #include "ipcz/api_object.h" #include "ipcz/driver_memory.h" @@ -13,9 +16,12 @@ #include "ipcz/link_side.h" #include "ipcz/node_messages.h" #include "ipcz/node_name.h" +#include "ipcz/node_type.h" +#include "ipcz/operation_context.h" #include "third_party/abseil-cpp/absl/container/flat_hash_map.h" #include "third_party/abseil-cpp/absl/container/flat_hash_set.h" #include "third_party/abseil-cpp/absl/synchronization/mutex.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/abseil-cpp/absl/types/span.h" namespace ipcz { @@ -29,17 +35,19 @@ class NodeLinkMemory; // introduced to each other exclusively through such brokers. class Node : public APIObjectImpl<Node, APIObject::kNode> { public: - enum class Type { - // A broker node assigns its own name and is able to assign names to other - // nodes upon connection. Brokers are trusted to introduce nodes to each - // other upon request, and brokers may connect to other brokers in order to - // share information and effectively bridge two node networks together. - kBroker, - - // A "normal" (i.e. non-broker) node is assigned a permanent name by the - // first broker node it connects to, and it can only make contact with other - // nodes by requesting an introduction from that broker. - kNormal, + using Type = NodeType; + + // State regarding a connection to a single remote node. + struct Connection { + // The NodeLink used to communicate with the remote node. + Ref<NodeLink> link; + + // The NodeLink used to communicate with the broker of the remote node's + // network. If the remote node belongs to the same network as the local + // node, then this is the same link the local node's `broker_link_`. If the + // local node *is* the broker for the remote node on `link`, then this link + // is null. + Ref<NodeLink> broker; }; // Constructs a new node of the given `type`, using `driver` to support IPC. @@ -69,30 +77,23 @@ class Node : public APIObjectImpl<Node, APIObject::kNode> { // Gets a reference to the node's broker link, if it has one. Ref<NodeLink> GetBrokerLink(); - // Sets this node's broker link, which is used e.g. to make introduction - // requests. - // - // This is called by a NodeConnector implementation after accepting a valid - // handshake message from a broker node, and `link` will be used as this - // node's permanent broker. - // - // Note that like any other NodeLink used by this Node, the same `link` must - // also be registered via AddLink() to associate it with its remote Node's - // name. This is also done by NodeConnector. - void SetBrokerLink(Ref<NodeLink> link); - // Sets this node's assigned name as given by a broker. NodeConnector is // responsible for calling on non-broker Nodes this after receiving the // expected handshake from a broker. Must not be called on broker nodes, as // they assign their own name at construction time. void SetAssignedName(const NodeName& name); - // Registers a new NodeLink for the given `remote_node_name`. - bool AddLink(const NodeName& remote_node_name, Ref<NodeLink> link); + // Registers a new connection for the given `remote_node_name`. + bool AddConnection(const NodeName& remote_node_name, Connection connection); + + // Returns a copy of the Connection to the remote node named by `name`, or + // null if this node has no connection to that node. + absl::optional<Node::Connection> GetConnection(const NodeName& name); // Returns a reference to the NodeLink used by this Node to communicate with // the remote node identified by `name`; or null if this node has no NodeLink - // connected to that node. + // connected to that node. This is shorthand for GetConnection() in the common + // case where the caller only wants the underlying NodeLink. Ref<NodeLink> GetLink(const NodeName& name); // Generates a new random NodeName using this node's driver as a source of @@ -138,14 +139,15 @@ class Node : public APIObjectImpl<Node, APIObject::kNode> { void AcceptIntroduction(NodeLink& from_node_link, const NodeName& name, LinkSide side, + Node::Type remote_node_type, uint32_t remote_protocol_version, Ref<DriverTransport> transport, Ref<NodeLinkMemory> memory); - // Handles a rejected introduction from the broker. This is called on a - // non-broker node that previously requested an introduction to `name` if - // the broker could not satisfy the request. - bool CancelIntroduction(const NodeName& name); + // Handles a rejected introduction for the node named `name` from the + // identified broker. This is called on a node that previously requested an + // introduction if the broker is unable or unwilling to satisfy the request. + void NotifyIntroductionFailed(NodeLink& from_broker, const NodeName& name); // Relays a message to its destination on behalf of `from_node`. bool RelayMessage(const NodeName& from_node, msg::RelayMessage& relay); @@ -154,8 +156,8 @@ class Node : public APIObjectImpl<Node, APIObject::kNode> { // the relay source directly. bool AcceptRelayedMessage(msg::AcceptRelayedMessage& accept); - // Drops this node's link to the named node, if one exists. - void DropLink(const NodeName& name); + // Drops this node's connection to the named node, if one exists. + void DropConnection(const OperationContext& context, const NodeName& name); // Asynchronously waits for this Node to acquire a broker link and then // invokes `callback` with it. If this node already has a broker link then the @@ -163,7 +165,17 @@ class Node : public APIObjectImpl<Node, APIObject::kNode> { using BrokerLinkCallback = std::function<void(Ref<NodeLink>)>; void WaitForBrokerLinkAsync(BrokerLinkCallback callback); + // Processes a request for an indirect cross-network node introduction. The + // request was sent by `from_node_link` (a link to another broker) and is + // asking us to introduce `our_node` within our network to `their_node` in the + // the requestor's network. + bool HandleIndirectIntroductionRequest(NodeLink& from_node_link, + const NodeName& our_node, + const NodeName& their_node); + private: + class PendingIntroduction; + ~Node() override; // Deactivates all NodeLinks and their underlying driver transports in @@ -174,6 +186,10 @@ class Node : public APIObjectImpl<Node, APIObject::kNode> { // failure. void CancelAllIntroductions(); + // Creates a new transport and link memory and sends introduction messages to + // introduce the remote node on `first` to the remote node on `second`. + void IntroduceRemoteNodes(NodeLink& first, NodeLink& second); + const Type type_; const IpczDriver& driver_; const IpczDriverHandle driver_node_; @@ -186,7 +202,9 @@ class Node : public APIObjectImpl<Node, APIObject::kNode> { NodeName assigned_name_ ABSL_GUARDED_BY(mutex_); // A link to the first broker this node connected to. If this link is broken, - // the node will lose all its other links too. + // the node will lose all its other links too. This is always null on broker + // nodes, though brokers may keep track of links to other brokers within + // `other_brokers_`. Ref<NodeLink> broker_link_ ABSL_GUARDED_BY(mutex_); // A link over which all internal shared memory allocation is delegated. If @@ -199,14 +217,14 @@ class Node : public APIObjectImpl<Node, APIObject::kNode> { // if this is a non-broker node. If this is a broker node, these links are // either assigned by this node itself, or received from other brokers in the // system. - using NodeLinkMap = absl::flat_hash_map<NodeName, Ref<NodeLink>>; - NodeLinkMap node_links_ ABSL_GUARDED_BY(mutex_); + using ConnectionMap = absl::flat_hash_map<NodeName, Connection>; + ConnectionMap connections_ ABSL_GUARDED_BY(mutex_); - // A map of other nodes to which this node is waiting for an introduction from - // `broker_link_`. Once such an introduction is received, all callbacks for - // that NodeName are executed. + // A map of other nodes to which this node is waiting for an introduction, + // either from its own broker or (if we are a broker) all the other known + // brokers we're connected to. using PendingIntroductionMap = - absl::flat_hash_map<NodeName, std::vector<EstablishLinkCallback>>; + absl::flat_hash_map<NodeName, std::unique_ptr<PendingIntroduction>>; PendingIntroductionMap pending_introductions_ ABSL_GUARDED_BY(mutex_); // Nodes may race to request introductions to each other from the same broker. @@ -243,6 +261,12 @@ class Node : public APIObjectImpl<Node, APIObject::kNode> { // broker link. std::vector<BrokerLinkCallback> broker_link_callbacks_ ABSL_GUARDED_BY(mutex_); + + // Mapping of links to other known brokers in the system. This is the subset + // of `links_` which corresponds to remote broker nodes NOT in this node's own + // network. This map can only be non-empty on broker nodes. + absl::flat_hash_map<NodeName, Ref<NodeLink>> other_brokers_ + ABSL_GUARDED_BY(mutex_); }; } // namespace ipcz diff --git a/chromium/third_party/ipcz/src/ipcz/node_connector.cc b/chromium/third_party/ipcz/src/ipcz/node_connector.cc index fc44b57f135..41a64de551d 100644 --- a/chromium/third_party/ipcz/src/ipcz/node_connector.cc +++ b/chromium/third_party/ipcz/src/ipcz/node_connector.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -14,6 +14,7 @@ #include "ipcz/link_type.h" #include "ipcz/node_link.h" #include "ipcz/node_link_memory.h" +#include "ipcz/operation_context.h" #include "ipcz/portal.h" #include "ipcz/remote_router_link.h" #include "ipcz/router.h" @@ -71,13 +72,12 @@ class NodeConnectorForBrokerToNonBroker : public NodeConnector { << broker_name_.ToString() << " from new node " << new_remote_node_name_.ToString(); - AcceptConnection( - NodeLink::CreateActive( - node_, LinkSide::kA, broker_name_, new_remote_node_name_, - Node::Type::kNormal, connect.params().protocol_version, transport_, - NodeLinkMemory::Create(node_, - std::move(link_memory_allocation_.mapping))), - LinkSide::kA, connect.params().num_initial_portals); + Ref<NodeLink> link = NodeLink::CreateActive( + node_, LinkSide::kA, broker_name_, new_remote_node_name_, + Node::Type::kNormal, connect.params().protocol_version, transport_, + NodeLinkMemory::Create(node_, + std::move(link_memory_allocation_.mapping))); + AcceptConnection({.link = link}, connect.params().num_initial_portals); return true; } @@ -131,12 +131,11 @@ class NodeConnectorForNonBrokerToBroker : public NodeConnector { connect.params().protocol_version, transport_, NodeLinkMemory::Create(node_, buffer_memory.Map())); node_->SetAssignedName(connect.params().receiver_name); - node_->SetBrokerLink(new_link); if ((flags_ & IPCZ_CONNECT_NODE_TO_ALLOCATION_DELEGATE) != 0) { node_->SetAllocationDelegate(new_link); } - AcceptConnection(std::move(new_link), LinkSide::kB, + AcceptConnection({.link = new_link, .broker = new_link}, connect.params().num_initial_portals); return true; } @@ -179,13 +178,13 @@ class NodeConnectorForReferrer : public NodeConnector { broker_link_->ReferNonBroker( std::move(transport_for_broker_), checked_cast<uint32_t>(num_portals()), - [connector = WrapRefCounted(this)]( + [connector = WrapRefCounted(this), broker = broker_link_]( Ref<NodeLink> link_to_referred_node, uint32_t remote_num_initial_portals) { if (link_to_referred_node) { - connector->AcceptConnection(std::move(link_to_referred_node), - LinkSide::kA, - remote_num_initial_portals); + connector->AcceptConnection( + {.link = link_to_referred_node, .broker = broker}, + remote_num_initial_portals); } else { connector->RejectConnection(); } @@ -255,11 +254,14 @@ class NodeConnectorForReferredNonBroker : public NodeConnector { broker_protocol_version, transport_, NodeLinkMemory::Create(node_, broker_buffer.Map())); node_->SetAssignedName(connect.params().name); - node_->SetBrokerLink(broker_link); if ((flags_ & IPCZ_CONNECT_NODE_TO_ALLOCATION_DELEGATE) != 0) { node_->SetAllocationDelegate(broker_link); } - node_->AddLink(connect.params().broker_name, std::move(broker_link)); + node_->AddConnection(connect.params().broker_name, + { + .link = broker_link, + .broker = broker_link, + }); const uint32_t referrer_protocol_version = std::min( connect.params().referrer_protocol_version, msg::kProtocolVersion); @@ -269,7 +271,7 @@ class NodeConnectorForReferredNonBroker : public NodeConnector { referrer_protocol_version, std::move(referrer_transport), NodeLinkMemory::Create(node_, referrer_buffer.Map())); - AcceptConnection(referrer_link, LinkSide::kB, + AcceptConnection({.link = referrer_link, .broker = broker_link}, connect.params().num_initial_portals); referrer_link->Activate(); return true; @@ -324,7 +326,7 @@ class NodeConnectorForBrokerReferral : public NodeConnector { node_, LinkSide::kA, broker_name_, referred_node_name_, Node::Type::kNormal, protocol_version, transport_, NodeLinkMemory::Create(node_, std::move(link_memory_.mapping))); - AcceptConnection(link_to_referree, LinkSide::kA, /*num_remote_portals=*/0); + AcceptConnection({.link = link_to_referree}, /*num_remote_portals=*/0); // Now we can create a new link to introduce both clients -- the referrer // and the referree -- to each other. @@ -391,6 +393,75 @@ class NodeConnectorForBrokerReferral : public NodeConnector { NodeLinkMemory::AllocateMemory(node_->driver())}; }; +class NodeConnectorForBrokerToBroker : public NodeConnector { + public: + NodeConnectorForBrokerToBroker(Ref<Node> node, + Ref<DriverTransport> transport, + IpczConnectNodeFlags flags, + std::vector<Ref<Portal>> waiting_portals, + ConnectCallback callback) + : NodeConnector(std::move(node), + std::move(transport), + flags, + std::move(waiting_portals), + std::move(callback)), + link_memory_allocation_( + NodeLinkMemory::AllocateMemory(node_->driver())) { + ABSL_ASSERT(link_memory_allocation_.mapping.is_valid()); + } + + ~NodeConnectorForBrokerToBroker() override = default; + + // NodeConnector: + bool Connect() override { + DVLOG(4) << "Sending direct ConnectFromBrokerToBroker from broker " + << local_name_.ToString() << " with " << num_portals() + << " initial portals"; + + ABSL_ASSERT(node_->type() == Node::Type::kBroker); + msg::ConnectFromBrokerToBroker connect; + connect.params().name = local_name_; + connect.params().protocol_version = msg::kProtocolVersion; + connect.params().num_initial_portals = + checked_cast<uint32_t>(num_portals()); + connect.params().buffer = connect.AppendDriverObject( + link_memory_allocation_.memory.TakeDriverObject()); + return IPCZ_RESULT_OK == transport_->Transmit(connect); + } + + // NodeMessageListener overrides: + bool OnConnectFromBrokerToBroker( + msg::ConnectFromBrokerToBroker& connect) override { + const NodeName& remote_name = connect.params().name; + DVLOG(4) << "Accepting ConnectFromBrokerToBroker on broker " + << local_name_.ToString() << " from other broker " + << remote_name.ToString(); + + const LinkSide this_side = + remote_name < local_name_ ? LinkSide::kA : LinkSide::kB; + DriverMemory their_memory( + connect.TakeDriverObject(connect.params().buffer)); + if (!their_memory.is_valid()) { + return false; + } + + DriverMemoryMapping primary_buffer_mapping = + this_side.is_side_a() ? std::move(link_memory_allocation_.mapping) + : their_memory.Map(); + Ref<NodeLink> link = NodeLink::CreateActive( + node_, this_side, local_name_, remote_name, Node::Type::kBroker, + connect.params().protocol_version, transport_, + NodeLinkMemory::Create(node_, std::move(primary_buffer_mapping))); + AcceptConnection({.link = link, .broker = link}, + connect.params().num_initial_portals); + return true; + } + + private: + const NodeName local_name_{node_->GetAssignedName()}; + DriverMemoryWithMapping link_memory_allocation_; +}; + std::pair<Ref<NodeConnector>, IpczResult> CreateConnector( Ref<Node> node, Ref<DriverTransport> transport, @@ -404,9 +475,10 @@ std::pair<Ref<NodeConnector>, IpczResult> CreateConnector( const bool inherit_broker = (flags & IPCZ_CONNECT_NODE_INHERIT_BROKER) != 0; if (from_broker) { if (to_broker) { - // TODO: Implement broker-to-broker connections. - ABSL_ASSERT(false); - return {nullptr, IPCZ_RESULT_INVALID_ARGUMENT}; + return {MakeRefCounted<NodeConnectorForBrokerToBroker>( + std::move(node), std::move(transport), flags, initial_portals, + std::move(callback)), + IPCZ_RESULT_OK}; } return {MakeRefCounted<NodeConnectorForBrokerToNonBroker>( @@ -511,21 +583,20 @@ NodeConnector::NodeConnector(Ref<Node> node, NodeConnector::~NodeConnector() = default; -void NodeConnector::AcceptConnection(Ref<NodeLink> new_link, - LinkSide link_side, +void NodeConnector::AcceptConnection(Node::Connection connection, uint32_t num_remote_portals) { - node_->AddLink(new_link->remote_node_name(), new_link); + node_->AddConnection(connection.link->remote_node_name(), connection); if (callback_) { - callback_(new_link); + callback_(connection.link); } - EstablishWaitingPortals(std::move(new_link), link_side, num_remote_portals); + EstablishWaitingPortals(connection.link, num_remote_portals); } void NodeConnector::RejectConnection() { if (callback_) { callback_(nullptr); } - EstablishWaitingPortals(nullptr, LinkSide::kA, 0); + EstablishWaitingPortals(nullptr, 0); if (transport_) { transport_->Deactivate(); } @@ -541,28 +612,30 @@ bool NodeConnector::ActivateTransport() { } void NodeConnector::EstablishWaitingPortals(Ref<NodeLink> to_link, - LinkSide link_side, size_t max_valid_portals) { + // All paths to this function come from a transport notification. + const OperationContext context{OperationContext::kTransportNotification}; + ABSL_ASSERT(to_link != nullptr || max_valid_portals == 0); const size_t num_valid_portals = std::min(max_valid_portals, waiting_portals_.size()); for (size_t i = 0; i < num_valid_portals; ++i) { const Ref<Router> router = waiting_portals_[i]->router(); Ref<RouterLink> link = to_link->AddRemoteRouterLink( - SublinkId(i), to_link->memory().GetInitialRouterLinkState(i), - LinkType::kCentral, link_side, router); + context, SublinkId(i), to_link->memory().GetInitialRouterLinkState(i), + LinkType::kCentral, to_link->link_side(), router); if (link) { - router->SetOutwardLink(std::move(link)); + router->SetOutwardLink(context, std::move(link)); } else { - router->AcceptRouteDisconnectedFrom(LinkType::kCentral); + router->AcceptRouteDisconnectedFrom(context, LinkType::kCentral); } } // Elicit immediate peer closure on any surplus portals that were established // on this side of the link. for (size_t i = num_valid_portals; i < waiting_portals_.size(); ++i) { - waiting_portals_[i]->router()->AcceptRouteClosureFrom(LinkType::kCentral, - SequenceNumber(0)); + waiting_portals_[i]->router()->AcceptRouteClosureFrom( + context, LinkType::kCentral, SequenceNumber(0)); } } diff --git a/chromium/third_party/ipcz/src/ipcz/node_connector.h b/chromium/third_party/ipcz/src/ipcz/node_connector.h index d1379431927..96d3dc228b0 100644 --- a/chromium/third_party/ipcz/src/ipcz/node_connector.h +++ b/chromium/third_party/ipcz/src/ipcz/node_connector.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -12,13 +12,13 @@ #include "ipcz/driver_transport.h" #include "ipcz/ipcz.h" #include "ipcz/link_side.h" +#include "ipcz/node.h" #include "ipcz/node_messages.h" #include "third_party/abseil-cpp/absl/types/span.h" #include "util/ref_counted.h" namespace ipcz { -class Node; class NodeLink; class Portal; @@ -73,11 +73,9 @@ class NodeConnector : public msg::NodeMessageListener { size_t num_portals() const { return waiting_portals_.size(); } - // Invoked once by the implementation when it has completed the handshake. - // `new_link` has already assumed ownership of the underlying transport and - // is listening for incoming messages on it. Destroys `this`. - void AcceptConnection(Ref<NodeLink> new_link, - LinkSide link_side, + // Invoked once by the implementation when it has completed its handshake. + // Destroys `this`. + void AcceptConnection(Node::Connection connection, uint32_t num_remote_portals); // Invoked if the transport observes an error before receiving the expected @@ -95,9 +93,7 @@ class NodeConnector : public msg::NodeMessageListener { private: bool ActivateTransport(); - void EstablishWaitingPortals(Ref<NodeLink> to_link, - LinkSide link_side, - size_t max_valid_portals); + void EstablishWaitingPortals(Ref<NodeLink> to_link, size_t max_valid_portals); const ConnectCallback callback_; }; diff --git a/chromium/third_party/ipcz/src/ipcz/node_connector_test.cc b/chromium/third_party/ipcz/src/ipcz/node_connector_test.cc index 7bd3fe7cc3a..1d822d6ffec 100644 --- a/chromium/third_party/ipcz/src/ipcz/node_connector_test.cc +++ b/chromium/third_party/ipcz/src/ipcz/node_connector_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/node_link.cc b/chromium/third_party/ipcz/src/ipcz/node_link.cc index 1835fcc43a9..0fb03fe5835 100644 --- a/chromium/third_party/ipcz/src/ipcz/node_link.cc +++ b/chromium/third_party/ipcz/src/ipcz/node_link.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -22,6 +22,7 @@ #include "ipcz/node_connector.h" #include "ipcz/node_link_memory.h" #include "ipcz/node_messages.h" +#include "ipcz/operation_context.h" #include "ipcz/parcel.h" #include "ipcz/portal.h" #include "ipcz/remote_router_link.h" @@ -123,12 +124,13 @@ void NodeLink::Activate() { } Ref<RemoteRouterLink> NodeLink::AddRemoteRouterLink( + const OperationContext& context, SublinkId sublink, FragmentRef<RouterLinkState> link_state, LinkType type, LinkSide side, Ref<Router> router) { - auto link = RemoteRouterLink::Create(WrapRefCounted(this), sublink, + auto link = RemoteRouterLink::Create(context, WrapRefCounted(this), sublink, std::move(link_state), type, side); absl::MutexLock lock(&mutex_); @@ -191,6 +193,7 @@ void NodeLink::RequestIntroduction(const NodeName& name) { void NodeLink::AcceptIntroduction(const NodeName& name, LinkSide side, + Node::Type remote_node_type, uint32_t remote_protocol_version, Ref<DriverTransport> transport, DriverMemory memory) { @@ -199,6 +202,7 @@ void NodeLink::AcceptIntroduction(const NodeName& name, msg::AcceptIntroduction accept; accept.params().name = name; accept.params().link_side = side; + accept.params().remote_node_type = remote_node_type; accept.params().remote_protocol_version = remote_protocol_version; accept.params().transport = accept.AppendDriverObject(transport->TakeDriverObject()); @@ -318,7 +322,7 @@ bool NodeLink::DispatchRelayedMessage(msg::AcceptRelayedMessage& accept) { } } -void NodeLink::Deactivate() { +void NodeLink::Deactivate(const OperationContext& context) { { absl::MutexLock lock(&mutex_); if (activation_state_ != kActive) { @@ -327,7 +331,7 @@ void NodeLink::Deactivate() { activation_state_ = kDeactivated; } - OnTransportError(); + HandleTransportError(context); transport_->Deactivate(); memory_->SetNodeLink(nullptr); } @@ -432,9 +436,7 @@ bool NodeLink::OnNonBrokerReferralRejected( } bool NodeLink::OnRequestIntroduction(msg::RequestIntroduction& request) { - // TODO: Support broker-to-broker introduction requests. - if (remote_node_type_ != Node::Type::kNormal || - node()->type() != Node::Type::kBroker) { + if (node()->type() != Node::Type::kBroker) { return false; } @@ -447,11 +449,6 @@ bool NodeLink::OnAcceptIntroduction(msg::AcceptIntroduction& accept) { return false; } - if (node()->type() != Node::Type::kNormal) { - // TODO: Support broker-to-broker introductions. - return false; - } - auto memory = DriverMemory(accept.TakeDriverObject(accept.params().memory)); if (!memory.is_valid()) { return false; @@ -466,8 +463,8 @@ bool NodeLink::OnAcceptIntroduction(msg::AcceptIntroduction& accept) { accept.TakeDriverObject(accept.params().transport)); node()->AcceptIntroduction( *this, accept.params().name, accept.params().link_side, - accept.params().remote_protocol_version, std::move(transport), - NodeLinkMemory::Create(node(), std::move(mapping))); + accept.params().remote_node_type, accept.params().remote_protocol_version, + std::move(transport), NodeLinkMemory::Create(node(), std::move(mapping))); return true; } @@ -476,12 +473,21 @@ bool NodeLink::OnRejectIntroduction(msg::RejectIntroduction& reject) { return false; } - if (node()->type() != Node::Type::kNormal) { - // TODO: Support broker-to-broker introductions. + node()->NotifyIntroductionFailed(*this, reject.params().name); + return true; +} + +bool NodeLink::OnRequestIndirectIntroduction( + msg::RequestIndirectIntroduction& request) { + // By convention only a broker on side B of a broker-to-broker link will send + // this message, and so only side-A broker-to-broker links can receive it. + if (remote_node_type_ != Node::Type::kBroker || + node()->type() != Node::Type::kBroker || !link_side_.is_side_a()) { return false; } - return node()->CancelIntroduction(reject.params().name); + return node()->HandleIndirectIntroductionRequest( + *this, request.params().target_node, request.params().source_node); } bool NodeLink::OnAddBlockBuffer(msg::AddBlockBuffer& add) { @@ -609,8 +615,10 @@ bool NodeLink::OnRouteClosed(msg::RouteClosed& route_closed) { return true; } + const OperationContext context{OperationContext::kTransportNotification}; return sublink->receiver->AcceptRouteClosureFrom( - sublink->router_link->GetType(), route_closed.params().sequence_length); + context, sublink->router_link->GetType(), + route_closed.params().sequence_length); } bool NodeLink::OnRouteDisconnected(msg::RouteDisconnected& route_closed) { @@ -622,13 +630,15 @@ bool NodeLink::OnRouteDisconnected(msg::RouteDisconnected& route_closed) { DVLOG(4) << "Accepting RouteDisconnected at " << sublink->router_link->Describe(); + const OperationContext context{OperationContext::kTransportNotification}; return sublink->receiver->AcceptRouteDisconnectedFrom( - sublink->router_link->GetType()); + context, sublink->router_link->GetType()); } -bool NodeLink::OnNotifyDataConsumed(msg::NotifyDataConsumed& notify) { - if (Ref<Router> router = GetRouter(notify.params().sublink)) { - router->NotifyPeerConsumedData(); +bool NodeLink::OnSnapshotPeerQueueState(msg::SnapshotPeerQueueState& snapshot) { + const OperationContext context{OperationContext::kTransportNotification}; + if (Ref<Router> router = GetRouter(snapshot.params().sublink)) { + router->SnapshotPeerQueueState(context); } return true; } @@ -641,7 +651,8 @@ bool NodeLink::OnBypassPeer(msg::BypassPeer& bypass) { // NOTE: This request is authenticated by the receiving Router, within // BypassPeer(). - return sublink->receiver->BypassPeer(*sublink->router_link, + const OperationContext context{OperationContext::kTransportNotification}; + return sublink->receiver->BypassPeer(context, *sublink->router_link, bypass.params().bypass_target_node, bypass.params().bypass_target_sublink); } @@ -674,8 +685,9 @@ bool NodeLink::OnAcceptBypassLink(msg::AcceptBypassLink& accept) { return false; } + const OperationContext context{OperationContext::kTransportNotification}; return receiver->AcceptBypassLink( - *this, accept.params().new_sublink, std::move(link_state), + context, *this, accept.params().new_sublink, std::move(link_state), accept.params().inbound_sequence_length_from_bypassed_link); } @@ -685,7 +697,8 @@ bool NodeLink::OnStopProxying(msg::StopProxying& stop) { return true; } - return router->StopProxying(stop.params().inbound_sequence_length, + const OperationContext context{OperationContext::kTransportNotification}; + return router->StopProxying(context, stop.params().inbound_sequence_length, stop.params().outbound_sequence_length); } @@ -695,8 +708,9 @@ bool NodeLink::OnProxyWillStop(msg::ProxyWillStop& will_stop) { return true; } + const OperationContext context{OperationContext::kTransportNotification}; return router->NotifyProxyWillStop( - will_stop.params().inbound_sequence_length); + context, will_stop.params().inbound_sequence_length); } bool NodeLink::OnBypassPeerWithLink(msg::BypassPeerWithLink& bypass) { @@ -710,7 +724,9 @@ bool NodeLink::OnBypassPeerWithLink(msg::BypassPeerWithLink& bypass) { if (link_state.is_null()) { return false; } - return router->AcceptBypassLink(*this, bypass.params().new_sublink, + + const OperationContext context{OperationContext::kTransportNotification}; + return router->AcceptBypassLink(context, *this, bypass.params().new_sublink, std::move(link_state), bypass.params().inbound_sequence_length); } @@ -721,13 +737,15 @@ bool NodeLink::OnStopProxyingToLocalPeer(msg::StopProxyingToLocalPeer& stop) { return true; } + const OperationContext context{OperationContext::kTransportNotification}; return router->StopProxyingToLocalPeer( - stop.params().outbound_sequence_length); + context, stop.params().outbound_sequence_length); } bool NodeLink::OnFlushRouter(msg::FlushRouter& flush) { if (Ref<Router> router = GetRouter(flush.params().sublink)) { - router->Flush(Router::kForceProxyBypassAttempt); + const OperationContext context{OperationContext::kTransportNotification}; + router->Flush(context, Router::kForceProxyBypassAttempt); } return true; } @@ -782,6 +800,11 @@ bool NodeLink::OnAcceptRelayedMessage(msg::AcceptRelayedMessage& accept) { } void NodeLink::OnTransportError() { + const OperationContext context{OperationContext::kTransportNotification}; + HandleTransportError(context); +} + +void NodeLink::HandleTransportError(const OperationContext& context) { SublinkMap sublinks; { absl::MutexLock lock(&mutex_); @@ -792,11 +815,11 @@ void NodeLink::OnTransportError() { DVLOG(4) << "NodeLink disconnection dropping " << sublink.router_link->Describe() << " which is bound to router " << sublink.receiver.get(); - sublink.receiver->NotifyLinkDisconnected(*sublink.router_link); + sublink.receiver->NotifyLinkDisconnected(context, *sublink.router_link); } Ref<NodeLink> self = WrapRefCounted(this); - node_->DropLink(remote_node_name_); + node_->DropConnection(context, remote_node_name_); } void NodeLink::WaitForParcelFragmentToResolve( @@ -915,17 +938,20 @@ bool NodeLink::AcceptCompleteParcel(SublinkId for_sublink, Parcel& parcel) { << for_sublink; return true; } + + const OperationContext context{OperationContext::kTransportNotification}; + parcel.set_remote_source(WrapRefCounted(this)); const LinkType link_type = sublink->router_link->GetType(); if (link_type.is_outward()) { DVLOG(4) << "Accepting inbound " << parcel.Describe() << " at " << sublink->router_link->Describe(); - return sublink->receiver->AcceptInboundParcel(parcel); + return sublink->receiver->AcceptInboundParcel(context, parcel); } ABSL_ASSERT(link_type.is_peripheral_inward()); DVLOG(4) << "Accepting outbound " << parcel.Describe() << " at " << sublink->router_link->Describe(); - return sublink->receiver->AcceptOutboundParcel(parcel); + return sublink->receiver->AcceptOutboundParcel(context, parcel); } NodeLink::Sublink::Sublink(Ref<RemoteRouterLink> router_link, diff --git a/chromium/third_party/ipcz/src/ipcz/node_link.h b/chromium/third_party/ipcz/src/ipcz/node_link.h index 2b9bf420906..fcd412ace5e 100644 --- a/chromium/third_party/ipcz/src/ipcz/node_link.h +++ b/chromium/third_party/ipcz/src/ipcz/node_link.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -103,6 +103,7 @@ class NodeLink : public msg::NodeMessageListener { // shared RouterLinkState structure for the new link. Only central links // require a RouterLinkState. Ref<RemoteRouterLink> AddRemoteRouterLink( + const OperationContext& context, SublinkId sublink, FragmentRef<RouterLinkState> link_state, LinkType type, @@ -137,6 +138,7 @@ class NodeLink : public msg::NodeMessageListener { // construct a new NodeLink to that node. void AcceptIntroduction(const NodeName& name, LinkSide side, + Node::Type remote_node_type, uint32_t remote_protocol_version, Ref<DriverTransport> transport, DriverMemory memory); @@ -199,7 +201,7 @@ class NodeLink : public msg::NodeMessageListener { // Must only be called on an activated NodeLink, either one which was created // with CreateActive(), or one which was activated later by calling // Activate(). - void Deactivate(); + void Deactivate(const OperationContext& context); // Finalizes serialization of DriverObjects within `message` and transmits it // to the NodeLink's peer, either over the DriverTransport or through shared @@ -235,13 +237,15 @@ class NodeLink : public msg::NodeMessageListener { bool OnRequestIntroduction(msg::RequestIntroduction& request) override; bool OnAcceptIntroduction(msg::AcceptIntroduction& accept) override; bool OnRejectIntroduction(msg::RejectIntroduction& reject) override; + bool OnRequestIndirectIntroduction( + msg::RequestIndirectIntroduction& request) override; bool OnAddBlockBuffer(msg::AddBlockBuffer& add) override; bool OnAcceptParcel(msg::AcceptParcel& accept) override; bool OnAcceptParcelDriverObjects( msg::AcceptParcelDriverObjects& accept) override; bool OnRouteClosed(msg::RouteClosed& route_closed) override; bool OnRouteDisconnected(msg::RouteDisconnected& route_disconnected) override; - bool OnNotifyDataConsumed(msg::NotifyDataConsumed& notify) override; + bool OnSnapshotPeerQueueState(msg::SnapshotPeerQueueState& snapshot) override; bool OnBypassPeer(msg::BypassPeer& bypass) override; bool OnAcceptBypassLink(msg::AcceptBypassLink& accept) override; bool OnStopProxying(msg::StopProxying& stop) override; @@ -255,6 +259,8 @@ class NodeLink : public msg::NodeMessageListener { bool OnAcceptRelayedMessage(msg::AcceptRelayedMessage& accept) override; void OnTransportError() override; + void HandleTransportError(const OperationContext& context); + // Invoked when we receive a Parcel whose data fragment resides in a buffer // not yet known to the local node. This schedules the parcel for acceptance // as soon as that buffer is available. diff --git a/chromium/third_party/ipcz/src/ipcz/node_link_memory.cc b/chromium/third_party/ipcz/src/ipcz/node_link_memory.cc index 598cd7ad647..0cfadfa02aa 100644 --- a/chromium/third_party/ipcz/src/ipcz/node_link_memory.cc +++ b/chromium/third_party/ipcz/src/ipcz/node_link_memory.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -27,8 +27,8 @@ namespace { constexpr BufferId kPrimaryBufferId{0}; -// Fixed allocation size for each NodeLink's primary shared buffer. -constexpr size_t kPrimaryBufferSize = 64 * 1024; +// Fixed allocation size for each NodeLink's primary shared buffer. (2 MB) +constexpr size_t kPrimaryBufferSize = 2 * 1024 * 1024; // The front of the primary buffer is reserved for special current and future // uses which require synchronous availability throughout a link's lifetime. @@ -49,7 +49,7 @@ constexpr size_t kMinBlockAllocatorCapacity = 8; // given fragment size within the BufferPool. This is not a hard cap on capacity // per fragment size, but it sets a limit on how large the pool will grow // automatically in response to failed allocation requests. -constexpr size_t kMaxBlockAllocatorCapacityPerFragmentSize = 256 * 1024; +constexpr size_t kMaxBlockAllocatorCapacityPerFragmentSize = 2 * 1024 * 1024; // The minimum fragment size (in bytes) to support with dedicated BufferPool // capacity. All fragment sizes are powers of two. Fragment allocations below @@ -59,7 +59,7 @@ constexpr size_t kMinFragmentSize = 64; // The maximum fragment size to support with dedicated BlockAllocator capacity // within the BufferPool. Allocations beyond this size must fail or fall back // onto a different allocation scheme which does not use a BlockAllocator. -constexpr size_t kMaxFragmentSizeForBlockAllocation = 16 * 1024; +constexpr size_t kMaxFragmentSizeForBlockAllocation = 1024 * 1024; // The minimum fallback fragment size to attempt for best-effort allocations // when the requested size cannot be accommodated. @@ -118,11 +118,15 @@ struct IPCZ_ALIGN(8) NodeLinkMemory::PrimaryBuffer { // Reserved memory for a series of fixed block allocators. Additional // allocators may be adopted by a NodeLinkMemory over its lifetime, but these // ones remain fixed within the primary buffer. - std::array<uint8_t, 4096> mem_for_64_byte_blocks; - std::array<uint8_t, 12288> mem_for_256_byte_blocks; - std::array<uint8_t, 15360> mem_for_512_byte_blocks; - std::array<uint8_t, 11264> mem_for_1024_byte_blocks; - std::array<uint8_t, 16384> mem_for_2048_byte_blocks; + std::array<uint8_t, 64 * 64> mem_for_64_byte_blocks; + std::array<uint8_t, 256 * 48> mem_for_256_byte_blocks; + std::array<uint8_t, 512 * 30> mem_for_512_byte_blocks; + std::array<uint8_t, 1024 * 11> mem_for_1k_blocks; + std::array<uint8_t, 2048 * 8> mem_for_2k_blocks; + std::array<uint8_t, 4096 * 16> mem_for_4k_blocks; + std::array<uint8_t, 16384 * 16> mem_for_16k_blocks; + std::array<uint8_t, 32768 * 8> mem_for_32k_blocks; + std::array<uint8_t, 65536 * 22> mem_for_64k_blocks; BlockAllocator block_allocator_64() { return BlockAllocator(absl::MakeSpan(mem_for_64_byte_blocks), 64); @@ -136,12 +140,28 @@ struct IPCZ_ALIGN(8) NodeLinkMemory::PrimaryBuffer { return BlockAllocator(absl::MakeSpan(mem_for_512_byte_blocks), 512); } - BlockAllocator block_allocator_1024() { - return BlockAllocator(absl::MakeSpan(mem_for_1024_byte_blocks), 1024); + BlockAllocator block_allocator_1k() { + return BlockAllocator(absl::MakeSpan(mem_for_1k_blocks), 1024); } - BlockAllocator block_allocator_2048() { - return BlockAllocator(absl::MakeSpan(mem_for_2048_byte_blocks), 2048); + BlockAllocator block_allocator_2k() { + return BlockAllocator(absl::MakeSpan(mem_for_2k_blocks), 2 * 1024); + } + + BlockAllocator block_allocator_4k() { + return BlockAllocator(absl::MakeSpan(mem_for_4k_blocks), 4 * 1024); + } + + BlockAllocator block_allocator_16k() { + return BlockAllocator(absl::MakeSpan(mem_for_16k_blocks), 16 * 1024); + } + + BlockAllocator block_allocator_32k() { + return BlockAllocator(absl::MakeSpan(mem_for_32k_blocks), 32 * 1024); + } + + BlockAllocator block_allocator_64k() { + return BlockAllocator(absl::MakeSpan(mem_for_64k_blocks), 64 * 1024); } }; @@ -160,8 +180,12 @@ NodeLinkMemory::NodeLinkMemory(Ref<Node> node, primary_buffer_.block_allocator_64(), primary_buffer_.block_allocator_256(), primary_buffer_.block_allocator_512(), - primary_buffer_.block_allocator_1024(), - primary_buffer_.block_allocator_2048(), + primary_buffer_.block_allocator_1k(), + primary_buffer_.block_allocator_2k(), + primary_buffer_.block_allocator_4k(), + primary_buffer_.block_allocator_16k(), + primary_buffer_.block_allocator_32k(), + primary_buffer_.block_allocator_64k(), }; buffer_pool_.AddBlockBuffer(kPrimaryBufferId, @@ -219,8 +243,12 @@ DriverMemoryWithMapping NodeLinkMemory::AllocateMemory( primary_buffer.block_allocator_64().InitializeRegion(); primary_buffer.block_allocator_256().InitializeRegion(); primary_buffer.block_allocator_512().InitializeRegion(); - primary_buffer.block_allocator_1024().InitializeRegion(); - primary_buffer.block_allocator_2048().InitializeRegion(); + primary_buffer.block_allocator_1k().InitializeRegion(); + primary_buffer.block_allocator_2k().InitializeRegion(); + primary_buffer.block_allocator_4k().InitializeRegion(); + primary_buffer.block_allocator_16k().InitializeRegion(); + primary_buffer.block_allocator_32k().InitializeRegion(); + primary_buffer.block_allocator_64k().InitializeRegion(); return {std::move(memory), std::move(mapping)}; } diff --git a/chromium/third_party/ipcz/src/ipcz/node_link_memory.h b/chromium/third_party/ipcz/src/ipcz/node_link_memory.h index 88a1c3927bc..1eb43cb6ac3 100644 --- a/chromium/third_party/ipcz/src/ipcz/node_link_memory.h +++ b/chromium/third_party/ipcz/src/ipcz/node_link_memory.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/node_link_memory_test.cc b/chromium/third_party/ipcz/src/ipcz/node_link_memory_test.cc index d3707f3e7f1..44ba853fa60 100644 --- a/chromium/third_party/ipcz/src/ipcz/node_link_memory_test.cc +++ b/chromium/third_party/ipcz/src/ipcz/node_link_memory_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -44,8 +44,9 @@ class NodeLinkMemoryTest : public testing::Test { node_b_, LinkSide::kB, kTestNonBrokerName, kTestBrokerName, Node::Type::kBroker, 0, transports.second, NodeLinkMemory::Create(node_b_, buffer.memory.Map())); - node_a_->AddLink(kTestNonBrokerName, link_a_); - node_b_->AddLink(kTestBrokerName, link_b_); + node_a_->AddConnection(kTestNonBrokerName, {.link = link_a_}); + node_b_->AddConnection(kTestBrokerName, + {.link = link_b_, .broker = link_a_}); link_a_->Activate(); link_b_->Activate(); } @@ -201,15 +202,15 @@ TEST_F(NodeLinkMemoryTest, OversizedAllocation) { TEST_F(NodeLinkMemoryTest, NewBlockSizes) { // NodeLinkMemory begins life with a fixed set of block allocators available - // for certain common block sizes. These are capped out at 2 kB blocks, but + // for certain common block sizes. These are capped out at 64 kB blocks, but // NodeLinkMemory still supports block allocation of larger blocks as well -- - // at least up to 16 kB in size. Verify that we can trigger new capacity for + // at least up to 1 MB in size. Verify that we can trigger new capacity for // such sizes by attempting to allocate them. - constexpr size_t kPrettyBig = 16 * 1024; + constexpr size_t kPrettyBig = 512 * 1024; Fragment fragment = memory_a().AllocateFragment(kPrettyBig); - // No initial capacity for 16 kB fragments. + // No initial capacity for 256 kB fragments. EXPECT_TRUE(fragment.is_null()); // But the failure above should have triggered expansion of capacity for that diff --git a/chromium/third_party/ipcz/src/ipcz/node_link_test.cc b/chromium/third_party/ipcz/src/ipcz/node_link_test.cc index 26f6c6bf113..29c26eb5775 100644 --- a/chromium/third_party/ipcz/src/ipcz/node_link_test.cc +++ b/chromium/third_party/ipcz/src/ipcz/node_link_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,6 +10,7 @@ #include "ipcz/link_side.h" #include "ipcz/link_type.h" #include "ipcz/node_link_memory.h" +#include "ipcz/operation_context.h" #include "ipcz/remote_router_link.h" #include "ipcz/router.h" #include "ipcz/sublink_id.h" @@ -61,15 +62,21 @@ TEST_F(NodeLinkTest, BasicTransmission) { Ref<Node> node1 = MakeRefCounted<Node>(Node::Type::kNormal, kDriver, IPCZ_INVALID_DRIVER_HANDLE); + // The choice of OperationContext is arbitrary and irrelevant for this test. + const OperationContext context{OperationContext::kTransportNotification}; auto [link0, link1] = LinkNodes(node0, node1); auto router0 = MakeRefCounted<Router>(); auto router1 = MakeRefCounted<Router>(); FragmentRef<RouterLinkState> link_state = link0->memory().GetInitialRouterLinkState(0); - router0->SetOutwardLink(link0->AddRemoteRouterLink( - SublinkId(0), link_state, LinkType::kCentral, LinkSide::kA, router0)); - router1->SetOutwardLink(link1->AddRemoteRouterLink( - SublinkId(0), link_state, LinkType::kCentral, LinkSide::kB, router1)); + router0->SetOutwardLink( + context, + link0->AddRemoteRouterLink(context, SublinkId(0), link_state, + LinkType::kCentral, LinkSide::kA, router0)); + router1->SetOutwardLink( + context, + link1->AddRemoteRouterLink(context, SublinkId(0), link_state, + LinkType::kCentral, LinkSide::kB, router1)); link_state->status = RouterLinkState::kStable; EXPECT_FALSE(router1->IsPeerClosed()); @@ -77,8 +84,8 @@ TEST_F(NodeLinkTest, BasicTransmission) { EXPECT_TRUE(router1->IsPeerClosed()); router1->CloseRoute(); - link0->Deactivate(); - link1->Deactivate(); + link0->Deactivate(context); + link1->Deactivate(context); } } // namespace diff --git a/chromium/third_party/ipcz/src/ipcz/node_messages.cc b/chromium/third_party/ipcz/src/ipcz/node_messages.cc index 5396bb442cf..9941643a479 100644 --- a/chromium/third_party/ipcz/src/ipcz/node_messages.cc +++ b/chromium/third_party/ipcz/src/ipcz/node_messages.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/node_messages.h b/chromium/third_party/ipcz/src/ipcz/node_messages.h index ed92d7a389c..790502e3e07 100644 --- a/chromium/third_party/ipcz/src/ipcz/node_messages.h +++ b/chromium/third_party/ipcz/src/ipcz/node_messages.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -14,6 +14,7 @@ #include "ipcz/link_side.h" #include "ipcz/message.h" #include "ipcz/node_name.h" +#include "ipcz/node_type.h" #include "ipcz/router_descriptor.h" #include "ipcz/sequence_number.h" #include "ipcz/sublink_id.h" diff --git a/chromium/third_party/ipcz/src/ipcz/node_messages_generator.h b/chromium/third_party/ipcz/src/ipcz/node_messages_generator.h index 65bc189a868..3d553baefb4 100644 --- a/chromium/third_party/ipcz/src/ipcz/node_messages_generator.h +++ b/chromium/third_party/ipcz/src/ipcz/node_messages_generator.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -183,11 +183,37 @@ IPCZ_MSG_BEGIN(NonBrokerReferralRejected, IPCZ_MSG_ID(6), IPCZ_MSG_VERSION(0)) IPCZ_MSG_PARAM(uint64_t, referral_id) IPCZ_MSG_END() -// Sent by a non-broker node to a broker node, asking the broker to introduce -// the non-broker to the node identified by `name`. If the broker is willing and -// able to comply with this request, it will send an AcceptIntroduction message -// (see below) to both the sender of this message and the node identified by -// `name`. +// Sent from one broker to another to establish an initial link between two +// distinct node networks. Once two brokers are connected their networks are +// effectively merged and nodes in either network can become interconnected by +// ipcz. +IPCZ_MSG_BEGIN(ConnectFromBrokerToBroker, IPCZ_MSG_ID(7), IPCZ_MSG_VERSION(0)) + // The name of the sending node. + IPCZ_MSG_PARAM(NodeName, name) + + // The highest protocol version known and desired by the sending broker. + IPCZ_MSG_PARAM(uint32_t, protocol_version) + + // The number of initial portals assumed by the sending broker on its end of + // the link established by this handshake. + IPCZ_MSG_PARAM(uint32_t, num_initial_portals) + + // A primary buffer memory object which may be used to establish a + // NodeLinkMemory between the two brokers. Since each broker sends a memory + // object during this handshake, the one to use for the primary buffer is + // determined by whichever broker's name is the lesser of the two when + // compared numerically. + IPCZ_MSG_PARAM_DRIVER_OBJECT(buffer) +IPCZ_MSG_END() + +// Sent to a broker to ask for an introduction to one of the non-broker nodes in +// its own network. This may be sent either from a non-broker in the same +// network, or a broker from another network. +// +// The non-broker to be introduced to the sender is identified by `name`. If the +// broker is willing and able to comply with this request, it will send an +// AcceptIntroduction message (see below) to both the sender of this message and +// the node identified by `name`. // // If the broker does not know the node named `name`, it will send only a // RejectIntroduction message back to the sender to indicate failure. @@ -205,6 +231,9 @@ IPCZ_MSG_BEGIN(AcceptIntroduction, IPCZ_MSG_ID(11), IPCZ_MSG_VERSION(0)) // for the NodeLink it will establish over `transport`. IPCZ_MSG_PARAM(LinkSide, link_side) + // Indicates the type of the remote node being introduced. + IPCZ_MSG_PARAM(NodeType, remote_node_type) + // Indicates the highest ipcz protocol version which the remote side of // `transport` able and willing to use according to the broker. IPCZ_MSG_PARAM(uint32_t, remote_protocol_version) @@ -220,13 +249,27 @@ IPCZ_MSG_BEGIN(AcceptIntroduction, IPCZ_MSG_ID(11), IPCZ_MSG_VERSION(0)) IPCZ_MSG_PARAM_DRIVER_OBJECT(memory) IPCZ_MSG_END() -// Sent back to a non-broker if the broker did not recognzie the subject of an -// introduction request. +// Sent to a node in response to RequestIntroduction if the broker receiving +// that message did not recognize or otherwise could not introduce the requested +// node. IPCZ_MSG_BEGIN(RejectIntroduction, IPCZ_MSG_ID(12), IPCZ_MSG_VERSION(0)) // The name of the node whose introduction cannot be fulfilled. IPCZ_MSG_PARAM(NodeName, name) IPCZ_MSG_END() +// Sent from a broker to another broker to request that the receiving broker +// introduce a named node in its own network to a named node in the sender's +// network. +IPCZ_MSG_BEGIN(RequestIndirectIntroduction, + IPCZ_MSG_ID(13), + IPCZ_MSG_VERSION(0)) + // The name of the node to be introduced on the sender's own network. + IPCZ_MSG_PARAM(NodeName, source_node) + + // The name of the node to be introduced on the recipient's own network. + IPCZ_MSG_PARAM(NodeName, target_node) +IPCZ_MSG_END() + // Shares a new buffer to support allocation of blocks of `block_size` bytes. // The sender must initialize an appropriate BlockAllocator within the buffer's // memory before sending this message. @@ -323,9 +366,9 @@ IPCZ_MSG_BEGIN(RouteDisconnected, IPCZ_MSG_ID(23), IPCZ_MSG_VERSION(0)) IPCZ_MSG_PARAM(SublinkId, sublink) IPCZ_MSG_END() -// Notifies a Router that the other side of its route has consumed some parcels -// or parcel data from its inbound queue. -IPCZ_MSG_BEGIN(NotifyDataConsumed, IPCZ_MSG_ID(24), IPCZ_MSG_VERSION(0)) +// Notifies a router that it may be interested in a recent change to its outward +// peer's visible queue state. +IPCZ_MSG_BEGIN(SnapshotPeerQueueState, IPCZ_MSG_ID(24), IPCZ_MSG_VERSION(0)) // Identifies the router to receive this message. IPCZ_MSG_PARAM(SublinkId, sublink) IPCZ_MSG_END() diff --git a/chromium/third_party/ipcz/src/ipcz/node_name.cc b/chromium/third_party/ipcz/src/ipcz/node_name.cc index d89aff365bf..f7aedb34bf9 100644 --- a/chromium/third_party/ipcz/src/ipcz/node_name.cc +++ b/chromium/third_party/ipcz/src/ipcz/node_name.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/node_name.h b/chromium/third_party/ipcz/src/ipcz/node_name.h index 0b9fda9aff7..d2bd6ac4dc2 100644 --- a/chromium/third_party/ipcz/src/ipcz/node_name.h +++ b/chromium/third_party/ipcz/src/ipcz/node_name.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/node_test.cc b/chromium/third_party/ipcz/src/ipcz/node_test.cc index eae23ac4d77..20ebb841559 100644 --- a/chromium/third_party/ipcz/src/ipcz/node_test.cc +++ b/chromium/third_party/ipcz/src/ipcz/node_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -58,9 +58,8 @@ class NodeTest : public testing::Test { node, LinkSide::kB, name, broker_name, Node::Type::kBroker, 0, transports.second, NodeLinkMemory::Create(node, buffer.memory.Map())); node->SetAssignedName(name); - broker_->AddLink(name, broker_link); - node->AddLink(broker_name, node_link); - node->SetBrokerLink(node_link); + broker_->AddConnection(name, {.link = broker_link}); + node->AddConnection(broker_name, {.link = node_link, .broker = node_link}); broker_link->Activate(); node_link->Activate(); } diff --git a/chromium/third_party/ipcz/src/ipcz/node_type.h b/chromium/third_party/ipcz/src/ipcz/node_type.h new file mode 100644 index 00000000000..8d245b84039 --- /dev/null +++ b/chromium/third_party/ipcz/src/ipcz/node_type.h @@ -0,0 +1,30 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPCZ_SRC_IPCZ_NODE_TYPE_H_ +#define IPCZ_SRC_IPCZ_NODE_TYPE_H_ + +#include <cstdint> + +namespace ipcz { + +// Enumeration indicating the role a Node plays in its network of nodes. Note +// that this is used by internal wire messages, so values must not be changed or +// removed. +enum class NodeType : uint8_t { + // A broker node assigns its own name and is able to assign names to other + // nodes upon connection. Brokers are trusted to introduce nodes to each + // other upon request, and brokers may connect to other brokers in order to + // share information and effectively bridge two node networks together. + kBroker, + + // A "normal" (i.e. non-broker) node is assigned a permanent name by the + // first broker node it connects to, and it can only make contact with other + // nodes by requesting an introduction from that broker. + kNormal, +}; + +} // namespace ipcz + +#endif // IPCZ_SRC_IPCZ_NODE_TYPE_H_ diff --git a/chromium/third_party/ipcz/src/ipcz/operation_context.h b/chromium/third_party/ipcz/src/ipcz/operation_context.h new file mode 100644 index 00000000000..0d6c0bac607 --- /dev/null +++ b/chromium/third_party/ipcz/src/ipcz/operation_context.h @@ -0,0 +1,46 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPCZ_SRC_IPCZ_OPERATION_CONTEXT_H_ +#define IPCZ_SRC_IPCZ_OPERATION_CONTEXT_H_ + +namespace ipcz { + +// Structure to capture any relevant context regarding an ongoing ipcz +// operation. This is plumbed throughout methods on Router and other related +// objects as needed to provide context for any events emitted by ipcz. +struct OperationContext { + // Indicates the nature of the innermost entry point into ipcz for the current + // call stack. For any call stack within ipcz which propagates an + // OperationContext, the correct EntryPoint can be deduced by walking up the + // stack until hitting either an API entry point (i.e. an explicit IpczAPI + // function invocation) OR a driver transport notification (i.e. an + // IpczTransportActivityHandler invocation.) + enum class EntryPoint { + // The current innermost stack frame entering ipcz is a direct IpczAPI call. + kAPICall, + + // The current innermost stack frame entering ipcz is a driver transport + // activity notification. + kTransportNotification, + }; + + static constexpr EntryPoint kAPICall = EntryPoint::kAPICall; + static constexpr EntryPoint kTransportNotification = + EntryPoint::kTransportNotification; + + explicit OperationContext(EntryPoint entry_point) + : entry_point_(entry_point) {} + OperationContext(const OperationContext&) = default; + OperationContext& operator=(const OperationContext&) = default; + + bool is_api_call() const { return entry_point_ == kAPICall; } + + private: + EntryPoint entry_point_; +}; + +} // namespace ipcz + +#endif // IPCZ_SRC_IPCZ_OPERATION_CONTEXT_H_ diff --git a/chromium/third_party/ipcz/src/ipcz/parcel.cc b/chromium/third_party/ipcz/src/ipcz/parcel.cc index f5a62390b35..fa8ed8eb858 100644 --- a/chromium/third_party/ipcz/src/ipcz/parcel.cc +++ b/chromium/third_party/ipcz/src/ipcz/parcel.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -11,6 +11,7 @@ #include <string> #include <utility> +#include "ipcz/node_link.h" #include "ipcz/node_link_memory.h" #include "third_party/abseil-cpp/absl/base/macros.h" #include "third_party/abseil-cpp/absl/types/span.h" @@ -26,6 +27,7 @@ Parcel::Parcel(SequenceNumber sequence_number) // to explicitly clear the data and object views of the moved-from Parcel. Parcel::Parcel(Parcel&& other) : sequence_number_(other.sequence_number_), + remote_source_(std::move(other.remote_source_)), inlined_data_(std::move(other.inlined_data_)), data_fragment_(std::exchange(other.data_fragment_, {})), data_fragment_memory_( @@ -36,6 +38,7 @@ Parcel::Parcel(Parcel&& other) Parcel& Parcel::operator=(Parcel&& other) { sequence_number_ = other.sequence_number_; + remote_source_ = std::move(other.remote_source_); inlined_data_ = std::move(other.inlined_data_); data_fragment_ = std::exchange(other.data_fragment_, {}); data_fragment_memory_ = std::move(other.data_fragment_memory_); @@ -72,7 +75,7 @@ void Parcel::AllocateData(size_t num_bytes, ABSL_ASSERT(data_view_.empty()); Fragment fragment; - if (memory) { + if (memory && num_bytes > 0) { const size_t requested_fragment_size = num_bytes + sizeof(FragmentHeader); if (allow_partial) { fragment = memory->AllocateFragmentBestEffort(requested_fragment_size); diff --git a/chromium/third_party/ipcz/src/ipcz/parcel.h b/chromium/third_party/ipcz/src/ipcz/parcel.h index 6281efa3adf..20fe1ec5358 100644 --- a/chromium/third_party/ipcz/src/ipcz/parcel.h +++ b/chromium/third_party/ipcz/src/ipcz/parcel.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -21,6 +21,7 @@ namespace ipcz { +class NodeLink; class NodeLinkMemory; // Represents a parcel queued within a portal, either for inbound retrieval or @@ -64,6 +65,11 @@ class Parcel { // this returns false. bool AdoptDataFragment(Ref<NodeLinkMemory> memory, const Fragment& fragment); + void set_remote_source(Ref<NodeLink> source) { + remote_source_ = std::move(source); + } + const Ref<NodeLink>& remote_source() const { return remote_source_; } + absl::Span<uint8_t> data_view() { return data_view_; } absl::Span<const uint8_t> data_view() const { return data_view_; } @@ -123,6 +129,10 @@ class Parcel { SequenceNumber sequence_number_{0}; + // If this Parcel was received from a remote node, this tracks the NodeLink + // which received it. + Ref<NodeLink> remote_source_; + // A copy of the parcel's data, owned by the Parcel itself. Used only if // `data_fragment_` is null. std::vector<uint8_t> inlined_data_; diff --git a/chromium/third_party/ipcz/src/ipcz/parcel_queue.cc b/chromium/third_party/ipcz/src/ipcz/parcel_queue.cc index 8ddaedceba6..3f8b0874585 100644 --- a/chromium/third_party/ipcz/src/ipcz/parcel_queue.cc +++ b/chromium/third_party/ipcz/src/ipcz/parcel_queue.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -16,7 +16,7 @@ bool ParcelQueue::Consume(size_t num_bytes_consumed, ABSL_ASSERT(p.data_size() >= num_bytes_consumed); ABSL_ASSERT(p.num_objects() >= handles.size()); p.Consume(num_bytes_consumed, handles); - ReduceNextElementSize(num_bytes_consumed); + PartiallyConsumeNextElement(num_bytes_consumed); if (p.empty()) { Parcel discarded; const bool ok = Pop(discarded); diff --git a/chromium/third_party/ipcz/src/ipcz/parcel_queue.h b/chromium/third_party/ipcz/src/ipcz/parcel_queue.h index c531978005a..d4a26718da2 100644 --- a/chromium/third_party/ipcz/src/ipcz/parcel_queue.h +++ b/chromium/third_party/ipcz/src/ipcz/parcel_queue.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/parcel_queue_test.cc b/chromium/third_party/ipcz/src/ipcz/parcel_queue_test.cc index 3f65e932d24..5c5651019ae 100644 --- a/chromium/third_party/ipcz/src/ipcz/parcel_queue_test.cc +++ b/chromium/third_party/ipcz/src/ipcz/parcel_queue_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/portal.cc b/chromium/third_party/ipcz/src/ipcz/portal.cc index ce4268ded92..fd8d645078f 100644 --- a/chromium/third_party/ipcz/src/ipcz/portal.cc +++ b/chromium/third_party/ipcz/src/ipcz/portal.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,6 +10,7 @@ #include "ipcz/api_object.h" #include "ipcz/local_router_link.h" +#include "ipcz/operation_context.h" #include "ipcz/router.h" #include "third_party/abseil-cpp/absl/types/span.h" #include "util/log.h" @@ -47,10 +48,11 @@ Portal::Pair Portal::CreatePair(Ref<Node> node) { DVLOG(5) << "Created new portal pair with routers " << routers.first.get() << " and " << routers.second.get(); + const OperationContext context{OperationContext::kAPICall}; auto links = LocalRouterLink::CreatePair(LinkType::kCentral, routers, LocalRouterLink::kStable); - routers.first->SetOutwardLink(std::move(links.first)); - routers.second->SetOutwardLink(std::move(links.second)); + routers.first->SetOutwardLink(context, std::move(links.first)); + routers.second->SetOutwardLink(context, std::move(links.second)); return {MakeRefCounted<Portal>(node, std::move(routers.first)), MakeRefCounted<Portal>(node, std::move(routers.second))}; } @@ -146,7 +148,7 @@ IpczResult Portal::BeginPut(IpczBeginPutFlags flags, num_data_bytes = pending_parcel_->data_view().size(); if (data) { - *data = pending_parcel_->data_view().data(); + *data = num_data_bytes ? pending_parcel_->data_view().data() : nullptr; } return IPCZ_RESULT_OK; } @@ -207,9 +209,10 @@ IpczResult Portal::Get(IpczGetFlags flags, void* data, size_t* num_data_bytes, IpczHandle* handles, - size_t* num_handles) { + size_t* num_handles, + IpczHandle* validator) { return router_->GetNextInboundParcel(flags, data, num_data_bytes, handles, - num_handles); + num_handles, validator); } IpczResult Portal::BeginGet(const void** data, @@ -233,14 +236,15 @@ IpczResult Portal::BeginGet(const void** data, } IpczResult Portal::CommitGet(size_t num_data_bytes_consumed, - absl::Span<IpczHandle> handles) { + absl::Span<IpczHandle> handles, + IpczHandle* validator) { absl::MutexLock lock(&mutex_); if (!in_two_phase_get_) { return IPCZ_RESULT_FAILED_PRECONDITION; } - IpczResult result = - router_->CommitGetNextIncomingParcel(num_data_bytes_consumed, handles); + IpczResult result = router_->CommitGetNextIncomingParcel( + num_data_bytes_consumed, handles, validator); if (result == IPCZ_RESULT_OK) { in_two_phase_get_ = false; } diff --git a/chromium/third_party/ipcz/src/ipcz/portal.h b/chromium/third_party/ipcz/src/ipcz/portal.h index a2662cc13ab..e646cad6727 100644 --- a/chromium/third_party/ipcz/src/ipcz/portal.h +++ b/chromium/third_party/ipcz/src/ipcz/portal.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -61,12 +61,14 @@ class Portal : public APIObjectImpl<Portal, APIObject::kPortal> { void* data, size_t* num_data_bytes, IpczHandle* handles, - size_t* num_handles); + size_t* num_handles, + IpczHandle* validator); IpczResult BeginGet(const void** data, size_t* num_data_bytes, size_t* num_handles); IpczResult CommitGet(size_t num_data_bytes_consumed, - absl::Span<IpczHandle> handles); + absl::Span<IpczHandle> handles, + IpczHandle* validator); IpczResult AbortGet(); private: diff --git a/chromium/third_party/ipcz/src/ipcz/ref_counted_fragment.cc b/chromium/third_party/ipcz/src/ipcz/ref_counted_fragment.cc index cd4a2af315f..fa17e5db176 100644 --- a/chromium/third_party/ipcz/src/ipcz/ref_counted_fragment.cc +++ b/chromium/third_party/ipcz/src/ipcz/ref_counted_fragment.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/ref_counted_fragment.h b/chromium/third_party/ipcz/src/ipcz/ref_counted_fragment.h index 09c01f374c9..7238debb903 100644 --- a/chromium/third_party/ipcz/src/ipcz/ref_counted_fragment.h +++ b/chromium/third_party/ipcz/src/ipcz/ref_counted_fragment.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/ref_counted_fragment_test.cc b/chromium/third_party/ipcz/src/ipcz/ref_counted_fragment_test.cc index ed33e61dc62..d5a2243a693 100644 --- a/chromium/third_party/ipcz/src/ipcz/ref_counted_fragment_test.cc +++ b/chromium/third_party/ipcz/src/ipcz/ref_counted_fragment_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/remote_router_link.cc b/chromium/third_party/ipcz/src/ipcz/remote_router_link.cc index ac468619666..a2dbc961d6c 100644 --- a/chromium/third_party/ipcz/src/ipcz/remote_router_link.cc +++ b/chromium/third_party/ipcz/src/ipcz/remote_router_link.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -19,7 +19,8 @@ namespace ipcz { -RemoteRouterLink::RemoteRouterLink(Ref<NodeLink> node_link, +RemoteRouterLink::RemoteRouterLink(const OperationContext& context, + Ref<NodeLink> node_link, SublinkId sublink, FragmentRef<RouterLinkState> link_state, LinkType type, @@ -33,7 +34,7 @@ RemoteRouterLink::RemoteRouterLink(Ref<NodeLink> node_link, ABSL_ASSERT(type.is_central() == !link_state.is_null()); if (type.is_central()) { - SetLinkState(std::move(link_state)); + SetLinkState(context, std::move(link_state)); } } @@ -41,16 +42,18 @@ RemoteRouterLink::~RemoteRouterLink() = default; // static Ref<RemoteRouterLink> RemoteRouterLink::Create( + const OperationContext& context, Ref<NodeLink> node_link, SublinkId sublink, FragmentRef<RouterLinkState> link_state, LinkType type, LinkSide side) { - return AdoptRef(new RemoteRouterLink(std::move(node_link), sublink, + return AdoptRef(new RemoteRouterLink(context, std::move(node_link), sublink, std::move(link_state), type, side)); } -void RemoteRouterLink::SetLinkState(FragmentRef<RouterLinkState> state) { +void RemoteRouterLink::SetLinkState(const OperationContext& context, + FragmentRef<RouterLinkState> state) { ABSL_ASSERT(type_.is_central()); ABSL_ASSERT(!state.is_null()); @@ -60,9 +63,9 @@ void RemoteRouterLink::SetLinkState(FragmentRef<RouterLinkState> state) { FragmentDescriptor descriptor = state.fragment().descriptor(); memory->WaitForBufferAsync( descriptor.buffer_id(), - [self = WrapRefCounted(this), memory, descriptor] { - self->SetLinkState(memory->AdoptFragmentRef<RouterLinkState>( - memory->GetFragment(descriptor))); + [self = WrapRefCounted(this), memory, descriptor, context] { + self->SetLinkState(context, memory->AdoptFragmentRef<RouterLinkState>( + memory->GetFragment(descriptor))); }); return; } @@ -72,10 +75,19 @@ void RemoteRouterLink::SetLinkState(FragmentRef<RouterLinkState> state) { // SetLinkState() must be called with an addressable fragment only once. ABSL_ASSERT(link_state_.load(std::memory_order_acquire) == nullptr); - // The release when storing `link_state_` is balanced by an acquire in - // GetLinkState(). link_state_fragment_ = std::move(state); - link_state_.store(link_state_fragment_.get(), std::memory_order_release); + + std::vector<std::function<void()>> callbacks; + { + absl::MutexLock lock(&mutex_); + // This store-release is balanced by a load-acquire in GetLinkState(). + link_state_.store(link_state_fragment_.get(), std::memory_order_release); + link_state_callbacks_.swap(callbacks); + } + + for (auto& callback : callbacks) { + callback(); + } // If this side of the link was already marked stable before the // RouterLinkState was available, `side_is_stable_` will be true. In that @@ -86,7 +98,7 @@ void RemoteRouterLink::SetLinkState(FragmentRef<RouterLinkState> state) { MarkSideStable(); } if (Ref<Router> router = node_link()->GetRouter(sublink_)) { - router->Flush(Router::kForceProxyBypassAttempt); + router->Flush(context, Router::kForceProxyBypassAttempt); } } @@ -98,6 +110,18 @@ RouterLinkState* RemoteRouterLink::GetLinkState() const { return link_state_.load(std::memory_order_acquire); } +void RemoteRouterLink::WaitForLinkStateAsync(std::function<void()> callback) { + { + absl::MutexLock lock(&mutex_); + if (!link_state_.load(std::memory_order_relaxed)) { + link_state_callbacks_.push_back(std::move(callback)); + return; + } + } + + callback(); +} + Ref<Router> RemoteRouterLink::GetLocalPeer() { return nullptr; } @@ -112,7 +136,8 @@ void RemoteRouterLink::AllocateParcelData(size_t num_bytes, parcel.AllocateData(num_bytes, allow_partial, &node_link()->memory()); } -void RemoteRouterLink::AcceptParcel(Parcel& parcel) { +void RemoteRouterLink::AcceptParcel(const OperationContext& context, + Parcel& parcel) { const absl::Span<Ref<APIObject>> objects = parcel.objects_view(); msg::AcceptParcel accept; @@ -195,7 +220,14 @@ void RemoteRouterLink::AcceptParcel(Parcel& parcel) { // Serialize attached objects. We accumulate the Routers of all attached // portals, because we need to reference them again after transmission, with // a 1:1 correspondence to the serialized RouterDescriptors. - absl::InlinedVector<Ref<Router>, 4> routers_to_proxy; + absl::InlinedVector<Ref<Router>, 4> routers_to_proxy(num_portals); + absl::InlinedVector<RouterDescriptor, 4> descriptors(num_portals); + + // Explicitly zero the descriptor memory since there may be padding bits + // within and we'll be copying the full contents into message data below. + memset(descriptors.data(), 0, descriptors.size() * sizeof(descriptors[0])); + + size_t portal_index = 0; for (size_t i = 0; i < objects.size(); ++i) { APIObject& object = *objects[i]; @@ -204,8 +236,11 @@ void RemoteRouterLink::AcceptParcel(Parcel& parcel) { handle_types[i] = HandleType::kPortal; Ref<Router> router = Portal::FromObject(&object)->router(); - router->SerializeNewRouter(*node_link(), new_routers[i]); - routers_to_proxy.push_back(std::move(router)); + ABSL_ASSERT(portal_index < num_portals); + router->SerializeNewRouter(context, *node_link(), + descriptors[portal_index]); + routers_to_proxy[portal_index] = std::move(router); + ++portal_index; break; } @@ -220,6 +255,11 @@ void RemoteRouterLink::AcceptParcel(Parcel& parcel) { } } + // Copy all the serialized router descriptors into the message. Our local + // copy will supply inputs for BeginProxyingToNewRouter() calls below. + memcpy(new_routers.data(), descriptors.data(), + new_routers.size() * sizeof(new_routers[0])); + if (must_split_parcel) { msg::AcceptParcelDriverObjects accept_objects; accept_objects.params().sublink = sublink_; @@ -241,9 +281,10 @@ void RemoteRouterLink::AcceptParcel(Parcel& parcel) { // Now that the parcel has been transmitted, it's safe to start proxying from // any routers whose routes have just been extended to the destination. - ABSL_ASSERT(routers_to_proxy.size() == new_routers.size()); + ABSL_ASSERT(routers_to_proxy.size() == descriptors.size()); for (size_t i = 0; i < routers_to_proxy.size(); ++i) { - routers_to_proxy[i]->BeginProxyingToNewRouter(*node_link(), new_routers[i]); + routers_to_proxy[i]->BeginProxyingToNewRouter(context, *node_link(), + descriptors[i]); } // Finally, a Parcel will normally close all attached objects when destroyed. @@ -254,60 +295,36 @@ void RemoteRouterLink::AcceptParcel(Parcel& parcel) { } } -void RemoteRouterLink::AcceptRouteClosure(SequenceNumber sequence_length) { +void RemoteRouterLink::AcceptRouteClosure(const OperationContext& context, + SequenceNumber sequence_length) { msg::RouteClosed route_closed; route_closed.params().sublink = sublink_; route_closed.params().sequence_length = sequence_length; node_link()->Transmit(route_closed); } -size_t RemoteRouterLink::GetParcelCapacityInBytes(const IpczPutLimits& limits) { - if (limits.max_queued_bytes == 0 || limits.max_queued_parcels == 0) { - return 0; - } - - RouterLinkState* state = GetLinkState(); - if (!state) { - // This is only a best-effort estimate. With no link state yet, err on the - // side of more data flow. - return limits.max_queued_bytes; - } - - const RouterLinkState::QueueState peer_queue = - state->GetQueueState(side_.opposite()); - if (peer_queue.num_parcels >= limits.max_queued_parcels || - peer_queue.num_bytes >= limits.max_queued_bytes) { - return 0; +AtomicQueueState* RemoteRouterLink::GetPeerQueueState() { + if (auto* state = GetLinkState()) { + return &state->GetQueueState(side_.opposite()); } - - return limits.max_queued_bytes - peer_queue.num_bytes; + return nullptr; } -RouterLinkState::QueueState RemoteRouterLink::GetPeerQueueState() { +AtomicQueueState* RemoteRouterLink::GetLocalQueueState() { if (auto* state = GetLinkState()) { - return state->GetQueueState(side_.opposite()); + return &state->GetQueueState(side_); } - return {.num_parcels = 0, .num_bytes = 0}; -} - -bool RemoteRouterLink::UpdateInboundQueueState(size_t num_parcels, - size_t num_bytes) { - RouterLinkState* state = GetLinkState(); - return state && state->UpdateQueueState(side_, num_parcels, num_bytes); -} - -void RemoteRouterLink::NotifyDataConsumed() { - msg::NotifyDataConsumed notify; - notify.params().sublink = sublink_; - node_link()->Transmit(notify); + return nullptr; } -bool RemoteRouterLink::EnablePeerMonitoring(bool enable) { - RouterLinkState* state = GetLinkState(); - return state && state->SetSideIsMonitoringPeer(side_, enable); +void RemoteRouterLink::SnapshotPeerQueueState(const OperationContext& context) { + msg::SnapshotPeerQueueState snapshot; + snapshot.params().sublink = sublink_; + node_link()->Transmit(snapshot); } -void RemoteRouterLink::AcceptRouteDisconnected() { +void RemoteRouterLink::AcceptRouteDisconnected( + const OperationContext& context) { msg::RouteDisconnected route_disconnected; route_disconnected.params().sublink = sublink_; node_link()->Transmit(route_disconnected); @@ -341,7 +358,8 @@ void RemoteRouterLink::Unlock() { } } -bool RemoteRouterLink::FlushOtherSideIfWaiting() { +bool RemoteRouterLink::FlushOtherSideIfWaiting( + const OperationContext& context) { RouterLinkState* state = GetLinkState(); if (!state || !state->ResetWaitingBit(side_.opposite())) { return false; @@ -369,7 +387,8 @@ void RemoteRouterLink::Deactivate() { node_link()->RemoveRemoteRouterLink(sublink_); } -void RemoteRouterLink::BypassPeer(const NodeName& bypass_target_node, +void RemoteRouterLink::BypassPeer(const OperationContext& context, + const NodeName& bypass_target_node, SublinkId bypass_target_sublink) { msg::BypassPeer bypass; bypass.params().sublink = sublink_; @@ -379,7 +398,8 @@ void RemoteRouterLink::BypassPeer(const NodeName& bypass_target_node, node_link()->Transmit(bypass); } -void RemoteRouterLink::StopProxying(SequenceNumber inbound_sequence_length, +void RemoteRouterLink::StopProxying(const OperationContext& context, + SequenceNumber inbound_sequence_length, SequenceNumber outbound_sequence_length) { msg::StopProxying stop; stop.params().sublink = sublink_; @@ -388,7 +408,8 @@ void RemoteRouterLink::StopProxying(SequenceNumber inbound_sequence_length, node_link()->Transmit(stop); } -void RemoteRouterLink::ProxyWillStop(SequenceNumber inbound_sequence_length) { +void RemoteRouterLink::ProxyWillStop(const OperationContext& context, + SequenceNumber inbound_sequence_length) { msg::ProxyWillStop will_stop; will_stop.params().sublink = sublink_; will_stop.params().inbound_sequence_length = inbound_sequence_length; @@ -396,6 +417,7 @@ void RemoteRouterLink::ProxyWillStop(SequenceNumber inbound_sequence_length) { } void RemoteRouterLink::BypassPeerWithLink( + const OperationContext& context, SublinkId new_sublink, FragmentRef<RouterLinkState> new_link_state, SequenceNumber inbound_sequence_length) { @@ -409,6 +431,7 @@ void RemoteRouterLink::BypassPeerWithLink( } void RemoteRouterLink::StopProxyingToLocalPeer( + const OperationContext& context, SequenceNumber outbound_sequence_length) { msg::StopProxyingToLocalPeer stop; stop.params().sublink = sublink_; diff --git a/chromium/third_party/ipcz/src/ipcz/remote_router_link.h b/chromium/third_party/ipcz/src/ipcz/remote_router_link.h index 0eddf338c00..970bf550579 100644 --- a/chromium/third_party/ipcz/src/ipcz/remote_router_link.h +++ b/chromium/third_party/ipcz/src/ipcz/remote_router_link.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,6 +6,8 @@ #define IPCZ_SRC_IPCZ_REMOTE_ROUTER_LINK_H_ #include <atomic> +#include <functional> +#include <vector> #include "ipcz/fragment_ref.h" #include "ipcz/link_side.h" @@ -13,6 +15,7 @@ #include "ipcz/router_link.h" #include "ipcz/router_link_state.h" #include "ipcz/sublink_id.h" +#include "third_party/abseil-cpp/absl/synchronization/mutex.h" #include "util/ref_counted.h" namespace ipcz { @@ -39,7 +42,8 @@ class RemoteRouterLink : public RouterLink { // of link it is -- which for remote links must be either kCentral, // kPeripheralInward, or kPeripheralOutward. If the link is kCentral, a // non-null `link_state` must be provided for the link's RouterLinkState. - static Ref<RemoteRouterLink> Create(Ref<NodeLink> node_link, + static Ref<RemoteRouterLink> Create(const OperationContext& context, + Ref<NodeLink> node_link, SublinkId sublink, FragmentRef<RouterLinkState> link_state, LinkType type, @@ -51,40 +55,46 @@ class RemoteRouterLink : public RouterLink { // RouterLink: LinkType GetType() const override; RouterLinkState* GetLinkState() const override; + void WaitForLinkStateAsync(std::function<void()> callback) override; Ref<Router> GetLocalPeer() override; RemoteRouterLink* AsRemoteRouterLink() override; void AllocateParcelData(size_t num_bytes, bool allow_partial, Parcel& parcel) override; - void AcceptParcel(Parcel& parcel) override; - void AcceptRouteClosure(SequenceNumber sequence_length) override; - void AcceptRouteDisconnected() override; - size_t GetParcelCapacityInBytes(const IpczPutLimits& limits) override; - RouterLinkState::QueueState GetPeerQueueState() override; - bool UpdateInboundQueueState(size_t num_parcels, size_t num_bytes) override; - void NotifyDataConsumed() override; - bool EnablePeerMonitoring(bool enable) override; + void AcceptParcel(const OperationContext& context, Parcel& parcel) override; + void AcceptRouteClosure(const OperationContext& context, + SequenceNumber sequence_length) override; + void AcceptRouteDisconnected(const OperationContext& context) override; + AtomicQueueState* GetPeerQueueState() override; + AtomicQueueState* GetLocalQueueState() override; + void SnapshotPeerQueueState(const OperationContext& context) override; void MarkSideStable() override; bool TryLockForBypass(const NodeName& bypass_request_source) override; bool TryLockForClosure() override; void Unlock() override; - bool FlushOtherSideIfWaiting() override; + bool FlushOtherSideIfWaiting(const OperationContext& context) override; bool CanNodeRequestBypass(const NodeName& bypass_request_source) override; - void BypassPeer(const NodeName& bypass_target_node, + void BypassPeer(const OperationContext& context, + const NodeName& bypass_target_node, SublinkId bypass_request_sublink) override; - void StopProxying(SequenceNumber inbound_sequence_length, + void StopProxying(const OperationContext& context, + SequenceNumber inbound_sequence_length, SequenceNumber outbound_sequence_length) override; - void ProxyWillStop(SequenceNumber inbound_sequence_length) override; - void BypassPeerWithLink(SublinkId new_sublink, + void ProxyWillStop(const OperationContext& context, + SequenceNumber inbound_sequence_length) override; + void BypassPeerWithLink(const OperationContext& context, + SublinkId new_sublink, FragmentRef<RouterLinkState> new_link_state, SequenceNumber inbound_sequence_length) override; void StopProxyingToLocalPeer( + const OperationContext& context, SequenceNumber outbound_sequence_length) override; void Deactivate() override; std::string Describe() const override; private: - RemoteRouterLink(Ref<NodeLink> node_link, + RemoteRouterLink(const OperationContext& context, + Ref<NodeLink> node_link, SublinkId sublink, FragmentRef<RouterLinkState> link_state, LinkType type, @@ -94,7 +104,8 @@ class RemoteRouterLink : public RouterLink { // Sets this link's RouterLinkState. `state` must be pending or addressable // and this must be a central link. - void SetLinkState(FragmentRef<RouterLinkState> state); + void SetLinkState(const OperationContext& context, + FragmentRef<RouterLinkState> state); const Ref<NodeLink> node_link_; const SublinkId sublink_; @@ -118,6 +129,11 @@ class RemoteRouterLink : public RouterLink { // that value indefinitely, so any non-null value loaded from this field is // safe to dereference for the duration of the RemoteRouterLink's lifetime. std::atomic<RouterLinkState*> link_state_{nullptr}; + + // Set of callbacks to be invoked as soon as this link has a RouterLinkState. + absl::Mutex mutex_; + std::vector<std::function<void()>> link_state_callbacks_ + ABSL_GUARDED_BY(mutex_); }; } // namespace ipcz diff --git a/chromium/third_party/ipcz/src/ipcz/route_edge.cc b/chromium/third_party/ipcz/src/ipcz/route_edge.cc index 5b6a37171e4..836d65c799b 100644 --- a/chromium/third_party/ipcz/src/ipcz/route_edge.cc +++ b/chromium/third_party/ipcz/src/ipcz/route_edge.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/route_edge.h b/chromium/third_party/ipcz/src/ipcz/route_edge.h index 372ad2cf4c7..278d11bd925 100644 --- a/chromium/third_party/ipcz/src/ipcz/route_edge.h +++ b/chromium/third_party/ipcz/src/ipcz/route_edge.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/route_edge_test.cc b/chromium/third_party/ipcz/src/ipcz/route_edge_test.cc index 8d84a242b75..39a9469404c 100644 --- a/chromium/third_party/ipcz/src/ipcz/route_edge_test.cc +++ b/chromium/third_party/ipcz/src/ipcz/route_edge_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/router.cc b/chromium/third_party/ipcz/src/ipcz/router.cc index eae86db2c20..ba698426f23 100644 --- a/chromium/third_party/ipcz/src/ipcz/router.cc +++ b/chromium/third_party/ipcz/src/ipcz/router.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -9,12 +9,15 @@ #include <cstring> #include <utility> +#include "ipcz/atomic_queue_state.h" #include "ipcz/ipcz.h" #include "ipcz/local_router_link.h" #include "ipcz/node_link.h" +#include "ipcz/operation_context.h" #include "ipcz/remote_router_link.h" #include "ipcz/sequence_number.h" #include "ipcz/trap_event_dispatcher.h" +#include "ipcz/validator.h" #include "third_party/abseil-cpp/absl/base/macros.h" #include "third_party/abseil-cpp/absl/container/inlined_vector.h" #include "third_party/abseil-cpp/absl/synchronization/mutex.h" @@ -96,6 +99,12 @@ bool Router::IsOnCentralRemoteLink() { void Router::QueryStatus(IpczPortalStatus& status) { absl::MutexLock lock(&mutex_); + AtomicQueueState::QueryResult result; + if (auto* state = GetPeerQueueState()) { + result = state->Query({.monitor_parcels = false, .monitor_bytes = false}); + } + + UpdateStatusForPeerQueueState(result); const size_t size = std::min(status.size, status_.size); memcpy(&status, &status_, size); status.size = size; @@ -136,7 +145,8 @@ IpczResult Router::SendOutboundParcel(Parcel& parcel) { outbound_parcels_.GetCurrentSequenceLength(); parcel.set_sequence_number(sequence_number); if (outward_edge_.primary_link() && - outbound_parcels_.MaybeSkipSequenceNumber(sequence_number)) { + outbound_parcels_.SkipElement(sequence_number, + parcel.data_view().size())) { link = outward_edge_.primary_link(); } else { // If there are no unsent parcels ahead of this one in the outbound @@ -151,28 +161,30 @@ IpczResult Router::SendOutboundParcel(Parcel& parcel) { } } + const OperationContext context{OperationContext::kAPICall}; if (link) { - link->AcceptParcel(parcel); + link->AcceptParcel(context, parcel); } else { - Flush(); + Flush(context); } return IPCZ_RESULT_OK; } void Router::CloseRoute() { + const OperationContext context{OperationContext::kAPICall}; TrapEventDispatcher dispatcher; Ref<RouterLink> link; { absl::MutexLock lock(&mutex_); outbound_parcels_.SetFinalSequenceLength( outbound_parcels_.GetCurrentSequenceLength()); - traps_.RemoveAll(dispatcher); + traps_.RemoveAll(context, dispatcher); } - - Flush(); + Flush(context); } -void Router::SetOutwardLink(Ref<RouterLink> link) { +void Router::SetOutwardLink(const OperationContext& context, + Ref<RouterLink> link) { ABSL_ASSERT(link); { @@ -192,12 +204,12 @@ void Router::SetOutwardLink(Ref<RouterLink> link) { if (link) { // If the link wasn't adopted, this Router has already been disconnected. - link->AcceptRouteDisconnected(); + link->AcceptRouteDisconnected(context); link->Deactivate(); return; } - Flush(kForceProxyBypassAttempt); + Flush(context, kForceProxyBypassAttempt); } size_t Router::GetOutboundCapacityInBytes(const IpczPutLimits& limits) { @@ -205,45 +217,32 @@ size_t Router::GetOutboundCapacityInBytes(const IpczPutLimits& limits) { return 0; } - size_t num_queued_bytes = 0; - Ref<RouterLink> link; - { - absl::MutexLock lock(&mutex_); - if (outbound_parcels_.GetNumAvailableElements() >= - limits.max_queued_parcels) { - return 0; - } - if (outbound_parcels_.GetTotalAvailableElementSize() > - limits.max_queued_bytes) { - return 0; - } + const OperationContext context{OperationContext::kAPICall}; + SnapshotPeerQueueState(context); - num_queued_bytes = outbound_parcels_.GetTotalAvailableElementSize(); - link = outward_edge_.primary_link(); + absl::MutexLock lock(&mutex_); + if (status_.num_remote_parcels >= limits.max_queued_parcels || + status_.num_remote_bytes >= limits.max_queued_bytes) { + return 0; } - size_t link_capacity = - link ? link->GetParcelCapacityInBytes(limits) : limits.max_queued_bytes; - if (link_capacity <= num_queued_bytes) { + if (outbound_parcels_.GetNumAvailableElements() >= + limits.max_queued_parcels - status_.num_remote_parcels) { return 0; } - return link_capacity - num_queued_bytes; -} - -size_t Router::GetInboundCapacityInBytes(const IpczPutLimits& limits) { - absl::MutexLock lock(&mutex_); - const size_t num_queued_parcels = inbound_parcels_.GetNumAvailableElements(); - const size_t num_queued_bytes = - inbound_parcels_.GetTotalAvailableElementSize(); - if (num_queued_bytes >= limits.max_queued_bytes || - num_queued_parcels >= limits.max_queued_parcels) { + const size_t num_bytes_pending = + outbound_parcels_.GetTotalAvailableElementSize(); + const size_t available_capacity = + limits.max_queued_bytes - status_.num_remote_bytes; + if (num_bytes_pending >= available_capacity) { return 0; } - return limits.max_queued_bytes - num_queued_bytes; + return available_capacity - num_bytes_pending; } -bool Router::AcceptInboundParcel(Parcel& parcel) { +bool Router::AcceptInboundParcel(const OperationContext& context, + Parcel& parcel) { TrapEventDispatcher dispatcher; { absl::MutexLock lock(&mutex_); @@ -258,22 +257,17 @@ bool Router::AcceptInboundParcel(Parcel& parcel) { // If this is a terminal router, we may have trap events to fire. status_.num_local_parcels = inbound_parcels_.GetNumAvailableElements(); status_.num_local_bytes = inbound_parcels_.GetTotalAvailableElementSize(); - traps_.UpdatePortalStatus(status_, TrapSet::UpdateReason::kNewLocalParcel, - dispatcher); - - const Ref<RouterLink>& outward_link = outward_edge_.primary_link(); - if (outward_link && outward_link->GetType().is_central()) { - outward_link->UpdateInboundQueueState(status_.num_local_parcels, - status_.num_local_bytes); - } + traps_.UpdatePortalStatus( + context, status_, TrapSet::UpdateReason::kNewLocalParcel, dispatcher); } } - Flush(); + Flush(context); return true; } -bool Router::AcceptOutboundParcel(Parcel& parcel) { +bool Router::AcceptOutboundParcel(const OperationContext& context, + Parcel& parcel) { { absl::MutexLock lock(&mutex_); @@ -294,11 +288,12 @@ bool Router::AcceptOutboundParcel(Parcel& parcel) { } } - Flush(); + Flush(context); return true; } -bool Router::AcceptRouteClosureFrom(LinkType link_type, +bool Router::AcceptRouteClosureFrom(const OperationContext& context, + LinkType link_type, SequenceNumber sequence_length) { TrapEventDispatcher dispatcher; { @@ -316,8 +311,10 @@ bool Router::AcceptRouteClosureFrom(LinkType link_type, if (inbound_parcels_.IsSequenceFullyConsumed()) { status_.flags |= IPCZ_PORTAL_STATUS_DEAD; } - traps_.UpdatePortalStatus(status_, TrapSet::UpdateReason::kPeerClosed, - dispatcher); + status_.num_remote_bytes = 0; + status_.num_remote_parcels = 0; + traps_.UpdatePortalStatus( + context, status_, TrapSet::UpdateReason::kPeerClosed, dispatcher); } } else if (link_type.is_peripheral_inward()) { if (!outbound_parcels_.SetFinalSequenceLength(sequence_length)) { @@ -334,11 +331,12 @@ bool Router::AcceptRouteClosureFrom(LinkType link_type, } } - Flush(); + Flush(context); return true; } -bool Router::AcceptRouteDisconnectedFrom(LinkType link_type) { +bool Router::AcceptRouteDisconnectedFrom(const OperationContext& context, + LinkType link_type) { TrapEventDispatcher dispatcher; absl::InlinedVector<Ref<RouterLink>, 4> forwarding_links; { @@ -369,53 +367,82 @@ bool Router::AcceptRouteDisconnectedFrom(LinkType link_type) { if (inbound_parcels_.IsSequenceFullyConsumed()) { status_.flags |= IPCZ_PORTAL_STATUS_DEAD; } - traps_.UpdatePortalStatus(status_, TrapSet::UpdateReason::kPeerClosed, - dispatcher); + status_.num_remote_parcels = 0; + status_.num_remote_bytes = 0; + traps_.UpdatePortalStatus(context, status_, + TrapSet::UpdateReason::kPeerClosed, dispatcher); } } for (const Ref<RouterLink>& link : forwarding_links) { if (link) { DVLOG(4) << "Forwarding disconnection over " << link->Describe(); - link->AcceptRouteDisconnected(); + link->AcceptRouteDisconnected(context); link->Deactivate(); } } - Flush(); + Flush(context); return true; } -void Router::NotifyPeerConsumedData() { +void Router::SnapshotPeerQueueState(const OperationContext& context) { TrapEventDispatcher dispatcher; - { - absl::MutexLock lock(&mutex_); - const Ref<RouterLink>& outward_link = outward_edge_.primary_link(); - if (!outward_link || !outward_link->GetType().is_central() || - inward_edge_) { - return; - } + absl::ReleasableMutexLock lock(&mutex_); + Ref<RouterLink> outward_link = outward_edge_.primary_link(); + if (!outward_link || !outward_link->GetType().is_central() || inward_edge_) { + return; + } - const RouterLinkState::QueueState peer_state = - outward_link->GetPeerQueueState(); - status_.num_remote_parcels = peer_state.num_parcels; - status_.num_remote_bytes = peer_state.num_bytes; - traps_.UpdatePortalStatus(status_, TrapSet::UpdateReason::kRemoteActivity, - dispatcher); + AtomicQueueState* peer_state = outward_link->GetPeerQueueState(); + if (!peer_state) { + lock.Release(); + // Try again after we have RouterLinkState access. + outward_link->WaitForLinkStateAsync([self = WrapRefCounted(this), context] { + self->SnapshotPeerQueueState(context); + }); + return; + } - if (!traps_.need_remote_state()) { - outward_link->EnablePeerMonitoring(false); - } + // Start with a cheaper snapshot, which may be good enough. + const AtomicQueueState::QueryResult state = + peer_state->Query({.monitor_parcels = false, .monitor_bytes = false}); + UpdateStatusForPeerQueueState(state); + traps_.UpdatePortalStatus(context, status_, + TrapSet::UpdateReason::kRemoteActivity, dispatcher); + if (!traps_.need_remote_state()) { + return; } + + const bool monitor_sequence_length = + traps_.need_remote_parcels() && !state.num_parcels_consumed.monitored; + const bool monitor_num_bytes = + traps_.need_remote_bytes() && !state.num_bytes_consumed.monitored; + if (!monitor_sequence_length && !monitor_num_bytes) { + return; + } + + // We have at least one trap interested in remote queue state, the caller + // requested monitoring, and the state isn't currently being monitored. Take + // another snapshot, this time flipping any appropriate monitor bits. + UpdateStatusForPeerQueueState(peer_state->Query({ + .monitor_parcels = traps_.need_remote_parcels(), + .monitor_bytes = traps_.need_remote_bytes(), + })); + traps_.UpdatePortalStatus(context, status_, + TrapSet::UpdateReason::kRemoteActivity, dispatcher); } IpczResult Router::GetNextInboundParcel(IpczGetFlags flags, void* data, size_t* num_bytes, IpczHandle* handles, - size_t* num_handles) { + size_t* num_handles, + IpczHandle* validator) { + const OperationContext context{OperationContext::kAPICall}; TrapEventDispatcher dispatcher; Ref<RouterLink> link_to_notify; + Ref<NodeLink> remote_source; { absl::MutexLock lock(&mutex_); if (inbound_parcels_.IsSequenceFullyConsumed()) { @@ -447,6 +474,10 @@ IpczResult Router::GetNextInboundParcel(IpczGetFlags flags, return IPCZ_RESULT_RESOURCE_EXHAUSTED; } + if (validator) { + remote_source = p.remote_source(); + } + memcpy(data, p.data_view().data(), data_size); const bool ok = inbound_parcels_.Consume( data_size, absl::MakeSpan(handles, handles_size)); @@ -457,20 +488,23 @@ IpczResult Router::GetNextInboundParcel(IpczGetFlags flags, if (inbound_parcels_.IsSequenceFullyConsumed()) { status_.flags |= IPCZ_PORTAL_STATUS_DEAD; } - traps_.UpdatePortalStatus( - status_, TrapSet::UpdateReason::kLocalParcelConsumed, dispatcher); - - const Ref<RouterLink>& outward_link = outward_edge_.primary_link(); - if (outward_link && outward_link->GetType().is_central() && - outward_link->UpdateInboundQueueState(status_.num_local_parcels, - status_.num_local_bytes)) { - link_to_notify = outward_link; + traps_.UpdatePortalStatus(context, status_, + TrapSet::UpdateReason::kLocalParcelConsumed, + dispatcher); + if (RefreshLocalQueueState()) { + link_to_notify = outward_edge_.primary_link(); } } if (link_to_notify) { - link_to_notify->NotifyDataConsumed(); + link_to_notify->SnapshotPeerQueueState(context); + } + + if (validator) { + *validator = Validator::ReleaseAsHandle( + MakeRefCounted<Validator>(std::move(remote_source))); } + return IPCZ_RESULT_OK; } @@ -505,8 +539,11 @@ IpczResult Router::BeginGetNextIncomingParcel(const void** data, } IpczResult Router::CommitGetNextIncomingParcel(size_t num_data_bytes_consumed, - absl::Span<IpczHandle> handles) { + absl::Span<IpczHandle> handles, + IpczHandle* validator) { + const OperationContext context{OperationContext::kAPICall}; Ref<RouterLink> link_to_notify; + Ref<NodeLink> remote_source; TrapEventDispatcher dispatcher; { absl::MutexLock lock(&mutex_); @@ -523,6 +560,10 @@ IpczResult Router::CommitGetNextIncomingParcel(size_t num_data_bytes_consumed, return IPCZ_RESULT_OUT_OF_RANGE; } + if (validator) { + remote_source = p.remote_source(); + } + const bool ok = inbound_parcels_.Consume(num_data_bytes_consumed, handles); ABSL_ASSERT(ok); @@ -531,19 +572,21 @@ IpczResult Router::CommitGetNextIncomingParcel(size_t num_data_bytes_consumed, if (inbound_parcels_.IsSequenceFullyConsumed()) { status_.flags |= IPCZ_PORTAL_STATUS_DEAD; } - traps_.UpdatePortalStatus( - status_, TrapSet::UpdateReason::kLocalParcelConsumed, dispatcher); - - const Ref<RouterLink>& outward_link = outward_edge_.primary_link(); - if (outward_link && outward_link->GetType().is_central() && - outward_link->UpdateInboundQueueState(status_.num_local_parcels, - status_.num_local_bytes)) { - link_to_notify = outward_link; + traps_.UpdatePortalStatus(context, status_, + TrapSet::UpdateReason::kLocalParcelConsumed, + dispatcher); + if (RefreshLocalQueueState()) { + link_to_notify = outward_edge_.primary_link(); } } if (link_to_notify) { - link_to_notify->NotifyDataConsumed(); + link_to_notify->SnapshotPeerQueueState(context); + } + + if (validator) { + *validator = Validator::ReleaseAsHandle( + MakeRefCounted<Validator>(std::move(remote_source))); } return IPCZ_RESULT_OK; @@ -554,46 +597,42 @@ IpczResult Router::Trap(const IpczTrapConditions& conditions, uint64_t context, IpczTrapConditionFlags* satisfied_condition_flags, IpczPortalStatus* status) { - const bool need_remote_state = - (conditions.flags & (IPCZ_TRAP_BELOW_MAX_REMOTE_PARCELS | - IPCZ_TRAP_BELOW_MAX_REMOTE_BYTES)) != 0; - { - absl::MutexLock lock(&mutex_); - const Ref<RouterLink>& outward_link = outward_edge_.primary_link(); - if (need_remote_state) { - status_.num_remote_parcels = outbound_parcels_.GetNumAvailableElements(); - status_.num_remote_bytes = - outbound_parcels_.GetTotalAvailableElementSize(); - - if (outward_link && outward_link->GetType().is_central()) { - const RouterLinkState::QueueState peer_state = - outward_link->GetPeerQueueState(); - status_.num_remote_parcels = - SaturatedAdd(status_.num_remote_parcels, - static_cast<size_t>(peer_state.num_parcels)); - status_.num_remote_bytes = - SaturatedAdd(status_.num_remote_bytes, - static_cast<size_t>(peer_state.num_bytes)); - } - } - - const bool already_monitoring_remote_state = traps_.need_remote_state(); - IpczResult result = traps_.Add(conditions, handler, context, status_, - satisfied_condition_flags, status); - if (result != IPCZ_RESULT_OK || !need_remote_state) { - return result; - } + absl::MutexLock lock(&mutex_); - if (!already_monitoring_remote_state) { - outward_link->EnablePeerMonitoring(true); + const bool need_remote_parcels = + (conditions.flags & IPCZ_TRAP_BELOW_MAX_REMOTE_PARCELS) != 0; + const bool need_remote_bytes = + (conditions.flags & IPCZ_TRAP_BELOW_MAX_REMOTE_BYTES) != 0; + if (need_remote_parcels || need_remote_bytes) { + if (AtomicQueueState* peer_state = GetPeerQueueState()) { + const AtomicQueueState::QueryResult state = + peer_state->Query({.monitor_parcels = false, .monitor_bytes = false}); + UpdateStatusForPeerQueueState(state); + + // If the status already meets some conditions and would block trap + // installation, OR if it's already being monitored for changes, we can + // just go ahead and install the trap. Otherwise we have to re-query and + // set any monitoring bits ourselves. + const bool monitor_parcels = + need_remote_parcels && !state.num_parcels_consumed.monitored; + const bool monitor_bytes = + need_remote_bytes && !state.num_bytes_consumed.monitored; + if (!TrapSet::GetSatisfiedConditions(conditions, status_) && + (monitor_parcels || monitor_bytes)) { + UpdateStatusForPeerQueueState( + peer_state->Query({.monitor_parcels = need_remote_parcels, + .monitor_bytes = need_remote_bytes})); + } + } else { + status_.num_remote_parcels = + outbound_parcels_.GetCurrentSequenceLength().value(); + status_.num_remote_bytes = saturated_cast<size_t>( + outbound_parcels_.GetTotalElementSizeQueuedSoFar()); } } - // Safeguard against races between remote state changes and the new trap being - // installed above. Only reached if the new trap monitors remote state. - ABSL_ASSERT(need_remote_state); - NotifyPeerConsumedData(); - return IPCZ_RESULT_OK; + return traps_.Add(conditions, handler, context, status_, + satisfied_condition_flags, status); } IpczResult Router::MergeRoute(const Ref<Router>& other) { @@ -627,23 +666,33 @@ IpczResult Router::MergeRoute(const Ref<Router>& other) { other->bridge_->SetPrimaryLink(std::move(links.second)); } - Flush(); + const OperationContext context{OperationContext::kAPICall}; + Flush(context); return IPCZ_RESULT_OK; } // static Ref<Router> Router::Deserialize(const RouterDescriptor& descriptor, NodeLink& from_node_link) { + // All Router deserialization occurs as a direct result of some transport + // notification. + const OperationContext context{OperationContext::kTransportNotification}; + bool disconnected = false; auto router = MakeRefCounted<Router>(); + Ref<RemoteRouterLink> new_outward_link; { absl::MutexLock lock(&router->mutex_); - router->outbound_parcels_.ResetInitialSequenceNumber( - descriptor.next_outgoing_sequence_number); - router->inbound_parcels_.ResetInitialSequenceNumber( - descriptor.next_incoming_sequence_number); + router->outbound_parcels_.ResetSequence( + descriptor.next_outgoing_sequence_number, + descriptor.num_bytes_produced); + router->inbound_parcels_.ResetSequence( + descriptor.next_incoming_sequence_number, + descriptor.num_bytes_consumed); if (descriptor.peer_closed) { router->status_.flags |= IPCZ_PORTAL_STATUS_PEER_CLOSED; + router->status_.num_remote_parcels = 0; + router->status_.num_remote_bytes = 0; if (!router->inbound_parcels_.SetFinalSequenceLength( descriptor.closed_peer_sequence_length)) { return nullptr; @@ -653,126 +702,352 @@ Ref<Router> Router::Deserialize(const RouterDescriptor& descriptor, } } - Ref<RemoteRouterLink> new_link = from_node_link.AddRemoteRouterLink( - descriptor.new_sublink, nullptr, LinkType::kPeripheralOutward, - LinkSide::kB, router); - if (new_link) { - router->outward_edge_.SetPrimaryLink(std::move(new_link)); + if (descriptor.proxy_already_bypassed) { + // When split from a local peer, our remote counterpart (our remote peer's + // former local peer) will use this link to forward parcels it already + // received from our peer. This link decays like any other decaying link + // once its usefulness has expired. + // + // The sequence length toward this link is the current outbound sequence + // length, which is to say, we will not be sending any parcels that way. + // The sequence length from the link is whatever had already been sent + // to our counterpart back on the peer's node. + Ref<RemoteRouterLink> new_decaying_link = + from_node_link.AddRemoteRouterLink( + context, descriptor.new_decaying_sublink, nullptr, + LinkType::kPeripheralOutward, LinkSide::kB, router); + if (!new_decaying_link) { + return nullptr; + } + router->outward_edge_.SetPrimaryLink(std::move(new_decaying_link)); + router->outward_edge_.BeginPrimaryLinkDecay(); + router->outward_edge_.set_length_to_decaying_link( + router->outbound_parcels_.current_sequence_number()); + router->outward_edge_.set_length_from_decaying_link( + descriptor.decaying_incoming_sequence_length > SequenceNumber(0) + ? descriptor.decaying_incoming_sequence_length + : descriptor.next_incoming_sequence_number); + + new_outward_link = from_node_link.AddRemoteRouterLink( + context, descriptor.new_sublink, + from_node_link.memory().AdoptFragmentRef<RouterLinkState>( + from_node_link.memory().GetFragment( + descriptor.new_link_state_fragment)), + LinkType::kCentral, LinkSide::kB, router); + if (!new_outward_link) { + return nullptr; + } + router->outward_edge_.SetPrimaryLink(new_outward_link); DVLOG(4) << "Route extended from " << from_node_link.remote_node_name().ToString() << " to " << from_node_link.local_node_name().ToString() << " via sublink " - << descriptor.new_sublink; - } else if (!descriptor.peer_closed) { - // The new portal is DOA, either because the associated NodeLink is dead, - // or the sublink ID was already in use. The latter implies a bug or bad - // behavior, but it should be harmless to ignore beyond this point. - disconnected = true; + << descriptor.new_sublink << " and decaying sublink " + << descriptor.new_decaying_sublink; + } else { + if (!descriptor.new_link_state_fragment.is_null()) { + // No RouterLinkState fragment should be provided for this new + // peripheral link. + return nullptr; + } + new_outward_link = from_node_link.AddRemoteRouterLink( + context, descriptor.new_sublink, nullptr, + LinkType::kPeripheralOutward, LinkSide::kB, router); + if (new_outward_link) { + router->outward_edge_.SetPrimaryLink(new_outward_link); + + DVLOG(4) << "Route extended from " + << from_node_link.remote_node_name().ToString() << " to " + << from_node_link.local_node_name().ToString() + << " via sublink " << descriptor.new_sublink; + } else if (!descriptor.peer_closed) { + // The new portal is DOA, either because the associated NodeLink is + // dead, or the sublink ID was already in use. The latter implies a bug + // or bad behavior, but it should be harmless to ignore beyond this + // point. + disconnected = true; + } } } if (disconnected) { DVLOG(4) << "Disconnected new Router immediately after deserialization"; - router->AcceptRouteDisconnectedFrom(LinkType::kPeripheralOutward); + router->AcceptRouteDisconnectedFrom(context, LinkType::kPeripheralOutward); + } else if (descriptor.proxy_peer_node_name.is_valid()) { + // The source router rolled some peer bypass details into our descriptor to + // avoid some IPC overhead. We can begin bypassing the proxy now. + ABSL_ASSERT(new_outward_link); + router->BypassPeer(context, *new_outward_link, + descriptor.proxy_peer_node_name, + descriptor.proxy_peer_sublink); } - router->Flush(kForceProxyBypassAttempt); + + router->Flush(context, kForceProxyBypassAttempt); return router; } -void Router::SerializeNewRouter(NodeLink& to_node_link, +void Router::SerializeNewRouter(const OperationContext& context, + NodeLink& to_node_link, RouterDescriptor& descriptor) { TrapEventDispatcher dispatcher; - const SublinkId new_sublink = to_node_link.memory().AllocateSublinkIds(1); - descriptor.new_sublink = new_sublink; + Ref<Router> local_peer; + bool initiate_proxy_bypass = false; { absl::MutexLock lock(&mutex_); - traps_.RemoveAll(dispatcher); + traps_.RemoveAll(context, dispatcher); + local_peer = outward_edge_.GetLocalPeer(); + initiate_proxy_bypass = outward_edge_.primary_link() && + outward_edge_.primary_link()->TryLockForBypass( + to_node_link.remote_node_name()); + } - descriptor.next_outgoing_sequence_number = - outbound_parcels_.GetCurrentSequenceLength(); - descriptor.next_incoming_sequence_number = - inbound_parcels_.current_sequence_number(); + if (local_peer && initiate_proxy_bypass && + SerializeNewRouterWithLocalPeer(context, to_node_link, descriptor, + local_peer)) { + return; + } - // Initialize an inward edge but with no link yet. This ensures that we - // don't look like a terminal router while waiting for a link to be set, - // which can only happen after `descriptor` is transmitted. - inward_edge_.emplace(); + SerializeNewRouterAndConfigureProxy(context, to_node_link, descriptor, + initiate_proxy_bypass); +} - if (status_.flags & IPCZ_PORTAL_STATUS_PEER_CLOSED) { - descriptor.peer_closed = true; - descriptor.closed_peer_sequence_length = - *inbound_parcels_.final_sequence_length(); +bool Router::SerializeNewRouterWithLocalPeer(const OperationContext& context, + NodeLink& to_node_link, + RouterDescriptor& descriptor, + Ref<Router> local_peer) { + MultiMutexLock lock(&mutex_, &local_peer->mutex_); + if (local_peer->outward_edge_.GetLocalPeer() != this) { + // If the peer was closed, its link to us may already be invalidated. + return false; + } - // Ensure that the new edge decays its link as soon as it has one, since - // we know the link will not be used. - inward_edge_->BeginPrimaryLinkDecay(); - inward_edge_->set_length_to_decaying_link( - *inbound_parcels_.final_sequence_length()); - inward_edge_->set_length_from_decaying_link( - outbound_parcels_.current_sequence_number()); - } + FragmentRef<RouterLinkState> new_link_state = + to_node_link.memory().TryAllocateRouterLinkState(); + if (!new_link_state.is_addressable()) { + // If we couldn't allocate a RouterLinkState for a new central link, then + // we can't replace the central link yet. Fall back to the proxying case. + return false; + } - // Once `descriptor` is transmitted to the destination node and the new - // Router is created there, it may immediately begin transmitting messages - // back to this node regarding `new_sublink`. We establish a new - // RemoteRouterLink now and register it to `new_sublink` on `to_node_link`, - // so that any such incoming messages are routed to `this`. - // - // NOTE: We do not yet provide `this` itself with a reference to the new - // RemoteRouterLink, because it's not yet safe for us to send messages to - // the remote node regarding `new_sublink`. `descriptor` must be transmitted - // first. - Ref<RemoteRouterLink> new_link = to_node_link.AddRemoteRouterLink( - new_sublink, nullptr, LinkType::kPeripheralInward, LinkSide::kA, - WrapRefCounted(this)); + const SequenceNumber proxy_inbound_sequence_length = + local_peer->outbound_parcels_.current_sequence_number(); - DVLOG(4) << "Router " << this << " extending route with tentative new " - << new_link->Describe(); - } + // The local peer no longer needs its link to us. We'll give it a new + // outward link in BeginProxyingToNewRouter() after this descriptor is + // transmitted. + local_peer->outward_edge_.ReleasePrimaryLink(); + + // The primary new sublink to the destination node will act as the route's + // new central link between our local peer and the new remote router. + // + // An additional sublink is allocated to act as a decaying inward link from + // this router to the new one, so we can forward any inbound parcels that have + // already been queued here. + const SublinkId new_sublink = to_node_link.memory().AllocateSublinkIds(2); + const SublinkId decaying_sublink = SublinkId(new_sublink.value() + 1); + + // Register the new routes on the NodeLink. Note that we don't provide them to + // any routers yet since we don't want the routers using them until this + // descriptor is transmitted to its destination node. The links will be + // adopted after transmission in BeginProxyingToNewRouter(). + Ref<RouterLink> new_link = to_node_link.AddRemoteRouterLink( + context, new_sublink, new_link_state, LinkType::kCentral, LinkSide::kA, + local_peer); + + to_node_link.AddRemoteRouterLink(context, decaying_sublink, nullptr, + LinkType::kPeripheralInward, LinkSide::kA, + WrapRefCounted(this)); + + descriptor.new_sublink = new_sublink; + descriptor.new_link_state_fragment = new_link_state.release().descriptor(); + descriptor.new_decaying_sublink = decaying_sublink; + descriptor.proxy_already_bypassed = true; + descriptor.next_outgoing_sequence_number = + outbound_parcels_.GetCurrentSequenceLength(); + descriptor.num_bytes_produced = + outbound_parcels_.total_consumed_element_size(); + descriptor.next_incoming_sequence_number = + inbound_parcels_.current_sequence_number(); + descriptor.num_bytes_consumed = + inbound_parcels_.total_consumed_element_size(); + descriptor.decaying_incoming_sequence_length = proxy_inbound_sequence_length; + + DVLOG(4) << "Splitting local pair to move router with outbound sequence " + << "length " << descriptor.next_outgoing_sequence_number + << " and current inbound sequence number " + << descriptor.next_incoming_sequence_number; + + if (inbound_parcels_.final_sequence_length()) { + descriptor.peer_closed = true; + descriptor.closed_peer_sequence_length = + *inbound_parcels_.final_sequence_length(); + } + + // Initialize an inward edge that will immediately begin decaying once it has + // a link (established in BeginProxyingToNewRouter()). + inward_edge_.emplace(); + inward_edge_->BeginPrimaryLinkDecay(); + inward_edge_->set_length_to_decaying_link(proxy_inbound_sequence_length); + inward_edge_->set_length_from_decaying_link( + outbound_parcels_.GetCurrentSequenceLength()); + return true; } -void Router::BeginProxyingToNewRouter(NodeLink& to_node_link, - const RouterDescriptor& descriptor) { - // Acquire a reference to the RemoteRouterLink created by an earlier call to - // SerializeNewRouter(). If the NodeLink has already been disconnected, this - // may be null. - if (auto new_sublink = to_node_link.GetSublink(descriptor.new_sublink)) { - Ref<RemoteRouterLink> new_router_link = new_sublink->router_link; - { - absl::MutexLock lock(&mutex_); - ABSL_ASSERT(inward_edge_); +void Router::SerializeNewRouterAndConfigureProxy( + const OperationContext& context, + NodeLink& to_node_link, + RouterDescriptor& descriptor, + bool initiate_proxy_bypass) { + const SublinkId new_sublink = to_node_link.memory().AllocateSublinkIds(1); - // If the new router has already been closed or disconnected, we will - // discard the new link to it. - if (!outbound_parcels_.final_sequence_length() && !is_disconnected_) { - DVLOG(4) << "Router " << this << " will proxy to new router over " - << new_router_link->Describe(); + absl::MutexLock lock(&mutex_); + descriptor.new_sublink = new_sublink; + descriptor.new_link_state_fragment = FragmentDescriptor(); + descriptor.proxy_already_bypassed = false; + descriptor.next_outgoing_sequence_number = + outbound_parcels_.GetCurrentSequenceLength(); + descriptor.num_bytes_produced = + outbound_parcels_.total_consumed_element_size(); + descriptor.next_incoming_sequence_number = + inbound_parcels_.current_sequence_number(); + descriptor.num_bytes_consumed = + inbound_parcels_.total_consumed_element_size(); + + // Initialize an inward edge but with no link yet. This ensures that we + // don't look like a terminal router while waiting for a link to be set, + // which can only happen after `descriptor` is transmitted. + inward_edge_.emplace(); + + if (status_.flags & IPCZ_PORTAL_STATUS_PEER_CLOSED) { + descriptor.peer_closed = true; + descriptor.closed_peer_sequence_length = + *inbound_parcels_.final_sequence_length(); + + // Ensure that the new edge decays its link as soon as it has one, since + // we know the link will not be used. + inward_edge_->BeginPrimaryLinkDecay(); + inward_edge_->set_length_to_decaying_link( + *inbound_parcels_.final_sequence_length()); + inward_edge_->set_length_from_decaying_link( + outbound_parcels_.current_sequence_number()); + } else if (initiate_proxy_bypass && outward_edge_.primary_link()) { + RemoteRouterLink* remote_link = + outward_edge_.primary_link()->AsRemoteRouterLink(); + if (remote_link) { + descriptor.proxy_peer_node_name = + remote_link->node_link()->remote_node_name(); + descriptor.proxy_peer_sublink = remote_link->sublink(); + DVLOG(4) << "Will initiate proxy bypass immediately on deserialization " + << "with peer at " << descriptor.proxy_peer_node_name.ToString() + << " and peer route to proxy on sublink " + << descriptor.proxy_peer_sublink; - inward_edge_->SetPrimaryLink(std::move(new_router_link)); + inward_edge_->BeginPrimaryLinkDecay(); + outward_edge_.BeginPrimaryLinkDecay(); + } else { + // The link was locked in anticipation of initiating a proxy bypass, but + // that's no longer going to happen. + outward_edge_.primary_link()->Unlock(); + } + } + + // Once `descriptor` is transmitted to the destination node and the new + // Router is created there, it may immediately begin transmitting messages + // back to this node regarding `new_sublink`. We establish a new + // RemoteRouterLink now and register it to `new_sublink` on `to_node_link`, + // so that any such incoming messages are routed to `this`. + // + // NOTE: We do not yet provide `this` itself with a reference to the new + // RemoteRouterLink, because it's not yet safe for us to send messages to + // the remote node regarding `new_sublink`. `descriptor` must be transmitted + // first. + Ref<RemoteRouterLink> new_link = to_node_link.AddRemoteRouterLink( + context, new_sublink, nullptr, LinkType::kPeripheralInward, LinkSide::kA, + WrapRefCounted(this)); + DVLOG(4) << "Router " << this << " extending route with tentative new " + << new_link->Describe(); +} - Ref<RouterLink> outward_link = outward_edge_.primary_link(); - if (outward_link && outward_edge_.is_stable() && - inward_edge_->is_stable()) { - outward_link->MarkSideStable(); - } +void Router::BeginProxyingToNewRouter(const OperationContext& context, + NodeLink& to_node_link, + const RouterDescriptor& descriptor) { + Ref<RouterLink> peer_link; + Ref<Router> local_peer; + + // Acquire references to RemoteRouterLink(s) created by an earlier call to + // SerializeNewRouter(). If the NodeLink has already been disconnected, these + // may be null. + auto new_sublink = to_node_link.GetSublink(descriptor.new_sublink); + auto new_decaying_sublink = + to_node_link.GetSublink(descriptor.new_decaying_sublink); + if (!new_sublink) { + Flush(context, kForceProxyBypassAttempt); + return; + } + + Ref<RemoteRouterLink> new_primary_link = new_sublink->router_link; + Ref<RemoteRouterLink> new_decaying_link; + { + absl::MutexLock lock(&mutex_); + ABSL_ASSERT(inward_edge_); + + if (descriptor.proxy_already_bypassed) { + peer_link = outward_edge_.ReleasePrimaryLink(); + local_peer = peer_link ? peer_link->GetLocalPeer() : nullptr; + new_decaying_link = + new_decaying_sublink ? new_decaying_sublink->router_link : nullptr; + } + + if (local_peer && new_decaying_link && !is_disconnected_) { + // We've already bypassed this router. Use the new decaying link for our + // inward edge in case we need to forward parcels to the new router. The + // new primary link will be adopted by our peer further below. + inward_edge_->SetPrimaryLink(std::move(new_decaying_link)); + } else if (!outbound_parcels_.final_sequence_length() && + !new_decaying_link && !is_disconnected_) { + DVLOG(4) << "Router " << this << " will proxy to new router over " + << new_primary_link->Describe(); + inward_edge_->SetPrimaryLink(std::move(new_primary_link)); + + Ref<RouterLink> outward_link = outward_edge_.primary_link(); + if (outward_link && outward_edge_.is_stable() && + inward_edge_->is_stable()) { + outward_link->MarkSideStable(); } } + } - if (new_router_link) { - // The link was not adopted, so deactivate and discard it. - DVLOG(4) << "Dropping link to new router " << new_router_link->Describe(); - new_router_link->AcceptRouteDisconnected(); - new_router_link->Deactivate(); - return; - } + if (local_peer && new_primary_link && !new_decaying_link) { + // If we have a `local_peer` and no decaying link, this means the decaying + // link was successfully adopted for our own inward edge; and the primary + // link is therefore meant to serve as our local peer's new outward link + // directly to the new remote router. + local_peer->SetOutwardLink(context, std::move(new_primary_link)); + } + + // New links were not adopted, implying that the new router has already been + // closed or disconnected. + if (new_primary_link) { + DVLOG(4) << "Dropping link to new router " << new_primary_link->Describe(); + new_primary_link->AcceptRouteDisconnected(context); + new_primary_link->Deactivate(); + } + if (new_decaying_link) { + DVLOG(4) << "Dropping link to new router " << new_decaying_link->Describe(); + new_decaying_link->AcceptRouteDisconnected(context); + new_decaying_link->Deactivate(); } // We may have inbound parcels queued which need to be forwarded to the new // Router, so give them a chance to be flushed out. - Flush(kForceProxyBypassAttempt); + Flush(context, kForceProxyBypassAttempt); + if (local_peer) { + local_peer->Flush(context, kForceProxyBypassAttempt); + } } -bool Router::BypassPeer(RemoteRouterLink& requestor, +bool Router::BypassPeer(const OperationContext& context, + RemoteRouterLink& requestor, const NodeName& bypass_target_node, SublinkId bypass_target_sublink) { NodeLink& from_node_link = *requestor.node_link(); @@ -804,7 +1079,7 @@ bool Router::BypassPeer(RemoteRouterLink& requestor, from_node_link.node()->GetLink(bypass_target_node); if (link_to_bypass_target) { return BypassPeerWithNewRemoteLink( - requestor, *link_to_bypass_target, bypass_target_sublink, + context, requestor, *link_to_bypass_target, bypass_target_sublink, link_to_bypass_target->memory().TryAllocateRouterLinkState()); } @@ -812,25 +1087,28 @@ bool Router::BypassPeer(RemoteRouterLink& requestor, from_node_link.node()->EstablishLink( bypass_target_node, [router = WrapRefCounted(this), requestor = WrapRefCounted(&requestor), - bypass_target_sublink](NodeLink* link_to_bypass_target) { + bypass_target_sublink, context](NodeLink* link_to_bypass_target) { if (!link_to_bypass_target) { DLOG(ERROR) << "Disconnecting Router due to failed introduction"; - router->AcceptRouteDisconnectedFrom(LinkType::kPeripheralOutward); + router->AcceptRouteDisconnectedFrom(context, + LinkType::kPeripheralOutward); return; } router->BypassPeerWithNewRemoteLink( - *requestor, *link_to_bypass_target, bypass_target_sublink, + context, *requestor, *link_to_bypass_target, + bypass_target_sublink, link_to_bypass_target->memory().TryAllocateRouterLinkState()); }); return true; } // The second case is when the proxy's outward peer lives on our own node. - return BypassPeerWithNewLocalLink(requestor, bypass_target_sublink); + return BypassPeerWithNewLocalLink(context, requestor, bypass_target_sublink); } bool Router::AcceptBypassLink( + const OperationContext& context, NodeLink& new_node_link, SublinkId new_sublink, FragmentRef<RouterLinkState> new_link_state, @@ -873,7 +1151,7 @@ bool Router::AcceptBypassLink( // By convention the initiator of a bypass assumes side A of the bypass // link, so we assume side B. new_link = new_node_link.AddRemoteRouterLink( - new_sublink, std::move(new_link_state), LinkType::kCentral, + context, new_sublink, std::move(new_link_state), LinkType::kCentral, LinkSide::kB, WrapRefCounted(this)); if (new_link) { @@ -891,7 +1169,7 @@ bool Router::AcceptBypassLink( } if (!new_link) { - AcceptRouteDisconnectedFrom(LinkType::kCentral); + AcceptRouteDisconnectedFrom(context, LinkType::kCentral); return true; } @@ -899,20 +1177,21 @@ bool Router::AcceptBypassLink( // If the new link goes to the same place as the old link, we only need // to tell the proxy there to stop proxying. It has already conspired with // its local outward peer. - old_link->StopProxyingToLocalPeer(length_to_proxy_from_us); + old_link->StopProxyingToLocalPeer(context, length_to_proxy_from_us); } else { // Otherwise, tell the proxy to stop proxying and let its inward peer (our // new outward peer) know that the proxy will stop. - old_link->StopProxying(length_to_proxy_from_us, + old_link->StopProxying(context, length_to_proxy_from_us, inbound_sequence_length_from_bypassed_link); - new_link->ProxyWillStop(length_to_proxy_from_us); + new_link->ProxyWillStop(context, length_to_proxy_from_us); } - Flush(); + Flush(context); return true; } -bool Router::StopProxying(SequenceNumber inbound_sequence_length, +bool Router::StopProxying(const OperationContext& context, + SequenceNumber inbound_sequence_length, SequenceNumber outbound_sequence_length) { Ref<Router> bridge_peer; { @@ -965,14 +1244,15 @@ bool Router::StopProxying(SequenceNumber inbound_sequence_length, outbound_sequence_length); } - Flush(); + Flush(context); if (bridge_peer) { - bridge_peer->Flush(); + bridge_peer->Flush(context); } return true; } -bool Router::NotifyProxyWillStop(SequenceNumber inbound_sequence_length) { +bool Router::NotifyProxyWillStop(const OperationContext& context, + SequenceNumber inbound_sequence_length) { { absl::MutexLock lock(&mutex_); if (outward_edge_.is_stable()) { @@ -988,11 +1268,12 @@ bool Router::NotifyProxyWillStop(SequenceNumber inbound_sequence_length) { outward_edge_.set_length_from_decaying_link(inbound_sequence_length); } - Flush(); + Flush(context); return true; } -bool Router::StopProxyingToLocalPeer(SequenceNumber outbound_sequence_length) { +bool Router::StopProxyingToLocalPeer(const OperationContext& context, + SequenceNumber outbound_sequence_length) { Ref<Router> local_peer; Ref<Router> bridge_peer; { @@ -1072,15 +1353,16 @@ bool Router::StopProxyingToLocalPeer(SequenceNumber outbound_sequence_length) { return false; } - Flush(); - local_peer->Flush(); + Flush(context); + local_peer->Flush(context); if (bridge_peer) { - bridge_peer->Flush(); + bridge_peer->Flush(context); } return true; } -void Router::NotifyLinkDisconnected(RemoteRouterLink& link) { +void Router::NotifyLinkDisconnected(const OperationContext& context, + RemoteRouterLink& link) { { absl::MutexLock lock(&mutex_); if (outward_edge_.primary_link() == &link) { @@ -1099,13 +1381,13 @@ void Router::NotifyLinkDisconnected(RemoteRouterLink& link) { } if (link.GetType().is_outward()) { - AcceptRouteDisconnectedFrom(LinkType::kPeripheralOutward); + AcceptRouteDisconnectedFrom(context, LinkType::kPeripheralOutward); } else { - AcceptRouteDisconnectedFrom(LinkType::kPeripheralInward); + AcceptRouteDisconnectedFrom(context, LinkType::kPeripheralInward); } } -void Router::Flush(FlushBehavior behavior) { +void Router::Flush(const OperationContext& context, FlushBehavior behavior) { Ref<RouterLink> outward_link; Ref<RouterLink> inward_link; Ref<RouterLink> bridge_link; @@ -1120,6 +1402,8 @@ void Router::Flush(FlushBehavior behavior) { bool inward_link_decayed = false; bool outward_link_decayed = false; bool dropped_last_decaying_link = false; + bool snapshot_peer_queue_state = false; + bool peer_needs_local_state_update = false; ParcelsToFlush parcels_to_flush; { absl::MutexLock lock(&mutex_); @@ -1132,6 +1416,8 @@ void Router::Flush(FlushBehavior behavior) { decaying_inward_link = inward_edge_ ? inward_edge_->decaying_link() : nullptr; on_central_link = outward_link && outward_link->GetType().is_central(); + snapshot_peer_queue_state = on_central_link && traps_.need_remote_state(); + peer_needs_local_state_update = on_central_link && RefreshLocalQueueState(); if (bridge_) { // Bridges have either a primary link or decaying link, but never both. bridge_link = bridge_->primary_link() ? bridge_->primary_link() @@ -1233,7 +1519,7 @@ void Router::Flush(FlushBehavior behavior) { } for (ParcelToFlush& parcel : parcels_to_flush) { - parcel.link->AcceptParcel(parcel.parcel); + parcel.link->AcceptParcel(context, parcel.parcel); } if (outward_link_decayed) { @@ -1246,26 +1532,29 @@ void Router::Flush(FlushBehavior behavior) { if (bridge_link && outward_link && !inward_link && !decaying_inward_link && !decaying_outward_link) { - MaybeStartBridgeBypass(); + MaybeStartBridgeBypass(context); } if (dead_outward_link) { if (final_outward_sequence_length) { - dead_outward_link->AcceptRouteClosure(*final_outward_sequence_length); + dead_outward_link->AcceptRouteClosure(context, + *final_outward_sequence_length); } dead_outward_link->Deactivate(); } if (dead_inward_link) { if (final_inward_sequence_length) { - dead_inward_link->AcceptRouteClosure(*final_inward_sequence_length); + dead_inward_link->AcceptRouteClosure(context, + *final_inward_sequence_length); } dead_inward_link->Deactivate(); } if (dead_bridge_link) { if (final_inward_sequence_length) { - dead_bridge_link->AcceptRouteClosure(*final_inward_sequence_length); + dead_bridge_link->AcceptRouteClosure(context, + *final_inward_sequence_length); } } @@ -1274,21 +1563,95 @@ void Router::Flush(FlushBehavior behavior) { return; } + if (snapshot_peer_queue_state) { + SnapshotPeerQueueState(context); + } + + if (peer_needs_local_state_update) { + outward_link->SnapshotPeerQueueState(context); + } + if (!dropped_last_decaying_link && behavior != kForceProxyBypassAttempt) { // No relevant state changes, so there are no new bypass opportunities. return; } - if (inward_link && MaybeStartSelfBypass()) { + if (inward_link && MaybeStartSelfBypass(context)) { return; } if (outward_link) { - outward_link->FlushOtherSideIfWaiting(); + outward_link->FlushOtherSideIfWaiting(context); } } -bool Router::MaybeStartSelfBypass() { +AtomicQueueState* Router::GetPeerQueueState() { + if (!outward_edge_.primary_link()) { + return nullptr; + } + + if (!outward_edge_.primary_link()->GetType().is_central()) { + return nullptr; + } + + return outward_edge_.primary_link()->GetPeerQueueState(); +} + +bool Router::RefreshLocalQueueState() { + const Ref<RouterLink>& outward_link = outward_edge_.primary_link(); + if (!outward_link) { + return false; + } + + auto* state = outward_link->GetLocalQueueState(); + if (!state) { + return false; + } + + const uint64_t num_parcels_consumed = + inbound_parcels_.current_sequence_number().value(); + const uint64_t num_bytes_consumed = + inbound_parcels_.total_consumed_element_size(); + if (last_queue_update_ && + last_queue_update_->num_parcels_consumed == num_parcels_consumed && + last_queue_update_->num_bytes_consumed == num_bytes_consumed) { + // If our current status doesn't differ in some way from the last time we + // updated the local AtomicQueueState, there's nothing to do. + return false; + } + + last_queue_update_ = AtomicQueueState::UpdateValue{ + .num_parcels_consumed = num_parcels_consumed, + .num_bytes_consumed = num_bytes_consumed, + }; + return state->Update(*last_queue_update_); +} + +void Router::UpdateStatusForPeerQueueState( + const AtomicQueueState::QueryResult& state) { + // The consumed amounts should never exceed produced amounts. If they do, + // treat them as zero. + const uint64_t num_parcels_produced = + outbound_parcels_.GetCurrentSequenceLength().value(); + uint64_t num_parcels_consumed = 0; + if (state.num_parcels_consumed.value <= num_parcels_produced) { + num_parcels_consumed = state.num_parcels_consumed.value; + } + + const uint64_t num_bytes_produced = + outbound_parcels_.GetTotalElementSizeQueuedSoFar(); + uint64_t num_bytes_consumed = 0; + if (state.num_bytes_consumed.value <= num_bytes_produced) { + num_bytes_consumed = state.num_bytes_consumed.value; + } + + status_.num_remote_parcels = + saturated_cast<size_t>(num_parcels_produced - num_parcels_consumed); + status_.num_remote_bytes = + saturated_cast<size_t>(num_bytes_produced - num_bytes_consumed); +} + +bool Router::MaybeStartSelfBypass(const OperationContext& context) { Ref<RemoteRouterLink> remote_inward_link; Ref<RemoteRouterLink> remote_outward_link; Ref<Router> local_outward_peer; @@ -1343,7 +1706,7 @@ bool Router::MaybeStartSelfBypass() { << remote_outward_link->Describe(); remote_inward_link->BypassPeer( - remote_outward_link->node_link()->remote_node_name(), + context, remote_outward_link->node_link()->remote_node_name(), remote_outward_link->sublink()); return true; } @@ -1352,22 +1715,24 @@ bool Router::MaybeStartSelfBypass() { // establish the bypass link immediately and send it to the remote inward // peer. return StartSelfBypassToLocalPeer( - *local_outward_peer, *remote_inward_link, + context, *local_outward_peer, *remote_inward_link, remote_inward_link->node_link()->memory().TryAllocateRouterLinkState()); } bool Router::StartSelfBypassToLocalPeer( + const OperationContext& context, Router& local_outward_peer, RemoteRouterLink& inward_link, FragmentRef<RouterLinkState> new_link_state) { if (new_link_state.is_null()) { NodeLinkMemory& memory = inward_link.node_link()->memory(); memory.AllocateRouterLinkState( - [router = WrapRefCounted(this), + [router = WrapRefCounted(this), context, local_outward_peer = WrapRefCounted(&local_outward_peer), inward_link = WrapRefCounted(&inward_link)]( FragmentRef<RouterLinkState> new_link_state) { - router->StartSelfBypassToLocalPeer(*local_outward_peer, *inward_link, + router->StartSelfBypassToLocalPeer(context, *local_outward_peer, + *inward_link, std::move(new_link_state)); }); return true; @@ -1408,25 +1773,26 @@ bool Router::StartSelfBypassToLocalPeer( inward_edge_->set_length_to_decaying_link(length_from_outward_peer); new_link = inward_link.node_link()->AddRemoteRouterLink( - new_sublink, new_link_state, LinkType::kCentral, LinkSide::kA, + context, new_sublink, new_link_state, LinkType::kCentral, LinkSide::kA, WrapRefCounted(&local_outward_peer)); } if (!new_link) { - AcceptRouteDisconnectedFrom(LinkType::kCentral); + AcceptRouteDisconnectedFrom(context, LinkType::kCentral); return false; } // Inform our inward peer on another node that they can bypass us using the // new link we just created to our own outward local peer. Once that message // is sent, it's safe for that local peer to adopt the new link. - inward_link.BypassPeerWithLink(new_sublink, std::move(new_link_state), + inward_link.BypassPeerWithLink(context, new_sublink, + std::move(new_link_state), length_from_outward_peer); - local_outward_peer.SetOutwardLink(std::move(new_link)); + local_outward_peer.SetOutwardLink(context, std::move(new_link)); return true; } -void Router::MaybeStartBridgeBypass() { +void Router::MaybeStartBridgeBypass(const OperationContext& context) { Ref<Router> first_bridge = WrapRefCounted(this); Ref<Router> second_bridge; { @@ -1501,7 +1867,7 @@ void Router::MaybeStartBridgeBypass() { second_bridge->bridge_->BeginPrimaryLinkDecay(); } second_remote_link->BypassPeer( - first_remote_link->node_link()->remote_node_name(), + context, first_remote_link->node_link()->remote_node_name(), first_remote_link->sublink()); return; } @@ -1512,10 +1878,12 @@ void Router::MaybeStartBridgeBypass() { // it's a bit more complex than the cases above and below. if (!second_local_peer) { StartBridgeBypassFromLocalPeer( + context, second_remote_link->node_link()->memory().TryAllocateRouterLinkState()); return; } else if (!first_local_peer) { second_bridge->StartBridgeBypassFromLocalPeer( + context, first_remote_link->node_link()->memory().TryAllocateRouterLinkState()); return; } @@ -1568,13 +1936,14 @@ void Router::MaybeStartBridgeBypass() { second_local_peer->outward_edge_.SetPrimaryLink(std::move(links.second)); } - first_bridge->Flush(); - second_bridge->Flush(); - first_local_peer->Flush(); - second_local_peer->Flush(); + first_bridge->Flush(context); + second_bridge->Flush(context); + first_local_peer->Flush(context); + second_local_peer->Flush(context); } void Router::StartBridgeBypassFromLocalPeer( + const OperationContext& context, FragmentRef<RouterLinkState> link_state) { Ref<Router> local_peer; Ref<Router> other_bridge; @@ -1609,9 +1978,10 @@ void Router::StartBridgeBypassFromLocalPeer( // We need a new RouterLinkState on the remote link before we can complete // this operation. remote_link->node_link()->memory().AllocateRouterLinkState( - [router = WrapRefCounted(this)](FragmentRef<RouterLinkState> state) { + [router = WrapRefCounted(this), + context](FragmentRef<RouterLinkState> state) { if (!state.is_null()) { - router->StartBridgeBypassFromLocalPeer(std::move(state)); + router->StartBridgeBypassFromLocalPeer(context, std::move(state)); } }); return; @@ -1627,7 +1997,8 @@ void Router::StartBridgeBypassFromLocalPeer( const SublinkId bypass_sublink = node_link_to_peer->memory().AllocateSublinkIds(1); Ref<RemoteRouterLink> new_link = node_link_to_peer->AddRemoteRouterLink( - bypass_sublink, link_state, LinkType::kCentral, LinkSide::kA, local_peer); + context, bypass_sublink, link_state, LinkType::kCentral, LinkSide::kA, + local_peer); { MultiMutexLock lock(&mutex_, &other_bridge->mutex_, &local_peer->mutex_); @@ -1653,15 +2024,16 @@ void Router::StartBridgeBypassFromLocalPeer( other_bridge_edge.set_length_from_decaying_link(length_from_local_peer); } - remote_link->BypassPeerWithLink(bypass_sublink, std::move(link_state), - length_from_local_peer); - local_peer->SetOutwardLink(std::move(new_link)); - Flush(); - other_bridge->Flush(); - local_peer->Flush(); + remote_link->BypassPeerWithLink( + context, bypass_sublink, std::move(link_state), length_from_local_peer); + local_peer->SetOutwardLink(context, std::move(new_link)); + Flush(context); + other_bridge->Flush(context); + local_peer->Flush(context); } bool Router::BypassPeerWithNewRemoteLink( + const OperationContext& context, RemoteRouterLink& requestor, NodeLink& node_link, SublinkId bypass_target_sublink, @@ -1671,9 +2043,9 @@ bool Router::BypassPeerWithNewRemoteLink( // RouterLinkState. node_link.memory().AllocateRouterLinkState( [router = WrapRefCounted(this), requestor = WrapRefCounted(&requestor), - node_link = WrapRefCounted(&node_link), + node_link = WrapRefCounted(&node_link), context, bypass_target_sublink](FragmentRef<RouterLinkState> new_link_state) { - router->BypassPeerWithNewRemoteLink(*requestor, *node_link, + router->BypassPeerWithNewRemoteLink(context, *requestor, *node_link, bypass_target_sublink, std::move(new_link_state)); }); @@ -1700,16 +2072,16 @@ bool Router::BypassPeerWithNewRemoteLink( length_to_decaying_link = outbound_parcels_.current_sequence_number(); outward_edge_.set_length_to_decaying_link(length_to_decaying_link); - new_link = node_link.AddRemoteRouterLink(new_sublink, new_link_state, - LinkType::kCentral, LinkSide::kA, - WrapRefCounted(this)); + new_link = node_link.AddRemoteRouterLink( + context, new_sublink, new_link_state, LinkType::kCentral, LinkSide::kA, + WrapRefCounted(this)); } if (!new_link) { // The NodeLink was disconnected before we could create a new link for // this Router. This is not the requestor's fault, so it's not treated as // an error. - AcceptRouteDisconnectedFrom(LinkType::kCentral); + AcceptRouteDisconnectedFrom(context, LinkType::kCentral); return true; } @@ -1729,11 +2101,12 @@ bool Router::BypassPeerWithNewRemoteLink( // above message. Otherwise the router might race on another thread to send // messages via `new_sublink`, and the remote node would have no idea where // to route them. - SetOutwardLink(std::move(new_link)); + SetOutwardLink(context, std::move(new_link)); return true; } -bool Router::BypassPeerWithNewLocalLink(RemoteRouterLink& requestor, +bool Router::BypassPeerWithNewLocalLink(const OperationContext& context, + RemoteRouterLink& requestor, SublinkId bypass_target_sublink) { NodeLink& from_node_link = *requestor.node_link(); const Ref<Router> new_local_peer = @@ -1741,7 +2114,7 @@ bool Router::BypassPeerWithNewLocalLink(RemoteRouterLink& requestor, if (!new_local_peer) { // The peer may have already been destroyed or disconnected from the proxy // by the time we get here. - AcceptRouteDisconnectedFrom(LinkType::kPeripheralOutward); + AcceptRouteDisconnectedFrom(context, LinkType::kPeripheralOutward); return true; } @@ -1789,11 +2162,11 @@ bool Router::BypassPeerWithNewLocalLink(RemoteRouterLink& requestor, new_local_peer->outward_edge_.SetPrimaryLink(std::move(links.second)); } - link_from_new_local_peer_to_proxy->StopProxying(length_from_proxy_to_us, - length_to_proxy_from_us); + link_from_new_local_peer_to_proxy->StopProxying( + context, length_from_proxy_to_us, length_to_proxy_from_us); - Flush(); - new_local_peer->Flush(); + Flush(context); + new_local_peer->Flush(context); return true; } diff --git a/chromium/third_party/ipcz/src/ipcz/router.h b/chromium/third_party/ipcz/src/ipcz/router.h index 092e7a61583..eb43fb5f076 100644 --- a/chromium/third_party/ipcz/src/ipcz/router.h +++ b/chromium/third_party/ipcz/src/ipcz/router.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,6 +10,7 @@ #include "ipcz/fragment_ref.h" #include "ipcz/ipcz.h" +#include "ipcz/operation_context.h" #include "ipcz/parcel_queue.h" #include "ipcz/route_edge.h" #include "ipcz/router_descriptor.h" @@ -23,6 +24,7 @@ namespace ipcz { +class AtomicQueueState; class NodeLink; class RemoteRouterLink; struct RouterLinkState; @@ -104,7 +106,8 @@ class Router : public RefCounted { // in active use by another Router, as `this` Router may already be in a // transitional state and must be able to block decay around `link` from // within this call. - void SetOutwardLink(Ref<RouterLink> link); + void SetOutwardLink(const OperationContext& context, + const Ref<RouterLink> link); // Returns a best-effort estimation of the maximum parcel size (in bytes) that // can be sent outward from this router without the receiving portal exceeding @@ -117,23 +120,27 @@ class Router : public RefCounted { size_t GetInboundCapacityInBytes(const IpczPutLimits& limits); // Accepts an inbound parcel from the outward edge of this router, either to - // queue it for retrieval or forward it further inward. - bool AcceptInboundParcel(Parcel& parcel); + // queue it for retrieval or forward it further inward. `source` indicates + // whether the parcel is arriving as a direct result of some local ipcz API + // call, or if it came from a remote node. + bool AcceptInboundParcel(const OperationContext& context, Parcel& parcel); // Accepts an outbound parcel here from some other Router. The parcel is // transmitted immediately or queued for later transmission over the Router's // outward link. Called only on proxying Routers. - bool AcceptOutboundParcel(Parcel& parcel); + bool AcceptOutboundParcel(const OperationContext& context, Parcel& parcel); // Accepts notification that the other end of the route has been closed and // that the closed end transmitted a total of `sequence_length` parcels before - // closing. - bool AcceptRouteClosureFrom(LinkType link_type, + // closing. `source` indicates whether the portal's peer was closed locally, + // or if we were notified of its closure from a remote node. + bool AcceptRouteClosureFrom(const OperationContext& context, + LinkType link_type, SequenceNumber sequence_length); - // Informs this router that its outward peer consumed some inbound parcels or - // parcel data. - void NotifyPeerConsumedData(); + // Queries the remote peer's queue state and performs any local state upates + // needed to reflect it. `source` indicates why the snapshot is being taken. + void SnapshotPeerQueueState(const OperationContext& context); // Accepts notification from a link bound to this Router that some node along // the route (in the direction of that link) has been disconnected, e.g. due @@ -142,14 +149,16 @@ class Router : public RefCounted { // deliver the complete sequence of parcels transmitted from that end of the // route. `link_type` specifies the type of link which is propagating the // notification to this rouer. - bool AcceptRouteDisconnectedFrom(LinkType link_type); + bool AcceptRouteDisconnectedFrom(const OperationContext& context, + LinkType link_type); // Retrieves the next available inbound parcel from this Router, if present. IpczResult GetNextInboundParcel(IpczGetFlags flags, void* data, size_t* num_bytes, IpczHandle* handles, - size_t* num_handles); + size_t* num_handles, + IpczHandle* validator); // Begins a two-phase retrieval of the next available inbound parcel. IpczResult BeginGetNextIncomingParcel(const void** data, @@ -160,7 +169,8 @@ class Router : public RefCounted { // consuming some (possibly all) bytes and handles from that parcel. Once a // parcel is fully consumed, it's removed from the inbound queue. IpczResult CommitGetNextIncomingParcel(size_t num_data_bytes_consumed, - absl::Span<IpczHandle> handles); + absl::Span<IpczHandle> handles, + IpczHandle* validator); // Attempts to install a new trap on this Router, to invoke `handler` as soon // as one or more conditions in `conditions` is met. This method effectively @@ -184,12 +194,15 @@ class Router : public RefCounted { // Serializes a description of a new Router which will be used to extend this // Router's route across `to_node_link` by introducing a new Router on the // remote node. - void SerializeNewRouter(NodeLink& to_node_link, RouterDescriptor& descriptor); + void SerializeNewRouter(const OperationContext& context, + NodeLink& to_node_link, + RouterDescriptor& descriptor); // Configures this Router to begin proxying incoming parcels toward (and // outgoing parcels from) the Router described by `descriptor`, living on the // remote node of `to_node_link`. - void BeginProxyingToNewRouter(NodeLink& to_node_link, + void BeginProxyingToNewRouter(const OperationContext& context, + NodeLink& to_node_link, const RouterDescriptor& descriptor); // Notifies this router that it should reach out to its outward peer's own @@ -218,7 +231,8 @@ class Router : public RefCounted { // invalid. Note that a return value of true does not necessarily imply that // bypass was or will be successful (e.g. it may silently fail due to lost // node connections). - bool BypassPeer(RemoteRouterLink& requestor, + bool BypassPeer(const OperationContext& context, + RemoteRouterLink& requestor, const NodeName& bypass_target_node, SublinkId bypass_target_sublink); @@ -242,6 +256,7 @@ class Router : public RefCounted { // RouterLinkState's `allowed_bypass_request_source` field. This method // authenticates the request accordingly. bool AcceptBypassLink( + const OperationContext& context, NodeLink& new_node_link, SublinkId new_sublink, FragmentRef<RouterLinkState> new_link_state, @@ -254,7 +269,8 @@ class Router : public RefCounted { // // Returns true if and only if this router is a proxy with decaying inward and // outward links. Otherwise returns false, indicating an invalid request. - bool StopProxying(SequenceNumber inbound_sequence_length, + bool StopProxying(const OperationContext& context, + SequenceNumber inbound_sequence_length, SequenceNumber outbound_sequence_length); // Configures the final length of the inbound parcel sequence coming from the @@ -265,7 +281,8 @@ class Router : public RefCounted { // Returns true if this router has a decaying outward link -- implying that // its outward peer is a proxy -- or the router has been disconnected. // Otherwise the request is invalid and this returns false. - bool NotifyProxyWillStop(SequenceNumber inbound_sequence_length); + bool NotifyProxyWillStop(const OperationContext& context, + SequenceNumber inbound_sequence_length); // Configures the final sequence length of outbound parcels to expect on this // proxying Router's decaying inward link. Once this is set and the decaying @@ -273,7 +290,8 @@ class Router : public RefCounted { // // Returns true if the request is valid, meaning that this Router is a proxy // whose outward peer is local to the same node. Otherwise this returns false. - bool StopProxyingToLocalPeer(SequenceNumber outbound_sequence_length); + bool StopProxyingToLocalPeer(const OperationContext& context, + SequenceNumber outbound_sequence_length); // Notifies this Router that one of its links has been disconnected from a // remote node. The link is identified by a combination of a specific NodeLink @@ -287,7 +305,8 @@ class Router : public RefCounted { // For a proxying router which is generally only kept alive by the links // which are bound to it, this call will typically be followed by imminent // destruction of this Router once the caller releases its own reference. - void NotifyLinkDisconnected(RemoteRouterLink& link); + void NotifyLinkDisconnected(const OperationContext& context, + RemoteRouterLink& link); // Flushes any inbound or outbound parcels, as well as any route closure // notifications. RouterLinks which are no longer needed for the operation of @@ -309,12 +328,32 @@ class Router : public RefCounted { // invoke Flush() may also elicit state changes that can unblock a bypass // operation. These operatoins may specify kForceProxyBypassAttempt in such // cases. + // + // `source` indicates why the flush is occurring. enum FlushBehavior { kDefault, kForceProxyBypassAttempt }; - void Flush(FlushBehavior behavior = kDefault); + void Flush(const OperationContext& context, + FlushBehavior behavior = kDefault); private: ~Router() override; + // Returns a handle to the outward peer's queue state, if available. Otherwise + // returns null. + AtomicQueueState* GetPeerQueueState() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Updates the AtomicQueueState shared with this Router's outward peer, based + // on the current portal status. Any monitor bit set by the remote peer is + // reset, and this returns the value of that bit prior to the reset. If this + // returns true, the caller is responsible for notifying the remote peer about + // a state change. + [[nodiscard]] bool RefreshLocalQueueState() + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Updates this Router's status to reflect how many parcels and total bytes of + // parcel data remain on the remote peer's inbound queue. + void UpdateStatusForPeerQueueState(const AtomicQueueState::QueryResult& state) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + // Attempts to initiate bypass of this router by its peers, and ultimately to // remove this router from its route. // @@ -322,7 +361,7 @@ class Router : public RefCounted { // last decaying link, or if Flush() was called with kForceProxyBypassAttempt, // indicating that some significant state has changed on the route which might // unblock our bypass. - bool MaybeStartSelfBypass(); + bool MaybeStartSelfBypass(const OperationContext& context); // Starts bypass of this Router when its outward peer lives on the same node. // This must only be called once the central link is already locked. If @@ -333,7 +372,8 @@ class Router : public RefCounted { // Returns true if and only if self-bypass has been initiated by reaching out // to this router's inward peer with with a BypassPeer() or // BypassPeerWithLink() request. Otherwise returns false. - bool StartSelfBypassToLocalPeer(Router& local_outward_peer, + bool StartSelfBypassToLocalPeer(const OperationContext& context, + Router& local_outward_peer, RemoteRouterLink& inward_link, FragmentRef<RouterLinkState> new_link_state); @@ -342,7 +382,7 @@ class Router : public RefCounted { // other side. This method will attempt to lock this Router's outward link as // well as the outward link of this Router's bridge peer. If either fails, // both are left unlocked and this operation cannot yet proceed. - void MaybeStartBridgeBypass(); + void MaybeStartBridgeBypass(const OperationContext& context); // Starts bypass of this Router, which must be on a bridge link and must have // a local outward peer link. The router on the other side of the bridge must @@ -350,7 +390,8 @@ class Router : public RefCounted { // establish a new remote link to that peer to bypass the entire bridge. If // `link_state` is null, the operation will be deferred until a fragment can // be allocated. - void StartBridgeBypassFromLocalPeer(FragmentRef<RouterLinkState> link_state); + void StartBridgeBypassFromLocalPeer(const OperationContext& context, + FragmentRef<RouterLinkState> link_state); // Attempts to bypass the link identified by `requestor` in favor of a new // link that runs over `node_link`. If `new_link_state` is non-null, it will @@ -358,7 +399,8 @@ class Router : public RefCounted { // will be allocated asynchronously before proceeding. // // Returns true if and only if this request was valid. - bool BypassPeerWithNewRemoteLink(RemoteRouterLink& requestor, + bool BypassPeerWithNewRemoteLink(const OperationContext& context, + RemoteRouterLink& requestor, NodeLink& node_link, SublinkId bypass_target_sublink, FragmentRef<RouterLinkState> new_link_state); @@ -368,15 +410,42 @@ class Router : public RefCounted { // NodeLink as `requestor`. // // Returns true if and only if this request was valid. - bool BypassPeerWithNewLocalLink(RemoteRouterLink& requestor, + bool BypassPeerWithNewLocalLink(const OperationContext& context, + RemoteRouterLink& requestor, SublinkId bypass_target_sublink); + // Optimized Router serialization case when the Router's peer is local to the + // same node and the existing (local) central link can be replaced with a new + // remote link, without establishing an intermediate proxy. Returns true on + // success, or false indicating that the caller must fall back onto the slower + // Router serialization path defined below. + bool SerializeNewRouterWithLocalPeer(const OperationContext& context, + NodeLink& to_node_link, + RouterDescriptor& descriptor, + Ref<Router> local_peer); + + // Default Router serialization case when the serializing Router must stay + // behind as an intermediate proxy between its (remote) peer and the newly + // established Router that will result from this serialization. As an + // optimization, `initiate_proxy_bypass` may be true if the serializing router + // is on the central link and was able to lock that link for bypass prior to + // serialization. + void SerializeNewRouterAndConfigureProxy(const OperationContext& context, + NodeLink& to_node_link, + RouterDescriptor& descriptor, + bool initiate_proxy_bypass); + absl::Mutex mutex_; // The current computed portal status to be reflected by a portal controlling // this router, iff this is a terminal router. IpczPortalStatus status_ ABSL_GUARDED_BY(mutex_) = {sizeof(status_)}; + // A local cache of the most recently stored value for our own local + // AtomicQueueState. + absl::optional<AtomicQueueState::UpdateValue> last_queue_update_ + ABSL_GUARDED_BY(mutex_); + // A set of traps installed via a controlling portal where applicable. These // traps are notified about any interesting state changes within the router. TrapSet traps_ ABSL_GUARDED_BY(mutex_); diff --git a/chromium/third_party/ipcz/src/ipcz/router_descriptor.cc b/chromium/third_party/ipcz/src/ipcz/router_descriptor.cc index 3e0676a8674..4dbfbe39d2c 100644 --- a/chromium/third_party/ipcz/src/ipcz/router_descriptor.cc +++ b/chromium/third_party/ipcz/src/ipcz/router_descriptor.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/router_descriptor.h b/chromium/third_party/ipcz/src/ipcz/router_descriptor.h index 2e472ed2443..9cb539b0d57 100644 --- a/chromium/third_party/ipcz/src/ipcz/router_descriptor.h +++ b/chromium/third_party/ipcz/src/ipcz/router_descriptor.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -31,18 +31,45 @@ struct IPCZ_ALIGN(8) RouterDescriptor { // end. SequenceNumber closed_peer_sequence_length; - // A new sublink and RouterLinkState fragment allocated by the sender on the - // NodeLink which sends this descriptor. The sublink is used as a peripheral - // link, inward to (and outward from) the new router. + // A new sublink allocated by the sender on the NodeLink which sends this + // descriptor. The sublink may be used as a peripheral link, inward to (and + // outward from) the new router, or it may be used the route's central link + // if and only if `proxy_already_bypassed` is true below. In the latter case, + // `new_link_state_fragment` must be valid and is used as the new central + // link's RouterLinkState. SublinkId new_sublink; + FragmentDescriptor new_link_state_fragment; + + // When `proxy_already_bypassed` is true below, this is another new sublink + // allocated by the sender on the NodeLink which sends this descriptor. This + // sublink is used as peripheral link to the new router's outward -- peer back + // on the sending node -- as a way for that router to forward any inbound + // parcels that were still queued or in flight when this router was + // serialized. + SublinkId new_decaying_sublink; // The SequenceNumber of the next outbound parcel which can be produced by // this router. SequenceNumber next_outgoing_sequence_number; + // The total number of outgoing bytes produced by the router's portal so far. + uint64_t num_bytes_produced; + // The SequenceNumber of the next inbound parcel expected by this router. SequenceNumber next_incoming_sequence_number; + // The total length of the sequence of parcels expected on the decaying link + // established by `new_decaying_sublink`, if and only if + // `proxy_already_bypassed` is true. The decaying link is expected to receive + // only parcels between `next_incoming_sequence_number` (inclusive) and + // `decaying_incoming_sequence_length` (exclusive). If those fields are equal + // then the decaying link should be ignored and `new_decaying_sublink` may + // not be valid. + SequenceNumber decaying_incoming_sequence_length; + + // The total number of incoming bytes consumed from router's portal so far. + uint64_t num_bytes_consumed; + // Indicates that the other end of the route is already known to be closed. // In this case sending any new outbound parcels from this router would be // pointless, but there may still be in-flight parcels to receive from the @@ -50,6 +77,27 @@ struct IPCZ_ALIGN(8) RouterDescriptor { // parcels sent from that end, and `next_incoming_sequence_number` can be used // to determine whether there are any parcels left to receive. bool peer_closed : 1; + + // Indicates that, as an optimization, the sender was able to circumvent the + // usual process of first establishing a peripheral link and then initiating + // proxy bypass. Instead the outward peer of this new router is already + // configured to route messages directly to the new router, and its former + // (and local) outward peer is configured to proxy any previously queued or + // in-flight messages to us over the decaying link described above. + bool proxy_already_bypassed : 1; + + // Reserved padding out to the next 8-byte boundary. + uint8_t reserved0[7]; + + // These fields are set if and only if proxy bypass should be initiated + // immediately on deserialization of the new Router. The deserializing node + // must contact `proxy_peer_node_name` with the name of the node who sent this + // descriptor, along with `proxy_peer_sublink` (an existing sublink + // between those two nodes, identifying the link we want to bypass). These + // fields may be set as an optimization to avoid additional messaging overhead + // in the common case of transferring a yet-unused portal. + NodeName proxy_peer_node_name; + SublinkId proxy_peer_sublink; }; } // namespace ipcz diff --git a/chromium/third_party/ipcz/src/ipcz/router_link.h b/chromium/third_party/ipcz/src/ipcz/router_link.h index fb5ae27486c..a8031b3a2c0 100644 --- a/chromium/third_party/ipcz/src/ipcz/router_link.h +++ b/chromium/third_party/ipcz/src/ipcz/router_link.h @@ -1,13 +1,20 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef IPCZ_SRC_IPCZ_ROUTER_LINK_H_ #define IPCZ_SRC_IPCZ_ROUTER_LINK_H_ +#include <cstddef> +#include <functional> +#include <string> +#include <utility> + +#include "ipcz/atomic_queue_state.h" #include "ipcz/fragment_ref.h" #include "ipcz/link_type.h" #include "ipcz/node_name.h" +#include "ipcz/operation_context.h" #include "ipcz/router_link_state.h" #include "ipcz/sequence_number.h" #include "ipcz/sublink_id.h" @@ -39,6 +46,10 @@ class RouterLink : public RefCounted { // returns null. virtual RouterLinkState* GetLinkState() const = 0; + // Runs `callback` as soon as this RouterLink has a RouterLinkState. If the + // link already has a RouterLinkState then `callback` is invoked immediately. + virtual void WaitForLinkStateAsync(std::function<void()> callback) = 0; + // Returns the Router on the other end of this link, if this is a // LocalRouterLink. Otherwise returns null. virtual Ref<Router> GetLocalPeer() = 0; @@ -59,43 +70,33 @@ class RouterLink : public RefCounted { // Passes a parcel to the Router on the other side of this link to be queued // and/or router further. - virtual void AcceptParcel(Parcel& parcel) = 0; + virtual void AcceptParcel(const OperationContext& context, + Parcel& parcel) = 0; // Notifies the Router on the other side of the link that the route has been // closed from this side. `sequence_length` is the total number of parcels // transmitted from the closed side before it was closed. - virtual void AcceptRouteClosure(SequenceNumber sequence_length) = 0; + virtual void AcceptRouteClosure(const OperationContext& context, + SequenceNumber sequence_length) = 0; // Notifies the Router on the other side of the link that the route has been // unexpectedly disconnected from this side. Unlike clean route closure above, // in this case we don't know the final sequence length and can't guarantee // delivery of any further parcels. - virtual void AcceptRouteDisconnected() = 0; - - // Returns a best-effort estimation of how much new parcel data can be - // transmitted across the link before one or more limits described by `limits` - // would be exceeded on the receiving portal. - virtual size_t GetParcelCapacityInBytes(const IpczPutLimits& limits) = 0; + virtual void AcceptRouteDisconnected(const OperationContext& context) = 0; - // Returns a best-effort snapshot of the last known state of the inbound - // parcel queue on the other side of this link. This is only meaningful for - // central links. - virtual RouterLinkState::QueueState GetPeerQueueState() = 0; - - // Updates the QueueState for this side of the link, returning true if and - // only if the other side wants to be notified about the update. - virtual bool UpdateInboundQueueState(size_t num_parcels, - size_t num_bytes) = 0; + // Returns the AtomicQueueState for the other side of this link if available. + // Otherwise returns null. + virtual AtomicQueueState* GetPeerQueueState() = 0; - // Notifies the other side that this side has consumed some parcels or parcel - // data from its inbound queue. Should only be called on central links when - // the other side has expressed interest in such notifications. - virtual void NotifyDataConsumed() = 0; + // Returns the AtomicQueueState for this side of the link if available. + // Otherwise returns null. + virtual AtomicQueueState* GetLocalQueueState() = 0; - // Controls whether the caller's side of the link is interested in being - // notified about data consumption on the opposite side of the link. Returns - // the previous value of this bit. - virtual bool EnablePeerMonitoring(bool enable) = 0; + // Notifies the other side that this side has updated its visible queue state + // in some way which may be interesting to them. This should be called + // sparingly to avoid redundant IPC traffic and redundant idle wakes. + virtual void SnapshotPeerQueueState(const OperationContext& context) = 0; // Signals that this side of the link is in a stable state suitable for one // side or the other to lock the link, either for bypass or closure @@ -130,7 +131,7 @@ class RouterLink : public RefCounted { // itself as waiting for both sides of the link to become stable, and both // sides of the link are stable. Returns true if and only if a flush was // actually issued to the other side. - virtual bool FlushOtherSideIfWaiting() = 0; + virtual bool FlushOtherSideIfWaiting(const OperationContext& context) = 0; // Indicates whether this link can be bypassed by a request from the named // node to one side of the link. True if and only if the proxy on the other @@ -142,7 +143,8 @@ class RouterLink : public RefCounted { // on this side. `bypass_target_node` is the name node where the router's // outward peer lives, and `bypass_target_sublink` identifies the link between // that router and the router on the other side of this link. - virtual void BypassPeer(const NodeName& bypass_target_node, + virtual void BypassPeer(const OperationContext& context, + const NodeName& bypass_target_node, SublinkId bypass_target_sublink) = 0; // Informs the router on the other side of this link about when it can drop @@ -152,14 +154,16 @@ class RouterLink : public RefCounted { // `outbound_sequence_length` is the final length of the parcel sequence the // router must expect to receive from its inward peer and forward to its // outward peer. - virtual void StopProxying(SequenceNumber inbound_sequence_length, + virtual void StopProxying(const OperationContext& context, + SequenceNumber inbound_sequence_length, SequenceNumber outbound_sequence_length) = 0; // Informs the router on the other side of this link that the router it most // recently bypassed will stop sending it parcels once the router's inbound // sequence length reaches `inbound_sequence_length`, at which point the // router's link to the proxy can be dropped. - virtual void ProxyWillStop(SequenceNumber inbound_sequence_length) = 0; + virtual void ProxyWillStop(const OperationContext& context, + SequenceNumber inbound_sequence_length) = 0; // Informs the router on the other side of this link that its outward peer // (and the router on this side of this link) can be bypassed, and provides a @@ -167,7 +171,8 @@ class RouterLink : public RefCounted { // `new_link_state` is a freshly allocated RouterLinkState fragment for the // new link, and `inbound_sequence_length` is the current inbound sequence // length of the router on this side of the link. - virtual void BypassPeerWithLink(SublinkId new_sublink, + virtual void BypassPeerWithLink(const OperationContext& context, + SublinkId new_sublink, FragmentRef<RouterLinkState> new_link_state, SequenceNumber inbound_sequence_length) = 0; @@ -179,6 +184,7 @@ class RouterLink : public RefCounted { // router on this side of the link at the moment it switches to the new link // for its outward transmissions. virtual void StopProxyingToLocalPeer( + const OperationContext& context, SequenceNumber outbound_sequence_length) = 0; // Deactivates this RouterLink to sever any binding it may have to a specific diff --git a/chromium/third_party/ipcz/src/ipcz/router_link_state.cc b/chromium/third_party/ipcz/src/ipcz/router_link_state.cc index 70e842dc0c8..441b6d4720c 100644 --- a/chromium/third_party/ipcz/src/ipcz/router_link_state.cc +++ b/chromium/third_party/ipcz/src/ipcz/router_link_state.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -125,38 +125,8 @@ bool RouterLinkState::ResetWaitingBit(LinkSide side) { return true; } -RouterLinkState::QueueState RouterLinkState::GetQueueState( - LinkSide side) const { - return { - .num_parcels = SelectBySide(side, num_parcels_on_a, num_parcels_on_b) - .load(std::memory_order_relaxed), - .num_bytes = SelectBySide(side, num_bytes_on_a, num_bytes_on_b) - .load(std::memory_order_relaxed), - }; -} - -bool RouterLinkState::UpdateQueueState(LinkSide side, - size_t num_parcels, - size_t num_bytes) { - StoreSaturated(SelectBySide(side, num_parcels_on_a, num_parcels_on_b), - num_parcels); - StoreSaturated(SelectBySide(side, num_bytes_on_a, num_bytes_on_b), num_bytes); - const uint32_t other_side_monitoring_this_side = - SelectBySide(side, kSideBMonitoringSideA, kSideAMonitoringSideB); - return (status.load(std::memory_order_relaxed) & - other_side_monitoring_this_side) != 0; -} - -bool RouterLinkState::SetSideIsMonitoringPeer(LinkSide side, - bool is_monitoring) { - const uint32_t monitoring_bit = - SelectBySide(side, kSideAMonitoringSideB, kSideBMonitoringSideA); - uint32_t expected = kStable; - while (!status.compare_exchange_weak(expected, expected | monitoring_bit, - std::memory_order_relaxed, - std::memory_order_relaxed)) { - } - return (expected & monitoring_bit) != 0; +AtomicQueueState& RouterLinkState::GetQueueState(LinkSide side) { + return SelectBySide(side, side_a_queue_state, side_b_queue_state); } } // namespace ipcz diff --git a/chromium/third_party/ipcz/src/ipcz/router_link_state.h b/chromium/third_party/ipcz/src/ipcz/router_link_state.h index 0d1ae54e1b0..b6336887737 100644 --- a/chromium/third_party/ipcz/src/ipcz/router_link_state.h +++ b/chromium/third_party/ipcz/src/ipcz/router_link_state.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -9,6 +9,7 @@ #include <cstdint> #include <type_traits> +#include "ipcz/atomic_queue_state.h" #include "ipcz/ipcz.h" #include "ipcz/link_side.h" #include "ipcz/node_name.h" @@ -62,13 +63,6 @@ struct IPCZ_ALIGN(8) RouterLinkState : public RefCountedFragment { static constexpr Status kLockedBySideA = 1 << 4; static constexpr Status kLockedBySideB = 1 << 5; - // Set if the link on either side A or B wishes to be notified when parcels - // or parcel data are consumed by the other side. In practice these are only - // set when a router has a trap installed to monitor such conditions, which - // applications may leverage to e.g. implement a back-pressure mechanism. - static constexpr Status kSideAMonitoringSideB = 1 << 6; - static constexpr Status kSideBMonitoringSideA = 1 << 7; - std::atomic<Status> status{kUnstable}; // In a situation with three routers A-B-C and a central link between A and @@ -78,16 +72,14 @@ struct IPCZ_ALIGN(8) RouterLinkState : public RefCountedFragment { // validate that C is an appropriate source of such a bypass request. NodeName allowed_bypass_request_source; - // These fields approximate the number of parcels and data bytes received and - // queued for retrieval on each side of this link. Values here are saturated - // if the actual values would exceed the max uint32_t value. - std::atomic<uint32_t> num_parcels_on_a{0}; - std::atomic<uint32_t> num_bytes_on_a{0}; - std::atomic<uint32_t> num_parcels_on_b{0}; - std::atomic<uint32_t> num_bytes_on_b{0}; + // An approximation of the queue state on each side of the link. These are + // used both for best-effort querying of remote conditions as well as for + // reliable synchronization against remote activity. + AtomicQueueState side_a_queue_state; + AtomicQueueState side_b_queue_state; // More reserved slots, padding out this structure to 64 bytes. - uint32_t reserved1[6] = {0}; + uint32_t reserved1[2] = {0}; bool is_locked_by(LinkSide side) const { Status s = status.load(std::memory_order_relaxed); @@ -124,24 +116,9 @@ struct IPCZ_ALIGN(8) RouterLinkState : public RefCountedFragment { // still unstable. bool ResetWaitingBit(LinkSide side); - // Returns a snapshot of the inbound parcel queue state on the given side of + // Returns a view of the inbound parcel queue state for the given `side` of // this link. - struct QueueState { - uint32_t num_parcels; - uint32_t num_bytes; - }; - QueueState GetQueueState(LinkSide side) const; - - // Updates the queue state for the given side of this link. Values which - // exceed 2**32-1 are clamped to that value. Returns true if and only if the - // opposite side of the link wants to be notified about this update. - bool UpdateQueueState(LinkSide side, size_t num_parcels, size_t num_bytes); - - // Sets an appropriate bit to indicate whether the router on the given side of - // this link should notify the opposite side after consuming inbound parcels - // or parcel data. Returns the previous value of the relevant bit, which may - // be the same as the old value. - bool SetSideIsMonitoringPeer(LinkSide side, bool is_monitoring); + AtomicQueueState& GetQueueState(LinkSide side); }; // The size of this structure is fixed at 64 bytes to ensure that it fits the diff --git a/chromium/third_party/ipcz/src/ipcz/router_link_test.cc b/chromium/third_party/ipcz/src/ipcz/router_link_test.cc index a90dffea75d..36c23ffa78e 100644 --- a/chromium/third_party/ipcz/src/ipcz/router_link_test.cc +++ b/chromium/third_party/ipcz/src/ipcz/router_link_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -17,6 +17,7 @@ #include "ipcz/node.h" #include "ipcz/node_link.h" #include "ipcz/node_name.h" +#include "ipcz/operation_context.h" #include "ipcz/remote_router_link.h" #include "ipcz/router.h" #include "ipcz/router_link_state.h" @@ -58,8 +59,9 @@ class TestNodePair { node_b_, LinkSide::kB, kTestNonBrokerName, kTestBrokerName, Node::Type::kBroker, 0, transports.second, NodeLinkMemory::Create(node_b_, buffer.memory.Map())); - node_a_->AddLink(kTestNonBrokerName, node_link_a_); - node_b_->AddLink(kTestBrokerName, node_link_b_); + node_a_->AddConnection(kTestNonBrokerName, {.link = node_link_a_}); + node_b_->AddConnection(kTestBrokerName, + {.link = node_link_b_, .broker = node_link_b_}); } ~TestNodePair() { @@ -84,13 +86,17 @@ class TestNodePair { FragmentRef<RouterLinkState> a_state, Ref<Router> b, FragmentRef<RouterLinkState> b_state) { + // The choice of OperationContext is arbitrary and irrelevant for this test. + const OperationContext context{OperationContext::kTransportNotification}; const SublinkId sublink = node_link_a_->memory().AllocateSublinkIds(1); - Ref<RemoteRouterLink> a_link = node_link_a_->AddRemoteRouterLink( - sublink, std::move(a_state), LinkType::kCentral, LinkSide::kA, a); - Ref<RemoteRouterLink> b_link = node_link_b_->AddRemoteRouterLink( - sublink, std::move(b_state), LinkType::kCentral, LinkSide::kB, b); - a->SetOutwardLink(a_link); - b->SetOutwardLink(b_link); + Ref<RemoteRouterLink> a_link = + node_link_a_->AddRemoteRouterLink(context, sublink, std::move(a_state), + LinkType::kCentral, LinkSide::kA, a); + Ref<RemoteRouterLink> b_link = + node_link_b_->AddRemoteRouterLink(context, sublink, std::move(b_state), + LinkType::kCentral, LinkSide::kB, b); + a->SetOutwardLink(context, a_link); + b->SetOutwardLink(context, b_link); return {a_link, b_link}; } @@ -125,12 +131,14 @@ class RouterLinkTest : public testing::Test, public testing::WithParamInterface<RouterLinkTestMode> { public: void SetUp() override { + // The choice of OperationContext is arbitrary and irrelevant for this test. + const OperationContext context{OperationContext::kTransportNotification}; switch (GetParam()) { case RouterLinkTestMode::kLocal: std::tie(a_link_, b_link_) = LocalRouterLink::CreatePair(LinkType::kCentral, {a_, b_}); - a_->SetOutwardLink(a_link_); - b_->SetOutwardLink(b_link_); + a_->SetOutwardLink(context, a_link_); + b_->SetOutwardLink(context, b_link_); break; case RouterLinkTestMode::kRemote: { @@ -222,9 +230,11 @@ TEST_P(RouterLinkTest, FlushOtherSideIfWaiting) { link_state().status = RouterLinkState::kUnstable; // FlushOtherSideIfWaiting() does nothing if the other side is not, in fact, - // waiting for something. - EXPECT_FALSE(a_link().FlushOtherSideIfWaiting()); - EXPECT_FALSE(b_link().FlushOtherSideIfWaiting()); + // waiting for something. The choice of OperationContext is arbitrary and + // irrelevant for this test. + const OperationContext context{OperationContext::kTransportNotification}; + EXPECT_FALSE(a_link().FlushOtherSideIfWaiting(context)); + EXPECT_FALSE(b_link().FlushOtherSideIfWaiting(context)); EXPECT_EQ(RouterLinkState::kUnstable, link_status()); // Mark B stable and try to lock the link. Since A is not yet stable, this @@ -239,7 +249,7 @@ TEST_P(RouterLinkTest, FlushOtherSideIfWaiting) { a_link().MarkSideStable(); EXPECT_EQ(RouterLinkState::kStable | RouterLinkState::kSideBWaiting, link_status()); - EXPECT_TRUE(a_link().FlushOtherSideIfWaiting()); + EXPECT_TRUE(a_link().FlushOtherSideIfWaiting(context)); EXPECT_EQ(RouterLinkState::kStable, link_status()); } diff --git a/chromium/third_party/ipcz/src/ipcz/sequence_number.h b/chromium/third_party/ipcz/src/ipcz/sequence_number.h index 7400faf1a56..189049cc2b2 100644 --- a/chromium/third_party/ipcz/src/ipcz/sequence_number.h +++ b/chromium/third_party/ipcz/src/ipcz/sequence_number.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/sequenced_queue.h b/chromium/third_party/ipcz/src/ipcz/sequenced_queue.h index c2de7f75cd5..860e0887063 100644 --- a/chromium/third_party/ipcz/src/ipcz/sequenced_queue.h +++ b/chromium/third_party/ipcz/src/ipcz/sequenced_queue.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,11 +6,13 @@ #define IPCZ_SRC_IPCZ_SEQUENCED_QUEUE_H_ #include <cstddef> +#include <cstdint> #include <vector> #include "ipcz/sequence_number.h" #include "third_party/abseil-cpp/absl/container/inlined_vector.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#include "util/safe_math.h" namespace ipcz { @@ -90,6 +92,11 @@ class SequencedQueue { return base_sequence_number_; } + // The total size of all element from this queue so far. + uint64_t total_consumed_element_size() const { + return total_consumed_element_size_; + } + // The final length of the sequence that can be popped from this queue. Null // if a final length has not yet been set. If the final length is N, then the // last ordered element that can be pushed to or popped from the queue has a @@ -124,6 +131,13 @@ class SequencedQueue { return entries_[0]->total_span_size; } + // Returns the total size of all elements previously popped from this queue, + // plus the total size of all elemenets currently ready for popping. + uint64_t GetTotalElementSizeQueuedSoFar() const { + return CheckAdd(total_consumed_element_size_, + static_cast<uint64_t>(GetTotalAvailableElementSize())); + } + // Returns the total length of the contiguous sequence already pushed and/or // popped from this queue so far. This is essentially // `current_sequence_number()` plus `GetNumAvailableElements()`. If @@ -213,24 +227,29 @@ class SequencedQueue { return !HasNextElement() && !ExpectsMoreElements(); } - // Resets this queue to start at the initial SequenceNumber `n`. Must be - // called only on an empty queue and only when the caller can be sure they - // won't want to push any elements with a SequenceNumber below `n`. - void ResetInitialSequenceNumber(SequenceNumber n) { + // Resets this queue to a state which behaves as if a sequence of parcels of + // length `n` has already been pushed and popped from the queue, with a total + // cumulative element size of `total_consumed_element_size`. Must be called + // only on an empty queue and only when the caller can be sure they won't want + // to push any elements with a SequenceNumber below `n`. + void ResetSequence(SequenceNumber n, uint64_t total_consumed_element_size) { ABSL_ASSERT(num_entries_ == 0); base_sequence_number_ = n; + total_consumed_element_size_ = total_consumed_element_size; } // Attempts to skip SequenceNumber `n` in the sequence by advancing the // current SequenceNumber by one. Returns true on success and false on - // failure. + // failure. `element_size` is the size of the skipped element as it would have + // been reported by ElementTraits::GetElementSize() if the element in question + // were actually pushed into the queue. // // This can only succeed when `current_sequence_number()` is equal to `n`, no - // entry for SequenceNumber `n` is already in the queue, and the `n` is less + // entry for SequenceNumber `n` is already in the queue, and n` is less than // the final sequence length if applicable. Success is equivalent to pushing // and immediately popping element `n` except that it does not grow, shrink, // or otherwise modify the queue's underlying storage. - bool MaybeSkipSequenceNumber(SequenceNumber n) { + bool SkipElement(SequenceNumber n, size_t element_size) { if (base_sequence_number_ != n || HasNextElement() || (final_sequence_length_ && *final_sequence_length_ <= n)) { return false; @@ -240,6 +259,8 @@ class SequencedQueue { if (num_entries_ != 0) { entries_.remove_prefix(1); } + total_consumed_element_size_ = CheckAdd( + total_consumed_element_size_, static_cast<uint64_t>(element_size)); return true; } @@ -307,13 +328,13 @@ class SequencedQueue { base_sequence_number_ = SequenceNumber{base_sequence_number_.value() + 1}; // Make sure the next queued entry has up-to-date accounting, if present. + const size_t element_size = ElementTraits::GetElementSize(element); if (entries_.size() > 1 && entries_[1]) { Entry& next = *entries_[1]; next.span_start = head.span_start; next.span_end = head.span_end; next.num_entries_in_span = head.num_entries_in_span - 1; - next.total_span_size = - head.total_span_size - ElementTraits::GetElementSize(element); + next.total_span_size = head.total_span_size - element_size; size_t tail_index = next.span_end.value() - sequence_number.value(); if (tail_index > 1) { @@ -333,6 +354,8 @@ class SequencedQueue { entries_ = EntryView(storage_.data(), entries_.size()); } + total_consumed_element_size_ = CheckAdd( + total_consumed_element_size_, static_cast<uint64_t>(element_size)); return true; } @@ -344,10 +367,16 @@ class SequencedQueue { } protected: - void ReduceNextElementSize(size_t amount) { + // Adjusts the recorded size of the element at the head of this queue, as if + // the element were partially consumed. After this call, the value returned by + // GetTotalAvailableElementSize() will be decreased by `amount`, and the value + // returned by total_consumed_element_size() will increase by the same. + void PartiallyConsumeNextElement(size_t amount) { ABSL_ASSERT(HasNextElement()); ABSL_ASSERT(entries_[0]->total_span_size >= amount); entries_[0]->total_span_size -= amount; + total_consumed_element_size_ = + CheckAdd(total_consumed_element_size_, static_cast<uint64_t>(amount)); } private: @@ -556,6 +585,10 @@ class SequencedQueue { // The number of slots in `entries_` which are actually occupied. size_t num_entries_ = 0; + // Tracks the sum of the element sizes of every element fully or partially + // consumed from the queue so far. + uint64_t total_consumed_element_size_ = 0; + // The final length of the sequence to be enqueued, if known. absl::optional<SequenceNumber> final_sequence_length_; }; diff --git a/chromium/third_party/ipcz/src/ipcz/sequenced_queue_test.cc b/chromium/third_party/ipcz/src/ipcz/sequenced_queue_test.cc index 54ed16f9a0e..2b5113e126a 100644 --- a/chromium/third_party/ipcz/src/ipcz/sequenced_queue_test.cc +++ b/chromium/third_party/ipcz/src/ipcz/sequenced_queue_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -181,35 +181,66 @@ TEST(SequencedQueueTest, FullyConsumed) { EXPECT_TRUE(q.IsSequenceFullyConsumed()); } -TEST(SequencedQueueTest, MaybeSkipSequenceNumber) { - TestQueue q; +TEST(SequencedQueueTest, SkipElement) { + TestQueueWithSize q; const std::string kEntry = "woot"; - EXPECT_TRUE(q.MaybeSkipSequenceNumber(SequenceNumber(0))); - EXPECT_FALSE(q.MaybeSkipSequenceNumber(SequenceNumber(0))); + constexpr size_t kTestElementSize = 42; + + // Skipping an element should update accounting appropriately. + EXPECT_TRUE(q.SkipElement(SequenceNumber(0), kTestElementSize)); + EXPECT_EQ(0u, q.GetTotalAvailableElementSize()); + EXPECT_EQ(kTestElementSize, q.GetTotalElementSizeQueuedSoFar()); + + // We can't skip or push an element that's already been skipped. + EXPECT_FALSE(q.SkipElement(SequenceNumber(0), kTestElementSize)); EXPECT_FALSE(q.Push(SequenceNumber(0), kEntry)); + + // And we can't skip an element that's already been pushed. EXPECT_TRUE(q.Push(SequenceNumber(1), kEntry)); - EXPECT_FALSE(q.MaybeSkipSequenceNumber(SequenceNumber(1))); + EXPECT_FALSE(q.SkipElement(SequenceNumber(1), 7)); + EXPECT_EQ(kEntry.size(), q.GetTotalAvailableElementSize()); + EXPECT_EQ(kEntry.size() + kTestElementSize, + q.GetTotalElementSizeQueuedSoFar()); std::string s; EXPECT_TRUE(q.Pop(s)); + EXPECT_EQ(0u, q.GetTotalAvailableElementSize()); + EXPECT_EQ(kEntry.size() + kTestElementSize, + q.GetTotalElementSizeQueuedSoFar()); - // Skip ahead to SequenceNumber 4. - EXPECT_TRUE(q.MaybeSkipSequenceNumber(SequenceNumber(2))); - EXPECT_TRUE(q.MaybeSkipSequenceNumber(SequenceNumber(3))); + // Skip ahead past SequenceNumber 2 and 3. + EXPECT_TRUE(q.SkipElement(SequenceNumber(2), kTestElementSize)); + EXPECT_EQ(0u, q.GetTotalAvailableElementSize()); + EXPECT_EQ(kEntry.size() + kTestElementSize * 2, + q.GetTotalElementSizeQueuedSoFar()); + EXPECT_TRUE(q.SkipElement(SequenceNumber(3), kTestElementSize)); + EXPECT_EQ(0u, q.GetTotalAvailableElementSize()); + EXPECT_EQ(kEntry.size() + kTestElementSize * 3, + q.GetTotalElementSizeQueuedSoFar()); + + // SequenceNumber 4 can now be pushed while 2 and 3 cannot. EXPECT_FALSE(q.Push(SequenceNumber(2), kEntry)); EXPECT_FALSE(q.Push(SequenceNumber(3), kEntry)); EXPECT_TRUE(q.Push(SequenceNumber(4), kEntry)); - EXPECT_FALSE(q.MaybeSkipSequenceNumber(SequenceNumber(4))); + EXPECT_FALSE(q.SkipElement(SequenceNumber(4), kTestElementSize)); + EXPECT_EQ(kEntry.size(), q.GetTotalAvailableElementSize()); + EXPECT_EQ(kEntry.size() * 2 + kTestElementSize * 3, + q.GetTotalElementSizeQueuedSoFar()); + // Cap the sequence at 6 elements and verify that accounting remains intact + // when we skip the last element. EXPECT_TRUE(q.SetFinalSequenceLength(SequenceNumber(6))); EXPECT_FALSE(q.IsSequenceFullyConsumed()); EXPECT_TRUE(q.Pop(s)); EXPECT_FALSE(q.IsSequenceFullyConsumed()); - EXPECT_TRUE(q.MaybeSkipSequenceNumber(SequenceNumber(5))); + EXPECT_TRUE(q.SkipElement(SequenceNumber(5), kTestElementSize)); + EXPECT_EQ(0u, q.GetTotalAvailableElementSize()); + EXPECT_EQ(kEntry.size() * 2 + kTestElementSize * 4, + q.GetTotalElementSizeQueuedSoFar()); EXPECT_TRUE(q.IsSequenceFullyConsumed()); // Fully consumed queue: skipping must fail. - EXPECT_FALSE(q.MaybeSkipSequenceNumber(SequenceNumber(6))); + EXPECT_FALSE(q.SkipElement(SequenceNumber(6), kTestElementSize)); } TEST(SequencedQueueTest, Accounting) { diff --git a/chromium/third_party/ipcz/src/ipcz/sublink_id.h b/chromium/third_party/ipcz/src/ipcz/sublink_id.h index 5b9158e27c0..c5a658753c4 100644 --- a/chromium/third_party/ipcz/src/ipcz/sublink_id.h +++ b/chromium/third_party/ipcz/src/ipcz/sublink_id.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/test_messages.cc b/chromium/third_party/ipcz/src/ipcz/test_messages.cc index 12c4f36721e..6bd43e7bd84 100644 --- a/chromium/third_party/ipcz/src/ipcz/test_messages.cc +++ b/chromium/third_party/ipcz/src/ipcz/test_messages.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/test_messages.h b/chromium/third_party/ipcz/src/ipcz/test_messages.h index 88e7ce84da8..fbbbd3474af 100644 --- a/chromium/third_party/ipcz/src/ipcz/test_messages.h +++ b/chromium/third_party/ipcz/src/ipcz/test_messages.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/test_messages_generator.h b/chromium/third_party/ipcz/src/ipcz/test_messages_generator.h index 5f33a18349f..63420da10d1 100644 --- a/chromium/third_party/ipcz/src/ipcz/test_messages_generator.h +++ b/chromium/third_party/ipcz/src/ipcz/test_messages_generator.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/trap_event_dispatcher.cc b/chromium/third_party/ipcz/src/ipcz/trap_event_dispatcher.cc index d47aaa5caae..aa08b95a722 100644 --- a/chromium/third_party/ipcz/src/ipcz/trap_event_dispatcher.cc +++ b/chromium/third_party/ipcz/src/ipcz/trap_event_dispatcher.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/trap_event_dispatcher.h b/chromium/third_party/ipcz/src/ipcz/trap_event_dispatcher.h index 44d312c99a8..60029c299f4 100644 --- a/chromium/third_party/ipcz/src/ipcz/trap_event_dispatcher.h +++ b/chromium/third_party/ipcz/src/ipcz/trap_event_dispatcher.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/ipcz/trap_set.cc b/chromium/third_party/ipcz/src/ipcz/trap_set.cc index 2aacb02e618..c0d26cdf2ca 100644 --- a/chromium/third_party/ipcz/src/ipcz/trap_set.cc +++ b/chromium/third_party/ipcz/src/ipcz/trap_set.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -16,7 +16,7 @@ namespace ipcz { namespace { -IpczTrapConditionFlags GetSatisfiedConditions( +IpczTrapConditionFlags GetSatisfiedConditionsForUpdate( const IpczTrapConditions& conditions, TrapSet::UpdateReason reason, const IpczPortalStatus& status) { @@ -52,9 +52,12 @@ IpczTrapConditionFlags GetSatisfiedConditions( return event_flags; } -bool NeedRemoteState(IpczTrapConditionFlags flags) { - return (flags & (IPCZ_TRAP_BELOW_MAX_REMOTE_PARCELS | - IPCZ_TRAP_BELOW_MAX_REMOTE_BYTES)) != 0; +bool NeedRemoteParcels(IpczTrapConditionFlags flags) { + return (flags & IPCZ_TRAP_BELOW_MAX_REMOTE_PARCELS) != 0; +} + +bool NeedRemoteBytes(IpczTrapConditionFlags flags) { + return (flags & IPCZ_TRAP_BELOW_MAX_REMOTE_BYTES) != 0; } } // namespace @@ -65,6 +68,14 @@ TrapSet::~TrapSet() { ABSL_ASSERT(empty()); } +// static +IpczTrapConditionFlags TrapSet::GetSatisfiedConditions( + const IpczTrapConditions& conditions, + const IpczPortalStatus& current_status) { + return GetSatisfiedConditionsForUpdate(conditions, UpdateReason::kInstallTrap, + current_status); +} + IpczResult TrapSet::Add(const IpczTrapConditions& conditions, IpczTrapEventHandler handler, uintptr_t context, @@ -72,8 +83,8 @@ IpczResult TrapSet::Add(const IpczTrapConditions& conditions, IpczTrapConditionFlags* satisfied_condition_flags, IpczPortalStatus* status) { last_known_status_ = current_status; - IpczTrapConditionFlags flags = GetSatisfiedConditions( - conditions, UpdateReason::kInstallTrap, current_status); + IpczTrapConditionFlags flags = + GetSatisfiedConditions(conditions, current_status); if (flags != 0) { if (satisfied_condition_flags) { *satisfied_condition_flags = flags; @@ -91,40 +102,56 @@ IpczResult TrapSet::Add(const IpczTrapConditions& conditions, } traps_.emplace_back(conditions, handler, context); - if (NeedRemoteState(conditions.flags)) { - ++num_traps_monitoring_remote_state_; + if (NeedRemoteParcels(conditions.flags)) { + ++num_traps_monitoring_remote_parcels_; + } + if (NeedRemoteBytes(conditions.flags)) { + ++num_traps_monitoring_remote_bytes_; } return IPCZ_RESULT_OK; } -void TrapSet::UpdatePortalStatus(const IpczPortalStatus& status, +void TrapSet::UpdatePortalStatus(const OperationContext& context, + const IpczPortalStatus& status, UpdateReason reason, TrapEventDispatcher& dispatcher) { last_known_status_ = status; for (auto* it = traps_.begin(); it != traps_.end();) { const Trap& trap = *it; - const IpczTrapConditionFlags flags = - GetSatisfiedConditions(trap.conditions, reason, status); + IpczTrapConditionFlags flags = + GetSatisfiedConditionsForUpdate(trap.conditions, reason, status); if (!flags) { ++it; continue; } + if (context.is_api_call()) { + flags |= IPCZ_TRAP_WITHIN_API_CALL; + } dispatcher.DeferEvent(trap.handler, trap.context, flags, status); it = traps_.erase(it); - if (NeedRemoteState(flags)) { - --num_traps_monitoring_remote_state_; + if (NeedRemoteParcels(flags)) { + --num_traps_monitoring_remote_parcels_; + } + if (NeedRemoteBytes(flags)) { + --num_traps_monitoring_remote_bytes_; } } } -void TrapSet::RemoveAll(TrapEventDispatcher& dispatcher) { +void TrapSet::RemoveAll(const OperationContext& context, + TrapEventDispatcher& dispatcher) { + IpczTrapConditionFlags flags = IPCZ_TRAP_REMOVED; + if (context.is_api_call()) { + flags |= IPCZ_TRAP_WITHIN_API_CALL; + } for (const Trap& trap : traps_) { - dispatcher.DeferEvent(trap.handler, trap.context, IPCZ_TRAP_REMOVED, + dispatcher.DeferEvent(trap.handler, trap.context, flags, last_known_status_); } traps_.clear(); - num_traps_monitoring_remote_state_ = 0; + num_traps_monitoring_remote_parcels_ = 0; + num_traps_monitoring_remote_bytes_ = 0; } TrapSet::Trap::Trap(IpczTrapConditions conditions, diff --git a/chromium/third_party/ipcz/src/ipcz/trap_set.h b/chromium/third_party/ipcz/src/ipcz/trap_set.h index 671f0cd7ca9..a772796d416 100644 --- a/chromium/third_party/ipcz/src/ipcz/trap_set.h +++ b/chromium/third_party/ipcz/src/ipcz/trap_set.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,6 +8,7 @@ #include <cstdint> #include "ipcz/ipcz.h" +#include "ipcz/operation_context.h" #include "third_party/abseil-cpp/absl/container/inlined_vector.h" namespace ipcz { @@ -51,10 +52,25 @@ class TrapSet { // Indicates whether any installed traps in this set require monitoring of // remote queue state. + bool need_remote_parcels() const { + return num_traps_monitoring_remote_parcels_ > 0; + } + bool need_remote_bytes() const { + return num_traps_monitoring_remote_bytes_ > 0; + } bool need_remote_state() const { - return num_traps_monitoring_remote_state_ > 0; + return need_remote_parcels() || need_remote_bytes(); } + // Returns the set of trap condition flags within `conditions` that would be + // raised right now if a trap were installed to watch for them, given + // `current_status` as the status of the portal being watched. If this returns + // zero (IPCZ_NO_FLAGS), then no watched conditions are satisfied and a + // corresponding call to Add() would succeed. + static IpczTrapConditionFlags GetSatisfiedConditions( + const IpczTrapConditions& conditions, + const IpczPortalStatus& current_status); + // Attempts to install a new trap in the set. This effectively implements // the ipcz Trap() API. If `conditions` are already met, returns // IPCZ_RESULT_FAILED_PRECONDITION and populates `satisfied_condition_flags` @@ -70,13 +86,15 @@ class TrapSet { // If the state change is interesting to any trap in the set, an appropriate // event may be appended to `dispatcher` for imminent dispatch and the trap is // removed from the set before returning. - void UpdatePortalStatus(const IpczPortalStatus& status, + void UpdatePortalStatus(const OperationContext& context, + const IpczPortalStatus& status, UpdateReason reason, TrapEventDispatcher& dispatcher); // Immediately removes all traps from the set. Every trap present appends an // IPCZ_TRAP_REMOVED event to `dispatcher` before removal. - void RemoveAll(TrapEventDispatcher& dispatcher); + void RemoveAll(const OperationContext& context, + TrapEventDispatcher& dispatcher); private: struct Trap { @@ -92,7 +110,8 @@ class TrapSet { using TrapList = absl::InlinedVector<Trap, 4>; TrapList traps_; - size_t num_traps_monitoring_remote_state_ = 0; + size_t num_traps_monitoring_remote_parcels_ = 0; + size_t num_traps_monitoring_remote_bytes_ = 0; IpczPortalStatus last_known_status_ = {.size = sizeof(last_known_status_)}; }; diff --git a/chromium/third_party/ipcz/src/ipcz/validator.cc b/chromium/third_party/ipcz/src/ipcz/validator.cc new file mode 100644 index 00000000000..bba6ad3919b --- /dev/null +++ b/chromium/third_party/ipcz/src/ipcz/validator.cc @@ -0,0 +1,37 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ipcz/validator.h" + +#include "ipcz/driver_object.h" +#include "ipcz/driver_transport.h" +#include "ipcz/ipcz.h" +#include "ipcz/node.h" +#include "ipcz/node_link.h" +#include "util/ref_counted.h" + +namespace ipcz { + +Validator::Validator(Ref<NodeLink> remote_source) + : remote_source_(std::move(remote_source)) {} + +Validator::~Validator() = default; + +IpczResult Validator::Close() { + return IPCZ_RESULT_OK; +} + +IpczResult Validator::Reject(uintptr_t context) { + if (!remote_source_) { + return IPCZ_RESULT_FAILED_PRECONDITION; + } + + const IpczDriver& driver = remote_source_->node()->driver(); + const Ref<DriverTransport>& transport = remote_source_->transport(); + driver.ReportBadTransportActivity(transport->driver_object().handle(), + context, IPCZ_NO_FLAGS, nullptr); + return IPCZ_RESULT_OK; +} + +} // namespace ipcz diff --git a/chromium/third_party/ipcz/src/ipcz/validator.h b/chromium/third_party/ipcz/src/ipcz/validator.h new file mode 100644 index 00000000000..556f25d9633 --- /dev/null +++ b/chromium/third_party/ipcz/src/ipcz/validator.h @@ -0,0 +1,43 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPCZ_SRC_IPCZ_VALIDATOR_H_ +#define IPCZ_SRC_IPCZ_VALIDATOR_H_ + +#include "ipcz/validator.h" + +#include "ipcz/api_object.h" +#include "util/ref_counted.h" + +namespace ipcz { + +class NodeLink; + +// A validator object retains context associated with a specific inbound parcel. +// Applications can use these objects to report their own application-level +// validation failures to ipcz, and ipcz can use the context within to propagate +// the failure out to an appropriate driver transport. +class Validator : public APIObjectImpl<Validator, APIObject::kValidator> { + public: + explicit Validator(Ref<NodeLink> remote_source); + + // APIObject: + IpczResult Close() override; + + // Signals application-level rejection of whatever this validator is + // associated with. `context` is an opaque value passed by the application + // and propagated to the driver when appropriate. See the Reject() API. + IpczResult Reject(uintptr_t context); + + private: + ~Validator() override; + + // The remote source which sent the parcel to the local node. If this is null, + // the parcel originated from the local node. + const Ref<NodeLink> remote_source_; +}; + +} // namespace ipcz + +#endif // IPCZ_SRC_IPCZ_VALIDATOR_H_ diff --git a/chromium/third_party/ipcz/src/merge_portals_test.cc b/chromium/third_party/ipcz/src/merge_portals_test.cc index a25008aa3b1..50f849d1849 100644 --- a/chromium/third_party/ipcz/src/merge_portals_test.cc +++ b/chromium/third_party/ipcz/src/merge_portals_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -30,7 +30,7 @@ MULTINODE_TEST_NODE(MergePortalsTestNode, MergeWithInitialPortalClient) { Close(b); } -TEST_P(MergePortalsTest, MergeWithInitialPortal) { +MULTINODE_TEST(MergePortalsTest, MergeWithInitialPortal) { IpczHandle c = SpawnTestNode<MergeWithInitialPortalClient>(); auto [q, p] = OpenPortals(); EXPECT_EQ(IPCZ_RESULT_OK, Merge(c, p)); @@ -43,7 +43,7 @@ TEST_P(MergePortalsTest, MergeWithInitialPortal) { Close(q); } -TEST_P(MergePortalsTest, MergeWithClosedLocalPeer) { +MULTINODE_TEST(MergePortalsTest, MergeWithClosedLocalPeer) { auto [q, p] = OpenPortals(); auto [d, b] = OpenPortals(); @@ -64,7 +64,7 @@ MULTINODE_TEST_NODE(MergePortalsTestNode, MergeWithClosedRemotePeerClient) { Close(b); } -TEST_P(MergePortalsTest, MergeWithClosedRemotePeer) { +MULTINODE_TEST(MergePortalsTest, MergeWithClosedRemotePeer) { IpczHandle c = SpawnTestNode<MergeWithClosedRemotePeerClient>(); auto [r, s] = OpenPortals(); EXPECT_EQ(IPCZ_RESULT_OK, Put(c, "", {&r, 1})); @@ -95,7 +95,7 @@ MULTINODE_TEST_NODE(MergePortalsTestNode, MergeComplexRoutesClient) { CloseAll({b, portal, other_client}); } -TEST_P(MergePortalsTest, MergeComplexRoutes) { +MULTINODE_TEST(MergePortalsTest, MergeComplexRoutes) { IpczHandle c1 = SpawnTestNode<MergeComplexRoutesClient>(); IpczHandle c2 = SpawnTestNode<MergeComplexRoutesClient>(); @@ -123,7 +123,5 @@ TEST_P(MergePortalsTest, MergeComplexRoutes) { CloseAll({c1, c2}); } -INSTANTIATE_MULTINODE_TEST_SUITE_P(MergePortalsTest); - } // namespace } // namespace ipcz diff --git a/chromium/third_party/ipcz/src/queueing_test.cc b/chromium/third_party/ipcz/src/queueing_test.cc index a3ed6b7433d..96fe47259cf 100644 --- a/chromium/third_party/ipcz/src/queueing_test.cc +++ b/chromium/third_party/ipcz/src/queueing_test.cc @@ -1,7 +1,10 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <limits> +#include <string> + #include "ipcz/ipcz.h" #include "test/multinode_test.h" #include "testing/gtest/include/gtest/gtest.h" @@ -41,7 +44,7 @@ MULTINODE_TEST_NODE(QueueingTestNode, RemoteQueueFeedbackClient) { Close(b); } -TEST_P(QueueingTest, RemoteQueueFeedback) { +MULTINODE_TEST(QueueingTest, RemoteQueueFeedback) { // Exercises operations which rely on feedback from the remote peer regarding // its inbound parcel queue state. IpczHandle c = SpawnTestNode<RemoteQueueFeedbackClient>(); @@ -120,13 +123,13 @@ MULTINODE_TEST_NODE(QueueingTestNode, TwoPhaseQueueingClient) { // The producer should only have been able to put 3 out of its 4 bytes. EXPECT_EQ("ipc", std::string_view(reinterpret_cast<const char*>(data), num_bytes)); - EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().EndGet(b, num_bytes, 0, IPCZ_NO_FLAGS, nullptr, nullptr)); + EXPECT_EQ(IPCZ_RESULT_OK, ipcz().EndGet(b, num_bytes, 0, IPCZ_NO_FLAGS, + nullptr, nullptr, nullptr)); Close(b); } -TEST_P(QueueingTest, TwoPhaseQueueing) { +MULTINODE_TEST(QueueingTest, TwoPhaseQueueing) { IpczHandle c = SpawnTestNode<TwoPhaseQueueingClient>(); WaitForDirectRemoteLink(c); @@ -171,12 +174,13 @@ MULTINODE_TEST_NODE(QueueingTestNode, TwoPhaseFeedbackClient) { EXPECT_EQ("hello?", std::string_view(reinterpret_cast<const char*>(data), num_bytes)); - EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().EndGet(b, num_bytes, 0, IPCZ_NO_FLAGS, nullptr, nullptr)); + EXPECT_EQ(IPCZ_RESULT_OK, ipcz().EndGet(b, num_bytes, 0, IPCZ_NO_FLAGS, + nullptr, nullptr, nullptr)); Close(b); } -TEST_P(QueueingTest, TwoPhaseFeedback) { +// TODO(https://crbug.com/1361670): Fix flakiness and re-enable this test. +MULTINODE_TEST(QueueingTest, DISABLED_TwoPhaseFeedback) { IpczHandle c = SpawnTestNode<TwoPhaseFeedbackClient>(); WaitForDirectRemoteLink(c); EXPECT_EQ(IPCZ_RESULT_OK, Put(c, "hello?")); @@ -186,7 +190,80 @@ TEST_P(QueueingTest, TwoPhaseFeedback) { Close(c); } -INSTANTIATE_MULTINODE_TEST_SUITE_P(QueueingTest); +constexpr size_t kStressTestPortalCapacity = 256; +constexpr size_t kStressTestPayloadSize = 4 * 1024 * 1024; + +MULTINODE_TEST_NODE(QueueingTestNode, RemoteQueueFeedbackStressTestClient) { + IpczHandle b = ConnectToBroker(); + + size_t bytes_received = 0; + while (bytes_received < kStressTestPayloadSize) { + // Consistency check: ensure that the portal never has more than + // kStressTestPortalCapacity bytes available to retrieve. Otherwise limits + // were not properly enforced by the sender. + IpczPortalStatus status = {.size = sizeof(status)}; + EXPECT_EQ(IPCZ_RESULT_OK, + ipcz().QueryPortalStatus(b, IPCZ_NO_FLAGS, nullptr, &status)); + EXPECT_LE(status.num_local_bytes, kStressTestPortalCapacity); + + const void* data; + size_t num_bytes; + const IpczResult begin_result = + ipcz().BeginGet(b, IPCZ_NO_FLAGS, nullptr, &data, &num_bytes, nullptr); + if (begin_result == IPCZ_RESULT_OK) { + bytes_received += num_bytes; + EXPECT_EQ(std::string_view(static_cast<const char*>(data), num_bytes), + std::string(num_bytes, '!')); + EXPECT_EQ(IPCZ_RESULT_OK, ipcz().EndGet(b, num_bytes, 0, IPCZ_NO_FLAGS, + nullptr, nullptr, nullptr)); + continue; + } + + ASSERT_EQ(IPCZ_RESULT_UNAVAILABLE, begin_result); + WaitForConditions( + b, {.flags = IPCZ_TRAP_ABOVE_MIN_LOCAL_BYTES, .min_local_bytes = 0}); + } + + Close(b); +} + +MULTINODE_TEST(QueueingTest, RemoteQueueFeedbackStressTest) { + IpczHandle c = SpawnTestNode<RemoteQueueFeedbackStressTestClient>(); + + size_t bytes_remaining = kStressTestPayloadSize; + while (bytes_remaining) { + void* data; + size_t capacity = bytes_remaining; + const IpczPutLimits limits = { + .size = sizeof(limits), + .max_queued_parcels = std::numeric_limits<size_t>::max(), + .max_queued_bytes = kStressTestPortalCapacity, + }; + const IpczBeginPutOptions options = { + .size = sizeof(options), + .limits = &limits, + }; + const IpczResult begin_result = ipcz().BeginPut( + c, IPCZ_BEGIN_PUT_ALLOW_PARTIAL, &options, &capacity, &data); + if (begin_result == IPCZ_RESULT_OK) { + size_t num_bytes = std::min(bytes_remaining, capacity); + bytes_remaining -= num_bytes; + memset(data, '!', num_bytes); + EXPECT_EQ(IPCZ_RESULT_OK, ipcz().EndPut(c, num_bytes, nullptr, 0, + IPCZ_NO_FLAGS, nullptr)); + continue; + } + ASSERT_EQ(IPCZ_RESULT_RESOURCE_EXHAUSTED, begin_result); + + EXPECT_EQ( + IPCZ_RESULT_OK, + WaitForConditions(c, {.flags = IPCZ_TRAP_BELOW_MAX_REMOTE_BYTES, + .max_remote_bytes = kStressTestPortalCapacity})); + } + + WaitForConditionFlags(c, IPCZ_TRAP_PEER_CLOSED); + Close(c); +} } // namespace } // namespace ipcz diff --git a/chromium/third_party/ipcz/src/reference_drivers/async_reference_driver.cc b/chromium/third_party/ipcz/src/reference_drivers/async_reference_driver.cc index 3ebbe8e6763..f4714ffd539 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/async_reference_driver.cc +++ b/chromium/third_party/ipcz/src/reference_drivers/async_reference_driver.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -274,6 +274,7 @@ const IpczDriver kAsyncReferenceDriver = { ActivateTransport, DeactivateTransport, Transmit, + kSingleProcessReferenceDriverBase.ReportBadTransportActivity, kSingleProcessReferenceDriverBase.AllocateSharedMemory, kSingleProcessReferenceDriverBase.GetSharedMemoryInfo, kSingleProcessReferenceDriverBase.DuplicateSharedMemory, @@ -290,6 +291,7 @@ const IpczDriver kAsyncReferenceDriverWithForcedBrokering = { ActivateTransport, DeactivateTransport, Transmit, + kSingleProcessReferenceDriverBase.ReportBadTransportActivity, kSingleProcessReferenceDriverBase.AllocateSharedMemory, kSingleProcessReferenceDriverBase.GetSharedMemoryInfo, kSingleProcessReferenceDriverBase.DuplicateSharedMemory, @@ -306,4 +308,12 @@ AsyncTransportPair CreateAsyncTransportPair() { }; } +std::pair<IpczDriverHandle, IpczDriverHandle> +CreateAsyncTransportPairForBrokers() { + AsyncTransport::Pair transports = AsyncTransport::CreatePair( + AsyncTransport::NodeType::kBroker, AsyncTransport::NodeType::kBroker); + return {Object::ReleaseAsHandle(std::move(transports.first)), + Object::ReleaseAsHandle(std::move(transports.second))}; +} + } // namespace ipcz::reference_drivers diff --git a/chromium/third_party/ipcz/src/reference_drivers/async_reference_driver.h b/chromium/third_party/ipcz/src/reference_drivers/async_reference_driver.h index 2371be15467..270a107db57 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/async_reference_driver.h +++ b/chromium/third_party/ipcz/src/reference_drivers/async_reference_driver.h @@ -1,10 +1,12 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef IPCZ_SRC_DRIVERS_ASYNC_REFERENCE_DRIVER_H_ #define IPCZ_SRC_DRIVERS_ASYNC_REFERENCE_DRIVER_H_ +#include <utility> + #include "ipcz/ipcz.h" namespace ipcz::reference_drivers { @@ -28,6 +30,11 @@ struct AsyncTransportPair { }; AsyncTransportPair CreateAsyncTransportPair(); +// Creates a new pair of async transport endpoints, one for each of two +// different brokers to be connected. +std::pair<IpczDriverHandle, IpczDriverHandle> +CreateAsyncTransportPairForBrokers(); + } // namespace ipcz::reference_drivers #endif // IPCZ_SRC_DRIVERS_ASYNC_REFERENCE_DRIVER_H_ diff --git a/chromium/third_party/ipcz/src/reference_drivers/blob.cc b/chromium/third_party/ipcz/src/reference_drivers/blob.cc deleted file mode 100644 index bbebebe9b9c..00000000000 --- a/chromium/third_party/ipcz/src/reference_drivers/blob.cc +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2022 The Chromium 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 "reference_drivers/blob.h" - -#include <iterator> - -#include "util/ref_counted.h" - -namespace ipcz::reference_drivers { - -Blob::RefCountedFlag::RefCountedFlag() = default; - -Blob::RefCountedFlag::~RefCountedFlag() = default; - -Blob::Blob(std::string_view message) : message_(message) {} - -Blob::~Blob() = default; - -IpczResult Blob::Close() { - destruction_flag_for_testing_->set(true); - return IPCZ_RESULT_OK; -} - -// static -Blob* Blob::FromHandle(IpczDriverHandle handle) { - Object* object = Object::FromHandle(handle); - if (!object || object->type() != kBlob) { - return nullptr; - } - - return static_cast<Blob*>(object); -} - -// static -Ref<Blob> Blob::TakeFromHandle(IpczDriverHandle handle) { - return AdoptRef(FromHandle(handle)); -} - -} // namespace ipcz::reference_drivers diff --git a/chromium/third_party/ipcz/src/reference_drivers/blob.h b/chromium/third_party/ipcz/src/reference_drivers/blob.h deleted file mode 100644 index 84be3788759..00000000000 --- a/chromium/third_party/ipcz/src/reference_drivers/blob.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2022 The Chromium 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 IPCZ_SRC_REFERENCE_DRIVERS_BLOB_H_ -#define IPCZ_SRC_REFERENCE_DRIVERS_BLOB_H_ - -#include <string> -#include <string_view> - -#include "reference_drivers/object.h" -#include "util/ref_counted.h" - -namespace ipcz::reference_drivers { - -// A driver-managed object which packages arbitrary string data. Blobs are used -// to exercise driver object boxing in tests. -// -// Note that unlike the transport and memory objects defined by the reference -// drivers, a blob is not a type of object known to ipcz. Instead it is used to -// demonstrate how drivers can define arbitrary new types of transferrable -// objects to extend ipcz. -class Blob : public ObjectImpl<Blob, Object::kBlob> { - public: - class RefCountedFlag : public RefCounted { - public: - RefCountedFlag(); - - bool get() const { return flag_; } - void set(bool flag) { flag_ = flag; } - - private: - ~RefCountedFlag() override; - bool flag_ = false; - }; - - explicit Blob(std::string_view message); - - // Object: - IpczResult Close() override; - - std::string& message() { return message_; } - - const Ref<RefCountedFlag>& destruction_flag_for_testing() const { - return destruction_flag_for_testing_; - } - - static Blob* FromHandle(IpczDriverHandle handle); - static Ref<Blob> TakeFromHandle(IpczDriverHandle handle); - - protected: - ~Blob() override; - - private: - std::string message_; - const Ref<RefCountedFlag> destruction_flag_for_testing_{ - MakeRefCounted<RefCountedFlag>()}; -}; - -} // namespace ipcz::reference_drivers - -#endif // IPCZ_SRC_REFERENCE_DRIVERS_BLOB_H_ diff --git a/chromium/third_party/ipcz/src/reference_drivers/file_descriptor.cc b/chromium/third_party/ipcz/src/reference_drivers/file_descriptor.cc index b00f44addaa..e73f4beafbd 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/file_descriptor.cc +++ b/chromium/third_party/ipcz/src/reference_drivers/file_descriptor.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/reference_drivers/file_descriptor.h b/chromium/third_party/ipcz/src/reference_drivers/file_descriptor.h index 6e2233e6910..e92afbe00e9 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/file_descriptor.h +++ b/chromium/third_party/ipcz/src/reference_drivers/file_descriptor.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/reference_drivers/handle_eintr.h b/chromium/third_party/ipcz/src/reference_drivers/handle_eintr.h index 927e9bd70c1..db4c4a81df9 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/handle_eintr.h +++ b/chromium/third_party/ipcz/src/reference_drivers/handle_eintr.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/reference_drivers/memfd_memory.cc b/chromium/third_party/ipcz/src/reference_drivers/memfd_memory.cc index c5e839be201..fadf967c313 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/memfd_memory.cc +++ b/chromium/third_party/ipcz/src/reference_drivers/memfd_memory.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/reference_drivers/memfd_memory.h b/chromium/third_party/ipcz/src/reference_drivers/memfd_memory.h index b989106da07..07f7b6a6f36 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/memfd_memory.h +++ b/chromium/third_party/ipcz/src/reference_drivers/memfd_memory.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/reference_drivers/memfd_memory_test.cc b/chromium/third_party/ipcz/src/reference_drivers/memfd_memory_test.cc index 67d5d105aa7..89d3dfbbe7a 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/memfd_memory_test.cc +++ b/chromium/third_party/ipcz/src/reference_drivers/memfd_memory_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/reference_drivers/multiprocess_reference_driver.cc b/chromium/third_party/ipcz/src/reference_drivers/multiprocess_reference_driver.cc index fe967b10edd..445affe9141 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/multiprocess_reference_driver.cc +++ b/chromium/third_party/ipcz/src/reference_drivers/multiprocess_reference_driver.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -11,7 +11,6 @@ #include <utility> #include "ipcz/ipcz.h" -#include "reference_drivers/blob.h" #include "reference_drivers/file_descriptor.h" #include "reference_drivers/memfd_memory.h" #include "reference_drivers/object.h" @@ -206,11 +205,6 @@ IpczResult IPCZ_API Serialize(IpczDriverHandle handle, required_num_handles = 1; break; - case Object::kBlob: - required_num_bytes += Blob::FromObject(object)->message().size(); - required_num_handles = 0; - break; - default: return IPCZ_RESULT_INVALID_ARGUMENT; } @@ -246,12 +240,6 @@ IpczResult IPCZ_API Serialize(IpczDriverHandle handle, break; } - case Object::kBlob: { - auto blob = Blob::TakeFromObject(object); - memcpy(&header + 1, blob->message().data(), blob->message().size()); - break; - } - default: return IPCZ_RESULT_INVALID_ARGUMENT; } @@ -290,12 +278,6 @@ IpczResult IPCZ_API Deserialize(const void* data, } break; - case Object::kBlob: - object = MakeRefCounted<Blob>( - std::string_view(reinterpret_cast<const char*>(&header + 1), - num_bytes - sizeof(header))); - break; - default: break; } @@ -352,6 +334,13 @@ IpczResult IPCZ_API Transmit(IpczDriverHandle driver_transport, absl::MakeSpan(handles, num_handles)); } +IpczResult IPCZ_API ReportBadTransportActivity(IpczDriverHandle transport, + uintptr_t context, + uint32_t flags, + const void* options) { + return IPCZ_RESULT_OK; +} + IpczResult IPCZ_API AllocateSharedMemory(size_t num_bytes, uint32_t flags, const void* options, @@ -415,6 +404,7 @@ const IpczDriver kMultiprocessReferenceDriver = { ActivateTransport, DeactivateTransport, Transmit, + ReportBadTransportActivity, AllocateSharedMemory, GetSharedMemoryInfo, DuplicateSharedMemory, diff --git a/chromium/third_party/ipcz/src/reference_drivers/multiprocess_reference_driver.h b/chromium/third_party/ipcz/src/reference_drivers/multiprocess_reference_driver.h index 2aa2c183740..ebe564a8307 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/multiprocess_reference_driver.h +++ b/chromium/third_party/ipcz/src/reference_drivers/multiprocess_reference_driver.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/reference_drivers/multiprocess_reference_driver_test.cc b/chromium/third_party/ipcz/src/reference_drivers/multiprocess_reference_driver_test.cc index aa966bae945..94f960fe2a4 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/multiprocess_reference_driver_test.cc +++ b/chromium/third_party/ipcz/src/reference_drivers/multiprocess_reference_driver_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/reference_drivers/object.cc b/chromium/third_party/ipcz/src/reference_drivers/object.cc index 5028d07e087..005924be08f 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/object.cc +++ b/chromium/third_party/ipcz/src/reference_drivers/object.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/reference_drivers/object.h b/chromium/third_party/ipcz/src/reference_drivers/object.h index 56e88cd8eac..e562d2beea2 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/object.h +++ b/chromium/third_party/ipcz/src/reference_drivers/object.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -21,11 +21,6 @@ class Object : public RefCounted { kMemory, kMapping, - // A non-standard driver object type, used to exercise more complex, custom - // driver object de/serialization via boxing and unboxing in tests. See the - // Blob definition in src/reference_drivers/blob.h. - kBlob, - #if defined(OS_LINUX) // A non-standard driver object type which wraps a FileDescriptor object. kFileDescriptor, diff --git a/chromium/third_party/ipcz/src/reference_drivers/random.cc b/chromium/third_party/ipcz/src/reference_drivers/random.cc index 91312250c0f..83c0e6f2553 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/random.cc +++ b/chromium/third_party/ipcz/src/reference_drivers/random.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/reference_drivers/random.h b/chromium/third_party/ipcz/src/reference_drivers/random.h index 28596baabb7..aee39e34a52 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/random.h +++ b/chromium/third_party/ipcz/src/reference_drivers/random.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/reference_drivers/single_process_reference_driver_base.cc b/chromium/third_party/ipcz/src/reference_drivers/single_process_reference_driver_base.cc index f9b5cb1e6b0..f6832d31624 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/single_process_reference_driver_base.cc +++ b/chromium/third_party/ipcz/src/reference_drivers/single_process_reference_driver_base.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -55,6 +55,12 @@ class InProcessMapping : public ObjectImpl<InProcessMapping, Object::kMapping> { const Ref<InProcessMemory> memory_; }; +BadTransportActivityCallback& GetBadTransportActivityCallback() { + static BadTransportActivityCallback* callback = + new BadTransportActivityCallback(); + return *callback; +} + IpczResult IPCZ_API Close(IpczDriverHandle handle, uint32_t flags, const void* options) { @@ -110,6 +116,17 @@ IpczResult IPCZ_API Deserialize(const void* data, return IPCZ_RESULT_OK; } +IpczResult IPCZ_API ReportBadTransportActivity(IpczDriverHandle transport, + uintptr_t context, + uint32_t flags, + const void* options) { + auto& callback = GetBadTransportActivityCallback(); + if (callback) { + callback(transport, context); + } + return IPCZ_RESULT_OK; +} + IpczResult IPCZ_API AllocateSharedMemory(size_t num_bytes, uint32_t flags, const void* options, @@ -177,6 +194,7 @@ const IpczDriver kSingleProcessReferenceDriverBase = { nullptr, nullptr, nullptr, + ReportBadTransportActivity, AllocateSharedMemory, GetSharedMemoryInfo, DuplicateSharedMemory, @@ -184,4 +202,8 @@ const IpczDriver kSingleProcessReferenceDriverBase = { GenerateRandomBytes, }; +void SetBadTransportActivityCallback(BadTransportActivityCallback callback) { + GetBadTransportActivityCallback() = std::move(callback); +} + } // namespace ipcz::reference_drivers diff --git a/chromium/third_party/ipcz/src/reference_drivers/single_process_reference_driver_base.h b/chromium/third_party/ipcz/src/reference_drivers/single_process_reference_driver_base.h index e0e89b5aca3..cb2b9986a64 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/single_process_reference_driver_base.h +++ b/chromium/third_party/ipcz/src/reference_drivers/single_process_reference_driver_base.h @@ -1,10 +1,12 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef IPCZ_SRC_REFERENCE_DRIVERS_SINGLE_PROCESS_REFERENCE_DRIVER_BASE_H_ #define IPCZ_SRC_REFERENCE_DRIVERS_SINGLE_PROCESS_REFERENCE_DRIVER_BASE_H_ +#include <functional> + #include "ipcz/ipcz.h" namespace ipcz::reference_drivers { @@ -13,6 +15,13 @@ namespace ipcz::reference_drivers { // and async single-process drivers. extern const IpczDriver kSingleProcessReferenceDriverBase; +// Installs a hook to be invoked any time ReportBadTransportActivity() is called +// on any single-process reference driver. If called with null, any previously +// installed hook is removed. +using BadTransportActivityCallback = + std::function<void(IpczDriverHandle, uintptr_t)>; +void SetBadTransportActivityCallback(BadTransportActivityCallback callback); + } // namespace ipcz::reference_drivers #endif // IPCZ_SRC_REFERENCE_DRIVERS_SINGLE_PROCESS_REFERENCE_DRIVER_BASE_H_ diff --git a/chromium/third_party/ipcz/src/reference_drivers/socket_transport.cc b/chromium/third_party/ipcz/src/reference_drivers/socket_transport.cc index 695fffea80e..91eca3084a8 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/socket_transport.cc +++ b/chromium/third_party/ipcz/src/reference_drivers/socket_transport.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/reference_drivers/socket_transport.h b/chromium/third_party/ipcz/src/reference_drivers/socket_transport.h index a016c5bb6a6..d48310afbd2 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/socket_transport.h +++ b/chromium/third_party/ipcz/src/reference_drivers/socket_transport.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/reference_drivers/socket_transport_test.cc b/chromium/third_party/ipcz/src/reference_drivers/socket_transport_test.cc index 7b3f7fb39cc..d55e4f7c748 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/socket_transport_test.cc +++ b/chromium/third_party/ipcz/src/reference_drivers/socket_transport_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/reference_drivers/sync_reference_driver.cc b/chromium/third_party/ipcz/src/reference_drivers/sync_reference_driver.cc index 6384c32a180..8b086c6bfc0 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/sync_reference_driver.cc +++ b/chromium/third_party/ipcz/src/reference_drivers/sync_reference_driver.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -379,6 +379,7 @@ const IpczDriver kSyncReferenceDriver = { ActivateTransport, DeactivateTransport, Transmit, + kSingleProcessReferenceDriverBase.ReportBadTransportActivity, kSingleProcessReferenceDriverBase.AllocateSharedMemory, kSingleProcessReferenceDriverBase.GetSharedMemoryInfo, kSingleProcessReferenceDriverBase.DuplicateSharedMemory, diff --git a/chromium/third_party/ipcz/src/reference_drivers/sync_reference_driver.h b/chromium/third_party/ipcz/src/reference_drivers/sync_reference_driver.h index 4dd4c269897..faaa02f26d9 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/sync_reference_driver.h +++ b/chromium/third_party/ipcz/src/reference_drivers/sync_reference_driver.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/reference_drivers/sync_reference_driver_test.cc b/chromium/third_party/ipcz/src/reference_drivers/sync_reference_driver_test.cc index 2ec1f9a2f71..ee5b1284b84 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/sync_reference_driver_test.cc +++ b/chromium/third_party/ipcz/src/reference_drivers/sync_reference_driver_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/reference_drivers/wrapped_file_descriptor.cc b/chromium/third_party/ipcz/src/reference_drivers/wrapped_file_descriptor.cc index 675b874d574..f7f63b1951d 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/wrapped_file_descriptor.cc +++ b/chromium/third_party/ipcz/src/reference_drivers/wrapped_file_descriptor.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/reference_drivers/wrapped_file_descriptor.h b/chromium/third_party/ipcz/src/reference_drivers/wrapped_file_descriptor.h index e91154bd3ca..651f29127fc 100644 --- a/chromium/third_party/ipcz/src/reference_drivers/wrapped_file_descriptor.h +++ b/chromium/third_party/ipcz/src/reference_drivers/wrapped_file_descriptor.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/remote_portal_test.cc b/chromium/third_party/ipcz/src/remote_portal_test.cc index 798fdfe97a0..b8982c5698c 100644 --- a/chromium/third_party/ipcz/src/remote_portal_test.cc +++ b/chromium/third_party/ipcz/src/remote_portal_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,8 +6,8 @@ #include <string_view> #include <utility> +#include "build/build_config.h" #include "ipcz/ipcz.h" -#include "reference_drivers/blob.h" #include "test/multinode_test.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/strings/str_cat.h" @@ -31,7 +31,7 @@ MULTINODE_TEST_NODE(RemotePortalTestNode, BasicConnectionClient) { Close(b); } -TEST_P(RemotePortalTest, BasicConnection) { +MULTINODE_TEST(RemotePortalTest, BasicConnection) { IpczHandle c = SpawnTestNode<BasicConnectionClient>(); std::string message; @@ -55,7 +55,7 @@ MULTINODE_TEST_NODE(RemotePortalTestNode, PortalTransferClient) { CloseAll({p, b}); } -TEST_P(RemotePortalTest, PortalTransfer) { +MULTINODE_TEST(RemotePortalTest, PortalTransfer) { IpczHandle c = SpawnTestNode<PortalTransferClient>(); auto [q, p] = OpenPortals(); @@ -110,7 +110,7 @@ MULTINODE_TEST_NODE(RemotePortalTestNode, MultipleHopsClient2) { CloseAll({p, b}); } -TEST_P(RemotePortalTest, MultipleHops) { +MULTINODE_TEST(RemotePortalTest, MultipleHops) { IpczHandle c1 = SpawnTestNode<MultipleHopsClient1>(); IpczHandle c2 = SpawnTestNode<MultipleHopsClient2>(); @@ -138,7 +138,7 @@ MULTINODE_TEST_NODE(RemotePortalTestNode, TransferBackAndForthClient) { Close(b); } -TEST_P(RemotePortalTest, TransferBackAndForth) { +MULTINODE_TEST(RemotePortalTest, TransferBackAndForth) { IpczHandle c = SpawnTestNode<TransferBackAndForthClient>(); constexpr std::string_view kMessage = "hihihihi"; @@ -176,22 +176,12 @@ MULTINODE_TEST_NODE(RemotePortalTestNode, HugeNumberOfPortalsClient) { EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(other_client, nullptr, {&portals[i], 1})); - IpczDriverHandle blob = reference_drivers::Blob::ReleaseAsHandle( - MakeRefCounted<reference_drivers::Blob>(absl::StrCat(absl::Dec(i)))); - IpczHandle box; - EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().Box(node(), blob, IPCZ_NO_FLAGS, nullptr, &box)); + IpczHandle box = BoxBlob(absl::StrCat(absl::Dec(i))); EXPECT_EQ(IPCZ_RESULT_OK, Put(portals[i], "", {&box, 1})); - } - for (size_t i = 0; i < kHugeNumberOfPortalsCount; ++i) { - IpczHandle box; + box = IPCZ_INVALID_HANDLE; EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(portals[i], nullptr, {&box, 1})); - - IpczDriverHandle blob; - EXPECT_EQ(IPCZ_RESULT_OK, ipcz().Unbox(box, IPCZ_NO_FLAGS, nullptr, &blob)); - EXPECT_EQ(absl::StrCat(absl::Dec(i)), - reference_drivers::Blob::TakeFromHandle(blob)->message()); + EXPECT_EQ(absl::StrCat(absl::Dec(i)), UnboxBlob(box)); } EXPECT_EQ(IPCZ_RESULT_OK, Put(b, "", {&other_client, 1})); @@ -200,7 +190,7 @@ MULTINODE_TEST_NODE(RemotePortalTestNode, HugeNumberOfPortalsClient) { Close(b); } -TEST_P(RemotePortalTest, HugeNumberOfPortals) { +MULTINODE_TEST(RemotePortalTest, HugeNumberOfPortals) { // Opens a very large number of portals, and sends them all to client nodes. // The client nodes exchange these portals with each other and transmit // parcels over them, with and without driver objects. This exercises @@ -238,7 +228,7 @@ MULTINODE_TEST_NODE(RemotePortalTestNode, RoutingStressTestClient) { Close(b); } -TEST_P(RemotePortalTest, RoutingStressTest) { +MULTINODE_TEST(RemotePortalTest, RoutingStressTest) { // This test spawns a bunch of nodes and bounces two portals back and forth // among them over a large number of iterations, then waits for all the // intermediate routers to be removed. Every iteration also sends a message @@ -254,11 +244,7 @@ TEST_P(RemotePortalTest, RoutingStressTest) { auto [a, b] = OpenPortals(); for (size_t j = 0; j < kRouteExpansionStressTestNumIterations; ++j) { - IpczDriverHandle blob = reference_drivers::Blob::ReleaseAsHandle( - MakeRefCounted<reference_drivers::Blob>(absl::StrCat(absl::Dec(j)))); - IpczHandle box; - EXPECT_EQ(IPCZ_RESULT_OK, - ipcz().Box(node(), blob, IPCZ_NO_FLAGS, nullptr, &box)); + IpczHandle box = BoxBlob(absl::StrCat(absl::Dec(j))); EXPECT_EQ(IPCZ_RESULT_OK, Put(a, absl::StrCat("a", absl::Dec(j)), {&box, 1})); EXPECT_EQ(IPCZ_RESULT_OK, Put(b, absl::StrCat("b", absl::Dec(j)))); @@ -279,11 +265,7 @@ TEST_P(RemotePortalTest, RoutingStressTest) { EXPECT_EQ(absl::StrCat("b", absl::Dec(i)), message); EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(b, &message, {&box, 1})); EXPECT_EQ(absl::StrCat("a", absl::Dec(i)), message); - - IpczDriverHandle blob; - EXPECT_EQ(IPCZ_RESULT_OK, ipcz().Unbox(box, IPCZ_NO_FLAGS, nullptr, &blob)); - EXPECT_EQ(absl::StrCat(absl::Dec(i)), - reference_drivers::Blob::TakeFromHandle(blob)->message()); + EXPECT_EQ(absl::StrCat(absl::Dec(i)), UnboxBlob(box)); } for (auto& pair : client_pairs) { @@ -340,7 +322,7 @@ MULTINODE_TEST_NODE(RemotePortalTestNode, DisconnectThroughProxyClient3) { #else #define MAYBE_DisconnectThroughProxy DisconnectThroughProxy #endif -TEST_P(RemotePortalTest, MAYBE_DisconnectThroughProxy) { +MULTINODE_TEST(RemotePortalTest, MAYBE_DisconnectThroughProxy) { // Exercises node disconnection. Namely if portals on nodes 1 and 3 are // connected via proxy on node 2, and node 3 disappears, node 1's portal // should observe peer closure. @@ -372,7 +354,5 @@ TEST_P(RemotePortalTest, MAYBE_DisconnectThroughProxy) { CloseAll({c1, c2, c3}); } -INSTANTIATE_MULTINODE_TEST_SUITE_P(RemotePortalTest); - } // namespace } // namespace ipcz diff --git a/chromium/third_party/ipcz/src/standalone/BUILD.gn b/chromium/third_party/ipcz/src/standalone/BUILD.gn index f94f5152a8f..19c1fc2dab0 100644 --- a/chromium/third_party/ipcz/src/standalone/BUILD.gn +++ b/chromium/third_party/ipcz/src/standalone/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2022 The Chromium Authors. All rights reserved. +# Copyright 2022 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/standalone/base/logging.cc b/chromium/third_party/ipcz/src/standalone/base/logging.cc index b304758fbb4..8db8a859565 100644 --- a/chromium/third_party/ipcz/src/standalone/base/logging.cc +++ b/chromium/third_party/ipcz/src/standalone/base/logging.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/standalone/base/logging.h b/chromium/third_party/ipcz/src/standalone/base/logging.h index e12cc3ba29c..0cdff606b5c 100644 --- a/chromium/third_party/ipcz/src/standalone/base/logging.h +++ b/chromium/third_party/ipcz/src/standalone/base/logging.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/standalone/base/stack_trace.cc b/chromium/third_party/ipcz/src/standalone/base/stack_trace.cc index 159e4a339eb..65dbdd55442 100644 --- a/chromium/third_party/ipcz/src/standalone/base/stack_trace.cc +++ b/chromium/third_party/ipcz/src/standalone/base/stack_trace.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/standalone/base/stack_trace.h b/chromium/third_party/ipcz/src/standalone/base/stack_trace.h index b14df86a77c..f23bbb94093 100644 --- a/chromium/third_party/ipcz/src/standalone/base/stack_trace.h +++ b/chromium/third_party/ipcz/src/standalone/base/stack_trace.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/trap_test.cc b/chromium/third_party/ipcz/src/trap_test.cc index 5c2f537ab6b..d23c0a90ca3 100644 --- a/chromium/third_party/ipcz/src/trap_test.cc +++ b/chromium/third_party/ipcz/src/trap_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -40,12 +40,14 @@ TEST_F(TrapTest, RemoveOnClose) { .flags = IPCZ_TRAP_NEW_LOCAL_PARCEL, }; EXPECT_EQ(IPCZ_RESULT_OK, Trap(b, conditions, [&](const IpczTrapEvent& e) { - EXPECT_EQ(IPCZ_TRAP_REMOVED, e.condition_flags); + EXPECT_EQ(IPCZ_TRAP_REMOVED | IPCZ_TRAP_WITHIN_API_CALL, + e.condition_flags); parcel_trap_removed = true; })); conditions.flags = IPCZ_TRAP_PEER_CLOSED; EXPECT_EQ(IPCZ_RESULT_OK, Trap(b, conditions, [&](const IpczTrapEvent& e) { - EXPECT_EQ(IPCZ_TRAP_REMOVED, e.condition_flags); + EXPECT_EQ(IPCZ_TRAP_REMOVED | IPCZ_TRAP_WITHIN_API_CALL, + e.condition_flags); closure_trap_removed = true; })); @@ -67,7 +69,8 @@ TEST_F(TrapTest, PeerClosed) { }; bool received_event = false; EXPECT_EQ(IPCZ_RESULT_OK, Trap(b, conditions, [&](const IpczTrapEvent& e) { - EXPECT_EQ(IPCZ_TRAP_PEER_CLOSED, e.condition_flags); + EXPECT_EQ(IPCZ_TRAP_PEER_CLOSED | IPCZ_TRAP_WITHIN_API_CALL, + e.condition_flags); received_event = true; })); @@ -96,7 +99,9 @@ TEST_F(TrapTest, MinLocalParcels) { }; bool received_event = false; EXPECT_EQ(IPCZ_RESULT_OK, Trap(b, conditions, [&](const IpczTrapEvent& e) { - EXPECT_EQ(IPCZ_TRAP_ABOVE_MIN_LOCAL_PARCELS, e.condition_flags); + EXPECT_EQ( + IPCZ_TRAP_ABOVE_MIN_LOCAL_PARCELS | IPCZ_TRAP_WITHIN_API_CALL, + e.condition_flags); received_event = true; })); @@ -118,7 +123,9 @@ TEST_F(TrapTest, MinLocalParcels) { received_event = false; conditions.min_local_parcels = 2; EXPECT_EQ(IPCZ_RESULT_OK, Trap(b, conditions, [&](const IpczTrapEvent& e) { - EXPECT_EQ(IPCZ_TRAP_ABOVE_MIN_LOCAL_PARCELS, e.condition_flags); + EXPECT_EQ( + IPCZ_TRAP_ABOVE_MIN_LOCAL_PARCELS | IPCZ_TRAP_WITHIN_API_CALL, + e.condition_flags); received_event = true; })); @@ -150,7 +157,9 @@ TEST_F(TrapTest, MinLocalBytes) { }; bool received_event = false; EXPECT_EQ(IPCZ_RESULT_OK, Trap(b, conditions, [&](const IpczTrapEvent& e) { - EXPECT_EQ(IPCZ_TRAP_ABOVE_MIN_LOCAL_BYTES, e.condition_flags); + EXPECT_EQ( + IPCZ_TRAP_ABOVE_MIN_LOCAL_BYTES | IPCZ_TRAP_WITHIN_API_CALL, + e.condition_flags); received_event = true; })); @@ -184,7 +193,8 @@ TEST_F(TrapTest, NewLocalParcel) { }; bool received_event = false; EXPECT_EQ(IPCZ_RESULT_OK, Trap(b, conditions, [&](const IpczTrapEvent& e) { - EXPECT_EQ(IPCZ_TRAP_NEW_LOCAL_PARCEL, e.condition_flags); + EXPECT_EQ(IPCZ_TRAP_NEW_LOCAL_PARCEL | IPCZ_TRAP_WITHIN_API_CALL, + e.condition_flags); received_event = true; })); @@ -194,7 +204,8 @@ TEST_F(TrapTest, NewLocalParcel) { received_event = false; EXPECT_EQ(IPCZ_RESULT_OK, Trap(b, conditions, [&](const IpczTrapEvent& e) { - EXPECT_EQ(IPCZ_TRAP_NEW_LOCAL_PARCEL, e.condition_flags); + EXPECT_EQ(IPCZ_TRAP_NEW_LOCAL_PARCEL | IPCZ_TRAP_WITHIN_API_CALL, + e.condition_flags); received_event = true; })); @@ -214,7 +225,8 @@ TEST_F(TrapTest, DeadPortal) { }; bool received_event = false; EXPECT_EQ(IPCZ_RESULT_OK, Trap(b, conditions, [&](const IpczTrapEvent& e) { - EXPECT_EQ(IPCZ_TRAP_DEAD, e.condition_flags); + EXPECT_EQ(IPCZ_TRAP_DEAD | IPCZ_TRAP_WITHIN_API_CALL, + e.condition_flags); received_event = true; })); @@ -255,19 +267,22 @@ TEST_F(TrapTest, MultipleTraps) { .flags = IPCZ_TRAP_NEW_LOCAL_PARCEL, }; EXPECT_EQ(IPCZ_RESULT_OK, Trap(b, conditions, [&](const IpczTrapEvent& e) { - EXPECT_EQ(IPCZ_TRAP_NEW_LOCAL_PARCEL, e.condition_flags); + EXPECT_EQ(IPCZ_TRAP_NEW_LOCAL_PARCEL | IPCZ_TRAP_WITHIN_API_CALL, + e.condition_flags); observed_parcel = true; })); conditions.flags = IPCZ_TRAP_PEER_CLOSED; EXPECT_EQ(IPCZ_RESULT_OK, Trap(b, conditions, [&](const IpczTrapEvent& e) { - EXPECT_EQ(IPCZ_TRAP_PEER_CLOSED, e.condition_flags); + EXPECT_EQ(IPCZ_TRAP_PEER_CLOSED | IPCZ_TRAP_WITHIN_API_CALL, + e.condition_flags); observed_closure = true; })); conditions.flags = IPCZ_TRAP_DEAD; EXPECT_EQ(IPCZ_RESULT_OK, Trap(b, conditions, [&](const IpczTrapEvent& e) { - EXPECT_EQ(IPCZ_TRAP_DEAD, e.condition_flags); + EXPECT_EQ(IPCZ_TRAP_DEAD | IPCZ_TRAP_WITHIN_API_CALL, + e.condition_flags); observed_death = true; })); diff --git a/chromium/third_party/ipcz/src/util/log.h b/chromium/third_party/ipcz/src/util/log.h index 3aba4e27f4e..52476a48286 100644 --- a/chromium/third_party/ipcz/src/util/log.h +++ b/chromium/third_party/ipcz/src/util/log.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/util/multi_mutex_lock.h b/chromium/third_party/ipcz/src/util/multi_mutex_lock.h index 2633dad16ee..6d303c6c54a 100644 --- a/chromium/third_party/ipcz/src/util/multi_mutex_lock.h +++ b/chromium/third_party/ipcz/src/util/multi_mutex_lock.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/util/ref_counted.cc b/chromium/third_party/ipcz/src/util/ref_counted.cc index 9c64c18bb30..40f508ee45b 100644 --- a/chromium/third_party/ipcz/src/util/ref_counted.cc +++ b/chromium/third_party/ipcz/src/util/ref_counted.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/util/ref_counted.h b/chromium/third_party/ipcz/src/util/ref_counted.h index d991b5049df..98921e960c7 100644 --- a/chromium/third_party/ipcz/src/util/ref_counted.h +++ b/chromium/third_party/ipcz/src/util/ref_counted.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/util/ref_counted_test.cc b/chromium/third_party/ipcz/src/util/ref_counted_test.cc index ffa3f93a86f..c589a2e586d 100644 --- a/chromium/third_party/ipcz/src/util/ref_counted_test.cc +++ b/chromium/third_party/ipcz/src/util/ref_counted_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/util/safe_math.h b/chromium/third_party/ipcz/src/util/safe_math.h index 5affb986e71..3c4c7b39768 100644 --- a/chromium/third_party/ipcz/src/util/safe_math.h +++ b/chromium/third_party/ipcz/src/util/safe_math.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,6 +6,7 @@ #define IPCZ_SRC_UTIL_SAFE_MATH_ #include <limits> +#include <type_traits> #include "third_party/abseil-cpp/absl/base/macros.h" #include "third_party/abseil-cpp/absl/base/optimization.h" @@ -22,6 +23,18 @@ constexpr Dst checked_cast(Src value) { return static_cast<Dst>(value); } +template <typename Dst, typename Src> +constexpr Dst saturated_cast(Src value) { + static_assert(std::is_unsigned_v<Src> && std::is_unsigned_v<Dst>, + "saturated_cast only supports unsigned types"); + constexpr Dst kMaxDst = std::numeric_limits<Dst>::max(); + constexpr Src kMaxSrc = std::numeric_limits<Src>::max(); + if (ABSL_PREDICT_TRUE(kMaxDst >= kMaxSrc || value <= kMaxDst)) { + return static_cast<Dst>(value); + } + return kMaxDst; +} + template <typename T> constexpr T CheckAdd(T a, T b) { T result; diff --git a/chromium/third_party/ipcz/src/util/safe_math_test.cc b/chromium/third_party/ipcz/src/util/safe_math_test.cc new file mode 100644 index 00000000000..0ccd2cd6dda --- /dev/null +++ b/chromium/third_party/ipcz/src/util/safe_math_test.cc @@ -0,0 +1,34 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "util/safe_math.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace ipcz { +namespace { + +template <typename T> +uint64_t AsUint64(T value) { + return static_cast<uint64_t>(value); +} + +TEST(SafeMathTest, SaturatedCast) { + const uint32_t kMaxUint32 = 0xffffffff; + const uint64_t kSmallUint64 = 0x12345678; + const uint64_t kLargeUint64 = 0x123456789abcull; + + // Casting to a smaller type within its range yields the same value. + EXPECT_EQ(kSmallUint64, AsUint64(saturated_cast<uint32_t>(kSmallUint64))); + + // Casting to a smaller type outside of its range yields the max value for + // the destination type. + EXPECT_EQ(kMaxUint32, saturated_cast<uint32_t>(kLargeUint64)); + + // Casting to a larger type always yields the same value. + EXPECT_EQ(AsUint64(kMaxUint32), saturated_cast<uint64_t>(kMaxUint32)); +} + +} // namespace +} // namespace ipcz diff --git a/chromium/third_party/ipcz/src/util/stack_trace.h b/chromium/third_party/ipcz/src/util/stack_trace.h index bcf462bd80e..55b6fd36ff1 100644 --- a/chromium/third_party/ipcz/src/util/stack_trace.h +++ b/chromium/third_party/ipcz/src/util/stack_trace.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/util/stack_trace_test.cc b/chromium/third_party/ipcz/src/util/stack_trace_test.cc index bce69ac7b4a..ea0d23aec34 100644 --- a/chromium/third_party/ipcz/src/util/stack_trace_test.cc +++ b/chromium/third_party/ipcz/src/util/stack_trace_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/third_party/ipcz/src/util/strong_alias.h b/chromium/third_party/ipcz/src/util/strong_alias.h index d6faf97005b..eeea5b89b12 100644 --- a/chromium/third_party/ipcz/src/util/strong_alias.h +++ b/chromium/third_party/ipcz/src/util/strong_alias.h @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. |