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 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
200 } | 200 } |
201 | 201 |
202 ClientSocketPoolBaseHelper::~ClientSocketPoolBaseHelper() { | 202 ClientSocketPoolBaseHelper::~ClientSocketPoolBaseHelper() { |
203 // Clean up any idle sockets and pending connect jobs. Assert that we have no | 203 // Clean up any idle sockets and pending connect jobs. Assert that we have no |
204 // remaining active sockets or pending requests. They should have all been | 204 // remaining active sockets or pending requests. They should have all been |
205 // cleaned up prior to |this| being destroyed. | 205 // cleaned up prior to |this| being destroyed. |
206 Flush(); | 206 Flush(); |
207 DCHECK(group_map_.empty()); | 207 DCHECK(group_map_.empty()); |
208 DCHECK(pending_callback_map_.empty()); | 208 DCHECK(pending_callback_map_.empty()); |
209 DCHECK_EQ(0, connecting_socket_count_); | 209 DCHECK_EQ(0, connecting_socket_count_); |
| 210 DCHECK(higher_layer_pools_.empty()); |
210 | 211 |
211 NetworkChangeNotifier::RemoveIPAddressObserver(this); | 212 NetworkChangeNotifier::RemoveIPAddressObserver(this); |
212 } | 213 } |
213 | 214 |
214 // InsertRequestIntoQueue inserts the request into the queue based on | 215 // InsertRequestIntoQueue inserts the request into the queue based on |
215 // priority. Highest priorities are closest to the front. Older requests are | 216 // priority. Highest priorities are closest to the front. Older requests are |
216 // prioritized over requests of equal priority. | 217 // prioritized over requests of equal priority. |
217 // | 218 // |
218 // static | 219 // static |
219 void ClientSocketPoolBaseHelper::InsertRequestIntoQueue( | 220 void ClientSocketPoolBaseHelper::InsertRequestIntoQueue( |
220 const Request* r, RequestQueue* pending_requests) { | 221 const Request* r, RequestQueue* pending_requests) { |
221 RequestQueue::iterator it = pending_requests->begin(); | 222 RequestQueue::iterator it = pending_requests->begin(); |
222 while (it != pending_requests->end() && r->priority() >= (*it)->priority()) | 223 while (it != pending_requests->end() && r->priority() >= (*it)->priority()) |
223 ++it; | 224 ++it; |
224 pending_requests->insert(it, r); | 225 pending_requests->insert(it, r); |
225 } | 226 } |
226 | 227 |
227 // static | 228 // static |
228 const ClientSocketPoolBaseHelper::Request* | 229 const ClientSocketPoolBaseHelper::Request* |
229 ClientSocketPoolBaseHelper::RemoveRequestFromQueue( | 230 ClientSocketPoolBaseHelper::RemoveRequestFromQueue( |
230 const RequestQueue::iterator& it, Group* group) { | 231 const RequestQueue::iterator& it, Group* group) { |
231 const Request* req = *it; | 232 const Request* req = *it; |
232 group->mutable_pending_requests()->erase(it); | 233 group->mutable_pending_requests()->erase(it); |
233 // If there are no more requests, we kill the backup timer. | 234 // If there are no more requests, we kill the backup timer. |
234 if (group->pending_requests().empty()) | 235 if (group->pending_requests().empty()) |
235 group->CleanupBackupJob(); | 236 group->CleanupBackupJob(); |
236 return req; | 237 return req; |
237 } | 238 } |
238 | 239 |
| 240 void ClientSocketPoolBaseHelper::AddLayeredPool(LayeredPool* pool) { |
| 241 CHECK(pool); |
| 242 CHECK(!ContainsKey(higher_layer_pools_, pool)); |
| 243 higher_layer_pools_.insert(pool); |
| 244 } |
| 245 |
| 246 void ClientSocketPoolBaseHelper::RemoveLayeredPool(LayeredPool* pool) { |
| 247 CHECK(pool); |
| 248 CHECK(ContainsKey(higher_layer_pools_, pool)); |
| 249 higher_layer_pools_.erase(pool); |
| 250 } |
| 251 |
239 int ClientSocketPoolBaseHelper::RequestSocket( | 252 int ClientSocketPoolBaseHelper::RequestSocket( |
240 const std::string& group_name, | 253 const std::string& group_name, |
241 const Request* request) { | 254 const Request* request) { |
242 CHECK(request->callback()); | 255 CHECK(request->callback()); |
243 CHECK(request->handle()); | 256 CHECK(request->handle()); |
244 | 257 |
245 // Cleanup any timed-out idle sockets if no timer is used. | 258 // Cleanup any timed-out idle sockets if no timer is used. |
246 if (!use_cleanup_timer_) | 259 if (!use_cleanup_timer_) |
247 CleanupIdleSockets(false); | 260 CleanupIdleSockets(false); |
248 | 261 |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
327 if (AssignIdleSocketToGroup(request, group)) | 340 if (AssignIdleSocketToGroup(request, group)) |
328 return OK; | 341 return OK; |
329 } | 342 } |
330 | 343 |
331 if (!preconnecting && group->TryToUsePreconnectConnectJob()) | 344 if (!preconnecting && group->TryToUsePreconnectConnectJob()) |
332 return ERR_IO_PENDING; | 345 return ERR_IO_PENDING; |
333 | 346 |
334 // Can we make another active socket now? | 347 // Can we make another active socket now? |
335 if (!group->HasAvailableSocketSlot(max_sockets_per_group_) && | 348 if (!group->HasAvailableSocketSlot(max_sockets_per_group_) && |
336 !request->ignore_limits()) { | 349 !request->ignore_limits()) { |
| 350 // TODO(willchan): Consider whether or not we need to close a socket in a |
| 351 // higher layered group. I don't think this makes sense since we would just |
| 352 // reuse that socket then if we needed one and wouldn't make it down to this |
| 353 // layer. |
337 request->net_log().AddEvent( | 354 request->net_log().AddEvent( |
338 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP, NULL); | 355 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP, NULL); |
339 return ERR_IO_PENDING; | 356 return ERR_IO_PENDING; |
340 } | 357 } |
341 | 358 |
342 if (ReachedMaxSocketsLimit() && !request->ignore_limits()) { | 359 if (ReachedMaxSocketsLimit() && !request->ignore_limits()) { |
343 if (idle_socket_count() > 0) { | 360 if (idle_socket_count() > 0) { |
| 361 // There's an idle socket in this pool. Either that's because there's |
| 362 // still one in this group, but we got here due to preconnecting bypassing |
| 363 // idle sockets, or because there's an idle socket in another group. |
344 bool closed = CloseOneIdleSocketExceptInGroup(group); | 364 bool closed = CloseOneIdleSocketExceptInGroup(group); |
345 if (preconnecting && !closed) | 365 if (preconnecting && !closed) |
346 return ERR_PRECONNECT_MAX_SOCKET_LIMIT; | 366 return ERR_PRECONNECT_MAX_SOCKET_LIMIT; |
347 } else { | 367 } else { |
348 // We could check if we really have a stalled group here, but it requires | 368 do { |
349 // a scan of all groups, so just flip a flag here, and do the check later. | 369 if (!CloseOneIdleConnectionInLayeredPool()) { |
350 request->net_log().AddEvent( | 370 // We could check if we really have a stalled group here, but it |
351 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS, NULL); | 371 // requires a scan of all groups, so just flip a flag here, and do |
352 return ERR_IO_PENDING; | 372 // the check later. |
| 373 request->net_log().AddEvent( |
| 374 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS, NULL); |
| 375 return ERR_IO_PENDING; |
| 376 } |
| 377 } while (ReachedMaxSocketsLimit()); |
353 } | 378 } |
354 } | 379 } |
355 | 380 |
356 // We couldn't find a socket to reuse, so allocate and connect a new one. | 381 // We couldn't find a socket to reuse, and there's space to allocate one, |
| 382 // so allocate and connect a new one. |
357 scoped_ptr<ConnectJob> connect_job( | 383 scoped_ptr<ConnectJob> connect_job( |
358 connect_job_factory_->NewConnectJob(group_name, *request, this)); | 384 connect_job_factory_->NewConnectJob(group_name, *request, this)); |
359 | 385 |
360 connect_job->Initialize(preconnecting); | 386 connect_job->Initialize(preconnecting); |
361 int rv = connect_job->Connect(); | 387 int rv = connect_job->Connect(); |
362 if (rv == OK) { | 388 if (rv == OK) { |
363 LogBoundConnectJobToRequest(connect_job->net_log().source(), request); | 389 LogBoundConnectJobToRequest(connect_job->net_log().source(), request); |
364 if (!preconnecting) { | 390 if (!preconnecting) { |
365 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */, | 391 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */, |
366 handle, base::TimeDelta(), group, request->net_log()); | 392 handle, base::TimeDelta(), group, request->net_log()); |
(...skipping 416 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
783 // its limit, may be left with other stalled groups that could be | 809 // its limit, may be left with other stalled groups that could be |
784 // woken. This isn't optimal, but there is no starvation, so to avoid | 810 // woken. This isn't optimal, but there is no starvation, so to avoid |
785 // the looping we leave it at this. | 811 // the looping we leave it at this. |
786 OnAvailableSocketSlot(top_group_name, top_group); | 812 OnAvailableSocketSlot(top_group_name, top_group); |
787 } | 813 } |
788 | 814 |
789 // Search for the highest priority pending request, amongst the groups that | 815 // Search for the highest priority pending request, amongst the groups that |
790 // are not at the |max_sockets_per_group_| limit. Note: for requests with | 816 // are not at the |max_sockets_per_group_| limit. Note: for requests with |
791 // the same priority, the winner is based on group hash ordering (and not | 817 // the same priority, the winner is based on group hash ordering (and not |
792 // insertion order). | 818 // insertion order). |
793 bool ClientSocketPoolBaseHelper::FindTopStalledGroup(Group** group, | 819 bool ClientSocketPoolBaseHelper::FindTopStalledGroup( |
794 std::string* group_name) { | 820 Group** group, |
| 821 std::string* group_name) const { |
| 822 CHECK((group && group_name) || (!group && !group_name)); |
795 Group* top_group = NULL; | 823 Group* top_group = NULL; |
796 const std::string* top_group_name = NULL; | 824 const std::string* top_group_name = NULL; |
797 bool has_stalled_group = false; | 825 bool has_stalled_group = false; |
798 for (GroupMap::iterator i = group_map_.begin(); | 826 for (GroupMap::const_iterator i = group_map_.begin(); |
799 i != group_map_.end(); ++i) { | 827 i != group_map_.end(); ++i) { |
800 Group* curr_group = i->second; | 828 Group* curr_group = i->second; |
801 const RequestQueue& queue = curr_group->pending_requests(); | 829 const RequestQueue& queue = curr_group->pending_requests(); |
802 if (queue.empty()) | 830 if (queue.empty()) |
803 continue; | 831 continue; |
804 if (curr_group->IsStalled(max_sockets_per_group_)) { | 832 if (curr_group->IsStalled(max_sockets_per_group_)) { |
| 833 if (!group) |
| 834 return true; |
805 has_stalled_group = true; | 835 has_stalled_group = true; |
806 bool has_higher_priority = !top_group || | 836 bool has_higher_priority = !top_group || |
807 curr_group->TopPendingPriority() < top_group->TopPendingPriority(); | 837 curr_group->TopPendingPriority() < top_group->TopPendingPriority(); |
808 if (has_higher_priority) { | 838 if (has_higher_priority) { |
809 top_group = curr_group; | 839 top_group = curr_group; |
810 top_group_name = &i->first; | 840 top_group_name = &i->first; |
811 } | 841 } |
812 } | 842 } |
813 } | 843 } |
814 | 844 |
815 if (top_group) { | 845 if (top_group) { |
| 846 CHECK(group); |
816 *group = top_group; | 847 *group = top_group; |
817 *group_name = *top_group_name; | 848 *group_name = *top_group_name; |
| 849 } else { |
| 850 CHECK(!has_stalled_group); |
818 } | 851 } |
819 return has_stalled_group; | 852 return has_stalled_group; |
820 } | 853 } |
821 | 854 |
822 void ClientSocketPoolBaseHelper::OnConnectJobComplete( | 855 void ClientSocketPoolBaseHelper::OnConnectJobComplete( |
823 int result, ConnectJob* job) { | 856 int result, ConnectJob* job) { |
824 DCHECK_NE(ERR_IO_PENDING, result); | 857 DCHECK_NE(ERR_IO_PENDING, result); |
825 const std::string group_name = job->group_name(); | 858 const std::string group_name = job->group_name(); |
826 GroupMap::iterator group_it = group_map_.find(group_name); | 859 GroupMap::iterator group_it = group_map_.find(group_name); |
827 CHECK(group_it != group_map_.end()); | 860 CHECK(group_it != group_map_.end()); |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
880 Flush(); | 913 Flush(); |
881 } | 914 } |
882 | 915 |
883 void ClientSocketPoolBaseHelper::Flush() { | 916 void ClientSocketPoolBaseHelper::Flush() { |
884 pool_generation_number_++; | 917 pool_generation_number_++; |
885 CancelAllConnectJobs(); | 918 CancelAllConnectJobs(); |
886 CloseIdleSockets(); | 919 CloseIdleSockets(); |
887 AbortAllRequests(); | 920 AbortAllRequests(); |
888 } | 921 } |
889 | 922 |
| 923 bool ClientSocketPoolBaseHelper::IsStalled() const { |
| 924 if ((handed_out_socket_count_ + connecting_socket_count_) < max_sockets_) |
| 925 return false; |
| 926 for (GroupMap::const_iterator it = group_map_.begin(); |
| 927 it != group_map_.end(); it++) { |
| 928 if (it->second->IsStalled(max_sockets_per_group_)) |
| 929 return true; |
| 930 } |
| 931 return false; |
| 932 } |
| 933 |
890 void ClientSocketPoolBaseHelper::RemoveConnectJob(ConnectJob* job, | 934 void ClientSocketPoolBaseHelper::RemoveConnectJob(ConnectJob* job, |
891 Group* group) { | 935 Group* group) { |
892 CHECK_GT(connecting_socket_count_, 0); | 936 CHECK_GT(connecting_socket_count_, 0); |
893 connecting_socket_count_--; | 937 connecting_socket_count_--; |
894 | 938 |
895 DCHECK(group); | 939 DCHECK(group); |
896 DCHECK(ContainsKey(group->jobs(), job)); | 940 DCHECK(ContainsKey(group->jobs(), job)); |
897 group->RemoveJob(job); | 941 group->RemoveJob(job); |
898 | 942 |
899 // If we've got no more jobs for this group, then we no longer need a | 943 // If we've got no more jobs for this group, then we no longer need a |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1016 // Each connecting socket will eventually connect and be handed out. | 1060 // Each connecting socket will eventually connect and be handed out. |
1017 int total = handed_out_socket_count_ + connecting_socket_count_ + | 1061 int total = handed_out_socket_count_ + connecting_socket_count_ + |
1018 idle_socket_count(); | 1062 idle_socket_count(); |
1019 // There can be more sockets than the limit since some requests can ignore | 1063 // There can be more sockets than the limit since some requests can ignore |
1020 // the limit | 1064 // the limit |
1021 if (total < max_sockets_) | 1065 if (total < max_sockets_) |
1022 return false; | 1066 return false; |
1023 return true; | 1067 return true; |
1024 } | 1068 } |
1025 | 1069 |
1026 void ClientSocketPoolBaseHelper::CloseOneIdleSocket() { | 1070 bool ClientSocketPoolBaseHelper::CloseOneIdleSocket() { |
1027 CloseOneIdleSocketExceptInGroup(NULL); | 1071 if (idle_socket_count() == 0) |
| 1072 return false; |
| 1073 return CloseOneIdleSocketExceptInGroup(NULL); |
1028 } | 1074 } |
1029 | 1075 |
1030 bool ClientSocketPoolBaseHelper::CloseOneIdleSocketExceptInGroup( | 1076 bool ClientSocketPoolBaseHelper::CloseOneIdleSocketExceptInGroup( |
1031 const Group* exception_group) { | 1077 const Group* exception_group) { |
1032 CHECK_GT(idle_socket_count(), 0); | 1078 CHECK_GT(idle_socket_count(), 0); |
1033 | 1079 |
1034 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end(); ++i) { | 1080 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end(); ++i) { |
1035 Group* group = i->second; | 1081 Group* group = i->second; |
1036 if (exception_group == group) | 1082 if (exception_group == group) |
1037 continue; | 1083 continue; |
1038 std::list<IdleSocket>* idle_sockets = group->mutable_idle_sockets(); | 1084 std::list<IdleSocket>* idle_sockets = group->mutable_idle_sockets(); |
1039 | 1085 |
1040 if (!idle_sockets->empty()) { | 1086 if (!idle_sockets->empty()) { |
1041 delete idle_sockets->front().socket; | 1087 delete idle_sockets->front().socket; |
1042 idle_sockets->pop_front(); | 1088 idle_sockets->pop_front(); |
1043 DecrementIdleCount(); | 1089 DecrementIdleCount(); |
1044 if (group->IsEmpty()) | 1090 if (group->IsEmpty()) |
1045 RemoveGroup(i); | 1091 RemoveGroup(i); |
1046 | 1092 |
1047 return true; | 1093 return true; |
1048 } | 1094 } |
1049 } | 1095 } |
1050 | 1096 |
1051 if (!exception_group) | |
1052 LOG(DFATAL) << "No idle socket found to close!."; | |
1053 | |
1054 return false; | 1097 return false; |
1055 } | 1098 } |
1056 | 1099 |
| 1100 bool ClientSocketPoolBaseHelper::CloseOneIdleConnectionInLayeredPool() { |
| 1101 // This pool doesn't have any idle sockets. It's possible that a pool at a |
| 1102 // higher layer is holding one of this sockets active, but it's actually idle. |
| 1103 // Query the higher layers. |
| 1104 for (std::set<LayeredPool*>::const_iterator it = higher_layer_pools_.begin(); |
| 1105 it != higher_layer_pools_.end(); ++it) { |
| 1106 if ((*it)->CloseOneIdleConnection()) |
| 1107 return true; |
| 1108 } |
| 1109 return false; |
| 1110 } |
| 1111 |
1057 void ClientSocketPoolBaseHelper::InvokeUserCallbackLater( | 1112 void ClientSocketPoolBaseHelper::InvokeUserCallbackLater( |
1058 ClientSocketHandle* handle, OldCompletionCallback* callback, int rv) { | 1113 ClientSocketHandle* handle, OldCompletionCallback* callback, int rv) { |
1059 CHECK(!ContainsKey(pending_callback_map_, handle)); | 1114 CHECK(!ContainsKey(pending_callback_map_, handle)); |
1060 pending_callback_map_[handle] = CallbackResultPair(callback, rv); | 1115 pending_callback_map_[handle] = CallbackResultPair(callback, rv); |
1061 MessageLoop::current()->PostTask( | 1116 MessageLoop::current()->PostTask( |
1062 FROM_HERE, | 1117 FROM_HERE, |
1063 method_factory_.NewRunnableMethod( | 1118 method_factory_.NewRunnableMethod( |
1064 &ClientSocketPoolBaseHelper::InvokeUserCallback, | 1119 &ClientSocketPoolBaseHelper::InvokeUserCallback, |
1065 handle)); | 1120 handle)); |
1066 } | 1121 } |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1151 // Delete active jobs. | 1206 // Delete active jobs. |
1152 STLDeleteElements(&jobs_); | 1207 STLDeleteElements(&jobs_); |
1153 | 1208 |
1154 // Cancel pending backup job. | 1209 // Cancel pending backup job. |
1155 method_factory_.RevokeAll(); | 1210 method_factory_.RevokeAll(); |
1156 } | 1211 } |
1157 | 1212 |
1158 } // namespace internal | 1213 } // namespace internal |
1159 | 1214 |
1160 } // namespace net | 1215 } // namespace net |
OLD | NEW |