diff options
author | willchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-24 15:47:02 +0000 |
---|---|---|
committer | willchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-24 15:47:02 +0000 |
commit | ff579d4a9e7af6d7c8dea51b9feedcacb7fd2a38 (patch) | |
tree | 7cc49aa2cba1efa0ac313551aa61b7b86d538849 /net | |
parent | c6e4a341394648def8e9a5b0c73ef4979f188bad (diff) | |
download | chromium_src-ff579d4a9e7af6d7c8dea51b9feedcacb7fd2a38.zip chromium_src-ff579d4a9e7af6d7c8dea51b9feedcacb7fd2a38.tar.gz chromium_src-ff579d4a9e7af6d7c8dea51b9feedcacb7fd2a38.tar.bz2 |
Break out classes from tcp_client_socket_pool.h into their own files.
This is just shuffling code around to new files and adjusting header dependencies accordingly.
BUG=http://crbug.com/13289
TEST=none
Review URL: http://codereview.chromium.org/132060
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@19123 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/net.gyp | 2 | ||||
-rw-r--r-- | net/socket/client_socket_pool_base.cc | 384 | ||||
-rw-r--r-- | net/socket/client_socket_pool_base.h | 232 | ||||
-rw-r--r-- | net/socket/tcp_client_socket_pool.cc | 374 | ||||
-rw-r--r-- | net/socket/tcp_client_socket_pool.h | 248 |
5 files changed, 650 insertions, 590 deletions
diff --git a/net/net.gyp b/net/net.gyp index b508ddd..de904b6 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -273,6 +273,8 @@ 'socket/client_socket_handle.cc', 'socket/client_socket_handle.h', 'socket/client_socket_pool.h', + 'socket/client_socket_pool_base.cc', + 'socket/client_socket_pool_base.h', 'socket/socket.h', 'socket/socks_client_socket.cc', 'socket/socks_client_socket.h', diff --git a/net/socket/client_socket_pool_base.cc b/net/socket/client_socket_pool_base.cc new file mode 100644 index 0000000..a67e9c9 --- /dev/null +++ b/net/socket/client_socket_pool_base.cc @@ -0,0 +1,384 @@ +// 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/socket/client_socket_pool_base.h" + +#include "base/compiler_specific.h" +#include "base/message_loop.h" +#include "base/stl_util-inl.h" +#include "base/time.h" +#include "net/base/net_errors.h" +#include "net/socket/client_socket_handle.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 { + +ClientSocketPoolBase::ClientSocketPoolBase( + int max_sockets_per_group, + HostResolver* host_resolver, + ConnectJobFactory* connect_job_factory) + : idle_socket_count_(0), + max_sockets_per_group_(max_sockets_per_group), + host_resolver_(host_resolver), + connect_job_factory_(connect_job_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(connect_job_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(connect_job_map_, handle)); + + ConnectJob* connect_job = + connect_job_factory_->NewConnectJob(group_name, r, this); + connect_job_map_[handle] = connect_job; + int rv = connect_job->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()) { + RemoveConnectJob(handle); + group.connecting_requests.erase(map_it); + RemoveActiveSocket(group_name, &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.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; + } + + RemoveActiveSocket(group_name, &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) { + RemoveActiveSocket(group_name, &group); + } else { + request.handle->set_socket(socket); + request.handle->set_is_reused(false); + group.sockets_handed_out_count++; + + CheckSocketCounts(group); + } + + RemoveConnectJob(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::RemoveConnectJob( + const ClientSocketHandle* handle) { + ConnectJobMap::iterator it = connect_job_map_.find(handle); + CHECK(it != connect_job_map_.end()); + delete it->second; + connect_job_map_.erase(it); +} + +void ClientSocketPoolBase::RemoveActiveSocket(const std::string& group_name, + Group* group) { + group->active_socket_count--; + + if (!group->pending_requests.empty()) { + ProcessPendingRequest(group_name, group); + // |group| may no longer be valid after this point. Be careful not to + // access it again. + } else if (group->active_socket_count == 0 && group->idle_sockets.empty()) { + // Delete |group| if no longer needed. |group| will no longer be valid. + DCHECK(group->connecting_requests.empty()); + group_map_.erase(group_name); + } else { + CheckSocketCounts(*group); + } +} + +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); +} + +} // namespace net diff --git a/net/socket/client_socket_pool_base.h b/net/socket/client_socket_pool_base.h new file mode 100644 index 0000000..6280048 --- /dev/null +++ b/net/socket/client_socket_pool_base.h @@ -0,0 +1,232 @@ +// 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. + +#ifndef NET_SOCKET_CLIENT_SOCKET_POOL_BASE_H_ +#define NET_SOCKET_CLIENT_SOCKET_POOL_BASE_H_ + +#include <deque> +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "base/time.h" +#include "base/timer.h" +#include "net/base/address_list.h" +#include "net/base/completion_callback.h" +#include "net/base/host_resolver.h" +#include "net/base/load_states.h" +#include "net/socket/client_socket_pool.h" + +namespace net { + +class ClientSocket; +class ClientSocketHandle; +class ClientSocketPoolBase; + +// ConnectJob provides an abstract interface for "connecting" a socket. +// The connection may involve host resolution, tcp connection, ssl connection, +// etc. +class ConnectJob { + public: + ConnectJob() {} + virtual ~ConnectJob() {} + + // Begins connecting the socket. Returns OK on success, ERR_IO_PENDING if it + // cannot complete synchronously without blocking, or another net error code + // on error. + virtual int Connect() = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(ConnectJob); +}; + +// A ClientSocketPoolBase is used to restrict the number of sockets open at +// a time. It also maintains a list of idle persistent sockets. +// +class ClientSocketPoolBase : public base::RefCounted<ClientSocketPoolBase> { + public: + // A Request is allocated per call to RequestSocket that results in + // ERR_IO_PENDING. + struct Request { + // HostResolver::RequestInfo has no default constructor, so fudge something. + Request() : resolve_info(std::string(), 0) {} + + Request(ClientSocketHandle* handle, + CompletionCallback* callback, + int priority, + const HostResolver::RequestInfo& resolve_info, + LoadState load_state) + : handle(handle), callback(callback), priority(priority), + resolve_info(resolve_info), load_state(load_state) { + } + + ClientSocketHandle* handle; + CompletionCallback* callback; + int priority; + HostResolver::RequestInfo resolve_info; + LoadState load_state; + }; + + class ConnectJobFactory { + public: + ConnectJobFactory() {} + virtual ~ConnectJobFactory() {} + + virtual ConnectJob* NewConnectJob( + const std::string& group_name, + const Request& request, + ClientSocketPoolBase* pool) const = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(ConnectJobFactory); + }; + + ClientSocketPoolBase(int max_sockets_per_group, + HostResolver* host_resolver, + ConnectJobFactory* connect_job_factory); + + ~ClientSocketPoolBase(); + + int RequestSocket(const std::string& group_name, + const HostResolver::RequestInfo& resolve_info, + int priority, + ClientSocketHandle* handle, + CompletionCallback* callback); + + void CancelRequest(const std::string& group_name, + const ClientSocketHandle* handle); + + void ReleaseSocket(const std::string& group_name, + ClientSocket* socket); + + void CloseIdleSockets(); + + HostResolver* GetHostResolver() const { + return host_resolver_; + } + + int idle_socket_count() const { + return idle_socket_count_; + } + + int IdleSocketCountInGroup(const std::string& group_name) const; + + LoadState GetLoadState(const std::string& group_name, + const ClientSocketHandle* handle) const; + + // Used by ConnectJob until we remove the coupling between a specific + // ConnectJob and a ClientSocketHandle: + + // Returns NULL if not found. Otherwise it returns the Request* + // corresponding to the ConnectJob (keyed by |group_name| and |handle|. + // Note that this pointer may be invalidated after any call that might mutate + // the RequestMap or GroupMap, so the user should not hold onto the pointer + // for long. + Request* GetConnectingRequest(const std::string& group_name, + const ClientSocketHandle* handle); + + // Handles the completed Request corresponding to the ConnectJob (keyed + // by |group_name| and |handle|. |deactivate| indicates whether or not to + // deactivate the socket, making the socket slot available for a new socket + // connection. If |deactivate| is false, then set |socket| into |handle|. + // Returns the callback to run. + CompletionCallback* OnConnectingRequestComplete( + const std::string& group_name, + const ClientSocketHandle* handle, + bool deactivate, + ClientSocket* socket); + + private: + // Entry for a persistent socket which became idle at time |start_time|. + struct IdleSocket { + ClientSocket* socket; + base::TimeTicks start_time; + + // An idle socket should be removed if it can't be reused, or has been idle + // for too long. |now| is the current time value (TimeTicks::Now()). + // + // An idle socket can't be reused if it is disconnected or has received + // data unexpectedly (hence no longer idle). The unread data would be + // mistaken for the beginning of the next response if we were to reuse the + // socket for a new request. + bool ShouldCleanup(base::TimeTicks now) const; + }; + + typedef std::deque<Request> RequestQueue; + typedef std::map<const ClientSocketHandle*, Request> RequestMap; + + // A Group is allocated per group_name when there are idle sockets or pending + // requests. Otherwise, the Group object is removed from the map. + struct Group { + Group() : active_socket_count(0), sockets_handed_out_count(0) {} + std::deque<IdleSocket> idle_sockets; + RequestQueue pending_requests; + RequestMap connecting_requests; + int active_socket_count; // number of active sockets + int sockets_handed_out_count; // number of sockets given to clients + }; + + typedef std::map<std::string, Group> GroupMap; + + typedef std::map<const ClientSocketHandle*, ConnectJob*> ConnectJobMap; + + static void InsertRequestIntoQueue(const Request& r, + RequestQueue* pending_requests); + + // Closes all idle sockets if |force| is true. Else, only closes idle + // sockets that timed out or can't be reused. + void CleanupIdleSockets(bool force); + + // Called when the number of idle sockets changes. + void IncrementIdleCount(); + void DecrementIdleCount(); + + // Called via PostTask by ReleaseSocket. + void DoReleaseSocket(const std::string& group_name, ClientSocket* socket); + + // Called when timer_ fires. This method scans the idle sockets removing + // sockets that timed out or can't be reused. + void OnCleanupTimerFired() { + CleanupIdleSockets(false); + } + + // Removes the ConnectJob corresponding to |handle| from the + // |connect_job_map_|. + void RemoveConnectJob(const ClientSocketHandle* handle); + + static void CheckSocketCounts(const Group& group); + + // Remove an active socket. + void RemoveActiveSocket(const std::string& group_name, Group* group); + + // Process a request from a group's pending_requests queue. + void ProcessPendingRequest(const std::string& group_name, Group* group); + + GroupMap group_map_; + + ConnectJobMap connect_job_map_; + + // Timer used to periodically prune idle sockets that timed out or can't be + // reused. + base::RepeatingTimer<ClientSocketPoolBase> timer_; + + // The total number of idle sockets in the system. + int idle_socket_count_; + + // The maximum number of sockets kept per group. + const int max_sockets_per_group_; + + // The host resolver that will be used to do DNS lookups for connecting + // sockets. + HostResolver* const host_resolver_; + + scoped_ptr<ConnectJobFactory> connect_job_factory_; + + DISALLOW_COPY_AND_ASSIGN(ClientSocketPoolBase); +}; + +} // namespace net + +#endif // NET_SOCKET_CLIENT_SOCKET_POOL_BASE_H_ diff --git a/net/socket/tcp_client_socket_pool.cc b/net/socket/tcp_client_socket_pool.cc index e0530c3b..f571ef9 100644 --- a/net/socket/tcp_client_socket_pool.cc +++ b/net/socket/tcp_client_socket_pool.cc @@ -5,13 +5,13 @@ #include "net/socket/tcp_client_socket_pool.h" #include "base/compiler_specific.h" -#include "base/field_trial.h" +#include "base/logging.h" #include "base/message_loop.h" #include "base/time.h" -#include "base/stl_util-inl.h" #include "net/base/net_errors.h" #include "net/socket/client_socket_factory.h" #include "net/socket/client_socket_handle.h" +#include "net/socket/client_socket_pool_base.h" #include "net/socket/tcp_client_socket.h" using base::TimeDelta; @@ -33,7 +33,7 @@ const int kIdleTimeout = 300; // 5 minutes. namespace net { -TCPConnectingSocket::TCPConnectingSocket( +TCPConnectJob::TCPConnectJob( const std::string& group_name, const HostResolver::RequestInfo& resolve_info, const ClientSocketHandle* handle, @@ -45,27 +45,27 @@ TCPConnectingSocket::TCPConnectingSocket( client_socket_factory_(client_socket_factory), ALLOW_THIS_IN_INITIALIZER_LIST( callback_(this, - &TCPConnectingSocket::OnIOComplete)), + &TCPConnectJob::OnIOComplete)), pool_(pool), resolver_(pool->GetHostResolver()) {} -TCPConnectingSocket::~TCPConnectingSocket() { +TCPConnectJob::~TCPConnectJob() { // 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 TCPConnectJob::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) { +void TCPConnectJob::OnIOComplete(int result) { OnIOCompleteInternal(result, false /* asynchronous */); } -int TCPConnectingSocket::OnIOCompleteInternal( +int TCPConnectJob::OnIOCompleteInternal( int result, bool synchronous) { CHECK(result != ERR_IO_PENDING); @@ -123,363 +123,11 @@ int TCPConnectingSocket::OnIOCompleteInternal( 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); - RemoveActiveSocket(group_name, &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.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; - } - - RemoveActiveSocket(group_name, &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) { - RemoveActiveSocket(group_name, &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::RemoveActiveSocket(const std::string& group_name, - Group* group) { - group->active_socket_count--; - - if (!group->pending_requests.empty()) { - ProcessPendingRequest(group_name, group); - // |group| may no longer be valid after this point. Be careful not to - // access it again. - } else if (group->active_socket_count == 0 && group->idle_sockets.empty()) { - // Delete |group| if no longer needed. |group| will no longer be valid. - DCHECK(group->connecting_requests.empty()); - group_map_.erase(group_name); - } else { - CheckSocketCounts(*group); - } -} - -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( +ConnectJob* TCPClientSocketPool::TCPConnectJobFactory::NewConnectJob( const std::string& group_name, const ClientSocketPoolBase::Request& request, ClientSocketPoolBase* pool) const { - return new TCPConnectingSocket( + return new TCPConnectJob( group_name, request.resolve_info, request.handle, client_socket_factory_, pool); } @@ -490,7 +138,7 @@ TCPClientSocketPool::TCPClientSocketPool( ClientSocketFactory* client_socket_factory) : base_(new ClientSocketPoolBase( max_sockets_per_group, host_resolver, - new TCPConnectingSocketFactory(client_socket_factory))) {} + new TCPConnectJobFactory(client_socket_factory))) {} TCPClientSocketPool::~TCPClientSocketPool() {} diff --git a/net/socket/tcp_client_socket_pool.h b/net/socket/tcp_client_socket_pool.h index 421ea9a..11982b6 100644 --- a/net/socket/tcp_client_socket_pool.h +++ b/net/socket/tcp_client_socket_pool.h @@ -5,50 +5,30 @@ #ifndef NET_SOCKET_TCP_CLIENT_SOCKET_POOL_H_ #define NET_SOCKET_TCP_CLIENT_SOCKET_POOL_H_ -#include <deque> -#include <map> #include <string> +#include "base/basictypes.h" +#include "base/ref_counted.h" #include "base/scoped_ptr.h" -#include "base/timer.h" -#include "net/base/address_list.h" -#include "net/base/host_resolver.h" +#include "net/socket/client_socket_pool_base.h" #include "net/socket/client_socket_pool.h" namespace net { class ClientSocketFactory; -class ClientSocketPoolBase; -// ConnectingSocket provides an abstract interface for "connecting" a socket. -// The connection may involve host resolution, tcp connection, ssl connection, -// etc. -class ConnectingSocket { - public: - ConnectingSocket() {} - virtual ~ConnectingSocket() {} - - // Begins connecting the socket. Returns OK on success, ERR_IO_PENDING if it - // cannot complete synchronously without blocking, or another net error code - // on error. - virtual int Connect() = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(ConnectingSocket); -}; - -// TCPConnectingSocket handles the host resolution necessary for socket creation +// TCPConnectJob handles the host resolution necessary for socket creation // and the tcp connect. -class TCPConnectingSocket : public ConnectingSocket { +class TCPConnectJob : public ConnectJob { public: - TCPConnectingSocket(const std::string& group_name, - const HostResolver::RequestInfo& resolve_info, - const ClientSocketHandle* handle, - ClientSocketFactory* client_socket_factory, - ClientSocketPoolBase* pool); - ~TCPConnectingSocket(); + TCPConnectJob(const std::string& group_name, + const HostResolver::RequestInfo& resolve_info, + const ClientSocketHandle* handle, + ClientSocketFactory* client_socket_factory, + ClientSocketPoolBase* pool); + ~TCPConnectJob(); - // ConnectingSocket methods. + // ConnectJob methods. // Begins the host resolution and the TCP connect. Returns OK on success // and ERR_IO_PENDING if it cannot immediately service the request. @@ -71,7 +51,7 @@ class TCPConnectingSocket : public ConnectingSocket { const HostResolver::RequestInfo resolve_info_; const ClientSocketHandle* const handle_; ClientSocketFactory* const client_socket_factory_; - CompletionCallbackImpl<TCPConnectingSocket> callback_; + CompletionCallbackImpl<TCPConnectJob> callback_; scoped_ptr<ClientSocket> socket_; ClientSocketPoolBase* const pool_; SingleRequestHostResolver resolver_; @@ -80,193 +60,7 @@ class TCPConnectingSocket : public ConnectingSocket { // The time the Connect() method was called (if it got called). base::TimeTicks connect_start_time_; - DISALLOW_COPY_AND_ASSIGN(TCPConnectingSocket); -}; - -// A ClientSocketPoolBase is used to restrict the number of sockets open at -// a time. It also maintains a list of idle persistent sockets. -// -class ClientSocketPoolBase : public base::RefCounted<ClientSocketPoolBase> { - public: - // A Request is allocated per call to RequestSocket that results in - // ERR_IO_PENDING. - struct Request { - // HostResolver::RequestInfo has no default constructor, so fudge something. - Request() : resolve_info(std::string(), 0) {} - - Request(ClientSocketHandle* handle, - CompletionCallback* callback, - int priority, - const HostResolver::RequestInfo& resolve_info, - LoadState load_state) - : handle(handle), callback(callback), priority(priority), - resolve_info(resolve_info), load_state(load_state) { - } - - ClientSocketHandle* handle; - CompletionCallback* callback; - int priority; - HostResolver::RequestInfo resolve_info; - LoadState load_state; - }; - - class ConnectingSocketFactory { - public: - ConnectingSocketFactory() {} - virtual ~ConnectingSocketFactory() {} - - virtual ConnectingSocket* NewConnectingSocket( - const std::string& group_name, - const Request& request, - ClientSocketPoolBase* pool) const = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(ConnectingSocketFactory); - }; - - ClientSocketPoolBase(int max_sockets_per_group, - HostResolver* host_resolver, - ConnectingSocketFactory* connecting_socket_factory); - - ~ClientSocketPoolBase(); - - int RequestSocket(const std::string& group_name, - const HostResolver::RequestInfo& resolve_info, - int priority, - ClientSocketHandle* handle, - CompletionCallback* callback); - - void CancelRequest(const std::string& group_name, - const ClientSocketHandle* handle); - - void ReleaseSocket(const std::string& group_name, - ClientSocket* socket); - - void CloseIdleSockets(); - - HostResolver* GetHostResolver() const { - return host_resolver_; - } - - int idle_socket_count() const { - return idle_socket_count_; - } - - int IdleSocketCountInGroup(const std::string& group_name) const; - - LoadState GetLoadState(const std::string& group_name, - const ClientSocketHandle* handle) const; - - // Used by ConnectingSocket until we remove the coupling between a specific - // ConnectingSocket and a ClientSocketHandle: - - // Returns NULL if not found. Otherwise it returns the Request* - // corresponding to the ConnectingSocket (keyed by |group_name| and |handle|. - // Note that this pointer may be invalidated after any call that might mutate - // the RequestMap or GroupMap, so the user should not hold onto the pointer - // for long. - Request* GetConnectingRequest(const std::string& group_name, - const ClientSocketHandle* handle); - - // Handles the completed Request corresponding to the ConnectingSocket (keyed - // by |group_name| and |handle|. |deactivate| indicates whether or not to - // deactivate the socket, making the socket slot available for a new socket - // connection. If |deactivate| is false, then set |socket| into |handle|. - // Returns the callback to run. - CompletionCallback* OnConnectingRequestComplete( - const std::string& group_name, - const ClientSocketHandle* handle, - bool deactivate, - ClientSocket* socket); - - private: - // Entry for a persistent socket which became idle at time |start_time|. - struct IdleSocket { - ClientSocket* socket; - base::TimeTicks start_time; - - // An idle socket should be removed if it can't be reused, or has been idle - // for too long. |now| is the current time value (TimeTicks::Now()). - // - // An idle socket can't be reused if it is disconnected or has received - // data unexpectedly (hence no longer idle). The unread data would be - // mistaken for the beginning of the next response if we were to reuse the - // socket for a new request. - bool ShouldCleanup(base::TimeTicks now) const; - }; - - typedef std::deque<Request> RequestQueue; - typedef std::map<const ClientSocketHandle*, Request> RequestMap; - - // A Group is allocated per group_name when there are idle sockets or pending - // requests. Otherwise, the Group object is removed from the map. - struct Group { - Group() : active_socket_count(0), sockets_handed_out_count(0) {} - std::deque<IdleSocket> idle_sockets; - RequestQueue pending_requests; - RequestMap connecting_requests; - int active_socket_count; // number of active sockets - int sockets_handed_out_count; // number of sockets given to clients - }; - - typedef std::map<std::string, Group> GroupMap; - - typedef std::map<const ClientSocketHandle*, ConnectingSocket*> - ConnectingSocketMap; - - static void InsertRequestIntoQueue(const Request& r, - RequestQueue* pending_requests); - - // Closes all idle sockets if |force| is true. Else, only closes idle - // sockets that timed out or can't be reused. - void CleanupIdleSockets(bool force); - - // Called when the number of idle sockets changes. - void IncrementIdleCount(); - void DecrementIdleCount(); - - // Called via PostTask by ReleaseSocket. - void DoReleaseSocket(const std::string& group_name, ClientSocket* socket); - - // Called when timer_ fires. This method scans the idle sockets removing - // sockets that timed out or can't be reused. - void OnCleanupTimerFired() { - CleanupIdleSockets(false); - } - - // Removes the ConnectingSocket corresponding to |handle| from the - // |connecting_socket_map_|. - void RemoveConnectingSocket(const ClientSocketHandle* handle); - - static void CheckSocketCounts(const Group& group); - - // Remove an active socket. - void RemoveActiveSocket(const std::string& group_name, Group* group); - - // Process a request from a group's pending_requests queue. - void ProcessPendingRequest(const std::string& group_name, Group* group); - - GroupMap group_map_; - - ConnectingSocketMap connecting_socket_map_; - - // Timer used to periodically prune idle sockets that timed out or can't be - // reused. - base::RepeatingTimer<ClientSocketPoolBase> timer_; - - // The total number of idle sockets in the system. - int idle_socket_count_; - - // The maximum number of sockets kept per group. - const int max_sockets_per_group_; - - // The host resolver that will be used to do DNS lookups for connecting - // sockets. - HostResolver* const host_resolver_; - - scoped_ptr<ConnectingSocketFactory> connecting_socket_factory_; - - DISALLOW_COPY_AND_ASSIGN(ClientSocketPoolBase); + DISALLOW_COPY_AND_ASSIGN(TCPConnectJob); }; class TCPClientSocketPool : public ClientSocketPool { @@ -307,17 +101,17 @@ class TCPClientSocketPool : public ClientSocketPool { private: virtual ~TCPClientSocketPool(); - class TCPConnectingSocketFactory - : public ClientSocketPoolBase::ConnectingSocketFactory { + class TCPConnectJobFactory + : public ClientSocketPoolBase::ConnectJobFactory { public: - TCPConnectingSocketFactory(ClientSocketFactory* client_socket_factory) + explicit TCPConnectJobFactory(ClientSocketFactory* client_socket_factory) : client_socket_factory_(client_socket_factory) {} - virtual ~TCPConnectingSocketFactory() {} + virtual ~TCPConnectJobFactory() {} - // ClientSocketPoolBase::ConnectingSocketFactory methods. + // ClientSocketPoolBase::ConnectJobFactory methods. - virtual ConnectingSocket* NewConnectingSocket( + virtual ConnectJob* NewConnectJob( const std::string& group_name, const ClientSocketPoolBase::Request& request, ClientSocketPoolBase* pool) const; @@ -325,7 +119,7 @@ class TCPClientSocketPool : public ClientSocketPool { private: ClientSocketFactory* const client_socket_factory_; - DISALLOW_COPY_AND_ASSIGN(TCPConnectingSocketFactory); + DISALLOW_COPY_AND_ASSIGN(TCPConnectJobFactory); }; // One might ask why ClientSocketPoolBase is also refcounted if its |