diff options
author | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-16 22:37:32 +0000 |
---|---|---|
committer | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-16 22:37:32 +0000 |
commit | 4e5540ac3f8e4ac006d956b3033df9b8415f9df5 (patch) | |
tree | 70f1e8e257e5e46198b2efc9b8719bbb9e9b5853 /net/socket | |
parent | 48fae61b8a6c9b741f001d478c595b6c7c0af4d9 (diff) | |
download | chromium_src-4e5540ac3f8e4ac006d956b3033df9b8415f9df5.zip chromium_src-4e5540ac3f8e4ac006d956b3033df9b8415f9df5.tar.gz chromium_src-4e5540ac3f8e4ac006d956b3033df9b8415f9df5.tar.bz2 |
Attempt to close idle connections in higher layer socket pools when a lower layer pool is stalled.
BUG=62364, 92244
TEST=ClientSocketPoolBaseTest.\*CloseIdleSocket\*
Review URL: http://codereview.chromium.org/9999030
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@132478 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/socket')
-rw-r--r-- | net/socket/client_socket_pool_base.cc | 20 | ||||
-rw-r--r-- | net/socket/client_socket_pool_base_unittest.cc | 167 |
2 files changed, 182 insertions, 5 deletions
diff --git a/net/socket/client_socket_pool_base.cc b/net/socket/client_socket_pool_base.cc index ab959cf..f55a017 100644 --- a/net/socket/client_socket_pool_base.cc +++ b/net/socket/client_socket_pool_base.cc @@ -369,11 +369,21 @@ int ClientSocketPoolBaseHelper::RequestSocketInternal( if (preconnecting && !closed) return ERR_PRECONNECT_MAX_SOCKET_LIMIT; } else { - // We could check if we really have a stalled group here, but it requires - // a scan of all groups, so just flip a flag here, and do the check later. - request->net_log().AddEvent( - NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS, NULL); - return ERR_IO_PENDING; + do { + if (!CloseOneIdleConnectionInLayeredPool()) { + // We could check if we really have a stalled group here, but it + // requires a scan of all groups, so return ERR_IO_PENDING, and do + // the check later. + request->net_log().AddEvent( + NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS, NULL); + return ERR_IO_PENDING; + } + } while (ReachedMaxSocketsLimit()); + + // It is possible that CloseOneIdleConnectionInLayeredPool() has deleted + // our Group (see http://crbug.com/109876), so look it up again + // to be safe. + group = GetOrCreateGroup(group_name); } } diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc index 099a1c6..d5689c5 100644 --- a/net/socket/client_socket_pool_base_unittest.cc +++ b/net/socket/client_socket_pool_base_unittest.cc @@ -3416,6 +3416,173 @@ TEST_F(ClientSocketPoolBaseTest, PreconnectWithBackupJob) { EXPECT_EQ(1, pool_->NumActiveSocketsInGroup("a")); } +class MockLayeredPool : public LayeredPool { + public: + MockLayeredPool(TestClientSocketPool* pool, + const std::string& group_name) + : pool_(pool), + params_(new TestSocketParams), + group_name_(group_name), + can_release_connection_(true) { + pool_->AddLayeredPool(this); + } + + ~MockLayeredPool() { + pool_->RemoveLayeredPool(this); + } + + int RequestSocket(TestClientSocketPool* pool) { + return handle_.Init(group_name_, params_, kDefaultPriority, + callback_.callback(), pool, BoundNetLog()); + } + + int RequestSocketWithoutLimits(TestClientSocketPool* pool) { + params_->set_ignore_limits(true); + return handle_.Init(group_name_, params_, kDefaultPriority, + callback_.callback(), pool, BoundNetLog()); + } + + bool ReleaseOneConnection() { + if (!handle_.is_initialized() || !can_release_connection_) { + return false; + } + handle_.socket()->Disconnect(); + handle_.Reset(); + return true; + } + + void set_can_release_connection(bool can_release_connection) { + can_release_connection_ = can_release_connection; + } + + MOCK_METHOD0(CloseOneIdleConnection, bool()); + + private: + TestClientSocketPool* const pool_; + scoped_refptr<TestSocketParams> params_; + ClientSocketHandle handle_; + TestCompletionCallback callback_; + const std::string group_name_; + bool can_release_connection_; +}; + +TEST_F(ClientSocketPoolBaseTest, FailToCloseIdleSocketsNotHeldByLayeredPool) { + CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup); + connect_job_factory_->set_job_type(TestConnectJob::kMockJob); + + MockLayeredPool mock_layered_pool(pool_.get(), "foo"); + EXPECT_EQ(OK, mock_layered_pool.RequestSocket(pool_.get())); + EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection()) + .WillOnce(Return(false)); + EXPECT_FALSE(pool_->CloseOneIdleConnectionInLayeredPool()); +} + +TEST_F(ClientSocketPoolBaseTest, ForciblyCloseIdleSocketsHeldByLayeredPool) { + CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup); + connect_job_factory_->set_job_type(TestConnectJob::kMockJob); + + MockLayeredPool mock_layered_pool(pool_.get(), "foo"); + EXPECT_EQ(OK, mock_layered_pool.RequestSocket(pool_.get())); + EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection()) + .WillOnce(Invoke(&mock_layered_pool, + &MockLayeredPool::ReleaseOneConnection)); + EXPECT_TRUE(pool_->CloseOneIdleConnectionInLayeredPool()); +} + +// This test exercises the codepath which caused http://crbug.com/109876 +TEST_F(ClientSocketPoolBaseTest, + CloseIdleSocketsHeldByLayeredPoolInSameGroupWhenNeeded) { + CreatePool(2, 2); + std::list<TestConnectJob::JobType> job_types; + job_types.push_back(TestConnectJob::kMockJob); + job_types.push_back(TestConnectJob::kMockJob); + job_types.push_back(TestConnectJob::kMockFailingJob); + job_types.push_back(TestConnectJob::kMockJob); + connect_job_factory_->set_job_types(&job_types); + + ClientSocketHandle handle1; + TestCompletionCallback callback1; + EXPECT_EQ(OK, handle1.Init("group1", + params_, + kDefaultPriority, + callback1.callback(), + pool_.get(), + BoundNetLog())); + + MockLayeredPool mock_layered_pool(pool_.get(), "group2"); + EXPECT_EQ(OK, mock_layered_pool.RequestSocket(pool_.get())); + EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection()) + .WillRepeatedly(Invoke(&mock_layered_pool, + &MockLayeredPool::ReleaseOneConnection)); + mock_layered_pool.set_can_release_connection(false); + + // This connection attempt will fail when the next request causes the + // MockLayeredPool to delete the socket it's holding. This request is + // needed to trigger the destruction of the "group2" Group. + ClientSocketHandle handle3; + TestCompletionCallback callback3; + EXPECT_EQ(ERR_IO_PENDING, handle3.Init("group2", + params_, + kDefaultPriority, + callback3.callback(), + pool_.get(), + BoundNetLog())); + + mock_layered_pool.set_can_release_connection(true); + ClientSocketHandle handle4; + TestCompletionCallback callback4; + EXPECT_EQ(OK, handle4.Init("group2", + params_, + kDefaultPriority, + callback4.callback(), + pool_.get(), + BoundNetLog())); +} + +TEST_F(ClientSocketPoolBaseTest, CloseIdleSocketsHeldByLayeredPoolWhenNeeded) { + CreatePool(1, 1); + connect_job_factory_->set_job_type(TestConnectJob::kMockJob); + + MockLayeredPool mock_layered_pool(pool_.get(), "foo"); + EXPECT_EQ(OK, mock_layered_pool.RequestSocket(pool_.get())); + EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection()) + .WillOnce(Invoke(&mock_layered_pool, + &MockLayeredPool::ReleaseOneConnection)); + ClientSocketHandle handle; + TestCompletionCallback callback; + EXPECT_EQ(OK, handle.Init("a", + params_, + kDefaultPriority, + callback.callback(), + pool_.get(), + BoundNetLog())); +} + +TEST_F(ClientSocketPoolBaseTest, + CloseMultipleIdleSocketsHeldByLayeredPoolWhenNeeded) { + CreatePool(1, 1); + connect_job_factory_->set_job_type(TestConnectJob::kMockJob); + + MockLayeredPool mock_layered_pool1(pool_.get(), "foo"); + EXPECT_EQ(OK, mock_layered_pool1.RequestSocket(pool_.get())); + EXPECT_CALL(mock_layered_pool1, CloseOneIdleConnection()) + .WillRepeatedly(Invoke(&mock_layered_pool1, + &MockLayeredPool::ReleaseOneConnection)); + MockLayeredPool mock_layered_pool2(pool_.get(), "bar"); + EXPECT_EQ(OK, mock_layered_pool2.RequestSocketWithoutLimits(pool_.get())); + EXPECT_CALL(mock_layered_pool2, CloseOneIdleConnection()) + .WillRepeatedly(Invoke(&mock_layered_pool2, + &MockLayeredPool::ReleaseOneConnection)); + ClientSocketHandle handle; + TestCompletionCallback callback; + EXPECT_EQ(OK, handle.Init("a", + params_, + kDefaultPriority, + callback.callback(), + pool_.get(), + BoundNetLog())); +} + } // namespace } // namespace net |