// Copyright 2024 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef COMPONENTS_METRICS_STRUCTURED_LIB_PERSISTENT_PROTO_INTERNAL_H_ #define COMPONENTS_METRICS_STRUCTURED_LIB_PERSISTENT_PROTO_INTERNAL_H_ #include #include #include #include "base/files/file_path.h" #include "base/files/important_file_writer.h" #include "base/functional/callback_forward.h" #include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" #include "base/task/sequenced_task_runner.h" #include "base/time/time.h" #include "base/types/expected.h" #include "third_party/protobuf/src/google/protobuf/message_lite.h" namespace metrics::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, }; namespace internal { // Implementation to be used for PersistentProto. Refer to persistent_proto.h // for more details. class PersistentProtoInternal : public base::ImportantFileWriter::DataSerializer { public: using ReadCallback = base::OnceCallback; using WriteCallback = base::RepeatingCallback; PersistentProtoInternal(const base::FilePath& path, base::TimeDelta write_delay, PersistentProtoInternal::ReadCallback on_read, PersistentProtoInternal::WriteCallback on_write); PersistentProtoInternal(const PersistentProtoInternal&) = delete; PersistentProtoInternal& operator=(const PersistentProtoInternal&) = delete; ~PersistentProtoInternal() override; // Retrieves the underlying proto. Must never be null. virtual google::protobuf::MessageLite* GetProto() = 0; google::protobuf::MessageLite* get() { return proto_; } const google::protobuf::MessageLite* get() const { return proto_; } // Queues a write task on the current task runner. void QueueWrite(); // Purges the proto by resetting |proto_| and triggering a write. If called // before |proto_| is ready, |proto_| will be purged once it becomes ready. void Purge(); constexpr bool has_value() const { return proto_ != nullptr; } constexpr explicit operator bool() const { return has_value(); } const base::FilePath& path() { return proto_file_->path(); } // base::ImportantFileWriter::DataSerializer: std::optional SerializeData() override; // Schedules a write to be executed immediately. Only to be used for tests. void StartWriteForTesting(); // Updates the path of this persistent proto to a new file. The contents at // |path| will be merged with existing content of |proto_|. Optional fields // are overwritten and repeated fields are appended. // |on_read| is called once the read of the new path is complete. // |remove_existing| specifies if the existing file should be removed. void UpdatePath(const base::FilePath& path, ReadCallback on_read, bool remove_existing = false); protected: // Cleans up the in-memory proto. void DeallocProto(); private: // Queues a task to delete the backing file. void QueueFileDelete(); // Completes a write if there is a queued one. // // This is needed because it needs to be called by the class that owns the // proto. If this is called in PersistentProtoInternal dtor the owning proto // has already been destructed. void FlushQueuedWrites(); // Serializes |proto_| to |write_buffer_|. void SerializeProtoForWrite(); // Callback when the file has been loaded into a file. void OnReadComplete(ReadCallback callback, base::expected read_status); // Called after |proto_file_| has attempted to write with the write status // captured in |write_successful|. void OnWriteAttempt(bool write_successful); // Called after OnWriteAttempt() or if the write was unsuccessful earlier. void OnWriteComplete(WriteStatus status); // Whether we should immediately clear the proto after reading it. bool purge_after_reading_ = false; // Run when the cache finishes writing to disk, if provided. WriteCallback on_write_; // Boolean to flag whether the path is being updated. // // If the path is being updated queuing a write needs to be blocked. std::atomic_bool updating_path_ = false; // Buffer to be used for flushing |proto_| contents into |proto_file_|. When // it is time to flush |proto_| into disk, a string copy will be stored in // |write_buffer_| to be flushed to avoid race conditions. The buffer will be // flushed when the write is complete. std::string write_buffer_; // The proto itself. raw_ptr proto_ = nullptr; // Task runner for reads and writes to be queued. scoped_refptr task_runner_; // Persistence for |proto_|. std::unique_ptr proto_file_; base::WeakPtrFactory weak_factory_{this}; }; } // namespace internal } // namespace metrics::structured #endif // COMPONENTS_METRICS_STRUCTURED_LIB_PERSISTENT_PROTO_INTERNAL_H_