Chromium Code Reviews| Index: rlz/chromeos/lib/rlz_value_store_chromeos.cc |
| diff --git a/rlz/chromeos/lib/rlz_value_store_chromeos.cc b/rlz/chromeos/lib/rlz_value_store_chromeos.cc |
| index ddf73e7e1d10b51a01194f463bb367a2ce152185..c94d801e27a54584a2f0e48937ea6e6a4ded9c49 100644 |
| --- a/rlz/chromeos/lib/rlz_value_store_chromeos.cc |
| +++ b/rlz/chromeos/lib/rlz_value_store_chromeos.cc |
| @@ -4,16 +4,16 @@ |
| #include "rlz/chromeos/lib/rlz_value_store_chromeos.h" |
| -#include "base/file_path.h" |
| #include "base/file_util.h" |
| +#include "base/files/important_file_writer.h" |
| +#include "base/json/json_file_value_serializer.h" |
| +#include "base/json/json_string_value_serializer.h" |
| #include "base/logging.h" |
| -#include "base/memory/singleton.h" |
| -#include "base/prefs/json_pref_store.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/string_number_conversions.h" |
| #include "base/values.h" |
| #include "rlz/lib/lib_values.h" |
| -#include "rlz/lib/recursive_lock.h" |
| +#include "rlz/lib/recursive_cross_process_lock_posix.h" |
| #include "rlz/lib/rlz_lib.h" |
| namespace rlz_lib { |
| @@ -36,6 +36,10 @@ const char kNoSupplementaryBrand[] = "_"; |
| // RLZ store filename. |
| const FilePath::CharType kRLZDataFileName[] = FILE_PATH_LITERAL("RLZ Data"); |
| +// RLZ store lock filename |
| +const FilePath::CharType kRLZLockFileName[] = |
| + FILE_PATH_LITERAL("RLZ Data.lock"); |
| + |
| // RLZ store path for testing. |
| FilePath g_testing_rlz_store_path_; |
| @@ -46,6 +50,13 @@ FilePath GetRlzStorePath() { |
| g_testing_rlz_store_path_.Append(kRLZDataFileName); |
| } |
| +// Returns file path of the RLZ storage lock file. |
| +FilePath GetRlzStoreLockPath() { |
| + return g_testing_rlz_store_path_.empty() ? |
| + file_util::GetHomeDir().Append(kRLZLockFileName) : |
| + g_testing_rlz_store_path_.Append(kRLZLockFileName); |
| +} |
| + |
| // Returns the dictionary key for storing access point-related prefs. |
| std::string GetKeyName(std::string key, AccessPoint access_point) { |
| std::string brand = SupplementaryBranding::GetBrand(); |
| @@ -64,89 +75,60 @@ std::string GetKeyName(std::string key, Product product) { |
| } // namespace |
| -// static |
| -base::SequencedTaskRunner* RlzValueStoreChromeOS::io_task_runner_ = NULL; |
| - |
| -// static |
| -bool RlzValueStoreChromeOS::created_; |
| - |
| -// static |
| -RlzValueStoreChromeOS* RlzValueStoreChromeOS::GetInstance() { |
| - return Singleton<RlzValueStoreChromeOS>::get(); |
| -} |
| - |
| -// static |
| -void RlzValueStoreChromeOS::SetIOTaskRunner( |
| - base::SequencedTaskRunner* io_task_runner) { |
| - io_task_runner_ = io_task_runner; |
| - // Make sure |io_task_runner_| lives until constructor is called. |
| - io_task_runner_->AddRef(); |
| -} |
| - |
| -// static |
| -void RlzValueStoreChromeOS::ResetForTesting() { |
| - // Make sure we don't create an instance if it didn't exist. |
| - if (created_) |
| - GetInstance()->ReadPrefs(); |
| -} |
| - |
| -// static |
| -void RlzValueStoreChromeOS::Cleanup() { |
| - if (created_) |
| - GetInstance()->rlz_store_ = NULL; |
| -} |
| - |
| -RlzValueStoreChromeOS::RlzValueStoreChromeOS() { |
| - ReadPrefs(); |
| - created_ = true; |
| +RlzValueStoreChromeOS::RlzValueStoreChromeOS(const FilePath& store_path) |
| + : rlz_store_(new base::DictionaryValue), |
| + store_path_(store_path), |
| + read_only_(true) { |
| + DCHECK(CalledOnValidThread()); |
|
Roger Tawa OOO till Jul 10th
2012/11/26 19:16:51
why call CalledOnValidThread() in ctor?
Ivan Korotkov
2012/11/27 11:37:25
Right, removed.
|
| + ReadStore(); |
| } |
| RlzValueStoreChromeOS::~RlzValueStoreChromeOS() { |
| + DCHECK(CalledOnValidThread()); |
|
Roger Tawa OOO till Jul 10th
2012/11/26 19:16:51
the base class dtor already calls CalledOnValidThr
Ivan Korotkov
2012/11/27 11:37:25
Removed.
|
| + WriteStore(); |
| } |
| bool RlzValueStoreChromeOS::HasAccess(AccessType type) { |
| - return type == kReadAccess || !rlz_store_->ReadOnly(); |
| + DCHECK(CalledOnValidThread()); |
| + return type == kReadAccess || !read_only_; |
| } |
| bool RlzValueStoreChromeOS::WritePingTime(Product product, int64 time) { |
| - std::string value = base::Int64ToString(time); |
| - rlz_store_->SetValue(GetKeyName(kPingTimeKey, product), |
| - base::Value::CreateStringValue(value)); |
| + DCHECK(CalledOnValidThread()); |
| + rlz_store_->SetString(GetKeyName(kPingTimeKey, product), |
| + base::Int64ToString(time)); |
|
Roger Tawa OOO till Jul 10th
2012/11/26 19:16:51
one extra space.
Ivan Korotkov
2012/11/27 11:37:25
Done.
|
| return true; |
| } |
| bool RlzValueStoreChromeOS::ReadPingTime(Product product, int64* time) { |
| - const base::Value* value = NULL; |
| - rlz_store_->GetValue(GetKeyName(kPingTimeKey, product), &value); |
| - std::string s_value; |
| - return value && value->GetAsString(&s_value) && |
| - base::StringToInt64(s_value, time); |
| + DCHECK(CalledOnValidThread()); |
| + std::string ping_time; |
| + return rlz_store_->GetString(GetKeyName(kPingTimeKey, product), &ping_time) && |
| + base::StringToInt64(ping_time, time); |
| } |
| bool RlzValueStoreChromeOS::ClearPingTime(Product product) { |
| - rlz_store_->RemoveValue(GetKeyName(kPingTimeKey, product)); |
| + DCHECK(CalledOnValidThread()); |
| + rlz_store_->Remove(GetKeyName(kPingTimeKey, product), NULL); |
| return true; |
| } |
| bool RlzValueStoreChromeOS::WriteAccessPointRlz(AccessPoint access_point, |
| const char* new_rlz) { |
| - rlz_store_->SetValue( |
| - GetKeyName(kAccessPointKey, access_point), |
| - base::Value::CreateStringValue(new_rlz)); |
| + DCHECK(CalledOnValidThread()); |
| + rlz_store_->SetString( |
| + GetKeyName(kAccessPointKey, access_point), new_rlz); |
| return true; |
| } |
| bool RlzValueStoreChromeOS::ReadAccessPointRlz(AccessPoint access_point, |
| char* rlz, |
| size_t rlz_size) { |
| - const base::Value* value = NULL; |
| - rlz_store_->GetValue( |
| - GetKeyName(kAccessPointKey, access_point), &value); |
| - std::string s_value; |
| - if (value) |
| - value->GetAsString(&s_value); |
| - if (s_value.size() < rlz_size) { |
| - strncpy(rlz, s_value.c_str(), rlz_size); |
| + DCHECK(CalledOnValidThread()); |
| + std::string rlz_value; |
| + rlz_store_->GetString(GetKeyName(kAccessPointKey, access_point), &rlz_value); |
| + if (rlz_value.size() < rlz_size) { |
| + strncpy(rlz, rlz_value.c_str(), rlz_size); |
| return true; |
| } |
| if (rlz_size > 0) |
| @@ -155,13 +137,14 @@ bool RlzValueStoreChromeOS::ReadAccessPointRlz(AccessPoint access_point, |
| } |
| bool RlzValueStoreChromeOS::ClearAccessPointRlz(AccessPoint access_point) { |
| - rlz_store_->RemoveValue( |
| - GetKeyName(kAccessPointKey, access_point)); |
| + DCHECK(CalledOnValidThread()); |
| + rlz_store_->Remove(GetKeyName(kAccessPointKey, access_point), NULL); |
| return true; |
| } |
| bool RlzValueStoreChromeOS::AddProductEvent(Product product, |
| const char* event_rlz) { |
| + DCHECK(CalledOnValidThread()); |
| return AddValueToList(GetKeyName(kProductEventKey, product), |
| base::Value::CreateStringValue(event_rlz)); |
| } |
| @@ -169,8 +152,9 @@ bool RlzValueStoreChromeOS::AddProductEvent(Product product, |
| bool RlzValueStoreChromeOS::ReadProductEvents( |
| Product product, |
| std::vector<std::string>* events) { |
| - base::ListValue* events_list = GetList(GetKeyName(kProductEventKey, product)); |
| - if (!events_list) |
| + DCHECK(CalledOnValidThread()); |
| + base::ListValue* events_list = NULL; ; |
| + if (!rlz_store_->GetList(GetKeyName(kProductEventKey, product), &events_list)) |
| return false; |
| events->clear(); |
| for (size_t i = 0; i < events_list->GetSize(); ++i) { |
| @@ -183,99 +167,163 @@ bool RlzValueStoreChromeOS::ReadProductEvents( |
| bool RlzValueStoreChromeOS::ClearProductEvent(Product product, |
| const char* event_rlz) { |
| + DCHECK(CalledOnValidThread()); |
| base::StringValue event_value(event_rlz); |
| return RemoveValueFromList(GetKeyName(kProductEventKey, product), |
| event_value); |
| } |
| bool RlzValueStoreChromeOS::ClearAllProductEvents(Product product) { |
| - rlz_store_->RemoveValue(GetKeyName(kProductEventKey, product)); |
| + DCHECK(CalledOnValidThread()); |
| + rlz_store_->Remove(GetKeyName(kProductEventKey, product), NULL); |
| return true; |
| } |
| bool RlzValueStoreChromeOS::AddStatefulEvent(Product product, |
| const char* event_rlz) { |
| + DCHECK(CalledOnValidThread()); |
| return AddValueToList(GetKeyName(kStatefulEventKey, product), |
| base::Value::CreateStringValue(event_rlz)); |
| } |
| bool RlzValueStoreChromeOS::IsStatefulEvent(Product product, |
| const char* event_rlz) { |
| - base::ListValue* events_list = |
| - GetList(GetKeyName(kStatefulEventKey, product)); |
| + DCHECK(CalledOnValidThread()); |
| base::StringValue event_value(event_rlz); |
| - return events_list && events_list->Find(event_value) != events_list->end(); |
| + base::ListValue* events_list = NULL; |
| + return rlz_store_->GetList(GetKeyName(kStatefulEventKey, product), |
| + &events_list) && |
| + events_list->Find(event_value) != events_list->end(); |
| } |
| bool RlzValueStoreChromeOS::ClearAllStatefulEvents(Product product) { |
| - rlz_store_->RemoveValue(GetKeyName(kStatefulEventKey, product)); |
| + DCHECK(CalledOnValidThread()); |
| + rlz_store_->Remove(GetKeyName(kStatefulEventKey, product), NULL); |
| return true; |
| } |
| void RlzValueStoreChromeOS::CollectGarbage() { |
| + DCHECK(CalledOnValidThread()); |
| NOTIMPLEMENTED(); |
| } |
| -void RlzValueStoreChromeOS::ReadPrefs() { |
| - DCHECK(io_task_runner_) |
| - << "Calling GetInstance or ResetForTesting before SetIOTaskRunner?"; |
| - rlz_store_ = new JsonPrefStore(GetRlzStorePath(), io_task_runner_); |
| - rlz_store_->ReadPrefs(); |
| - switch (rlz_store_->GetReadError()) { |
| - case PersistentPrefStore::PREF_READ_ERROR_NONE: |
| - case PersistentPrefStore::PREF_READ_ERROR_NO_FILE: |
| +void RlzValueStoreChromeOS::ReadStore() { |
| + int error_code = 0; |
| + std::string error_msg; |
| + JSONFileValueSerializer serializer(store_path_); |
| + scoped_ptr<base::Value> value( |
| + serializer.Deserialize(&error_code, &error_msg)); |
| + switch (error_code) { |
| + case JSONFileValueSerializer::JSON_NO_SUCH_FILE: |
| + read_only_ = false; |
|
Roger Tawa OOO till Jul 10th
2012/11/26 19:16:51
why set read-only to false here?
Ivan Korotkov
2012/11/27 11:37:25
NO_SUCH_FILE means RLZ store hasn't been created y
|
| + break; |
| + case JSONFileValueSerializer::JSON_NO_ERROR: |
| + read_only_ = false; |
| + rlz_store_.reset(static_cast<base::DictionaryValue*>(value.release())); |
| break; |
| default: |
| - LOG(ERROR) << "Error read RLZ store: " << rlz_store_->GetReadError(); |
| + LOG(ERROR) << "Error reading RLZ store: " << error_msg; |
| } |
| - // Restore refcount modified by SetIOTaskRunner(). |
| - io_task_runner_->Release(); |
| - io_task_runner_ = NULL; |
| } |
| -base::ListValue* RlzValueStoreChromeOS::GetList(std::string list_name) { |
| - base::Value* list_value = NULL; |
| - rlz_store_->GetMutableValue(list_name, &list_value); |
| - base::ListValue* list = NULL; |
| - if (!list_value || !list_value->GetAsList(&list)) |
| - return NULL; |
| - return list; |
| +void RlzValueStoreChromeOS::WriteStore() { |
| + std::string json_data; |
| + JSONStringValueSerializer serializer(&json_data); |
| + serializer.set_pretty_print(true); |
| + scoped_ptr<DictionaryValue> copy(rlz_store_->DeepCopyWithoutEmptyChildren()); |
| + if (!serializer.Serialize(*copy.get())) { |
| + LOG(ERROR) << "Failed to serialize RLZ data"; |
| + NOTREACHED(); |
| + return; |
| + } |
| + if (!base::ImportantFileWriter::WriteToDisk(store_path_, json_data)) |
| + LOG(ERROR) << "Error writing RLZ store"; |
| } |
| bool RlzValueStoreChromeOS::AddValueToList(std::string list_name, |
| base::Value* value) { |
| - base::ListValue* list = GetList(list_name); |
| - if (!list) { |
| - list = new base::ListValue; |
| - rlz_store_->SetValue(list_name, list); |
| + base::ListValue* list_value = NULL; |
| + if (!rlz_store_->GetList(list_name, &list_value)) { |
| + list_value = new base::ListValue; |
| + rlz_store_->Set(list_name, list_value); |
| } |
| - if (list->AppendIfNotPresent(value)) |
| - rlz_store_->ReportValueChanged(list_name); |
| + list_value->AppendIfNotPresent(value); |
| return true; |
| } |
| bool RlzValueStoreChromeOS::RemoveValueFromList(std::string list_name, |
| const base::Value& value) { |
| - base::ListValue* list = GetList(list_name); |
| - if (!list) |
| + base::ListValue* list_value = NULL; |
| + if (!rlz_store_->GetList(list_name, &list_value)) |
| return false; |
| - rlz_store_->MarkNeedsEmptyValue(list_name); |
| + // rlz_store_->MarkNeedsEmptyValue(list_name); |
|
Roger Tawa OOO till Jul 10th
2012/11/26 19:16:51
remove line?
Ivan Korotkov
2012/11/27 11:37:25
Done.
|
| size_t index; |
| - if (list->Remove(value, &index)) |
| - rlz_store_->ReportValueChanged(list_name); |
| + list_value->Remove(value, &index); |
| return true; |
| } |
| +namespace { |
| -ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() |
| - : store_(RlzValueStoreChromeOS::GetInstance()) { |
| +// RlzValueStoreChromeOS keeps its data in memory and only writes it to disk |
| +// when ScopedRlzValueStoreLock goes out of scope. Hence, if several |
| +// ScopedRlzValueStoreLocks are nested, they all need to use the same store |
| +// object. |
| + |
| +RecursiveCrossProcessLock g_recursive_lock = |
| + RECURSIVE_CROSS_PROCESS_LOCK_INITIALIZER; |
| + |
| +// This counts the nesting depth of |ScopedRlzValueStoreLock|. |
| +int g_lock_depth = 0; |
| + |
| +// This is the shared store object. Non-|NULL| only when |g_lock_depth > 0|. |
| +RlzValueStoreChromeOS* g_store = NULL; |
| + |
| +} // namespace |
| + |
| +ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() { |
| + bool got_cross_process_lock = |
| + g_recursive_lock.TryGetCrossProcessLock(GetRlzStoreLockPath()); |
| + // At this point, we hold the in-process lock, no matter the value of |
| + // |got_cross_process_lock|. |
| + |
| + ++g_lock_depth; |
| + if (!got_cross_process_lock) { |
| + // Acquiring cross-process lock failed, so simply return here. |
| + // In-process lock will be released in dtor. |
| + DCHECK(!g_store); |
|
Roger Tawa OOO till Jul 10th
2012/11/26 19:16:51
Maybe add DCHECK(g_lock_depth==1)? Does not happe
Ivan Korotkov
2012/11/27 11:37:25
See my comment in Lock
|
| + return; |
| + } |
| + |
| + if (g_lock_depth > 1) { |
| + // Reuse the already existing store object. |
| + DCHECK(g_store); |
| + store_.reset(g_store); |
| + return; |
| + } |
| + |
| + // This is the topmost lock, create a new store object. |
| + DCHECK(!g_store); |
| + g_store = new RlzValueStoreChromeOS(GetRlzStorePath()); |
| + store_.reset(g_store); |
| } |
| ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() { |
| + --g_lock_depth; |
| + DCHECK(g_lock_depth >= 0); |
| + |
| + if (g_lock_depth > 0) { |
| + // Other locks are still using store_, so don't free it yet. |
| + ignore_result(store_.release()); |
| + return; |
| + } |
| + |
| + g_store = NULL; |
| + |
| + g_recursive_lock.ReleaseLock(); |
| } |
| RlzValueStore* ScopedRlzValueStoreLock::GetStore() { |
| - return store_; |
| + return store_.get(); |
| } |
| namespace testing { |
| @@ -284,6 +332,10 @@ void SetRlzStoreDirectory(const FilePath& directory) { |
| g_testing_rlz_store_path_ = directory; |
| } |
| +std::string RlzStoreFilenameStr() { |
| + return GetRlzStorePath().value(); |
| +} |
| + |
| } // namespace testing |
| } // namespace rlz_lib |