blob: c21216550af5f0e1e9e3e4668e43eca2e534f695 [file] [log] [blame]
// 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);
PCHECK(f.IsValid()) << "cannot open " << path_.MaybeAsASCII();
const char* version_ptr = reinterpret_cast<const char*>(&version_);
const char* value_ptr = reinterpret_cast<const char*>(&value_);
PCHECK(f.Write(0, version_ptr, sizeof(version_)) == sizeof(version_) &&
f.Write(sizeof(version_), value_ptr, sizeof(value_)) == sizeof(value_))
<< "cannot write to " << path_.MaybeAsASCII();
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