| Index: net/base/tcp_client_socket_pool.cc
|
| ===================================================================
|
| --- net/base/tcp_client_socket_pool.cc (revision 18948)
|
| +++ net/base/tcp_client_socket_pool.cc (working copy)
|
| @@ -1,555 +0,0 @@
|
| -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "net/base/tcp_client_socket_pool.h"
|
| -
|
| -#include "base/compiler_specific.h"
|
| -#include "base/field_trial.h"
|
| -#include "base/message_loop.h"
|
| -#include "base/time.h"
|
| -#include "base/stl_util-inl.h"
|
| -#include "net/base/client_socket_factory.h"
|
| -#include "net/base/client_socket_handle.h"
|
| -#include "net/base/net_errors.h"
|
| -#include "net/base/tcp_client_socket.h"
|
| -
|
| -using base::TimeDelta;
|
| -
|
| -namespace {
|
| -
|
| -// The timeout value, in seconds, used to clean up idle sockets that can't be
|
| -// reused.
|
| -//
|
| -// Note: It's important to close idle sockets that have received data as soon
|
| -// as possible because the received data may cause BSOD on Windows XP under
|
| -// some conditions. See http://crbug.com/4606.
|
| -const int kCleanupInterval = 10; // DO NOT INCREASE THIS TIMEOUT.
|
| -
|
| -// The maximum duration, in seconds, to keep idle persistent sockets alive.
|
| -const int kIdleTimeout = 300; // 5 minutes.
|
| -
|
| -} // namespace
|
| -
|
| -namespace net {
|
| -
|
| -TCPConnectingSocket::TCPConnectingSocket(
|
| - const std::string& group_name,
|
| - const HostResolver::RequestInfo& resolve_info,
|
| - const ClientSocketHandle* handle,
|
| - ClientSocketFactory* client_socket_factory,
|
| - ClientSocketPoolBase* pool)
|
| - : group_name_(group_name),
|
| - resolve_info_(resolve_info),
|
| - handle_(handle),
|
| - client_socket_factory_(client_socket_factory),
|
| - ALLOW_THIS_IN_INITIALIZER_LIST(
|
| - callback_(this,
|
| - &TCPConnectingSocket::OnIOComplete)),
|
| - pool_(pool),
|
| - resolver_(pool->GetHostResolver()) {}
|
| -
|
| -TCPConnectingSocket::~TCPConnectingSocket() {
|
| - // We don't worry about cancelling the host resolution and TCP connect, since
|
| - // ~SingleRequestHostResolver and ~ClientSocket will take care of it.
|
| -}
|
| -
|
| -int TCPConnectingSocket::Connect() {
|
| - int rv = resolver_.Resolve(resolve_info_, &addresses_, &callback_);
|
| - if (rv != ERR_IO_PENDING)
|
| - rv = OnIOCompleteInternal(rv, true /* synchronous */);
|
| - return rv;
|
| -}
|
| -
|
| -void TCPConnectingSocket::OnIOComplete(int result) {
|
| - OnIOCompleteInternal(result, false /* asynchronous */);
|
| -}
|
| -
|
| -int TCPConnectingSocket::OnIOCompleteInternal(
|
| - int result, bool synchronous) {
|
| - CHECK(result != ERR_IO_PENDING);
|
| -
|
| - ClientSocketPoolBase::Request* request = pool_->GetConnectingRequest(
|
| - group_name_, handle_);
|
| - CHECK(request);
|
| -
|
| - if (result == OK && request->load_state == LOAD_STATE_RESOLVING_HOST) {
|
| - request->load_state = LOAD_STATE_CONNECTING;
|
| - socket_.reset(client_socket_factory_->CreateTCPClientSocket(addresses_));
|
| - connect_start_time_ = base::TimeTicks::Now();
|
| - result = socket_->Connect(&callback_);
|
| - if (result == ERR_IO_PENDING)
|
| - return result;
|
| - }
|
| -
|
| - if (result == OK) {
|
| - CHECK(request->load_state == LOAD_STATE_CONNECTING);
|
| - CHECK(connect_start_time_ != base::TimeTicks());
|
| - base::TimeDelta connect_duration =
|
| - base::TimeTicks::Now() - connect_start_time_;
|
| -
|
| - UMA_HISTOGRAM_CLIPPED_TIMES("Net.TCP_Connection_Latency",
|
| - connect_duration,
|
| - base::TimeDelta::FromMilliseconds(1),
|
| - base::TimeDelta::FromMinutes(10),
|
| - 100);
|
| - }
|
| -
|
| - // Now, we either succeeded at Connect()'ing, or we failed at host resolution
|
| - // or Connect()'ing. Either way, we'll run the callback to alert the client.
|
| -
|
| - CompletionCallback* callback = NULL;
|
| -
|
| - if (result == OK) {
|
| - callback = pool_->OnConnectingRequestComplete(
|
| - group_name_,
|
| - handle_,
|
| - false /* don't deactivate socket */,
|
| - socket_.release());
|
| - } else {
|
| - callback = pool_->OnConnectingRequestComplete(
|
| - group_name_,
|
| - handle_,
|
| - true /* deactivate socket */,
|
| - NULL /* no connected socket to give */);
|
| - }
|
| -
|
| - // |this| is deleted after this point.
|
| -
|
| - CHECK(callback);
|
| -
|
| - if (!synchronous)
|
| - callback->Run(result);
|
| - return result;
|
| -}
|
| -
|
| -ClientSocketPoolBase::ClientSocketPoolBase(
|
| - int max_sockets_per_group,
|
| - HostResolver* host_resolver,
|
| - ConnectingSocketFactory* connecting_socket_factory)
|
| - : idle_socket_count_(0),
|
| - max_sockets_per_group_(max_sockets_per_group),
|
| - host_resolver_(host_resolver),
|
| - connecting_socket_factory_(connecting_socket_factory) {}
|
| -
|
| -ClientSocketPoolBase::~ClientSocketPoolBase() {
|
| - // 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.
|
| - CloseIdleSockets();
|
| - DCHECK(group_map_.empty());
|
| - DCHECK(connecting_socket_map_.empty());
|
| -}
|
| -
|
| -// InsertRequestIntoQueue inserts the request into the queue based on
|
| -// priority. Highest priorities are closest to the front. Older requests are
|
| -// prioritized over requests of equal priority.
|
| -//
|
| -// static
|
| -void ClientSocketPoolBase::InsertRequestIntoQueue(
|
| - const Request& r, RequestQueue* pending_requests) {
|
| - RequestQueue::iterator it = pending_requests->begin();
|
| - while (it != pending_requests->end() && r.priority <= it->priority)
|
| - ++it;
|
| - pending_requests->insert(it, r);
|
| -}
|
| -
|
| -int ClientSocketPoolBase::RequestSocket(
|
| - const std::string& group_name,
|
| - const HostResolver::RequestInfo& resolve_info,
|
| - int priority,
|
| - ClientSocketHandle* handle,
|
| - CompletionCallback* callback) {
|
| - DCHECK(!resolve_info.hostname().empty());
|
| - DCHECK_GE(priority, 0);
|
| - Group& group = group_map_[group_name];
|
| -
|
| - CheckSocketCounts(group);
|
| -
|
| - // Can we make another active socket now?
|
| - if (group.active_socket_count == max_sockets_per_group_) {
|
| - CHECK(callback);
|
| - Request r(handle, callback, priority, resolve_info, LOAD_STATE_IDLE);
|
| - InsertRequestIntoQueue(r, &group.pending_requests);
|
| - return ERR_IO_PENDING;
|
| - }
|
| -
|
| - // OK, we are going to activate one.
|
| - group.active_socket_count++;
|
| -
|
| - while (!group.idle_sockets.empty()) {
|
| - IdleSocket idle_socket = group.idle_sockets.back();
|
| - group.idle_sockets.pop_back();
|
| - DecrementIdleCount();
|
| - if (idle_socket.socket->IsConnectedAndIdle()) {
|
| - // We found one we can reuse!
|
| - handle->set_socket(idle_socket.socket);
|
| - handle->set_is_reused(true);
|
| - group.sockets_handed_out_count++;
|
| - CheckSocketCounts(group);
|
| - return OK;
|
| - }
|
| - delete idle_socket.socket;
|
| - }
|
| -
|
| - // We couldn't find a socket to reuse, so allocate and connect a new one.
|
| -
|
| - CHECK(callback);
|
| - Request r(handle, callback, priority, resolve_info,
|
| - LOAD_STATE_RESOLVING_HOST);
|
| - group.connecting_requests[handle] = r;
|
| -
|
| - CHECK(!ContainsKey(connecting_socket_map_, handle));
|
| -
|
| - ConnectingSocket* connecting_socket =
|
| - connecting_socket_factory_->NewConnectingSocket(group_name, r, this);
|
| - connecting_socket_map_[handle] = connecting_socket;
|
| - int rv = connecting_socket->Connect();
|
| -
|
| - CheckSocketCounts(group);
|
| - return rv;
|
| -}
|
| -
|
| -void ClientSocketPoolBase::CancelRequest(const std::string& group_name,
|
| - const ClientSocketHandle* handle) {
|
| - CHECK(ContainsKey(group_map_, group_name));
|
| -
|
| - Group& group = group_map_[group_name];
|
| -
|
| - CheckSocketCounts(group);
|
| -
|
| - // Search pending_requests for matching handle.
|
| - RequestQueue::iterator it = group.pending_requests.begin();
|
| - for (; it != group.pending_requests.end(); ++it) {
|
| - if (it->handle == handle) {
|
| - group.pending_requests.erase(it);
|
| - return;
|
| - }
|
| - }
|
| -
|
| - // 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()) {
|
| - RemoveConnectingSocket(handle);
|
| -
|
| - group.connecting_requests.erase(map_it);
|
| - group.active_socket_count--;
|
| -
|
| - if (!group.pending_requests.empty()) {
|
| - ProcessPendingRequest(group_name, &group);
|
| - return; // |group| may be invalid after this, so return to be safe.
|
| - }
|
| -
|
| - // Delete group if no longer needed.
|
| - if (group.active_socket_count == 0 && group.idle_sockets.empty()) {
|
| - CHECK(group.pending_requests.empty());
|
| - CHECK(group.connecting_requests.empty());
|
| - group_map_.erase(group_name);
|
| - return; // |group| is invalid after this, so return to be safe.
|
| - }
|
| - }
|
| -
|
| - CheckSocketCounts(group);
|
| -}
|
| -
|
| -void ClientSocketPoolBase::ReleaseSocket(const std::string& group_name,
|
| - ClientSocket* socket) {
|
| - // Run this asynchronously to allow the caller to finish before we let
|
| - // another to begin doing work. This also avoids nasty recursion issues.
|
| - // NOTE: We cannot refer to the handle argument after this method returns.
|
| - MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
|
| - this, &ClientSocketPoolBase::DoReleaseSocket, group_name, socket));
|
| -}
|
| -
|
| -void ClientSocketPoolBase::CloseIdleSockets() {
|
| - CleanupIdleSockets(true);
|
| -}
|
| -
|
| -int ClientSocketPoolBase::IdleSocketCountInGroup(
|
| - const std::string& group_name) const {
|
| - GroupMap::const_iterator i = group_map_.find(group_name);
|
| - CHECK(i != group_map_.end());
|
| -
|
| - return i->second.idle_sockets.size();
|
| -}
|
| -
|
| -LoadState ClientSocketPoolBase::GetLoadState(
|
| - const std::string& group_name,
|
| - const ClientSocketHandle* handle) const {
|
| - if (!ContainsKey(group_map_, group_name)) {
|
| - NOTREACHED() << "ClientSocketPool does not contain group: " << group_name
|
| - << " for handle: " << handle;
|
| - return LOAD_STATE_IDLE;
|
| - }
|
| -
|
| - // Can't use operator[] since it is non-const.
|
| - const Group& group = group_map_.find(group_name)->second;
|
| -
|
| - // Search connecting_requests for matching handle.
|
| - RequestMap::const_iterator map_it = group.connecting_requests.find(handle);
|
| - if (map_it != group.connecting_requests.end()) {
|
| - const LoadState load_state = map_it->second.load_state;
|
| - CHECK(load_state == LOAD_STATE_RESOLVING_HOST ||
|
| - load_state == LOAD_STATE_CONNECTING);
|
| - return load_state;
|
| - }
|
| -
|
| - // Search pending_requests for matching handle.
|
| - RequestQueue::const_iterator it = group.pending_requests.begin();
|
| - for (; it != group.pending_requests.end(); ++it) {
|
| - if (it->handle == handle) {
|
| - CHECK(LOAD_STATE_IDLE == it->load_state);
|
| - // TODO(wtc): Add a state for being on the wait list.
|
| - // See http://www.crbug.com/5077.
|
| - return LOAD_STATE_IDLE;
|
| - }
|
| - }
|
| -
|
| - NOTREACHED();
|
| - return LOAD_STATE_IDLE;
|
| -}
|
| -
|
| -bool ClientSocketPoolBase::IdleSocket::ShouldCleanup(
|
| - base::TimeTicks now) const {
|
| - bool timed_out = (now - start_time) >=
|
| - base::TimeDelta::FromSeconds(kIdleTimeout);
|
| - return timed_out || !socket->IsConnectedAndIdle();
|
| -}
|
| -
|
| -void ClientSocketPoolBase::CleanupIdleSockets(bool force) {
|
| - if (idle_socket_count_ == 0)
|
| - return;
|
| -
|
| - // Current time value. Retrieving it once at the function start rather than
|
| - // inside the inner loop, since it shouldn't change by any meaningful amount.
|
| - base::TimeTicks now = base::TimeTicks::Now();
|
| -
|
| - GroupMap::iterator i = group_map_.begin();
|
| - while (i != group_map_.end()) {
|
| - Group& group = i->second;
|
| -
|
| - std::deque<IdleSocket>::iterator j = group.idle_sockets.begin();
|
| - while (j != group.idle_sockets.end()) {
|
| - if (force || j->ShouldCleanup(now)) {
|
| - delete j->socket;
|
| - j = group.idle_sockets.erase(j);
|
| - DecrementIdleCount();
|
| - } else {
|
| - ++j;
|
| - }
|
| - }
|
| -
|
| - // Delete group if no longer needed.
|
| - if (group.active_socket_count == 0 && group.idle_sockets.empty()) {
|
| - CHECK(group.pending_requests.empty());
|
| - CHECK(group.connecting_requests.empty());
|
| - group_map_.erase(i++);
|
| - } else {
|
| - ++i;
|
| - }
|
| - }
|
| -}
|
| -
|
| -void ClientSocketPoolBase::IncrementIdleCount() {
|
| - if (++idle_socket_count_ == 1)
|
| - timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this,
|
| - &ClientSocketPoolBase::OnCleanupTimerFired);
|
| -}
|
| -
|
| -void ClientSocketPoolBase::DecrementIdleCount() {
|
| - if (--idle_socket_count_ == 0)
|
| - timer_.Stop();
|
| -}
|
| -
|
| -void ClientSocketPoolBase::DoReleaseSocket(const std::string& group_name,
|
| - ClientSocket* socket) {
|
| - GroupMap::iterator i = group_map_.find(group_name);
|
| - CHECK(i != group_map_.end());
|
| -
|
| - Group& group = i->second;
|
| -
|
| - CHECK(group.active_socket_count > 0);
|
| - CheckSocketCounts(group);
|
| -
|
| - group.active_socket_count--;
|
| - group.sockets_handed_out_count--;
|
| -
|
| - const bool can_reuse = socket->IsConnectedAndIdle();
|
| - if (can_reuse) {
|
| - IdleSocket idle_socket;
|
| - idle_socket.socket = socket;
|
| - idle_socket.start_time = base::TimeTicks::Now();
|
| -
|
| - group.idle_sockets.push_back(idle_socket);
|
| - IncrementIdleCount();
|
| - } else {
|
| - delete socket;
|
| - }
|
| -
|
| - // Process one pending request.
|
| - if (!group.pending_requests.empty()) {
|
| - ProcessPendingRequest(group_name, &group);
|
| - return;
|
| - }
|
| -
|
| - // Delete group if no longer needed.
|
| - if (group.active_socket_count == 0 && group.idle_sockets.empty()) {
|
| - CHECK(group.pending_requests.empty());
|
| - CHECK(group.connecting_requests.empty());
|
| - group_map_.erase(i);
|
| - } else {
|
| - CheckSocketCounts(group);
|
| - }
|
| -}
|
| -
|
| -ClientSocketPoolBase::Request* ClientSocketPoolBase::GetConnectingRequest(
|
| - const std::string& group_name, const ClientSocketHandle* handle) {
|
| - GroupMap::iterator group_it = group_map_.find(group_name);
|
| - if (group_it == group_map_.end())
|
| - return NULL;
|
| -
|
| - Group& group = group_it->second;
|
| -
|
| - RequestMap* request_map = &group.connecting_requests;
|
| - RequestMap::iterator it = request_map->find(handle);
|
| - if (it == request_map->end())
|
| - return NULL;
|
| -
|
| - return &it->second;
|
| -}
|
| -
|
| -CompletionCallback* ClientSocketPoolBase::OnConnectingRequestComplete(
|
| - const std::string& group_name,
|
| - const ClientSocketHandle* handle,
|
| - bool deactivate,
|
| - ClientSocket* socket) {
|
| - CHECK((deactivate && !socket) || (!deactivate && socket));
|
| - GroupMap::iterator group_it = group_map_.find(group_name);
|
| - CHECK(group_it != group_map_.end());
|
| - Group& group = group_it->second;
|
| -
|
| - CheckSocketCounts(group);
|
| -
|
| - RequestMap* request_map = &group.connecting_requests;
|
| -
|
| - RequestMap::iterator it = request_map->find(handle);
|
| - CHECK(it != request_map->end());
|
| - Request request = it->second;
|
| - request_map->erase(it);
|
| - DCHECK_EQ(request.handle, handle);
|
| -
|
| - if (deactivate) {
|
| - group.active_socket_count--;
|
| -
|
| - // Delete group if no longer needed.
|
| - if (group.active_socket_count == 0 && group.idle_sockets.empty()) {
|
| - DCHECK(group.pending_requests.empty());
|
| - DCHECK(group.connecting_requests.empty());
|
| - group_map_.erase(group_name);
|
| - } else {
|
| - CheckSocketCounts(group);
|
| - }
|
| - } else {
|
| - request.handle->set_socket(socket);
|
| - request.handle->set_is_reused(false);
|
| - group.sockets_handed_out_count++;
|
| -
|
| - CheckSocketCounts(group);
|
| - }
|
| -
|
| - RemoveConnectingSocket(request.handle);
|
| -
|
| - return request.callback;
|
| -}
|
| -
|
| -// static
|
| -void ClientSocketPoolBase::CheckSocketCounts(const Group& group) {
|
| - CHECK(group.active_socket_count ==
|
| - group.sockets_handed_out_count +
|
| - static_cast<int>(group.connecting_requests.size()))
|
| - << "[active_socket_count: " << group.active_socket_count
|
| - << " ] [sockets_handed_out_count: " << group.sockets_handed_out_count
|
| - << " ] [connecting_requests size: " << group.connecting_requests.size();
|
| -}
|
| -
|
| -void ClientSocketPoolBase::RemoveConnectingSocket(
|
| - const ClientSocketHandle* handle) {
|
| - ConnectingSocketMap::iterator it = connecting_socket_map_.find(handle);
|
| - CHECK(it != connecting_socket_map_.end());
|
| - delete it->second;
|
| - connecting_socket_map_.erase(it);
|
| -}
|
| -
|
| -void ClientSocketPoolBase::ProcessPendingRequest(const std::string& group_name,
|
| - Group* group) {
|
| - Request r = group->pending_requests.front();
|
| - group->pending_requests.pop_front();
|
| -
|
| - int rv = RequestSocket(
|
| - group_name, r.resolve_info, r.priority, r.handle, r.callback);
|
| -
|
| - // |group| may be invalid after RequestSocket.
|
| -
|
| - if (rv != ERR_IO_PENDING)
|
| - r.callback->Run(rv);
|
| -}
|
| -
|
| -ConnectingSocket*
|
| -TCPClientSocketPool::TCPConnectingSocketFactory::NewConnectingSocket(
|
| - const std::string& group_name,
|
| - const ClientSocketPoolBase::Request& request,
|
| - ClientSocketPoolBase* pool) const {
|
| - return new TCPConnectingSocket(
|
| - group_name, request.resolve_info, request.handle,
|
| - client_socket_factory_, pool);
|
| -}
|
| -
|
| -TCPClientSocketPool::TCPClientSocketPool(
|
| - int max_sockets_per_group,
|
| - HostResolver* host_resolver,
|
| - ClientSocketFactory* client_socket_factory)
|
| - : base_(new ClientSocketPoolBase(
|
| - max_sockets_per_group, host_resolver,
|
| - new TCPConnectingSocketFactory(client_socket_factory))) {}
|
| -
|
| -TCPClientSocketPool::~TCPClientSocketPool() {}
|
| -
|
| -int TCPClientSocketPool::RequestSocket(
|
| - const std::string& group_name,
|
| - const HostResolver::RequestInfo& resolve_info,
|
| - int priority,
|
| - ClientSocketHandle* handle,
|
| - CompletionCallback* callback) {
|
| - return base_->RequestSocket(
|
| - group_name, resolve_info, priority, handle, callback);
|
| -}
|
| -
|
| -void TCPClientSocketPool::CancelRequest(
|
| - const std::string& group_name,
|
| - const ClientSocketHandle* handle) {
|
| - base_->CancelRequest(group_name, handle);
|
| -}
|
| -
|
| -void TCPClientSocketPool::ReleaseSocket(
|
| - const std::string& group_name,
|
| - ClientSocket* socket) {
|
| - base_->ReleaseSocket(group_name, socket);
|
| -}
|
| -
|
| -void TCPClientSocketPool::CloseIdleSockets() {
|
| - base_->CloseIdleSockets();
|
| -}
|
| -
|
| -int TCPClientSocketPool::IdleSocketCountInGroup(
|
| - const std::string& group_name) const {
|
| - return base_->IdleSocketCountInGroup(group_name);
|
| -}
|
| -
|
| -LoadState TCPClientSocketPool::GetLoadState(
|
| - const std::string& group_name, const ClientSocketHandle* handle) const {
|
| - return base_->GetLoadState(group_name, handle);
|
| -}
|
| -
|
| -} // namespace net
|
|
|