| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 // Some notes that may help anyone trying to reason about this code: | |
| 6 // | |
| 7 // - The source thread must be guaranteed to outlive the target | |
| 8 // thread. This is so that it is guaranteed that any task posted to | |
| 9 // the source thread will eventually be run. In particular, we want | |
| 10 // to make sure that Detach() causes us to eventually be removed as | |
| 11 // an observer. | |
| 12 // | |
| 13 // Note that this implies that any task posted to the target thread | |
| 14 // from the source thread may not run. But we post only | |
| 15 // TargetObserverOnIPAddressChanged() tasks on the target thread, | |
| 16 // which we can safely drop. | |
| 17 // | |
| 18 // - The source NetworkChangeNotifier must be guaranteed to outlive | |
| 19 // the target thread. This is so that it is guaranteed that any | |
| 20 // task posted to the source thread can safely access the source | |
| 21 // NetworkChangeNotifier. | |
| 22 // | |
| 23 // - Ref-counting this class is necessary, although as a consequence | |
| 24 // we can't make any guarantees about which thread will destroy an | |
| 25 // instance. Earlier versions of this class tried to get away | |
| 26 // without ref-counting. One version deleted the class in | |
| 27 // Unobserve(); this didn't work because there may still be | |
| 28 // TargetObserverOnIPAddressChanged() tasks on the target thread. | |
| 29 // An attempt to fix this was to post a DeleteTask on the target | |
| 30 // thread from Unobserve(), but this meant that there would be no | |
| 31 // way of knowing when on the target thread the instance would be | |
| 32 // deleted. Indeed, as mentioned above, any tasks posted on the | |
| 33 // target thread may not run, so this introduced the possibility of | |
| 34 // a memory leak. | |
| 35 // | |
| 36 // - It is important that all posted tasks that work with a proxy be | |
| 37 // RunnableMethods so that the ref-counting guarantees that the | |
| 38 // proxy is still valid when the task runs. | |
| 39 | |
| 40 #include "chrome/common/net/network_change_observer_proxy.h" | |
| 41 | |
| 42 #include <cstddef> | |
| 43 | |
| 44 #include "base/logging.h" | |
| 45 #include "base/message_loop.h" | |
| 46 #include "base/task.h" | |
| 47 #include "chrome/common/net/network_change_notifier_thread.h" | |
| 48 #include "net/base/network_change_notifier.h" | |
| 49 | |
| 50 namespace chrome_common_net { | |
| 51 | |
| 52 NetworkChangeObserverProxy::NetworkChangeObserverProxy( | |
| 53 const NetworkChangeNotifierThread* source_thread, | |
| 54 MessageLoop* target_message_loop) | |
| 55 : source_thread_(source_thread), | |
| 56 target_message_loop_(target_message_loop), | |
| 57 target_observer_(NULL) { | |
| 58 DCHECK(source_thread_); | |
| 59 MessageLoop* source_message_loop = source_thread_->GetMessageLoop(); | |
| 60 DCHECK(source_message_loop); | |
| 61 DCHECK(target_message_loop_); | |
| 62 DCHECK_NE(source_message_loop, target_message_loop_); | |
| 63 DCHECK_EQ(MessageLoop::current(), target_message_loop_); | |
| 64 } | |
| 65 | |
| 66 void NetworkChangeObserverProxy::Attach( | |
| 67 net::NetworkChangeNotifier::Observer* target_observer) { | |
| 68 DCHECK_EQ(MessageLoop::current(), target_message_loop_); | |
| 69 DCHECK(!target_observer_); | |
| 70 target_observer_ = target_observer; | |
| 71 DCHECK(target_observer_); | |
| 72 source_thread_->GetMessageLoop()->PostTask( | |
| 73 FROM_HERE, | |
| 74 NewRunnableMethod(this, &NetworkChangeObserverProxy::Observe)); | |
| 75 } | |
| 76 | |
| 77 void NetworkChangeObserverProxy::Detach() { | |
| 78 DCHECK_EQ(MessageLoop::current(), target_message_loop_); | |
| 79 DCHECK(target_observer_); | |
| 80 target_observer_ = NULL; | |
| 81 source_thread_->GetMessageLoop()->PostTask( | |
| 82 FROM_HERE, | |
| 83 NewRunnableMethod(this, &NetworkChangeObserverProxy::Unobserve)); | |
| 84 } | |
| 85 | |
| 86 NetworkChangeObserverProxy::~NetworkChangeObserverProxy() { | |
| 87 MessageLoop* current_message_loop = MessageLoop::current(); | |
| 88 // We can be deleted on either the source or target thread, so the | |
| 89 // best we can do is check that we're on either. | |
| 90 DCHECK((current_message_loop == source_thread_->GetMessageLoop()) || | |
| 91 (current_message_loop == target_message_loop_)); | |
| 92 // Even though only the target thread uses target_observer_, it | |
| 93 // should still be unset even if we're on the source thread; posting | |
| 94 // a task is effectively a memory barrier. | |
| 95 DCHECK(!target_observer_); | |
| 96 } | |
| 97 | |
| 98 void NetworkChangeObserverProxy::Observe() { | |
| 99 DCHECK_EQ(MessageLoop::current(), source_thread_->GetMessageLoop()); | |
| 100 net::NetworkChangeNotifier* source_network_change_notifier = | |
| 101 source_thread_->GetNetworkChangeNotifier(); | |
| 102 DCHECK(source_network_change_notifier); | |
| 103 source_network_change_notifier->AddObserver(this); | |
| 104 } | |
| 105 | |
| 106 // The Task from which this was called may hold the last reference to | |
| 107 // us (this is how we can get deleted on the source thread). | |
| 108 void NetworkChangeObserverProxy::Unobserve() { | |
| 109 DCHECK_EQ(MessageLoop::current(), source_thread_->GetMessageLoop()); | |
| 110 net::NetworkChangeNotifier* source_network_change_notifier = | |
| 111 source_thread_->GetNetworkChangeNotifier(); | |
| 112 DCHECK(source_network_change_notifier); | |
| 113 source_network_change_notifier->RemoveObserver(this); | |
| 114 } | |
| 115 | |
| 116 // Although we may get this event after Detach() has been called on | |
| 117 // the target thread, we know that Unobserve() hasn't been called yet. | |
| 118 // But we know that it has been posted, so it at least holds a | |
| 119 // reference to us. | |
| 120 void NetworkChangeObserverProxy::OnIPAddressChanged() { | |
| 121 DCHECK_EQ(MessageLoop::current(), source_thread_->GetMessageLoop()); | |
| 122 target_message_loop_->PostTask( | |
| 123 FROM_HERE, | |
| 124 NewRunnableMethod( | |
| 125 this, | |
| 126 &NetworkChangeObserverProxy::TargetObserverOnIPAddressChanged)); | |
| 127 } | |
| 128 | |
| 129 // The Task from which this was called may hold the last reference to | |
| 130 // us (this is how we can get deleted on the target thread). | |
| 131 void NetworkChangeObserverProxy::TargetObserverOnIPAddressChanged() { | |
| 132 DCHECK_EQ(MessageLoop::current(), target_message_loop_); | |
| 133 if (target_observer_) | |
| 134 target_observer_->OnIPAddressChanged(); | |
| 135 } | |
| 136 | |
| 137 } // namespace chrome_common_net | |
| OLD | NEW |