OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 CHECK(higher_layer_pools_.empty()); | |
211 | 210 |
212 NetworkChangeNotifier::RemoveIPAddressObserver(this); | 211 NetworkChangeNotifier::RemoveIPAddressObserver(this); |
213 } | 212 } |
214 | 213 |
215 ClientSocketPoolBaseHelper::CallbackResultPair::~CallbackResultPair() {} | 214 ClientSocketPoolBaseHelper::CallbackResultPair::~CallbackResultPair() {} |
216 | 215 |
217 // InsertRequestIntoQueue inserts the request into the queue based on | 216 // InsertRequestIntoQueue inserts the request into the queue based on |
218 // priority. Highest priorities are closest to the front. Older requests are | 217 // priority. Highest priorities are closest to the front. Older requests are |
219 // prioritized over requests of equal priority. | 218 // prioritized over requests of equal priority. |
220 // | 219 // |
(...skipping 11 matching lines...) Expand all Loading... |
232 ClientSocketPoolBaseHelper::RemoveRequestFromQueue( | 231 ClientSocketPoolBaseHelper::RemoveRequestFromQueue( |
233 const RequestQueue::iterator& it, Group* group) { | 232 const RequestQueue::iterator& it, Group* group) { |
234 const Request* req = *it; | 233 const Request* req = *it; |
235 group->mutable_pending_requests()->erase(it); | 234 group->mutable_pending_requests()->erase(it); |
236 // If there are no more requests, we kill the backup timer. | 235 // If there are no more requests, we kill the backup timer. |
237 if (group->pending_requests().empty()) | 236 if (group->pending_requests().empty()) |
238 group->CleanupBackupJob(); | 237 group->CleanupBackupJob(); |
239 return req; | 238 return req; |
240 } | 239 } |
241 | 240 |
242 void ClientSocketPoolBaseHelper::AddLayeredPool(LayeredPool* pool) { | |
243 CHECK(pool); | |
244 CHECK(!ContainsKey(higher_layer_pools_, pool)); | |
245 higher_layer_pools_.insert(pool); | |
246 } | |
247 | |
248 void ClientSocketPoolBaseHelper::RemoveLayeredPool(LayeredPool* pool) { | |
249 CHECK(pool); | |
250 CHECK(ContainsKey(higher_layer_pools_, pool)); | |
251 higher_layer_pools_.erase(pool); | |
252 } | |
253 | |
254 int ClientSocketPoolBaseHelper::RequestSocket( | 241 int ClientSocketPoolBaseHelper::RequestSocket( |
255 const std::string& group_name, | 242 const std::string& group_name, |
256 const Request* request) { | 243 const Request* request) { |
257 CHECK(!request->callback().is_null()); | 244 CHECK(!request->callback().is_null()); |
258 CHECK(request->handle()); | 245 CHECK(request->handle()); |
259 | 246 |
260 // Cleanup any timed-out idle sockets if no timer is used. | 247 // Cleanup any timed-out idle sockets if no timer is used. |
261 if (!use_cleanup_timer_) | 248 if (!use_cleanup_timer_) |
262 CleanupIdleSockets(false); | 249 CleanupIdleSockets(false); |
263 | 250 |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
342 if (AssignIdleSocketToGroup(request, group)) | 329 if (AssignIdleSocketToGroup(request, group)) |
343 return OK; | 330 return OK; |
344 } | 331 } |
345 | 332 |
346 if (!preconnecting && group->TryToUsePreconnectConnectJob()) | 333 if (!preconnecting && group->TryToUsePreconnectConnectJob()) |
347 return ERR_IO_PENDING; | 334 return ERR_IO_PENDING; |
348 | 335 |
349 // Can we make another active socket now? | 336 // Can we make another active socket now? |
350 if (!group->HasAvailableSocketSlot(max_sockets_per_group_) && | 337 if (!group->HasAvailableSocketSlot(max_sockets_per_group_) && |
351 !request->ignore_limits()) { | 338 !request->ignore_limits()) { |
352 // TODO(willchan): Consider whether or not we need to close a socket in a | |
353 // higher layered group. I don't think this makes sense since we would just | |
354 // reuse that socket then if we needed one and wouldn't make it down to this | |
355 // layer. | |
356 request->net_log().AddEvent( | 339 request->net_log().AddEvent( |
357 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP, NULL); | 340 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP, NULL); |
358 return ERR_IO_PENDING; | 341 return ERR_IO_PENDING; |
359 } | 342 } |
360 | 343 |
361 if (ReachedMaxSocketsLimit() && !request->ignore_limits()) { | 344 if (ReachedMaxSocketsLimit() && !request->ignore_limits()) { |
362 // NOTE(mmenke): Wonder if we really need different code for each case | |
363 // here. Only reason for them now seems to be preconnects. | |
364 if (idle_socket_count() > 0) { | 345 if (idle_socket_count() > 0) { |
365 // There's an idle socket in this pool. Either that's because there's | |
366 // still one in this group, but we got here due to preconnecting bypassing | |
367 // idle sockets, or because there's an idle socket in another group. | |
368 bool closed = CloseOneIdleSocketExceptInGroup(group); | 346 bool closed = CloseOneIdleSocketExceptInGroup(group); |
369 if (preconnecting && !closed) | 347 if (preconnecting && !closed) |
370 return ERR_PRECONNECT_MAX_SOCKET_LIMIT; | 348 return ERR_PRECONNECT_MAX_SOCKET_LIMIT; |
371 } else { | 349 } else { |
372 do { | 350 // We could check if we really have a stalled group here, but it requires |
373 if (!CloseOneIdleConnectionInLayeredPool()) { | 351 // a scan of all groups, so just flip a flag here, and do the check later. |
374 // We could check if we really have a stalled group here, but it | 352 request->net_log().AddEvent( |
375 // requires a scan of all groups, so just flip a flag here, and do | 353 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS, NULL); |
376 // the check later. | 354 return ERR_IO_PENDING; |
377 request->net_log().AddEvent( | |
378 NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS, NULL); | |
379 return ERR_IO_PENDING; | |
380 } | |
381 } while (ReachedMaxSocketsLimit()); | |
382 | |
383 // It is possible that CloseOneIdleConnectionInLayeredPool() has deleted | |
384 // our Group (see http://crbug.com/109876), so look it up again | |
385 // to be safe. | |
386 group = GetOrCreateGroup(group_name); | |
387 } | 355 } |
388 } | 356 } |
389 | 357 |
390 // We couldn't find a socket to reuse, and there's space to allocate one, | 358 // We couldn't find a socket to reuse, so allocate and connect a new one. |
391 // so allocate and connect a new one. | |
392 scoped_ptr<ConnectJob> connect_job( | 359 scoped_ptr<ConnectJob> connect_job( |
393 connect_job_factory_->NewConnectJob(group_name, *request, this)); | 360 connect_job_factory_->NewConnectJob(group_name, *request, this)); |
394 | 361 |
395 connect_job->Initialize(preconnecting); | 362 connect_job->Initialize(preconnecting); |
396 int rv = connect_job->Connect(); | 363 int rv = connect_job->Connect(); |
397 if (rv == OK) { | 364 if (rv == OK) { |
398 LogBoundConnectJobToRequest(connect_job->net_log().source(), request); | 365 LogBoundConnectJobToRequest(connect_job->net_log().source(), request); |
399 if (!preconnecting) { | 366 if (!preconnecting) { |
400 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */, | 367 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */, |
401 handle, base::TimeDelta(), group, request->net_log()); | 368 handle, base::TimeDelta(), group, request->net_log()); |
(...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
643 | 610 |
644 ListValue* connect_jobs_list = new ListValue(); | 611 ListValue* connect_jobs_list = new ListValue(); |
645 std::set<ConnectJob*>::const_iterator job = group->jobs().begin(); | 612 std::set<ConnectJob*>::const_iterator job = group->jobs().begin(); |
646 for (job = group->jobs().begin(); job != group->jobs().end(); job++) { | 613 for (job = group->jobs().begin(); job != group->jobs().end(); job++) { |
647 int source_id = (*job)->net_log().source().id; | 614 int source_id = (*job)->net_log().source().id; |
648 connect_jobs_list->Append(Value::CreateIntegerValue(source_id)); | 615 connect_jobs_list->Append(Value::CreateIntegerValue(source_id)); |
649 } | 616 } |
650 group_dict->Set("connect_jobs", connect_jobs_list); | 617 group_dict->Set("connect_jobs", connect_jobs_list); |
651 | 618 |
652 group_dict->SetBoolean("is_stalled", | 619 group_dict->SetBoolean("is_stalled", |
653 group->IsStalledOnPoolMaxSockets( | 620 group->IsStalled(max_sockets_per_group_)); |
654 max_sockets_per_group_)); | |
655 group_dict->SetBoolean("has_backup_job", group->HasBackupJob()); | 621 group_dict->SetBoolean("has_backup_job", group->HasBackupJob()); |
656 | 622 |
657 all_groups_dict->SetWithoutPathExpansion(it->first, group_dict); | 623 all_groups_dict->SetWithoutPathExpansion(it->first, group_dict); |
658 } | 624 } |
659 dict->Set("groups", all_groups_dict); | 625 dict->Set("groups", all_groups_dict); |
660 return dict; | 626 return dict; |
661 } | 627 } |
662 | 628 |
663 bool ClientSocketPoolBaseHelper::IdleSocket::ShouldCleanup( | 629 bool ClientSocketPoolBaseHelper::IdleSocket::ShouldCleanup( |
664 base::TimeTicks now, | 630 base::TimeTicks now, |
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
819 // its limit, may be left with other stalled groups that could be | 785 // its limit, may be left with other stalled groups that could be |
820 // woken. This isn't optimal, but there is no starvation, so to avoid | 786 // woken. This isn't optimal, but there is no starvation, so to avoid |
821 // the looping we leave it at this. | 787 // the looping we leave it at this. |
822 OnAvailableSocketSlot(top_group_name, top_group); | 788 OnAvailableSocketSlot(top_group_name, top_group); |
823 } | 789 } |
824 | 790 |
825 // Search for the highest priority pending request, amongst the groups that | 791 // Search for the highest priority pending request, amongst the groups that |
826 // are not at the |max_sockets_per_group_| limit. Note: for requests with | 792 // are not at the |max_sockets_per_group_| limit. Note: for requests with |
827 // the same priority, the winner is based on group hash ordering (and not | 793 // the same priority, the winner is based on group hash ordering (and not |
828 // insertion order). | 794 // insertion order). |
829 bool ClientSocketPoolBaseHelper::FindTopStalledGroup( | 795 bool ClientSocketPoolBaseHelper::FindTopStalledGroup(Group** group, |
830 Group** group, | 796 std::string* group_name) { |
831 std::string* group_name) const { | |
832 CHECK((group && group_name) || (!group && !group_name)); | |
833 Group* top_group = NULL; | 797 Group* top_group = NULL; |
834 const std::string* top_group_name = NULL; | 798 const std::string* top_group_name = NULL; |
835 bool has_stalled_group = false; | 799 bool has_stalled_group = false; |
836 for (GroupMap::const_iterator i = group_map_.begin(); | 800 for (GroupMap::iterator i = group_map_.begin(); |
837 i != group_map_.end(); ++i) { | 801 i != group_map_.end(); ++i) { |
838 Group* curr_group = i->second; | 802 Group* curr_group = i->second; |
839 const RequestQueue& queue = curr_group->pending_requests(); | 803 const RequestQueue& queue = curr_group->pending_requests(); |
840 if (queue.empty()) | 804 if (queue.empty()) |
841 continue; | 805 continue; |
842 if (curr_group->IsStalledOnPoolMaxSockets(max_sockets_per_group_)) { | 806 if (curr_group->IsStalled(max_sockets_per_group_)) { |
843 if (!group) | |
844 return true; | |
845 has_stalled_group = true; | 807 has_stalled_group = true; |
846 bool has_higher_priority = !top_group || | 808 bool has_higher_priority = !top_group || |
847 curr_group->TopPendingPriority() < top_group->TopPendingPriority(); | 809 curr_group->TopPendingPriority() < top_group->TopPendingPriority(); |
848 if (has_higher_priority) { | 810 if (has_higher_priority) { |
849 top_group = curr_group; | 811 top_group = curr_group; |
850 top_group_name = &i->first; | 812 top_group_name = &i->first; |
851 } | 813 } |
852 } | 814 } |
853 } | 815 } |
854 | 816 |
855 if (top_group) { | 817 if (top_group) { |
856 CHECK(group); | |
857 *group = top_group; | 818 *group = top_group; |
858 *group_name = *top_group_name; | 819 *group_name = *top_group_name; |
859 } else { | |
860 CHECK(!has_stalled_group); | |
861 } | 820 } |
862 return has_stalled_group; | 821 return has_stalled_group; |
863 } | 822 } |
864 | 823 |
865 void ClientSocketPoolBaseHelper::OnConnectJobComplete( | 824 void ClientSocketPoolBaseHelper::OnConnectJobComplete( |
866 int result, ConnectJob* job) { | 825 int result, ConnectJob* job) { |
867 DCHECK_NE(ERR_IO_PENDING, result); | 826 DCHECK_NE(ERR_IO_PENDING, result); |
868 const std::string group_name = job->group_name(); | 827 const std::string group_name = job->group_name(); |
869 GroupMap::iterator group_it = group_map_.find(group_name); | 828 GroupMap::iterator group_it = group_map_.find(group_name); |
870 CHECK(group_it != group_map_.end()); | 829 CHECK(group_it != group_map_.end()); |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
923 Flush(); | 882 Flush(); |
924 } | 883 } |
925 | 884 |
926 void ClientSocketPoolBaseHelper::Flush() { | 885 void ClientSocketPoolBaseHelper::Flush() { |
927 pool_generation_number_++; | 886 pool_generation_number_++; |
928 CancelAllConnectJobs(); | 887 CancelAllConnectJobs(); |
929 CloseIdleSockets(); | 888 CloseIdleSockets(); |
930 AbortAllRequests(); | 889 AbortAllRequests(); |
931 } | 890 } |
932 | 891 |
933 bool ClientSocketPoolBaseHelper::IsStalled() const { | |
934 // If we are not using |max_sockets_|, then clearly we are not stalled | |
935 if ((handed_out_socket_count_ + connecting_socket_count_) < max_sockets_) | |
936 return false; | |
937 // So in order to be stalled we need to be using |max_sockets_| AND | |
938 // we need to have a request that is actually stalled on the global | |
939 // socket limit. To find such a request, we look for a group that | |
940 // a has more requests that jobs AND where the number of jobs is less | |
941 // than |max_sockets_per_group_|. (If the number of jobs is equal to | |
942 // |max_sockets_per_group_|, then the request is stalled on the group, | |
943 // which does not count.) | |
944 for (GroupMap::const_iterator it = group_map_.begin(); | |
945 it != group_map_.end(); it++) { | |
946 if (it->second->IsStalledOnPoolMaxSockets(max_sockets_per_group_)) | |
947 return true; | |
948 } | |
949 return false; | |
950 } | |
951 | |
952 void ClientSocketPoolBaseHelper::RemoveConnectJob(ConnectJob* job, | 892 void ClientSocketPoolBaseHelper::RemoveConnectJob(ConnectJob* job, |
953 Group* group) { | 893 Group* group) { |
954 CHECK_GT(connecting_socket_count_, 0); | 894 CHECK_GT(connecting_socket_count_, 0); |
955 connecting_socket_count_--; | 895 connecting_socket_count_--; |
956 | 896 |
957 DCHECK(group); | 897 DCHECK(group); |
958 DCHECK(ContainsKey(group->jobs(), job)); | 898 DCHECK(ContainsKey(group->jobs(), job)); |
959 group->RemoveJob(job); | 899 group->RemoveJob(job); |
960 | 900 |
961 // If we've got no more jobs for this group, then we no longer need a | 901 // 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... |
1078 // Each connecting socket will eventually connect and be handed out. | 1018 // Each connecting socket will eventually connect and be handed out. |
1079 int total = handed_out_socket_count_ + connecting_socket_count_ + | 1019 int total = handed_out_socket_count_ + connecting_socket_count_ + |
1080 idle_socket_count(); | 1020 idle_socket_count(); |
1081 // There can be more sockets than the limit since some requests can ignore | 1021 // There can be more sockets than the limit since some requests can ignore |
1082 // the limit | 1022 // the limit |
1083 if (total < max_sockets_) | 1023 if (total < max_sockets_) |
1084 return false; | 1024 return false; |
1085 return true; | 1025 return true; |
1086 } | 1026 } |
1087 | 1027 |
1088 bool ClientSocketPoolBaseHelper::CloseOneIdleSocket() { | 1028 void ClientSocketPoolBaseHelper::CloseOneIdleSocket() { |
1089 if (idle_socket_count() == 0) | 1029 CloseOneIdleSocketExceptInGroup(NULL); |
1090 return false; | |
1091 return CloseOneIdleSocketExceptInGroup(NULL); | |
1092 } | 1030 } |
1093 | 1031 |
1094 bool ClientSocketPoolBaseHelper::CloseOneIdleSocketExceptInGroup( | 1032 bool ClientSocketPoolBaseHelper::CloseOneIdleSocketExceptInGroup( |
1095 const Group* exception_group) { | 1033 const Group* exception_group) { |
1096 CHECK_GT(idle_socket_count(), 0); | 1034 CHECK_GT(idle_socket_count(), 0); |
1097 | 1035 |
1098 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end(); ++i) { | 1036 for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end(); ++i) { |
1099 Group* group = i->second; | 1037 Group* group = i->second; |
1100 if (exception_group == group) | 1038 if (exception_group == group) |
1101 continue; | 1039 continue; |
1102 std::list<IdleSocket>* idle_sockets = group->mutable_idle_sockets(); | 1040 std::list<IdleSocket>* idle_sockets = group->mutable_idle_sockets(); |
1103 | 1041 |
1104 if (!idle_sockets->empty()) { | 1042 if (!idle_sockets->empty()) { |
1105 delete idle_sockets->front().socket; | 1043 delete idle_sockets->front().socket; |
1106 idle_sockets->pop_front(); | 1044 idle_sockets->pop_front(); |
1107 DecrementIdleCount(); | 1045 DecrementIdleCount(); |
1108 if (group->IsEmpty()) | 1046 if (group->IsEmpty()) |
1109 RemoveGroup(i); | 1047 RemoveGroup(i); |
1110 | 1048 |
1111 return true; | 1049 return true; |
1112 } | 1050 } |
1113 } | 1051 } |
1114 | 1052 |
| 1053 if (!exception_group) |
| 1054 LOG(DFATAL) << "No idle socket found to close!."; |
| 1055 |
1115 return false; | 1056 return false; |
1116 } | 1057 } |
1117 | 1058 |
1118 bool ClientSocketPoolBaseHelper::CloseOneIdleConnectionInLayeredPool() { | |
1119 // This pool doesn't have any idle sockets. It's possible that a pool at a | |
1120 // higher layer is holding one of this sockets active, but it's actually idle. | |
1121 // Query the higher layers. | |
1122 for (std::set<LayeredPool*>::const_iterator it = higher_layer_pools_.begin(); | |
1123 it != higher_layer_pools_.end(); ++it) { | |
1124 if ((*it)->CloseOneIdleConnection()) | |
1125 return true; | |
1126 } | |
1127 return false; | |
1128 } | |
1129 | |
1130 void ClientSocketPoolBaseHelper::InvokeUserCallbackLater( | 1059 void ClientSocketPoolBaseHelper::InvokeUserCallbackLater( |
1131 ClientSocketHandle* handle, const CompletionCallback& callback, int rv) { | 1060 ClientSocketHandle* handle, const CompletionCallback& callback, int rv) { |
1132 CHECK(!ContainsKey(pending_callback_map_, handle)); | 1061 CHECK(!ContainsKey(pending_callback_map_, handle)); |
1133 pending_callback_map_[handle] = CallbackResultPair(callback, rv); | 1062 pending_callback_map_[handle] = CallbackResultPair(callback, rv); |
1134 MessageLoop::current()->PostTask( | 1063 MessageLoop::current()->PostTask( |
1135 FROM_HERE, | 1064 FROM_HERE, |
1136 base::Bind(&ClientSocketPoolBaseHelper::InvokeUserCallback, | 1065 base::Bind(&ClientSocketPoolBaseHelper::InvokeUserCallback, |
1137 weak_factory_.GetWeakPtr(), handle)); | 1066 weak_factory_.GetWeakPtr(), handle)); |
1138 } | 1067 } |
1139 | 1068 |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1223 // Delete active jobs. | 1152 // Delete active jobs. |
1224 STLDeleteElements(&jobs_); | 1153 STLDeleteElements(&jobs_); |
1225 | 1154 |
1226 // Cancel pending backup job. | 1155 // Cancel pending backup job. |
1227 weak_factory_.InvalidateWeakPtrs(); | 1156 weak_factory_.InvalidateWeakPtrs(); |
1228 } | 1157 } |
1229 | 1158 |
1230 } // namespace internal | 1159 } // namespace internal |
1231 | 1160 |
1232 } // namespace net | 1161 } // namespace net |
OLD | NEW |