OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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/socket/client_socket_pool_base.h" | 5 #include "net/socket/client_socket_pool_base.h" |
6 | 6 |
7 #include <math.h> | 7 #include <math.h> |
8 #include "base/compiler_specific.h" | 8 #include "base/compiler_specific.h" |
9 #include "base/format_macros.h" | 9 #include "base/format_macros.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
195 } | 195 } |
196 | 196 |
197 ClientSocketPoolBaseHelper::~ClientSocketPoolBaseHelper() { | 197 ClientSocketPoolBaseHelper::~ClientSocketPoolBaseHelper() { |
198 // Clean up any idle sockets and pending connect jobs. Assert that we have no | 198 // Clean up any idle sockets and pending connect jobs. Assert that we have no |
199 // remaining active sockets or pending requests. They should have all been | 199 // remaining active sockets or pending requests. They should have all been |
200 // cleaned up prior to |this| being destroyed. | 200 // cleaned up prior to |this| being destroyed. |
201 Flush(); | 201 Flush(); |
202 DCHECK(group_map_.empty()); | 202 DCHECK(group_map_.empty()); |
203 DCHECK(pending_callback_map_.empty()); | 203 DCHECK(pending_callback_map_.empty()); |
204 DCHECK_EQ(0, connecting_socket_count_); | 204 DCHECK_EQ(0, connecting_socket_count_); |
205 DCHECK(higher_layer_pools_.empty()); | |
205 | 206 |
206 NetworkChangeNotifier::RemoveIPAddressObserver(this); | 207 NetworkChangeNotifier::RemoveIPAddressObserver(this); |
207 } | 208 } |
208 | 209 |
209 // InsertRequestIntoQueue inserts the request into the queue based on | 210 // InsertRequestIntoQueue inserts the request into the queue based on |
210 // priority. Highest priorities are closest to the front. Older requests are | 211 // priority. Highest priorities are closest to the front. Older requests are |
211 // prioritized over requests of equal priority. | 212 // prioritized over requests of equal priority. |
212 // | 213 // |
213 // static | 214 // static |
214 void ClientSocketPoolBaseHelper::InsertRequestIntoQueue( | 215 void ClientSocketPoolBaseHelper::InsertRequestIntoQueue( |
215 const Request* r, RequestQueue* pending_requests) { | 216 const Request* r, RequestQueue* pending_requests) { |
216 RequestQueue::iterator it = pending_requests->begin(); | 217 RequestQueue::iterator it = pending_requests->begin(); |
217 while (it != pending_requests->end() && r->priority() >= (*it)->priority()) | 218 while (it != pending_requests->end() && r->priority() >= (*it)->priority()) |
218 ++it; | 219 ++it; |
219 pending_requests->insert(it, r); | 220 pending_requests->insert(it, r); |
220 } | 221 } |
221 | 222 |
222 // static | 223 // static |
223 const ClientSocketPoolBaseHelper::Request* | 224 const ClientSocketPoolBaseHelper::Request* |
224 ClientSocketPoolBaseHelper::RemoveRequestFromQueue( | 225 ClientSocketPoolBaseHelper::RemoveRequestFromQueue( |
225 const RequestQueue::iterator& it, Group* group) { | 226 const RequestQueue::iterator& it, Group* group) { |
226 const Request* req = *it; | 227 const Request* req = *it; |
227 group->mutable_pending_requests()->erase(it); | 228 group->mutable_pending_requests()->erase(it); |
228 // If there are no more requests, we kill the backup timer. | 229 // If there are no more requests, we kill the backup timer. |
229 if (group->pending_requests().empty()) | 230 if (group->pending_requests().empty()) |
230 group->CleanupBackupJob(); | 231 group->CleanupBackupJob(); |
231 return req; | 232 return req; |
232 } | 233 } |
233 | 234 |
235 void ClientSocketPoolBaseHelper::AddLayeredPool(LayeredPool* pool) { | |
236 CHECK(pool); | |
237 CHECK(!ContainsKey(higher_layer_pools_, pool)); | |
238 higher_layer_pools_.insert(pool); | |
239 } | |
240 | |
241 void ClientSocketPoolBaseHelper::RemoveLayeredPool(LayeredPool* pool) { | |
242 CHECK(pool); | |
243 CHECK(ContainsKey(higher_layer_pools_, pool)); | |
244 higher_layer_pools_.erase(pool); | |
245 } | |
246 | |
234 int ClientSocketPoolBaseHelper::RequestSocket( | 247 int ClientSocketPoolBaseHelper::RequestSocket( |
235 const std::string& group_name, | 248 const std::string& group_name, |
236 const Request* request) { | 249 const Request* request) { |
237 CHECK(request->callback()); | 250 CHECK(request->callback()); |
238 CHECK(request->handle()); | 251 CHECK(request->handle()); |
239 | 252 |
240 request->net_log().BeginEvent(NetLog::TYPE_SOCKET_POOL, NULL); | 253 request->net_log().BeginEvent(NetLog::TYPE_SOCKET_POOL, NULL); |
241 Group* group = GetOrCreateGroup(group_name); | 254 Group* group = GetOrCreateGroup(group_name); |
242 | 255 |
243 int rv = RequestSocketInternal(group_name, request); | 256 int rv = RequestSocketInternal(group_name, request); |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
314 if (AssignIdleSocketToGroup(request, group)) | 327 if (AssignIdleSocketToGroup(request, group)) |
315 return OK; | 328 return OK; |
316 } | 329 } |
317 | 330 |
318 if (!preconnecting && group->TryToUsePreconnectConnectJob()) | 331 if (!preconnecting && group->TryToUsePreconnectConnectJob()) |
319 return ERR_IO_PENDING; | 332 return ERR_IO_PENDING; |
320 | 333 |
321 // Can we make another active socket now? | 334 // Can we make another active socket now? |
322 if (!group->HasAvailableSocketSlot(max_sockets_per_group_) && | 335 if (!group->HasAvailableSocketSlot(max_sockets_per_group_) && |
323 !request->ignore_limits()) { | 336 !request->ignore_limits()) { |
337 // TODO(willchan): Consider whether or not we need to close a socket in a | |
338 // higher layered group. I don't think this makes sense since we would just | |
339 // reuse that socket then if we needed one and wouldn't make it down to this | |
340 // layer. | |
324 request->net_log().AddEvent( | 341 request->net_log().AddEvent( |
325 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP, NULL); | 342 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP, NULL); |
326 return ERR_IO_PENDING; | 343 return ERR_IO_PENDING; |
327 } | 344 } |
328 | 345 |
329 if (ReachedMaxSocketsLimit() && !request->ignore_limits()) { | 346 if (ReachedMaxSocketsLimit() && !request->ignore_limits()) { |
330 if (idle_socket_count() > 0) { | 347 if (idle_socket_count() > 0) { |
348 // There's an idle socket in this pool. Either that's because there's | |
349 // still one in this group, but we got here due to preconnecting bypassing | |
350 // idle sockets, or because there's an idle socket in another group. | |
331 bool closed = CloseOneIdleSocketExceptInGroup(group); | 351 bool closed = CloseOneIdleSocketExceptInGroup(group); |
332 if (preconnecting && !closed) | 352 if (preconnecting && !closed) |
333 return ERR_PRECONNECT_MAX_SOCKET_LIMIT; | 353 return ERR_PRECONNECT_MAX_SOCKET_LIMIT; |
334 } else { | 354 } else if (!CloseOneIdleConnectionInLayeredPool()) { |
mmenke
2011/10/27 18:52:37
Is this guaranteed to result in ReachedMaxSocketsL
willchan no longer on Chromium
2011/11/08 22:26:13
Sorry, I don't grok. Which path do you mean? Also
mmenke
2011/11/08 22:41:04
So you have a request with ignore_limits() set to
willchan no longer on Chromium
2011/11/15 18:44:06
GREAT catch. I'm impressed you caught this given y
| |
335 // We could check if we really have a stalled group here, but it requires | 355 // We could check if we really have a stalled group here, but it requires |
336 // a scan of all groups, so just flip a flag here, and do the check later. | 356 // a scan of all groups, so just flip a flag here, and do the check later. |
337 request->net_log().AddEvent( | 357 request->net_log().AddEvent( |
338 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS, NULL); | 358 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS, NULL); |
339 return ERR_IO_PENDING; | 359 return ERR_IO_PENDING; |
340 } | 360 } |
341 } | 361 } |
342 | 362 |
343 // We couldn't find a socket to reuse, so allocate and connect a new one. | 363 // We couldn't find a socket to reuse, and there's space to allocate one, |
364 // so allocate and connect a new one. | |
344 scoped_ptr<ConnectJob> connect_job( | 365 scoped_ptr<ConnectJob> connect_job( |
345 connect_job_factory_->NewConnectJob(group_name, *request, this)); | 366 connect_job_factory_->NewConnectJob(group_name, *request, this)); |
346 | 367 |
347 connect_job->Initialize(preconnecting); | 368 connect_job->Initialize(preconnecting); |
348 int rv = connect_job->Connect(); | 369 int rv = connect_job->Connect(); |
349 if (rv == OK) { | 370 if (rv == OK) { |
350 LogBoundConnectJobToRequest(connect_job->net_log().source(), request); | 371 LogBoundConnectJobToRequest(connect_job->net_log().source(), request); |
351 if (!preconnecting) { | 372 if (!preconnecting) { |
352 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */, | 373 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */, |
353 handle, base::TimeDelta(), group, request->net_log()); | 374 handle, base::TimeDelta(), group, request->net_log()); |
(...skipping 400 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
754 // its limit, may be left with other stalled groups that could be | 775 // its limit, may be left with other stalled groups that could be |
755 // woken. This isn't optimal, but there is no starvation, so to avoid | 776 // woken. This isn't optimal, but there is no starvation, so to avoid |
756 // the looping we leave it at this. | 777 // the looping we leave it at this. |
757 OnAvailableSocketSlot(top_group_name, top_group); | 778 OnAvailableSocketSlot(top_group_name, top_group); |
758 } | 779 } |
759 | 780 |
760 // Search for the highest priority pending request, amongst the groups that | 781 // Search for the highest priority pending request, amongst the groups that |
761 // are not at the |max_sockets_per_group_| limit. Note: for requests with | 782 // are not at the |max_sockets_per_group_| limit. Note: for requests with |
762 // the same priority, the winner is based on group hash ordering (and not | 783 // the same priority, the winner is based on group hash ordering (and not |
763 // insertion order). | 784 // insertion order). |
764 bool ClientSocketPoolBaseHelper::FindTopStalledGroup(Group** group, | 785 bool ClientSocketPoolBaseHelper::FindTopStalledGroup( |
765 std::string* group_name) { | 786 Group** group, |
787 std::string* group_name) const { | |
788 CHECK((group && group_name) || (!group && !group_name)); | |
766 Group* top_group = NULL; | 789 Group* top_group = NULL; |
767 const std::string* top_group_name = NULL; | 790 const std::string* top_group_name = NULL; |
768 bool has_stalled_group = false; | 791 bool has_stalled_group = false; |
769 for (GroupMap::iterator i = group_map_.begin(); | 792 for (GroupMap::const_iterator i = group_map_.begin(); |
770 i != group_map_.end(); ++i) { | 793 i != group_map_.end(); ++i) { |
771 Group* curr_group = i->second; | 794 Group* curr_group = i->second; |
772 const RequestQueue& queue = curr_group->pending_requests(); | 795 const RequestQueue& queue = curr_group->pending_requests(); |
773 if (queue.empty()) | 796 if (queue.empty()) |
774 continue; | 797 continue; |
775 if (curr_group->IsStalled(max_sockets_per_group_)) { | 798 if (curr_group->IsStalled(max_sockets_per_group_)) { |
799 if (!group) | |
800 return true; | |
776 has_stalled_group = true; | 801 has_stalled_group = true; |
777 bool has_higher_priority = !top_group || | 802 bool has_higher_priority = !top_group || |
778 curr_group->TopPendingPriority() < top_group->TopPendingPriority(); | 803 curr_group->TopPendingPriority() < top_group->TopPendingPriority(); |
779 if (has_higher_priority) { | 804 if (has_higher_priority) { |
780 top_group = curr_group; | 805 top_group = curr_group; |
781 top_group_name = &i->first; | 806 top_group_name = &i->first; |
782 } | 807 } |
783 } | 808 } |
784 } | 809 } |
785 | 810 |
786 if (top_group) { | 811 if (top_group) { |
812 CHECK(group); | |
787 *group = top_group; | 813 *group = top_group; |
788 *group_name = *top_group_name; | 814 *group_name = *top_group_name; |
815 } else { | |
816 CHECK(!has_stalled_group); | |
789 } | 817 } |
790 return has_stalled_group; | 818 return has_stalled_group; |
791 } | 819 } |
792 | 820 |
793 void ClientSocketPoolBaseHelper::OnConnectJobComplete( | 821 void ClientSocketPoolBaseHelper::OnConnectJobComplete( |
794 int result, ConnectJob* job) { | 822 int result, ConnectJob* job) { |
795 DCHECK_NE(ERR_IO_PENDING, result); | 823 DCHECK_NE(ERR_IO_PENDING, result); |
796 const std::string group_name = job->group_name(); | 824 const std::string group_name = job->group_name(); |
797 GroupMap::iterator group_it = group_map_.find(group_name); | 825 GroupMap::iterator group_it = group_map_.find(group_name); |
798 CHECK(group_it != group_map_.end()); | 826 CHECK(group_it != group_map_.end()); |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
851 Flush(); | 879 Flush(); |
852 } | 880 } |
853 | 881 |
854 void ClientSocketPoolBaseHelper::Flush() { | 882 void ClientSocketPoolBaseHelper::Flush() { |
855 pool_generation_number_++; | 883 pool_generation_number_++; |
856 CancelAllConnectJobs(); | 884 CancelAllConnectJobs(); |
857 CloseIdleSockets(); | 885 CloseIdleSockets(); |
858 AbortAllRequests(); | 886 AbortAllRequests(); |
859 } | 887 } |
860 | 888 |
889 bool ClientSocketPoolBaseHelper::IsStalled() const { | |
890 CHECK_LE(handed_out_socket_count_ + connecting_socket_count_, max_sockets_); | |
mmenke
2011/10/27 18:52:37
Given that requests have an "ignore_limits" flag,
willchan no longer on Chromium
2011/11/08 22:26:13
Good point!
| |
891 if ((handed_out_socket_count_ + connecting_socket_count_) != max_sockets_) | |
892 return false; | |
893 for (GroupMap::const_iterator it = group_map_.begin(); | |
894 it != group_map_.end(); it++) { | |
895 if (it->second->IsStalled(max_sockets_per_group_)) | |
896 return true; | |
897 } | |
898 return false; | |
899 } | |
900 | |
861 void ClientSocketPoolBaseHelper::RemoveConnectJob(ConnectJob* job, | 901 void ClientSocketPoolBaseHelper::RemoveConnectJob(ConnectJob* job, |
862 Group* group) { | 902 Group* group) { |
863 CHECK_GT(connecting_socket_count_, 0); | 903 CHECK_GT(connecting_socket_count_, 0); |
864 connecting_socket_count_--; | 904 connecting_socket_count_--; |
865 | 905 |
866 DCHECK(group); | 906 DCHECK(group); |
867 DCHECK(ContainsKey(group->jobs(), job)); | 907 DCHECK(ContainsKey(group->jobs(), job)); |
868 group->RemoveJob(job); | 908 group->RemoveJob(job); |
869 | 909 |
870 // If we've got no more jobs for this group, then we no longer need a | 910 // If we've got no more jobs for this group, then we no longer need a |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
988 // Each connecting socket will eventually connect and be handed out. | 1028 // Each connecting socket will eventually connect and be handed out. |
989 int total = handed_out_socket_count_ + connecting_socket_count_ + | 1029 int total = handed_out_socket_count_ + connecting_socket_count_ + |
990 idle_socket_count(); | 1030 idle_socket_count(); |
991 // There can be more sockets than the limit since some requests can ignore | 1031 // There can be more sockets than the limit since some requests can ignore |
992 // the limit | 1032 // the limit |
993 if (total < max_sockets_) | 1033 if (total < max_sockets_) |
994 return false; | 1034 return false; |
995 return true; | 1035 return true; |
996 } | 1036 } |
997 | 1037 |
998 void ClientSocketPoolBaseHelper::CloseOneIdleSocket() { | 1038 bool ClientSocketPoolBaseHelper::CloseOneIdleSocket() { |
999 CloseOneIdleSocketExceptInGroup(NULL); | 1039 if (idle_socket_count() == 0) |
1040 return false; | |
1041 return CloseOneIdleSocketExceptInGroup(NULL); | |
1000 } | 1042 } |
1001 | 1043 |
1002 bool ClientSocketPoolBaseHelper::CloseOneIdleSocketExceptInGroup( | 1044 bool ClientSocketPoolBaseHelper::CloseOneIdleSocketExceptInGroup( |
1003 const Group* exception_group) { | 1045 const Group* exception_group) { |
1004 CHECK_GT(idle_socket_count(), 0); | 1046 CHECK_GT(idle_socket_count(), 0); |
1005 | 1047 |
1006 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end(); ++i) { | 1048 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end(); ++i) { |
1007 Group* group = i->second; | 1049 Group* group = i->second; |
1008 if (exception_group == group) | 1050 if (exception_group == group) |
1009 continue; | 1051 continue; |
1010 std::list<IdleSocket>* idle_sockets = group->mutable_idle_sockets(); | 1052 std::list<IdleSocket>* idle_sockets = group->mutable_idle_sockets(); |
1011 | 1053 |
1012 if (!idle_sockets->empty()) { | 1054 if (!idle_sockets->empty()) { |
1013 delete idle_sockets->front().socket; | 1055 delete idle_sockets->front().socket; |
1014 idle_sockets->pop_front(); | 1056 idle_sockets->pop_front(); |
1015 DecrementIdleCount(); | 1057 DecrementIdleCount(); |
1016 if (group->IsEmpty()) | 1058 if (group->IsEmpty()) |
1017 RemoveGroup(i); | 1059 RemoveGroup(i); |
1018 | 1060 |
1019 return true; | 1061 return true; |
1020 } | 1062 } |
1021 } | 1063 } |
1022 | 1064 |
1023 if (!exception_group) | |
1024 LOG(DFATAL) << "No idle socket found to close!."; | |
1025 | |
1026 return false; | 1065 return false; |
1027 } | 1066 } |
1028 | 1067 |
1068 bool ClientSocketPoolBaseHelper::CloseOneIdleConnectionInLayeredPool() { | |
1069 // This pool doesn't have any idle sockets. It's possible that a pool at a | |
1070 // higher layer is holding one of this sockets active, but it's actually idle. | |
1071 // Query the higher layers. | |
1072 for (std::set<LayeredPool*>::const_iterator it = | |
1073 higher_layer_pools_.begin(); it != higher_layer_pools_.end(); ++it) { | |
mmenke
2011/10/27 18:52:37
nit: Looks to me like higher_layer_pools_.begin()
willchan no longer on Chromium
2011/11/08 22:26:13
Done.
| |
1074 if ((*it)->CloseOneIdleConnection()) | |
1075 return true; | |
1076 } | |
1077 return false; | |
1078 } | |
1079 | |
1029 void ClientSocketPoolBaseHelper::InvokeUserCallbackLater( | 1080 void ClientSocketPoolBaseHelper::InvokeUserCallbackLater( |
1030 ClientSocketHandle* handle, OldCompletionCallback* callback, int rv) { | 1081 ClientSocketHandle* handle, OldCompletionCallback* callback, int rv) { |
1031 CHECK(!ContainsKey(pending_callback_map_, handle)); | 1082 CHECK(!ContainsKey(pending_callback_map_, handle)); |
1032 pending_callback_map_[handle] = CallbackResultPair(callback, rv); | 1083 pending_callback_map_[handle] = CallbackResultPair(callback, rv); |
1033 MessageLoop::current()->PostTask( | 1084 MessageLoop::current()->PostTask( |
1034 FROM_HERE, | 1085 FROM_HERE, |
1035 method_factory_.NewRunnableMethod( | 1086 method_factory_.NewRunnableMethod( |
1036 &ClientSocketPoolBaseHelper::InvokeUserCallback, | 1087 &ClientSocketPoolBaseHelper::InvokeUserCallback, |
1037 handle)); | 1088 handle)); |
1038 } | 1089 } |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1123 // Delete active jobs. | 1174 // Delete active jobs. |
1124 STLDeleteElements(&jobs_); | 1175 STLDeleteElements(&jobs_); |
1125 | 1176 |
1126 // Cancel pending backup job. | 1177 // Cancel pending backup job. |
1127 method_factory_.RevokeAll(); | 1178 method_factory_.RevokeAll(); |
1128 } | 1179 } |
1129 | 1180 |
1130 } // namespace internal | 1181 } // namespace internal |
1131 | 1182 |
1132 } // namespace net | 1183 } // namespace net |
OLD | NEW |