Index: net/base/network_change_notifier_mac.cc |
=================================================================== |
--- net/base/network_change_notifier_mac.cc (revision 50775) |
+++ net/base/network_change_notifier_mac.cc (working copy) |
@@ -2,168 +2,116 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-// There are three classes involved here. There's NetworkChangeNotifierMac, |
-// which is the Mac specific implementation of NetworkChangeNotifier. It is the |
-// class with which clients can register themselves as network change |
-// observers. There's NetworkChangeNotifierThread, which is a base::Thread |
-// subclass of MessageLoop::TYPE_UI (since it needs a CFRunLoop) that contains |
-// the NetworkChangeNotifierImpl. NetworkChangeNotifierImpl is the object |
-// that receives the actual OS X notifications and posts them to the |
-// NetworkChangeNotifierMac's message loop, so that NetworkChangeNotifierMac |
-// can notify all its observers. |
-// |
-// When NetworkChangeNotifierMac is being deleted, it will delete the |
-// NetworkChangeNotifierThread, which will Stop() it and also delete the |
-// NetworkChangeNotifierImpl. Therefore, NetworkChangeNotifierImpl and |
-// NetworkChangeNotifierThread's lifetimes generally begin after and end before |
-// NetworkChangeNotifierMac. There is an edge case where a notification task |
-// gets posted to the IO thread, thereby maintaining a reference to |
-// NetworkChangeNotifierImpl beyond the lifetime of NetworkChangeNotifierThread. |
-// In this case, the notification is cancelled, and NetworkChangeNotifierImpl |
-// will be deleted once all notification tasks that reference it have been run. |
- |
#include "net/base/network_change_notifier_mac.h" |
-#include <SystemConfiguration/SCDynamicStore.h> |
+ |
#include <SystemConfiguration/SCDynamicStoreKey.h> |
#include <SystemConfiguration/SCSchemaDefinitions.h> |
#include <algorithm> |
-#include "base/logging.h" |
-#include "base/message_loop.h" |
-#include "base/scoped_cftyperef.h" |
+ |
#include "base/thread.h" |
+// We only post tasks to a child thread we own, so we don't need refcounting. |
+DISABLE_RUNNABLE_METHOD_REFCOUNT(net::NetworkChangeNotifierMac); |
+ |
namespace net { |
-namespace { |
+NetworkChangeNotifierMac::NetworkChangeNotifierMac() |
+ : notifier_thread_(new base::Thread("NetworkChangeNotifier")) { |
+ // We create this notifier thread because the notification implementation |
+ // needs a thread with a CFRunLoop, and there's no guarantee that |
+ // MessageLoop::current() meets that criterion. |
+ base::Thread::Options thread_options(MessageLoop::TYPE_UI, 0); |
+ notifier_thread_->StartWithOptions(thread_options); |
+ // TODO(willchan): Look to see if there's a better signal for when it's ok to |
+ // initialize this, rather than just delaying it by a fixed time. |
+ const int kNotifierThreadInitializationDelayMS = 1000; |
+ notifier_thread_->message_loop()->PostDelayedTask(FROM_HERE, |
+ NewRunnableMethod(this, &NetworkChangeNotifierMac::Init), |
+ kNotifierThreadInitializationDelayMS); |
+} |
-// NetworkChangeNotifierImpl should be created on a thread with a CFRunLoop, |
-// since it requires one to pump notifications. However, it also runs some |
-// methods on |notifier_loop_|, because it cannot post calls to |notifier_| |
-// since NetworkChangeNotifier is not ref counted in a thread safe manner. |
-class NetworkChangeNotifierImpl |
- : public base::RefCountedThreadSafe<NetworkChangeNotifierImpl> { |
- public: |
- NetworkChangeNotifierImpl(MessageLoop* notifier_loop, |
- NetworkChangeNotifierMac* notifier); |
+NetworkChangeNotifierMac::~NetworkChangeNotifierMac() { |
+ // We don't need to explicitly Stop(), but doing so allows us to sanity- |
+ // check that the notifier thread shut down properly. |
+ notifier_thread_->Stop(); |
+ DCHECK(run_loop_source_ == NULL); |
+} |
- void Shutdown(); |
+// static |
+void NetworkChangeNotifierMac::DynamicStoreCallback( |
+ SCDynamicStoreRef /* store */, |
+ CFArrayRef changed_keys, |
+ void* config) { |
+ NetworkChangeNotifierMac* net_config = |
+ static_cast<NetworkChangeNotifierMac*>(config); |
+ net_config->OnNetworkConfigChange(changed_keys); |
+} |
- private: |
- friend class base::RefCountedThreadSafe<NetworkChangeNotifierImpl>; |
- ~NetworkChangeNotifierImpl(); |
+void NetworkChangeNotifierMac::WillDestroyCurrentMessageLoop() { |
+ DCHECK(notifier_thread_ != NULL); |
+ // We can't check the notifier_thread_'s message_loop(), as it's now 0. |
+ // DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current()); |
- static void DynamicStoreCallback(SCDynamicStoreRef /* store */, |
- CFArrayRef changed_keys, |
- void* config); |
+ DCHECK(run_loop_source_ != NULL); |
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), run_loop_source_.get(), |
+ kCFRunLoopCommonModes); |
+ run_loop_source_.reset(); |
+} |
- void OnNetworkConfigChange(CFArrayRef changed_keys); |
+void NetworkChangeNotifierMac::Init() { |
+ DCHECK(notifier_thread_ != NULL); |
+ DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current()); |
- // Runs on |notifier_loop_|. |
- void OnIPAddressChanged(); |
- |
- // Raw pointers. Note that |notifier_| _must_ outlive the |
- // NetworkChangeNotifierImpl. For lifecycle management details, read the |
- // comment at the top of the file. |
- MessageLoop* const notifier_loop_; |
- NetworkChangeNotifierMac* notifier_; |
- |
- scoped_cftyperef<CFRunLoopSourceRef> source_; |
- |
- DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierImpl); |
-}; |
- |
-NetworkChangeNotifierImpl::NetworkChangeNotifierImpl( |
- MessageLoop* notifier_loop, NetworkChangeNotifierMac* notifier) |
- : notifier_loop_(notifier_loop), |
- notifier_(notifier) { |
- DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type()); |
+ // Add a run loop source for a dynamic store to the current run loop. |
SCDynamicStoreContext context = { |
- 0, // Version 0. |
- this, // User data. |
- NULL, // This is not reference counted. No retain function. |
- NULL, // This is not reference counted. No release function. |
- NULL, // No description for this. |
+ 0, // Version 0. |
+ this, // User data. |
+ NULL, // This is not reference counted. No retain function. |
+ NULL, // This is not reference counted. No release function. |
+ NULL, // No description for this. |
}; |
- |
- // Get a reference to the dynamic store. |
- scoped_cftyperef<SCDynamicStoreRef> store( |
- SCDynamicStoreCreate(NULL /* use default allocator */, |
- CFSTR("org.chromium"), |
- DynamicStoreCallback, &context)); |
- |
- // Create a run loop source for the dynamic store. |
- source_.reset(SCDynamicStoreCreateRunLoopSource( |
- NULL /* use default allocator */, |
- store.get(), |
- 0 /* 0 sounds like a fine source order to me! */)); |
- |
- // Add the run loop source to the current run loop. |
- CFRunLoopAddSource(CFRunLoopGetCurrent(), |
- source_.get(), |
+ scoped_cftyperef<SCDynamicStoreRef> store(SCDynamicStoreCreate( |
+ NULL, CFSTR("org.chromium"), DynamicStoreCallback, &context)); |
+ run_loop_source_.reset(SCDynamicStoreCreateRunLoopSource( |
+ NULL, store.get(), 0)); |
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), run_loop_source_.get(), |
kCFRunLoopCommonModes); |
- // Set up the notification keys. |
+ // Set up notifications for interface and IP address changes. |
scoped_cftyperef<CFMutableArrayRef> notification_keys( |
CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks)); |
- |
- // Monitor interface changes. |
- scoped_cftyperef<CFStringRef> key( |
- SCDynamicStoreKeyCreateNetworkGlobalEntity( |
- NULL /* default allocator */, kSCDynamicStoreDomainState, |
- kSCEntNetInterface)); |
+ scoped_cftyperef<CFStringRef> key(SCDynamicStoreKeyCreateNetworkGlobalEntity( |
+ NULL, kSCDynamicStoreDomainState, kSCEntNetInterface)); |
CFArrayAppendValue(notification_keys.get(), key.get()); |
- |
- // Monitor IP address changes. |
- |
key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity( |
- NULL /* default allocator */, kSCDynamicStoreDomainState, |
- kSCEntNetIPv4)); |
+ NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4)); |
CFArrayAppendValue(notification_keys.get(), key.get()); |
- |
key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity( |
- NULL /* default allocator */, kSCDynamicStoreDomainState, |
- kSCEntNetIPv6)); |
+ NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6)); |
CFArrayAppendValue(notification_keys.get(), key.get()); |
- // Ok, let's ask for notifications! |
+ // Set the notification keys. This starts us receiving notifications. |
+ bool ret = SCDynamicStoreSetNotificationKeys( |
+ store.get(), notification_keys.get(), NULL); |
// TODO(willchan): Figure out a proper way to handle this rather than crash. |
- CHECK(SCDynamicStoreSetNotificationKeys( |
- store.get(), notification_keys.get(), NULL)); |
-} |
+ CHECK(ret); |
-NetworkChangeNotifierImpl::~NetworkChangeNotifierImpl() { |
- CFRunLoopRemoveSource(CFRunLoopGetCurrent(), |
- source_.get(), |
- kCFRunLoopCommonModes); |
+ MessageLoop::current()->AddDestructionObserver(this); |
} |
-void NetworkChangeNotifierImpl::Shutdown() { |
- CHECK(notifier_); |
- notifier_ = NULL; |
-} |
+void NetworkChangeNotifierMac::OnNetworkConfigChange(CFArrayRef changed_keys) { |
+ DCHECK(notifier_thread_ != NULL); |
+ DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current()); |
-// static |
-void NetworkChangeNotifierImpl::DynamicStoreCallback( |
- SCDynamicStoreRef /* store */, |
- CFArrayRef changed_keys, |
- void* config) { |
- NetworkChangeNotifierImpl* net_config = |
- static_cast<NetworkChangeNotifierImpl*>(config); |
- net_config->OnNetworkConfigChange(changed_keys); |
-} |
- |
-void NetworkChangeNotifierImpl::OnNetworkConfigChange(CFArrayRef changed_keys) { |
for (CFIndex i = 0; i < CFArrayGetCount(changed_keys); ++i) { |
CFStringRef key = static_cast<CFStringRef>( |
CFArrayGetValueAtIndex(changed_keys, i)); |
if (CFStringHasSuffix(key, kSCEntNetIPv4) || |
CFStringHasSuffix(key, kSCEntNetIPv6)) { |
- notifier_loop_->PostTask( |
- FROM_HERE, |
- NewRunnableMethod( |
- this, |
- &NetworkChangeNotifierImpl::OnIPAddressChanged)); |
- } else if (CFStringHasSuffix(key, kSCEntNetInterface)) { |
+ NotifyObserversOfIPAddressChange(); |
+ return; |
+ } |
+ if (CFStringHasSuffix(key, kSCEntNetInterface)) { |
// TODO(willchan): Does not appear to be working. Look into this. |
// Perhaps this isn't needed anyway. |
} else { |
@@ -172,91 +120,4 @@ |
} |
} |
-void NetworkChangeNotifierImpl::OnIPAddressChanged() { |
- // If |notifier_| doesn't exist, then that means we're shutting down, so |
- // notifications are all cancelled. |
- if (notifier_) |
- notifier_->OnIPAddressChanged(); |
-} |
- |
-class NetworkChangeNotifierThread : public base::Thread { |
- public: |
- NetworkChangeNotifierThread(MessageLoop* notifier_loop, |
- NetworkChangeNotifierMac* notifier); |
- ~NetworkChangeNotifierThread(); |
- |
- protected: |
- virtual void Init(); |
- |
- private: |
- MessageLoop* const notifier_loop_; |
- NetworkChangeNotifierMac* const notifier_; |
- scoped_refptr<NetworkChangeNotifierImpl> notifier_impl_; |
- |
- DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierThread); |
-}; |
- |
-NetworkChangeNotifierThread::NetworkChangeNotifierThread( |
- MessageLoop* notifier_loop, NetworkChangeNotifierMac* notifier) |
- : base::Thread("NetworkChangeNotifier"), |
- notifier_loop_(notifier_loop), |
- notifier_(notifier) {} |
- |
-NetworkChangeNotifierThread::~NetworkChangeNotifierThread() { |
- notifier_impl_->Shutdown(); |
- Stop(); |
-} |
- |
-// Note that |notifier_impl_| is initialized on the network change |
-// notifier thread, not whatever thread constructs the |
-// NetworkChangeNotifierThread object. This is important, because this thread |
-// is the one that has a CFRunLoop. |
-void NetworkChangeNotifierThread::Init() { |
- notifier_impl_ = |
- new NetworkChangeNotifierImpl(notifier_loop_, notifier_); |
-} |
- |
-} // namespace |
- |
-NetworkChangeNotifierMac::NetworkChangeNotifierMac() |
- : notifier_thread_(NULL), |
- method_factory_(this) { |
- // TODO(willchan): Look to see if there's a better signal for when it's ok to |
- // initialize this, rather than just delaying it by a fixed time. |
- const int kNotifierThreadInitializationDelayMS = 1000; |
- MessageLoop* loop = MessageLoop::current(); |
- loop->PostDelayedTask( |
- FROM_HERE, |
- method_factory_.NewRunnableMethod( |
- &NetworkChangeNotifierMac::InitializeNotifierThread, loop), |
- kNotifierThreadInitializationDelayMS); |
-} |
- |
-void NetworkChangeNotifierMac::OnIPAddressChanged() { |
- DCHECK(CalledOnValidThread()); |
- FOR_EACH_OBSERVER(Observer, observers_, OnIPAddressChanged()); |
-} |
- |
-void NetworkChangeNotifierMac::AddObserver(Observer* observer) { |
- DCHECK(CalledOnValidThread()); |
- observers_.AddObserver(observer); |
-} |
- |
-void NetworkChangeNotifierMac::RemoveObserver(Observer* observer) { |
- DCHECK(CalledOnValidThread()); |
- observers_.RemoveObserver(observer); |
-} |
- |
-NetworkChangeNotifierMac::~NetworkChangeNotifierMac() { |
- DCHECK(CalledOnValidThread()); |
-} |
- |
-void NetworkChangeNotifierMac::InitializeNotifierThread(MessageLoop* loop) { |
- DCHECK(CalledOnValidThread()); |
- notifier_thread_.reset(new NetworkChangeNotifierThread(loop, this)); |
- base::Thread::Options thread_options; |
- thread_options.message_loop_type = MessageLoop::TYPE_UI; |
- notifier_thread_->StartWithOptions(thread_options); |
-} |
- |
} // namespace net |