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

Unified Diff: webrtc/p2p/base/p2ptransportchannel.cc

Issue 1311433009: A few updates on connection states (Closed) Base URL: https://chromium.googlesource.com/external/webrtc@master
Patch Set: Created 5 years, 3 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 side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698