Reland "sigslot: make PortInterface::SignalRoleConflict a CallbackList"
This is a reland of commit 1c0ba91c855f26868a6864ff048ca7ebf03ce724
PS1: original reland
PS2+: changes
Original change's description:
> sigslot: make PortInterface::SignalRoleConflict a CallbackLst
>
> Bug: webrtc:42222066
> Change-Id: I15dab9209a58fec8c42b4096dc5682e46a844e19
> Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/403220
> Reviewed-by: Evan Shrubsole <eshr@webrtc.org>
> Reviewed-by: Harald Alvestrand <hta@webrtc.org>
> Commit-Queue: Philipp Hancke <phancke@meta.com>
> Cr-Commit-Position: refs/heads/main@{#45406}
Bug: webrtc:42222066
Change-Id: Ia45c36f8346d8d16da72c05a3409a78942ba8fbd
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/406041
Reviewed-by: Tomas Gunnarsson <tommi@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Philipp Hancke <phancke@meta.com>
Cr-Commit-Position: refs/heads/main@{#45476}
diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn
index af0a607..67927db 100644
--- a/p2p/BUILD.gn
+++ b/p2p/BUILD.gn
@@ -650,6 +650,7 @@
"../rtc_base:socket_address",
"../rtc_base/network:sent_packet",
"../rtc_base/third_party/sigslot",
+ "//third_party/abseil-cpp/absl/functional:any_invocable",
"//third_party/abseil-cpp/absl/strings:string_view",
]
}
diff --git a/p2p/base/connection.cc b/p2p/base/connection.cc
index 662c990..ce58ee8 100644
--- a/p2p/base/connection.cc
+++ b/p2p/base/connection.cc
@@ -1603,7 +1603,7 @@
error_code == STUN_ERROR_UNAUTHORIZED) {
// Recoverable error, retry
} else if (error_code == STUN_ERROR_ROLE_CONFLICT) {
- port_->SignalRoleConflict(port_.get());
+ port_->NotifyRoleConflict();
} else if (request->msg()->type() == GOOG_PING_REQUEST) {
// Race, retry.
} else {
diff --git a/p2p/base/p2p_transport_channel.cc b/p2p/base/p2p_transport_channel.cc
index 6662732..9d6643b 100644
--- a/p2p/base/p2p_transport_channel.cc
+++ b/p2p/base/p2p_transport_channel.cc
@@ -911,15 +911,14 @@
ports_.push_back(port);
port->SignalUnknownAddress.connect(this,
&P2PTransportChannel::OnUnknownAddress);
+ port->SignalSentPacket.connect(this, &P2PTransportChannel::OnSentPacket);
+
port->SubscribePortDestroyed(
[this](PortInterface* port) { OnPortDestroyed(port); });
-
- port->SignalRoleConflict.connect(this, &P2PTransportChannel::OnRoleConflict);
- port->SignalSentPacket.connect(this, &P2PTransportChannel::OnSentPacket);
+ port->SubscribeRoleConflict([this]() { NotifyRoleConflict(); });
// Attempt to create a connection from this new port to all of the remote
// candidates that we were given so far.
-
std::vector<RemoteCandidate>::iterator iter;
for (iter = remote_candidates_.begin(); iter != remote_candidates_.end();
++iter) {
@@ -1123,7 +1122,7 @@
}
}
-void P2PTransportChannel::OnRoleConflict(PortInterface* /* port */) {
+void P2PTransportChannel::NotifyRoleConflict() {
SignalRoleConflict(this); // STUN ping will be sent when SetRole is called
// from Transport.
}
diff --git a/p2p/base/p2p_transport_channel.h b/p2p/base/p2p_transport_channel.h
index 6ca91e0..dd4cba3 100644
--- a/p2p/base/p2p_transport_channel.h
+++ b/p2p/base/p2p_transport_channel.h
@@ -362,7 +362,7 @@
// When pruning a port, move it from `ports_` to `pruned_ports_`.
// Returns true if the port is found and removed from `ports_`.
bool PrunePort(PortInterface* port);
- void OnRoleConflict(PortInterface* port);
+ void NotifyRoleConflict();
void OnConnectionStateChange(Connection* connection);
void OnReadPacket(Connection* connection, const ReceivedIpPacket& packet);
diff --git a/p2p/base/port.cc b/p2p/base/port.cc
index 30756bc..05d3b2b 100644
--- a/p2p/base/port.cc
+++ b/p2p/base/port.cc
@@ -131,6 +131,7 @@
tiebreaker_(0),
shared_socket_(shared_socket),
network_cost_(args.network->GetCost(env_.field_trials())),
+ role_conflict_callback_(nullptr),
weak_factory_(this) {
RTC_DCHECK_RUN_ON(thread_);
RTC_DCHECK(factory_ != nullptr);
@@ -701,7 +702,7 @@
case ICEROLE_CONTROLLING:
if (ICEROLE_CONTROLLING == remote_ice_role) {
if (remote_tiebreaker >= tiebreaker_) {
- SignalRoleConflict(this);
+ NotifyRoleConflict();
} else {
// Send Role Conflict (487) error response.
SendBindingErrorResponse(stun_msg, addr, STUN_ERROR_ROLE_CONFLICT,
@@ -713,7 +714,7 @@
case ICEROLE_CONTROLLED:
if (ICEROLE_CONTROLLED == remote_ice_role) {
if (remote_tiebreaker < tiebreaker_) {
- SignalRoleConflict(this);
+ NotifyRoleConflict();
} else {
// Send Role Conflict (487) error response.
SendBindingErrorResponse(stun_msg, addr, STUN_ERROR_ROLE_CONFLICT,
@@ -1040,4 +1041,22 @@
std::move(callback)(status);
}
+void Port::SubscribeRoleConflict(absl::AnyInvocable<void()> callback) {
+ RTC_DCHECK_RUN_ON(thread_);
+ RTC_DCHECK(callback);
+ RTC_DCHECK(!role_conflict_callback_);
+ RTC_DCHECK(SignalRoleConflict.is_empty());
+ role_conflict_callback_ = std::move(callback);
+}
+
+void Port::NotifyRoleConflict() {
+ RTC_DCHECK_RUN_ON(thread_);
+ if (role_conflict_callback_) {
+ RTC_DCHECK(SignalRoleConflict.is_empty());
+ role_conflict_callback_();
+ } else {
+ SignalRoleConflict(this);
+ }
+}
+
} // namespace webrtc
diff --git a/p2p/base/port.h b/p2p/base/port.h
index 015a2d3..833f118 100644
--- a/p2p/base/port.h
+++ b/p2p/base/port.h
@@ -392,6 +392,10 @@
void GetStunStats(std::optional<StunStats>* /* stats */) override {}
+ // Signals for ICE role conflicts.
+ void SubscribeRoleConflict(absl::AnyInvocable<void()> callback) override;
+ void NotifyRoleConflict() override;
+
protected:
void UpdateNetworkCost() override;
@@ -563,6 +567,8 @@
CallbackList<Port*, const Candidate&> candidate_ready_callback_list_
RTC_GUARDED_BY(thread_);
+ absl::AnyInvocable<void()> role_conflict_callback_ RTC_GUARDED_BY(thread_);
+
// Keep as the last member variable.
WeakPtrFactory<Port> weak_factory_ RTC_GUARDED_BY(thread_);
};
diff --git a/p2p/base/port_interface.h b/p2p/base/port_interface.h
index 1484a14..23f808c 100644
--- a/p2p/base/port_interface.h
+++ b/p2p/base/port_interface.h
@@ -19,6 +19,7 @@
#include <string>
#include <vector>
+#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "api/candidate.h"
#include "api/packet_socket_factory.h"
@@ -124,7 +125,10 @@
std::function<void(PortInterface*)> callback) = 0;
// Signaled when Port discovers ice role conflict with the peer.
+ // TODO: bugs.webrtc.org/42222066 - remove slot.
sigslot::signal1<PortInterface*> SignalRoleConflict;
+ virtual void SubscribeRoleConflict(absl::AnyInvocable<void()> callback) = 0;
+ virtual void NotifyRoleConflict() = 0;
// Normally, packets arrive through a connection (or they result signaling of
// unknown address). Calling this method turns off delivery of packets
diff --git a/p2p/base/port_unittest.cc b/p2p/base/port_unittest.cc
index 60aa47d..b4e0cec 100644
--- a/p2p/base/port_unittest.cc
+++ b/p2p/base/port_unittest.cc
@@ -879,7 +879,7 @@
.ice_username_fragment = username,
.ice_password = password};
auto port = std::make_unique<TestPort>(args, 0, 0);
- port->SignalRoleConflict.connect(this, &PortTest::OnRoleConflict);
+ port->SubscribeRoleConflict([this]() { OnRoleConflict(); });
return port;
}
std::unique_ptr<TestPort> CreateTestPort(const SocketAddress& addr,
@@ -903,11 +903,21 @@
.ice_username_fragment = username,
.ice_password = password};
auto port = std::make_unique<TestPort>(args, 0, 0);
- port->SignalRoleConflict.connect(this, &PortTest::OnRoleConflict);
+ port->SubscribeRoleConflict([this]() { OnRoleConflict(); });
return port;
}
- void OnRoleConflict(PortInterface* port) { role_conflict_ = true; }
+ std::unique_ptr<TestPort> CreateRawTestPort() {
+ Port::PortParametersRef args = {
+ .env = env_,
+ .network_thread = &main_,
+ .socket_factory = &socket_factory_,
+ .network = MakeNetwork(kLocalAddr1),
+ };
+ return std::make_unique<TestPort>(args, 0, 0);
+ }
+
+ void OnRoleConflict() { role_conflict_ = true; }
bool role_conflict() const { return role_conflict_; }
void ConnectToSignalDestroyed(PortInterface* port) {
@@ -4026,6 +4036,32 @@
EXPECT_TRUE(port->GetConnection(address) != nullptr);
}
+#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+class DeathChannel : public sigslot::has_slots<> {
+ public:
+ explicit DeathChannel(std::unique_ptr<Port> port) : port_(std::move(port)) {}
+ void IgnoredSlot(PortInterface* /* port */) {}
+ void AddSignal() {
+ port_->SignalRoleConflict.connect(this, &DeathChannel::IgnoredSlot);
+ }
+ void AddCallback() {
+ port_->SubscribeRoleConflict([]() {});
+ }
+
+ private:
+ std::unique_ptr<Port> port_;
+};
+
+class PortDeathTest : public PortTest {};
+
+TEST_F(PortDeathTest, AddSignalThenCallback) {
+ DeathChannel dc(CreateRawTestPort());
+ dc.AddSignal();
+ EXPECT_DEATH(dc.AddCallback(), "");
+}
+
+#endif // RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+
// TODO(webrtc:11463) : Move Connection tests into separate unit test
// splitting out shared test code as needed.