summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/socket/client_socket_pool_base.cc152
-rw-r--r--net/socket/client_socket_pool_base.h33
-rw-r--r--net/socket/client_socket_pool_base_unittest.cc539
-rw-r--r--net/socket/tcp_client_socket_pool.cc5
-rw-r--r--net/socket/tcp_client_socket_pool.h1
-rw-r--r--net/socket/tcp_client_socket_pool_unittest.cc8
6 files changed, 668 insertions, 70 deletions
diff --git a/net/socket/client_socket_pool_base.cc b/net/socket/client_socket_pool_base.cc
index ffe7a1d..037c9ff 100644
--- a/net/socket/client_socket_pool_base.cc
+++ b/net/socket/client_socket_pool_base.cc
@@ -32,14 +32,17 @@ const int kMaxNumLoadLogEntries = 50;
namespace net {
ConnectJob::ConnectJob(const std::string& group_name,
+ const ClientSocketHandle* key_handle,
base::TimeDelta timeout_duration,
Delegate* delegate,
LoadLog* load_log)
: group_name_(group_name),
+ key_handle_(key_handle),
timeout_duration_(timeout_duration),
delegate_(delegate),
load_log_(load_log) {
DCHECK(!group_name.empty());
+ DCHECK(key_handle);
DCHECK(delegate);
}
@@ -90,6 +93,8 @@ void ConnectJob::OnTimeout() {
namespace internal {
+bool ClientSocketPoolBaseHelper::g_late_binding = false;
+
ClientSocketPoolBaseHelper::ClientSocketPoolBaseHelper(
int max_sockets,
int max_sockets_per_group,
@@ -115,8 +120,8 @@ ClientSocketPoolBaseHelper::ClientSocketPoolBaseHelper(
}
ClientSocketPoolBaseHelper::~ClientSocketPoolBaseHelper() {
- CancelAllConnectJobs();
-
+ if (g_late_binding)
+ CancelAllConnectJobs();
// Clean up any idle sockets. Assert that we have no remaining active
// sockets or pending requests. They should have all been cleaned up prior
// to the manager being destroyed.
@@ -151,7 +156,7 @@ ClientSocketPoolBaseHelper::RemoveRequestFromQueue(
const Request* req = *it;
LoadLog::EndEvent(req->load_log(),
- LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE);
+ LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE);
pending_requests->erase(it);
return req;
@@ -203,13 +208,17 @@ int ClientSocketPoolBaseHelper::RequestSocket(
// See if we already have enough connect jobs or sockets that will be released
// soon.
- if (group.HasReleasingSockets()) {
+ if (g_late_binding && group.HasReleasingSockets()) {
InsertRequestIntoQueue(request, &group.pending_requests);
return ERR_IO_PENDING;
}
// We couldn't find a socket to reuse, so allocate and connect a new one.
- scoped_refptr<LoadLog> job_load_log = new LoadLog(kMaxNumLoadLogEntries);
+
+ // If we aren't using late binding, the job lines up with a request so
+ // just write directly into the request's LoadLog.
+ scoped_refptr<LoadLog> job_load_log = g_late_binding ?
+ new LoadLog(kMaxNumLoadLogEntries) : request->load_log();
scoped_ptr<ConnectJob> connect_job(
connect_job_factory_->NewConnectJob(group_name, *request, this,
@@ -217,7 +226,7 @@ int ClientSocketPoolBaseHelper::RequestSocket(
int rv = connect_job->Connect();
- if (rv != ERR_IO_PENDING && request->load_log())
+ if (g_late_binding && rv != ERR_IO_PENDING && request->load_log())
request->load_log()->Append(job_load_log);
if (rv == OK) {
@@ -227,8 +236,14 @@ int ClientSocketPoolBaseHelper::RequestSocket(
connecting_socket_count_++;
ConnectJob* job = connect_job.release();
- CHECK(!ContainsKey(connect_job_map_, handle));
- InsertRequestIntoQueue(request, &group.pending_requests);
+ if (g_late_binding) {
+ CHECK(!ContainsKey(connect_job_map_, handle));
+ InsertRequestIntoQueue(request, &group.pending_requests);
+ } else {
+ group.connecting_requests[handle] = request;
+ CHECK(!ContainsKey(connect_job_map_, handle));
+ connect_job_map_[handle] = job;
+ }
group.jobs.insert(job);
} else if (group.IsEmpty()) {
group_map_.erase(group_name);
@@ -251,14 +266,29 @@ void ClientSocketPoolBaseHelper::CancelRequest(
LoadLog::AddEvent(req->load_log(), LoadLog::TYPE_CANCELLED);
LoadLog::EndEvent(req->load_log(), LoadLog::TYPE_SOCKET_POOL);
delete req;
- if (group.jobs.size() > group.pending_requests.size() + 1) {
+ if (g_late_binding &&
+ group.jobs.size() > group.pending_requests.size() + 1) {
// TODO(willchan): Cancel the job in the earliest LoadState.
- RemoveConnectJob(*group.jobs.begin(), &group);
+ RemoveConnectJob(handle, *group.jobs.begin(), &group);
OnAvailableSocketSlot(group_name, &group);
}
return;
}
}
+
+ if (!g_late_binding) {
+ // It's invalid to cancel a non-existent request.
+ CHECK(ContainsKey(group.connecting_requests, handle));
+
+ RequestMap::iterator map_it = group.connecting_requests.find(handle);
+ if (map_it != group.connecting_requests.end()) {
+ scoped_refptr<LoadLog> log(map_it->second->load_log());
+ LoadLog::AddEvent(log, LoadLog::TYPE_CANCELLED);
+ LoadLog::EndEvent(log, LoadLog::TYPE_SOCKET_POOL);
+ RemoveConnectJob(handle, NULL, &group);
+ OnAvailableSocketSlot(group_name, &group);
+ }
+ }
}
void ClientSocketPoolBaseHelper::ReleaseSocket(const std::string& group_name,
@@ -312,7 +342,7 @@ LoadState ClientSocketPoolBaseHelper::GetLoadState(
RequestQueue::const_iterator it = group.pending_requests.begin();
for (size_t i = 0; it != group.pending_requests.end(); ++it, ++i) {
if ((*it)->handle() == handle) {
- if (i < group.jobs.size()) {
+ if (g_late_binding && i < group.jobs.size()) {
LoadState max_state = LOAD_STATE_IDLE;
for (ConnectJobSet::const_iterator job_it = group.jobs.begin();
job_it != group.jobs.end(); ++job_it) {
@@ -450,38 +480,67 @@ void ClientSocketPoolBaseHelper::OnConnectJobComplete(
CHECK(group_it != group_map_.end());
Group& group = group_it->second;
+ const ClientSocketHandle* const key_handle = job->key_handle();
scoped_ptr<ClientSocket> socket(job->ReleaseSocket());
- scoped_refptr<LoadLog> job_load_log(job->load_log());
- RemoveConnectJob(job, &group);
+ if (g_late_binding) {
+ scoped_refptr<LoadLog> job_load_log(job->load_log());
+ RemoveConnectJob(key_handle, job, &group);
- LoadLog::EndEvent(job_load_log, LoadLog::TYPE_SOCKET_POOL);
-
- if (result == OK) {
- DCHECK(socket.get());
+ scoped_ptr<const Request> r;
if (!group.pending_requests.empty()) {
- scoped_ptr<const Request> r(RemoveRequestFromQueue(
+ r.reset(RemoveRequestFromQueue(
group.pending_requests.begin(), &group.pending_requests));
+
if (r->load_log())
r->load_log()->Append(job_load_log);
- HandOutSocket(
- socket.release(), false /* unused socket */, r->handle(),
- base::TimeDelta(), &group);
- r->callback()->Run(result);
+
+ LoadLog::EndEvent(r->load_log(), LoadLog::TYPE_SOCKET_POOL);
+ }
+
+ if (result == OK) {
+ DCHECK(socket.get());
+ if (r.get()) {
+ HandOutSocket(
+ socket.release(), false /* unused socket */, r->handle(),
+ base::TimeDelta(), &group);
+ r->callback()->Run(result);
+ } else {
+ AddIdleSocket(socket.release(), false /* unused socket */, &group);
+ OnAvailableSocketSlot(group_name, &group);
+ }
} else {
- AddIdleSocket(socket.release(), false /* unused socket */, &group);
- OnAvailableSocketSlot(group_name, &group);
+ DCHECK(!socket.get());
+ if (r.get())
+ r->callback()->Run(result);
+ MaybeOnAvailableSocketSlot(group_name);
}
- } else {
+
+ return;
+ }
+
+ RequestMap* request_map = &group.connecting_requests;
+ RequestMap::iterator it = request_map->find(key_handle);
+ CHECK(it != request_map->end());
+ const Request* request = it->second;
+ ClientSocketHandle* const handle = request->handle();
+ CompletionCallback* const callback = request->callback();
+
+ LoadLog::EndEvent(request->load_log(), LoadLog::TYPE_SOCKET_POOL);
+
+ RemoveConnectJob(key_handle, job, &group);
+
+ if (result != OK) {
DCHECK(!socket.get());
- if (!group.pending_requests.empty()) {
- scoped_ptr<const Request> r(RemoveRequestFromQueue(
- group.pending_requests.begin(), &group.pending_requests));
- if (r->load_log())
- r->load_log()->Append(job_load_log);
- r->callback()->Run(result);
- }
+ callback->Run(result); // |group| is not necessarily valid after this.
+ // |group| may be invalid after the callback, we need to search
+ // |group_map_| again.
MaybeOnAvailableSocketSlot(group_name);
+ } else {
+ DCHECK(socket.get());
+ HandOutSocket(socket.release(), false /* not reused */, handle,
+ base::TimeDelta(), &group);
+ callback->Run(result);
}
}
@@ -489,13 +548,30 @@ void ClientSocketPoolBaseHelper::OnIPAddressChanged() {
CloseIdleSockets();
}
-void ClientSocketPoolBaseHelper::RemoveConnectJob(const ConnectJob *job,
- Group* group) {
+void ClientSocketPoolBaseHelper::EnableLateBindingOfSockets(bool enabled) {
+ g_late_binding = enabled;
+}
+
+void ClientSocketPoolBaseHelper::RemoveConnectJob(
+ const ClientSocketHandle* handle, const ConnectJob *job, Group* group) {
CHECK(connecting_socket_count_ > 0);
connecting_socket_count_--;
- DCHECK(job);
- delete job;
+ if (g_late_binding) {
+ DCHECK(job);
+ delete job;
+ } else {
+ ConnectJobMap::iterator it = connect_job_map_.find(handle);
+ CHECK(it != connect_job_map_.end());
+ job = it->second;
+ delete job;
+ connect_job_map_.erase(it);
+ RequestMap::iterator map_it = group->connecting_requests.find(handle);
+ CHECK(map_it != group->connecting_requests.end());
+ const Request* request = map_it->second;
+ delete request;
+ group->connecting_requests.erase(map_it);
+ }
if (group) {
DCHECK(ContainsKey(group->jobs, job));
@@ -603,4 +679,8 @@ bool ClientSocketPoolBaseHelper::ReachedMaxSocketsLimit() const {
} // namespace internal
+void EnableLateBindingOfSockets(bool enabled) {
+ internal::ClientSocketPoolBaseHelper::EnableLateBindingOfSockets(enabled);
+}
+
} // namespace net
diff --git a/net/socket/client_socket_pool_base.h b/net/socket/client_socket_pool_base.h
index c3440fc..8132065 100644
--- a/net/socket/client_socket_pool_base.h
+++ b/net/socket/client_socket_pool_base.h
@@ -65,6 +65,7 @@ class ConnectJob {
// A |timeout_duration| of 0 corresponds to no timeout.
ConnectJob(const std::string& group_name,
+ const ClientSocketHandle* key_handle,
base::TimeDelta timeout_duration,
Delegate* delegate,
LoadLog* load_log);
@@ -72,6 +73,7 @@ class ConnectJob {
// Accessors
const std::string& group_name() const { return group_name_; }
+ const ClientSocketHandle* key_handle() const { return key_handle_; }
LoadLog* load_log() { return load_log_; }
// Releases |socket_| to the client. On connection error, this should return
@@ -100,6 +102,8 @@ class ConnectJob {
void OnTimeout();
const std::string group_name_;
+ // Temporarily needed until we switch to late binding.
+ const ClientSocketHandle* const key_handle_;
const base::TimeDelta timeout_duration_;
// Timer to abort jobs that take too long.
base::OneShotTimer<ConnectJob> timer_;
@@ -205,6 +209,14 @@ class ClientSocketPoolBaseHelper
// NetworkChangeNotifier::Observer methods:
virtual void OnIPAddressChanged();
+ // Enables late binding of sockets. In this mode, socket requests are
+ // decoupled from socket connection jobs. A socket request may initiate a
+ // socket connection job, but there is no guarantee that that socket
+ // connection will service the request (for example, a released socket may
+ // service the request sooner, or a higher priority request may come in
+ // afterward and receive the socket from the job).
+ static void EnableLateBindingOfSockets(bool enabled);
+
// For testing.
bool may_have_stalled_group() const { return may_have_stalled_group_; }
@@ -309,8 +321,14 @@ class ClientSocketPoolBaseHelper
CleanupIdleSockets(false);
}
- // Removes |job| from |connect_job_set_|. Also updates |group| if non-NULL.
- void RemoveConnectJob(const ConnectJob* job, Group* group);
+ // Removes the ConnectJob corresponding to |handle| from the
+ // |connect_job_map_| or |connect_job_set_| depending on whether or not late
+ // binding is enabled. |job| must be non-NULL when late binding is
+ // enabled. Also updates |group| if non-NULL. When late binding is disabled,
+ // this will also delete the Request from |group->connecting_requests|.
+ void RemoveConnectJob(const ClientSocketHandle* handle,
+ const ConnectJob* job,
+ Group* group);
// Same as OnAvailableSocketSlot except it looks up the Group first to see if
// it's there.
@@ -388,6 +406,9 @@ class ClientSocketPoolBaseHelper
const scoped_ptr<ConnectJobFactory> connect_job_factory_;
const scoped_refptr<NetworkChangeNotifier> network_change_notifier_;
+
+ // Controls whether or not we use late binding of sockets.
+ static bool g_late_binding;
};
} // namespace internal
@@ -557,6 +578,14 @@ class ClientSocketPoolBase {
DISALLOW_COPY_AND_ASSIGN(ClientSocketPoolBase);
};
+// Enables late binding of sockets. In this mode, socket requests are
+// decoupled from socket connection jobs. A socket request may initiate a
+// socket connection job, but there is no guarantee that that socket
+// connection will service the request (for example, a released socket may
+// service the request sooner, or a higher priority request may come in
+// afterward and receive the socket from the job).
+void EnableLateBindingOfSockets(bool enabled);
+
} // namespace net
#endif // NET_SOCKET_CLIENT_SOCKET_POOL_BASE_H_
diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc
index bbbb097..6f3f5ef 100644
--- a/net/socket/client_socket_pool_base_unittest.cc
+++ b/net/socket/client_socket_pool_base_unittest.cc
@@ -117,7 +117,8 @@ class TestConnectJob : public ConnectJob {
ConnectJob::Delegate* delegate,
MockClientSocketFactory* client_socket_factory,
LoadLog* load_log)
- : ConnectJob(group_name, timeout_duration, delegate, load_log),
+ : ConnectJob(group_name, request.handle(), timeout_duration,
+ delegate, load_log),
job_type_(job_type),
client_socket_factory_(client_socket_factory),
method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
@@ -428,6 +429,8 @@ class ClientSocketPoolBaseTest : public ClientSocketPoolTest {
pool_ = NULL;
requests_.reset();
+ EnableLateBindingOfSockets(false);
+
ClientSocketPoolTest::TearDown();
}
@@ -515,6 +518,29 @@ TEST_F(ClientSocketPoolBaseTest, BasicSynchronous) {
ExpectLogContains(log, 3, LoadLog::TYPE_SOCKET_POOL, LoadLog::PHASE_END);
}
+TEST_F(ClientSocketPoolBaseTest, BasicAsynchronous) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+ scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
+ TestSocketRequest req(&request_order_, &completion_count_);
+ int rv = InitHandle(req.handle(), "a", LOW, &req, pool_.get(), log);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_EQ(LOAD_STATE_CONNECTING, pool_->GetLoadState("a", req.handle()));
+ EXPECT_EQ(OK, req.WaitForResult());
+ EXPECT_TRUE(req.handle()->is_initialized());
+ EXPECT_TRUE(req.handle()->socket());
+ req.handle()->Reset();
+
+ EXPECT_EQ(4u, log->entries().size());
+ ExpectLogContains(log, 0, LoadLog::TYPE_SOCKET_POOL, LoadLog::PHASE_BEGIN);
+ ExpectLogContains(log, 1, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB,
+ LoadLog::PHASE_BEGIN);
+ ExpectLogContains(log, 2, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB,
+ LoadLog::PHASE_END);
+ ExpectLogContains(log, 3, LoadLog::TYPE_SOCKET_POOL, LoadLog::PHASE_END);
+}
+
TEST_F(ClientSocketPoolBaseTest, InitConnectionFailure) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
@@ -534,6 +560,27 @@ TEST_F(ClientSocketPoolBaseTest, InitConnectionFailure) {
ExpectLogContains(log, 3, LoadLog::TYPE_SOCKET_POOL, LoadLog::PHASE_END);
}
+TEST_F(ClientSocketPoolBaseTest, InitConnectionAsynchronousFailure) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
+ scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
+ TestSocketRequest req(&request_order_, &completion_count_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ InitHandle(req.handle(), "a", kDefaultPriority, &req,
+ pool_.get(), log));
+ EXPECT_EQ(LOAD_STATE_CONNECTING, pool_->GetLoadState("a", req.handle()));
+ EXPECT_EQ(ERR_CONNECTION_FAILED, req.WaitForResult());
+
+ EXPECT_EQ(4u, log->entries().size());
+ ExpectLogContains(log, 0, LoadLog::TYPE_SOCKET_POOL, LoadLog::PHASE_BEGIN);
+ ExpectLogContains(log, 1, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB,
+ LoadLog::PHASE_BEGIN);
+ ExpectLogContains(log, 2, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB,
+ LoadLog::PHASE_END);
+ ExpectLogContains(log, 3, LoadLog::TYPE_SOCKET_POOL, LoadLog::PHASE_END);
+}
+
TEST_F(ClientSocketPoolBaseTest, TotalLimit) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
@@ -836,6 +883,26 @@ TEST_F(ClientSocketPoolBaseTest, CancelRequestClearGroup) {
req.handle()->Reset();
}
+TEST_F(ClientSocketPoolBaseTest, TwoRequestsCancelOne) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+ TestSocketRequest req(&request_order_, &completion_count_);
+ TestSocketRequest req2(&request_order_, &completion_count_);
+
+ EXPECT_EQ(ERR_IO_PENDING,
+ InitHandle(req.handle(), "a", kDefaultPriority, &req,
+ pool_.get(), NULL));
+ EXPECT_EQ(ERR_IO_PENDING,
+ InitHandle(req2.handle(), "a", kDefaultPriority, &req2,
+ pool_.get(), NULL));
+
+ req.handle()->Reset();
+
+ EXPECT_EQ(OK, req2.WaitForResult());
+ req2.handle()->Reset();
+}
+
TEST_F(ClientSocketPoolBaseTest, ConnectCancelConnect) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
@@ -1070,6 +1137,93 @@ TEST_F(ClientSocketPoolBaseTest, CancelActiveRequestThenRequestSocket) {
EXPECT_EQ(2, client_socket_factory_.allocation_count());
}
+// A pending asynchronous job completes, which will free up a socket slot. The
+// next job finishes synchronously. The callback for the asynchronous job
+// should be first though.
+TEST_F(ClientSocketPoolBaseTest, PendingJobCompletionOrder) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+
+ // First two jobs are async.
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
+
+ // Start job 1 (async error).
+ TestSocketRequest req1(&request_order_, &completion_count_);
+ int rv = InitHandle(req1.handle(), "a", kDefaultPriority, &req1,
+ pool_.get(), NULL);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ // Start job 2 (async error).
+ TestSocketRequest req2(&request_order_, &completion_count_);
+ rv = InitHandle(req2.handle(), "a", kDefaultPriority, &req2,
+ pool_.get(), NULL);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ // The pending job is sync.
+ connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
+
+ // Request 3 does not have a ConnectJob yet. It's just pending.
+ TestSocketRequest req3(&request_order_, &completion_count_);
+ rv = InitHandle(
+ req3.handle(), "a", kDefaultPriority, &req3, pool_.get(), NULL);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ EXPECT_EQ(ERR_CONNECTION_FAILED, req1.WaitForResult());
+ EXPECT_EQ(ERR_CONNECTION_FAILED, req2.WaitForResult());
+ EXPECT_EQ(OK, req3.WaitForResult());
+
+ ASSERT_EQ(3U, request_order_.size());
+
+ // After job 1 finishes unsuccessfully, it will try to process the pending
+ // requests queue, so it starts up job 3 for request 3. This job
+ // synchronously succeeds, so the request order is 1, 3, 2.
+ EXPECT_EQ(&req1, request_order_[0]);
+ EXPECT_EQ(&req2, request_order_[2]);
+ EXPECT_EQ(&req3, request_order_[1]);
+}
+
+// When a ConnectJob is coupled to a request, even if a free socket becomes
+// available, the request will be serviced by the ConnectJob.
+TEST_F(ClientSocketPoolBaseTest, ReleaseSockets) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+ EnableLateBindingOfSockets(false);
+
+ // Start job 1 (async OK)
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+
+ TestSocketRequest req1(&request_order_, &completion_count_);
+ int rv = InitHandle(req1.handle(), "a", kDefaultPriority, &req1,
+ pool_.get(), NULL);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_EQ(OK, req1.WaitForResult());
+
+ // Job 1 finished OK. Start job 2 (also async OK). Release socket 1.
+ connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
+
+ TestSocketRequest req2(&request_order_, &completion_count_);
+ rv = InitHandle(req2.handle(), "a", kDefaultPriority, &req2,
+ pool_.get(), NULL);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ req1.handle()->Reset();
+ MessageLoop::current()->RunAllPending(); // Run the DoReleaseSocket()
+
+ // Job 2 is pending. Start request 3 (which has no associated job since it
+ // will use the idle socket).
+
+ TestSocketRequest req3(&request_order_, &completion_count_);
+ rv = InitHandle(req3.handle(), "a", kDefaultPriority, &req3,
+ pool_.get(), NULL);
+ EXPECT_EQ(OK, rv);
+
+ EXPECT_FALSE(req2.handle()->socket());
+ client_socket_factory_.SignalJobs();
+ EXPECT_EQ(OK, req2.WaitForResult());
+
+ ASSERT_EQ(2U, request_order_.size());
+ EXPECT_EQ(&req1, request_order_[0]);
+ EXPECT_EQ(&req2, request_order_[1]);
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+}
+
// Regression test for http://crbug.com/17985.
TEST_F(ClientSocketPoolBaseTest, GroupWithPendingRequestsIsNotEmpty) {
const int kMaxSockets = 3;
@@ -1109,10 +1263,70 @@ class ClientSocketPoolBaseTest_LateBinding : public ClientSocketPoolBaseTest {
protected:
virtual void SetUp() {
ClientSocketPoolBaseTest::SetUp();
+ EnableLateBindingOfSockets(true);
}
};
-TEST_F(ClientSocketPoolBaseTest, BasicAsynchronous) {
+// Even though a timeout is specified, it doesn't time out on a synchronous
+// completion.
+TEST_F(ClientSocketPoolBaseTest_LateBinding,
+ ConnectJob_NoTimeoutOnSynchronousCompletion) {
+ TestConnectJobDelegate delegate;
+ ClientSocketHandle ignored;
+ TestClientSocketPoolBase::Request request(&ignored, NULL, LOWEST, NULL,
+ NULL);
+ scoped_ptr<TestConnectJob> job(
+ new TestConnectJob(TestConnectJob::kMockJob,
+ "a",
+ request,
+ base::TimeDelta::FromMicroseconds(1),
+ &delegate,
+ &client_socket_factory_,
+ NULL));
+ EXPECT_EQ(OK, job->Connect());
+}
+
+TEST_F(ClientSocketPoolBaseTest_LateBinding, ConnectJob_TimedOut) {
+ TestConnectJobDelegate delegate;
+ ClientSocketHandle ignored;
+ TestClientSocketPoolBase::Request request(&ignored, NULL, LOWEST, NULL,
+ NULL);
+ // Deleted by TestConnectJobDelegate.
+ TestConnectJob* job =
+ new TestConnectJob(TestConnectJob::kMockPendingJob,
+ "a",
+ request,
+ base::TimeDelta::FromMicroseconds(1),
+ &delegate,
+ &client_socket_factory_,
+ NULL);
+ ASSERT_EQ(ERR_IO_PENDING, job->Connect());
+ PlatformThread::Sleep(1);
+ EXPECT_EQ(ERR_TIMED_OUT, delegate.WaitForResult());
+}
+
+TEST_F(ClientSocketPoolBaseTest_LateBinding, BasicSynchronous) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+
+ TestCompletionCallback callback;
+ ClientSocketHandle handle;
+ scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
+ EXPECT_EQ(OK, InitHandle(&handle, "a", kDefaultPriority, &callback,
+ pool_.get(), log));
+ EXPECT_TRUE(handle.is_initialized());
+ EXPECT_TRUE(handle.socket());
+ handle.Reset();
+
+ EXPECT_EQ(4u, log->entries().size());
+ ExpectLogContains(log, 0, LoadLog::TYPE_SOCKET_POOL, LoadLog::PHASE_BEGIN);
+ ExpectLogContains(log, 1, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB,
+ LoadLog::PHASE_BEGIN);
+ ExpectLogContains(log, 2, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB,
+ LoadLog::PHASE_END);
+ ExpectLogContains(log, 3, LoadLog::TYPE_SOCKET_POOL, LoadLog::PHASE_END);
+}
+
+TEST_F(ClientSocketPoolBaseTest_LateBinding, BasicAsynchronous) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
@@ -1127,22 +1341,38 @@ TEST_F(ClientSocketPoolBaseTest, BasicAsynchronous) {
req.handle()->Reset();
EXPECT_EQ(6u, log->entries().size());
- EXPECT_TRUE(LogContains(
- *log, 0, LoadLog::TYPE_SOCKET_POOL, LoadLog::PHASE_BEGIN));
- EXPECT_TRUE(LogContains(
- *log, 1, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE,
- LoadLog::PHASE_BEGIN));
- EXPECT_TRUE(LogContains(
- *log, 2, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE, LoadLog::PHASE_END));
- EXPECT_TRUE(LogContains(
- *log, 3, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB, LoadLog::PHASE_BEGIN));
- EXPECT_TRUE(LogContains(
- *log, 4, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB, LoadLog::PHASE_END));
- EXPECT_TRUE(LogContains(
- *log, 5, LoadLog::TYPE_SOCKET_POOL, LoadLog::PHASE_END));
+ ExpectLogContains(log, 0, LoadLog::TYPE_SOCKET_POOL, LoadLog::PHASE_BEGIN);
+ ExpectLogContains(log, 1, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE,
+ LoadLog::PHASE_BEGIN);
+ ExpectLogContains(log, 2, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE,
+ LoadLog::PHASE_END);
+ ExpectLogContains(log, 3, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB,
+ LoadLog::PHASE_BEGIN);
+ ExpectLogContains(log, 4, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB,
+ LoadLog::PHASE_END);
+ ExpectLogContains(log, 5, LoadLog::TYPE_SOCKET_POOL, LoadLog::PHASE_END);
+}
+
+TEST_F(ClientSocketPoolBaseTest_LateBinding, InitConnectionFailure) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+
+ connect_job_factory_->set_job_type(TestConnectJob::kMockFailingJob);
+ TestSocketRequest req(&request_order_, &completion_count_);
+ scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
+ EXPECT_EQ(ERR_CONNECTION_FAILED,
+ InitHandle(req.handle(), "a", kDefaultPriority, &req,
+ pool_.get(), log));
+
+ EXPECT_EQ(4u, log->entries().size());
+ ExpectLogContains(log, 0, LoadLog::TYPE_SOCKET_POOL, LoadLog::PHASE_BEGIN);
+ ExpectLogContains(log, 1, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB,
+ LoadLog::PHASE_BEGIN);
+ ExpectLogContains(log, 2, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB,
+ LoadLog::PHASE_END);
+ ExpectLogContains(log, 3, LoadLog::TYPE_SOCKET_POOL, LoadLog::PHASE_END);
}
-TEST_F(ClientSocketPoolBaseTest,
+TEST_F(ClientSocketPoolBaseTest_LateBinding,
InitConnectionAsynchronousFailure) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
@@ -1168,7 +1398,71 @@ TEST_F(ClientSocketPoolBaseTest,
ExpectLogContains(log, 5, LoadLog::TYPE_SOCKET_POOL, LoadLog::PHASE_END);
}
-TEST_F(ClientSocketPoolBaseTest, TwoRequestsCancelOne) {
+TEST_F(ClientSocketPoolBaseTest_LateBinding, PendingRequests) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+
+ EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
+ EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOWEST));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", MEDIUM));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", HIGHEST));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOW));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOWEST));
+
+ ReleaseAllConnections(KEEP_ALIVE);
+
+ EXPECT_EQ(kDefaultMaxSocketsPerGroup,
+ client_socket_factory_.allocation_count());
+ EXPECT_EQ(requests_.size() - kDefaultMaxSocketsPerGroup, completion_count_);
+
+ EXPECT_EQ(1, GetOrderOfRequest(1));
+ EXPECT_EQ(2, GetOrderOfRequest(2));
+ EXPECT_EQ(6, GetOrderOfRequest(3));
+ EXPECT_EQ(4, GetOrderOfRequest(4));
+ EXPECT_EQ(3, GetOrderOfRequest(5));
+ EXPECT_EQ(5, GetOrderOfRequest(6));
+ EXPECT_EQ(7, GetOrderOfRequest(7));
+
+ // Make sure we test order of all requests made.
+ EXPECT_EQ(kIndexOutOfBounds, GetOrderOfRequest(8));
+}
+
+TEST_F(ClientSocketPoolBaseTest_LateBinding, PendingRequests_NoKeepAlive) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+
+ EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
+ EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOWEST));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", MEDIUM));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", HIGHEST));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOW));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOWEST));
+
+ ReleaseAllConnections(NO_KEEP_ALIVE);
+
+ for (size_t i = kDefaultMaxSocketsPerGroup; i < requests_.size(); ++i)
+ EXPECT_EQ(OK, requests_[i]->WaitForResult());
+
+ EXPECT_EQ(static_cast<int>(requests_.size()),
+ client_socket_factory_.allocation_count());
+ EXPECT_EQ(requests_.size() - kDefaultMaxSocketsPerGroup, completion_count_);
+}
+
+// This test will start up a RequestSocket() and then immediately Cancel() it.
+// The pending connect job will be cancelled and should not call back into
+// ClientSocketPoolBase.
+TEST_F(ClientSocketPoolBaseTest_LateBinding, CancelRequestClearGroup) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+ TestSocketRequest req(&request_order_, &completion_count_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ InitHandle(req.handle(), "a", kDefaultPriority, &req,
+ pool_.get(), NULL));
+ req.handle()->Reset();
+}
+
+TEST_F(ClientSocketPoolBaseTest_LateBinding, TwoRequestsCancelOne) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
@@ -1219,7 +1513,67 @@ TEST_F(ClientSocketPoolBaseTest, TwoRequestsCancelOne) {
}
-TEST_F(ClientSocketPoolBaseTest, CancelRequestLimitsJobs) {
+TEST_F(ClientSocketPoolBaseTest_LateBinding, ConnectCancelConnect) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+ ClientSocketHandle handle;
+ TestCompletionCallback callback;
+ TestSocketRequest req(&request_order_, &completion_count_);
+
+ EXPECT_EQ(ERR_IO_PENDING,
+ InitHandle(&handle, "a", kDefaultPriority, &callback,
+ pool_.get(), NULL));
+
+ handle.Reset();
+
+ TestCompletionCallback callback2;
+ EXPECT_EQ(ERR_IO_PENDING,
+ InitHandle(&handle, "a", kDefaultPriority, &callback2,
+ pool_.get(), NULL));
+
+ EXPECT_EQ(OK, callback2.WaitForResult());
+ EXPECT_FALSE(callback.have_result());
+
+ handle.Reset();
+}
+
+TEST_F(ClientSocketPoolBaseTest_LateBinding, CancelRequest) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+
+ EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
+ EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOWEST));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", MEDIUM));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", HIGHEST));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOW));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOWEST));
+
+ // Cancel a request.
+ size_t index_to_cancel = kDefaultMaxSocketsPerGroup + 2;
+ EXPECT_FALSE(requests_[index_to_cancel]->handle()->is_initialized());
+ requests_[index_to_cancel]->handle()->Reset();
+
+ ReleaseAllConnections(KEEP_ALIVE);
+
+ EXPECT_EQ(kDefaultMaxSocketsPerGroup,
+ client_socket_factory_.allocation_count());
+ EXPECT_EQ(requests_.size() - kDefaultMaxSocketsPerGroup - 1,
+ completion_count_);
+
+ EXPECT_EQ(1, GetOrderOfRequest(1));
+ EXPECT_EQ(2, GetOrderOfRequest(2));
+ EXPECT_EQ(5, GetOrderOfRequest(3));
+ EXPECT_EQ(3, GetOrderOfRequest(4));
+ EXPECT_EQ(kRequestNotFound, GetOrderOfRequest(5)); // Canceled request.
+ EXPECT_EQ(4, GetOrderOfRequest(6));
+ EXPECT_EQ(6, GetOrderOfRequest(7));
+
+ // Make sure we test order of all requests made.
+ EXPECT_EQ(kIndexOutOfBounds, GetOrderOfRequest(8));
+}
+
+TEST_F(ClientSocketPoolBaseTest_LateBinding, CancelRequestLimitsJobs) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
@@ -1241,9 +1595,114 @@ TEST_F(ClientSocketPoolBaseTest, CancelRequestLimitsJobs) {
EXPECT_EQ(kDefaultMaxSocketsPerGroup - 1, pool_->NumConnectJobsInGroup("a"));
}
+TEST_F(ClientSocketPoolBaseTest_LateBinding, RequestPendingJobTwice) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+ ClientSocketHandle handle;
+ RequestSocketCallback callback(
+ &handle, pool_.get(), connect_job_factory_,
+ TestConnectJob::kMockPendingJob);
+ int rv = InitHandle(
+ &handle, "a", kDefaultPriority, &callback, pool_.get(), NULL);
+ ASSERT_EQ(ERR_IO_PENDING, rv);
+
+ EXPECT_EQ(OK, callback.WaitForResult());
+}
+
+TEST_F(ClientSocketPoolBaseTest_LateBinding, RequestPendingJobThenSynchronous) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+ ClientSocketHandle handle;
+ RequestSocketCallback callback(
+ &handle, pool_.get(), connect_job_factory_, TestConnectJob::kMockJob);
+ int rv = InitHandle(
+ &handle, "a", kDefaultPriority, &callback,
+ pool_.get(), NULL);
+ ASSERT_EQ(ERR_IO_PENDING, rv);
+
+ EXPECT_EQ(OK, callback.WaitForResult());
+}
+
+// Make sure that pending requests get serviced after active requests get
+// cancelled.
+TEST_F(ClientSocketPoolBaseTest_LateBinding,
+ CancelActiveRequestWithPendingRequests) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
+
+ // Now, kDefaultMaxSocketsPerGroup requests should be active.
+ // Let's cancel them.
+ for (int i = 0; i < kDefaultMaxSocketsPerGroup; ++i) {
+ ASSERT_FALSE(requests_[i]->handle()->is_initialized());
+ requests_[i]->handle()->Reset();
+ }
+
+ // Let's wait for the rest to complete now.
+ for (size_t i = kDefaultMaxSocketsPerGroup; i < requests_.size(); ++i) {
+ EXPECT_EQ(OK, requests_[i]->WaitForResult());
+ requests_[i]->handle()->Reset();
+ }
+
+ EXPECT_EQ(requests_.size() - kDefaultMaxSocketsPerGroup, completion_count_);
+}
+
+// Make sure that pending requests get serviced after active requests fail.
+TEST_F(ClientSocketPoolBaseTest_LateBinding,
+ FailingActiveRequestWithPendingRequests) {
+ const int kMaxSockets = 5;
+ CreatePool(kMaxSockets, kDefaultMaxSocketsPerGroup);
+
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
+
+ const int kNumberOfRequests = 2 * kDefaultMaxSocketsPerGroup + 1;
+ ASSERT_LE(kNumberOfRequests, kMaxSockets); // Otherwise the test hangs.
+
+ // Queue up all the requests
+ for (int i = 0; i < kNumberOfRequests; ++i)
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
+
+ for (int i = 0; i < kNumberOfRequests; ++i)
+ EXPECT_EQ(ERR_CONNECTION_FAILED, requests_[i]->WaitForResult());
+}
+
+TEST_F(ClientSocketPoolBaseTest_LateBinding,
+ CancelActiveRequestThenRequestSocket) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+
+ TestSocketRequest req(&request_order_, &completion_count_);
+ int rv = InitHandle(req.handle(), "a", kDefaultPriority, &req,
+ pool_.get(), NULL);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ // Cancel the active request.
+ req.handle()->Reset();
+
+ rv = InitHandle(req.handle(), "a", kDefaultPriority, &req,
+ pool_.get(), NULL);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_EQ(OK, req.WaitForResult());
+
+ EXPECT_FALSE(req.handle()->is_reused());
+ EXPECT_EQ(1U, completion_count_);
+ EXPECT_EQ(2, client_socket_factory_.allocation_count());
+}
+
// When requests and ConnectJobs are not coupled, the request will get serviced
// by whatever comes first.
-TEST_F(ClientSocketPoolBaseTest, ReleaseSockets) {
+TEST_F(ClientSocketPoolBaseTest_LateBinding, ReleaseSockets) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
// Start job 1 (async OK)
@@ -1290,7 +1749,7 @@ TEST_F(ClientSocketPoolBaseTest, ReleaseSockets) {
// The requests are not coupled to the jobs. So, the requests should finish in
// their priority / insertion order.
-TEST_F(ClientSocketPoolBaseTest, PendingJobCompletionOrder) {
+TEST_F(ClientSocketPoolBaseTest_LateBinding, PendingJobCompletionOrder) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
// First two jobs are async.
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
@@ -1323,7 +1782,7 @@ TEST_F(ClientSocketPoolBaseTest, PendingJobCompletionOrder) {
EXPECT_EQ(&req3, request_order_[2]);
}
-TEST_F(ClientSocketPoolBaseTest, DISABLED_LoadState) {
+TEST_F(ClientSocketPoolBaseTest_LateBinding, DISABLED_LoadState) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(
TestConnectJob::kMockAdvancingLoadStateJob);
@@ -1344,7 +1803,43 @@ TEST_F(ClientSocketPoolBaseTest, DISABLED_LoadState) {
EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, req2.handle()->GetLoadState());
}
-TEST_F(ClientSocketPoolBaseTest, CleanupTimedOutIdleSockets) {
+// Regression test for http://crbug.com/17985.
+TEST_F(ClientSocketPoolBaseTest_LateBinding,
+ GroupWithPendingRequestsIsNotEmpty) {
+ const int kMaxSockets = 3;
+ const int kMaxSocketsPerGroup = 2;
+ CreatePool(kMaxSockets, kMaxSocketsPerGroup);
+
+ const RequestPriority kHighPriority = HIGHEST;
+
+ EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
+ EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
+
+ // This is going to be a pending request in an otherwise empty group.
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
+
+ // Reach the maximum socket limit.
+ EXPECT_EQ(OK, StartRequest("b", kDefaultPriority));
+
+ // Create a stalled group with high priorities.
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", kHighPriority));
+ EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", kHighPriority));
+ EXPECT_TRUE(pool_->base()->may_have_stalled_group());
+
+ // Release the first two sockets from "a", which will make room
+ // for requests from "c". After that "a" will have no active sockets
+ // and one pending request.
+ EXPECT_TRUE(ReleaseOneConnection(KEEP_ALIVE));
+ EXPECT_TRUE(ReleaseOneConnection(KEEP_ALIVE));
+
+ // Closing idle sockets should not get us into trouble, but in the bug
+ // we were hitting a CHECK here.
+ EXPECT_EQ(2, pool_->IdleSocketCountInGroup("a"));
+ pool_->CloseIdleSockets();
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+}
+
+TEST_F(ClientSocketPoolBaseTest_LateBinding, CleanupTimedOutIdleSockets) {
CreatePoolWithIdleTimeouts(
kDefaultMaxSockets, kDefaultMaxSocketsPerGroup,
base::TimeDelta(), // Time out unused sockets immediately.
diff --git a/net/socket/tcp_client_socket_pool.cc b/net/socket/tcp_client_socket_pool.cc
index e37bfe7..a64c073 100644
--- a/net/socket/tcp_client_socket_pool.cc
+++ b/net/socket/tcp_client_socket_pool.cc
@@ -33,12 +33,13 @@ static const int kTCPConnectJobTimeoutInSeconds = 240; // 4 minutes.
TCPConnectJob::TCPConnectJob(
const std::string& group_name,
const HostResolver::RequestInfo& resolve_info,
+ const ClientSocketHandle* handle,
base::TimeDelta timeout_duration,
ClientSocketFactory* client_socket_factory,
HostResolver* host_resolver,
Delegate* delegate,
LoadLog* load_log)
- : ConnectJob(group_name, timeout_duration, delegate, load_log),
+ : ConnectJob(group_name, handle, timeout_duration, delegate, load_log),
resolve_info_(resolve_info),
client_socket_factory_(client_socket_factory),
ALLOW_THIS_IN_INITIALIZER_LIST(
@@ -160,7 +161,7 @@ ConnectJob* TCPClientSocketPool::TCPConnectJobFactory::NewConnectJob(
ConnectJob::Delegate* delegate,
LoadLog* load_log) const {
return new TCPConnectJob(
- group_name, request.params(),
+ group_name, request.params(), request.handle(),
base::TimeDelta::FromSeconds(kTCPConnectJobTimeoutInSeconds),
client_socket_factory_, host_resolver_, delegate, load_log);
}
diff --git a/net/socket/tcp_client_socket_pool.h b/net/socket/tcp_client_socket_pool.h
index 6e36ec3..4da6ea8 100644
--- a/net/socket/tcp_client_socket_pool.h
+++ b/net/socket/tcp_client_socket_pool.h
@@ -26,6 +26,7 @@ class TCPConnectJob : public ConnectJob {
public:
TCPConnectJob(const std::string& group_name,
const HostResolver::RequestInfo& resolve_info,
+ const ClientSocketHandle* handle,
base::TimeDelta timeout_duration,
ClientSocketFactory* client_socket_factory,
HostResolver* host_resolver,
diff --git a/net/socket/tcp_client_socket_pool_unittest.cc b/net/socket/tcp_client_socket_pool_unittest.cc
index cf648f9..8aa9ea0 100644
--- a/net/socket/tcp_client_socket_pool_unittest.cc
+++ b/net/socket/tcp_client_socket_pool_unittest.cc
@@ -500,15 +500,7 @@ class RequestSocketCallback : public CallbackRunner< Tuple1<int> > {
ASSERT_EQ(OK, params.a);
if (!within_callback_) {
- // Don't allow reuse of the socket. Disconnect it and then release it and
- // run through the MessageLoop once to get it completely released.
- handle_->socket()->Disconnect();
handle_->Reset();
- {
- MessageLoop::ScopedNestableTaskAllower nestable(
- MessageLoop::current());
- MessageLoop::current()->RunAllPending();
- }
within_callback_ = true;
int rv = handle_->Init(
"a", HostResolver::RequestInfo("www.google.com", 80), LOWEST,