Chromium Code Reviews| Index: webrtc/p2p/base/p2ptransportchannel.cc |
| diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc |
| index ee9906053cd772766821e491b4a42c826a211a78..18cfd2d9f4cc8a29f930c25023e69cff786a062d 100644 |
| --- a/webrtc/p2p/base/p2ptransportchannel.cc |
| +++ b/webrtc/p2p/base/p2ptransportchannel.cc |
| @@ -10,6 +10,7 @@ |
| #include "webrtc/p2p/base/p2ptransportchannel.h" |
| +#include <algorithm> |
| #include <set> |
| #include "webrtc/p2p/base/common.h" |
| #include "webrtc/p2p/base/relayport.h" // For RELAY_PORT_TYPE. |
| @@ -22,11 +23,7 @@ |
| namespace { |
| // messages for queuing up work for ourselves |
| -enum { |
| - MSG_SORT = 1, |
| - MSG_PING, |
| - MSG_CHECK_RECEIVING |
| -}; |
| +enum { MSG_SORT = 1, MSG_CHECK_AND_PING }; |
| // When the socket is unwritable, we will use 10 Kbps (ignoring IP+UDP headers) |
| // for pinging. When the socket is writable, we will use only 1 Kbps because |
| @@ -34,12 +31,17 @@ enum { |
| // well on a 28.8K modem, which is the slowest connection on which the voice |
| // quality is reasonable at all. |
| static const uint32 PING_PACKET_SIZE = 60 * 8; |
| -static const uint32 WRITABLE_DELAY = 1000 * PING_PACKET_SIZE / 1000; // 480ms |
| -static const uint32 UNWRITABLE_DELAY = 1000 * PING_PACKET_SIZE / 10000; // 50ms |
| - |
| -// If there is a current writable connection, then we will also try hard to |
| -// make sure it is pinged at this rate. |
| -static const uint32 MAX_CURRENT_WRITABLE_DELAY = 900; // 2*WRITABLE_DELAY - bit |
| +// STRONG_PING_DELAY (480ms) is applied when the best connection is both |
| +// writable and receiving. |
| +static const uint32 STRONG_PING_DELAY = 1000 * PING_PACKET_SIZE / 1000; |
| +// WEAK_PING_DELAY (48ms) is applied when the best connection is either not |
| +// writable or not receiving. |
| +static const uint32 WEAK_PING_DELAY = 1000 * PING_PACKET_SIZE / 10000; |
| + |
| +// If the current best connection is both writable and receiving, then we will |
| +// also try hard to make sure it is pinged at this rate (a little less than |
| +// 2 * STRONG_PING_DELAY). |
| +static const uint32 MAX_CURRENT_STRONG_DELAY = 900; |
| static const int MIN_CHECK_RECEIVING_DELAY = 50; // ms |
| @@ -70,15 +72,20 @@ int CompareConnectionCandidates(cricket::Connection* a, |
| (b->remote_candidate().generation() + b->port()->generation()); |
| } |
| -// Compare two connections based on their connected state, writability and |
| -// static preferences. |
| -int CompareConnections(cricket::Connection *a, cricket::Connection *b) { |
| +// Compare two connections based on their writing, receiving, and connected |
| +// states. |
| +int CompareConnectionStates(cricket::Connection* a, cricket::Connection* b) { |
| // Sort based on write-state. Better states have lower values. |
| if (a->write_state() < b->write_state()) |
| return 1; |
| if (a->write_state() > b->write_state()) |
| return -1; |
| + if (a->receiving() && !b->receiving()) |
| + return 1; |
| + if (!a->receiving() && b->receiving()) |
| + return -1; |
|
pthatcher1
2015/09/22 23:41:46
Can you put a comment along the lines of "We prefe
honghaiz3
2015/09/23 03:16:10
Done. I do not see that the pinging order is relat
|
| + |
| // WARNING: Some complexity here about TCP reconnecting. |
| // When a TCP connection fails because of a TCP socket disconnecting, the |
| // active side of the connection will attempt to reconnect for 5 seconds while |
| @@ -110,7 +117,14 @@ int CompareConnections(cricket::Connection *a, cricket::Connection *b) { |
| return -1; |
| } |
| } |
| + return 0; |
| +} |
| +int CompareConnections(cricket::Connection* a, cricket::Connection* b) { |
| + int state_cmp = CompareConnectionStates(a, b); |
| + if (state_cmp != 0) { |
| + return state_cmp; |
| + } |
| // Compare the candidate information. |
| return CompareConnectionCandidates(a, b); |
| } |
| @@ -148,19 +162,33 @@ class ConnectionCompare { |
| }; |
| // Determines whether we should switch between two connections, based first on |
| -// static preferences and then (if those are equal) on latency estimates. |
| -bool ShouldSwitch(cricket::Connection* a_conn, cricket::Connection* b_conn) { |
| +// connection states, static preferences, and then (if those are equal) on |
| +// latency estimates. |
| +bool ShouldSwitch(cricket::Connection* a_conn, |
| + cricket::Connection* b_conn, |
| + cricket::IceRole ice_role) { |
| if (a_conn == b_conn) |
| return false; |
| if (!a_conn || !b_conn) // don't think the latter should happen |
| return true; |
| - int prefs_cmp = CompareConnections(a_conn, b_conn); |
| - if (prefs_cmp < 0) |
| - return true; |
| - if (prefs_cmp > 0) |
| + // If the connection states (WRITE/RECEIVING/CONNECT) are different, we should |
| + // switch regardless of the nominated state. Otherwise, do not switch if |
| + // |a_conn| is nominated on the controlled side. |
|
pthatcher1
2015/09/22 23:41:46
Can you reword this as something like "We prefer t
honghaiz3
2015/09/23 03:16:10
Done.
|
| + int state_cmp = CompareConnectionStates(a_conn, b_conn); |
| + if (state_cmp != 0) { |
| + return state_cmp < 0; |
| + } |
| + if (ice_role == cricket::ICEROLE_CONTROLLED && a_conn->nominated()) { |
| + LOG(LS_VERBOSE) << "Controlled side did not switch due to nominated status"; |
| return false; |
| + } |
| + |
| + int prefs_cmp = CompareConnectionCandidates(a_conn, b_conn); |
| + if (prefs_cmp != 0) { |
| + return prefs_cmp < 0; |
| + } |
| return b_conn->rtt() <= a_conn->rtt() + kMinImprovement; |
| } |
| @@ -172,25 +200,24 @@ namespace cricket { |
| P2PTransportChannel::P2PTransportChannel(const std::string& content_name, |
| int component, |
| P2PTransport* transport, |
| - PortAllocator *allocator) : |
| - TransportChannelImpl(content_name, component), |
| - transport_(transport), |
| - allocator_(allocator), |
| - worker_thread_(rtc::Thread::Current()), |
| - incoming_only_(false), |
| - waiting_for_signaling_(false), |
| - error_(0), |
| - best_connection_(NULL), |
| - pending_best_connection_(NULL), |
| - sort_dirty_(false), |
| - was_writable_(false), |
| - remote_ice_mode_(ICEMODE_FULL), |
| - ice_role_(ICEROLE_UNKNOWN), |
| - tiebreaker_(0), |
| - remote_candidate_generation_(0), |
| - check_receiving_delay_(MIN_CHECK_RECEIVING_DELAY * 5), |
| - receiving_timeout_(MIN_CHECK_RECEIVING_DELAY * 50) { |
| -} |
| + PortAllocator* allocator) |
| + : TransportChannelImpl(content_name, component), |
| + transport_(transport), |
| + allocator_(allocator), |
| + worker_thread_(rtc::Thread::Current()), |
| + incoming_only_(false), |
| + waiting_for_signaling_(false), |
| + error_(0), |
| + best_connection_(nullptr), |
| + pending_best_connection_(nullptr), |
| + sort_dirty_(false), |
| + was_writable_(false), |
| + remote_ice_mode_(ICEMODE_FULL), |
| + ice_role_(ICEROLE_UNKNOWN), |
| + tiebreaker_(0), |
| + remote_candidate_generation_(0), |
| + check_receiving_delay_(MIN_CHECK_RECEIVING_DELAY * 5), |
| + receiving_timeout_(MIN_CHECK_RECEIVING_DELAY * 50) {} |
| P2PTransportChannel::~P2PTransportChannel() { |
| ASSERT(worker_thread_ == rtc::Thread::Current()); |
| @@ -362,11 +389,8 @@ void P2PTransportChannel::Connect() { |
| // Kick off an allocator session |
| Allocate(); |
| - // Start pinging as the ports come in. |
| - thread()->Post(this, MSG_PING); |
| - |
| - thread()->PostDelayed( |
| - check_receiving_delay_, this, MSG_CHECK_RECEIVING); |
| + // Start checking and pinging as the ports come in. |
| + thread()->Post(this, MSG_CHECK_AND_PING); |
| } |
| // A new port is available, attempt to make connections for it |
| @@ -925,7 +949,8 @@ void P2PTransportChannel::SortConnections() { |
| // need to consider switching to. |
| ConnectionCompare cmp; |
| std::stable_sort(connections_.begin(), connections_.end(), cmp); |
| - LOG(LS_VERBOSE) << "Sorting available connections:"; |
| + LOG(LS_VERBOSE) << "Sorting " << connections_.size() |
| + << " available connections:"; |
| for (uint32 i = 0; i < connections_.size(); ++i) { |
| LOG(LS_VERBOSE) << connections_[i]->ToString(); |
| } |
| @@ -936,10 +961,7 @@ void P2PTransportChannel::SortConnections() { |
| // If necessary, switch to the new choice. |
| // Note that |top_connection| doesn't have to be writable to become the best |
| // connection although it will have higher priority if it is writable. |
| - // The controlled side can switch the best connection only if the current |
| - // |best connection_| has not been nominated by the controlling side yet. |
| - if ((ice_role_ == ICEROLE_CONTROLLING || !best_nominated_connection()) && |
| - ShouldSwitch(best_connection_, top_connection)) { |
| + if (ShouldSwitch(best_connection_, top_connection, ice_role_)) { |
| LOG(LS_INFO) << "Switching best connection: " << top_connection->ToString(); |
| SwitchBestConnectionTo(top_connection); |
| } |
| @@ -1025,7 +1047,6 @@ void P2PTransportChannel::SwitchBestConnectionTo(Connection* conn) { |
| LOG_J(LS_INFO, this) << "New best connection: " |
| << best_connection_->ToString(); |
| SignalRouteChange(this, best_connection_->remote_candidate()); |
| - set_receiving(best_connection_->receiving()); |
| } else { |
| LOG_J(LS_INFO, this) << "No best connection"; |
| } |
| @@ -1034,15 +1055,19 @@ void P2PTransportChannel::SwitchBestConnectionTo(Connection* conn) { |
| void P2PTransportChannel::UpdateChannelState() { |
| // The Handle* functions already set the writable state. We'll just double- |
| // check it here. |
| - bool writable = ((best_connection_ != NULL) && |
| - (best_connection_->write_state() == |
| - Connection::STATE_WRITABLE)); |
| + bool writable = best_connection_ && best_connection_->writable(); |
| ASSERT(writable == this->writable()); |
| if (writable != this->writable()) |
| LOG(LS_ERROR) << "UpdateChannelState: writable state mismatch"; |
| - // TODO(honghaiz): The channel receiving state is set in OnCheckReceiving. |
| - // Will revisit in a subsequent code change. |
| + bool receiving = false; |
| + for (const Connection* connection : connections_) { |
| + if (connection->receiving()) { |
| + receiving = true; |
| + break; |
| + } |
| + } |
|
pthatcher1
2015/09/22 23:41:46
I thought this was more readable using std::any_of
honghaiz3
2015/09/23 03:16:10
Using that caused the compiling to break on some M
|
| + set_receiving(receiving); |
| } |
| // We checked the status of our connections and we had at least one that |
| @@ -1075,6 +1100,10 @@ void P2PTransportChannel::HandleAllTimedOut() { |
| HandleNotWritable(); |
| } |
| +bool P2PTransportChannel::Weak() const { |
| + return !(best_connection_ && best_connection_->receiving() && writable()); |
|
pthatcher1
2015/09/22 23:41:46
Isn't the same as "!(best_connection_ && best_conn
honghaiz3
2015/09/23 03:16:10
Done.
|
| +} |
| + |
| // If we have a best connection, return it, otherwise return top one in the |
| // list (later we will mark it best). |
| Connection* P2PTransportChannel::GetBestConnectionOnNetwork( |
| @@ -1098,11 +1127,8 @@ void P2PTransportChannel::OnMessage(rtc::Message *pmsg) { |
| case MSG_SORT: |
| OnSort(); |
| break; |
| - case MSG_PING: |
| - OnPing(); |
| - break; |
| - case MSG_CHECK_RECEIVING: |
| - OnCheckReceiving(); |
| + case MSG_CHECK_AND_PING: |
| + OnCheckAndPing(); |
| break; |
| default: |
| ASSERT(false); |
| @@ -1116,30 +1142,22 @@ void P2PTransportChannel::OnSort() { |
| SortConnections(); |
| } |
| -// Handle queued up ping request |
| -void P2PTransportChannel::OnPing() { |
| +// Handle queued up check-and-ping request |
| +void P2PTransportChannel::OnCheckAndPing() { |
| // Make sure the states of the connections are up-to-date (since this affects |
| // which ones are pingable). |
| UpdateConnectionStates(); |
| - |
| - // Find the oldest pingable connection and have it do a ping. |
| - Connection* conn = FindNextPingableConnection(); |
| - if (conn) |
| - PingConnection(conn); |
| - |
| - // Post ourselves a message to perform the next ping. |
| - uint32 delay = writable() ? WRITABLE_DELAY : UNWRITABLE_DELAY; |
| - thread()->PostDelayed(delay, this, MSG_PING); |
| -} |
| - |
| -void P2PTransportChannel::OnCheckReceiving() { |
| - if (best_connection_) { |
| - bool receiving = rtc::Time() <= |
| - best_connection_->last_received() + receiving_timeout_; |
| - set_receiving(receiving); |
| + // When the best connection is either not receiving or not writable, |
| + // switch to weak ping delay. |
| + int ping_delay = Weak() ? WEAK_PING_DELAY : STRONG_PING_DELAY; |
| + if (rtc::Time() >= last_ping_sent_ms_ + ping_delay) { |
| + Connection* conn = FindNextPingableConnection(); |
| + if (conn) { |
| + PingConnection(conn); |
| + } |
| } |
| - |
| - thread()->PostDelayed(check_receiving_delay_, this, MSG_CHECK_RECEIVING); |
| + int check_delay = std::min(ping_delay, check_receiving_delay_); |
| + thread()->PostDelayed(check_delay, this, MSG_CHECK_AND_PING); |
| } |
|
pthatcher1
2015/09/22 23:41:46
So we only check for receiving() every 500ms? Doe
honghaiz3
2015/09/23 03:16:10
We took the minimum of the ping_delay and check_re
|
| // Is the connection in a state for us to even consider pinging the other side? |
| @@ -1161,9 +1179,9 @@ bool P2PTransportChannel::IsPingable(Connection* conn) { |
| return false; |
| } |
| - // If the channel is not writable, ping all candidates. Otherwise, we only |
| + // If the channel is weak, ping all candidates. Otherwise, we only |
| // want to ping connections that have not timed out on writing. |
| - return !writable() || conn->write_state() != Connection::STATE_WRITE_TIMEOUT; |
| + return Weak() || conn->write_state() != Connection::STATE_WRITE_TIMEOUT; |
| } |
| // Returns the next pingable connection to ping. This will be the oldest |
| @@ -1175,9 +1193,8 @@ bool P2PTransportChannel::IsPingable(Connection* conn) { |
| Connection* P2PTransportChannel::FindNextPingableConnection() { |
| uint32 now = rtc::Time(); |
| if (best_connection_ && best_connection_->connected() && |
| - (best_connection_->write_state() == Connection::STATE_WRITABLE) && |
| - (best_connection_->last_ping_sent() + MAX_CURRENT_WRITABLE_DELAY <= |
| - now)) { |
| + best_connection_->writable() && |
| + (best_connection_->last_ping_sent() + MAX_CURRENT_STRONG_DELAY <= now)) { |
| return best_connection_; |
| } |
| @@ -1236,7 +1253,8 @@ void P2PTransportChannel::PingConnection(Connection* conn) { |
| use_candidate = best_connection_->writable(); |
| } |
| conn->set_use_candidate_attr(use_candidate); |
| - conn->Ping(rtc::Time()); |
| + last_ping_sent_ms_ = rtc::Time(); |
| + conn->Ping(last_ping_sent_ms_); |
| } |
| // When a connection's state changes, we need to figure out who to use as |