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

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

Issue 2802015: Massively simplify the NetworkChangeNotifier infrastructure:... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 10 years, 6 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 | Annotate | Revision Log
OLDNEW
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 // There are three classes involved here. There's NetworkChangeNotifierMac, 5 #include "net/base/network_change_notifier_mac.h"
6 // which is the Mac specific implementation of NetworkChangeNotifier. It is the
7 // class with which clients can register themselves as network change
8 // observers. There's NetworkChangeNotifierThread, which is a base::Thread
9 // subclass of MessageLoop::TYPE_UI (since it needs a CFRunLoop) that contains
10 // the NetworkChangeNotifierImpl. NetworkChangeNotifierImpl is the object
11 // that receives the actual OS X notifications and posts them to the
12 // NetworkChangeNotifierMac's message loop, so that NetworkChangeNotifierMac
13 // can notify all its observers.
14 //
15 // When NetworkChangeNotifierMac is being deleted, it will delete the
16 // NetworkChangeNotifierThread, which will Stop() it and also delete the
17 // NetworkChangeNotifierImpl. Therefore, NetworkChangeNotifierImpl and
18 // NetworkChangeNotifierThread's lifetimes generally begin after and end before
19 // NetworkChangeNotifierMac. There is an edge case where a notification task
20 // gets posted to the IO thread, thereby maintaining a reference to
21 // NetworkChangeNotifierImpl beyond the lifetime of NetworkChangeNotifierThread.
22 // In this case, the notification is cancelled, and NetworkChangeNotifierImpl
23 // will be deleted once all notification tasks that reference it have been run.
24 6
25 #include "net/base/network_change_notifier_mac.h"
26 #include <SystemConfiguration/SCDynamicStore.h>
27 #include <SystemConfiguration/SCDynamicStoreKey.h> 7 #include <SystemConfiguration/SCDynamicStoreKey.h>
28 #include <SystemConfiguration/SCSchemaDefinitions.h> 8 #include <SystemConfiguration/SCSchemaDefinitions.h>
29 #include <algorithm> 9 #include <algorithm>
30 #include "base/logging.h" 10
31 #include "base/message_loop.h"
32 #include "base/scoped_cftyperef.h"
33 #include "base/thread.h" 11 #include "base/thread.h"
34 12
13 // We only post tasks to a child thread we own, so we don't need refcounting.
14 DISABLE_RUNNABLE_METHOD_REFCOUNT(net::NetworkChangeNotifierMac);
15
35 namespace net { 16 namespace net {
36 17
37 namespace { 18 NetworkChangeNotifierMac::NetworkChangeNotifierMac()
38 19 : notifier_thread_(new base::Thread("NetworkChangeNotifier")) {
39 // NetworkChangeNotifierImpl should be created on a thread with a CFRunLoop, 20 // We create this notifier thread because the notification implementation
40 // since it requires one to pump notifications. However, it also runs some 21 // needs a thread with a CFRunLoop, and there's no guarantee that
41 // methods on |notifier_loop_|, because it cannot post calls to |notifier_| 22 // MessageLoop::current() meets that criterion.
42 // since NetworkChangeNotifier is not ref counted in a thread safe manner. 23 base::Thread::Options thread_options(MessageLoop::TYPE_UI, 0);
43 class NetworkChangeNotifierImpl 24 notifier_thread_->StartWithOptions(thread_options);
44 : public base::RefCountedThreadSafe<NetworkChangeNotifierImpl> { 25 // TODO(willchan): Look to see if there's a better signal for when it's ok to
45 public: 26 // initialize this, rather than just delaying it by a fixed time.
46 NetworkChangeNotifierImpl(MessageLoop* notifier_loop, 27 const int kNotifierThreadInitializationDelayMS = 1000;
47 NetworkChangeNotifierMac* notifier); 28 notifier_thread_->message_loop()->PostDelayedTask(FROM_HERE,
48 29 NewRunnableMethod(this, &NetworkChangeNotifierMac::Init),
49 void Shutdown(); 30 kNotifierThreadInitializationDelayMS);
50
51 private:
52 friend class base::RefCountedThreadSafe<NetworkChangeNotifierImpl>;
53 ~NetworkChangeNotifierImpl();
54
55 static void DynamicStoreCallback(SCDynamicStoreRef /* store */,
56 CFArrayRef changed_keys,
57 void* config);
58
59 void OnNetworkConfigChange(CFArrayRef changed_keys);
60
61 // Runs on |notifier_loop_|.
62 void OnIPAddressChanged();
63
64 // Raw pointers. Note that |notifier_| _must_ outlive the
65 // NetworkChangeNotifierImpl. For lifecycle management details, read the
66 // comment at the top of the file.
67 MessageLoop* const notifier_loop_;
68 NetworkChangeNotifierMac* notifier_;
69
70 scoped_cftyperef<CFRunLoopSourceRef> source_;
71
72 DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierImpl);
73 };
74
75 NetworkChangeNotifierImpl::NetworkChangeNotifierImpl(
76 MessageLoop* notifier_loop, NetworkChangeNotifierMac* notifier)
77 : notifier_loop_(notifier_loop),
78 notifier_(notifier) {
79 DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type());
80 SCDynamicStoreContext context = {
81 0, // Version 0.
82 this, // User data.
83 NULL, // This is not reference counted. No retain function.
84 NULL, // This is not reference counted. No release function.
85 NULL, // No description for this.
86 };
87
88 // Get a reference to the dynamic store.
89 scoped_cftyperef<SCDynamicStoreRef> store(
90 SCDynamicStoreCreate(NULL /* use default allocator */,
91 CFSTR("org.chromium"),
92 DynamicStoreCallback, &context));
93
94 // Create a run loop source for the dynamic store.
95 source_.reset(SCDynamicStoreCreateRunLoopSource(
96 NULL /* use default allocator */,
97 store.get(),
98 0 /* 0 sounds like a fine source order to me! */));
99
100 // Add the run loop source to the current run loop.
101 CFRunLoopAddSource(CFRunLoopGetCurrent(),
102 source_.get(),
103 kCFRunLoopCommonModes);
104
105 // Set up the notification keys.
106 scoped_cftyperef<CFMutableArrayRef> notification_keys(
107 CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
108
109 // Monitor interface changes.
110 scoped_cftyperef<CFStringRef> key(
111 SCDynamicStoreKeyCreateNetworkGlobalEntity(
112 NULL /* default allocator */, kSCDynamicStoreDomainState,
113 kSCEntNetInterface));
114 CFArrayAppendValue(notification_keys.get(), key.get());
115
116 // Monitor IP address changes.
117
118 key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity(
119 NULL /* default allocator */, kSCDynamicStoreDomainState,
120 kSCEntNetIPv4));
121 CFArrayAppendValue(notification_keys.get(), key.get());
122
123 key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity(
124 NULL /* default allocator */, kSCDynamicStoreDomainState,
125 kSCEntNetIPv6));
126 CFArrayAppendValue(notification_keys.get(), key.get());
127
128 // Ok, let's ask for notifications!
129 // TODO(willchan): Figure out a proper way to handle this rather than crash.
130 CHECK(SCDynamicStoreSetNotificationKeys(
131 store.get(), notification_keys.get(), NULL));
132 } 31 }
133 32
134 NetworkChangeNotifierImpl::~NetworkChangeNotifierImpl() { 33 NetworkChangeNotifierMac::~NetworkChangeNotifierMac() {
135 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), 34 // We don't need to explicitly Stop(), but doing so allows us to sanity-
136 source_.get(), 35 // check that the notifier thread shut down properly.
137 kCFRunLoopCommonModes); 36 notifier_thread_->Stop();
138 } 37 DCHECK(run_loop_source_ == NULL);
139
140 void NetworkChangeNotifierImpl::Shutdown() {
141 CHECK(notifier_);
142 notifier_ = NULL;
143 } 38 }
144 39
145 // static 40 // static
146 void NetworkChangeNotifierImpl::DynamicStoreCallback( 41 void NetworkChangeNotifierMac::DynamicStoreCallback(
147 SCDynamicStoreRef /* store */, 42 SCDynamicStoreRef /* store */,
148 CFArrayRef changed_keys, 43 CFArrayRef changed_keys,
149 void* config) { 44 void* config) {
150 NetworkChangeNotifierImpl* net_config = 45 NetworkChangeNotifierMac* net_config =
151 static_cast<NetworkChangeNotifierImpl*>(config); 46 static_cast<NetworkChangeNotifierMac*>(config);
152 net_config->OnNetworkConfigChange(changed_keys); 47 net_config->OnNetworkConfigChange(changed_keys);
153 } 48 }
154 49
155 void NetworkChangeNotifierImpl::OnNetworkConfigChange(CFArrayRef changed_keys) { 50 void NetworkChangeNotifierMac::WillDestroyCurrentMessageLoop() {
51 DCHECK(notifier_thread_ != NULL);
52 // We can't check the notifier_thread_'s message_loop(), as it's now 0.
53 // DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current());
54
55 DCHECK(run_loop_source_ != NULL);
56 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), run_loop_source_.get(),
57 kCFRunLoopCommonModes);
58 run_loop_source_.reset();
59 }
60
61 void NetworkChangeNotifierMac::Init() {
62 DCHECK(notifier_thread_ != NULL);
63 DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current());
64
65 // Add a run loop source for a dynamic store to the current run loop.
66 SCDynamicStoreContext context = {
67 0, // Version 0.
68 this, // User data.
69 NULL, // This is not reference counted. No retain function.
70 NULL, // This is not reference counted. No release function.
71 NULL, // No description for this.
72 };
73 scoped_cftyperef<SCDynamicStoreRef> store(SCDynamicStoreCreate(
74 NULL, CFSTR("org.chromium"), DynamicStoreCallback, &context));
75 run_loop_source_.reset(SCDynamicStoreCreateRunLoopSource(
76 NULL, store.get(), 0));
77 CFRunLoopAddSource(CFRunLoopGetCurrent(), run_loop_source_.get(),
78 kCFRunLoopCommonModes);
79
80 // Set up notifications for interface and IP address changes.
81 scoped_cftyperef<CFMutableArrayRef> notification_keys(
82 CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
83 scoped_cftyperef<CFStringRef> key(SCDynamicStoreKeyCreateNetworkGlobalEntity(
84 NULL, kSCDynamicStoreDomainState, kSCEntNetInterface));
85 CFArrayAppendValue(notification_keys.get(), key.get());
86 key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity(
87 NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4));
88 CFArrayAppendValue(notification_keys.get(), key.get());
89 key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity(
90 NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6));
91 CFArrayAppendValue(notification_keys.get(), key.get());
92
93 // Set the notification keys. This starts us receiving notifications.
94 bool ret = SCDynamicStoreSetNotificationKeys(
95 store.get(), notification_keys.get(), NULL);
96 // TODO(willchan): Figure out a proper way to handle this rather than crash.
97 CHECK(ret);
98
99 MessageLoop::current()->AddDestructionObserver(this);
100 }
101
102 void NetworkChangeNotifierMac::OnNetworkConfigChange(CFArrayRef changed_keys) {
103 DCHECK(notifier_thread_ != NULL);
104 DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current());
105
156 for (CFIndex i = 0; i < CFArrayGetCount(changed_keys); ++i) { 106 for (CFIndex i = 0; i < CFArrayGetCount(changed_keys); ++i) {
157 CFStringRef key = static_cast<CFStringRef>( 107 CFStringRef key = static_cast<CFStringRef>(
158 CFArrayGetValueAtIndex(changed_keys, i)); 108 CFArrayGetValueAtIndex(changed_keys, i));
159 if (CFStringHasSuffix(key, kSCEntNetIPv4) || 109 if (CFStringHasSuffix(key, kSCEntNetIPv4) ||
160 CFStringHasSuffix(key, kSCEntNetIPv6)) { 110 CFStringHasSuffix(key, kSCEntNetIPv6)) {
161 notifier_loop_->PostTask( 111 NotifyObserversOfIPAddressChange();
162 FROM_HERE, 112 return;
163 NewRunnableMethod( 113 }
164 this, 114 if (CFStringHasSuffix(key, kSCEntNetInterface)) {
165 &NetworkChangeNotifierImpl::OnIPAddressChanged));
166 } else if (CFStringHasSuffix(key, kSCEntNetInterface)) {
167 // TODO(willchan): Does not appear to be working. Look into this. 115 // TODO(willchan): Does not appear to be working. Look into this.
168 // Perhaps this isn't needed anyway. 116 // Perhaps this isn't needed anyway.
169 } else { 117 } else {
170 NOTREACHED(); 118 NOTREACHED();
171 } 119 }
172 } 120 }
173 } 121 }
174 122
175 void NetworkChangeNotifierImpl::OnIPAddressChanged() {
176 // If |notifier_| doesn't exist, then that means we're shutting down, so
177 // notifications are all cancelled.
178 if (notifier_)
179 notifier_->OnIPAddressChanged();
180 }
181
182 class NetworkChangeNotifierThread : public base::Thread {
183 public:
184 NetworkChangeNotifierThread(MessageLoop* notifier_loop,
185 NetworkChangeNotifierMac* notifier);
186 ~NetworkChangeNotifierThread();
187
188 protected:
189 virtual void Init();
190
191 private:
192 MessageLoop* const notifier_loop_;
193 NetworkChangeNotifierMac* const notifier_;
194 scoped_refptr<NetworkChangeNotifierImpl> notifier_impl_;
195
196 DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierThread);
197 };
198
199 NetworkChangeNotifierThread::NetworkChangeNotifierThread(
200 MessageLoop* notifier_loop, NetworkChangeNotifierMac* notifier)
201 : base::Thread("NetworkChangeNotifier"),
202 notifier_loop_(notifier_loop),
203 notifier_(notifier) {}
204
205 NetworkChangeNotifierThread::~NetworkChangeNotifierThread() {
206 notifier_impl_->Shutdown();
207 Stop();
208 }
209
210 // Note that |notifier_impl_| is initialized on the network change
211 // notifier thread, not whatever thread constructs the
212 // NetworkChangeNotifierThread object. This is important, because this thread
213 // is the one that has a CFRunLoop.
214 void NetworkChangeNotifierThread::Init() {
215 notifier_impl_ =
216 new NetworkChangeNotifierImpl(notifier_loop_, notifier_);
217 }
218
219 } // namespace
220
221 NetworkChangeNotifierMac::NetworkChangeNotifierMac()
222 : notifier_thread_(NULL),
223 method_factory_(this) {
224 // TODO(willchan): Look to see if there's a better signal for when it's ok to
225 // initialize this, rather than just delaying it by a fixed time.
226 const int kNotifierThreadInitializationDelayMS = 1000;
227 MessageLoop* loop = MessageLoop::current();
228 loop->PostDelayedTask(
229 FROM_HERE,
230 method_factory_.NewRunnableMethod(
231 &NetworkChangeNotifierMac::InitializeNotifierThread, loop),
232 kNotifierThreadInitializationDelayMS);
233 }
234
235 void NetworkChangeNotifierMac::OnIPAddressChanged() {
236 DCHECK(CalledOnValidThread());
237 FOR_EACH_OBSERVER(Observer, observers_, OnIPAddressChanged());
238 }
239
240 void NetworkChangeNotifierMac::AddObserver(Observer* observer) {
241 DCHECK(CalledOnValidThread());
242 observers_.AddObserver(observer);
243 }
244
245 void NetworkChangeNotifierMac::RemoveObserver(Observer* observer) {
246 DCHECK(CalledOnValidThread());
247 observers_.RemoveObserver(observer);
248 }
249
250 NetworkChangeNotifierMac::~NetworkChangeNotifierMac() {
251 DCHECK(CalledOnValidThread());
252 }
253
254 void NetworkChangeNotifierMac::InitializeNotifierThread(MessageLoop* loop) {
255 DCHECK(CalledOnValidThread());
256 notifier_thread_.reset(new NetworkChangeNotifierThread(loop, this));
257 base::Thread::Options thread_options;
258 thread_options.message_loop_type = MessageLoop::TYPE_UI;
259 notifier_thread_->StartWithOptions(thread_options);
260 }
261
262 } // namespace net 123 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698