Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(503)

Side by Side Diff: webrtc/p2p/base/p2ptransportchannel.cc

Issue 2069493002: Do not switch best connection on the controlled side too frequently (Closed) Base URL: https://chromium.googlesource.com/external/webrtc@master
Patch Set: Add tests Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. 2 * Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license 4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source 5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found 6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may 7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree. 8 * be found in the AUTHORS file in the root of the source tree.
9 */ 9 */
10 10
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after
193 } 193 }
194 194
195 int prefs_cmp = CompareConnectionCandidates(a_conn, b_conn); 195 int prefs_cmp = CompareConnectionCandidates(a_conn, b_conn);
196 if (prefs_cmp != 0) { 196 if (prefs_cmp != 0) {
197 return prefs_cmp < 0; 197 return prefs_cmp < 0;
198 } 198 }
199 199
200 return b_conn->rtt() <= a_conn->rtt() + kMinImprovement; 200 return b_conn->rtt() <= a_conn->rtt() + kMinImprovement;
201 } 201 }
202 202
203 // Determines whether we should switch the best connection to |new_connection|
204 // based the writable state, the last nominated connection, and the last
205 // connection receiving data. This is to prevent that the controlled side may
206 // switch the best connection too frequently when the controlling side is doing
207 // aggressive nominations. With this implementation, the controlled side will
208 // switch the best connection if the new nominated connection is writable, and
209 // i) the best connection is nullptr, or
210 // ii) the best connection was not nominated, or
211 // iii) the new connection has been nominated twice consecutively, or
honghaiz3 2016/06/14 23:18:27 This would be useful for connections not receiving
pthatcher1 2016/06/15 21:59:53 As I mentioned in the another comment, I think tha
honghaiz3 2016/06/16 23:03:49 Done.
212 // iv) the new connection is nominated and is receiving data, or
213 // v) the new connection has lower cost or high priority.
214 // TODO(honghaiz): Stop the aggressive nomination on the controlling side and
215 // implement the ice-renomination option.
216 bool ShouldSwitchOnNominatedOrDataReceived(
pthatcher1 2016/06/15 21:59:53 I think this should be called ShouldSwitchSelected
honghaiz3 2016/06/16 23:03:48 I don't think it should use the current ShouldSwit
pthatcher1 2016/06/17 00:27:11 That will only happen if the controlling side isn'
honghaiz3 2016/06/17 19:18:18 Done.
217 cricket::Connection* best_connection,
218 cricket::Connection* new_connection,
219 cricket::Connection* last_nominated,
220 cricket::Connection* last_data_received) {
221 RTC_CHECK(new_connection != nullptr);
222 if (!new_connection->writable()) {
223 return false;
224 }
pthatcher1 2016/06/15 21:59:53 Shouldn't this use the "comparison of writable sta
honghaiz3 2016/06/16 01:13:08 Right. The new connection is already writable. Pl
honghaiz3 2016/06/16 23:03:49 If a nominated connection is not writable, it won'
pthatcher1 2016/06/17 00:27:11 True. "if (!wrtible()) { return false; }" is more
honghaiz3 2016/06/17 19:18:17 Changed this logic to just compare the write/recei
225 if (best_connection == nullptr || !best_connection->nominated()) {
226 return true;
227 }
pthatcher1 2016/06/15 21:59:53 But the new_connection might not be nominated eith
honghaiz3 2016/06/16 23:03:49 This method is primarily called when it is nominat
pthatcher1 2016/06/17 00:27:11 If the controlling side isn't aggressive and none
honghaiz3 2016/06/17 19:18:17 Done.
228 if (new_connection == last_nominated ||
229 new_connection == last_data_received) {
230 return true;
231 }
pthatcher1 2016/06/15 21:59:53 Perhaps instead of recording the last conn that re
honghaiz3 2016/06/16 23:03:48 using the timestamp is less effective as that only
pthatcher1 2016/06/17 00:27:11 I mean something like this: if (connection->last_
honghaiz3 2016/06/17 19:18:17 Done.
232 // Compare the network cost and priority.
233 return CompareConnectionCandidates(best_connection, new_connection) < 0;
pthatcher1 2016/06/15 21:59:53 I think we should make all the logic very similar
honghaiz3 2016/06/16 01:13:08 As I mentioned in another comment, if the current
pthatcher1 2016/06/17 00:27:11 I'm mostly suggesting we make the logic clear. Sw
honghaiz3 2016/06/17 19:18:17 Done.
234 }
235
203 } // unnamed namespace 236 } // unnamed namespace
204 237
205 namespace cricket { 238 namespace cricket {
206 239
207 // When the socket is unwritable, we will use 10 Kbps (ignoring IP+UDP headers) 240 // When the socket is unwritable, we will use 10 Kbps (ignoring IP+UDP headers)
208 // for pinging. When the socket is writable, we will use only 1 Kbps because 241 // for pinging. When the socket is writable, we will use only 1 Kbps because
209 // we don't want to degrade the quality on a modem. These numbers should work 242 // we don't want to degrade the quality on a modem. These numbers should work
210 // well on a 28.8K modem, which is the slowest connection on which the voice 243 // well on a 28.8K modem, which is the slowest connection on which the voice
211 // quality is reasonable at all. 244 // quality is reasonable at all.
212 static const int PING_PACKET_SIZE = 60 * 8; 245 static const int PING_PACKET_SIZE = 60 * 8;
(...skipping 18 matching lines...) Expand all
231 : P2PTransportChannel(transport_name, component, allocator) {} 264 : P2PTransportChannel(transport_name, component, allocator) {}
232 265
233 P2PTransportChannel::P2PTransportChannel(const std::string& transport_name, 266 P2PTransportChannel::P2PTransportChannel(const std::string& transport_name,
234 int component, 267 int component,
235 PortAllocator* allocator) 268 PortAllocator* allocator)
236 : TransportChannelImpl(transport_name, component), 269 : TransportChannelImpl(transport_name, component),
237 allocator_(allocator), 270 allocator_(allocator),
238 worker_thread_(rtc::Thread::Current()), 271 worker_thread_(rtc::Thread::Current()),
239 incoming_only_(false), 272 incoming_only_(false),
240 error_(0), 273 error_(0),
241 best_connection_(NULL),
242 pending_best_connection_(NULL),
243 sort_dirty_(false), 274 sort_dirty_(false),
244 remote_ice_mode_(ICEMODE_FULL), 275 remote_ice_mode_(ICEMODE_FULL),
245 ice_role_(ICEROLE_UNKNOWN), 276 ice_role_(ICEROLE_UNKNOWN),
246 tiebreaker_(0), 277 tiebreaker_(0),
247 gathering_state_(kIceGatheringNew), 278 gathering_state_(kIceGatheringNew),
248 check_receiving_interval_(MIN_CHECK_RECEIVING_INTERVAL * 5), 279 check_receiving_interval_(MIN_CHECK_RECEIVING_INTERVAL * 5),
249 config_(MIN_CHECK_RECEIVING_INTERVAL * 50 /* receiving_timeout */, 280 config_(MIN_CHECK_RECEIVING_INTERVAL * 50 /* receiving_timeout */,
250 0 /* backup_connection_ping_interval */, 281 0 /* backup_connection_ping_interval */,
251 false /* gather_continually */, 282 false /* gather_continually */,
252 false /* prioritize_most_likely_candidate_pairs */, 283 false /* prioritize_most_likely_candidate_pairs */,
(...skipping 469 matching lines...) Expand 10 before | Expand all | Expand 10 after
722 return nullptr; 753 return nullptr;
723 } 754 }
724 *generation = params.rend() - it - 1; 755 *generation = params.rend() - it - 1;
725 return &(*it); 756 return &(*it);
726 } 757 }
727 758
728 void P2PTransportChannel::OnNominated(Connection* conn) { 759 void P2PTransportChannel::OnNominated(Connection* conn) {
729 ASSERT(worker_thread_ == rtc::Thread::Current()); 760 ASSERT(worker_thread_ == rtc::Thread::Current());
730 ASSERT(ice_role_ == ICEROLE_CONTROLLED); 761 ASSERT(ice_role_ == ICEROLE_CONTROLLED);
731 762
732 if (conn->write_state() == Connection::STATE_WRITABLE) { 763 if (best_connection_ == conn) {
733 if (best_connection_ != conn) { 764 return;
734 pending_best_connection_ = NULL; 765 }
735 LOG(LS_INFO) << "Switching best connection on controlled side: " 766
736 << conn->ToString(); 767 if (ShouldSwitchOnNominatedOrDataReceived(best_connection_, conn,
737 SwitchBestConnectionTo(conn); 768 last_nominated_conn_,
738 // Now we have selected the best connection, time to prune other existing 769 last_data_received_conn_)) {
739 // connections and update the read/write state of the channel. 770 last_nominated_conn_ = nullptr;
740 RequestSort(); 771 last_data_received_conn_ = nullptr;
741 } 772 LOG(LS_INFO) << "Switching best connection on controlled side: "
773 << conn->ToString();
774 SwitchBestConnectionTo(conn);
775 // Now we have selected the best connection, time to prune other existing
776 // connections and update the read/write state of the channel.
777 RequestSort();
742 } else { 778 } else {
743 LOG(LS_INFO) << "Not switching the best connection on controlled side yet," 779 LOG(LS_INFO) << "Not switching the best connection on controlled side yet: "
744 << " because it's not writable: " << conn->ToString(); 780 << conn->ToString();
745 pending_best_connection_ = conn; 781 last_nominated_conn_ = conn;
746 } 782 }
pthatcher1 2016/06/15 21:59:53 Can this just be this? if (!ShouldSwitchSelectedC
honghaiz3 2016/06/16 23:03:49 Done. Regarding the logging, I think the benefit
pthatcher1 2016/06/17 00:27:11 What about RequestSort()? Shouldn't we do that in
747 } 783 }
748 784
749 void P2PTransportChannel::AddRemoteCandidate(const Candidate& candidate) { 785 void P2PTransportChannel::AddRemoteCandidate(const Candidate& candidate) {
750 ASSERT(worker_thread_ == rtc::Thread::Current()); 786 ASSERT(worker_thread_ == rtc::Thread::Current());
751 787
752 uint32_t generation = GetRemoteCandidateGeneration(candidate); 788 uint32_t generation = GetRemoteCandidateGeneration(candidate);
753 // If a remote candidate with a previous generation arrives, drop it. 789 // If a remote candidate with a previous generation arrives, drop it.
754 if (generation < remote_ice_generation()) { 790 if (generation < remote_ice_generation()) {
755 LOG(LS_WARNING) << "Dropping a remote candidate because its ufrag " 791 LOG(LS_WARNING) << "Dropping a remote candidate because its ufrag "
756 << candidate.username() 792 << candidate.username()
(...skipping 680 matching lines...) Expand 10 before | Expand all | Expand 10 after
1437 conn->Ping(last_ping_sent_ms_); 1473 conn->Ping(last_ping_sent_ms_);
1438 } 1474 }
1439 1475
1440 // When a connection's state changes, we need to figure out who to use as 1476 // When a connection's state changes, we need to figure out who to use as
1441 // the best connection again. It could have become usable, or become unusable. 1477 // the best connection again. It could have become usable, or become unusable.
1442 void P2PTransportChannel::OnConnectionStateChange(Connection* connection) { 1478 void P2PTransportChannel::OnConnectionStateChange(Connection* connection) {
1443 ASSERT(worker_thread_ == rtc::Thread::Current()); 1479 ASSERT(worker_thread_ == rtc::Thread::Current());
1444 1480
1445 // Update the best connection if the state change is from pending best 1481 // Update the best connection if the state change is from pending best
1446 // connection and role is controlled. 1482 // connection and role is controlled.
1447 if (ice_role_ == ICEROLE_CONTROLLED) { 1483 if (ice_role_ == ICEROLE_CONTROLLED && connection == last_nominated_conn_) {
1448 if (connection == pending_best_connection_ && connection->writable()) { 1484 // This essentially requires that the connection is both last nominated
1449 pending_best_connection_ = NULL; 1485 // and receiving data, or the best connection is nullptr, in order
1486 // to switch to |connection|.
1487 if (connection == best_connection_) {
1488 // If it is already the best connection, reset the last_nominated_conn_
1489 last_nominated_conn_ = nullptr;
1490 } else if (ShouldSwitchOnNominatedOrDataReceived(
1491 best_connection_, connection, nullptr,
1492 last_data_received_conn_)) {
1493 last_nominated_conn_ = nullptr;
1494 last_data_received_conn_ = nullptr;
1450 LOG(LS_INFO) << "Switching best connection on controlled side" 1495 LOG(LS_INFO) << "Switching best connection on controlled side"
1451 << " because it's now writable: " << connection->ToString(); 1496 << " because it's now writable: " << connection->ToString();
1452 SwitchBestConnectionTo(connection); 1497 SwitchBestConnectionTo(connection);
1453 } 1498 }
1454 } 1499 }
pthatcher1 2016/06/15 21:59:53 Can't this just be if (connection == last_nominat
honghaiz3 2016/06/16 23:03:49 Ah Yes.
1455 1500
1456 // May stop the allocator session when at least one connection becomes 1501 // May stop the allocator session when at least one connection becomes
1457 // strongly connected after starting to get ports and the local candidate of 1502 // strongly connected after starting to get ports and the local candidate of
1458 // the connection is at the latest generation. It is not enough to check 1503 // the connection is at the latest generation. It is not enough to check
1459 // that the connection becomes weakly connected because the connection may be 1504 // that the connection becomes weakly connected because the connection may be
1460 // changing from (writable, receiving) to (writable, not receiving). 1505 // changing from (writable, receiving) to (writable, not receiving).
1461 bool strongly_connected = !connection->weak(); 1506 bool strongly_connected = !connection->weak();
1462 bool latest_generation = connection->local_candidate().generation() >= 1507 bool latest_generation = connection->local_candidate().generation() >=
1463 allocator_session()->generation(); 1508 allocator_session()->generation();
1464 if (strongly_connected && latest_generation) { 1509 if (strongly_connected && latest_generation) {
(...skipping 17 matching lines...) Expand all
1482 std::vector<Connection*>::iterator iter = 1527 std::vector<Connection*>::iterator iter =
1483 std::find(connections_.begin(), connections_.end(), connection); 1528 std::find(connections_.begin(), connections_.end(), connection);
1484 ASSERT(iter != connections_.end()); 1529 ASSERT(iter != connections_.end());
1485 pinged_connections_.erase(*iter); 1530 pinged_connections_.erase(*iter);
1486 unpinged_connections_.erase(*iter); 1531 unpinged_connections_.erase(*iter);
1487 connections_.erase(iter); 1532 connections_.erase(iter);
1488 1533
1489 LOG_J(LS_INFO, this) << "Removed connection (" 1534 LOG_J(LS_INFO, this) << "Removed connection ("
1490 << static_cast<int>(connections_.size()) << " remaining)"; 1535 << static_cast<int>(connections_.size()) << " remaining)";
1491 1536
1492 if (pending_best_connection_ == connection) { 1537 if (last_nominated_conn_ == connection) {
1493 pending_best_connection_ = NULL; 1538 last_nominated_conn_ = nullptr;
1494 } 1539 }
pthatcher1 2016/06/15 21:59:53 I think we should just remove these lines and have
honghaiz3 2016/06/16 23:03:49 Done.
1495 1540
1496 // If this is currently the best connection, then we need to pick a new one. 1541 // If this is currently the best connection, then we need to pick a new one.
1497 // The call to SortConnections will pick a new one. It looks at the current 1542 // The call to SortConnections will pick a new one. It looks at the current
1498 // best connection in order to avoid switching between fairly similar ones. 1543 // best connection in order to avoid switching between fairly similar ones.
1499 // Since this connection is no longer an option, we can just set best to NULL 1544 // Since this connection is no longer an option, we can just set best to NULL
1500 // and re-choose a best assuming that there was no best connection. 1545 // and re-choose a best assuming that there was no best connection.
1501 if (best_connection_ == connection) { 1546 if (best_connection_ == connection) {
1502 LOG(LS_INFO) << "Best connection destroyed. Will choose a new one."; 1547 LOG(LS_INFO) << "Best connection destroyed. Will choose a new one.";
1503 SwitchBestConnectionTo(NULL); 1548 SwitchBestConnectionTo(NULL);
1504 RequestSort(); 1549 RequestSort();
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
1552 1597
1553 // Do not deliver, if packet doesn't belong to the correct transport channel. 1598 // Do not deliver, if packet doesn't belong to the correct transport channel.
1554 if (!FindConnection(connection)) 1599 if (!FindConnection(connection))
1555 return; 1600 return;
1556 1601
1557 // Let the client know of an incoming packet 1602 // Let the client know of an incoming packet
1558 SignalReadPacket(this, data, len, packet_time, 0); 1603 SignalReadPacket(this, data, len, packet_time, 0);
1559 1604
1560 // May need to switch the sending connection based on the receiving media path 1605 // May need to switch the sending connection based on the receiving media path
1561 // if this is the controlled side. 1606 // if this is the controlled side.
1562 if (ice_role_ == ICEROLE_CONTROLLED && !best_nominated_connection() && 1607 if (ice_role_ != ICEROLE_CONTROLLED || best_connection_ == connection) {
1563 connection->writable() && best_connection_ != connection) { 1608 return;
1609 }
1610
1611 if (ShouldSwitchOnNominatedOrDataReceived(best_connection_, connection,
1612 last_nominated_conn_, nullptr)) {
1564 SwitchBestConnectionTo(connection); 1613 SwitchBestConnectionTo(connection);
1614 last_data_received_conn_ = nullptr;
pthatcher1 2016/06/15 21:59:53 Why would we set this to nullptr?
honghaiz3 2016/06/16 23:03:48 Perhaps not necessary. Removed.
1615 last_nominated_conn_ = nullptr;
1616 } else {
1617 last_data_received_conn_ = connection;
1565 } 1618 }
pthatcher1 2016/06/15 21:59:53 Wouldn't it make more sense to do this? last_data
honghaiz3 2016/06/16 23:03:49 Done.
1566 } 1619 }
1567 1620
1568 void P2PTransportChannel::OnSentPacket(const rtc::SentPacket& sent_packet) { 1621 void P2PTransportChannel::OnSentPacket(const rtc::SentPacket& sent_packet) {
1569 ASSERT(worker_thread_ == rtc::Thread::Current()); 1622 ASSERT(worker_thread_ == rtc::Thread::Current());
1570 1623
1571 SignalSentPacket(this, sent_packet); 1624 SignalSentPacket(this, sent_packet);
1572 } 1625 }
1573 1626
1574 void P2PTransportChannel::OnReadyToSend(Connection* connection) { 1627 void P2PTransportChannel::OnReadyToSend(Connection* connection) {
1575 if (connection == best_connection_ && writable()) { 1628 if (connection == best_connection_ && writable()) {
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
1688 1741
1689 // During the initial state when nothing has been pinged yet, return the first 1742 // During the initial state when nothing has been pinged yet, return the first
1690 // one in the ordered |connections_|. 1743 // one in the ordered |connections_|.
1691 return *(std::find_if(connections_.begin(), connections_.end(), 1744 return *(std::find_if(connections_.begin(), connections_.end(),
1692 [conn1, conn2](Connection* conn) { 1745 [conn1, conn2](Connection* conn) {
1693 return conn == conn1 || conn == conn2; 1746 return conn == conn1 || conn == conn2;
1694 })); 1747 }));
1695 } 1748 }
1696 1749
1697 } // namespace cricket 1750 } // namespace cricket
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698