| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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/url_request/url_request_throttler_manager.h" | 5 #include "net/url_request/url_request_throttler_manager.h" |
| 6 | 6 |
| 7 #include <list> | 7 #include <list> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/string_util.h" | 10 #include "base/string_util.h" |
| 11 | 11 |
| 12 namespace { | |
| 13 | |
| 14 // TODO(joi): Remove after crbug.com/71721 is fixed. | |
| 15 struct IteratorHistory { | |
| 16 // Copy of 'this' pointer at time of access; this helps both because | |
| 17 // the this pointer is often obfuscated (at least for this particular | |
| 18 // stack trace) in fully optimized builds, and possibly to detect | |
| 19 // changes in the this pointer during iteration over the map (e.g. | |
| 20 // from another thread overwriting memory). | |
| 21 net::URLRequestThrottlerManager* self; | |
| 22 | |
| 23 // Copy of URL key. | |
| 24 char url[256]; | |
| 25 | |
| 26 // Not a refptr, we don't want to change behavior by keeping it alive. | |
| 27 net::URLRequestThrottlerEntryInterface* entry; | |
| 28 | |
| 29 // Set to true if the entry gets erased. Helpful to verify that entries | |
| 30 // with 0 refcount (since we don't take a refcount above) have been | |
| 31 // erased from the map. | |
| 32 bool was_erased; | |
| 33 }; | |
| 34 | |
| 35 } // namespace | |
| 36 | |
| 37 namespace net { | 12 namespace net { |
| 38 | 13 |
| 39 const unsigned int URLRequestThrottlerManager::kMaximumNumberOfEntries = 1500; | 14 const unsigned int URLRequestThrottlerManager::kMaximumNumberOfEntries = 1500; |
| 40 const unsigned int URLRequestThrottlerManager::kRequestsBetweenCollecting = 200; | 15 const unsigned int URLRequestThrottlerManager::kRequestsBetweenCollecting = 200; |
| 41 | 16 |
| 42 URLRequestThrottlerManager* URLRequestThrottlerManager::GetInstance() { | 17 URLRequestThrottlerManager* URLRequestThrottlerManager::GetInstance() { |
| 43 return Singleton<URLRequestThrottlerManager>::get(); | 18 return Singleton<URLRequestThrottlerManager>::get(); |
| 44 } | 19 } |
| 45 | 20 |
| 46 scoped_refptr<URLRequestThrottlerEntryInterface> | 21 scoped_refptr<URLRequestThrottlerEntryInterface> |
| 47 URLRequestThrottlerManager::RegisterRequestUrl(const GURL &url) { | 22 URLRequestThrottlerManager::RegisterRequestUrl(const GURL &url) { |
| 48 CHECK(being_tested_ || thread_checker_.CalledOnValidThread()); | 23 DCHECK(being_tested_ || CalledOnValidThread()); |
| 49 | 24 |
| 50 // Normalize the url. | 25 // Normalize the url. |
| 51 std::string url_id = GetIdFromUrl(url); | 26 std::string url_id = GetIdFromUrl(url); |
| 52 | 27 |
| 53 // Periodically garbage collect old entries. | 28 // Periodically garbage collect old entries. |
| 54 GarbageCollectEntriesIfNecessary(); | 29 GarbageCollectEntriesIfNecessary(); |
| 55 | 30 |
| 56 // Find the entry in the map or create it. | 31 // Find the entry in the map or create it. |
| 57 scoped_refptr<URLRequestThrottlerEntry>& entry = url_entries_[url_id]; | 32 scoped_refptr<URLRequestThrottlerEntry>& entry = url_entries_[url_id]; |
| 58 if (entry.get() == NULL) | 33 if (entry.get() == NULL) |
| 59 entry = new URLRequestThrottlerEntry(); | 34 entry = new URLRequestThrottlerEntry(); |
| 60 | 35 |
| 61 // TODO(joi): Demote CHECKs in this file to DCHECKs (or remove them) once | |
| 62 // we fully understand crbug.com/71721 | |
| 63 CHECK(entry.get()); | |
| 64 return entry; | 36 return entry; |
| 65 } | 37 } |
| 66 | 38 |
| 67 void URLRequestThrottlerManager::OverrideEntryForTests( | 39 void URLRequestThrottlerManager::OverrideEntryForTests( |
| 68 const GURL& url, | 40 const GURL& url, |
| 69 URLRequestThrottlerEntry* entry) { | 41 URLRequestThrottlerEntry* entry) { |
| 70 // Normalize the url. | 42 // Normalize the url. |
| 71 std::string url_id = GetIdFromUrl(url); | 43 std::string url_id = GetIdFromUrl(url); |
| 72 | 44 |
| 73 // Periodically garbage collect old entries. | 45 // Periodically garbage collect old entries. |
| (...skipping 13 matching lines...) Expand all Loading... |
| 87 being_tested_ = false; | 59 being_tested_ = false; |
| 88 } | 60 } |
| 89 | 61 |
| 90 URLRequestThrottlerManager::URLRequestThrottlerManager() | 62 URLRequestThrottlerManager::URLRequestThrottlerManager() |
| 91 : requests_since_last_gc_(0), | 63 : requests_since_last_gc_(0), |
| 92 enforce_throttling_(true), | 64 enforce_throttling_(true), |
| 93 being_tested_(true) { | 65 being_tested_(true) { |
| 94 // Construction/destruction is on main thread (because BrowserMain | 66 // Construction/destruction is on main thread (because BrowserMain |
| 95 // retrieves an instance to call InitializeOptions), but is from then on | 67 // retrieves an instance to call InitializeOptions), but is from then on |
| 96 // used on I/O thread. | 68 // used on I/O thread. |
| 97 thread_checker_.DetachFromThread(); | 69 DetachFromThread(); |
| 98 | 70 |
| 99 url_id_replacements_.ClearPassword(); | 71 url_id_replacements_.ClearPassword(); |
| 100 url_id_replacements_.ClearUsername(); | 72 url_id_replacements_.ClearUsername(); |
| 101 url_id_replacements_.ClearQuery(); | 73 url_id_replacements_.ClearQuery(); |
| 102 url_id_replacements_.ClearRef(); | 74 url_id_replacements_.ClearRef(); |
| 103 | |
| 104 // TODO(joi): Remove after crbug.com/71721 is fixed. | |
| 105 base::strlcpy(magic_buffer_1_, "MAGICZZ", arraysize(magic_buffer_1_)); | |
| 106 base::strlcpy(magic_buffer_2_, "GOOGYZZ", arraysize(magic_buffer_2_)); | |
| 107 } | 75 } |
| 108 | 76 |
| 109 URLRequestThrottlerManager::~URLRequestThrottlerManager() { | 77 URLRequestThrottlerManager::~URLRequestThrottlerManager() { |
| 110 // Destruction is on main thread (AtExit), but real use is on I/O thread. | 78 // Destruction is on main thread (AtExit), but real use is on I/O thread. |
| 111 thread_checker_.DetachFromThread(); | 79 DetachFromThread(); |
| 112 | 80 |
| 113 // Delete all entries. | 81 // Delete all entries. |
| 114 url_entries_.clear(); | 82 url_entries_.clear(); |
| 115 } | 83 } |
| 116 | 84 |
| 117 std::string URLRequestThrottlerManager::GetIdFromUrl(const GURL& url) const { | 85 std::string URLRequestThrottlerManager::GetIdFromUrl(const GURL& url) const { |
| 118 if (!url.is_valid()) | 86 if (!url.is_valid()) |
| 119 return url.possibly_invalid_spec(); | 87 return url.possibly_invalid_spec(); |
| 120 | 88 |
| 121 GURL id = url.ReplaceComponents(url_id_replacements_); | 89 GURL id = url.ReplaceComponents(url_id_replacements_); |
| 122 // TODO(joi): Remove "GOOGY/MONSTA" stuff once crbug.com/71721 is done | 90 return StringToLowerASCII(id.spec()).c_str(); |
| 123 return StringPrintf("GOOGY%sMONSTA", StringToLowerASCII(id.spec()).c_str()); | |
| 124 } | 91 } |
| 125 | 92 |
| 126 void URLRequestThrottlerManager::GarbageCollectEntriesIfNecessary() { | 93 void URLRequestThrottlerManager::GarbageCollectEntriesIfNecessary() { |
| 127 requests_since_last_gc_++; | 94 requests_since_last_gc_++; |
| 128 if (requests_since_last_gc_ < kRequestsBetweenCollecting) | 95 if (requests_since_last_gc_ < kRequestsBetweenCollecting) |
| 129 return; | 96 return; |
| 130 requests_since_last_gc_ = 0; | 97 requests_since_last_gc_ = 0; |
| 131 | 98 |
| 132 GarbageCollectEntries(); | 99 GarbageCollectEntries(); |
| 133 } | 100 } |
| 134 | 101 |
| 135 void URLRequestThrottlerManager::GarbageCollectEntries() { | 102 void URLRequestThrottlerManager::GarbageCollectEntries() { |
| 136 // TODO(joi): Remove these crash report aids once crbug.com/71721 | |
| 137 // is figured out. | |
| 138 IteratorHistory history[32] = { { 0 } }; | |
| 139 size_t history_ix = 0; | |
| 140 history[history_ix++].self = this; | |
| 141 | |
| 142 int nulls_found = 0; | |
| 143 UrlEntryMap::iterator i = url_entries_.begin(); | 103 UrlEntryMap::iterator i = url_entries_.begin(); |
| 144 while (i != url_entries_.end()) { | 104 while (i != url_entries_.end()) { |
| 145 if (i->second == NULL) { | 105 // TODO(joi): We know, as per crbug.com/71721, that values here can sometime
s |
| 146 ++nulls_found; | 106 // be NULL because of a a memory overwrite coming from somewhere else. |
| 147 } | 107 // Minidumps of the crash are at this point not giving us any new |
| 148 | 108 // information so adding the [i->second == NULL] check lessens the impact |
| 149 // Keep a log of the first 31 items accessed after the first | 109 // on our users (it seems to generally avoid the crash). |
| 150 // NULL encountered (hypothesis is there are multiple NULLs, | |
| 151 // and we may learn more about pattern of memory overwrite). | |
| 152 // We also log when we access the first entry, to get an original | |
| 153 // value for our this pointer. | |
| 154 if (nulls_found > 0 && history_ix < arraysize(history)) { | |
| 155 history[history_ix].self = this; | |
| 156 base::strlcpy(history[history_ix].url, i->first.c_str(), | |
| 157 arraysize(history[history_ix].url)); | |
| 158 history[history_ix].entry = i->second.get(); | |
| 159 history[history_ix].was_erased = false; | |
| 160 ++history_ix; | |
| 161 } | |
| 162 | |
| 163 // TODO(joi): Remove first i->second check when crbug.com/71721 is fixed. | |
| 164 if (i->second == NULL || (i->second)->IsEntryOutdated()) { | 110 if (i->second == NULL || (i->second)->IsEntryOutdated()) { |
| 165 url_entries_.erase(i++); | 111 url_entries_.erase(i++); |
| 166 | |
| 167 if (nulls_found > 0 && (history_ix - 1) < arraysize(history)) { | |
| 168 history[history_ix - 1].was_erased = true; | |
| 169 } | |
| 170 } else { | 112 } else { |
| 171 ++i; | 113 ++i; |
| 172 } | 114 } |
| 173 } | 115 } |
| 174 | 116 |
| 175 // TODO(joi): Make this a CHECK again after M11 branch point. | |
| 176 DCHECK(nulls_found == 0); | |
| 177 | |
| 178 // In case something broke we want to make sure not to grow indefinitely. | 117 // In case something broke we want to make sure not to grow indefinitely. |
| 179 while (url_entries_.size() > kMaximumNumberOfEntries) { | 118 while (url_entries_.size() > kMaximumNumberOfEntries) { |
| 180 url_entries_.erase(url_entries_.begin()); | 119 url_entries_.erase(url_entries_.begin()); |
| 181 } | 120 } |
| 182 } | 121 } |
| 183 | 122 |
| 184 } // namespace net | 123 } // namespace net |
| OLD | NEW |