Index: webrtc/p2p/base/p2ptransportchannel.cc |
diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc |
index fc1f0a4e6769ee96ce28f851d0f7cbc368fbf69c..ad09cc12582357f2c53a1516721618508fd29d3b 100644 |
--- a/webrtc/p2p/base/p2ptransportchannel.cc |
+++ b/webrtc/p2p/base/p2ptransportchannel.cc |
@@ -27,10 +27,15 @@ |
namespace { |
// messages for queuing up work for ourselves |
-enum { MSG_SORT = 1, MSG_CHECK_AND_PING }; |
+enum { |
+ MSG_SORT = 1, |
+ MSG_CHECK_AND_PING, |
+ MSG_CHECK_RESTORE_BACKUP_CONNECTION, |
pthatcher1
2016/06/07 18:54:33
It seems like a better name would be MSG_REGATHER_
honghaiz3
2016/06/29 02:48:22
MSG_REGATHER_ON_FAILED_NETWORKS better?
|
+ MSG_SIGNAL_CANDIDATES_REMOVED |
pthatcher1
2016/06/07 18:54:33
Why does this require a thread post? To coalesce
honghaiz3
2016/06/29 02:48:22
Yes. We can merge multiple candidate removals into
|
+}; |
// The minimum improvement in RTT that justifies a switch. |
-static const double kMinImprovement = 10; |
+const double kMinImprovement = 10; |
bool IsRelayRelay(cricket::Connection* conn) { |
return conn->local_candidate().type() == cricket::RELAY_PORT_TYPE && |
@@ -224,6 +229,10 @@ static const int MAX_CURRENT_STRONG_INTERVAL = 900; // ms |
static const int MIN_CHECK_RECEIVING_INTERVAL = 50; // ms |
+// We periodically check and restore backup connections on existing networks |
+// that do not have any connection at this interval. |
+static const int CHECK_RESTORE_BACKUP_CONNECTION_INTERVAL = 5 * 60 * 1000; |
pthatcher1
2016/06/07 18:54:33
I think a better name would be DEFAULT_REGATHER_FA
|
+ |
P2PTransportChannel::P2PTransportChannel(const std::string& transport_name, |
int component, |
P2PTransport* transport, |
@@ -246,6 +255,8 @@ P2PTransportChannel::P2PTransportChannel(const std::string& transport_name, |
tiebreaker_(0), |
gathering_state_(kIceGatheringNew), |
check_receiving_interval_(MIN_CHECK_RECEIVING_INTERVAL * 5), |
+ check_restore_backup_connection_interval_( |
+ CHECK_RESTORE_BACKUP_CONNECTION_INTERVAL), |
pthatcher1
2016/06/07 18:54:33
Can you put this in config_ instead of as its own
|
config_(MIN_CHECK_RECEIVING_INTERVAL * 50 /* receiving_timeout */, |
0 /* backup_connection_ping_interval */, |
false /* gather_continually */, |
@@ -280,6 +291,10 @@ void P2PTransportChannel::AddAllocatorSession( |
// created by this new session because these are replacing those of the |
// previous sessions. |
ports_.clear(); |
+ // We do not need to signal the candidate removals because a new allocator |
+ // session indicates an ICE restart, which will remove all old remote |
+ // candidates on the other side. |
+ candidates_removed_.clear(); |
pthatcher1
2016/06/07 18:54:33
This feels like implicit magic to me. Could we in
|
allocator_sessions_.push_back(std::move(session)); |
} |
@@ -340,7 +355,10 @@ TransportChannelState P2PTransportChannel::ComputeState() const { |
active_connections.push_back(connection); |
} |
} |
- if (active_connections.empty()) { |
+ // TODO(honghaiz): We should expose one transport channel state to the |
+ // the external world (e.g. TransportController), so that we won't need to |
+ // make both writable and channel state depend on IsRecoveringConnectivity. |
+ if (active_connections.empty() && !IsRecoveringConnectivity()) { |
pthatcher1
2016/06/07 18:54:33
I don't like the look of the complexity added to h
|
return TransportChannelState::STATE_FAILED; |
} |
@@ -456,6 +474,8 @@ void P2PTransportChannel::Connect() { |
// Start checking and pinging as the ports come in. |
thread()->Post(this, MSG_CHECK_AND_PING); |
+ thread()->PostDelayed(check_restore_backup_connection_interval_, this, |
+ MSG_CHECK_RESTORE_BACKUP_CONNECTION); |
} |
void P2PTransportChannel::MaybeStartGathering() { |
@@ -1237,7 +1257,11 @@ void P2PTransportChannel::UpdateState() { |
SignalStateChanged(this); |
} |
- bool writable = best_connection_ && best_connection_->writable(); |
+ // If it is recovering connectivity, it is considered writable to the |
+ // external world. |
+ // TODO(honghaiz): Expose the recovering state to the application. |
+ bool writable = (best_connection_ && best_connection_->writable()) || |
+ IsRecoveringConnectivity(); |
set_writable(writable); |
bool receiving = false; |
@@ -1306,6 +1330,17 @@ void P2PTransportChannel::OnMessage(rtc::Message *pmsg) { |
case MSG_CHECK_AND_PING: |
OnCheckAndPing(); |
break; |
+ case MSG_CHECK_RESTORE_BACKUP_CONNECTION: |
+ OnCheckAndRestoreBackupConnection(); |
+ break; |
+ case MSG_SIGNAL_CANDIDATES_REMOVED: |
+ if (!candidates_removed_.empty()) { |
+ LOG(LS_INFO) << "Signal removing " << candidates_removed_.size() |
+ << " candidates"; |
+ SignalCandidatesRemoved(this, candidates_removed_); |
+ candidates_removed_.clear(); |
+ } |
+ break; |
default: |
ASSERT(false); |
break; |
@@ -1344,6 +1379,41 @@ void P2PTransportChannel::OnCheckAndPing() { |
thread()->PostDelayed(delay, this, MSG_CHECK_AND_PING); |
} |
+void P2PTransportChannel::OnCheckAndRestoreBackupConnection() { |
+ if (!config_.gather_continually || !allocator_->network_manager()) { |
+ return; |
+ } |
+ rtc::NetworkManager::NetworkList networks; |
+ allocator_->network_manager()->GetNetworks(&networks); |
pthatcher1
2016/06/07 18:54:33
I'm not a fan of reaching into the allocator like
|
+ // Filter out loopback networks. |
+ auto network_end = std::remove_if( |
+ networks.begin(), networks.end(), [](rtc::Network* network) { |
+ return network->type() == rtc::ADAPTER_TYPE_LOOPBACK; |
+ }); |
+ |
+ // Filter out networks having connections in non-failed state. |
+ for (Connection* conn : connections_) { |
+ if (conn->state() == Connection::STATE_FAILED) { |
+ continue; |
+ } |
+ const std::string& interface_name = conn->port()->Network()->name(); |
+ network_end = std::remove_if(networks.begin(), network_end, |
+ [interface_name](rtc::Network* net) { |
+ return net->name() == interface_name; |
+ }); |
+ if (networks.begin() == network_end) { |
+ break; |
+ } |
+ } |
+ networks.erase(network_end, networks.end()); |
pthatcher1
2016/06/07 18:54:33
This logic seems a bit complicated. Would it mak
honghaiz3
2016/06/29 02:48:23
Done.
|
+ if (!networks.empty()) { |
+ StartRegathering(&networks); |
+ } |
pthatcher1
2016/06/07 18:54:33
It seems like a lot of this logic should be in a s
honghaiz3
2016/06/29 02:48:23
Done.
|
+ |
+ thread()->PostDelayed(check_restore_backup_connection_interval_, this, |
+ MSG_CHECK_RESTORE_BACKUP_CONNECTION); |
+} |
+ |
// A connection is considered a backup connection if the channel state |
// is completed, the connection is not the best connection and it is active. |
bool P2PTransportChannel::IsBackupConnection(Connection* conn) const { |
@@ -1411,6 +1481,14 @@ void P2PTransportChannel::MarkConnectionPinged(Connection* conn) { |
} |
} |
+void P2PTransportChannel::set_check_restore_backup_connection_interval( |
pthatcher1
2016/06/07 18:54:33
It sounds like this method should be called Regath
|
+ int interval) { |
+ check_restore_backup_connection_interval_ = interval; |
+ thread()->Clear(this, MSG_CHECK_RESTORE_BACKUP_CONNECTION, nullptr); |
+ thread()->PostDelayed(check_restore_backup_connection_interval_, this, |
+ MSG_CHECK_RESTORE_BACKUP_CONNECTION); |
+} |
+ |
// Apart from sending ping from |conn| this method also updates |
// |use_candidate_attr| flag. The criteria to update this flag is |
// explained below. |
@@ -1462,6 +1540,9 @@ void P2PTransportChannel::OnConnectionStateChange(Connection* connection) { |
bool latest_generation = connection->local_candidate().generation() >= |
allocator_session()->generation(); |
if (strongly_connected && latest_generation) { |
+ if (recovering_start_time_) { |
+ recovering_start_time_ = 0; |
+ } |
pthatcher1
2016/06/07 18:54:33
***
|
MaybeStopPortAllocatorSessions(); |
} |
@@ -1500,6 +1581,21 @@ void P2PTransportChannel::OnConnectionDestroyed(Connection* connection) { |
// and re-choose a best assuming that there was no best connection. |
if (best_connection_ == connection) { |
LOG(LS_INFO) << "Best connection destroyed. Will choose a new one."; |
+ // When continual gathering is enabled, we will attempt re-gathering only |
+ // if the best connection was removed and it was writable. If it was not |
+ // writable, most likely it is in the process of gathering on the current |
+ // session. |
+ if (config_.gather_continually && writable()) { |
+ bool all_connections_failed = std::all_of( |
+ connections_.begin(), connections_.end(), [](Connection* conn) { |
+ return conn->state() == Connection::STATE_FAILED; |
+ }); |
+ // Start regathering on all active networks if all connections failed. |
+ if (all_connections_failed && !IsRecoveringConnectivity()) { |
+ recovering_start_time_ = rtc::TimeMillis(); |
pthatcher1
2016/06/07 18:54:33
Should this be last_regather_time_, and should it
honghaiz3
2016/06/29 02:48:23
Done.
|
+ StartRegathering(nullptr); |
+ } |
pthatcher1
2016/06/07 18:54:33
Can we put this in a separate methods called Maybe
honghaiz3
2016/06/29 02:48:23
Removed.
|
+ } |
SwitchBestConnectionTo(NULL); |
RequestSort(); |
} |
@@ -1524,7 +1620,7 @@ void P2PTransportChannel::OnPortDestroyed(PortInterface* port) { |
void P2PTransportChannel::OnPortNetworkInactive(PortInterface* port) { |
// If it does not gather continually, the port will be removed from the list |
- // when ICE restarts. |
+ // when ICE restarts, so don't need to remove it here. |
if (!config_.gather_continually) { |
return; |
} |
@@ -1536,11 +1632,45 @@ void P2PTransportChannel::OnPortNetworkInactive(PortInterface* port) { |
ports_.erase(it); |
LOG(INFO) << "Removed port due to inactive networks: " << ports_.size() |
<< " remaining"; |
- std::vector<Candidate> candidates = port->Candidates(); |
- for (Candidate& candidate : candidates) { |
+ port->FailAndDestroyConnections(); |
pthatcher1
2016/06/07 18:54:33
It feels like this should be Close(), which should
|
+ |
+ std::vector<Candidate> candidates = |
+ allocator_session()->ReadyCandidates(port); |
+ for (Candidate candidate : candidates) { |
candidate.set_transport_name(transport_name()); |
+ candidates_removed_.push_back(candidate); |
} |
- SignalCandidatesRemoved(this, candidates); |
+ thread()->Post(this, MSG_SIGNAL_CANDIDATES_REMOVED); |
pthatcher1
2016/06/07 18:54:33
Why does this need a thread hop?
honghaiz3
2016/06/29 02:48:23
We can combine multiple candidate removal signals
|
+} |
+ |
+void P2PTransportChannel::StartRegathering( |
+ std::vector<rtc::Network*>* networks) { |
+ std::string log_msg; |
+ std::vector<PortInterface*> ports_to_remove; |
+ if (networks == nullptr) { |
+ log_msg = "Regathering on all networks"; |
+ ports_to_remove = ports_; |
+ } else { |
+ log_msg = "Regathering on networks:"; |
+ for (rtc::Network* network : *networks) { |
+ log_msg += " " + network->ToString(); |
+ } |
+ // Remove all ports using |networks|. |
+ for (PortInterface* port : ports_) { |
+ if (std::find(networks->begin(), networks->end(), port->Network()) != |
+ networks->end()) { |
+ ports_to_remove.push_back(port); |
+ } |
+ } |
+ } |
+ LOG(LS_INFO) << log_msg; |
+ for (PortInterface* port : ports_to_remove) { |
+ // Remove the port and signal the other side to remove the respective remote |
+ // candidates. |
+ OnPortNetworkInactive(port); |
pthatcher1
2016/06/07 18:54:33
I feel like we're confounding two things by trigge
|
+ } |
+ |
+ allocator_session()->GetPortsOnNetworks(networks); |
} |
// We data is available, let listeners know |