diff options
-rw-r--r-- | chrome/browser/browser_main.cc | 2 | ||||
-rw-r--r-- | net/http/http_network_transaction_unittest.cc | 14 | ||||
-rw-r--r-- | net/socket/client_socket_handle.cc | 19 | ||||
-rw-r--r-- | net/socket/client_socket_handle.h | 32 | ||||
-rw-r--r-- | net/socket/client_socket_pool.h | 2 | ||||
-rw-r--r-- | net/socket/client_socket_pool_base.cc | 147 | ||||
-rw-r--r-- | net/socket/client_socket_pool_base.h | 257 | ||||
-rw-r--r-- | net/socket/client_socket_pool_base_unittest.cc | 66 | ||||
-rw-r--r-- | net/socket/tcp_client_socket_pool.cc | 27 | ||||
-rw-r--r-- | net/socket/tcp_client_socket_pool.h | 19 |
10 files changed, 383 insertions, 202 deletions
diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc index 3501779..c111478 100644 --- a/chrome/browser/browser_main.cc +++ b/chrome/browser/browser_main.cc @@ -659,7 +659,7 @@ int BrowserMain(const MainFunctionParams& parameters) { const int late_binding_group = socket_late_binding_trial->AppendGroup("_enable_late_binding", 50); if (socket_late_binding_trial->group() == late_binding_group) - net::ClientSocketPoolBase::EnableLateBindingOfSockets(true); + net::EnableLateBindingOfSockets(true); #if defined(OS_WIN) // Init common control sex. diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index 91c0d0b..e298f64 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -169,8 +169,12 @@ class CaptureGroupNameSocketPool : public ClientSocketPool { public: CaptureGroupNameSocketPool() { } + const std::string last_group_name_received() const { + return last_group_name_; + } + virtual int RequestSocket(const std::string& group_name, - const HostResolver::RequestInfo& resolve_info, + const void* socket_params, int priority, ClientSocketHandle* handle, CompletionCallback* callback, @@ -178,11 +182,6 @@ class CaptureGroupNameSocketPool : public ClientSocketPool { last_group_name_ = group_name; return ERR_IO_PENDING; } - - const std::string last_group_name_received() const { - return last_group_name_; - } - virtual void CancelRequest(const std::string& group_name, const ClientSocketHandle* handle) { } virtual void ReleaseSocket(const std::string& group_name, @@ -201,7 +200,8 @@ class CaptureGroupNameSocketPool : public ClientSocketPool { const ClientSocketHandle* handle) const { return LOAD_STATE_IDLE; } - protected: + + private: std::string last_group_name_; }; diff --git a/net/socket/client_socket_handle.cc b/net/socket/client_socket_handle.cc index 7c3b182..66ca4ab 100644 --- a/net/socket/client_socket_handle.cc +++ b/net/socket/client_socket_handle.cc @@ -7,7 +7,6 @@ #include "base/compiler_specific.h" #include "base/logging.h" #include "net/base/net_errors.h" -#include "net/socket/client_socket.h" #include "net/socket/client_socket_pool.h" namespace net { @@ -23,24 +22,6 @@ ClientSocketHandle::~ClientSocketHandle() { Reset(); } -int ClientSocketHandle::Init(const std::string& group_name, - const HostResolver::RequestInfo& resolve_info, - int priority, - CompletionCallback* callback, - LoadLog* load_log) { - CHECK(!group_name.empty()); - ResetInternal(true); - group_name_ = group_name; - int rv = pool_->RequestSocket( - group_name, resolve_info, priority, this, &callback_, load_log); - if (rv == ERR_IO_PENDING) { - user_callback_ = callback; - } else { - HandleInitCompletion(rv); - } - return rv; -} - void ClientSocketHandle::Reset() { ResetInternal(true); } diff --git a/net/socket/client_socket_handle.h b/net/socket/client_socket_handle.h index 0dbb1bc..67c6cb8 100644 --- a/net/socket/client_socket_handle.h +++ b/net/socket/client_socket_handle.h @@ -7,17 +7,17 @@ #include <string> +#include "base/logging.h" #include "base/ref_counted.h" #include "base/scoped_ptr.h" #include "net/base/completion_callback.h" -#include "net/base/host_resolver.h" #include "net/base/load_states.h" +#include "net/base/net_errors.h" #include "net/socket/client_socket.h" +#include "net/socket/client_socket_pool.h" namespace net { -class ClientSocketPool; - // A container for a ClientSocket. // // The handle's |group_name| uniquely identifies the origin and type of the @@ -39,7 +39,7 @@ class ClientSocketHandle { // otherwise it will be set to a new connected socket. Consumers can then // call is_reused() to see if the socket was reused. If not reusing an // existing socket, ClientSocketPool may need to establish a new - // connection to the |resolve_info.host| |resolve_info.port| pair. + // connection using |socket_params|. // // This method returns ERR_IO_PENDING if it cannot complete synchronously, in // which case the consumer will be notified of completion via |callback|. @@ -47,8 +47,10 @@ class ClientSocketHandle { // Init may be called multiple times. // // Profiling information for the request is saved to |load_log| if non-NULL. + // + template <typename SocketParams> int Init(const std::string& group_name, - const HostResolver::RequestInfo& resolve_info, + const SocketParams& socket_params, int priority, CompletionCallback* callback, LoadLog* load_log); @@ -102,6 +104,26 @@ class ClientSocketHandle { DISALLOW_COPY_AND_ASSIGN(ClientSocketHandle); }; +// Template function implementation: +template <typename SocketParams> +int ClientSocketHandle::Init(const std::string& group_name, + const SocketParams& socket_params, + int priority, + CompletionCallback* callback, + LoadLog* load_log) { + CHECK(!group_name.empty()); + ResetInternal(true); + group_name_ = group_name; + int rv = pool_->RequestSocket( + group_name, &socket_params, priority, this, &callback_, load_log); + if (rv == ERR_IO_PENDING) { + user_callback_ = callback; + } else { + HandleInitCompletion(rv); + } + return rv; +} + } // namespace net #endif // NET_SOCKET_CLIENT_SOCKET_HANDLE_H_ diff --git a/net/socket/client_socket_pool.h b/net/socket/client_socket_pool.h index ae0f48a..ffb34b7 100644 --- a/net/socket/client_socket_pool.h +++ b/net/socket/client_socket_pool.h @@ -48,7 +48,7 @@ class ClientSocketPool : public base::RefCounted<ClientSocketPool> { // // Profiling information for the request is saved to |load_log| if non-NULL. virtual int RequestSocket(const std::string& group_name, - const HostResolver::RequestInfo& resolve_info, + const void* params, int priority, ClientSocketHandle* handle, CompletionCallback* callback, diff --git a/net/socket/client_socket_pool_base.cc b/net/socket/client_socket_pool_base.cc index a45d74a..bfe8405 100644 --- a/net/socket/client_socket_pool_base.cc +++ b/net/socket/client_socket_pool_base.cc @@ -30,8 +30,6 @@ const int kIdleTimeout = 300; // 5 minutes. namespace net { -bool ClientSocketPoolBase::g_late_binding = false; - ConnectJob::ConnectJob(const std::string& group_name, const ClientSocketHandle* key_handle, base::TimeDelta timeout_duration, @@ -63,7 +61,11 @@ void ConnectJob::OnTimeout() { delegate->OnConnectJobComplete(ERR_TIMED_OUT, this); } -ClientSocketPoolBase::ClientSocketPoolBase( +namespace internal { + +bool ClientSocketPoolBaseHelper::g_late_binding = false; + +ClientSocketPoolBaseHelper::ClientSocketPoolBaseHelper( int max_sockets, int max_sockets_per_group, ConnectJobFactory* connect_job_factory) @@ -78,7 +80,7 @@ ClientSocketPoolBase::ClientSocketPoolBase( DCHECK_LE(max_sockets_per_group, max_sockets); } -ClientSocketPoolBase::~ClientSocketPoolBase() { +ClientSocketPoolBaseHelper::~ClientSocketPoolBaseHelper() { if (g_late_binding) CancelAllConnectJobs(); // Clean up any idle sockets. Assert that we have no remaining active @@ -94,24 +96,22 @@ ClientSocketPoolBase::~ClientSocketPoolBase() { // prioritized over requests of equal priority. // // static -void ClientSocketPoolBase::InsertRequestIntoQueue( - const Request& r, RequestQueue* pending_requests) { +void ClientSocketPoolBaseHelper::InsertRequestIntoQueue( + const Request* r, RequestQueue* pending_requests) { RequestQueue::iterator it = pending_requests->begin(); - while (it != pending_requests->end() && r.priority <= it->priority) + while (it != pending_requests->end() && r->priority() <= (*it)->priority()) ++it; pending_requests->insert(it, r); } -int ClientSocketPoolBase::RequestSocket( +int ClientSocketPoolBaseHelper::RequestSocket( const std::string& group_name, - const HostResolver::RequestInfo& resolve_info, - int priority, - ClientSocketHandle* handle, - CompletionCallback* callback, - LoadLog* load_log) { - DCHECK(!resolve_info.hostname().empty()); - DCHECK_GE(priority, 0); - DCHECK(callback); + const Request* request) { + DCHECK_GE(request->priority(), 0); + CompletionCallback* const callback = request->callback(); + CHECK(callback); + ClientSocketHandle* const handle = request->handle(); + CHECK(handle); Group& group = group_map_[group_name]; // Can we make another active socket now? @@ -122,9 +122,7 @@ int ClientSocketPoolBase::RequestSocket( // a scan of all groups, so just flip a flag here, and do the check later. may_have_stalled_group_ = true; } - CHECK(callback); - Request r(handle, callback, priority, resolve_info, load_log); - InsertRequestIntoQueue(r, &group.pending_requests); + InsertRequestIntoQueue(request, &group.pending_requests); return ERR_IO_PENDING; } @@ -134,7 +132,8 @@ int ClientSocketPoolBase::RequestSocket( DecrementIdleCount(); if (idle_socket.socket->IsConnectedAndIdle()) { // We found one we can reuse! - HandOutSocket(idle_socket.socket, idle_socket.used, handle, &group); + HandOutSocket( + idle_socket.socket, idle_socket.used, handle, &group); return OK; } delete idle_socket.socket; @@ -142,10 +141,8 @@ int ClientSocketPoolBase::RequestSocket( // 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_log); scoped_ptr<ConnectJob> connect_job( - connect_job_factory_->NewConnectJob(group_name, r, this)); + connect_job_factory_->NewConnectJob(group_name, *request, this)); int rv = connect_job->Connect(); if (rv == OK) { @@ -157,9 +154,9 @@ int ClientSocketPoolBase::RequestSocket( ConnectJob* job = connect_job.release(); if (g_late_binding) { CHECK(!ContainsKey(connect_job_map_, handle)); - InsertRequestIntoQueue(r, &group.pending_requests); + InsertRequestIntoQueue(request, &group.pending_requests); } else { - group.connecting_requests[handle] = r; + group.connecting_requests[handle] = request; CHECK(!ContainsKey(connect_job_map_, handle)); connect_job_map_[handle] = job; } @@ -171,8 +168,8 @@ int ClientSocketPoolBase::RequestSocket( return rv; } -void ClientSocketPoolBase::CancelRequest(const std::string& group_name, - const ClientSocketHandle* handle) { +void ClientSocketPoolBaseHelper::CancelRequest( + const std::string& group_name, const ClientSocketHandle* handle) { CHECK(ContainsKey(group_map_, group_name)); Group& group = group_map_[group_name]; @@ -180,7 +177,8 @@ void ClientSocketPoolBase::CancelRequest(const std::string& group_name, // Search pending_requests for matching handle. RequestQueue::iterator it = group.pending_requests.begin(); for (; it != group.pending_requests.end(); ++it) { - if (it->handle == handle) { + if ((*it)->handle() == handle) { + delete *it; group.pending_requests.erase(it); if (g_late_binding && group.jobs.size() > group.pending_requests.size() + 1) { @@ -204,20 +202,20 @@ void ClientSocketPoolBase::CancelRequest(const std::string& group_name, } } -void ClientSocketPoolBase::ReleaseSocket(const std::string& group_name, - ClientSocket* socket) { +void ClientSocketPoolBaseHelper::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)); + this, &ClientSocketPoolBaseHelper::DoReleaseSocket, group_name, socket)); } -void ClientSocketPoolBase::CloseIdleSockets() { +void ClientSocketPoolBaseHelper::CloseIdleSockets() { CleanupIdleSockets(true); } -int ClientSocketPoolBase::IdleSocketCountInGroup( +int ClientSocketPoolBaseHelper::IdleSocketCountInGroup( const std::string& group_name) const { GroupMap::const_iterator i = group_map_.find(group_name); CHECK(i != group_map_.end()); @@ -225,7 +223,7 @@ int ClientSocketPoolBase::IdleSocketCountInGroup( return i->second.idle_sockets.size(); } -LoadState ClientSocketPoolBase::GetLoadState( +LoadState ClientSocketPoolBaseHelper::GetLoadState( const std::string& group_name, const ClientSocketHandle* handle) const { if (!ContainsKey(group_map_, group_name)) { @@ -251,7 +249,7 @@ LoadState ClientSocketPoolBase::GetLoadState( // Search pending_requests for matching handle. 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 ((*it)->handle() == handle) { if (g_late_binding && i < group.jobs.size()) { LoadState max_state = LOAD_STATE_IDLE; for (ConnectJobSet::const_iterator job_it = group.jobs.begin(); @@ -271,7 +269,7 @@ LoadState ClientSocketPoolBase::GetLoadState( return LOAD_STATE_IDLE; } -bool ClientSocketPoolBase::IdleSocket::ShouldCleanup( +bool ClientSocketPoolBaseHelper::IdleSocket::ShouldCleanup( base::TimeTicks now) const { bool timed_out = (now - start_time) >= base::TimeDelta::FromSeconds(kIdleTimeout); @@ -279,7 +277,7 @@ bool ClientSocketPoolBase::IdleSocket::ShouldCleanup( !(used ? socket->IsConnectedAndIdle() : socket->IsConnected()); } -void ClientSocketPoolBase::CleanupIdleSockets(bool force) { +void ClientSocketPoolBaseHelper::CleanupIdleSockets(bool force) { if (idle_socket_count_ == 0) return; @@ -311,19 +309,19 @@ void ClientSocketPoolBase::CleanupIdleSockets(bool force) { } } -void ClientSocketPoolBase::IncrementIdleCount() { +void ClientSocketPoolBaseHelper::IncrementIdleCount() { if (++idle_socket_count_ == 1) timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this, - &ClientSocketPoolBase::OnCleanupTimerFired); + &ClientSocketPoolBaseHelper::OnCleanupTimerFired); } -void ClientSocketPoolBase::DecrementIdleCount() { +void ClientSocketPoolBaseHelper::DecrementIdleCount() { if (--idle_socket_count_ == 0) timer_.Stop(); } -void ClientSocketPoolBase::DoReleaseSocket(const std::string& group_name, - ClientSocket* socket) { +void ClientSocketPoolBaseHelper::DoReleaseSocket(const std::string& group_name, + ClientSocket* socket) { GroupMap::iterator i = group_map_.find(group_name); CHECK(i != group_map_.end()); @@ -349,8 +347,8 @@ void ClientSocketPoolBase::DoReleaseSocket(const std::string& group_name, // are not at the |max_sockets_per_group_| limit. Note: for requests with // the same priority, the winner is based on group hash ordering (and not // insertion order). -int ClientSocketPoolBase::FindTopStalledGroup(Group** group, - std::string* group_name) { +int ClientSocketPoolBaseHelper::FindTopStalledGroup(Group** group, + std::string* group_name) { Group* top_group = NULL; const std::string* top_group_name = NULL; int stalled_group_count = 0; @@ -377,7 +375,8 @@ int ClientSocketPoolBase::FindTopStalledGroup(Group** group, return stalled_group_count; } -void ClientSocketPoolBase::OnConnectJobComplete(int result, ConnectJob* job) { +void ClientSocketPoolBaseHelper::OnConnectJobComplete( + int result, ConnectJob* job) { DCHECK_NE(ERR_IO_PENDING, result); const std::string group_name = job->group_name(); GroupMap::iterator group_it = group_map_.find(group_name); @@ -393,11 +392,11 @@ void ClientSocketPoolBase::OnConnectJobComplete(int result, ConnectJob* job) { if (result == OK) { DCHECK(socket.get()); if (!group.pending_requests.empty()) { - Request r = group.pending_requests.front(); + scoped_ptr<const Request> r(group.pending_requests.front()); group.pending_requests.pop_front(); HandOutSocket( - socket.release(), false /* unused socket */, r.handle, &group); - r.callback->Run(result); + socket.release(), false /* unused socket */, r->handle(), &group); + r->callback()->Run(result); } else { AddIdleSocket(socket.release(), false /* unused socket */, &group); OnAvailableSocketSlot(group_name, &group); @@ -405,9 +404,9 @@ void ClientSocketPoolBase::OnConnectJobComplete(int result, ConnectJob* job) { } else { DCHECK(!socket.get()); if (!group.pending_requests.empty()) { - Request r = group.pending_requests.front(); + scoped_ptr<const Request> r(group.pending_requests.front()); group.pending_requests.pop_front(); - r.callback->Run(result); + r->callback()->Run(result); } MaybeOnAvailableSocketSlot(group_name); } @@ -418,8 +417,9 @@ void ClientSocketPoolBase::OnConnectJobComplete(int result, ConnectJob* job) { RequestMap* request_map = &group.connecting_requests; RequestMap::iterator it = request_map->find(key_handle); CHECK(it != request_map->end()); - ClientSocketHandle* const handle = it->second.handle; - CompletionCallback* const callback = it->second.callback; + const Request* request = it->second; + ClientSocketHandle* const handle = request->handle(); + CompletionCallback* const callback = request->callback(); RemoveConnectJob(key_handle, job, &group); @@ -436,11 +436,11 @@ void ClientSocketPoolBase::OnConnectJobComplete(int result, ConnectJob* job) { } } -void ClientSocketPoolBase::EnableLateBindingOfSockets(bool enabled) { +void ClientSocketPoolBaseHelper::EnableLateBindingOfSockets(bool enabled) { g_late_binding = enabled; } -void ClientSocketPoolBase::RemoveConnectJob( +void ClientSocketPoolBaseHelper::RemoveConnectJob( const ClientSocketHandle* handle, const ConnectJob *job, Group* group) { CHECK(connecting_socket_count_ > 0); connecting_socket_count_--; @@ -454,7 +454,11 @@ void ClientSocketPoolBase::RemoveConnectJob( job = it->second; delete job; connect_job_map_.erase(it); - group->connecting_requests.erase(handle); + 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) { @@ -463,7 +467,7 @@ void ClientSocketPoolBase::RemoveConnectJob( } } -void ClientSocketPoolBase::MaybeOnAvailableSocketSlot( +void ClientSocketPoolBaseHelper::MaybeOnAvailableSocketSlot( const std::string& group_name) { GroupMap::iterator it = group_map_.find(group_name); if (it != group_map_.end()) { @@ -473,8 +477,8 @@ void ClientSocketPoolBase::MaybeOnAvailableSocketSlot( } } -void ClientSocketPoolBase::OnAvailableSocketSlot(const std::string& group_name, - Group* group) { +void ClientSocketPoolBaseHelper::OnAvailableSocketSlot( + const std::string& group_name, Group* group) { if (may_have_stalled_group_) { std::string top_group_name; Group* top_group; @@ -493,25 +497,26 @@ void ClientSocketPoolBase::OnAvailableSocketSlot(const std::string& group_name, } } -void ClientSocketPoolBase::ProcessPendingRequest(const std::string& group_name, - Group* group) { - Request r = group->pending_requests.front(); +void ClientSocketPoolBaseHelper::ProcessPendingRequest( + const std::string& group_name, Group* group) { + scoped_ptr<const 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, r.load_log); + int rv = RequestSocket(group_name, r.get()); if (rv != ERR_IO_PENDING) { - r.callback->Run(rv); + r->callback()->Run(rv); if (rv != OK) { // |group| may be invalid after the callback, we need to search // |group_map_| again. MaybeOnAvailableSocketSlot(group_name); } + } else { + r.release(); } } -void ClientSocketPoolBase::HandOutSocket( +void ClientSocketPoolBaseHelper::HandOutSocket( ClientSocket* socket, bool reused, ClientSocketHandle* handle, @@ -524,7 +529,7 @@ void ClientSocketPoolBase::HandOutSocket( group->active_socket_count++; } -void ClientSocketPoolBase::AddIdleSocket( +void ClientSocketPoolBaseHelper::AddIdleSocket( ClientSocket* socket, bool used, Group* group) { DCHECK(socket); IdleSocket idle_socket; @@ -536,7 +541,7 @@ void ClientSocketPoolBase::AddIdleSocket( IncrementIdleCount(); } -void ClientSocketPoolBase::CancelAllConnectJobs() { +void ClientSocketPoolBaseHelper::CancelAllConnectJobs() { for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end();) { Group& group = i->second; STLDeleteElements(&group.jobs); @@ -550,11 +555,17 @@ void ClientSocketPoolBase::CancelAllConnectJobs() { } } -bool ClientSocketPoolBase::ReachedMaxSocketsLimit() const { +bool ClientSocketPoolBaseHelper::ReachedMaxSocketsLimit() const { // Each connecting socket will eventually connect and be handed out. int total = handed_out_socket_count_ + connecting_socket_count_; DCHECK_LE(total, max_sockets_); return total == max_sockets_; } +} // 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 4ae96f8..bae88b7 100644 --- a/net/socket/client_socket_pool_base.h +++ b/net/socket/client_socket_pool_base.h @@ -1,7 +1,24 @@ // 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. - +// +// A ClientSocketPoolBase is used to restrict the number of sockets open at +// a time. It also maintains a list of idle persistent sockets for reuse. +// Subclasses of ClientSocketPool should compose ClientSocketPoolBase to handle +// the core logic of (1) restricting the number of active (connected or +// connecting) sockets per "group" (generally speaking, the hostname), (2) +// maintaining a per-group list of idle, persistent sockets for reuse, and (3) +// limiting the total number of active sockets in the system. +// +// ClientSocketPoolBase abstracts socket connection details behind ConnectJob, +// ConnectJobFactory, and SocketParams. When a socket "slot" becomes available, +// the ClientSocketPoolBase will ask the ConnectJobFactory to create a +// ConnectJob with a SocketParams. Subclasses of ClientSocketPool should +// implement their socket specific connection by subclassing ConnectJob and +// implementing ConnectJob::ConnectInternal(). They can control the parameters +// passed to each new ConnectJob instance via their ConnectJobFactory subclass +// and templated SocketParams parameter. +// #ifndef NET_SOCKET_CLIENT_SOCKET_POOL_BASE_H_ #define NET_SOCKET_CLIENT_SOCKET_POOL_BASE_H_ @@ -16,16 +33,15 @@ #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_log.h" #include "net/base/load_states.h" +#include "net/base/net_errors.h" #include "net/socket/client_socket.h" #include "net/socket/client_socket_pool.h" namespace net { class ClientSocketHandle; -class ClientSocketPoolBase; // ConnectJob provides an abstract interface for "connecting" a socket. // The connection may involve host resolution, tcp connection, ssl connection, @@ -93,37 +109,39 @@ class ConnectJob { 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>, +namespace internal { + +// ClientSocketPoolBaseHelper is an internal class that implements almost all +// the functionality from ClientSocketPoolBase without using templates. +// ClientSocketPoolBase adds templated definitions built on top of +// ClientSocketPoolBaseHelper. This class is not for external use, please use +// ClientSocketPoolBase instead. +class ClientSocketPoolBaseHelper + : public base::RefCounted<ClientSocketPoolBaseHelper>, public ConnectJob::Delegate { 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() - : handle(NULL), - callback(NULL), - priority(0), - resolve_info(std::string(), 0) {} - + class Request { + public: Request(ClientSocketHandle* handle, CompletionCallback* callback, int priority, - const HostResolver::RequestInfo& resolve_info, LoadLog* load_log) - : load_log(load_log), handle(handle), callback(callback), - priority(priority), resolve_info(resolve_info) { - } + : handle_(handle), callback_(callback), priority_(priority) {} + + virtual ~Request() {} - scoped_refptr<LoadLog> load_log; - ClientSocketHandle* handle; - CompletionCallback* callback; - int priority; - HostResolver::RequestInfo resolve_info; + ClientSocketHandle* handle() const { return handle_; } + CompletionCallback* callback() const { return callback_; } + int priority() const { return priority_; } + LoadLog* load_log() const { return load_log_.get(); } + + private: + ClientSocketHandle* const handle_; + CompletionCallback* const callback_; + const int priority_; + const scoped_refptr<LoadLog> load_log_; + + DISALLOW_COPY_AND_ASSIGN(Request); }; class ConnectJobFactory { @@ -140,36 +158,42 @@ class ClientSocketPoolBase DISALLOW_COPY_AND_ASSIGN(ConnectJobFactory); }; - ClientSocketPoolBase(int max_sockets, - int max_sockets_per_group, - ConnectJobFactory* connect_job_factory); + ClientSocketPoolBaseHelper(int max_sockets, + int max_sockets_per_group, + ConnectJobFactory* connect_job_factory); - ~ClientSocketPoolBase(); + ~ClientSocketPoolBaseHelper(); - int RequestSocket(const std::string& group_name, - const HostResolver::RequestInfo& resolve_info, - int priority, - ClientSocketHandle* handle, - CompletionCallback* callback, - LoadLog* load_log); + // See ClientSocketPool::RequestSocket for documentation on this function. + // Note that |request| must be heap allocated. If ERR_IO_PENDING is returned, + // then ClientSocketPoolBaseHelper takes ownership of |request|. + int RequestSocket(const std::string& group_name, const Request* request); + // See ClientSocketPool::CancelRequest for documentation on this function. void CancelRequest(const std::string& group_name, const ClientSocketHandle* handle); + // See ClientSocketPool::ReleaseSocket for documentation on this function. void ReleaseSocket(const std::string& group_name, ClientSocket* socket); + // See ClientSocketPool::CloseIdleSockets for documentation on this function. void CloseIdleSockets(); + // See ClientSocketPool::IdleSocketCount() for documentation on this function. int idle_socket_count() const { return idle_socket_count_; } + // See ClientSocketPool::IdleSocketCountInGroup() for documentation on this + // function. int IdleSocketCountInGroup(const std::string& group_name) const; + // See ClientSocketPool::GetLoadState() for documentation on this function. LoadState GetLoadState(const std::string& group_name, const ClientSocketHandle* handle) const; + // ConnectJob::Delegate methods: virtual void OnConnectJobComplete(int result, ConnectJob* job); // Enables late binding of sockets. In this mode, socket requests are @@ -182,6 +206,7 @@ class ClientSocketPoolBase // For testing. bool may_have_stalled_group() const { return may_have_stalled_group_; } + int NumConnectJobsInGroup(const std::string& group_name) const { return group_map_.find(group_name)->second.jobs.size(); } @@ -204,8 +229,8 @@ class ClientSocketPoolBase bool ShouldCleanup(base::TimeTicks now) const; }; - typedef std::deque<Request> RequestQueue; - typedef std::map<const ClientSocketHandle*, Request> RequestMap; + typedef std::deque<const Request*> RequestQueue; + typedef std::map<const ClientSocketHandle*, const 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. @@ -223,7 +248,7 @@ class ClientSocketPoolBase } int TopPendingPriority() const { - return pending_requests.front().priority; + return pending_requests.front()->priority(); } std::deque<IdleSocket> idle_sockets; @@ -238,7 +263,7 @@ class ClientSocketPoolBase typedef std::map<const ClientSocketHandle*, ConnectJob*> ConnectJobMap; typedef std::set<const ConnectJob*> ConnectJobSet; - static void InsertRequestIntoQueue(const Request& r, + static void InsertRequestIntoQueue(const Request* r, RequestQueue* pending_requests); // Closes all idle sockets if |force| is true. Else, only closes idle @@ -267,7 +292,8 @@ class ClientSocketPoolBase // 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. + // 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); @@ -307,7 +333,7 @@ class ClientSocketPoolBase // Timer used to periodically prune idle sockets that timed out or can't be // reused. - base::RepeatingTimer<ClientSocketPoolBase> timer_; + base::RepeatingTimer<ClientSocketPoolBaseHelper> timer_; // The total number of idle sockets in the system. int idle_socket_count_; @@ -345,9 +371,154 @@ class ClientSocketPoolBase // Controls whether or not we use late binding of sockets. static bool g_late_binding; +}; + +} // namespace internal + +template <typename SocketParams> +class ClientSocketPoolBase { + public: + class Request : public internal::ClientSocketPoolBaseHelper::Request { + public: + Request(ClientSocketHandle* handle, + CompletionCallback* callback, + int priority, + const SocketParams& params, + LoadLog* load_log) + : internal::ClientSocketPoolBaseHelper::Request( + handle, callback, priority, load_log), + params_(params) {} + + const SocketParams& params() const { return params_; } + + private: + SocketParams params_; + }; + + class ConnectJobFactory { + public: + ConnectJobFactory() {} + virtual ~ConnectJobFactory() {} + + virtual ConnectJob* NewConnectJob( + const std::string& group_name, + const Request& request, + ConnectJob::Delegate* delegate) const = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(ConnectJobFactory); + }; + + ClientSocketPoolBase(int max_sockets, + int max_sockets_per_group, + ConnectJobFactory* connect_job_factory) + : helper_(new internal::ClientSocketPoolBaseHelper( + max_sockets, max_sockets_per_group, + new ConnectJobFactoryAdaptor(connect_job_factory))) {} + + ~ClientSocketPoolBase() {} + + // These member functions simply forward to ClientSocketPoolBaseHelper. + + // RequestSocket bundles up the parameters into a Request and then forwards to + // ClientSocketPoolBaseHelper::RequestSocket(). Note that the memory + // ownership is transferred in the asynchronous (ERR_IO_PENDING) case. + int RequestSocket(const std::string& group_name, + const SocketParams& params, + int priority, + ClientSocketHandle* handle, + CompletionCallback* callback, + LoadLog* load_log) { + scoped_ptr<Request> request( + new Request(handle, callback, priority, params, load_log)); + int rv = helper_->RequestSocket(group_name, request.get()); + if (rv == ERR_IO_PENDING) + request.release(); + return rv; + } + + void CancelRequest(const std::string& group_name, + const ClientSocketHandle* handle) { + return helper_->CancelRequest(group_name, handle); + } + + void ReleaseSocket(const std::string& group_name, ClientSocket* socket) { + return helper_->ReleaseSocket(group_name, socket); + } + + void CloseIdleSockets() { return helper_->CloseIdleSockets(); } + + int idle_socket_count() const { return helper_->idle_socket_count(); } + + int IdleSocketCountInGroup(const std::string& group_name) const { + return helper_->IdleSocketCountInGroup(group_name); + } + + LoadState GetLoadState(const std::string& group_name, + const ClientSocketHandle* handle) const { + return helper_->GetLoadState(group_name, handle); + } + + virtual void OnConnectJobComplete(int result, ConnectJob* job) { + return helper_->OnConnectJobComplete(result, job); + } + + // For testing. + bool may_have_stalled_group() const { + return helper_->may_have_stalled_group(); + } + + int NumConnectJobsInGroup(const std::string& group_name) const { + return helper_->NumConnectJobsInGroup(group_name); + } + + private: + // This adaptor class exists to bridge the + // internal::ClientSocketPoolBaseHelper::ConnectJobFactory and + // ClientSocketPoolBase::ConnectJobFactory types, allowing clients to use the + // typesafe ClientSocketPoolBase::ConnectJobFactory, rather than having to + // static_cast themselves. + class ConnectJobFactoryAdaptor + : public internal::ClientSocketPoolBaseHelper::ConnectJobFactory { + public: + typedef typename ClientSocketPoolBase<SocketParams>::ConnectJobFactory + ConnectJobFactory; + + explicit ConnectJobFactoryAdaptor( + ConnectJobFactory* connect_job_factory) + : connect_job_factory_(connect_job_factory) {} + virtual ~ConnectJobFactoryAdaptor() {} + + virtual ConnectJob* NewConnectJob( + const std::string& group_name, + const internal::ClientSocketPoolBaseHelper::Request& request, + ConnectJob::Delegate* delegate) const { + const Request* casted_request = static_cast<const Request*>(&request); + return connect_job_factory_->NewConnectJob( + group_name, *casted_request, delegate); + } + + const scoped_ptr<ConnectJobFactory> connect_job_factory_; + }; + + // One might ask why ClientSocketPoolBaseHelper is also refcounted if its + // containing ClientSocketPool is already refcounted. The reason is because + // DoReleaseSocket() posts a task. If ClientSocketPool gets deleted between + // the posting of the task and the execution, then we'll hit the DCHECK that + // |ClientSocketPoolBaseHelper::group_map_| is empty. + scoped_refptr<internal::ClientSocketPoolBaseHelper> helper_; + 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 963d017..1f20c76 100644 --- a/net/socket/client_socket_pool_base_unittest.cc +++ b/net/socket/client_socket_pool_base_unittest.cc @@ -21,11 +21,11 @@ namespace net { namespace { const int kDefaultMaxSockets = 4; - const int kDefaultMaxSocketsPerGroup = 2; - const int kDefaultPriority = 5; +typedef ClientSocketPoolBase<const void*> TestClientSocketPoolBase; + class MockClientSocket : public ClientSocket { public: MockClientSocket() : connected_(false) {} @@ -107,11 +107,11 @@ class TestConnectJob : public ConnectJob { TestConnectJob(JobType job_type, const std::string& group_name, - const ClientSocketPoolBase::Request& request, + const TestClientSocketPoolBase::Request& request, base::TimeDelta timeout_duration, ConnectJob::Delegate* delegate, MockClientSocketFactory* client_socket_factory) - : ConnectJob(group_name, request.handle, timeout_duration, delegate), + : ConnectJob(group_name, request.handle(), timeout_duration, delegate), job_type_(job_type), client_socket_factory_(client_socket_factory), method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {} @@ -202,7 +202,8 @@ class TestConnectJob : public ConnectJob { DISALLOW_COPY_AND_ASSIGN(TestConnectJob); }; -class TestConnectJobFactory : public ClientSocketPoolBase::ConnectJobFactory { +class TestConnectJobFactory + : public TestClientSocketPoolBase::ConnectJobFactory { public: explicit TestConnectJobFactory(MockClientSocketFactory* client_socket_factory) : job_type_(TestConnectJob::kMockJob), @@ -220,7 +221,7 @@ class TestConnectJobFactory : public ClientSocketPoolBase::ConnectJobFactory { virtual ConnectJob* NewConnectJob( const std::string& group_name, - const ClientSocketPoolBase::Request& request, + const TestClientSocketPoolBase::Request& request, ConnectJob::Delegate* delegate) const { return new TestConnectJob(job_type_, group_name, @@ -243,56 +244,55 @@ class TestClientSocketPool : public ClientSocketPool { TestClientSocketPool( int max_sockets, int max_sockets_per_group, - ClientSocketPoolBase::ConnectJobFactory* connect_job_factory) - : base_(new ClientSocketPoolBase( - max_sockets, max_sockets_per_group, connect_job_factory)) {} + TestClientSocketPoolBase::ConnectJobFactory* connect_job_factory) + : base_(max_sockets, max_sockets_per_group, connect_job_factory) {} virtual int RequestSocket( const std::string& group_name, - const HostResolver::RequestInfo& resolve_info, + const void* params, int priority, ClientSocketHandle* handle, CompletionCallback* callback, LoadLog* load_log) { - return base_->RequestSocket( - group_name, resolve_info, priority, handle, callback, load_log); + return base_.RequestSocket( + group_name, params, priority, handle, callback, load_log); } virtual void CancelRequest( const std::string& group_name, const ClientSocketHandle* handle) { - base_->CancelRequest(group_name, handle); + base_.CancelRequest(group_name, handle); } virtual void ReleaseSocket( const std::string& group_name, ClientSocket* socket) { - base_->ReleaseSocket(group_name, socket); + base_.ReleaseSocket(group_name, socket); } virtual void CloseIdleSockets() { - base_->CloseIdleSockets(); + base_.CloseIdleSockets(); } - virtual int IdleSocketCount() const { return base_->idle_socket_count(); } + virtual int IdleSocketCount() const { return base_.idle_socket_count(); } virtual int IdleSocketCountInGroup(const std::string& group_name) const { - return base_->IdleSocketCountInGroup(group_name); + return base_.IdleSocketCountInGroup(group_name); } virtual LoadState GetLoadState(const std::string& group_name, const ClientSocketHandle* handle) const { - return base_->GetLoadState(group_name, handle); + return base_.GetLoadState(group_name, handle); } - const ClientSocketPoolBase* base() const { return base_.get(); } + const TestClientSocketPoolBase* base() const { return &base_; } int NumConnectJobsInGroup(const std::string& group_name) const { - return base_->NumConnectJobsInGroup(group_name); + return base_.NumConnectJobsInGroup(group_name); } private: - const scoped_refptr<ClientSocketPoolBase> base_; + TestClientSocketPoolBase base_; DISALLOW_COPY_AND_ASSIGN(TestClientSocketPool); }; @@ -366,7 +366,7 @@ class ClientSocketPoolBaseTest : public ClientSocketPoolTest { pool_ = NULL; requests_.reset(); - ClientSocketPoolBase::EnableLateBindingOfSockets(false); + EnableLateBindingOfSockets(false); ClientSocketPoolTest::TearDown(); } @@ -380,9 +380,9 @@ class ClientSocketPoolBaseTest : public ClientSocketPoolTest { // completion. TEST_F(ClientSocketPoolBaseTest, ConnectJob_NoTimeoutOnSynchronousCompletion) { TestConnectJobDelegate delegate; - ClientSocketPoolBase::Request request; - ClientSocketHandle ignored(pool_.get()); - request.handle = &ignored; + ClientSocketHandle ignored(NULL); + TestClientSocketPoolBase::Request request( + &ignored, NULL, kDefaultPriority, NULL, NULL); scoped_ptr<TestConnectJob> job( new TestConnectJob(TestConnectJob::kMockJob, "a", @@ -395,9 +395,9 @@ TEST_F(ClientSocketPoolBaseTest, ConnectJob_NoTimeoutOnSynchronousCompletion) { TEST_F(ClientSocketPoolBaseTest, ConnectJob_TimedOut) { TestConnectJobDelegate delegate; - ClientSocketPoolBase::Request request; - ClientSocketHandle ignored(pool_.get()); - request.handle = &ignored; + ClientSocketHandle ignored(NULL); + TestClientSocketPoolBase::Request request( + &ignored, NULL, kDefaultPriority, NULL, NULL); // Deleted by TestConnectJobDelegate. TestConnectJob* job = new TestConnectJob(TestConnectJob::kMockPendingJob, @@ -1027,7 +1027,7 @@ TEST_F(ClientSocketPoolBaseTest, PendingJobCompletionOrder) { // available, the request will be serviced by the ConnectJob. TEST_F(ClientSocketPoolBaseTest, ReleaseSockets) { CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup); - ClientSocketPoolBase::EnableLateBindingOfSockets(false); + EnableLateBindingOfSockets(false); // Start job 1 (async OK) connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob); @@ -1105,7 +1105,7 @@ class ClientSocketPoolBaseTest_LateBinding : public ClientSocketPoolBaseTest { protected: virtual void SetUp() { ClientSocketPoolBaseTest::SetUp(); - ClientSocketPoolBase::EnableLateBindingOfSockets(true); + EnableLateBindingOfSockets(true); } }; @@ -1114,9 +1114,8 @@ class ClientSocketPoolBaseTest_LateBinding : public ClientSocketPoolBaseTest { TEST_F(ClientSocketPoolBaseTest_LateBinding, ConnectJob_NoTimeoutOnSynchronousCompletion) { TestConnectJobDelegate delegate; - ClientSocketPoolBase::Request request; ClientSocketHandle ignored(pool_.get()); - request.handle = &ignored; + TestClientSocketPoolBase::Request request(&ignored, NULL, 0, NULL, NULL); scoped_ptr<TestConnectJob> job( new TestConnectJob(TestConnectJob::kMockJob, "a", @@ -1129,9 +1128,8 @@ TEST_F(ClientSocketPoolBaseTest_LateBinding, TEST_F(ClientSocketPoolBaseTest_LateBinding, ConnectJob_TimedOut) { TestConnectJobDelegate delegate; - ClientSocketPoolBase::Request request; ClientSocketHandle ignored(pool_.get()); - request.handle = &ignored; + TestClientSocketPoolBase::Request request(&ignored, NULL, 0, NULL, NULL); // Deleted by TestConnectJobDelegate. TestConnectJob* job = new TestConnectJob(TestConnectJob::kMockPendingJob, diff --git a/net/socket/tcp_client_socket_pool.cc b/net/socket/tcp_client_socket_pool.cc index ae080dd..2a23e55 100644 --- a/net/socket/tcp_client_socket_pool.cc +++ b/net/socket/tcp_client_socket_pool.cc @@ -129,10 +129,10 @@ int TCPConnectJob::DoTCPConnectComplete(int result) { ConnectJob* TCPClientSocketPool::TCPConnectJobFactory::NewConnectJob( const std::string& group_name, - const ClientSocketPoolBase::Request& request, + const PoolBase::Request& request, ConnectJob::Delegate* delegate) const { return new TCPConnectJob( - group_name, request.resolve_info, request.handle, + group_name, request.params(), request.handle(), base::TimeDelta::FromSeconds(kTCPConnectJobTimeoutInSeconds), client_socket_factory_, host_resolver_, delegate); } @@ -142,47 +142,48 @@ TCPClientSocketPool::TCPClientSocketPool( int max_sockets_per_group, HostResolver* host_resolver, ClientSocketFactory* client_socket_factory) - : base_(new ClientSocketPoolBase( - max_sockets, max_sockets_per_group, - new TCPConnectJobFactory(client_socket_factory, host_resolver))) {} + : base_(max_sockets, max_sockets_per_group, + new TCPConnectJobFactory(client_socket_factory, host_resolver)) {} TCPClientSocketPool::~TCPClientSocketPool() {} int TCPClientSocketPool::RequestSocket( const std::string& group_name, - const HostResolver::RequestInfo& resolve_info, + const void* resolve_info, int priority, ClientSocketHandle* handle, CompletionCallback* callback, LoadLog* load_log) { - return base_->RequestSocket( - group_name, resolve_info, priority, handle, callback, load_log); + const HostResolver::RequestInfo* casted_resolve_info = + static_cast<const HostResolver::RequestInfo*>(resolve_info); + return base_.RequestSocket( + group_name, *casted_resolve_info, priority, handle, callback, load_log); } void TCPClientSocketPool::CancelRequest( const std::string& group_name, const ClientSocketHandle* handle) { - base_->CancelRequest(group_name, handle); + base_.CancelRequest(group_name, handle); } void TCPClientSocketPool::ReleaseSocket( const std::string& group_name, ClientSocket* socket) { - base_->ReleaseSocket(group_name, socket); + base_.ReleaseSocket(group_name, socket); } void TCPClientSocketPool::CloseIdleSockets() { - base_->CloseIdleSockets(); + base_.CloseIdleSockets(); } int TCPClientSocketPool::IdleSocketCountInGroup( const std::string& group_name) const { - return base_->IdleSocketCountInGroup(group_name); + return base_.IdleSocketCountInGroup(group_name); } LoadState TCPClientSocketPool::GetLoadState( const std::string& group_name, const ClientSocketHandle* handle) const { - return base_->GetLoadState(group_name, handle); + return base_.GetLoadState(group_name, handle); } } // namespace net diff --git a/net/socket/tcp_client_socket_pool.h b/net/socket/tcp_client_socket_pool.h index 8533d0e..4532000 100644 --- a/net/socket/tcp_client_socket_pool.h +++ b/net/socket/tcp_client_socket_pool.h @@ -81,7 +81,7 @@ class TCPClientSocketPool : public ClientSocketPool { // ClientSocketPool methods: virtual int RequestSocket(const std::string& group_name, - const HostResolver::RequestInfo& resolve_info, + const void* resolve_info, int priority, ClientSocketHandle* handle, CompletionCallback* callback, @@ -96,7 +96,7 @@ class TCPClientSocketPool : public ClientSocketPool { virtual void CloseIdleSockets(); virtual int IdleSocketCount() const { - return base_->idle_socket_count(); + return base_.idle_socket_count(); } virtual int IdleSocketCountInGroup(const std::string& group_name) const; @@ -105,10 +105,10 @@ class TCPClientSocketPool : public ClientSocketPool { const ClientSocketHandle* handle) const; private: - virtual ~TCPClientSocketPool(); + typedef ClientSocketPoolBase<HostResolver::RequestInfo> PoolBase; class TCPConnectJobFactory - : public ClientSocketPoolBase::ConnectJobFactory { + : public PoolBase::ConnectJobFactory { public: TCPConnectJobFactory(ClientSocketFactory* client_socket_factory, HostResolver* host_resolver) @@ -121,7 +121,7 @@ class TCPClientSocketPool : public ClientSocketPool { virtual ConnectJob* NewConnectJob( const std::string& group_name, - const ClientSocketPoolBase::Request& request, + const PoolBase::Request& request, ConnectJob::Delegate* delegate) const; private: @@ -131,12 +131,9 @@ class TCPClientSocketPool : public ClientSocketPool { DISALLOW_COPY_AND_ASSIGN(TCPConnectJobFactory); }; - // One might ask why ClientSocketPoolBase is also refcounted if its - // containing ClientSocketPool is already refcounted. The reason is because - // DoReleaseSocket() posts a task. If ClientSocketPool gets deleted between - // the posting of the task and the execution, then we'll hit the DCHECK that - // |ClientSocketPoolBase::group_map_| is empty. - scoped_refptr<ClientSocketPoolBase> base_; + virtual ~TCPClientSocketPool(); + + PoolBase base_; DISALLOW_COPY_AND_ASSIGN(TCPClientSocketPool); }; |