| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/base/client_socket_pool.h" | 5 #include "net/base/client_socket_pool.h" |
| 6 | 6 |
| 7 #include "base/message_loop.h" | 7 #include "base/message_loop.h" |
| 8 #include "net/base/client_socket.h" | 8 #include "net/base/client_socket.h" |
| 9 #include "net/base/client_socket_handle.h" | 9 #include "net/base/client_socket_handle.h" |
| 10 #include "net/base/net_errors.h" | 10 #include "net/base/net_errors.h" |
| (...skipping 10 matching lines...) Expand all Loading... |
| 21 // some conditions. See http://crbug.com/4606. | 21 // some conditions. See http://crbug.com/4606. |
| 22 const int kCleanupInterval = 10; // DO NOT INCREASE THIS TIMEOUT. | 22 const int kCleanupInterval = 10; // DO NOT INCREASE THIS TIMEOUT. |
| 23 | 23 |
| 24 // The maximum duration, in seconds, to keep idle persistent sockets alive. | 24 // The maximum duration, in seconds, to keep idle persistent sockets alive. |
| 25 const int kIdleTimeout = 300; // 5 minutes. | 25 const int kIdleTimeout = 300; // 5 minutes. |
| 26 | 26 |
| 27 } // namespace | 27 } // namespace |
| 28 | 28 |
| 29 namespace net { | 29 namespace net { |
| 30 | 30 |
| 31 ClientSocketPool::ClientSocketPool(int max_sockets_per_group) | 31 ClientSocketPool::ClientSocketPool(int max_sockets_per_group, int max_sockets) |
| 32 : idle_socket_count_(0), | 32 : idle_socket_count_(0), |
| 33 max_sockets_per_group_(max_sockets_per_group) { | 33 active_socket_count_(0), |
| 34 max_sockets_per_group_(max_sockets_per_group), |
| 35 max_sockets_(max_sockets), |
| 36 may_have_stalled_group_(false) { |
| 37 DCHECK(max_sockets_per_group <= max_sockets); |
| 34 } | 38 } |
| 35 | 39 |
| 36 ClientSocketPool::~ClientSocketPool() { | 40 ClientSocketPool::~ClientSocketPool() { |
| 37 // Clean up any idle sockets. Assert that we have no remaining active | 41 // Clean up any idle sockets. Assert that we have no remaining active |
| 38 // sockets or pending requests. They should have all been cleaned up prior | 42 // sockets or pending requests. They should have all been cleaned up prior |
| 39 // to the manager being destroyed. | 43 // to the manager being destroyed. |
| 40 CloseIdleSockets(); | 44 CloseIdleSockets(); |
| 41 DCHECK(group_map_.empty()); | 45 DCHECK(group_map_.empty()); |
| 42 } | 46 } |
| 43 | 47 |
| 44 // InsertRequestIntoQueue inserts the request into the queue based on | 48 // InsertRequestIntoQueue inserts the request into the queue based on |
| 45 // priority. Highest priorities are closest to the front. Older requests are | 49 // priority. Highest priorities are closest to the front. Older requests are |
| 46 // prioritized over requests of equal priority. | 50 // prioritized over requests of equal priority. |
| 47 // | 51 // |
| 48 // static | 52 // static |
| 49 void ClientSocketPool::InsertRequestIntoQueue(const Request& r, | 53 void ClientSocketPool::InsertRequestIntoQueue(const Request& r, |
| 50 RequestQueue* pending_requests) { | 54 RequestQueue* pending_requests) { |
| 51 RequestQueue::iterator it = pending_requests->begin(); | 55 RequestQueue::iterator it = pending_requests->begin(); |
| 52 while (it != pending_requests->end() && r.priority <= it->priority) | 56 while (it != pending_requests->end() && r.priority <= it->priority) |
| 53 ++it; | 57 ++it; |
| 54 pending_requests->insert(it, r); | 58 pending_requests->insert(it, r); |
| 55 } | 59 } |
| 56 | 60 |
| 57 int ClientSocketPool::RequestSocket(ClientSocketHandle* handle, | 61 int ClientSocketPool::RequestSocket(ClientSocketHandle* handle, |
| 58 int priority, | 62 int priority, |
| 59 CompletionCallback* callback) { | 63 CompletionCallback* callback) { |
| 60 Group& group = group_map_[handle->group_name_]; | 64 Group& group = group_map_[handle->group_name_]; |
| 61 | 65 |
| 62 // Can we make another active socket now? | 66 // Can we make another active socket now? |
| 63 if (group.active_socket_count == max_sockets_per_group_) { | 67 bool has_max_sockets = (active_socket_count_ == max_sockets_); |
| 68 if (group.active_socket_count == max_sockets_per_group_ || has_max_sockets) { |
| 69 may_have_stalled_group_ |= has_max_sockets; |
| 64 Request r; | 70 Request r; |
| 65 r.handle = handle; | 71 r.handle = handle; |
| 66 DCHECK(callback); | 72 DCHECK(callback); |
| 67 r.callback = callback; | 73 r.callback = callback; |
| 68 r.priority = priority; | 74 r.priority = priority; |
| 69 InsertRequestIntoQueue(r, &group.pending_requests); | 75 InsertRequestIntoQueue(r, &group.pending_requests); |
| 70 return ERR_IO_PENDING; | 76 return ERR_IO_PENDING; |
| 71 } | 77 } |
| 72 | 78 |
| 73 // OK, we are going to activate one. | 79 // OK, we are going to activate one. |
| 74 group.active_socket_count++; | 80 group.active_socket_count++; |
| 81 active_socket_count_++; |
| 75 | 82 |
| 76 // Use idle sockets in LIFO order because they're more likely to be | 83 // Use idle sockets in LIFO order because they're more likely to be |
| 77 // still reusable. | 84 // still reusable. |
| 78 while (!group.idle_sockets.empty()) { | 85 while (!group.idle_sockets.empty()) { |
| 79 IdleSocket idle_socket = group.idle_sockets.back(); | 86 IdleSocket idle_socket = group.idle_sockets.back(); |
| 80 group.idle_sockets.pop_back(); | 87 group.idle_sockets.pop_back(); |
| 81 DecrementIdleCount(); | 88 DecrementIdleCount(); |
| 82 if ((*idle_socket.ptr)->IsConnectedAndIdle()) { | 89 if ((*idle_socket.ptr)->IsConnectedAndIdle()) { |
| 83 // We found one we can reuse! | 90 // We found one we can reuse! |
| 84 handle->socket_ = idle_socket.ptr; | 91 handle->socket_ = idle_socket.ptr; |
| 85 return OK; | 92 return OK; |
| 86 } | 93 } |
| 87 delete idle_socket.ptr; | 94 delete idle_socket.ptr; |
| 88 } | 95 } |
| 89 | 96 |
| 90 handle->socket_ = new ClientSocketPtr(); | 97 handle->socket_ = new ClientSocketPtr(); |
| 91 return OK; | 98 return OK; |
| 92 } | 99 } |
| 93 | 100 |
| 94 void ClientSocketPool::CancelRequest(ClientSocketHandle* handle) { | 101 void ClientSocketPool::CancelRequest(ClientSocketHandle* handle) { |
| 95 Group& group = group_map_[handle->group_name_]; | 102 GroupMap::iterator i = group_map_.find(handle->group_name_); |
| 96 | 103 DCHECK(i != group_map_.end()); |
| 97 // In order for us to be canceling a pending request, we must have active | 104 Group& group = i->second; |
| 98 // sockets equaling the limit. NOTE: The correctness of the code doesn't | |
| 99 // require this assertion. | |
| 100 DCHECK(group.active_socket_count == max_sockets_per_group_); | |
| 101 | 105 |
| 102 // Search pending_requests for matching handle. | 106 // Search pending_requests for matching handle. |
| 103 std::deque<Request>::iterator it = group.pending_requests.begin(); | 107 std::deque<Request>::iterator it = group.pending_requests.begin(); |
| 104 for (; it != group.pending_requests.end(); ++it) { | 108 for (; it != group.pending_requests.end(); ++it) { |
| 105 if (it->handle == handle) { | 109 if (it->handle == handle) { |
| 106 group.pending_requests.erase(it); | 110 group.pending_requests.erase(it); |
| 107 break; | 111 break; |
| 108 } | 112 } |
| 109 } | 113 } |
| 114 |
| 115 // Delete group if no longer needed. |
| 116 if (group.empty()) |
| 117 group_map_.erase(i); |
| 110 } | 118 } |
| 111 | 119 |
| 112 void ClientSocketPool::ReleaseSocket(ClientSocketHandle* handle) { | 120 void ClientSocketPool::ReleaseSocket(ClientSocketHandle* handle) { |
| 113 // Run this asynchronously to allow the caller to finish before we let | 121 // Run this asynchronously to allow the caller to finish before we let |
| 114 // another to begin doing work. This also avoids nasty recursion issues. | 122 // another to begin doing work. This also avoids nasty recursion issues. |
| 115 // NOTE: We cannot refer to the handle argument after this method returns. | 123 // NOTE: We cannot refer to the handle argument after this method returns. |
| 116 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( | 124 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( |
| 117 this, &ClientSocketPool::DoReleaseSocket, handle->group_name_, | 125 this, &ClientSocketPool::DoReleaseSocket, handle->group_name_, |
| 118 handle->socket_)); | 126 handle->socket_)); |
| 119 } | 127 } |
| (...skipping 25 matching lines...) Expand all Loading... |
| 145 if (force || j->ShouldCleanup(now)) { | 153 if (force || j->ShouldCleanup(now)) { |
| 146 delete j->ptr; | 154 delete j->ptr; |
| 147 j = group.idle_sockets.erase(j); | 155 j = group.idle_sockets.erase(j); |
| 148 DecrementIdleCount(); | 156 DecrementIdleCount(); |
| 149 } else { | 157 } else { |
| 150 ++j; | 158 ++j; |
| 151 } | 159 } |
| 152 } | 160 } |
| 153 | 161 |
| 154 // Delete group if no longer needed. | 162 // Delete group if no longer needed. |
| 155 if (group.active_socket_count == 0 && group.idle_sockets.empty()) { | 163 if (group.empty()) { |
| 156 DCHECK(group.pending_requests.empty()); | |
| 157 group_map_.erase(i++); | 164 group_map_.erase(i++); |
| 158 } else { | 165 } else { |
| 159 ++i; | 166 ++i; |
| 160 } | 167 } |
| 161 } | 168 } |
| 162 } | 169 } |
| 163 | 170 |
| 164 void ClientSocketPool::IncrementIdleCount() { | 171 void ClientSocketPool::IncrementIdleCount() { |
| 165 if (++idle_socket_count_ == 1) | 172 if (++idle_socket_count_ == 1) |
| 166 timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this, | 173 timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this, |
| 167 &ClientSocketPool::OnCleanupTimerFired); | 174 &ClientSocketPool::OnCleanupTimerFired); |
| 168 } | 175 } |
| 169 | 176 |
| 170 void ClientSocketPool::DecrementIdleCount() { | 177 void ClientSocketPool::DecrementIdleCount() { |
| 171 if (--idle_socket_count_ == 0) | 178 if (--idle_socket_count_ == 0) |
| 172 timer_.Stop(); | 179 timer_.Stop(); |
| 173 } | 180 } |
| 174 | 181 |
| 175 void ClientSocketPool::DoReleaseSocket(const std::string& group_name, | 182 void ClientSocketPool::DoReleaseSocket(const std::string& group_name, |
| 176 ClientSocketPtr* ptr) { | 183 ClientSocketPtr* ptr) { |
| 177 GroupMap::iterator i = group_map_.find(group_name); | 184 GroupMap::iterator i = group_map_.find(group_name); |
| 178 DCHECK(i != group_map_.end()); | 185 DCHECK(i != group_map_.end()); |
| 179 | 186 |
| 180 Group& group = i->second; | 187 Group& group = i->second; |
| 181 | 188 |
| 182 DCHECK(group.active_socket_count > 0); | 189 DCHECK(group.active_socket_count > 0); |
| 190 DCHECK(active_socket_count_ > 0); |
| 191 |
| 183 group.active_socket_count--; | 192 group.active_socket_count--; |
| 193 active_socket_count_--; |
| 184 | 194 |
| 185 bool can_reuse = ptr->get() && (*ptr)->IsConnectedAndIdle(); | 195 bool can_reuse = ptr->get() && (*ptr)->IsConnectedAndIdle(); |
| 186 if (can_reuse) { | 196 if (can_reuse) { |
| 187 IdleSocket idle_socket; | 197 IdleSocket idle_socket; |
| 188 idle_socket.ptr = ptr; | 198 idle_socket.ptr = ptr; |
| 189 idle_socket.start_time = base::TimeTicks::Now(); | 199 idle_socket.start_time = base::TimeTicks::Now(); |
| 190 | 200 |
| 191 group.idle_sockets.push_back(idle_socket); | 201 group.idle_sockets.push_back(idle_socket); |
| 192 IncrementIdleCount(); | 202 IncrementIdleCount(); |
| 193 } else { | 203 } else { |
| 194 delete ptr; | 204 delete ptr; |
| 195 } | 205 } |
| 196 | 206 |
| 197 // Process one pending request. | 207 // Process one pending request. |
| 198 if (!group.pending_requests.empty()) { | 208 if (may_have_stalled_group_) { |
| 199 Request r = group.pending_requests.front(); | 209 // The highest priority pending request may belong to another group, so |
| 200 group.pending_requests.pop_front(); | 210 // check all of them. Worst-case this takes |max_sockets_| iterations. |
| 201 int rv = RequestSocket(r.handle, r.priority, NULL); | 211 if (Group* top_group = FindTopStalledGroup()) |
| 202 DCHECK(rv == OK); | 212 StartPendingRequestForGroup(top_group); |
| 203 r.callback->Run(rv); | 213 |
| 204 return; | 214 // Check if there are any stalled groups left. |
| 215 if (!FindTopStalledGroup()) |
| 216 may_have_stalled_group_ = false; |
| 217 |
| 218 } else if (!group.pending_requests.empty()) { |
| 219 // Otherwise just dequeue the first pending request of |group|. |
| 220 StartPendingRequestForGroup(&group); |
| 205 } | 221 } |
| 206 | 222 |
| 207 // Delete group if no longer needed. | 223 // Delete group if no longer needed. |
| 208 if (group.active_socket_count == 0 && group.idle_sockets.empty()) { | 224 if (group.empty()) |
| 209 DCHECK(group.pending_requests.empty()); | |
| 210 group_map_.erase(i); | 225 group_map_.erase(i); |
| 226 } |
| 227 |
| 228 void ClientSocketPool::StartPendingRequestForGroup(Group* group) { |
| 229 Request r = group->pending_requests.front(); |
| 230 group->pending_requests.pop_front(); |
| 231 int rv = RequestSocket(r.handle, r.priority, NULL); |
| 232 DCHECK(rv == OK); |
| 233 r.callback->Run(rv); |
| 234 } |
| 235 |
| 236 // Search for the highest priority pending request, amongst the groups that |
| 237 // are not at the |max_sockets_per_group_| limit. Note: for requests with |
| 238 // the same priority, the winner is based on group hash ordering (and not |
| 239 // insertion order). |
| 240 ClientSocketPool::Group* ClientSocketPool::FindTopStalledGroup() { |
| 241 Group* top_group = NULL; |
| 242 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end(); ++i) { |
| 243 Group* group = &i->second; |
| 244 const RequestQueue& queue = group->pending_requests; |
| 245 if (group->active_socket_count < max_sockets_per_group_ && |
| 246 !queue.empty() && (top_group == NULL || queue.front().priority > |
| 247 top_group->pending_requests.front().priority)) { |
| 248 top_group = group; |
| 249 } |
| 211 } | 250 } |
| 251 return top_group; |
| 212 } | 252 } |
| 213 | 253 |
| 214 } // namespace net | 254 } // namespace net |
| OLD | NEW |