1 // Copyright 2024 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef COMPONENTS_METRICS_STRUCTURED_LIB_PERSISTENT_PROTO_INTERNAL_H_ 6 #define COMPONENTS_METRICS_STRUCTURED_LIB_PERSISTENT_PROTO_INTERNAL_H_ 7 8 #include <atomic> 9 #include <memory> 10 #include <string> 11 12 #include "base/files/file_path.h" 13 #include "base/files/important_file_writer.h" 14 #include "base/functional/callback_forward.h" 15 #include "base/memory/scoped_refptr.h" 16 #include "base/memory/weak_ptr.h" 17 #include "base/task/sequenced_task_runner.h" 18 #include "base/time/time.h" 19 #include "base/types/expected.h" 20 #include "third_party/protobuf/src/google/protobuf/message_lite.h" 21 22 namespace metrics::structured { 23 24 // The result of reading a backing file from disk. 25 enum class ReadStatus { 26 kOk = 0, 27 kMissing = 1, 28 kReadError = 2, 29 kParseError = 3, 30 }; 31 32 // The result of writing a backing file to disk. 33 enum class WriteStatus { 34 kOk = 0, 35 kWriteError = 1, 36 kSerializationError = 2, 37 }; 38 39 namespace internal { 40 41 // Implementation to be used for PersistentProto. Refer to persistent_proto.h 42 // for more details. 43 class PersistentProtoInternal 44 : public base::ImportantFileWriter::DataSerializer { 45 public: 46 using ReadCallback = base::OnceCallback<void(ReadStatus)>; 47 using WriteCallback = base::RepeatingCallback<void(WriteStatus)>; 48 49 PersistentProtoInternal(const base::FilePath& path, 50 base::TimeDelta write_delay, 51 PersistentProtoInternal::ReadCallback on_read, 52 PersistentProtoInternal::WriteCallback on_write); 53 54 PersistentProtoInternal(const PersistentProtoInternal&) = delete; 55 PersistentProtoInternal& operator=(const PersistentProtoInternal&) = delete; 56 57 ~PersistentProtoInternal() override; 58 59 // Retrieves the underlying proto. Must never be null. 60 virtual google::protobuf::MessageLite* GetProto() = 0; 61 get()62 google::protobuf::MessageLite* get() { return proto_; } get()63 const google::protobuf::MessageLite* get() const { return proto_; } 64 65 // Queues a write task on the current task runner. 66 void QueueWrite(); 67 68 // Purges the proto by resetting |proto_| and triggering a write. If called 69 // before |proto_| is ready, |proto_| will be purged once it becomes ready. 70 void Purge(); 71 has_value()72 constexpr bool has_value() const { return proto_ != nullptr; } 73 74 constexpr explicit operator bool() const { return has_value(); } 75 path()76 const base::FilePath& path() { return proto_file_->path(); } 77 78 // base::ImportantFileWriter::DataSerializer: 79 std::optional<std::string> SerializeData() override; 80 81 // Schedules a write to be executed immediately. Only to be used for tests. 82 void StartWriteForTesting(); 83 84 // Updates the path of this persistent proto to a new file. The contents at 85 // |path| will be merged with existing content of |proto_|. Optional fields 86 // are overwritten and repeated fields are appended. 87 // |on_read| is called once the read of the new path is complete. 88 // |remove_existing| specifies if the existing file should be removed. 89 void UpdatePath(const base::FilePath& path, 90 ReadCallback on_read, 91 bool remove_existing = false); 92 93 protected: 94 // Cleans up the in-memory proto. 95 void DeallocProto(); 96 97 private: 98 // Queues a task to delete the backing file. 99 void QueueFileDelete(); 100 101 // Completes a write if there is a queued one. 102 // 103 // This is needed because it needs to be called by the class that owns the 104 // proto. If this is called in PersistentProtoInternal dtor the owning proto 105 // has already been destructed. 106 void FlushQueuedWrites(); 107 108 // Serializes |proto_| to |write_buffer_|. 109 void SerializeProtoForWrite(); 110 111 // Callback when the file has been loaded into a file. 112 void OnReadComplete(ReadCallback callback, 113 base::expected<std::string, ReadStatus> read_status); 114 115 // Called after |proto_file_| has attempted to write with the write status 116 // captured in |write_successful|. 117 void OnWriteAttempt(bool write_successful); 118 119 // Called after OnWriteAttempt() or if the write was unsuccessful earlier. 120 void OnWriteComplete(WriteStatus status); 121 122 // Whether we should immediately clear the proto after reading it. 123 bool purge_after_reading_ = false; 124 125 // Run when the cache finishes writing to disk, if provided. 126 WriteCallback on_write_; 127 128 // Boolean to flag whether the path is being updated. 129 // 130 // If the path is being updated queuing a write needs to be blocked. 131 std::atomic_bool updating_path_ = false; 132 133 // Buffer to be used for flushing |proto_| contents into |proto_file_|. When 134 // it is time to flush |proto_| into disk, a string copy will be stored in 135 // |write_buffer_| to be flushed to avoid race conditions. The buffer will be 136 // flushed when the write is complete. 137 std::string write_buffer_; 138 139 // The proto itself. 140 raw_ptr<google::protobuf::MessageLite> proto_ = nullptr; 141 142 // Task runner for reads and writes to be queued. 143 scoped_refptr<base::SequencedTaskRunner> task_runner_; 144 145 // Persistence for |proto_|. 146 std::unique_ptr<base::ImportantFileWriter> proto_file_; 147 148 base::WeakPtrFactory<PersistentProtoInternal> weak_factory_{this}; 149 }; 150 151 } // namespace internal 152 } // namespace metrics::structured 153 154 #endif // COMPONENTS_METRICS_STRUCTURED_LIB_PERSISTENT_PROTO_INTERNAL_H_ 155