| // Copyright (c) 2014 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "metrics/persistent_integer.h" |
| |
| #include <algorithm> |
| #include <fcntl.h> |
| |
| #include <base/callback.h> |
| #include <base/check.h> |
| #include <base/files/file.h> |
| #include <base/logging.h> |
| #include <base/posix/eintr_wrapper.h> |
| #include <base/strings/string_util.h> |
| |
| #include "metrics/metrics_library.h" |
| |
| namespace chromeos_metrics { |
| |
| namespace { |
| |
| // Returns a pointer to the creation callback. The normal "Use a 'leaked' |
| // function-level static object to avoid global initialization & destruction |
| // issues" trick. |
| base::RepeatingCallback<void(const base::FilePath&)>* GetCreationCallback() { |
| static base::RepeatingCallback<void(const base::FilePath&)>* p = |
| new base::RepeatingCallback<void(const base::FilePath&)>; |
| |
| return p; |
| } |
| |
| } // namespace |
| |
| // Static class member instantiation. |
| bool PersistentInteger::testing_ = false; |
| |
| PersistentInteger::PersistentInteger(const base::FilePath& backing_file_path) |
| : path_(backing_file_path), synced_(false), value_(0), version_(kVersion) { |
| if (!GetCreationCallback()->is_null()) { |
| GetCreationCallback()->Run(backing_file_path); |
| } |
| } |
| |
| PersistentInteger::~PersistentInteger() {} |
| |
| void PersistentInteger::Set(int64_t value) { |
| value_ = value; |
| Write(); |
| } |
| |
| int64_t PersistentInteger::Get() { |
| // If not synced, then read. If the read fails, it's a good idea to write. |
| // The write will create the file if needed. |
| if (!synced_ && !Read()) |
| Write(); |
| return value_; |
| } |
| |
| int64_t PersistentInteger::GetAndClear() { |
| int64_t v = Get(); |
| Set(0); |
| return v; |
| } |
| |
| void PersistentInteger::Add(int64_t x) { |
| Set(Get() + x); |
| } |
| |
| void PersistentInteger::Max(int64_t x) { |
| Set(std::max(Get(), x)); |
| } |
| |
| void PersistentInteger::Write() { |
| // Open the backing file, creating it if it doesn't exist. |
| base::File f(path_, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); |
| if (!f.IsValid()) { |
| // The disk might be bad. Not much we (or the caller) can do; just log an |
| // ERROR. (That might fail, too, if the disk isn't writeable) |
| PLOG(ERROR) << "cannot open " << path_.MaybeAsASCII(); |
| return; |
| } |
| |
| const char* version_ptr = reinterpret_cast<const char*>(&version_); |
| const char* value_ptr = reinterpret_cast<const char*>(&value_); |
| if (!(f.Write(0, version_ptr, sizeof(version_)) == sizeof(version_) && |
| f.Write(sizeof(version_), value_ptr, sizeof(value_)) == |
| sizeof(value_))) { |
| PLOG(ERROR) << "cannot write to " << path_.MaybeAsASCII(); |
| return; |
| } |
| synced_ = true; |
| } |
| |
| bool PersistentInteger::Read() { |
| base::File f(path_, base::File::FLAG_OPEN | base::File::FLAG_READ); |
| if (!f.IsValid()) |
| return false; |
| int32_t version; |
| int64_t value; |
| char* version_ptr = reinterpret_cast<char*>(&version); |
| char* value_ptr = reinterpret_cast<char*>(&value); |
| if (f.Read(0, version_ptr, sizeof(version)) != sizeof(version) || |
| version != version_ || |
| f.Read(sizeof(version), value_ptr, sizeof(value)) != sizeof(value)) |
| return false; |
| value_ = value; |
| synced_ = true; |
| return true; |
| } |
| |
| // static |
| void PersistentInteger::SetCreationCallbackForTesting( |
| const base::RepeatingCallback<void(const base::FilePath&)>& |
| creation_callback) { |
| *GetCreationCallback() = creation_callback; |
| } |
| |
| // static |
| void PersistentInteger::ClearCreationCallbackForTesting() { |
| GetCreationCallback()->Reset(); |
| } |
| |
| } // namespace chromeos_metrics |