OLD | NEW |
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 <cmath> | 5 #include <cmath> |
6 #include <set> | 6 #include <set> |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/message_loop.h" | 11 #include "base/message_loop.h" |
12 #include "base/threading/thread.h" | 12 #include "base/threading/thread.h" |
13 #include "base/threading/thread_restrictions.h" | 13 #include "base/threading/worker_pool.h" |
14 #include "content/browser/device_orientation/orientation.h" | 14 #include "content/browser/device_orientation/orientation.h" |
15 #include "content/browser/device_orientation/provider_impl.h" | 15 #include "content/browser/device_orientation/provider_impl.h" |
16 | 16 |
| 17 namespace { |
| 18 |
| 19 bool IsElementSignificantlyDifferent(bool can_provide_element1, |
| 20 bool can_provide_element2, |
| 21 double element1, |
| 22 double element2) { |
| 23 const double kThreshold = 0.1; |
| 24 |
| 25 if (can_provide_element1 != can_provide_element2) |
| 26 return true; |
| 27 if (can_provide_element1 && |
| 28 std::fabs(element1 - element2) >= kThreshold) |
| 29 return true; |
| 30 return false; |
| 31 } |
| 32 |
| 33 void DeleteThread(base::Thread* thread) { |
| 34 delete thread; |
| 35 } |
| 36 |
| 37 } |
| 38 |
17 namespace device_orientation { | 39 namespace device_orientation { |
18 | 40 |
19 ProviderImpl::ProviderImpl(const DataFetcherFactory factories[]) | 41 class ProviderImpl::PollingThread : public base::Thread { |
20 : creator_loop_(MessageLoop::current()), | 42 public: |
21 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { | 43 PollingThread(const char* name, |
22 for (const DataFetcherFactory* fp = factories; *fp; ++fp) | 44 base::WeakPtr<ProviderImpl> provider, |
23 factories_.push_back(*fp); | 45 MessageLoop* creator_loop); |
| 46 virtual ~PollingThread(); |
| 47 |
| 48 // Method for finding a suitable DataFetcher and starting the polling. |
| 49 void Initialize(const std::vector<DataFetcherFactory>& factories); |
| 50 |
| 51 private: |
| 52 // Method for polling a DataFetcher. |
| 53 void DoPoll(); |
| 54 void ScheduleDoPoll(); |
| 55 |
| 56 // Schedule a notification to the |provider_| which lives on a different |
| 57 // thread (|creator_loop_| is its message loop). |
| 58 void ScheduleDoNotify(const Orientation& orientation); |
| 59 |
| 60 static bool SignificantlyDifferent(const Orientation& orientation1, |
| 61 const Orientation& orientation2); |
| 62 |
| 63 enum { kDesiredSamplingIntervalMs = 100 }; |
| 64 base::TimeDelta SamplingInterval() const; |
| 65 |
| 66 // The Message Loop on which this object was created. |
| 67 // Typically the I/O loop, but may be something else during testing. |
| 68 MessageLoop* creator_loop_; |
| 69 |
| 70 scoped_ptr<DataFetcher> data_fetcher_; |
| 71 Orientation last_orientation_; |
| 72 |
| 73 base::WeakPtr<ProviderImpl> provider_; |
| 74 }; |
| 75 |
| 76 ProviderImpl::PollingThread::PollingThread( |
| 77 const char* name, |
| 78 base::WeakPtr<ProviderImpl> provider, |
| 79 MessageLoop* creator_loop) |
| 80 : base::Thread(name), |
| 81 creator_loop_(creator_loop), |
| 82 provider_(provider) { |
24 } | 83 } |
25 | 84 |
26 ProviderImpl::~ProviderImpl() { | 85 ProviderImpl::PollingThread::~PollingThread() { |
27 } | 86 } |
28 | 87 |
29 void ProviderImpl::AddObserver(Observer* observer) { | 88 void ProviderImpl::PollingThread::Initialize( |
30 DCHECK(MessageLoop::current() == creator_loop_); | |
31 | |
32 observers_.insert(observer); | |
33 if (observers_.size() == 1) | |
34 Start(); | |
35 else | |
36 observer->OnOrientationUpdate(last_notification_); | |
37 } | |
38 | |
39 void ProviderImpl::RemoveObserver(Observer* observer) { | |
40 DCHECK(MessageLoop::current() == creator_loop_); | |
41 | |
42 observers_.erase(observer); | |
43 if (observers_.empty()) | |
44 Stop(); | |
45 } | |
46 | |
47 void ProviderImpl::Start() { | |
48 DCHECK(MessageLoop::current() == creator_loop_); | |
49 DCHECK(!polling_thread_.get()); | |
50 | |
51 polling_thread_.reset(new base::Thread("Device orientation polling thread")); | |
52 if (!polling_thread_->Start()) { | |
53 LOG(ERROR) << "Failed to start device orientation polling thread"; | |
54 polling_thread_.reset(); | |
55 return; | |
56 } | |
57 ScheduleInitializePollingThread(); | |
58 } | |
59 | |
60 void ProviderImpl::Stop() { | |
61 DCHECK(MessageLoop::current() == creator_loop_); | |
62 | |
63 // TODO(hans): Don't join the thread. See crbug.com/72286. | |
64 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
65 | |
66 polling_thread_.reset(); | |
67 data_fetcher_.reset(); | |
68 } | |
69 | |
70 void ProviderImpl::DoInitializePollingThread( | |
71 const std::vector<DataFetcherFactory>& factories) { | 89 const std::vector<DataFetcherFactory>& factories) { |
72 DCHECK(MessageLoop::current() == polling_thread_->message_loop()); | 90 DCHECK(MessageLoop::current() == message_loop()); |
73 | 91 |
74 typedef std::vector<DataFetcherFactory>::const_iterator Iterator; | 92 typedef std::vector<DataFetcherFactory>::const_iterator Iterator; |
75 for (Iterator i = factories_.begin(), e = factories_.end(); i != e; ++i) { | 93 for (Iterator i = factories.begin(); i != factories.end(); ++i) { |
76 DataFetcherFactory factory = *i; | 94 DataFetcherFactory factory = *i; |
77 scoped_ptr<DataFetcher> fetcher(factory()); | 95 scoped_ptr<DataFetcher> fetcher(factory()); |
78 Orientation orientation; | 96 Orientation orientation; |
79 | 97 |
80 if (fetcher.get() && fetcher->GetOrientation(&orientation)) { | 98 if (fetcher.get() && fetcher->GetOrientation(&orientation)) { |
81 // Pass ownership of fetcher to provider_. | 99 // Pass ownership of fetcher to provider_. |
82 data_fetcher_.swap(fetcher); | 100 data_fetcher_.swap(fetcher); |
83 last_orientation_ = orientation; | 101 last_orientation_ = orientation; |
84 | 102 |
85 // Notify observers. | 103 // Notify observers. |
86 if (!orientation.is_empty()) | 104 if (!orientation.is_empty()) |
87 ScheduleDoNotify(orientation); | 105 ScheduleDoNotify(orientation); |
88 | 106 |
89 // Start polling. | 107 // Start polling. |
90 ScheduleDoPoll(); | 108 ScheduleDoPoll(); |
91 return; | 109 return; |
92 } | 110 } |
93 } | 111 } |
94 | 112 |
95 // When no orientation data can be provided. | 113 // When no orientation data can be provided. |
96 ScheduleDoNotify(Orientation::Empty()); | 114 ScheduleDoNotify(Orientation::Empty()); |
97 } | 115 } |
98 | 116 |
99 void ProviderImpl::ScheduleInitializePollingThread() { | 117 void ProviderImpl::PollingThread::ScheduleDoNotify( |
100 DCHECK(MessageLoop::current() == creator_loop_); | 118 const Orientation& orientation) { |
| 119 DCHECK(MessageLoop::current() == message_loop()); |
101 | 120 |
102 MessageLoop* polling_loop = polling_thread_->message_loop(); | 121 creator_loop_->PostTask( |
103 polling_loop->PostTask(FROM_HERE, | 122 FROM_HERE, base::Bind(&ProviderImpl::DoNotify, provider_, orientation)); |
104 base::Bind(&ProviderImpl::DoInitializePollingThread, | |
105 this, | |
106 factories_)); | |
107 } | 123 } |
108 | 124 |
109 void ProviderImpl::DoNotify(const Orientation& orientation) { | 125 void ProviderImpl::PollingThread::DoPoll() { |
110 DCHECK(MessageLoop::current() == creator_loop_); | 126 DCHECK(MessageLoop::current() == message_loop()); |
111 | |
112 last_notification_ = orientation; | |
113 | |
114 typedef std::set<Observer*>::const_iterator Iterator; | |
115 for (Iterator i = observers_.begin(), e = observers_.end(); i != e; ++i) | |
116 (*i)->OnOrientationUpdate(orientation); | |
117 | |
118 if (orientation.is_empty()) { | |
119 // Notify observers about failure to provide data exactly once. | |
120 observers_.clear(); | |
121 Stop(); | |
122 } | |
123 } | |
124 | |
125 void ProviderImpl::ScheduleDoNotify(const Orientation& orientation) { | |
126 DCHECK(MessageLoop::current() == polling_thread_->message_loop()); | |
127 | |
128 creator_loop_->PostTask( | |
129 FROM_HERE, base::Bind(&ProviderImpl::DoNotify, this, orientation)); | |
130 } | |
131 | |
132 void ProviderImpl::DoPoll() { | |
133 DCHECK(MessageLoop::current() == polling_thread_->message_loop()); | |
134 | 127 |
135 Orientation orientation; | 128 Orientation orientation; |
136 if (!data_fetcher_->GetOrientation(&orientation)) { | 129 if (!data_fetcher_->GetOrientation(&orientation)) { |
137 LOG(ERROR) << "Failed to poll device orientation data fetcher."; | 130 LOG(ERROR) << "Failed to poll device orientation data fetcher."; |
138 | 131 |
139 ScheduleDoNotify(Orientation::Empty()); | 132 ScheduleDoNotify(Orientation::Empty()); |
140 return; | 133 return; |
141 } | 134 } |
142 | 135 |
143 if (!orientation.is_empty() && | 136 if (!orientation.is_empty() && |
144 SignificantlyDifferent(orientation, last_orientation_)) { | 137 SignificantlyDifferent(orientation, last_orientation_)) { |
145 last_orientation_ = orientation; | 138 last_orientation_ = orientation; |
146 ScheduleDoNotify(orientation); | 139 ScheduleDoNotify(orientation); |
147 } | 140 } |
148 | 141 |
149 ScheduleDoPoll(); | 142 ScheduleDoPoll(); |
150 } | 143 } |
151 | 144 |
152 void ProviderImpl::ScheduleDoPoll() { | 145 void ProviderImpl::PollingThread::ScheduleDoPoll() { |
153 DCHECK(MessageLoop::current() == polling_thread_->message_loop()); | 146 DCHECK(MessageLoop::current() == message_loop()); |
154 | 147 |
155 MessageLoop* polling_loop = polling_thread_->message_loop(); | 148 message_loop()->PostDelayedTask( |
156 polling_loop->PostDelayedTask( | |
157 FROM_HERE, | 149 FROM_HERE, |
158 base::Bind(&ProviderImpl::DoPoll, weak_factory_.GetWeakPtr()), | 150 base::Bind(&PollingThread::DoPoll, base::Unretained(this)), |
159 SamplingInterval()); | 151 SamplingInterval()); |
160 } | 152 } |
161 | 153 |
162 namespace { | |
163 | |
164 bool IsElementSignificantlyDifferent(bool can_provide_element1, | |
165 bool can_provide_element2, | |
166 double element1, | |
167 double element2) { | |
168 const double kThreshold = 0.1; | |
169 | |
170 if (can_provide_element1 != can_provide_element2) | |
171 return true; | |
172 if (can_provide_element1 && | |
173 std::fabs(element1 - element2) >= kThreshold) | |
174 return true; | |
175 return false; | |
176 } | |
177 } // namespace | |
178 | |
179 // Returns true if two orientations are considered different enough that | 154 // Returns true if two orientations are considered different enough that |
180 // observers should be notified of the new orientation. | 155 // observers should be notified of the new orientation. |
181 bool ProviderImpl::SignificantlyDifferent(const Orientation& o1, | 156 bool ProviderImpl::PollingThread::SignificantlyDifferent( |
182 const Orientation& o2) { | 157 const Orientation& o1, |
| 158 const Orientation& o2) { |
183 return IsElementSignificantlyDifferent(o1.can_provide_alpha(), | 159 return IsElementSignificantlyDifferent(o1.can_provide_alpha(), |
184 o2.can_provide_alpha(), | 160 o2.can_provide_alpha(), |
185 o1.alpha(), | 161 o1.alpha(), |
186 o2.alpha()) || | 162 o2.alpha()) || |
187 IsElementSignificantlyDifferent(o1.can_provide_beta(), | 163 IsElementSignificantlyDifferent(o1.can_provide_beta(), |
188 o2.can_provide_beta(), | 164 o2.can_provide_beta(), |
189 o1.beta(), | 165 o1.beta(), |
190 o2.beta()) || | 166 o2.beta()) || |
191 IsElementSignificantlyDifferent(o1.can_provide_gamma(), | 167 IsElementSignificantlyDifferent(o1.can_provide_gamma(), |
192 o2.can_provide_gamma(), | 168 o2.can_provide_gamma(), |
193 o1.gamma(), | 169 o1.gamma(), |
194 o2.gamma()) || | 170 o2.gamma()) || |
195 (o1.can_provide_absolute() != o2.can_provide_absolute() || | 171 (o1.can_provide_absolute() != o2.can_provide_absolute() || |
196 o1.absolute() != o2.absolute()); | 172 o1.absolute() != o2.absolute()); |
197 } | 173 } |
198 | 174 |
199 base::TimeDelta ProviderImpl::SamplingInterval() const { | 175 base::TimeDelta ProviderImpl::PollingThread::SamplingInterval() const { |
200 DCHECK(MessageLoop::current() == polling_thread_->message_loop()); | 176 DCHECK(MessageLoop::current() == message_loop()); |
201 DCHECK(data_fetcher_.get()); | 177 DCHECK(data_fetcher_.get()); |
202 | 178 |
203 // TODO(erg): There used to be unused code here, that called a default | 179 // TODO(erg): There used to be unused code here, that called a default |
204 // implementation on the DataFetcherInterface that was never defined. I'm | 180 // implementation on the DataFetcherInterface that was never defined. I'm |
205 // removing unused methods from headers. | 181 // removing unused methods from headers. |
206 return base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs); | 182 return base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs); |
207 } | 183 } |
208 | 184 |
| 185 ProviderImpl::ProviderImpl(const DataFetcherFactory factories[]) |
| 186 : creator_loop_(MessageLoop::current()), |
| 187 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), |
| 188 polling_thread_(NULL) { |
| 189 for (const DataFetcherFactory* fp = factories; *fp; ++fp) |
| 190 factories_.push_back(*fp); |
| 191 } |
| 192 |
| 193 ProviderImpl::~ProviderImpl() { |
| 194 Stop(); |
| 195 } |
| 196 |
| 197 void ProviderImpl::AddObserver(Observer* observer) { |
| 198 DCHECK(MessageLoop::current() == creator_loop_); |
| 199 |
| 200 observers_.insert(observer); |
| 201 if (observers_.size() == 1) |
| 202 Start(); |
| 203 else |
| 204 observer->OnOrientationUpdate(last_notification_); |
| 205 } |
| 206 |
| 207 void ProviderImpl::RemoveObserver(Observer* observer) { |
| 208 DCHECK(MessageLoop::current() == creator_loop_); |
| 209 |
| 210 observers_.erase(observer); |
| 211 if (observers_.empty()) |
| 212 Stop(); |
| 213 } |
| 214 |
| 215 void ProviderImpl::Start() { |
| 216 DCHECK(MessageLoop::current() == creator_loop_); |
| 217 DCHECK(!polling_thread_); |
| 218 |
| 219 polling_thread_ = new PollingThread("Device orientation polling thread", |
| 220 weak_factory_.GetWeakPtr(), |
| 221 creator_loop_); |
| 222 if (!polling_thread_->Start()) { |
| 223 LOG(ERROR) << "Failed to start device orientation polling thread"; |
| 224 delete polling_thread_; |
| 225 polling_thread_ = NULL; |
| 226 return; |
| 227 } |
| 228 ScheduleInitializePollingThread(); |
| 229 } |
| 230 |
| 231 void ProviderImpl::Stop() { |
| 232 DCHECK(MessageLoop::current() == creator_loop_); |
| 233 |
| 234 weak_factory_.InvalidateWeakPtrs(); |
| 235 if (polling_thread_) { |
| 236 polling_thread_->StopSoon(); |
| 237 bool posted = base::WorkerPool::PostTask( |
| 238 FROM_HERE, |
| 239 base::Bind(&DeleteThread, base::Unretained(polling_thread_)), |
| 240 true /* task is slow */); |
| 241 DCHECK(posted); |
| 242 polling_thread_ = NULL; |
| 243 } |
| 244 } |
| 245 |
| 246 void ProviderImpl::ScheduleInitializePollingThread() { |
| 247 DCHECK(MessageLoop::current() == creator_loop_); |
| 248 |
| 249 MessageLoop* polling_loop = polling_thread_->message_loop(); |
| 250 polling_loop->PostTask(FROM_HERE, |
| 251 base::Bind(&PollingThread::Initialize, |
| 252 base::Unretained(polling_thread_), |
| 253 factories_)); |
| 254 } |
| 255 |
| 256 void ProviderImpl::DoNotify(const Orientation& orientation) { |
| 257 DCHECK(MessageLoop::current() == creator_loop_); |
| 258 |
| 259 last_notification_ = orientation; |
| 260 |
| 261 typedef std::set<Observer*>::const_iterator Iterator; |
| 262 for (Iterator i = observers_.begin(); i != observers_.end(); ++i) |
| 263 (*i)->OnOrientationUpdate(orientation); |
| 264 |
| 265 if (orientation.is_empty()) { |
| 266 // Notify observers about failure to provide data exactly once. |
| 267 observers_.clear(); |
| 268 Stop(); |
| 269 } |
| 270 } |
| 271 |
209 } // namespace device_orientation | 272 } // namespace device_orientation |
OLD | NEW |