OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 "chromecast/base/device_capabilities_impl.h" | 5 #include "chromecast/base/device_capabilities_impl.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "base/single_thread_task_runner.h" | |
9 #include "base/thread_task_runner_handle.h" | |
8 #include "base/values.h" | 10 #include "base/values.h" |
9 #include "chromecast/base/serializers.h" | 11 #include "chromecast/base/serializers.h" |
10 | 12 |
11 namespace chromecast { | 13 namespace chromecast { |
12 | 14 |
13 namespace { | 15 namespace { |
14 | 16 |
15 const char kPathSeparator = '.'; | 17 const char kPathSeparator = '.'; |
16 | 18 |
17 // Determines if a key passed to Register() is valid. No path separators can | 19 // Determines if a key passed to Register() is valid. No path separators can |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
60 } | 62 } |
61 | 63 |
62 DeviceCapabilities::Validator::Validator(DeviceCapabilities* capabilities) | 64 DeviceCapabilities::Validator::Validator(DeviceCapabilities* capabilities) |
63 : capabilities_(capabilities) { | 65 : capabilities_(capabilities) { |
64 DCHECK(capabilities); | 66 DCHECK(capabilities); |
65 } | 67 } |
66 | 68 |
67 void DeviceCapabilities::Validator::SetValidatedValue( | 69 void DeviceCapabilities::Validator::SetValidatedValue( |
68 const std::string& path, | 70 const std::string& path, |
69 scoped_ptr<base::Value> new_value) const { | 71 scoped_ptr<base::Value> new_value) const { |
70 capabilities_->SetValidatedValueInternal(path, new_value.Pass()); | 72 capabilities_->SetValidatedValue(path, new_value.Pass()); |
73 } | |
74 | |
75 DeviceCapabilitiesImpl::ImmutableCapabilitiesData::ImmutableCapabilitiesData() | |
76 : dictionary_(new base::DictionaryValue), | |
77 json_string_(SerializeToJson(*dictionary_)) { | |
78 DCHECK(json_string_.get()); | |
79 } | |
80 | |
81 DeviceCapabilitiesImpl::ImmutableCapabilitiesData::ImmutableCapabilitiesData( | |
82 scoped_ptr<const base::DictionaryValue> dictionary) | |
83 : dictionary_(dictionary.Pass()), | |
84 json_string_(SerializeToJson(*dictionary_)) { | |
85 DCHECK(dictionary_.get()); | |
86 DCHECK(json_string_.get()); | |
87 } | |
88 | |
89 DeviceCapabilitiesImpl::ImmutableCapabilitiesData:: | |
90 ~ImmutableCapabilitiesData() {} | |
91 | |
92 DeviceCapabilitiesImpl::ValidatorInfo::ValidatorInfo(Validator* validator) | |
93 : validator_(validator), task_runner_(base::ThreadTaskRunnerHandle::Get()) { | |
94 DCHECK(validator_); | |
95 DCHECK(task_runner_.get()); | |
96 } | |
97 | |
98 DeviceCapabilitiesImpl::ValidatorInfo::~ValidatorInfo() { | |
99 // Check that ValidatorInfo is being destroyed on the same thread that it was | |
100 // constructed on. | |
101 DCHECK(task_runner_->BelongsToCurrentThread()); | |
102 } | |
103 | |
104 void DeviceCapabilitiesImpl::ValidatorInfo::Validate( | |
105 const std::string& path, | |
106 scoped_ptr<base::Value> proposed_value) const { | |
107 // Check that we are running Validate on the same thread that ValidatorInfo | |
108 // was constructed on. | |
109 DCHECK(task_runner_->BelongsToCurrentThread()); | |
110 validator_->Validate(path, proposed_value.Pass()); | |
71 } | 111 } |
72 | 112 |
73 DeviceCapabilitiesImpl::DeviceCapabilitiesImpl() | 113 DeviceCapabilitiesImpl::DeviceCapabilitiesImpl() |
74 : capabilities_(new base::DictionaryValue), | 114 : capabilities_data_(new ImmutableCapabilitiesData), |
75 capabilities_str_(SerializeToJson(*capabilities_)) { | 115 task_runner_for_writes_(base::ThreadTaskRunnerHandle::Get()), |
76 DCHECK(capabilities_str_.get()); | 116 observer_list_(new base::ObserverListThreadSafe<Observer>) { |
117 DCHECK(task_runner_for_writes_.get()); | |
77 } | 118 } |
78 | 119 |
79 DeviceCapabilitiesImpl::~DeviceCapabilitiesImpl() { | 120 DeviceCapabilitiesImpl::~DeviceCapabilitiesImpl() { |
80 DCHECK(thread_checker_.CalledOnValidThread()); | |
81 // Make sure that any registered Validators have unregistered at this point | 121 // Make sure that any registered Validators have unregistered at this point |
82 DCHECK(validator_map_.empty()); | 122 DCHECK(validator_map_.empty()); |
123 // Make sure that all observers have been removed at this point | |
124 observer_list_->AssertEmpty(); | |
83 } | 125 } |
84 | 126 |
85 void DeviceCapabilitiesImpl::Register(const std::string& key, | 127 void DeviceCapabilitiesImpl::Register(const std::string& key, |
86 Validator* validator) { | 128 Validator* validator) { |
87 DCHECK(thread_checker_.CalledOnValidThread()); | |
88 DCHECK(IsValidRegisterKey(key)); | 129 DCHECK(IsValidRegisterKey(key)); |
89 DCHECK(validator); | 130 DCHECK(validator); |
90 | 131 |
91 bool added = validator_map_.insert(std::make_pair(key, validator)).second; | 132 base::AutoLock auto_lock(validation_lock_); |
133 bool added = | |
134 validator_map_.insert(std::make_pair(key, new ValidatorInfo(validator))) | |
135 .second; | |
92 // Check that a validator has not already been registered for this key | 136 // Check that a validator has not already been registered for this key |
93 DCHECK(added); | 137 DCHECK(added); |
94 } | 138 } |
95 | 139 |
96 void DeviceCapabilitiesImpl::Unregister(const std::string& key, | 140 void DeviceCapabilitiesImpl::Unregister(const std::string& key, |
97 const Validator* validator) { | 141 const Validator* validator) { |
98 DCHECK(thread_checker_.CalledOnValidThread()); | 142 base::AutoLock auto_lock(validation_lock_); |
143 auto validator_it = validator_map_.find(key); | |
144 DCHECK(validator_it != validator_map_.end()); | |
99 // Check that validator being unregistered matches the original for |key|. | 145 // Check that validator being unregistered matches the original for |key|. |
100 // This prevents managers from accidentally unregistering incorrect | 146 // This prevents managers from accidentally unregistering incorrect |
101 // validators. | 147 // validators. |
102 DCHECK_EQ(validator, GetValidator(key)); | 148 DCHECK_EQ(validator, validator_it->second->validator()); |
103 bool erased = validator_map_.erase(key); | 149 // Check that validator is unregistering on same thread that it was |
104 DCHECK(erased); | 150 // registered on |
151 DCHECK(validator_it->second->task_runner()->BelongsToCurrentThread()); | |
152 delete validator_it->second; | |
byungchul
2015/10/28 01:09:43
Use base::ScopedPtrHashMap
esum
2015/10/28 04:16:27
Thanks didn't know about this. Just what I need. D
| |
153 validator_map_.erase(validator_it); | |
105 } | 154 } |
106 | 155 |
107 DeviceCapabilities::Validator* DeviceCapabilitiesImpl::GetValidator( | 156 DeviceCapabilities::Validator* DeviceCapabilitiesImpl::GetValidator( |
108 const std::string& key) const { | 157 const std::string& key) const { |
158 base::AutoLock auto_lock(validation_lock_); | |
109 auto validator_it = validator_map_.find(key); | 159 auto validator_it = validator_map_.find(key); |
110 return validator_it == validator_map_.end() ? nullptr : validator_it->second; | 160 return validator_it == validator_map_.end() |
161 ? nullptr | |
162 : validator_it->second->validator(); | |
111 } | 163 } |
112 | 164 |
113 bool DeviceCapabilitiesImpl::BluetoothSupported() const { | 165 bool DeviceCapabilitiesImpl::BluetoothSupported() const { |
114 DCHECK(thread_checker_.CalledOnValidThread()); | 166 scoped_refptr<ImmutableCapabilitiesData> capabilities_data_ref = |
167 GetCapabilitiesDataReference(); | |
115 bool bluetooth_supported = false; | 168 bool bluetooth_supported = false; |
116 bool found_key = | 169 bool found_key = capabilities_data_ref->dictionary()->GetBoolean( |
117 capabilities_->GetBoolean(kKeyBluetoothSupported, &bluetooth_supported); | 170 kKeyBluetoothSupported, &bluetooth_supported); |
118 DCHECK(found_key); | 171 DCHECK(found_key); |
119 return bluetooth_supported; | 172 return bluetooth_supported; |
120 } | 173 } |
121 | 174 |
122 bool DeviceCapabilitiesImpl::DisplaySupported() const { | 175 bool DeviceCapabilitiesImpl::DisplaySupported() const { |
123 DCHECK(thread_checker_.CalledOnValidThread()); | 176 scoped_refptr<ImmutableCapabilitiesData> capabilities_data_ref = |
177 GetCapabilitiesDataReference(); | |
124 bool display_supported = false; | 178 bool display_supported = false; |
125 bool found_key = | 179 bool found_key = capabilities_data_ref->dictionary()->GetBoolean( |
126 capabilities_->GetBoolean(kKeyDisplaySupported, &display_supported); | 180 kKeyDisplaySupported, &display_supported); |
127 DCHECK(found_key); | 181 DCHECK(found_key); |
128 return display_supported; | 182 return display_supported; |
129 } | 183 } |
130 | 184 |
131 bool DeviceCapabilitiesImpl::GetCapability( | 185 scoped_ptr<base::Value> DeviceCapabilitiesImpl::GetCapability( |
132 const std::string& path, | 186 const std::string& path) const { |
133 const base::Value** out_value) const { | 187 scoped_refptr<ImmutableCapabilitiesData> capabilities_data_ref = |
134 DCHECK(thread_checker_.CalledOnValidThread()); | 188 GetCapabilitiesDataReference(); |
135 return capabilities_ && capabilities_->Get(path, out_value); | 189 const base::Value* value = nullptr; |
190 bool found_path = capabilities_data_ref->dictionary()->Get(path, &value); | |
191 return found_path ? value->CreateDeepCopy() : scoped_ptr<base::Value>(); | |
136 } | 192 } |
137 | 193 |
138 const std::string& DeviceCapabilitiesImpl::GetCapabilitiesString() const { | 194 std::string DeviceCapabilitiesImpl::GetCapabilitiesString() const { |
139 DCHECK(thread_checker_.CalledOnValidThread()); | 195 scoped_refptr<ImmutableCapabilitiesData> capabilities_data_ref = |
140 return *capabilities_str_; | 196 GetCapabilitiesDataReference(); |
197 return *capabilities_data_ref->json_string(); | |
141 } | 198 } |
142 | 199 |
143 const base::DictionaryValue* DeviceCapabilitiesImpl::GetCapabilities() const { | 200 scoped_ptr<base::DictionaryValue> |
144 DCHECK(thread_checker_.CalledOnValidThread()); | 201 DeviceCapabilitiesImpl::GetCapabilities() const { |
145 return capabilities_.get(); | 202 scoped_refptr<ImmutableCapabilitiesData> capabilities_data_ref = |
203 GetCapabilitiesDataReference(); | |
204 return capabilities_data_ref->dictionary()->CreateDeepCopy(); | |
146 } | 205 } |
147 | 206 |
148 void DeviceCapabilitiesImpl::SetCapability( | 207 void DeviceCapabilitiesImpl::SetCapability( |
149 const std::string& path, | 208 const std::string& path, |
150 scoped_ptr<base::Value> proposed_value) { | 209 scoped_ptr<base::Value> proposed_value) { |
151 DCHECK(thread_checker_.CalledOnValidThread()); | |
152 DCHECK(proposed_value.get()); | 210 DCHECK(proposed_value.get()); |
153 if (!IsValidPath(path)) { | 211 if (!IsValidPath(path)) { |
154 LOG(DFATAL) << "Invalid capability path encountered for SetCapability()"; | 212 LOG(DFATAL) << "Invalid capability path encountered for SetCapability()"; |
155 return; | 213 return; |
156 } | 214 } |
157 | 215 |
158 // Check for Validator registered under first key per the Register() | 216 { |
159 // interface. | 217 base::AutoLock auto_lock(validation_lock_); |
160 auto validator_it = validator_map_.find(GetFirstKey(path)); | 218 // Check for Validator registered under first key per the Register() |
161 if (validator_it == validator_map_.end()) { | 219 // interface. |
162 SetValidatedValueInternal(path, proposed_value.Pass()); | 220 auto validator_it = validator_map_.find(GetFirstKey(path)); |
163 return; | 221 if (validator_it != validator_map_.end()) { |
222 // We do not want to post a task directly for the Validator's Validate() | |
223 // method here because if another thread is in the middle of unregistering | |
224 // that Validator, there will be an outstanding call to Validate() that | |
225 // occurs after it has unregistered. Since ValidatorInfo gets destroyed | |
226 // in Unregister() on same thread that validation should run on, we can | |
227 // post a task to the Validator's thread with weak_ptr. This way, if the | |
228 // Validator gets unregistered, the call to Validate will get skipped. | |
229 validator_it->second->task_runner()->PostTask( | |
230 FROM_HERE, base::Bind(&ValidatorInfo::Validate, | |
231 validator_it->second->AsWeakPtr(), path, | |
232 base::Passed(&proposed_value))); | |
233 return; | |
234 } | |
164 } | 235 } |
165 | 236 // Since we are done checking for a registered Validator at this point, we |
166 validator_it->second->Validate(path, proposed_value.Pass()); | 237 // can release the lock. All further member access will be for capabilities. |
238 SetValidatedValue(path, proposed_value.Pass()); | |
167 } | 239 } |
168 | 240 |
169 void DeviceCapabilitiesImpl::MergeDictionary( | 241 void DeviceCapabilitiesImpl::MergeDictionary( |
170 const base::DictionaryValue& dict_value) { | 242 const base::DictionaryValue& dict_value) { |
171 DCHECK(thread_checker_.CalledOnValidThread()); | |
172 for (base::DictionaryValue::Iterator it(dict_value); !it.IsAtEnd(); | 243 for (base::DictionaryValue::Iterator it(dict_value); !it.IsAtEnd(); |
173 it.Advance()) { | 244 it.Advance()) { |
174 SetCapability(it.key(), it.value().CreateDeepCopy()); | 245 SetCapability(it.key(), it.value().CreateDeepCopy()); |
175 } | 246 } |
176 } | 247 } |
177 | 248 |
178 void DeviceCapabilitiesImpl::AddCapabilitiesObserver(Observer* observer) { | 249 void DeviceCapabilitiesImpl::AddCapabilitiesObserver(Observer* observer) { |
179 DCHECK(observer); | 250 DCHECK(observer); |
180 DCHECK(thread_checker_.CalledOnValidThread()); | 251 observer_list_->AddObserver(observer); |
181 observer_list_.AddObserver(observer); | |
182 } | 252 } |
183 | 253 |
184 void DeviceCapabilitiesImpl::RemoveCapabilitiesObserver(Observer* observer) { | 254 void DeviceCapabilitiesImpl::RemoveCapabilitiesObserver(Observer* observer) { |
185 DCHECK(observer); | 255 DCHECK(observer); |
186 DCHECK(thread_checker_.CalledOnValidThread()); | 256 observer_list_->RemoveObserver(observer); |
187 observer_list_.RemoveObserver(observer); | |
188 } | 257 } |
189 | 258 |
190 void DeviceCapabilitiesImpl::SetValidatedValueInternal( | 259 void DeviceCapabilitiesImpl::SetValidatedValue( |
191 const std::string& path, | 260 const std::string& path, |
192 scoped_ptr<base::Value> new_value) { | 261 scoped_ptr<base::Value> new_value) { |
193 DCHECK(thread_checker_.CalledOnValidThread()); | 262 // All internal writes/modifications of capabilities must occur on same |
263 // thread to avoid race conditions. | |
264 if (!task_runner_for_writes_->BelongsToCurrentThread()) { | |
265 task_runner_for_writes_->PostTask( | |
266 FROM_HERE, | |
267 base::Bind(&DeviceCapabilitiesImpl::SetValidatedValue, | |
268 base::Unretained(this), path, base::Passed(&new_value))); | |
269 return; | |
270 } | |
271 | |
194 DCHECK(IsValidPath(path)); | 272 DCHECK(IsValidPath(path)); |
195 DCHECK(new_value.get()); | 273 DCHECK(new_value.get()); |
196 | 274 |
275 // We don't need to acquire lock here when reading capabilities_data_ because | |
276 // we know that all writes to capabilities_data_ must occur serially on thread | |
277 // that we're on. | |
197 const base::Value* cur_value = nullptr; | 278 const base::Value* cur_value = nullptr; |
198 bool capability_unchaged = | 279 bool capability_unchanged = |
199 GetCapability(path, &cur_value) && cur_value->Equals(new_value.get()); | 280 capabilities_data_->dictionary()->Get(path, &cur_value) && |
200 if (capability_unchaged) { | 281 cur_value->Equals(new_value.get()); |
282 if (capability_unchanged) { | |
201 VLOG(1) << "Ignoring unchanged capability: " << path; | 283 VLOG(1) << "Ignoring unchanged capability: " << path; |
202 return; | 284 return; |
203 } | 285 } |
204 | 286 |
205 capabilities_->Set(path, new_value.Pass()); | 287 // In this sequence, we create a deep copy, modify the deep copy, and then |
206 UpdateStrAndNotifyChanged(path); | 288 // do a pointer swap. We do this to have minimal time spent in the |
289 // capabilities_lock_. If we were to lock and modify the capabilities | |
290 // dictionary directly, there may be expensive writes that block other | |
291 // threads. | |
292 scoped_ptr<base::DictionaryValue> capabilities_deep_copy( | |
293 capabilities_data_->dictionary()->CreateDeepCopy()); | |
294 capabilities_deep_copy->Set(path, new_value.Pass()); | |
295 scoped_refptr<ImmutableCapabilitiesData> new_capabilities_data( | |
296 new ImmutableCapabilitiesData(capabilities_deep_copy.Pass())); | |
297 | |
298 { | |
299 base::AutoLock auto_lock(capabilities_lock_); | |
300 // Using swap instead of assignment operator here because it's a little | |
301 // faster. Avoids an extra call to AddRef()/Release(). | |
302 capabilities_data_.swap(new_capabilities_data); | |
303 } | |
304 | |
305 // Even though ObseverListThreadSafe notifications are always asynchronous | |
306 // (posts task even if to same thread), no locks should be held at this point | |
307 // in the code. This is just to be safe that no deadlocks occur if Observers | |
308 // call DeviceCapabilities methods in OnCapabilitiesChanged(). | |
309 observer_list_->Notify(FROM_HERE, &Observer::OnCapabilitiesChanged, path); | |
207 } | 310 } |
208 | 311 |
209 void DeviceCapabilitiesImpl::UpdateStrAndNotifyChanged( | 312 scoped_refptr<DeviceCapabilitiesImpl::ImmutableCapabilitiesData> |
210 const std::string& path) { | 313 DeviceCapabilitiesImpl::GetCapabilitiesDataReference() const { |
211 // Update capabilities string here since all updates to capabilities must | 314 // Need to acquire lock here when copy constructing capabilities_data_ |
212 // ultimately call this method no matter where the update originated from. | 315 // otherwise we could be concurrently be writing to scoped_refptr in |
213 capabilities_str_ = SerializeToJson(*capabilities_); | 316 // SetValidatedValue(), which could cause a bad scoped_refptr read. |
214 DCHECK(capabilities_str_.get()); | 317 base::AutoLock auto_lock(capabilities_lock_); |
215 FOR_EACH_OBSERVER(Observer, observer_list_, OnCapabilitiesChanged(path)); | 318 return capabilities_data_; |
216 } | 319 } |
217 | 320 |
218 } // namespace chromecast | 321 } // namespace chromecast |
OLD | NEW |