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