Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(507)

Side by Side Diff: net/base/network_change_notifier_win.cc

Issue 2893943002: [NetworkChangeNotifier] Run Windows connection type computation on other thread (Closed)
Patch Set: Nit Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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/base/network_change_notifier_win.h" 5 #include "net/base/network_change_notifier_win.h"
6 6
7 #include <iphlpapi.h> 7 #include <iphlpapi.h>
8 #include <winsock2.h> 8 #include <winsock2.h>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/location.h" 11 #include "base/location.h"
12 #include "base/logging.h" 12 #include "base/logging.h"
13 #include "base/macros.h" 13 #include "base/macros.h"
14 #include "base/message_loop/message_loop.h" 14 #include "base/message_loop/message_loop.h"
15 #include "base/metrics/histogram_macros.h" 15 #include "base/metrics/histogram_macros.h"
16 #include "base/single_thread_task_runner.h" 16 #include "base/single_thread_task_runner.h"
17 #include "base/task_runner_util.h"
17 #include "base/threading/thread.h" 18 #include "base/threading/thread.h"
18 #include "base/threading/thread_task_runner_handle.h" 19 #include "base/threading/thread_task_runner_handle.h"
19 #include "base/time/time.h" 20 #include "base/time/time.h"
20 #include "net/base/winsock_init.h" 21 #include "net/base/winsock_init.h"
21 #include "net/base/winsock_util.h" 22 #include "net/base/winsock_util.h"
22 #include "net/dns/dns_config_service.h" 23 #include "net/dns/dns_config_service.h"
23 24
24 namespace net { 25 namespace net {
25 26
26 namespace { 27 namespace {
27 28
28 // Time between NotifyAddrChange retries, on failure. 29 // Time between NotifyAddrChange retries, on failure.
29 const int kWatchForAddressChangeRetryIntervalMs = 500; 30 const int kWatchForAddressChangeRetryIntervalMs = 500;
30 31
31 } // namespace
32
33 // Thread on which we can run DnsConfigService, which requires AssertIOAllowed
34 // to open registry keys and to handle FilePathWatcher updates.
35 class NetworkChangeNotifierWin::DnsConfigServiceThread : public base::Thread {
36 public:
37 DnsConfigServiceThread() : base::Thread("DnsConfigService") {}
38
39 ~DnsConfigServiceThread() override { Stop(); }
40
41 void Init() override {
42 service_ = DnsConfigService::CreateSystemService();
43 service_->WatchConfig(base::Bind(&NetworkChangeNotifier::SetDnsConfig));
44 }
45
46 void CleanUp() override { service_.reset(); }
47
48 private:
49 std::unique_ptr<DnsConfigService> service_;
50
51 DISALLOW_COPY_AND_ASSIGN(DnsConfigServiceThread);
52 };
53
54 NetworkChangeNotifierWin::NetworkChangeNotifierWin()
55 : NetworkChangeNotifier(NetworkChangeCalculatorParamsWin()),
56 is_watching_(false),
57 sequential_failures_(0),
58 dns_config_service_thread_(new DnsConfigServiceThread()),
59 last_computed_connection_type_(RecomputeCurrentConnectionType()),
60 last_announced_offline_(last_computed_connection_type_ ==
61 CONNECTION_NONE),
62 weak_factory_(this) {
63 memset(&addr_overlapped_, 0, sizeof addr_overlapped_);
64 addr_overlapped_.hEvent = WSACreateEvent();
65 }
66
67 NetworkChangeNotifierWin::~NetworkChangeNotifierWin() {
68 if (is_watching_) {
69 CancelIPChangeNotify(&addr_overlapped_);
70 addr_watcher_.StopWatching();
71 }
72 WSACloseEvent(addr_overlapped_.hEvent);
73 }
74
75 // static
76 NetworkChangeNotifier::NetworkChangeCalculatorParams
77 NetworkChangeNotifierWin::NetworkChangeCalculatorParamsWin() {
78 NetworkChangeCalculatorParams params;
79 // Delay values arrived at by simple experimentation and adjusted so as to
80 // produce a single signal when switching between network connections.
81 params.ip_address_offline_delay_ = base::TimeDelta::FromMilliseconds(1500);
82 params.ip_address_online_delay_ = base::TimeDelta::FromMilliseconds(1500);
83 params.connection_type_offline_delay_ =
84 base::TimeDelta::FromMilliseconds(1500);
85 params.connection_type_online_delay_ = base::TimeDelta::FromMilliseconds(500);
86 return params;
87 }
88
89 // This implementation does not return the actual connection type but merely 32 // This implementation does not return the actual connection type but merely
90 // determines if the user is "online" (in which case it returns 33 // determines if the user is "online" (in which case it returns
91 // CONNECTION_UNKNOWN) or "offline" (and then it returns CONNECTION_NONE). 34 // CONNECTION_UNKNOWN) or "offline" (and then it returns CONNECTION_NONE).
92 // This is challenging since the only thing we can test with certainty is 35 // This is challenging since the only thing we can test with certainty is
93 // whether a *particular* host is reachable. 36 // whether a *particular* host is reachable.
94 // 37 //
95 // While we can't conclusively determine when a user is "online", we can at 38 // While we can't conclusively determine when a user is "online", we can at
96 // least reliably recognize some of the situtations when they are clearly 39 // least reliably recognize some of the situtations when they are clearly
97 // "offline". For example, if the user's laptop is not plugged into an ethernet 40 // "offline". For example, if the user's laptop is not plugged into an ethernet
98 // network and is not connected to any wireless networks, it must be offline. 41 // network and is not connected to any wireless networks, it must be offline.
(...skipping 28 matching lines...) Expand all
127 // workstation. Here is what I found: 70 // workstation. Here is what I found:
128 // * Approach (1) was pretty much zero-cost after the initial call. 71 // * Approach (1) was pretty much zero-cost after the initial call.
129 // * Approach (2) took an average of 3.25 milliseconds to enumerate the 72 // * Approach (2) took an average of 3.25 milliseconds to enumerate the
130 // adapters. 73 // adapters.
131 // * Approach (3) took an average of 0.8 ms to enumerate the providers. 74 // * Approach (3) took an average of 0.8 ms to enumerate the providers.
132 // 75 //
133 // In terms of correctness, all three approaches were comparable for the simple 76 // In terms of correctness, all three approaches were comparable for the simple
134 // experiments I ran... However none of them correctly returned "offline" when 77 // experiments I ran... However none of them correctly returned "offline" when
135 // executing 'ipconfig /release'. 78 // executing 'ipconfig /release'.
136 // 79 //
137 NetworkChangeNotifier::ConnectionType 80 // It is not thread safe, see crbug.com/324913.
138 NetworkChangeNotifierWin::RecomputeCurrentConnectionType() const { 81 NetworkChangeNotifier::ConnectionType RecomputeCurrentConnectionType() {
139 DCHECK(CalledOnValidThread());
140
141 EnsureWinsockInit(); 82 EnsureWinsockInit();
142 83
143 // The following code was adapted from: 84 // The following code was adapted from:
144 // http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/net/notifier/ base/win/async_network_alive_win32.cc?view=markup&pathrev=47343 85 // http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/net/notifier/ base/win/async_network_alive_win32.cc?view=markup&pathrev=47343
145 // The main difference is we only call WSALookupServiceNext once, whereas 86 // The main difference is we only call WSALookupServiceNext once, whereas
146 // the earlier code would traverse the entire list and pass LUP_FLUSHPREVIOUS 87 // the earlier code would traverse the entire list and pass LUP_FLUSHPREVIOUS
147 // to skip past the large results. 88 // to skip past the large results.
148 89
149 HANDLE ws_handle; 90 HANDLE ws_handle;
150 WSAQUERYSET query_set = {0}; 91 WSAQUERYSET query_set = {0};
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
194 } else { 135 } else {
195 LOG(WARNING) << "WSALookupServiceNext() failed with:" << result; 136 LOG(WARNING) << "WSALookupServiceNext() failed with:" << result;
196 } 137 }
197 } 138 }
198 139
199 result = WSALookupServiceEnd(ws_handle); 140 result = WSALookupServiceEnd(ws_handle);
200 LOG_IF(ERROR, result != 0) 141 LOG_IF(ERROR, result != 0)
201 << "WSALookupServiceEnd() failed with: " << result; 142 << "WSALookupServiceEnd() failed with: " << result;
202 143
203 // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN. 144 // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN.
204 return found_connection ? ConnectionTypeFromInterfaces() 145 return found_connection
205 : NetworkChangeNotifier::CONNECTION_NONE; 146 ? NetworkChangeNotifier::ConnectionTypeFromInterfaces()
147 : NetworkChangeNotifier::CONNECTION_NONE;
148 }
149
150 } // namespace
151
152 // Thread on which we can run DnsConfigService, which requires AssertIOAllowed
153 // to open registry keys and to handle FilePathWatcher updates.
154 class NetworkChangeNotifierWin::DnsConfigServiceThread : public base::Thread {
155 public:
156 DnsConfigServiceThread() : base::Thread("DnsConfigService") {}
157
158 ~DnsConfigServiceThread() override { Stop(); }
159
160 void Init() override {
161 service_ = DnsConfigService::CreateSystemService();
162 service_->WatchConfig(base::Bind(&NetworkChangeNotifier::SetDnsConfig));
163 }
164
165 void CleanUp() override { service_.reset(); }
166
167 private:
168 std::unique_ptr<DnsConfigService> service_;
169
170 DISALLOW_COPY_AND_ASSIGN(DnsConfigServiceThread);
171 };
172
173 NetworkChangeNotifierWin::NetworkChangeNotifierWin()
174 : NetworkChangeNotifier(NetworkChangeCalculatorParamsWin()),
175 is_watching_(false),
176 sequential_failures_(0),
177 dns_config_service_thread_(new DnsConfigServiceThread()),
178 last_computed_connection_type_(RecomputeCurrentConnectionType()),
179 last_announced_offline_(last_computed_connection_type_ ==
180 CONNECTION_NONE),
181 calculate_connection_type_(
182 base::BindRepeating(&RecomputeCurrentConnectionType)),
183 weak_factory_(this) {
184 memset(&addr_overlapped_, 0, sizeof addr_overlapped_);
185 addr_overlapped_.hEvent = WSACreateEvent();
186 }
187
188 NetworkChangeNotifierWin::~NetworkChangeNotifierWin() {
189 if (is_watching_) {
190 CancelIPChangeNotify(&addr_overlapped_);
191 addr_watcher_.StopWatching();
192 }
193 WSACloseEvent(addr_overlapped_.hEvent);
194 }
195
196 // static
197 NetworkChangeNotifier::NetworkChangeCalculatorParams
198 NetworkChangeNotifierWin::NetworkChangeCalculatorParamsWin() {
199 NetworkChangeCalculatorParams params;
200 // Delay values arrived at by simple experimentation and adjusted so as to
201 // produce a single signal when switching between network connections.
202 params.ip_address_offline_delay_ = base::TimeDelta::FromMilliseconds(1500);
203 params.ip_address_online_delay_ = base::TimeDelta::FromMilliseconds(1500);
204 params.connection_type_offline_delay_ =
205 base::TimeDelta::FromMilliseconds(1500);
206 params.connection_type_online_delay_ = base::TimeDelta::FromMilliseconds(500);
207 return params;
206 } 208 }
207 209
208 NetworkChangeNotifier::ConnectionType 210 NetworkChangeNotifier::ConnectionType
209 NetworkChangeNotifierWin::GetCurrentConnectionType() const { 211 NetworkChangeNotifierWin::GetCurrentConnectionType() const {
210 base::AutoLock auto_lock(last_computed_connection_type_lock_); 212 base::AutoLock auto_lock(last_computed_connection_type_lock_);
211 return last_computed_connection_type_; 213 return last_computed_connection_type_;
212 } 214 }
213 215
214 void NetworkChangeNotifierWin::SetCurrentConnectionType( 216 void NetworkChangeNotifierWin::SetCurrentConnectionType(
215 ConnectionType connection_type) { 217 ConnectionType connection_type) {
216 base::AutoLock auto_lock(last_computed_connection_type_lock_); 218 base::AutoLock auto_lock(last_computed_connection_type_lock_);
217 last_computed_connection_type_ = connection_type; 219 last_computed_connection_type_ = connection_type;
218 } 220 }
219 221
220 void NetworkChangeNotifierWin::OnObjectSignaled(HANDLE object) { 222 void NetworkChangeNotifierWin::OnObjectSignaled(HANDLE object) {
221 DCHECK(CalledOnValidThread()); 223 DCHECK(CalledOnValidThread());
222 DCHECK(is_watching_); 224 DCHECK(is_watching_);
223 is_watching_ = false; 225 is_watching_ = false;
224 226
225 // Start watching for the next address change. 227 // Start watching for the next address change.
226 WatchForAddressChange(); 228 WatchForAddressChange();
229 DCHECK(dns_task_runner_);
227 230
228 NotifyObservers(); 231 base::PostTaskAndReplyWithResult(
232 dns_task_runner_.get(), FROM_HERE, calculate_connection_type_,
233 base::Bind(&NetworkChangeNotifierWin::NotifyObservers,
234 weak_factory_.GetWeakPtr()));
229 } 235 }
230 236
231 void NetworkChangeNotifierWin::NotifyObservers() { 237 void NetworkChangeNotifierWin::NotifyObservers(ConnectionType connection_type) {
232 DCHECK(CalledOnValidThread()); 238 DCHECK(CalledOnValidThread());
233 SetCurrentConnectionType(RecomputeCurrentConnectionType()); 239 SetCurrentConnectionType(connection_type);
234 NotifyObserversOfIPAddressChange(); 240 NotifyObserversOfIPAddressChange();
235 241
236 // Calling GetConnectionType() at this very moment is likely to give 242 // Calling GetConnectionType() at this very moment is likely to give
237 // the wrong result, so we delay that until a little bit later. 243 // the wrong result, so we delay that until a little bit later.
238 // 244 //
239 // The one second delay chosen here was determined experimentally 245 // The one second delay chosen here was determined experimentally
240 // by adamk on Windows 7. 246 // by adamk on Windows 7.
241 // If after one second we determine we are still offline, we will 247 // If after one second we determine we are still offline, we will
242 // delay again. 248 // delay again.
243 offline_polls_ = 0; 249 offline_polls_ = 0;
250 // TODO(jkarlin): I'm not sure that calling with 'this' is safe. Can it be
251 // deleted?
244 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1), this, 252 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1), this,
245 &NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange); 253 &NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange);
246 } 254 }
247 255
248 void NetworkChangeNotifierWin::WatchForAddressChange() { 256 void NetworkChangeNotifierWin::WatchForAddressChange() {
249 DCHECK(CalledOnValidThread()); 257 DCHECK(CalledOnValidThread());
250 DCHECK(!is_watching_); 258 DCHECK(!is_watching_);
251 259
252 // NotifyAddrChange occasionally fails with ERROR_OPEN_FAILED for unknown 260 // NotifyAddrChange occasionally fails with ERROR_OPEN_FAILED for unknown
253 // reasons. More rarely, it's also been observed failing with 261 // reasons. More rarely, it's also been observed failing with
(...skipping 13 matching lines...) Expand all
267 FROM_HERE, base::Bind(&NetworkChangeNotifierWin::WatchForAddressChange, 275 FROM_HERE, base::Bind(&NetworkChangeNotifierWin::WatchForAddressChange,
268 weak_factory_.GetWeakPtr()), 276 weak_factory_.GetWeakPtr()),
269 base::TimeDelta::FromMilliseconds( 277 base::TimeDelta::FromMilliseconds(
270 kWatchForAddressChangeRetryIntervalMs)); 278 kWatchForAddressChangeRetryIntervalMs));
271 return; 279 return;
272 } 280 }
273 281
274 // Treat the transition from NotifyAddrChange failing to succeeding as a 282 // Treat the transition from NotifyAddrChange failing to succeeding as a
275 // network change event, since network changes were not being observed in 283 // network change event, since network changes were not being observed in
276 // that interval. 284 // that interval.
277 if (sequential_failures_ > 0) 285 if (sequential_failures_ > 0) {
278 NotifyObservers(); 286 base::PostTaskAndReplyWithResult(
287 dns_task_runner_.get(), FROM_HERE, calculate_connection_type_,
288 base::Bind(&NetworkChangeNotifierWin::NotifyObservers,
289 weak_factory_.GetWeakPtr()));
290 }
279 291
280 if (sequential_failures_ < 2000) { 292 if (sequential_failures_ < 2000) {
281 UMA_HISTOGRAM_COUNTS_10000("Net.NotifyAddrChangeFailures", 293 UMA_HISTOGRAM_COUNTS_10000("Net.NotifyAddrChangeFailures",
282 sequential_failures_); 294 sequential_failures_);
283 } 295 }
284 296
285 is_watching_ = true; 297 is_watching_ = true;
286 sequential_failures_ = 0; 298 sequential_failures_ = 0;
287 } 299 }
288 300
289 bool NetworkChangeNotifierWin::WatchForAddressChangeInternal() { 301 bool NetworkChangeNotifierWin::WatchForAddressChangeInternal() {
290 DCHECK(CalledOnValidThread()); 302 DCHECK(CalledOnValidThread());
291 303
292 if (!dns_config_service_thread_->IsRunning()) { 304 if (!dns_config_service_thread_->IsRunning()) {
293 dns_config_service_thread_->StartWithOptions( 305 dns_config_service_thread_->StartWithOptions(
294 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); 306 base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
307 if (!dns_task_runner_)
308 dns_task_runner_ =
309 dns_config_service_thread_->message_loop()->task_runner();
295 } 310 }
296 311
297 ResetEventIfSignaled(addr_overlapped_.hEvent); 312 ResetEventIfSignaled(addr_overlapped_.hEvent);
298 HANDLE handle = NULL; 313 HANDLE handle = NULL;
299 DWORD ret = NotifyAddrChange(&handle, &addr_overlapped_); 314 DWORD ret = NotifyAddrChange(&handle, &addr_overlapped_);
300 if (ret != ERROR_IO_PENDING) 315 if (ret != ERROR_IO_PENDING)
301 return false; 316 return false;
302 317
303 addr_watcher_.StartWatchingOnce(addr_overlapped_.hEvent, this); 318 addr_watcher_.StartWatchingOnce(addr_overlapped_.hEvent, this);
304 return true; 319 return true;
305 } 320 }
306 321
307 void NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange() { 322 void NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange() {
308 SetCurrentConnectionType(RecomputeCurrentConnectionType()); 323 base::PostTaskAndReplyWithResult(
324 dns_task_runner_.get(), FROM_HERE, calculate_connection_type_,
325 base::Bind(
326 &NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChangeImpl,
327 weak_factory_.GetWeakPtr()));
328 }
329
330 void NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChangeImpl(
331 ConnectionType connection_type) {
332 SetCurrentConnectionType(connection_type);
309 bool current_offline = IsOffline(); 333 bool current_offline = IsOffline();
310 offline_polls_++; 334 offline_polls_++;
311 // If we continue to appear offline, delay sending out the notification in 335 // If we continue to appear offline, delay sending out the notification in
312 // case we appear to go online within 20 seconds. UMA histogram data shows 336 // case we appear to go online within 20 seconds. UMA histogram data shows
313 // we may not detect the transition to online state after 1 second but within 337 // we may not detect the transition to online state after 1 second but within
314 // 20 seconds we generally do. 338 // 20 seconds we generally do.
315 if (last_announced_offline_ && current_offline && offline_polls_ <= 20) { 339 if (last_announced_offline_ && current_offline && offline_polls_ <= 20) {
316 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1), this, 340 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1), this,
317 &NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange); 341 &NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange);
318 return; 342 return;
319 } 343 }
320 if (last_announced_offline_) 344 if (last_announced_offline_)
321 UMA_HISTOGRAM_CUSTOM_COUNTS("NCN.OfflinePolls", offline_polls_, 1, 50, 50); 345 UMA_HISTOGRAM_CUSTOM_COUNTS("NCN.OfflinePolls", offline_polls_, 1, 50, 50);
322 last_announced_offline_ = current_offline; 346 last_announced_offline_ = current_offline;
323 347
324 NotifyObserversOfConnectionTypeChange(); 348 NotifyObserversOfConnectionTypeChange();
325 double max_bandwidth_mbps = 0.0; 349 double max_bandwidth_mbps = 0.0;
326 ConnectionType connection_type = CONNECTION_NONE; 350 ConnectionType connection_type2 = CONNECTION_NONE;
pauljensen 2017/05/22 18:01:51 can we give this variable a more descriptive name?
jkarlin 2017/05/23 11:44:38 Done.
327 GetCurrentMaxBandwidthAndConnectionType(&max_bandwidth_mbps, 351 GetCurrentMaxBandwidthAndConnectionType(&max_bandwidth_mbps,
328 &connection_type); 352 &connection_type2);
329 NotifyObserversOfMaxBandwidthChange(max_bandwidth_mbps, connection_type); 353 NotifyObserversOfMaxBandwidthChange(max_bandwidth_mbps, connection_type2);
330 } 354 }
331 355
332 } // namespace net 356 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698