Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(92)

Side by Side Diff: chromecast/base/device_capabilities_impl.cc

Issue 1409173006: Making DeviceCapabilities threadsafe with locking. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Just changing unit test assertions to new format. No other changes. Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
52 scoped_ptr<DeviceCapabilities> DeviceCapabilities::CreateForTesting() { 54 scoped_ptr<DeviceCapabilities> DeviceCapabilities::CreateForTesting() {
53 DeviceCapabilities* capabilities = new DeviceCapabilitiesImpl; 55 DeviceCapabilities* capabilities = new DeviceCapabilitiesImpl;
54 capabilities->SetCapability( 56 capabilities->SetCapability(
55 kKeyBluetoothSupported, 57 kKeyBluetoothSupported,
56 make_scoped_ptr(new base::FundamentalValue(false))); 58 make_scoped_ptr(new base::FundamentalValue(false)));
57 capabilities->SetCapability( 59 capabilities->SetCapability(
58 kKeyDisplaySupported, make_scoped_ptr(new base::FundamentalValue(true))); 60 kKeyDisplaySupported, make_scoped_ptr(new base::FundamentalValue(true)));
59 return make_scoped_ptr(capabilities); 61 return make_scoped_ptr(capabilities);
60 } 62 }
61 63
64 scoped_refptr<DeviceCapabilities::Data> DeviceCapabilities::CreateData() {
65 return make_scoped_refptr(new Data);
66 }
67
68 scoped_refptr<DeviceCapabilities::Data> DeviceCapabilities::CreateData(
69 scoped_ptr<const base::DictionaryValue> dictionary) {
70 DCHECK(dictionary.get());
71 return make_scoped_refptr(new Data(dictionary.Pass()));
72 }
73
62 DeviceCapabilities::Validator::Validator(DeviceCapabilities* capabilities) 74 DeviceCapabilities::Validator::Validator(DeviceCapabilities* capabilities)
63 : capabilities_(capabilities) { 75 : capabilities_(capabilities) {
64 DCHECK(capabilities); 76 DCHECK(capabilities);
65 } 77 }
66 78
67 void DeviceCapabilities::Validator::SetValidatedValue( 79 void DeviceCapabilities::Validator::SetValidatedValue(
68 const std::string& path, 80 const std::string& path,
69 scoped_ptr<base::Value> new_value) const { 81 scoped_ptr<base::Value> new_value) const {
70 capabilities_->SetValidatedValueInternal(path, new_value.Pass()); 82 capabilities_->SetValidatedValue(path, new_value.Pass());
83 }
84
85 DeviceCapabilities::Data::Data()
86 : dictionary_(new base::DictionaryValue),
87 json_string_(SerializeToJson(*dictionary_)) {
88 DCHECK(json_string_.get());
89 }
90
91 DeviceCapabilities::Data::Data(
92 scoped_ptr<const base::DictionaryValue> dictionary)
93 : dictionary_(dictionary.Pass()),
94 json_string_(SerializeToJson(*dictionary_)) {
95 DCHECK(dictionary_.get());
96 DCHECK(json_string_.get());
97 }
98
99 DeviceCapabilitiesImpl::Data::~Data() {}
100
101 DeviceCapabilitiesImpl::ValidatorInfo::ValidatorInfo(Validator* validator)
102 : validator_(validator), task_runner_(base::ThreadTaskRunnerHandle::Get()) {
103 DCHECK(validator_);
104 DCHECK(task_runner_.get());
105 }
106
107 DeviceCapabilitiesImpl::ValidatorInfo::~ValidatorInfo() {
108 // Check that ValidatorInfo is being destroyed on the same thread that it was
109 // constructed on.
110 DCHECK(task_runner_->BelongsToCurrentThread());
111 }
112
113 void DeviceCapabilitiesImpl::ValidatorInfo::Validate(
114 const std::string& path,
115 scoped_ptr<base::Value> proposed_value) const {
116 // Check that we are running Validate on the same thread that ValidatorInfo
117 // was constructed on.
118 DCHECK(task_runner_->BelongsToCurrentThread());
119 validator_->Validate(path, proposed_value.Pass());
71 } 120 }
72 121
73 DeviceCapabilitiesImpl::DeviceCapabilitiesImpl() 122 DeviceCapabilitiesImpl::DeviceCapabilitiesImpl()
74 : capabilities_(new base::DictionaryValue), 123 : data_(CreateData()),
75 capabilities_str_(SerializeToJson(*capabilities_)) { 124 task_runner_for_writes_(base::ThreadTaskRunnerHandle::Get()),
76 DCHECK(capabilities_str_.get()); 125 observer_list_(new base::ObserverListThreadSafe<Observer>) {
126 DCHECK(task_runner_for_writes_.get());
77 } 127 }
78 128
79 DeviceCapabilitiesImpl::~DeviceCapabilitiesImpl() { 129 DeviceCapabilitiesImpl::~DeviceCapabilitiesImpl() {
80 DCHECK(thread_checker_.CalledOnValidThread());
81 // Make sure that any registered Validators have unregistered at this point 130 // Make sure that any registered Validators have unregistered at this point
82 DCHECK(validator_map_.empty()); 131 DCHECK(validator_map_.empty());
132 // Make sure that all observers have been removed at this point
133 observer_list_->AssertEmpty();
83 } 134 }
84 135
85 void DeviceCapabilitiesImpl::Register(const std::string& key, 136 void DeviceCapabilitiesImpl::Register(const std::string& key,
86 Validator* validator) { 137 Validator* validator) {
87 DCHECK(thread_checker_.CalledOnValidThread());
88 DCHECK(IsValidRegisterKey(key)); 138 DCHECK(IsValidRegisterKey(key));
89 DCHECK(validator); 139 DCHECK(validator);
90 140
91 bool added = validator_map_.insert(std::make_pair(key, validator)).second; 141 base::AutoLock auto_lock(validation_lock_);
142 bool added =
143 validator_map_.add(key, make_scoped_ptr(new ValidatorInfo(validator)))
144 .second;
92 // Check that a validator has not already been registered for this key 145 // Check that a validator has not already been registered for this key
93 DCHECK(added); 146 DCHECK(added);
94 } 147 }
95 148
96 void DeviceCapabilitiesImpl::Unregister(const std::string& key, 149 void DeviceCapabilitiesImpl::Unregister(const std::string& key,
97 const Validator* validator) { 150 const Validator* validator) {
98 DCHECK(thread_checker_.CalledOnValidThread()); 151 base::AutoLock auto_lock(validation_lock_);
152 auto validator_it = validator_map_.find(key);
153 DCHECK(validator_it != validator_map_.end());
99 // Check that validator being unregistered matches the original for |key|. 154 // Check that validator being unregistered matches the original for |key|.
100 // This prevents managers from accidentally unregistering incorrect 155 // This prevents managers from accidentally unregistering incorrect
101 // validators. 156 // validators.
102 DCHECK_EQ(validator, GetValidator(key)); 157 DCHECK_EQ(validator, validator_it->second->validator());
103 bool erased = validator_map_.erase(key); 158 // Check that validator is unregistering on same thread that it was
104 DCHECK(erased); 159 // registered on
160 DCHECK(validator_it->second->task_runner()->BelongsToCurrentThread());
161 validator_map_.erase(validator_it);
105 } 162 }
106 163
107 DeviceCapabilities::Validator* DeviceCapabilitiesImpl::GetValidator( 164 DeviceCapabilities::Validator* DeviceCapabilitiesImpl::GetValidator(
108 const std::string& key) const { 165 const std::string& key) const {
166 base::AutoLock auto_lock(validation_lock_);
109 auto validator_it = validator_map_.find(key); 167 auto validator_it = validator_map_.find(key);
110 return validator_it == validator_map_.end() ? nullptr : validator_it->second; 168 return validator_it == validator_map_.end()
169 ? nullptr
170 : validator_it->second->validator();
111 } 171 }
112 172
113 bool DeviceCapabilitiesImpl::BluetoothSupported() const { 173 bool DeviceCapabilitiesImpl::BluetoothSupported() const {
114 DCHECK(thread_checker_.CalledOnValidThread()); 174 scoped_refptr<Data> data_ref = GetData();
115 bool bluetooth_supported = false; 175 bool bluetooth_supported = false;
116 bool found_key = 176 bool found_key = data_ref->dictionary().GetBoolean(kKeyBluetoothSupported,
117 capabilities_->GetBoolean(kKeyBluetoothSupported, &bluetooth_supported); 177 &bluetooth_supported);
118 DCHECK(found_key); 178 DCHECK(found_key);
119 return bluetooth_supported; 179 return bluetooth_supported;
120 } 180 }
121 181
122 bool DeviceCapabilitiesImpl::DisplaySupported() const { 182 bool DeviceCapabilitiesImpl::DisplaySupported() const {
123 DCHECK(thread_checker_.CalledOnValidThread()); 183 scoped_refptr<Data> data_ref = GetData();
124 bool display_supported = false; 184 bool display_supported = false;
125 bool found_key = 185 bool found_key = data_ref->dictionary().GetBoolean(kKeyDisplaySupported,
126 capabilities_->GetBoolean(kKeyDisplaySupported, &display_supported); 186 &display_supported);
127 DCHECK(found_key); 187 DCHECK(found_key);
128 return display_supported; 188 return display_supported;
129 } 189 }
130 190
131 bool DeviceCapabilitiesImpl::GetCapability( 191 scoped_ptr<base::Value> DeviceCapabilitiesImpl::GetCapability(
132 const std::string& path, 192 const std::string& path) const {
133 const base::Value** out_value) const { 193 scoped_refptr<Data> data_ref = GetData();
134 DCHECK(thread_checker_.CalledOnValidThread()); 194 const base::Value* value = nullptr;
135 return capabilities_ && capabilities_->Get(path, out_value); 195 bool found_path = data_ref->dictionary().Get(path, &value);
196 return found_path ? value->CreateDeepCopy() : scoped_ptr<base::Value>();
136 } 197 }
137 198
138 const std::string& DeviceCapabilitiesImpl::GetCapabilitiesString() const { 199 scoped_refptr<DeviceCapabilities::Data>
139 DCHECK(thread_checker_.CalledOnValidThread()); 200 DeviceCapabilitiesImpl::GetData() const {
140 return *capabilities_str_; 201 // Need to acquire lock here when copy constructing data_ otherwise we could
141 } 202 // be concurrently be writing to scoped_refptr in SetValidatedValue(), which
142 203 // could cause a bad scoped_refptr read.
143 const base::DictionaryValue* DeviceCapabilitiesImpl::GetCapabilities() const { 204 base::AutoLock auto_lock(data_lock_);
144 DCHECK(thread_checker_.CalledOnValidThread()); 205 return data_;
145 return capabilities_.get();
146 } 206 }
147 207
148 void DeviceCapabilitiesImpl::SetCapability( 208 void DeviceCapabilitiesImpl::SetCapability(
149 const std::string& path, 209 const std::string& path,
150 scoped_ptr<base::Value> proposed_value) { 210 scoped_ptr<base::Value> proposed_value) {
151 DCHECK(thread_checker_.CalledOnValidThread());
152 DCHECK(proposed_value.get()); 211 DCHECK(proposed_value.get());
153 if (!IsValidPath(path)) { 212 if (!IsValidPath(path)) {
154 LOG(DFATAL) << "Invalid capability path encountered for SetCapability()"; 213 LOG(DFATAL) << "Invalid capability path encountered for SetCapability()";
155 return; 214 return;
156 } 215 }
157 216
158 // Check for Validator registered under first key per the Register() 217 {
159 // interface. 218 base::AutoLock auto_lock(validation_lock_);
160 auto validator_it = validator_map_.find(GetFirstKey(path)); 219 // Check for Validator registered under first key per the Register()
161 if (validator_it == validator_map_.end()) { 220 // interface.
162 SetValidatedValueInternal(path, proposed_value.Pass()); 221 auto validator_it = validator_map_.find(GetFirstKey(path));
163 return; 222 if (validator_it != validator_map_.end()) {
223 // We do not want to post a task directly for the Validator's Validate()
224 // method here because if another thread is in the middle of unregistering
225 // that Validator, there will be an outstanding call to Validate() that
226 // occurs after it has unregistered. Since ValidatorInfo gets destroyed
227 // in Unregister() on same thread that validation should run on, we can
228 // post a task to the Validator's thread with weak_ptr. This way, if the
229 // Validator gets unregistered, the call to Validate will get skipped.
230 validator_it->second->task_runner()->PostTask(
231 FROM_HERE, base::Bind(&ValidatorInfo::Validate,
232 validator_it->second->AsWeakPtr(), path,
233 base::Passed(&proposed_value)));
234 return;
235 }
164 } 236 }
165 237 // Since we are done checking for a registered Validator at this point, we
166 validator_it->second->Validate(path, proposed_value.Pass()); 238 // can release the lock. All further member access will be for capabilities.
239 SetValidatedValue(path, proposed_value.Pass());
167 } 240 }
168 241
169 void DeviceCapabilitiesImpl::MergeDictionary( 242 void DeviceCapabilitiesImpl::MergeDictionary(
170 const base::DictionaryValue& dict_value) { 243 const base::DictionaryValue& dict_value) {
171 DCHECK(thread_checker_.CalledOnValidThread());
172 for (base::DictionaryValue::Iterator it(dict_value); !it.IsAtEnd(); 244 for (base::DictionaryValue::Iterator it(dict_value); !it.IsAtEnd();
173 it.Advance()) { 245 it.Advance()) {
174 SetCapability(it.key(), it.value().CreateDeepCopy()); 246 SetCapability(it.key(), it.value().CreateDeepCopy());
175 } 247 }
176 } 248 }
177 249
178 void DeviceCapabilitiesImpl::AddCapabilitiesObserver(Observer* observer) { 250 void DeviceCapabilitiesImpl::AddCapabilitiesObserver(Observer* observer) {
179 DCHECK(observer); 251 DCHECK(observer);
180 DCHECK(thread_checker_.CalledOnValidThread()); 252 observer_list_->AddObserver(observer);
181 observer_list_.AddObserver(observer);
182 } 253 }
183 254
184 void DeviceCapabilitiesImpl::RemoveCapabilitiesObserver(Observer* observer) { 255 void DeviceCapabilitiesImpl::RemoveCapabilitiesObserver(Observer* observer) {
185 DCHECK(observer); 256 DCHECK(observer);
186 DCHECK(thread_checker_.CalledOnValidThread()); 257 observer_list_->RemoveObserver(observer);
187 observer_list_.RemoveObserver(observer);
188 } 258 }
189 259
190 void DeviceCapabilitiesImpl::SetValidatedValueInternal( 260 void DeviceCapabilitiesImpl::SetValidatedValue(
191 const std::string& path, 261 const std::string& path,
192 scoped_ptr<base::Value> new_value) { 262 scoped_ptr<base::Value> new_value) {
193 DCHECK(thread_checker_.CalledOnValidThread()); 263 // All internal writes/modifications of capabilities must occur on same
264 // thread to avoid race conditions.
265 if (!task_runner_for_writes_->BelongsToCurrentThread()) {
266 task_runner_for_writes_->PostTask(
267 FROM_HERE,
268 base::Bind(&DeviceCapabilitiesImpl::SetValidatedValue,
269 base::Unretained(this), path, base::Passed(&new_value)));
270 return;
271 }
272
194 DCHECK(IsValidPath(path)); 273 DCHECK(IsValidPath(path));
195 DCHECK(new_value.get()); 274 DCHECK(new_value.get());
196 275
276 // We don't need to acquire lock here when reading data_ because we know that
277 // all writes to data_ must occur serially on thread 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 = data_->dictionary().Get(path, &cur_value) &&
199 GetCapability(path, &cur_value) && cur_value->Equals(new_value.get()); 280 cur_value->Equals(new_value.get());
200 if (capability_unchaged) { 281 if (capability_unchanged) {
201 VLOG(1) << "Ignoring unchanged capability: " << path; 282 VLOG(1) << "Ignoring unchanged capability: " << path;
202 return; 283 return;
203 } 284 }
204 285
205 capabilities_->Set(path, new_value.Pass()); 286 // In this sequence, we create a deep copy, modify the deep copy, and then
206 UpdateStrAndNotifyChanged(path); 287 // do a pointer swap. We do this to have minimal time spent in the
207 } 288 // data_lock_. If we were to lock and modify the capabilities
289 // dictionary directly, there may be expensive writes that block other
290 // threads.
291 scoped_ptr<base::DictionaryValue> dictionary_deep_copy(
292 data_->dictionary().CreateDeepCopy());
293 dictionary_deep_copy->Set(path, new_value.Pass());
294 scoped_refptr<Data> new_data(CreateData(dictionary_deep_copy.Pass()));
208 295
209 void DeviceCapabilitiesImpl::UpdateStrAndNotifyChanged( 296 {
210 const std::string& path) { 297 base::AutoLock auto_lock(data_lock_);
211 // Update capabilities string here since all updates to capabilities must 298 // Using swap instead of assignment operator here because it's a little
212 // ultimately call this method no matter where the update originated from. 299 // faster. Avoids an extra call to AddRef()/Release().
213 capabilities_str_ = SerializeToJson(*capabilities_); 300 data_.swap(new_data);
214 DCHECK(capabilities_str_.get()); 301 }
215 FOR_EACH_OBSERVER(Observer, observer_list_, OnCapabilitiesChanged(path)); 302
303 // Even though ObseverListThreadSafe notifications are always asynchronous
304 // (posts task even if to same thread), no locks should be held at this point
305 // in the code. This is just to be safe that no deadlocks occur if Observers
306 // call DeviceCapabilities methods in OnCapabilitiesChanged().
307 observer_list_->Notify(FROM_HERE, &Observer::OnCapabilitiesChanged, path);
216 } 308 }
217 309
218 } // namespace chromecast 310 } // namespace chromecast
OLDNEW
« no previous file with comments | « chromecast/base/device_capabilities_impl.h ('k') | chromecast/base/device_capabilities_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698