]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ipc/StoreMap.h
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / ipc / StoreMap.h
CommitLineData
bbc27441 1/*
b8ae064d 2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
bbc27441
AJ
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
44c95fcf
AR
9#ifndef SQUID_IPC_STORE_MAP_H
10#define SQUID_IPC_STORE_MAP_H
11
3a8c5551 12#include "ipc/mem/FlexibleArray.h"
68353d5a 13#include "ipc/mem/Pointer.h"
602d9612 14#include "ipc/ReadWriteLock.h"
65e41a45 15#include "sbuf/SBuf.h"
2745fea5 16#include "store/forward.h"
e1ba42a4 17#include "store_key_md5.h"
44c95fcf 18
abf396ec
AR
19#include <functional>
20
9199139f
AR
21namespace Ipc
22{
44c95fcf 23
f13833e9 24typedef int32_t StoreMapSliceId;
bc8b6522
AR
25
26/// a piece of Store entry, linked to other pieces, forming a chain
ce49546e 27/// slices may be appended by writers while readers read the entry
bc8b6522
AR
28class StoreMapSlice
29{
30public:
ce49546e 31 typedef uint32_t Size;
bc8b6522 32
ce49546e 33 StoreMapSlice(): size(0), next(-1) {}
d2b13bab
AJ
34 StoreMapSlice(const StoreMapSlice &o) {
35 size.exchange(o.size);
36 next.exchange(o.next);
37 }
38
39 StoreMapSlice &operator =(const StoreMapSlice &o) {
40 size.store(o.size);
41 next.store(o.next);
42 return *this;
43 }
44
c2037547
EB
45 /// restore default-constructed state
46 void clear() { size = 0; next = -1; }
47
d2b13bab
AJ
48 std::atomic<Size> size; ///< slice contents size
49 std::atomic<StoreMapSliceId> next; ///< ID of the next entry slice
bc8b6522
AR
50};
51
50dc81ec
AR
52/// Maintains shareable information about a StoreEntry as a whole.
53/// An anchor points to one or more StoreEntry slices. This is the
54/// only lockable part of shared StoreEntry information, providing
55/// protection for all StoreEntry slices.
56class StoreMapAnchor
9199139f 57{
44c95fcf 58public:
50dc81ec 59 StoreMapAnchor();
44c95fcf 60
50dc81ec 61 /// store StoreEntry key and basics for an inode slot
4310f8b0
EB
62 void set(const StoreEntry &anEntry, const cache_key *aKey = nullptr);
63 /// load StoreEntry basics that were previously stored with set()
64 void exportInto(StoreEntry &) const;
44c95fcf
AR
65
66 void setKey(const cache_key *const aKey);
67 bool sameKey(const cache_key *const aKey) const;
68
50dc81ec
AR
69 /// undo the effects of set(), setKey(), etc., but keep locks and state
70 void rewind();
71
ce49546e
AR
72 /* entry state may change immediately after calling these methods unless
73 * the caller holds an appropriate lock */
74 bool empty() const { return !key[0] && !key[1]; }
75 bool reading() const { return lock.readers; }
a3023c03 76 bool writing() const { return lock.writing; }
ce49546e
AR
77 bool complete() const { return !empty() && !writing(); }
78
44c95fcf
AR
79public:
80 mutable ReadWriteLock lock; ///< protects slot data below
d2b13bab 81 std::atomic<uint8_t> waitingToBeFreed; ///< may be accessed w/o a lock
4310f8b0
EB
82 /// whether StoreMap::abortWriting() was called for a read-locked entry
83 std::atomic<uint8_t> writerHalted;
44c95fcf 84
ce49546e 85 // fields marked with [app] can be modified when appending-while-reading
abf396ec 86 // fields marked with [update] can be modified when updating-while-reading
ce49546e 87
b56b37cf 88 uint64_t key[2] = {0, 0}; ///< StoreEntry key
44c95fcf
AR
89
90 // STORE_META_STD TLV field from StoreEntry
91 struct Basics {
b56b37cf
AJ
92 void clear() {
93 timestamp = 0;
94 lastref = 0;
95 expires = 0;
96 lastmod = 0;
97 swap_file_sz.store(0);
98 refcount = 0;
99 flags = 0;
100 }
101 time_t timestamp = 0;
102 time_t lastref = 0;
103 time_t expires = 0;
104 time_t lastmod = 0;
d2b13bab 105 std::atomic<uint64_t> swap_file_sz; // [app]
b56b37cf
AJ
106 uint16_t refcount = 0;
107 uint16_t flags = 0;
9199139f 108 } basics;
44c95fcf 109
e6d2c263 110 /// where the chain of StoreEntry slices begins [app]
d2b13bab 111 std::atomic<StoreMapSliceId> start;
abf396ec
AR
112
113 /// where the updated chain prefix containing metadata/headers ends [update]
114 /// if unset, this anchor points to a chain that was never updated
115 std::atomic<StoreMapSliceId> splicingPoint;
1860fbac
AR
116};
117
118/// an array of shareable Items
119/// must be the last data member or, if used as a parent class, the last parent
120template <class C>
121class StoreMapItems
122{
123public:
124 typedef C Item;
125 typedef Ipc::Mem::Owner< StoreMapItems<Item> > Owner;
126
127 explicit StoreMapItems(const int aCapacity): capacity(aCapacity), items(aCapacity) {}
50dc81ec 128
1860fbac
AR
129 size_t sharedMemorySize() const { return SharedMemorySize(capacity); }
130 static size_t SharedMemorySize(const int aCapacity) { return sizeof(StoreMapItems<Item>) + aCapacity*sizeof(Item); }
131
8ecbe78d
EB
132 Item &at(const int index)
133 {
134 assert(index >= 0);
135 assert(index < capacity);
136 return items[index];
137 }
138
139 const Item &at(const int index) const
140 {
141 return const_cast<StoreMapItems<C>&>(*this).at(index);
142 }
143
144 /// reset all items to the same value
145 void fill(const Item &value)
146 {
147 for (int index = 0; index < capacity; ++index)
148 items[index] = value;
149 }
150
1860fbac
AR
151 const int capacity; ///< total number of items
152 Ipc::Mem::FlexibleArray<Item> items; ///< storage
44c95fcf
AR
153};
154
1860fbac
AR
155/// StoreMapSlices indexed by their slice ID.
156typedef StoreMapItems<StoreMapSlice> StoreMapSlices;
157
abf396ec 158/// StoreMapAnchors (indexed by fileno) plus
1860fbac
AR
159/// sharing-safe basic housekeeping info about Store entries
160class StoreMapAnchors
9d4e9cfb 161{
50dc81ec 162public:
1860fbac
AR
163 typedef Ipc::Mem::Owner< StoreMapAnchors > Owner;
164
165 explicit StoreMapAnchors(const int aCapacity);
166
167 size_t sharedMemorySize() const;
168 static size_t SharedMemorySize(const int anAnchorLimit);
169
d2b13bab
AJ
170 std::atomic<int32_t> count; ///< current number of entries
171 std::atomic<uint32_t> victim; ///< starting point for purge search
1860fbac
AR
172 const int capacity; ///< total number of anchors
173 Ipc::Mem::FlexibleArray<StoreMapAnchor> items; ///< anchors storage
50dc81ec 174};
1860fbac 175// TODO: Find an elegant way to use StoreMapItems in StoreMapAnchors
50dc81ec 176
abf396ec
AR
177/// StoreMapAnchor positions, indexed by entry "name" (i.e., the entry key hash)
178typedef StoreMapItems< std::atomic<sfileno> > StoreMapFileNos;
179
180/// Aggregates information required for updating entry metadata and headers.
181class StoreMapUpdate
182{
183public:
184 /// During an update, the stored entry has two editions: stale and fresh.
185 class Edition
186 {
187 public:
188 Edition(): anchor(nullptr), fileNo(-1), name(-1), splicingPoint(-1) {}
189
190 /// whether this entry edition is currently used/initialized
191 explicit operator bool() const { return anchor; }
192
193 StoreMapAnchor *anchor; ///< StoreMap::anchors[fileNo], for convenience/speed
194 sfileno fileNo; ///< StoreMap::fileNos[name], for convenience/speed
195 sfileno name; ///< StoreEntry position in StoreMap::fileNos, for swapping Editions
196
197 /// the last slice in the chain still containing metadata/headers
198 StoreMapSliceId splicingPoint;
199 };
200
201 explicit StoreMapUpdate(StoreEntry *anEntry);
202 StoreMapUpdate(const StoreMapUpdate &other);
203 ~StoreMapUpdate();
204
205 StoreMapUpdate &operator =(const StoreMapUpdate &other) = delete;
206
207 StoreEntry *entry; ///< the store entry being updated
66d51f4f
AR
208 Edition stale; ///< old anchor and chain
209 Edition fresh; ///< new anchor and the updated chain prefix
abf396ec
AR
210};
211
7f6748c8
AR
212class StoreMapCleaner;
213
1860fbac 214/// Manages shared Store index (e.g., locking/unlocking/freeing entries) using
abf396ec
AR
215/// StoreMapFileNos indexed by hashed entry keys (a.k.a. entry names),
216/// StoreMapAnchors indexed by fileno, and
217/// StoreMapSlices indexed by slice ID.
44c95fcf
AR
218class StoreMap
219{
220public:
abf396ec 221 typedef StoreMapFileNos FileNos;
50dc81ec 222 typedef StoreMapAnchor Anchor;
1860fbac 223 typedef StoreMapAnchors Anchors;
50dc81ec
AR
224 typedef sfileno AnchorId;
225 typedef StoreMapSlice Slice;
1860fbac 226 typedef StoreMapSlices Slices;
50dc81ec 227 typedef StoreMapSliceId SliceId;
abf396ec 228 typedef StoreMapUpdate Update;
44c95fcf 229
1860fbac
AR
230public:
231 /// aggregates anchor and slice owners for Init() caller convenience
2da4bfe6
A
232 class Owner
233 {
f8f98441 234 public:
1860fbac
AR
235 Owner();
236 ~Owner();
abf396ec 237 FileNos::Owner *fileNos;
1860fbac
AR
238 Anchors::Owner *anchors;
239 Slices::Owner *slices;
240 private:
241 Owner(const Owner &); // not implemented
242 Owner &operator =(const Owner &); // not implemented
68353d5a
DK
243 };
244
68353d5a 245 /// initialize shared memory
1860fbac 246 static Owner *Init(const SBuf &path, const int slotLimit);
68353d5a 247
1860fbac 248 StoreMap(const SBuf &aPath);
44c95fcf 249
abf396ec
AR
250 /// computes map entry anchor position for a given entry key
251 sfileno fileNoByKey(const cache_key *const key) const;
50dc81ec
AR
252
253 /// Like strcmp(mapped, new), but for store entry versions/timestamps.
254 /// Returns +2 if the mapped entry does not exist; -1/0/+1 otherwise.
255 /// Comparison may be inaccurate unless the caller is a lock holder.
256 int compareVersions(const sfileno oldFileno, time_t newVersion) const;
257
258 /// finds, locks, and returns an anchor for an empty key position,
259 /// erasing the old entry (if any)
260 Anchor *openForWriting(const cache_key *const key, sfileno &fileno);
261 /// locks and returns an anchor for the empty fileno position; if
262 /// overwriteExisting is false and the position is not empty, returns nil
263 Anchor *openForWritingAt(sfileno fileno, bool overwriteExisting = true);
ce49546e
AR
264 /// restrict opened for writing entry to appending operations; allow reads
265 void startAppending(const sfileno fileno);
50dc81ec 266 /// successfully finish creating or updating the entry at fileno pos
8253d451
AR
267 void closeForWriting(const sfileno fileno);
268 /// stop writing (or updating) the locked entry and start reading it
269 void switchWritingToReading(const sfileno fileno);
50dc81ec
AR
270 /// unlock and "forget" openForWriting entry, making it Empty again
271 /// this call does not free entry slices so the caller has to do that
272 void forgetWritingEntry(const sfileno fileno);
273
abf396ec
AR
274 /// finds and locks the Update entry for an exclusive metadata update
275 bool openForUpdating(Update &update, sfileno fileNoHint);
276 /// makes updated info available to others, unlocks, and cleans up
277 void closeForUpdating(Update &update);
278 /// undoes partial update, unlocks, and cleans up
279 void abortUpdating(Update &update);
280
d1d3b4dc
EB
281 /// the caller must hold a lock on the entry
282 /// \returns nullptr unless the slice is readable
50dc81ec
AR
283 const Anchor *peekAtReader(const sfileno fileno) const;
284
d1d3b4dc
EB
285 /// the caller must hold a lock on the entry
286 /// \returns nullptr unless the slice is writeable
287 const Anchor *peekAtWriter(const sfileno fileno) const;
288
289 /// the caller must hold a lock on the entry
290 /// \returns the corresponding Anchor
d366a7fa
AR
291 const Anchor &peekAtEntry(const sfileno fileno) const;
292
ce49546e 293 /// free the entry if possible or mark it as waiting to be freed if not
4310f8b0
EB
294 /// \returns whether the entry was neither empty nor marked
295 bool freeEntry(const sfileno);
ce49546e
AR
296 /// free the entry if possible or mark it as waiting to be freed if not
297 /// does nothing if we cannot check that the key matches the cached entry
298 void freeEntryByKey(const cache_key *const key);
50dc81ec 299
4310f8b0
EB
300 /// whether the entry with the given key exists and was marked as
301 /// "waiting to be freed" some time ago
302 bool markedForDeletion(const cache_key *const);
303
304 /// whether the index contains a valid readable entry with the given key
305 bool hasReadableEntry(const cache_key *const);
306
50dc81ec
AR
307 /// opens entry (identified by key) for reading, increments read level
308 const Anchor *openForReading(const cache_key *const key, sfileno &fileno);
309 /// opens entry (identified by sfileno) for reading, increments read level
b2aca62a 310 const Anchor *openForReadingAt(const sfileno, const cache_key *const);
50dc81ec
AR
311 /// closes open entry after reading, decrements read level
312 void closeForReading(const sfileno fileno);
d1d3b4dc
EB
313 /// same as closeForReading() but also frees the entry if it is unlocked
314 void closeForReadingAndFreeIdle(const sfileno fileno);
44c95fcf 315
5bd8ce13
AR
316 /// openForReading() but creates a new entry if there is no old one
317 const Anchor *openOrCreateForReading(const cache_key *, sfileno &, const StoreEntry &);
318
50dc81ec
AR
319 /// writeable slice within an entry chain created by openForWriting()
320 Slice &writeableSlice(const AnchorId anchorId, const SliceId sliceId);
321 /// readable slice within an entry chain opened by openForReading()
322 const Slice &readableSlice(const AnchorId anchorId, const SliceId sliceId) const;
323 /// writeable anchor for the entry created by openForWriting()
324 Anchor &writeableEntry(const AnchorId anchorId);
ce49546e
AR
325 /// readable anchor for the entry created by openForReading()
326 const Anchor &readableEntry(const AnchorId anchorId) const;
44c95fcf 327
c2037547
EB
328 /// prepare a chain-unaffiliated slice for being added to an entry chain
329 void prepFreeSlice(const SliceId sliceId);
330
abf396ec
AR
331 /// Returns the ID of the entry slice containing n-th byte or
332 /// a negative ID if the entry does not store that many bytes (yet).
333 /// Requires a read lock.
334 SliceId sliceContaining(const sfileno fileno, const uint64_t nth) const;
335
a3023c03
AR
336 /// stop writing the entry, freeing its slot for others to use if possible
337 void abortWriting(const sfileno fileno);
44c95fcf 338
5bba33c9 339 /// either finds and frees an entry with at least 1 slice or returns false
50dc81ec 340 bool purgeOne();
44c95fcf 341
b2aca62a
EB
342 /// validates locked hit metadata and calls freeEntry() for invalid entries
343 /// \returns whether hit metadata is correct
344 bool validateHit(const sfileno);
345
346 void disableHitValidation() { hitValidation = false; }
347
50dc81ec
AR
348 /// copies slice to its designated position
349 void importSlice(const SliceId sliceId, const Slice &slice);
44c95fcf 350
36c84e19
AR
351 /* SwapFilenMax limits the number of entries, but not slices or slots */
352 bool validEntry(const int n) const; ///< whether n is a valid slice coordinate
353 bool validSlice(const int n) const; ///< whether n is a valid slice coordinate
50dc81ec
AR
354 int entryCount() const; ///< number of writeable and readable entries
355 int entryLimit() const; ///< maximum entryCount() possible
36c84e19 356 int sliceLimit() const; ///< maximum number of slices possible
44c95fcf
AR
357
358 /// adds approximate current stats to the supplied ones
359 void updateStats(ReadWriteLockStats &stats) const;
360
7f6748c8
AR
361 StoreMapCleaner *cleaner; ///< notified before a readable entry is freed
362
44c95fcf 363protected:
1860fbac 364 const SBuf path; ///< cache_dir path or similar cache name; for logging
abf396ec 365 Mem::Pointer<StoreMapFileNos> fileNos; ///< entry inodes (starting blocks)
1860fbac
AR
366 Mem::Pointer<StoreMapAnchors> anchors; ///< entry inodes (starting blocks)
367 Mem::Pointer<StoreMapSlices> slices; ///< chained entry pieces positions
44c95fcf
AR
368
369private:
abf396ec
AR
370 /// computes entry name (i.e., key hash) for a given entry key
371 sfileno nameByKey(const cache_key *const key) const;
372 /// computes anchor position for a given entry name
373 sfileno fileNoByName(const sfileno name) const;
374 void relocate(const sfileno name, const sfileno fileno);
375
1860fbac
AR
376 Anchor &anchorAt(const sfileno fileno);
377 const Anchor &anchorAt(const sfileno fileno) const;
50dc81ec 378 Anchor &anchorByKey(const cache_key *const key);
44c95fcf 379
1860fbac
AR
380 Slice &sliceAt(const SliceId sliceId);
381 const Slice &sliceAt(const SliceId sliceId) const;
50dc81ec 382 Anchor *openForReading(Slice &s);
abf396ec
AR
383 bool openKeyless(Update::Edition &edition);
384 void closeForUpdateFinal(Update &update);
385
386 typedef std::function<bool (const sfileno name)> NameFilter; // a "name"-based test
387 bool visitVictims(const NameFilter filter);
50dc81ec
AR
388
389 void freeChain(const sfileno fileno, Anchor &inode, const bool keepLock);
abf396ec 390 void freeChainAt(SliceId sliceId, const SliceId splicingPoint);
b2aca62a
EB
391
392 /// whether paranoid_hit_validation should be performed
393 bool hitValidation;
68353d5a 394};
44c95fcf 395
50dc81ec 396/// API for adjusting external state when dirty map slice is being freed
7f6748c8
AR
397class StoreMapCleaner
398{
399public:
400 virtual ~StoreMapCleaner() {}
401
50dc81ec 402 /// adjust slice-linked state before a locked Readable slice is erased
36c84e19 403 virtual void noteFreeMapSlice(const StoreMapSliceId sliceId) = 0;
7f6748c8
AR
404};
405
44c95fcf
AR
406} // namespace Ipc
407
75f8f9a2 408// We do not reuse FileMap because we cannot control its size,
44c95fcf
AR
409// resulting in sfilenos that are pointing beyond the database.
410
411#endif /* SQUID_IPC_STORE_MAP_H */
f53969cc 412