blob: 0ea83ecd7d7f20a68e879283733d7d46ce234add [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
5// See header file for description of class
6
7#include "chrome/browser/net/dns_master.h"
8
initial.commit09911bf2008-07-26 23:55:299#include <sstream>
initial.commit09911bf2008-07-26 23:55:2910
initial.commit09911bf2008-07-26 23:55:2911#include "base/histogram.h"
12#include "base/stats_counters.h"
initial.commit09911bf2008-07-26 23:55:2913#include "base/thread.h"
14#include "base/win_util.h"
15#include "chrome/browser/net/dns_slave.h"
16
[email protected]e1acf6f2008-10-27 20:43:3317using base::TimeDelta;
18
initial.commit09911bf2008-07-26 23:55:2919namespace chrome_browser_net {
20
21//------------------------------------------------------------------------------
22// This section contains methods for the DnsMaster class.
23//------------------------------------------------------------------------------
24DnsMaster::DnsMaster(TimeDelta shutdown_wait_time)
25 : slave_count_(0),
26 shutdown_(false),
27 running_slave_count_(0),
28 kShutdownWaitTime_(shutdown_wait_time),
29 slaves_have_work_(&lock_) {
30 for ( int i = 0; i < kSlaveCountMax; i++ ) {
31 thread_ids_[i] = 0;
32 thread_handles_[i] = 0;
33 slaves_[i] = NULL;
34 }
35}
36
37// Overloaded Resolve() to take a vector of names.
38void DnsMaster::ResolveList(const NameList& hostnames) {
39 {
40 AutoLock auto_lock(lock_);
41 if (shutdown_) return;
42 if (slave_count_ < kSlaveCountMin) {
43 for (int target_count = std::min(static_cast<int>(hostnames.size()),
44 kSlaveCountMin);
45 target_count > 0;
46 target_count--)
47 PreLockedCreateNewSlaveIfNeeded();
48 } else {
49 PreLockedCreateNewSlaveIfNeeded(); // Allocate one per list call.
50 }
51
52 for (NameList::const_iterator it = hostnames.begin();
53 it < hostnames.end();
54 it++) {
55 PreLockedResolve(*it);
56 }
57 }
58 slaves_have_work_.Signal();
59}
60
61// Basic Resolve() takes an invidual name, and adds it
62// to the queue.
63void DnsMaster::Resolve(const std::string& hostname) {
64 if (0 == hostname.length())
65 return;
66 {
67 AutoLock auto_lock(lock_);
68 if (shutdown_) return;
69 PreLockedCreateNewSlaveIfNeeded(); // Allocate one at a time.
70 PreLockedResolve(hostname);
71 }
72 slaves_have_work_.Signal();
73}
74
75bool DnsMaster::AcruePrefetchBenefits(DnsHostInfo* host_info) {
76 std::string hostname = host_info->hostname();
77 DnsBenefit benefit;
78 DnsHostInfo prefetched_host_info;
79 {
80 AutoLock auto_lock(lock_);
81 if (results_.find(hostname) == results_.end()) {
82 // Remain under lock to assure static HISTOGRAM constructor is safely run.
83 // Use UMA histogram to quantify potential future gains here.
84 UMA_HISTOGRAM_LONG_TIMES(L"DNS.UnexpectedResolutionL",
85 host_info->resolve_duration());
86 SIMPLE_STATS_COUNTER(L"DNS.PrefetchCacheOblivious");
87 host_info->DLogResultsStats("DNS PrefetchCacheOblivious");
88 return false;
89 }
90 benefit = results_[hostname].AcruePrefetchBenefits(host_info);
91 switch (benefit) {
92 case PREFETCH_NAME_FOUND:
93 case PREFETCH_NAME_NONEXISTANT:
94 // Remain under lock to push data.
95 cache_hits_.push_back(*host_info);
96 return true;
97
98 case PREFETCH_CACHE_EVICTION:
99 // Remain under lock to push data.
100 cache_eviction_map_[hostname] = *host_info;
101 return false;
102 case PREFETCH_NO_BENEFIT:
103 // Prefetch never hit the network. Name was pre-cached.
104 return false;
105
106 default:
107 DCHECK(false);
108 return false;
109 }
110 }
111}
112
113static char* PluralOptionalHostname(size_t count) {
114 if (count == 1)
115 return "hostname";
116 return "hostnames";
117}
118
119// Provide sort order so all .com's are together, etc.
120struct RightToLeftStringSorter {
121 bool operator()(const std::string& left, const std::string& right) const {
122 size_t left_length = left.length();
123 size_t right_length = right.length();
124 const char* left_data = left.data();
125 const char* right_data = right.data();
126 while (true) {
127 if (0 == right_length)
128 return false;
129 if (0 == left_length)
130 return true;
131 left_length--;
132 right_length--;
133 int difference = left_data[left_length] - right_data[right_length];
134 if (difference)
135 return difference < 0;
136 }
137 }
138};
139
140void DnsMaster::GetHtmlInfo(std::string* output) {
141 // Local lists for calling DnsHostInfo
142 DnsHostInfo::DnsInfoTable cache_hits;
143 DnsHostInfo::DnsInfoTable cache_evictions;
144 DnsHostInfo::DnsInfoTable name_not_found;
145 DnsHostInfo::DnsInfoTable network_hits;
146 DnsHostInfo::DnsInfoTable already_cached;
147
148 // Get copies of all useful data under protection of a lock.
149 typedef std::map<std::string, DnsHostInfo, RightToLeftStringSorter> Snapshot;
150 Snapshot snapshot;
151 {
152 AutoLock auto_lock(lock_);
153 // DnsHostInfo supports value semantics, so we can do a shallow copy.
154 for (Results::iterator it(results_.begin()); it != results_.end(); it++) {
155 snapshot[it->first] = it->second;
156 }
157 for (Results::iterator it(cache_eviction_map_.begin());
158 it != cache_eviction_map_.end();
159 it++) {
160 cache_evictions.push_back(it->second);
161 }
162 // Reverse list as we copy cache hits, so that new hits are at the top.
163 size_t index = cache_hits_.size();
164 while (index > 0) {
165 index--;
166 cache_hits.push_back(cache_hits_[index]);
167 }
168 }
169
170 // Partition the DnsHostInfo's into categories.
171 for (Snapshot::iterator it(snapshot.begin()); it != snapshot.end(); it++) {
172 if (it->second.was_nonexistant()) {
173 name_not_found.push_back(it->second);
174 continue;
175 }
176 if (!it->second.was_found())
177 continue; // Still being processed.
178 if (TimeDelta() != it->second.benefits_remaining()) {
179 network_hits.push_back(it->second); // With no benefit yet.
180 continue;
181 }
182 if (DnsHostInfo::kMaxNonNetworkDnsLookupDuration >
183 it->second.resolve_duration()) {
184 already_cached.push_back(it->second);
185 continue;
186 }
187 // Remaining case is where prefetch benefit was significant, and was used.
188 // Since we shot those cases as historical hits, we won't bother here.
189 }
190
191 bool brief = false;
192#ifdef NDEBUG
193 brief = true;
194#endif // NDEBUG
195
196 // Call for display of each table, along with title.
197 DnsHostInfo::GetHtmlTable(cache_hits,
198 "Prefetching DNS records produced benefits for ", false, output);
199 DnsHostInfo::GetHtmlTable(cache_evictions,
200 "Cache evictions negated DNS prefetching benefits for ", brief, output);
201 DnsHostInfo::GetHtmlTable(network_hits,
202 "Prefetching DNS records was not yet beneficial for ", brief, output);
203 DnsHostInfo::GetHtmlTable(already_cached,
204 "Previously cached resolutions were found for ", brief, output);
205 DnsHostInfo::GetHtmlTable(name_not_found,
206 "Prefetching DNS records revealed non-existance for ", brief, output);
207}
208
209void DnsMaster::PreLockedResolve(const std::string& hostname) {
210 // DCHECK(We have the lock);
211 DCHECK(0 != slave_count_);
212 DCHECK(0 != hostname.length());
213
214 DnsHostInfo* info = &results_[hostname];
215 info->SetHostname(hostname); // Initialize or DCHECK.
216 // TODO(jar): I need to discard names that have long since expired.
217 // Currently we only add to the domain map :-/
218
219 DCHECK(info->HasHostname(hostname));
220
221 static StatsCounter count(L"DNS.PrefetchContemplated");
222 count.Increment();
223
224 if (!info->NeedsDnsUpdate(hostname)) {
225 info->DLogResultsStats("DNS PrefetchNotUpdated");
226 return;
227 }
228
229 info->SetQueuedState();
230 name_buffer_.push(hostname);
231}
232
initial.commit09911bf2008-07-26 23:55:29233// GetNextAssignment() is executed on the thread associated with
234// with a prefetch slave instance.
235// Return value of false indicates slave thread termination is needed.
236// Return value of true means info argument was populated
237// with a pointer to the assigned DnsHostInfo instance.
238bool DnsMaster::GetNextAssignment(std::string* hostname) {
239 bool ask_for_help = false;
240 {
241 AutoLock auto_lock(lock_); // For map and buffer access
242 while (0 == name_buffer_.size() && !shutdown_) {
243 // No work pending, so just wait.
244 // wait on condition variable while releasing lock temporarilly.
245 slaves_have_work_.Wait();
246 }
247 if (shutdown_)
248 return false; // Tell slaves to terminate also.
249 *hostname = name_buffer_.front();
250 name_buffer_.pop();
251
252 DnsHostInfo* info = &results_[*hostname];
253 DCHECK(info->HasHostname(*hostname));
254 info->SetAssignedState();
255
256 ask_for_help = name_buffer_.size() > 0;
257 } // Release lock_
258 if (ask_for_help)
259 slaves_have_work_.Signal();
260 return true;
261}
262
263void DnsMaster::SetFoundState(const std::string hostname) {
264 AutoLock auto_lock(lock_); // For map access (changing info values).
265 DnsHostInfo* info = &results_[hostname];
266 DCHECK(info->HasHostname(hostname));
267 if (info->is_marked_to_delete())
268 results_.erase(hostname);
269 else
270 info->SetFoundState();
271}
272
273void DnsMaster::SetNoSuchNameState(const std::string hostname) {
274 AutoLock auto_lock(lock_); // For map access (changing info values).
275 DnsHostInfo* info = &results_[hostname];
276 DCHECK(info->HasHostname(hostname));
277 if (info->is_marked_to_delete())
278 results_.erase(hostname);
279 else
280 info->SetNoSuchNameState();
281}
282
283bool DnsMaster::PreLockedCreateNewSlaveIfNeeded() {
284 // Don't create more then max.
285 if (kSlaveCountMax <= slave_count_ || shutdown_)
286 return false;
287
288 DnsSlave* slave_instance = new DnsSlave(this, slave_count_);
289 DWORD thread_id;
290 size_t stack_size = 0;
291 unsigned int flags = CREATE_SUSPENDED;
292 if (win_util::GetWinVersion() >= win_util::WINVERSION_XP) {
293 // 128kb stack size.
294 stack_size = 128*1024;
295 flags |= STACK_SIZE_PARAM_IS_A_RESERVATION;
296 }
297 HANDLE handle = CreateThread(NULL, // security
298 stack_size,
299 DnsSlave::ThreadStart,
300 reinterpret_cast<void*>(slave_instance),
301 flags,
302 &thread_id);
303 DCHECK(NULL != handle);
304 if (NULL == handle)
305 return false;
306
307 // Carlos suggests it is not valuable to do a set priority.
308 // BOOL WINAPI SetThreadPriority(handle,int nPriority);
309
310 thread_ids_[slave_count_] = thread_id;
311 thread_handles_[slave_count_] = handle;
312 slaves_[slave_count_] = slave_instance;
313 slave_count_++;
314
315 ResumeThread(handle); // WINAPI call.
316 running_slave_count_++;
317
318 return true;
319}
320
321void DnsMaster::SetSlaveHasTerminated(int slave_index) {
322 DCHECK_EQ(GetCurrentThreadId(), thread_ids_[slave_index]);
323 AutoLock auto_lock(lock_);
324 running_slave_count_--;
325 DCHECK(thread_ids_[slave_index]);
326 thread_ids_[slave_index] = 0;
327}
328
329bool DnsMaster::ShutdownSlaves() {
330 int running_slave_count;
331 {
332 AutoLock auto_lock(lock_);
333 shutdown_ = true; // Block additional resolution requests.
334 // Empty the queue gracefully
335 while (name_buffer_.size() > 0) {
336 std::string hostname = name_buffer_.front();
337 name_buffer_.pop();
338 DnsHostInfo* info = &results_[hostname];
339 DCHECK(info->HasHostname(hostname));
340 // We should be in Queued state, so simulate to end of life.
341 info->SetAssignedState(); // Simulate slave assignment.
342 info->SetNoSuchNameState(); // Simulate failed lookup.
343 results_.erase(hostname);
344 }
345 running_slave_count = running_slave_count_;
346 // Release lock, so slaves can finish up.
347 }
348
349 if (running_slave_count) {
350 slaves_have_work_.Broadcast(); // Slaves need to check for termination.
351
352 DWORD result = WaitForMultipleObjects(
353 slave_count_,
354 thread_handles_,
355 TRUE, // Wait for all
356 static_cast<DWORD>(kShutdownWaitTime_.InMilliseconds()));
357
358 DCHECK(result != WAIT_TIMEOUT) << "Some slaves didn't stop";
359 if (WAIT_TIMEOUT == result)
360 return false;
361 }
362 {
363 AutoLock auto_lock(lock_);
364 while (0 < slave_count_--) {
365 if (0 == thread_ids_[slave_count_]) { // Thread terminated.
366 int result = CloseHandle(thread_handles_[slave_count_]);
367 CHECK(0 != result);
368 thread_handles_[slave_count_] = 0;
369 delete slaves_[slave_count_];
370 slaves_[slave_count_] = NULL;
371 }
372 }
373 }
374 return true;
375}
376
377void DnsMaster::DiscardAllResults() {
378 AutoLock auto_lock(lock_);
379 // Delete anything listed so far in this session that shows in about:dns.
380 cache_eviction_map_.clear();
381 cache_hits_.clear();
382
383
384 // Try to delete anything in our work queue.
385 while (!name_buffer_.empty()) {
386 // Emulate processing cycle as though host was not found.
387 std::string hostname = name_buffer_.front();
388 name_buffer_.pop();
389 DnsHostInfo* info = &results_[hostname];
390 DCHECK(info->HasHostname(hostname));
391 info->SetAssignedState();
392 info->SetNoSuchNameState();
393 }
394 // Now every result_ is either resolved, or is being worked on by a slave.
395
396 // Step through result_, recording names of all hosts that can't be erased.
397 // We can't erase anything being worked on by a slave.
398 Results assignees;
399 for (Results::iterator it = results_.begin(); results_.end() != it; ++it) {
400 std::string hostname = it->first;
401 DnsHostInfo* info = &it->second;
402 DCHECK(info->HasHostname(hostname));
403 if (info->is_assigned()) {
404 info->SetPendingDeleteState();
405 assignees[hostname] = *info;
406 }
407 }
408 DCHECK(kSlaveCountMax >= assignees.size());
409 results_.clear();
410 // Put back in the names being worked on by slaves.
411 for (Results::iterator it = assignees.begin(); assignees.end() != it; ++it) {
412 DCHECK(it->second.is_marked_to_delete());
413 results_[it->first] = it->second;
414 }
415}
416
417} // namespace chrome_browser_net
license.botbf09a502008-08-24 00:55:55418