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 |