GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/cache_ram.h
Date: 2026-04-26 02:35:59
Exec Total Coverage
Lines: 32 33 97.0%
Branches: 53 104 51.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4 #ifndef CVMFS_CACHE_RAM_H_
5 #define CVMFS_CACHE_RAM_H_
6
7 #include <pthread.h>
8
9 #include <cassert>
10 #include <cstdlib>
11 #include <string>
12
13 #include "cache.h"
14 #include "crypto/hash.h"
15 #include "fd_table.h"
16 #include "kvstore.h"
17 #include "statistics.h"
18
19
20 /**
21 * The @p RamCacheManager class provides a cache backend that operates
22 * entirely from the host's RAM. This backend does not require any
23 * additional privileges on the host such as filesystem access.
24 * This cache resides in a single process' memory, and does not
25 * support sharing or persistence. This cache backend is a good
26 * choice in highly restrictive environments, e.g. HPC resources,
27 * where it is not possible to make use of a shared/local filesystem.
28 *
29 * To use this cache backend, set @p CVMFS_CACHE_PRIMARY=ram. There
30 * are not many knobs to configure; at present only the size to be
31 * used for cached objects. @p CVMFS_CACHE_RAM_SIZE sets the amount
32 * of memory to use (in MB) for caching objects. If the size ends
33 * with a percent sign, the cache size will be set to that
34 * percentage of the system memory. If no size is specified, the
35 * RAM cache size defaults to ~3% of the system memory.
36 * The minimum cache size is 200 MB.
37 *
38 * RamCacheManager uses a custom heap allocator rather than
39 * the system's libc @p malloc(). To switch to libc malloc, set
40 * @p CVMFS_CACHE_RAM_MALLOC=libc
41 */
42 class RamCacheManager : public CacheManager {
43 public:
44 struct Counters {
45 perf::Counter *n_getsize;
46 perf::Counter *n_close;
47 perf::Counter *n_pread;
48 perf::Counter *n_dup;
49 perf::Counter *n_readahead;
50 perf::Counter *n_starttxn;
51 perf::Counter *n_write;
52 perf::Counter *n_reset;
53 perf::Counter *n_aborttxn;
54 perf::Counter *n_committxn;
55 perf::Counter *n_enfile;
56 perf::Counter *n_openregular;
57 perf::Counter *n_openvolatile;
58 perf::Counter *n_openmiss;
59 perf::Counter *n_overrun;
60 perf::Counter *n_full;
61 perf::Counter *n_realloc;
62
63 1135 explicit Counters(perf::StatisticsTemplate statistics) {
64
3/6
✓ Branch 2 taken 1135 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1135 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1135 times.
✗ Branch 10 not taken.
1135 n_getsize = statistics.RegisterTemplated("n_getsize",
65 "Number of GetSize calls");
66
3/6
✓ Branch 2 taken 1135 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1135 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1135 times.
✗ Branch 10 not taken.
1135 n_close = statistics.RegisterTemplated("n_close",
67 "Number of Close calls");
68
3/6
✓ Branch 2 taken 1135 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1135 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1135 times.
✗ Branch 10 not taken.
1135 n_pread = statistics.RegisterTemplated("n_pread",
69 "Number of Pread calls");
70
3/6
✓ Branch 2 taken 1135 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1135 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1135 times.
✗ Branch 10 not taken.
1135 n_dup = statistics.RegisterTemplated("n_dup", "Number of Dup calls");
71
3/6
✓ Branch 2 taken 1135 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1135 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1135 times.
✗ Branch 10 not taken.
1135 n_readahead = statistics.RegisterTemplated("n_readahead",
72 "Number of ReadAhead calls");
73
3/6
✓ Branch 2 taken 1135 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1135 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1135 times.
✗ Branch 10 not taken.
1135 n_starttxn = statistics.RegisterTemplated("n_starttxn",
74 "Number of StartTxn calls");
75
3/6
✓ Branch 2 taken 1135 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1135 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1135 times.
✗ Branch 10 not taken.
1135 n_write = statistics.RegisterTemplated("n_write",
76 "Number of Write calls");
77
3/6
✓ Branch 2 taken 1135 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1135 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1135 times.
✗ Branch 10 not taken.
1135 n_reset = statistics.RegisterTemplated("n_reset",
78 "Number of Reset calls");
79
3/6
✓ Branch 2 taken 1135 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1135 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1135 times.
✗ Branch 10 not taken.
1135 n_aborttxn = statistics.RegisterTemplated("n_aborttxn",
80 "Number of AbortTxn calls");
81
3/6
✓ Branch 2 taken 1135 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1135 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1135 times.
✗ Branch 10 not taken.
1135 n_committxn = statistics.RegisterTemplated("n_committxn",
82 "Number of Commit calls");
83
3/6
✓ Branch 2 taken 1135 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1135 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1135 times.
✗ Branch 10 not taken.
1135 n_enfile = statistics.RegisterTemplated(
84 "n_enfile", "Number of times the limit on handles was reached");
85
3/6
✓ Branch 2 taken 1135 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1135 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1135 times.
✗ Branch 10 not taken.
1135 n_openregular = statistics.RegisterTemplated(
86 "n_openregular", "Number of opens from the regular cache");
87
3/6
✓ Branch 2 taken 1135 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1135 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1135 times.
✗ Branch 10 not taken.
1135 n_openvolatile = statistics.RegisterTemplated(
88 "n_openvolatile", "Number of opens from the volatile cache");
89
3/6
✓ Branch 2 taken 1135 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1135 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1135 times.
✗ Branch 10 not taken.
1135 n_openmiss = statistics.RegisterTemplated("n_openmiss",
90 "Number of missed opens");
91
3/6
✓ Branch 2 taken 1135 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1135 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1135 times.
✗ Branch 10 not taken.
1135 n_realloc = statistics.RegisterTemplated("n_realloc",
92 "Number of reallocs");
93
3/6
✓ Branch 2 taken 1135 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1135 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1135 times.
✗ Branch 10 not taken.
1135 n_overrun = statistics.RegisterTemplated(
94 "n_overrun", "Number of cache limit overruns");
95
3/6
✓ Branch 2 taken 1135 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1135 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1135 times.
✗ Branch 10 not taken.
1135 n_full = statistics.RegisterTemplated(
96 "n_full", "Number of overruns that could not be resolved");
97 1135 }
98 };
99
100 108 virtual CacheManagerIds id() { return kRamCacheManager; }
101 virtual std::string Describe();
102
103 RamCacheManager(uint64_t max_size,
104 unsigned max_entries,
105 MemoryKvStore::MemoryAllocator alloc,
106 perf::StatisticsTemplate statistics);
107
108 virtual ~RamCacheManager();
109
110 virtual bool AcquireQuotaManager(QuotaManager *quota_mgr);
111
112 /**
113 * Open a new file descriptor into the cache. Note that opening entries
114 * effectively pins them in the cache, so it may be necessary to close
115 * unneeded file descriptors to allow eviction to make room in the cache
116 * @param object The tagged hash key
117 * @returns A nonnegative integer file descriptor
118 * @retval -ENOENT The entry is not in the cache
119 * @retval -ENFILE No handles are available
120 */
121 virtual int Open(const LabeledObject &object);
122
123 /**
124 * Look up the size in bytes of the open cache entry
125 * @param id The hash key
126 * @returns The size of the entry
127 * @retval -EBADF @p fd is not valid
128 */
129 virtual int64_t GetSize(int fd);
130
131 /**
132 * Close the descriptor in the cache. Entries not marked as pinned will become
133 * subject to eviction once closed
134 * @param id The hash key
135 * @retval -EBADF @p fd is not valid
136 */
137 virtual int Close(int fd);
138
139 /**
140 * Read a section from the cache entry. See pread(3) for a discussion of the
141 * arguments
142 * @param id The hash key
143 * @returns The number of bytes copied
144 * @retval -EBADF @p fd is not valid
145 */
146 virtual int64_t Pread(int fd, void *buf, uint64_t size, uint64_t offset);
147
148 /**
149 * Duplicates the open file descriptor, allowing the original and the new one
150 * to be used independently
151 * @param id The hash key
152 * @returns A new, nonnegative integer fd
153 * @retval -EBADF @p fd is not valid
154 * @retval -ENFILE No handles are available
155 */
156 virtual int Dup(int fd);
157
158 /**
159 * No effect for in-memory caches
160 * @param id The hash key
161 * @retval -EBADF @p fd is not valid
162 */
163 virtual int Readahead(int fd);
164
165
166 /**
167 * Get the amount of space to be allocated for a call to @ref StartTxn
168 */
169 1964 virtual uint32_t SizeOfTxn() { return sizeof(Transaction); }
170
171
172 /**
173 * Start a new transaction, allocating the required memory and initializing
174 * the transaction parameters
175 * @param id The hash key
176 * @param size The total size of the new cache entry
177 * @param txn A pointer to space allocated for storing the transaction details
178 */
179 virtual int StartTxn(const shash::Any &id, uint64_t size, void *txn);
180
181 /**
182 * Set the transaction parameters. At present, only @ref type is used for
183 * pinned, volatile, etc.
184 * @param description Unused
185 * @param type The type of the entry, e.g. pinned
186 * @param flags Unused
187 * @param txn A pointer to space allocated for storing the transaction details
188 */
189 virtual void CtrlTxn(const Label &label, const int flags, void *txn);
190
191 /**
192 * Copy the given memory region into the transaction buffer. Copying starts at
193 * the transaction's current offset
194 * @param buf The source address
195 * @param size The number of bytes to copy
196 * @param txn A pointer to space allocated for storing the transaction details
197 */
198 virtual int64_t Write(const void *buf, uint64_t size, void *txn);
199
200 /**
201 * Seek to the beginning of the transaction buffer
202 * @param txn A pointer to space allocated for storing the transaction details
203 */
204 virtual int Reset(void *txn);
205
206 /**
207 * Commit a transaction and open the resulting cache entry. This is necessary
208 * to avoid a race condition in which the cache entry is evicted between the
209 * calls to CommitTxn and Open.
210 * @param txn A pointer to space allocated for storing the transaction details
211 * @returns A file descriptor to the new cache entry, or a negative error from
212 * @ref CommitTxn
213 */
214 virtual int OpenFromTxn(void *txn);
215
216 /**
217 * Free the resources allocated for the pending transaction
218 * @param txn A pointer to space allocated for storing the transaction details
219 */
220 virtual int AbortTxn(void *txn);
221
222 /**
223 * Commit a transaction to the cache. If there is not enough free space in the
224 * cache, first try to make room by evicting volatile entries. If there is
225 * still not enough room, try evicting regular entries. If there is *still*
226 * not enough space, give up an return failure.
227 * @param txn A pointer to space allocated for storing the transaction details
228 * @retval -ENOSPC The transaction would exceed the size of the cache
229 * @retval -ENFILE No handles are available
230 * @retval -EEXIST An entry with nonzero reference count already exists
231 */
232 virtual int CommitTxn(void *txn);
233
234 virtual void Spawn() { }
235
236 private:
237 // The null hash (hashed output is all null bytes) serves as a marker for
238 // an invalid handle
239 static const shash::Any kInvalidHandle;
240
241 struct ReadOnlyHandle {
242 1135 ReadOnlyHandle() : handle(kInvalidHandle), is_volatile(false) { }
243 805 ReadOnlyHandle(const shash::Any &h, bool v) : handle(h), is_volatile(v) { }
244 1082011 bool operator==(const ReadOnlyHandle &other) const {
245 1082011 return this->handle == other.handle;
246 }
247 2161611 bool operator!=(const ReadOnlyHandle &other) const {
248 2161611 return this->handle != other.handle;
249 }
250
251 shash::Any handle;
252 bool is_volatile;
253 };
254
255 struct Transaction {
256 1736 Transaction() : buffer(), expected_size(0), pos(0) { }
257 MemoryBuffer buffer;
258 uint64_t expected_size;
259 uint64_t pos;
260 std::string description;
261 };
262
263 1082092 inline MemoryKvStore *GetStore(const ReadOnlyHandle &fd) {
264
2/2
✓ Branch 0 taken 228 times.
✓ Branch 1 taken 1081864 times.
1082092 if (fd.is_volatile) {
265 228 return &volatile_entries_;
266 } else {
267 1081864 return &regular_entries_;
268 }
269 }
270
271 inline MemoryKvStore *GetTransactionStore(Transaction *txn) {
272 if (txn->buffer.object_flags & CacheManager::kLabelVolatile) {
273 return &volatile_entries_;
274 } else {
275 return &regular_entries_;
276 }
277 }
278
279 int AddFd(const ReadOnlyHandle &handle);
280 int64_t CommitToKvStore(Transaction *transaction);
281 virtual int DoOpen(const shash::Any &id);
282
283 uint64_t max_size_;
284 FdTable<ReadOnlyHandle> fd_table_;
285 pthread_rwlock_t rwlock_;
286 MemoryKvStore regular_entries_;
287 MemoryKvStore volatile_entries_;
288 Counters counters_;
289 }; // class RamCacheManager
290
291 #endif // CVMFS_CACHE_RAM_H_
292