Fixing DCHECK in turnport.cc and doing some related cleanup.
Namely:
* Changing destruction_timestamp_ to rtc::Optional, instead of using 0
as a magic value.
* Adding some comments.
* Adding a log statement that would have helped debugging the issue
that hit this DCHECK.
* Getting rid of a 2-line method called in one place, which was not
really helping code readability.
Bug: None
Change-Id: I5fb1ce60edea29cab0c2a8c97e735f26c08aba62
Reviewed-on: https://webrtc-review.googlesource.com/7440
Commit-Queue: Taylor Brandstetter <deadbeef@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20196}
diff --git a/p2p/base/p2ptransportchannel.cc b/p2p/base/p2ptransportchannel.cc
index c02bac7..0b3b073 100644
--- a/p2p/base/p2ptransportchannel.cc
+++ b/p2p/base/p2ptransportchannel.cc
@@ -334,8 +334,9 @@
void P2PTransportChannel::SetRemoteIceParameters(
const IceParameters& ice_params) {
RTC_DCHECK(network_thread_ == rtc::Thread::Current());
- LOG(LS_INFO) << "Remote supports ICE renomination ? "
- << ice_params.renomination;
+ LOG(LS_INFO) << "Received remote ICE parameters: ufrag=" << ice_params.ufrag
+ << ", renomination "
+ << (ice_params.renomination ? "enabled" : "disabled");
IceParameters* current_ice = remote_ice();
if (!current_ice || *current_ice != ice_params) {
// Keep the ICE credentials so that newer connections
diff --git a/p2p/base/turnport.cc b/p2p/base/turnport.cc
index 0e68c46..bf1351b 100644
--- a/p2p/base/turnport.cc
+++ b/p2p/base/turnport.cc
@@ -13,6 +13,7 @@
#include <algorithm>
#include <functional>
+#include "api/optional.h"
#include "p2p/base/common.h"
#include "p2p/base/stun.h"
#include "rtc_base/asyncpacketsocket.h"
@@ -147,10 +148,15 @@
const rtc::SocketAddress& address() const { return ext_addr_; }
BindState state() const { return state_; }
- int64_t destruction_timestamp() { return destruction_timestamp_; }
- void set_destruction_timestamp(int64_t destruction_timestamp) {
- destruction_timestamp_ = destruction_timestamp;
+ // If the destruction timestamp is set, that means destruction has been
+ // scheduled (will occur TURN_PERMISSION_TIMEOUT after it's scheduled).
+ rtc::Optional<int64_t> destruction_timestamp() {
+ return destruction_timestamp_;
}
+ void set_destruction_timestamp(int64_t destruction_timestamp) {
+ destruction_timestamp_.emplace(destruction_timestamp);
+ }
+ void reset_destruction_timestamp() { destruction_timestamp_.reset(); }
// Helper methods to send permission and channel bind requests.
void SendCreatePermissionRequest(int delay);
@@ -174,11 +180,11 @@
int channel_id_;
rtc::SocketAddress ext_addr_;
BindState state_;
- // A non-zero value indicates that this entry is scheduled to be destroyed.
- // It is also used as an ID of the event scheduling. When the destruction
- // event actually fires, the TurnEntry will be destroyed only if the
- // timestamp here matches the one in the firing event.
- int64_t destruction_timestamp_ = 0;
+ // An unset value indicates that this entry is scheduled to be destroyed. It
+ // is also used as an ID of the event scheduling. When the destruction event
+ // actually fires, the TurnEntry will be destroyed only if the timestamp here
+ // matches the one in the firing event.
+ rtc::Optional<int64_t> destruction_timestamp_;
};
TurnPort::TurnPort(rtc::Thread* thread,
@@ -1040,9 +1046,21 @@
entry = new TurnEntry(this, next_channel_number_++, addr);
entries_.push_back(entry);
} else {
- // The channel binding request for the entry will be refreshed automatically
- // until the entry is destroyed.
- CancelEntryDestruction(entry);
+ if (entry->destruction_timestamp()) {
+ // Destruction should have only been scheduled (indicated by
+ // destruction_timestamp being set) if there were no connections using
+ // this address.
+ RTC_DCHECK(!GetConnection(addr));
+ // Resetting the destruction timestamp will ensure that any queued
+ // destruction tasks, when executed, will see that the timestamp doesn't
+ // match and do nothing. We do this because (currently) there's not a
+ // convenient way to cancel queued tasks.
+ entry->reset_destruction_timestamp();
+ } else {
+ // The only valid reason for destruction not being scheduled is that
+ // there's still one connection.
+ RTC_DCHECK(GetConnection(addr));
+ }
}
}
@@ -1057,8 +1075,12 @@
if (!EntryExists(entry)) {
return;
}
- bool cancelled = timestamp != entry->destruction_timestamp();
- if (!cancelled) {
+ // The destruction timestamp is used to manage pending destructions. Proceed
+ // with destruction if it's set, and matches the timestamp from the posted
+ // task. Note that CreateOrRefreshEntry will unset the timestamp, canceling
+ // destruction.
+ if (entry->destruction_timestamp() &&
+ timestamp == *entry->destruction_timestamp()) {
DestroyEntry(entry);
}
}
@@ -1073,7 +1095,7 @@
}
void TurnPort::ScheduleEntryDestruction(TurnEntry* entry) {
- RTC_DCHECK(entry->destruction_timestamp() == 0);
+ RTC_DCHECK(!entry->destruction_timestamp().has_value());
int64_t timestamp = rtc::TimeMillis();
entry->set_destruction_timestamp(timestamp);
invoker_.AsyncInvokeDelayed<void>(
@@ -1082,11 +1104,6 @@
TURN_PERMISSION_TIMEOUT);
}
-void TurnPort::CancelEntryDestruction(TurnEntry* entry) {
- RTC_DCHECK(entry->destruction_timestamp() != 0);
- entry->set_destruction_timestamp(0);
-}
-
bool TurnPort::SetEntryChannelId(const rtc::SocketAddress& address,
int channel_id) {
TurnEntry* entry = FindEntry(address);
diff --git a/p2p/base/turnport.h b/p2p/base/turnport.h
index 895055d..f891983 100644
--- a/p2p/base/turnport.h
+++ b/p2p/base/turnport.h
@@ -267,7 +267,6 @@
// in |entry|.
void DestroyEntryIfNotCancelled(TurnEntry* entry, int64_t timestamp);
void ScheduleEntryDestruction(TurnEntry* entry);
- void CancelEntryDestruction(TurnEntry* entry);
// Marks the connection with remote address |address| failed and
// pruned (a.k.a. write-timed-out). Returns true if a connection is found.
diff --git a/p2p/base/turnport_unittest.cc b/p2p/base/turnport_unittest.cc
index d6082ef..0bc4b31 100644
--- a/p2p/base/turnport_unittest.cc
+++ b/p2p/base/turnport_unittest.cc
@@ -1423,6 +1423,19 @@
TestTurnReleaseAllocation(PROTO_TLS);
}
+// Test that nothing bad happens if we try to create a connection to the same
+// remote address twice. Previously there was a bug that caused this to hit a
+// DCHECK.
+TEST_F(TurnPortTest, CanCreateTwoConnectionsToSameAddress) {
+ CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
+ PrepareTurnAndUdpPorts(PROTO_UDP);
+ Connection* conn1 = turn_port_->CreateConnection(udp_port_->Candidates()[0],
+ Port::ORIGIN_MESSAGE);
+ Connection* conn2 = turn_port_->CreateConnection(udp_port_->Candidates()[0],
+ Port::ORIGIN_MESSAGE);
+ EXPECT_NE(conn1, conn2);
+}
+
// This test verifies any FD's are not leaked after TurnPort is destroyed.
// https://code.google.com/p/webrtc/issues/detail?id=2651
#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)