| Index: webrtc/p2p/base/p2ptransportchannel.cc
|
| diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc
|
| index 6bcf0d7d2bfb09e9548501dccfde4e2ba249a5bd..e1a955c69611f21321ea5fa35772666e7fa45aae 100644
|
| --- a/webrtc/p2p/base/p2ptransportchannel.cc
|
| +++ b/webrtc/p2p/base/p2ptransportchannel.cc
|
| @@ -11,6 +11,7 @@
|
| #include "webrtc/p2p/base/p2ptransportchannel.h"
|
|
|
| #include <algorithm>
|
| +#include <iterator>
|
| #include <set>
|
|
|
| #include "webrtc/api/peerconnectioninterface.h"
|
| @@ -66,6 +67,8 @@ namespace cricket {
|
| // well on a 28.8K modem, which is the slowest connection on which the voice
|
| // quality is reasonable at all.
|
| static const int PING_PACKET_SIZE = 60 * 8;
|
| +
|
| +// The next two ping intervals are at the channel level.
|
| // STRONG_PING_INTERVAL (480ms) is applied when the selected connection is both
|
| // writable and receiving.
|
| static const int STRONG_PING_INTERVAL = 1000 * PING_PACKET_SIZE / 1000;
|
| @@ -73,11 +76,13 @@ static const int STRONG_PING_INTERVAL = 1000 * PING_PACKET_SIZE / 1000;
|
| // not writable or not receiving.
|
| const int WEAK_PING_INTERVAL = 1000 * PING_PACKET_SIZE / 10000;
|
|
|
| -// Writable connections are pinged at a faster rate while stabilizing.
|
| -const int STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL = 900; // ms
|
| -
|
| -// Writable connections are pinged at a slower rate once stabilized.
|
| -const int STABLE_WRITABLE_CONNECTION_PING_INTERVAL = 2500; // ms
|
| +// The next two ping intervals are at the connection level.
|
| +// Writable connections are pinged at a faster rate while the connections are
|
| +// stabilizing or the channel is weak.
|
| +const int WEAK_OR_STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL = 900; // ms
|
| +// Writable connections are pinged at a slower rate once they are stabilized and
|
| +// the channel is strongly connected.
|
| +const int STRONG_AND_STABLE_WRITABLE_CONNECTION_PING_INTERVAL = 2500; // ms
|
|
|
| static const int MIN_CHECK_RECEIVING_INTERVAL = 50; // ms
|
|
|
| @@ -116,7 +121,7 @@ P2PTransportChannel::P2PTransportChannel(const std::string& transport_name,
|
| DEFAULT_BACKUP_CONNECTION_PING_INTERVAL,
|
| GATHER_ONCE /* continual_gathering_policy */,
|
| false /* prioritize_most_likely_candidate_pairs */,
|
| - STABLE_WRITABLE_CONNECTION_PING_INTERVAL,
|
| + STRONG_AND_STABLE_WRITABLE_CONNECTION_PING_INTERVAL,
|
| true /* presume_writable_when_fully_relayed */,
|
| DEFAULT_REGATHER_ON_FAILED_NETWORKS_INTERVAL,
|
| RECEIVING_SWITCHING_DELAY) {
|
| @@ -1243,6 +1248,7 @@ void P2PTransportChannel::SortConnectionsAndUpdateState() {
|
| // that amongst equal preference, writable connections, this will choose the
|
| // one whose estimated latency is lowest. So it is the only one that we
|
| // need to consider switching to.
|
| + // TODO(honghaiz): Don't sort; Just use std::max_element in the right places.
|
| std::stable_sort(connections_.begin(), connections_.end(),
|
| [this](const Connection* a, const Connection* b) {
|
| int cmp = CompareConnections(
|
| @@ -1305,6 +1311,37 @@ void P2PTransportChannel::SortConnectionsAndUpdateState() {
|
| MaybeStartPinging();
|
| }
|
|
|
| +std::map<rtc::Network*, Connection*>
|
| +P2PTransportChannel::GetBestConnectionByNetwork() const {
|
| + // |connections_| has been sorted, so the first one in the list on a given
|
| + // network is the best connection on the network, except that the selected
|
| + // connection is always the best connection on the network.
|
| + std::map<rtc::Network*, Connection*> best_connection_by_network;
|
| + if (selected_connection_) {
|
| + best_connection_by_network[selected_connection_->port()->Network()] =
|
| + selected_connection_;
|
| + }
|
| + // TODO(honghaiz): Need to update this if |connections_| are not sorted.
|
| + for (Connection* conn : connections_) {
|
| + rtc::Network* network = conn->port()->Network();
|
| + // This only inserts when the network does not exist in the map.
|
| + best_connection_by_network.insert(std::make_pair(network, conn));
|
| + }
|
| + return best_connection_by_network;
|
| +}
|
| +
|
| +std::vector<Connection*>
|
| +P2PTransportChannel::GetBestWritableConnectionPerNetwork() const {
|
| + std::vector<Connection*> connections;
|
| + for (auto kv : GetBestConnectionByNetwork()) {
|
| + Connection* conn = kv.second;
|
| + if (conn->writable() && conn->connected()) {
|
| + connections.push_back(conn);
|
| + }
|
| + }
|
| + return connections;
|
| +}
|
| +
|
| void P2PTransportChannel::PruneConnections() {
|
| // We can prune any connection for which there is a connected, writable
|
| // connection on the same network with better or equal priority. We leave
|
| @@ -1312,28 +1349,19 @@ void P2PTransportChannel::PruneConnections() {
|
| // which point, we would prune out the current selected connection). We leave
|
| // connections on other networks because they may not be using the same
|
| // resources and they may represent very distinct paths over which we can
|
| - // switch. If the |premier| connection is not connected, we may be
|
| - // reconnecting a TCP connection and temporarily do not prune connections in
|
| - // this network. See the big comment in CompareConnectionStates.
|
| -
|
| - // Get a list of the networks that we are using.
|
| - std::set<rtc::Network*> networks;
|
| - for (const Connection* conn : connections_) {
|
| - networks.insert(conn->port()->Network());
|
| - }
|
| - for (rtc::Network* network : networks) {
|
| - Connection* premier = GetBestConnectionOnNetwork(network);
|
| - // Do not prune connections if the current selected connection is weak on
|
| - // this network. Otherwise, it may delete connections prematurely.
|
| - if (!premier || premier->weak()) {
|
| - continue;
|
| - }
|
| -
|
| - for (Connection* conn : connections_) {
|
| - if ((conn != premier) && (conn->port()->Network() == network) &&
|
| - (CompareConnectionCandidates(premier, conn) >= 0)) {
|
| - conn->Prune();
|
| - }
|
| + // switch. If |best_conn_on_network| is not connected, we may be reconnecting
|
| + // a TCP connection and should not prune connections in this network.
|
| + // See the big comment in CompareConnectionStates.
|
| + auto best_connection_by_network = GetBestConnectionByNetwork();
|
| + for (Connection* conn : connections_) {
|
| + // Do not prune connections if the current best connection on this network
|
| + // is weak. Otherwise, it may delete connections prematurely.
|
| + Connection* best_conn_on_network =
|
| + best_connection_by_network[conn->port()->Network()];
|
| + if (best_conn_on_network && conn != best_conn_on_network &&
|
| + !best_conn_on_network->weak() &&
|
| + CompareConnectionCandidates(best_conn_on_network, conn) >= 0) {
|
| + conn->Prune();
|
| }
|
| }
|
| }
|
| @@ -1471,26 +1499,6 @@ bool P2PTransportChannel::ReadyToSend(Connection* connection) const {
|
| PresumedWritable(connection));
|
| }
|
|
|
| -// If we have a selected connection, return it, otherwise return top one in the
|
| -// list (later we will mark it best).
|
| -Connection* P2PTransportChannel::GetBestConnectionOnNetwork(
|
| - rtc::Network* network) const {
|
| - // If the selected connection is on this network, then it wins.
|
| - if (selected_connection_ &&
|
| - (selected_connection_->port()->Network() == network)) {
|
| - return selected_connection_;
|
| - }
|
| -
|
| - // Otherwise, we return the top-most in sorted order.
|
| - for (size_t i = 0; i < connections_.size(); ++i) {
|
| - if (connections_[i]->port()->Network() == network) {
|
| - return connections_[i];
|
| - }
|
| - }
|
| -
|
| - return NULL;
|
| -}
|
| -
|
| // Handle any queued up requests
|
| void P2PTransportChannel::OnMessage(rtc::Message *pmsg) {
|
| switch (pmsg->message_id) {
|
| @@ -1592,18 +1600,14 @@ bool P2PTransportChannel::IsPingable(const Connection* conn,
|
|
|
| // Ping writable, active connections if it's been long enough since the last
|
| // ping.
|
| - int ping_interval = CalculateActiveWritablePingInterval(conn, now);
|
| - return (now >= conn->last_ping_sent() + ping_interval);
|
| + return WritableConnectionPastPingInterval(conn, now);
|
| }
|
|
|
| -bool P2PTransportChannel::IsSelectedConnectionPingable(int64_t now) {
|
| - if (!selected_connection_ || !selected_connection_->connected() ||
|
| - !selected_connection_->writable()) {
|
| - return false;
|
| - }
|
| -
|
| - int interval = CalculateActiveWritablePingInterval(selected_connection_, now);
|
| - return selected_connection_->last_ping_sent() + interval <= now;
|
| +bool P2PTransportChannel::WritableConnectionPastPingInterval(
|
| + const Connection* conn,
|
| + int64_t now) const {
|
| + int interval = CalculateActiveWritablePingInterval(conn, now);
|
| + return conn->last_ping_sent() + interval <= now;
|
| }
|
|
|
| int P2PTransportChannel::CalculateActiveWritablePingInterval(
|
| @@ -1616,28 +1620,92 @@ int P2PTransportChannel::CalculateActiveWritablePingInterval(
|
| }
|
|
|
| int stable_interval = config_.stable_writable_connection_ping_interval;
|
| - int stablizing_interval =
|
| - std::min(stable_interval, STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL);
|
| -
|
| - return conn->stable(now) ? stable_interval : stablizing_interval;
|
| + int weak_or_stablizing_interval = std::min(
|
| + stable_interval, WEAK_OR_STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL);
|
| + // If the channel is weak or the connection is not stable yet, use the
|
| + // weak_or_stablizing_interval.
|
| + return (!weak() && conn->stable(now)) ? stable_interval
|
| + : weak_or_stablizing_interval;
|
| }
|
|
|
| -// Returns the next pingable connection to ping. This will be the oldest
|
| -// pingable connection unless we have a connected, writable connection that is
|
| -// past the writable ping interval. When reconnecting a TCP
|
| -// connection, the selected connection is disconnected, although still WRITABLE
|
| -// while reconnecting. The newly created connection should be selected as the
|
| -// ping target to become writable instead. See the big comment in
|
| -// CompareConnectionStates.
|
| +// Returns the next pingable connection to ping.
|
| Connection* P2PTransportChannel::FindNextPingableConnection() {
|
| int64_t now = rtc::TimeMillis();
|
| - Connection* conn_to_ping = nullptr;
|
| - if (IsSelectedConnectionPingable(now)) {
|
| - conn_to_ping = selected_connection_;
|
| - } else {
|
| - conn_to_ping = FindConnectionToPing(now);
|
| +
|
| + // Rule 1: Selected connection takes priority over non-selected ones.
|
| + if (selected_connection_ && selected_connection_->connected() &&
|
| + selected_connection_->writable() &&
|
| + WritableConnectionPastPingInterval(selected_connection_, now)) {
|
| + return selected_connection_;
|
| }
|
| - return conn_to_ping;
|
| +
|
| + // Rule 2: If the channel is weak, we need to find a new writable and
|
| + // receiving connection, probably on a different network. If there are lots of
|
| + // connections, it may take several seconds between two pings for every
|
| + // non-selected connection. This will cause the receiving state of those
|
| + // connections to be false, and thus they won't be selected. This is
|
| + // problematic for network fail-over. We want to make sure at least one
|
| + // connection per network is pinged frequently enough in order for it to be
|
| + // selectable. So we prioritize one connection per network.
|
| + // Rule 2.1: Among such connections, pick the one with the earliest
|
| + // last-ping-sent time.
|
| + if (weak()) {
|
| + auto selectable_connections = GetBestWritableConnectionPerNetwork();
|
| + std::vector<Connection*> pingable_selectable_connections;
|
| + std::copy_if(selectable_connections.begin(), selectable_connections.end(),
|
| + std::back_inserter(pingable_selectable_connections),
|
| + [this, now](Connection* conn) {
|
| + return WritableConnectionPastPingInterval(conn, now);
|
| + });
|
| + auto iter = std::min_element(pingable_selectable_connections.begin(),
|
| + pingable_selectable_connections.end(),
|
| + [](Connection* conn1, Connection* conn2) {
|
| + return conn1->last_ping_sent() <
|
| + conn2->last_ping_sent();
|
| + });
|
| + if (iter != pingable_selectable_connections.end()) {
|
| + return *iter;
|
| + }
|
| + }
|
| +
|
| + // Rule 3: Triggered checks have priority over non-triggered connections.
|
| + // Rule 3.1: Among triggered checks, oldest takes precedence.
|
| + Connection* oldest_triggered_check =
|
| + FindOldestConnectionNeedingTriggeredCheck(now);
|
| + if (oldest_triggered_check) {
|
| + return oldest_triggered_check;
|
| + }
|
| +
|
| + // Rule 4: Unpinged connections have priority over pinged ones.
|
| + RTC_CHECK(connections_.size() ==
|
| + pinged_connections_.size() + unpinged_connections_.size());
|
| + // If there are unpinged and pingable connections, only ping those.
|
| + // Otherwise, treat everything as unpinged.
|
| + // TODO(honghaiz): Instead of adding two separate vectors, we can add a state
|
| + // "pinged" to filter out unpinged connections.
|
| + if (std::find_if(unpinged_connections_.begin(), unpinged_connections_.end(),
|
| + [this, now](Connection* conn) {
|
| + return this->IsPingable(conn, now);
|
| + }) == unpinged_connections_.end()) {
|
| + unpinged_connections_.insert(pinged_connections_.begin(),
|
| + pinged_connections_.end());
|
| + pinged_connections_.clear();
|
| + }
|
| +
|
| + // Among un-pinged pingable connections, "more pingable" takes precedence.
|
| + std::vector<Connection*> pingable_connections;
|
| + std::copy_if(unpinged_connections_.begin(), unpinged_connections_.end(),
|
| + std::back_inserter(pingable_connections),
|
| + [this, now](Connection* conn) { return IsPingable(conn, now); });
|
| + auto iter =
|
| + std::max_element(pingable_connections.begin(), pingable_connections.end(),
|
| + [this, now](Connection* conn1, Connection* conn2) {
|
| + return MorePingable(conn1, conn2) == conn2;
|
| + });
|
| + if (iter != pingable_connections.end()) {
|
| + return *iter;
|
| + }
|
| + return nullptr;
|
| }
|
|
|
| void P2PTransportChannel::MarkConnectionPinged(Connection* conn) {
|
| @@ -1749,8 +1817,9 @@ void P2PTransportChannel::OnConnectionDestroyed(Connection* connection) {
|
| unpinged_connections_.erase(*iter);
|
| connections_.erase(iter);
|
|
|
| - LOG_J(LS_INFO, this) << "Removed connection ("
|
| - << static_cast<int>(connections_.size()) << " remaining)";
|
| + LOG_J(LS_INFO, this) << "Removed connection " << std::hex << connection
|
| + << std::dec << " (" << connections_.size()
|
| + << " remaining)";
|
|
|
| // If this is currently the selected connection, then we need to pick a new
|
| // one. The call to SortConnectionsAndUpdateState will pick a new one. It
|
| @@ -1878,7 +1947,7 @@ void P2PTransportChannel::OnReadyToSend(Connection* connection) {
|
|
|
| // Find "triggered checks". We ping first those connections that have
|
| // received a ping but have not sent a ping since receiving it
|
| -// (last_received_ping > last_sent_ping). But we shouldn't do
|
| +// (last_ping_received > last_ping_sent). But we shouldn't do
|
| // triggered checks if the connection is already writable.
|
| Connection* P2PTransportChannel::FindOldestConnectionNeedingTriggeredCheck(
|
| int64_t now) {
|
| @@ -1905,39 +1974,6 @@ Connection* P2PTransportChannel::FindOldestConnectionNeedingTriggeredCheck(
|
| return oldest_needing_triggered_check;
|
| }
|
|
|
| -Connection* P2PTransportChannel::FindConnectionToPing(int64_t now) {
|
| - RTC_CHECK(connections_.size() ==
|
| - pinged_connections_.size() + unpinged_connections_.size());
|
| -
|
| - // If there is nothing pingable in the |unpinged_connections_|, copy
|
| - // over from |pinged_connections_|. We do this here such that the
|
| - // new connection will take precedence.
|
| - if (std::find_if(unpinged_connections_.begin(), unpinged_connections_.end(),
|
| - [this, now](Connection* conn) {
|
| - return this->IsPingable(conn, now);
|
| - }) == unpinged_connections_.end()) {
|
| - unpinged_connections_.insert(pinged_connections_.begin(),
|
| - pinged_connections_.end());
|
| - pinged_connections_.clear();
|
| - }
|
| -
|
| - Connection* conn_to_ping = FindOldestConnectionNeedingTriggeredCheck(now);
|
| - if (conn_to_ping) {
|
| - return conn_to_ping;
|
| - }
|
| -
|
| - for (Connection* conn : unpinged_connections_) {
|
| - if (!IsPingable(conn, now)) {
|
| - continue;
|
| - }
|
| - if (!conn_to_ping ||
|
| - SelectMostPingableConnection(conn_to_ping, conn) == conn) {
|
| - conn_to_ping = conn;
|
| - }
|
| - }
|
| - return conn_to_ping;
|
| -}
|
| -
|
| Connection* P2PTransportChannel::MostLikelyToWork(Connection* conn1,
|
| Connection* conn2) {
|
| bool rr1 = IsRelayRelay(conn1);
|
| @@ -1969,9 +2005,8 @@ Connection* P2PTransportChannel::LeastRecentlyPinged(Connection* conn1,
|
| return nullptr;
|
| }
|
|
|
| -Connection* P2PTransportChannel::SelectMostPingableConnection(
|
| - Connection* conn1,
|
| - Connection* conn2) {
|
| +Connection* P2PTransportChannel::MorePingable(Connection* conn1,
|
| + Connection* conn2) {
|
| RTC_DCHECK(conn1 != conn2);
|
| if (config_.prioritize_most_likely_candidate_pairs) {
|
| Connection* most_likely_to_work_conn = MostLikelyToWork(conn1, conn2);
|
|
|