Chromium Code Reviews| Index: webrtc/p2p/base/p2ptransportchannel.cc |
| diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc |
| index 094a8dcc8f1ffc937a436dc85890633c62d60760..c53bd8839e8d0b74f16ccd5f87bd7f9662811953 100644 |
| --- a/webrtc/p2p/base/p2ptransportchannel.cc |
| +++ b/webrtc/p2p/base/p2ptransportchannel.cc |
| @@ -22,11 +22,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 }; |
|
pthatcher1
2015/09/17 05:58:42
Why did you combine these? I think it made sense
honghaiz3
2015/09/17 19:47:56
There are two reasons.
1. When you do ping, you a
|
| // 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 +30,13 @@ 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 |
| +static const uint32 LONG_PING_DELAY = 1000 * PING_PACKET_SIZE / 1000; // 480ms |
| +static const uint32 SHORT_PING_DELAY = 1000 * PING_PACKET_SIZE / 10000; // 50ms |
|
pthatcher1
2015/09/17 05:58:41
I think a good name for these would be WEAK_CONNEC
honghaiz3
2015/09/17 19:47:55
Done.
|
| // 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 |
| +// 2 * LONG_PING_DELAY - bit |
| +static const uint32 MAX_CURRENT_WRITABLE_DELAY = 900; |
| static const int MIN_CHECK_RECEIVING_DELAY = 50; // ms |
| @@ -72,7 +69,15 @@ int CompareConnectionCandidates(cricket::Connection* a, |
| // Compare two connections based on their connected state, writability and |
| // static preferences. |
| -int CompareConnections(cricket::Connection *a, cricket::Connection *b) { |
| +int CompareConnectionStates(cricket::Connection* a, cricket::Connection* b) { |
| + // When the current best connection is not receiving but a backup connection |
| + // is receiving, the backup connection has a higher priority regardless |
| + // its writable state. |
|
pthatcher1
2015/09/17 05:58:42
It doesn't need to just be "backup". We could sim
honghaiz3
2015/09/17 19:47:55
Done.
|
| + if (a->receiving() && !b->receiving()) |
| + return 1; |
| + if (!a->receiving() && b->receiving()) |
| + return -1; |
| + |
| // Sort based on write-state. Better states have lower values. |
| if (a->write_state() < b->write_state()) |
| return 1; |
| @@ -110,7 +115,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); |
| } |
| @@ -149,18 +161,31 @@ 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) { |
| +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 RECEIVING/WRITE/CONNECT states are different, we should switch |
| + // regardless of the nominate state. Otherwise, do not switch if |a_conn| is |
|
pthatcher1
2015/09/17 05:58:42
nominate => nominated
honghaiz3
2015/09/17 19:47:56
Done.
|
| + // nominated but |b_conn| is not. |
| + 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() > b_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 +197,27 @@ 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), |
| + next_connection_to_ping_(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), |
| + ping_delay_(SHORT_PING_DELAY), |
| + last_ping_sent_(0) {} |
| P2PTransportChannel::~P2PTransportChannel() { |
| ASSERT(worker_thread_ == rtc::Thread::Current()); |
| @@ -221,6 +248,7 @@ void P2PTransportChannel::AddAllocatorSession(PortAllocatorSession* session) { |
| void P2PTransportChannel::AddConnection(Connection* connection) { |
| connections_.push_back(connection); |
| connection->set_remote_ice_mode(remote_ice_mode_); |
| + connection->set_receiving_timeout(receiving_timeout_); |
| connection->SignalReadPacket.connect( |
| this, &P2PTransportChannel::OnReadPacket); |
| connection->SignalReadyToSend.connect( |
| @@ -342,6 +370,9 @@ void P2PTransportChannel::SetReceivingTimeout(int receiving_timeout_ms) { |
| std::max(MIN_CHECK_RECEIVING_DELAY, receiving_timeout_ / 10); |
| LOG(LS_VERBOSE) << "Set ICE receiving timeout to " << receiving_timeout_ |
| << " milliseconds"; |
| + for (Connection* connection : connections_) { |
| + connection->set_receiving_timeout(receiving_timeout_); |
| + } |
| } |
| // Go into the state of processing candidates, and running in general |
| @@ -358,10 +389,7 @@ void P2PTransportChannel::Connect() { |
| Allocate(); |
| // Start pinging as the ports come in. |
| - thread()->Post(this, MSG_PING); |
| - |
| - thread()->PostDelayed( |
| - check_receiving_delay_, this, MSG_CHECK_RECEIVING); |
| + thread()->Post(this, MSG_CHECK_AND_PING); |
| } |
| // A new port is available, attempt to make connections for it |
| @@ -400,7 +428,7 @@ void P2PTransportChannel::OnPortReady(PortAllocatorSession *session, |
| std::vector<RemoteCandidate>::iterator iter; |
| for (iter = remote_candidates_.begin(); iter != remote_candidates_.end(); |
| ++iter) { |
| - CreateConnection(port, *iter, iter->origin_port(), false); |
| + CreateConnection(port, *iter, iter->origin_port()); |
| } |
| SortConnections(); |
| @@ -616,7 +644,7 @@ void P2PTransportChannel::OnCandidate(const Candidate& candidate) { |
| } |
| // Create connections to this remote candidate. |
| - CreateConnections(candidate, NULL, false); |
| + CreateConnections(candidate, NULL); |
| // Resort the connections list, which may have new elements. |
| SortConnections(); |
| @@ -626,8 +654,7 @@ void P2PTransportChannel::OnCandidate(const Candidate& candidate) { |
| // remote candidate. The return value is true if we created a connection from |
| // the origin port. |
| bool P2PTransportChannel::CreateConnections(const Candidate& remote_candidate, |
| - PortInterface* origin_port, |
| - bool readable) { |
| + PortInterface* origin_port) { |
| ASSERT(worker_thread_ == rtc::Thread::Current()); |
| Candidate new_remote_candidate(remote_candidate); |
| @@ -665,7 +692,7 @@ bool P2PTransportChannel::CreateConnections(const Candidate& remote_candidate, |
| bool created = false; |
| std::vector<PortInterface *>::reverse_iterator it; |
| for (it = ports_.rbegin(); it != ports_.rend(); ++it) { |
| - if (CreateConnection(*it, new_remote_candidate, origin_port, readable)) { |
| + if (CreateConnection(*it, new_remote_candidate, origin_port)) { |
| if (*it == origin_port) |
| created = true; |
| } |
| @@ -673,8 +700,7 @@ bool P2PTransportChannel::CreateConnections(const Candidate& remote_candidate, |
| if ((origin_port != NULL) && |
| std::find(ports_.begin(), ports_.end(), origin_port) == ports_.end()) { |
| - if (CreateConnection( |
| - origin_port, new_remote_candidate, origin_port, readable)) |
| + if (CreateConnection(origin_port, new_remote_candidate, origin_port)) |
| created = true; |
| } |
| @@ -688,8 +714,7 @@ bool P2PTransportChannel::CreateConnections(const Candidate& remote_candidate, |
| // And then listen to connection object for changes. |
| bool P2PTransportChannel::CreateConnection(PortInterface* port, |
| const Candidate& remote_candidate, |
| - PortInterface* origin_port, |
| - bool readable) { |
| + PortInterface* origin_port) { |
| // Look for an existing connection with this remote address. If one is not |
| // found, then we can create a new connection for this address. |
| Connection* connection = port->GetConnection(remote_candidate.address()); |
| @@ -724,11 +749,6 @@ bool P2PTransportChannel::CreateConnection(PortInterface* port, |
| << connections_.size() << " total)"; |
| } |
| - // If we are readable, it is because we are creating this in response to a |
| - // ping from the other side. This will cause the state to become readable. |
| - if (readable) |
| - connection->ReceivedPing(); |
| - |
| return true; |
| } |
| @@ -853,8 +873,7 @@ bool P2PTransportChannel::GetStats(ConnectionInfos *infos) { |
| Connection *connection = *it; |
| ConnectionInfo info; |
| info.best_connection = (best_connection_ == connection); |
| - info.readable = |
| - (connection->read_state() == Connection::STATE_READABLE); |
| + info.receiving = connection->receiving(); |
| info.writable = |
| (connection->write_state() == Connection::STATE_WRITABLE); |
| info.timeout = |
| @@ -895,10 +914,9 @@ void P2PTransportChannel::Allocate() { |
| // Monitor connection states. |
| void P2PTransportChannel::UpdateConnectionStates() { |
| - uint32 now = rtc::Time(); |
| - |
| // We need to copy the list of connections since some may delete themselves |
| // when we call UpdateState. |
| + uint32 now = rtc::Time(); |
| for (uint32 i = 0; i < connections_.size(); ++i) |
| connections_[i]->UpdateState(now); |
| } |
| @@ -937,13 +955,24 @@ void P2PTransportChannel::SortConnections() { |
| Connection* top_connection = |
| (connections_.size() > 0) ? connections_[0] : nullptr; |
| - // 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 (top_connection != best_connection_ && best_connection_->writable() && |
| + !top_connection->writable()) { |
| + // This is a very special case where the best_connection becomes |
| + // not receiving but the top connection is not writable. |
|
pthatcher1
2015/09/17 05:58:41
It seems like, instead, that we should always go i
honghaiz3
2015/09/17 19:47:56
Yes. That is what is done.
Combining the comments
|
| + // We don't switch but check the writability of the |top_connection|. |
| + // If we are in the long ping delay, reschedule it so that it will be |
| + // executed much sooner. If we are in the short ping delay, just make |
| + // it the next one to be pinged, so that we do not overflow the channel |
| + // with ping messages. |
| + if (ping_delay_ == LONG_PING_DELAY) { |
| + thread()->Clear(this, MSG_CHECK_AND_PING); |
| + thread()->Post(this, MSG_CHECK_AND_PING); |
| + } |
| + next_connection_to_ping_ = top_connection; |
|
pthatcher1
2015/09/17 05:58:42
Shouldn't the faster pinging work fine without sto
honghaiz3
2015/09/17 19:47:56
Yes. We don't need the extra speedup of the ping r
pthatcher1
2015/09/17 22:01:17
So are you going to remove next_connection_to_ping
|
| + } else if (ShouldSwitch(best_connection_, top_connection, ice_role_)) { |
| + // 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. |
| LOG(LS_INFO) << "Switching best connection: " << top_connection->ToString(); |
| SwitchBestConnectionTo(top_connection); |
| } |
| @@ -993,7 +1022,10 @@ void P2PTransportChannel::PruneConnections() { |
| // resources and they may represent very distinct paths over which we can |
| // switch. If the |primier| 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 CompareConnections. |
| + // this network. See the big comment in CompareConnections. If the |primier| |
| + // connection is not receiving, we do not prune the connections in the network |
| + // because either some connection will become receiving or all connections |
| + // in the network will time out and be deleted. |
|
pthatcher1
2015/09/17 05:58:42
I think this would be more clear as "once on conne
honghaiz3
2015/09/17 19:47:55
Removed the comments as we change back to check wr
|
| // Get a list of the networks that we are using. |
| std::set<rtc::Network*> networks; |
| @@ -1002,12 +1034,13 @@ void P2PTransportChannel::PruneConnections() { |
| } |
| for (rtc::Network* network : networks) { |
| Connection* primier = GetBestConnectionOnNetwork(network); |
| - if (!(primier && primier->writable() && primier->connected())) { |
| + if (!(primier && primier->receiving() && primier->connected())) { |
|
pthatcher1
2015/09/17 05:58:41
Why don't we want to keep it as writable()? Shoul
honghaiz3
2015/09/17 19:47:56
Yes. especially now that we put writable with high
|
| continue; |
| } |
| for (Connection* conn : connections_) { |
| if ((conn != primier) && (conn->port()->Network() == network) && |
| + (conn != pending_best_connection_) && |
| (CompareConnectionCandidates(primier, conn) >= 0)) { |
| conn->Prune(); |
| } |
| @@ -1039,21 +1072,13 @@ 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"; |
| - bool readable = false; |
| - for (uint32 i = 0; i < connections_.size(); ++i) { |
| - if (connections_[i]->read_state() == Connection::STATE_READABLE) { |
| - readable = true; |
| - break; |
| - } |
| - } |
| - set_readable(readable); |
| + bool receiving = best_connection_ && best_connection_->receiving(); |
|
pthatcher1
2015/09/17 05:58:42
Shouldn't the logic here be the same as the previo
honghaiz3
2015/09/17 19:47:56
Done.
I was thinking that we should use the best_
|
| + set_receiving(receiving); |
| } |
| // We checked the status of our connections and we had at least one that |
| @@ -1109,11 +1134,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); |
| @@ -1127,33 +1149,20 @@ void P2PTransportChannel::OnSort() { |
| SortConnections(); |
| } |
| -// Handle queued up ping request |
| -void P2PTransportChannel::OnPing() { |
| - // Make sure the states of the connections are up-to-date (since this affects |
| - // which ones are pingable). |
| +void P2PTransportChannel::OnCheckAndPing() { |
| 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() { |
| - // Check receiving only if the best connection has received data packets |
| - // because we want to detect not receiving any packets only after the media |
| - // have started flowing. |
| - if (best_connection_ && best_connection_->recv_total_bytes() > 0) { |
| - bool receiving = rtc::Time() <= |
| - best_connection_->last_received() + receiving_timeout_; |
| - set_receiving(receiving); |
| + // When the best connection is not receiving or writable, switch to short ping |
| + // delay. |
|
pthatcher1
2015/09/17 05:58:41
not receiving or writable => not receiving or not
honghaiz3
2015/09/17 19:47:56
Done with neither nor.
|
| + ping_delay_ = writable() && receiving() ? LONG_PING_DELAY : SHORT_PING_DELAY; |
|
pthatcher1
2015/09/17 05:58:41
We might even consider making a method
bool weak
honghaiz3
2015/09/17 19:47:56
Done.
|
| + uint32 now = rtc::Time(); |
| + if (now >= last_ping_sent_ + ping_delay_) { |
|
pthatcher1
2015/09/17 05:58:41
Might be more readable as:
uint32 time_since_last
honghaiz3
2015/09/17 19:47:55
Similar to the other comments. I try to avoid subt
|
| + Connection* conn = FindNextPingableConnection(); |
| + if (conn) { |
| + PingConnection(conn); |
| + } |
| } |
| - |
| - thread()->PostDelayed(check_receiving_delay_, this, MSG_CHECK_RECEIVING); |
| + uint32 check_delay = std::min(ping_delay_, check_receiving_delay_); |
| + thread()->PostDelayed(check_delay, this, MSG_CHECK_AND_PING); |
|
pthatcher1
2015/09/17 05:58:41
I think this was more clear before with two "loops
honghaiz3
2015/09/17 19:47:55
For the two reasons I mentioned at the top.
If yo
|
| } |
| // Is the connection in a state for us to even consider pinging the other side? |
| @@ -1175,19 +1184,14 @@ bool P2PTransportChannel::IsPingable(Connection* conn) { |
| return false; |
| } |
|
pthatcher1
2015/09/17 05:58:41
Might be more readable as:
// If the channel is w
honghaiz3
2015/09/17 19:47:56
Done.
|
| - if (writable()) { |
| - // If we are writable, then we only want to ping connections that could be |
| - // better than this one, i.e., the ones that were not pruned. |
| - return (conn->write_state() != Connection::STATE_WRITE_TIMEOUT); |
| - } else { |
| - // If we are not writable, then we need to try everything that might work. |
| - // This includes both connections that do not have write timeout as well as |
| - // ones that do not have read timeout. A connection could be readable but |
| - // be in write-timeout if we pruned it before. Since the other side is |
| - // still pinging it, it very well might still work. |
| - return (conn->write_state() != Connection::STATE_WRITE_TIMEOUT) || |
| - (conn->read_state() != Connection::STATE_READ_TIMEOUT); |
| + if (writable() && receiving()) { |
| + // If we are writable and also receiving, then we only want to ping |
| + // connections that could be better than this one, i.e., the ones that |
| + // were not pruned. |
| + return !conn->pruned(); |
| } |
| + // If we are not writable, then we need to try everything that might work. |
| + return true; |
| } |
| // Returns the next pingable connection to ping. This will be the oldest |
| @@ -1197,9 +1201,14 @@ bool P2PTransportChannel::IsPingable(Connection* conn) { |
| // reconnecting. The newly created connection should be selected as the ping |
| // target to become writable instead. See the big comment in CompareConnections. |
| Connection* P2PTransportChannel::FindNextPingableConnection() { |
| + if (next_connection_to_ping_ && !next_connection_to_ping_->writable()) { |
| + Connection* conn = next_connection_to_ping_; |
| + next_connection_to_ping_ = nullptr; |
| + return conn; |
| + } |
| uint32 now = rtc::Time(); |
| if (best_connection_ && best_connection_->connected() && |
| - (best_connection_->write_state() == Connection::STATE_WRITABLE) && |
| + best_connection_->writable() && best_connection_->receiving() && |
| (best_connection_->last_ping_sent() + MAX_CURRENT_WRITABLE_DELAY <= |
| now)) { |
| return best_connection_; |
| @@ -1260,7 +1269,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_ = rtc::Time(); |
| + conn->Ping(last_ping_sent_); |
| } |
| // When a connection's state changes, we need to figure out who to use as |