• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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