blob: 2b7cf32ba9fd31c2f86ae1e9c9b3e0fe05a8e97e [file] [log] [blame]
// Copyright 2021 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.
#ifndef METRICS_STRUCTURED_PERSISTENT_PROTO_H_
#define METRICS_STRUCTURED_PERSISTENT_PROTO_H_
#include <memory>
#include <string>
#include <utility>
#include <base/files/file_path.h>
#include <base/macros.h>
#include <base/memory/scoped_refptr.h>
#include <base/memory/weak_ptr.h>
#include <base/optional.h>
#include <base/sequenced_task_runner.h>
#include <base/time/time.h>
namespace metrics {
namespace structured {
// The result of reading a backing file from disk.
enum class ReadStatus {
kOk = 0,
kMissing = 1,
kReadError = 2,
kParseError = 3,
};
// The result of writing a backing file to disk.
enum class WriteStatus {
kOk = 0,
kWriteError = 1,
kSerializationError = 2,
};
// PersistentProto wraps a proto class and persists it to disk. Usage summary.
// - Init is asynchronous, usage before |on_read| is called will crash.
// - pproto->Method() will call Method on the underlying proto.
// - Call QueueWrite() to write to disk.
//
// Reading. The backing file is read asynchronously from disk once at
// initialization, and the |on_read| callback is run once this is done. Until
// |on_read| is called, has_value is false and get() will always return nullptr.
// If no proto file exists on disk, or it is invalid, a blank proto is
// constructed and immediately written to disk.
//
// Writing. Writes must be triggered manually. Two methods are available:
// - QueueWrite() delays writing to disk for |write_delay| time, in order to
// batch successive writes.
// - StartWrite() writes to disk as soon as the task scheduler allows.
// The |on_write| callback is run each time a write has completed.
//
// WARNING. Every proto this class can be used with needs to be listed at the
// bottom of the cc file.
template <class T>
class PersistentProto {
public:
using ReadCallback = base::OnceCallback<void(ReadStatus)>;
using WriteCallback = base::RepeatingCallback<void(WriteStatus)>;
PersistentProto(const base::FilePath& path,
base::TimeDelta write_delay,
typename PersistentProto<T>::ReadCallback on_read,
typename PersistentProto<T>::WriteCallback on_write);
~PersistentProto();
PersistentProto(const PersistentProto&) = delete;
PersistentProto& operator=(const PersistentProto&) = delete;
T* get() { return proto_.get(); }
T* operator->() {
CHECK(proto_);
return proto_.get();
}
T operator*() {
CHECK(proto_);
return *proto_;
}
constexpr bool has_value() const { return proto_.get() != nullptr; }
constexpr explicit operator bool() const { return has_value(); }
// Write the backing proto to disk after |save_delay_ms_| has elapsed.
void QueueWrite();
// Write the backing proto to disk 'now'.
void StartWrite();
// Safely clear this proto from memory and disk. This is preferred to clearing
// the proto, because it ensures the proto is wiped even if called before the
// backing file is read from disk. In this case, the file is overwritten after
// it has been read.
void Wipe();
private:
void OnReadComplete(std::pair<ReadStatus, std::unique_ptr<T>> result);
void OnWriteComplete(WriteStatus status);
void OnQueueWrite();
// Path on disk to read from and write to.
const base::FilePath path_;
// How long to delay writing to disk for on a call to QueueWrite.
const base::TimeDelta write_delay_;
// Whether or not a write is currently scheduled.
bool write_is_queued_ = false;
// Whether we should immediately clear the proto after reading it.
bool wipe_after_reading_ = false;
// Run when the cache finishes reading from disk, if provided.
ReadCallback on_read_;
// Run when the cache finishes writing to disk, if provided.
WriteCallback on_write_;
// The proto itself.
std::unique_ptr<T> proto_;
scoped_refptr<base::SequencedTaskRunner> task_runner_;
base::WeakPtrFactory<PersistentProto> weak_factory_{this};
};
} // namespace structured
} // namespace metrics
#endif // METRICS_STRUCTURED_PERSISTENT_PROTO_H_