]>
Commit | Line | Data |
---|---|---|
44c95fcf AR |
1 | #ifndef SQUID_IPC_STORE_MAP_H |
2 | #define SQUID_IPC_STORE_MAP_H | |
3 | ||
3a8c5551 | 4 | #include "ipc/mem/FlexibleArray.h" |
68353d5a | 5 | #include "ipc/mem/Pointer.h" |
602d9612 | 6 | #include "ipc/ReadWriteLock.h" |
6d68a230 | 7 | #include "typedefs.h" |
44c95fcf | 8 | |
9199139f AR |
9 | namespace Ipc |
10 | { | |
44c95fcf | 11 | |
f13833e9 | 12 | typedef int32_t StoreMapSliceId; |
bc8b6522 AR |
13 | |
14 | /// a piece of Store entry, linked to other pieces, forming a chain | |
ce49546e | 15 | /// slices may be appended by writers while readers read the entry |
bc8b6522 AR |
16 | class StoreMapSlice |
17 | { | |
18 | public: | |
ce49546e | 19 | typedef uint32_t Size; |
bc8b6522 | 20 | |
ce49546e AR |
21 | StoreMapSlice(): size(0), next(-1) {} |
22 | ||
23 | Atomic::WordT<Size> size; ///< slice contents size | |
24 | Atomic::WordT<StoreMapSliceId> next; ///< ID of the next entry slice | |
bc8b6522 AR |
25 | }; |
26 | ||
27 | ||
50dc81ec AR |
28 | /// Maintains shareable information about a StoreEntry as a whole. |
29 | /// An anchor points to one or more StoreEntry slices. This is the | |
30 | /// only lockable part of shared StoreEntry information, providing | |
31 | /// protection for all StoreEntry slices. | |
32 | class StoreMapAnchor | |
9199139f | 33 | { |
44c95fcf | 34 | public: |
50dc81ec | 35 | StoreMapAnchor(); |
44c95fcf | 36 | |
50dc81ec | 37 | /// store StoreEntry key and basics for an inode slot |
44c95fcf AR |
38 | void set(const StoreEntry &anEntry); |
39 | ||
40 | void setKey(const cache_key *const aKey); | |
41 | bool sameKey(const cache_key *const aKey) const; | |
42 | ||
50dc81ec AR |
43 | /// undo the effects of set(), setKey(), etc., but keep locks and state |
44 | void rewind(); | |
45 | ||
ce49546e AR |
46 | /* entry state may change immediately after calling these methods unless |
47 | * the caller holds an appropriate lock */ | |
48 | bool empty() const { return !key[0] && !key[1]; } | |
49 | bool reading() const { return lock.readers; } | |
a3023c03 | 50 | bool writing() const { return lock.writing; } |
ce49546e AR |
51 | bool complete() const { return !empty() && !writing(); } |
52 | ||
44c95fcf AR |
53 | public: |
54 | mutable ReadWriteLock lock; ///< protects slot data below | |
794d4c0c | 55 | Atomic::WordT<uint8_t> waitingToBeFreed; ///< may be accessed w/o a lock |
44c95fcf | 56 | |
ce49546e AR |
57 | // fields marked with [app] can be modified when appending-while-reading |
58 | ||
44c95fcf AR |
59 | uint64_t key[2]; ///< StoreEntry key |
60 | ||
61 | // STORE_META_STD TLV field from StoreEntry | |
62 | struct Basics { | |
63 | time_t timestamp; | |
64 | time_t lastref; | |
65 | time_t expires; | |
66 | time_t lastmod; | |
e6d2c263 | 67 | Atomic::WordT<uint64_t> swap_file_sz; // [app] |
89924985 AR |
68 | uint16_t refcount; |
69 | uint16_t flags; | |
9199139f | 70 | } basics; |
44c95fcf | 71 | |
e6d2c263 AR |
72 | /// where the chain of StoreEntry slices begins [app] |
73 | Atomic::WordT<StoreMapSliceId> start; | |
50dc81ec | 74 | |
ce49546e | 75 | #if 0 |
44c95fcf AR |
76 | /// possible persistent states |
77 | typedef enum { | |
78 | Empty, ///< ready for writing, with nothing of value | |
79 | Writeable, ///< transitions from Empty to Readable | |
80 | Readable, ///< ready for reading | |
9199139f | 81 | } State; |
44c95fcf | 82 | State state; ///< current state |
ce49546e | 83 | #endif |
44c95fcf AR |
84 | }; |
85 | ||
6d68a230 AR |
86 | /// A hack to allocate one shared array for both anchors and slices. |
87 | /// Anchors are indexed by store entry ID and are independent from each other. | |
88 | /// Slices are indexed by slice IDs and form entry chains using slice.next. | |
50dc81ec AR |
89 | class StoreMapSlot { |
90 | public: | |
6d68a230 AR |
91 | StoreMapAnchor anchor; ///< information about store entry as a whole |
92 | StoreMapSlice slice; ///< information about one stored entry piece | |
50dc81ec AR |
93 | }; |
94 | ||
7f6748c8 AR |
95 | class StoreMapCleaner; |
96 | ||
50dc81ec | 97 | /// map of StoreMapSlots indexed by their keys, with read/write slice locking |
44c95fcf AR |
98 | /// kids extend to store custom data |
99 | class StoreMap | |
100 | { | |
101 | public: | |
50dc81ec AR |
102 | typedef StoreMapAnchor Anchor; |
103 | typedef sfileno AnchorId; | |
104 | typedef StoreMapSlice Slice; | |
105 | typedef StoreMapSliceId SliceId; | |
44c95fcf | 106 | |
f8f98441 | 107 | /// data shared across maps in different processes |
fc3921ec A |
108 | class Shared |
109 | { | |
f8f98441 | 110 | public: |
68353d5a DK |
111 | Shared(const int aLimit, const size_t anExtrasSize); |
112 | size_t sharedMemorySize() const; | |
113 | static size_t SharedMemorySize(const int limit, const size_t anExtrasSize); | |
114 | ||
50dc81ec AR |
115 | const int limit; ///< maximum number of store entries |
116 | const size_t extrasSize; ///< size of slice extra data | |
117 | Atomic::Word count; ///< current number of entries | |
02458e9b | 118 | Atomic::WordT<uint32_t> victim; ///< starting point for purge search |
50dc81ec | 119 | Ipc::Mem::FlexibleArray<StoreMapSlot> slots; ///< storage |
68353d5a DK |
120 | }; |
121 | ||
122 | public: | |
123 | typedef Mem::Owner<Shared> Owner; | |
124 | ||
125 | /// initialize shared memory | |
126 | static Owner *Init(const char *const path, const int limit); | |
127 | ||
128 | StoreMap(const char *const aPath); | |
44c95fcf | 129 | |
50dc81ec AR |
130 | /// computes map entry position for a given entry key |
131 | sfileno anchorIndexByKey(const cache_key *const key) const; | |
132 | ||
133 | /// Like strcmp(mapped, new), but for store entry versions/timestamps. | |
134 | /// Returns +2 if the mapped entry does not exist; -1/0/+1 otherwise. | |
135 | /// Comparison may be inaccurate unless the caller is a lock holder. | |
136 | int compareVersions(const sfileno oldFileno, time_t newVersion) const; | |
137 | ||
138 | /// finds, locks, and returns an anchor for an empty key position, | |
139 | /// erasing the old entry (if any) | |
140 | Anchor *openForWriting(const cache_key *const key, sfileno &fileno); | |
141 | /// locks and returns an anchor for the empty fileno position; if | |
142 | /// overwriteExisting is false and the position is not empty, returns nil | |
143 | Anchor *openForWritingAt(sfileno fileno, bool overwriteExisting = true); | |
ce49546e AR |
144 | /// restrict opened for writing entry to appending operations; allow reads |
145 | void startAppending(const sfileno fileno); | |
50dc81ec | 146 | /// successfully finish creating or updating the entry at fileno pos |
44c95fcf | 147 | void closeForWriting(const sfileno fileno, bool lockForReading = false); |
50dc81ec AR |
148 | /// unlock and "forget" openForWriting entry, making it Empty again |
149 | /// this call does not free entry slices so the caller has to do that | |
150 | void forgetWritingEntry(const sfileno fileno); | |
151 | ||
152 | /// only works on locked entries; returns nil unless the slice is readable | |
153 | const Anchor *peekAtReader(const sfileno fileno) const; | |
154 | ||
d366a7fa AR |
155 | /// only works on locked entries; returns the corresponding Anchor |
156 | const Anchor &peekAtEntry(const sfileno fileno) const; | |
157 | ||
ce49546e | 158 | /// free the entry if possible or mark it as waiting to be freed if not |
50dc81ec | 159 | void freeEntry(const sfileno fileno); |
ce49546e AR |
160 | /// free the entry if possible or mark it as waiting to be freed if not |
161 | /// does nothing if we cannot check that the key matches the cached entry | |
162 | void freeEntryByKey(const cache_key *const key); | |
50dc81ec AR |
163 | |
164 | /// opens entry (identified by key) for reading, increments read level | |
165 | const Anchor *openForReading(const cache_key *const key, sfileno &fileno); | |
166 | /// opens entry (identified by sfileno) for reading, increments read level | |
167 | const Anchor *openForReadingAt(const sfileno fileno); | |
168 | /// closes open entry after reading, decrements read level | |
169 | void closeForReading(const sfileno fileno); | |
44c95fcf | 170 | |
50dc81ec AR |
171 | /// writeable slice within an entry chain created by openForWriting() |
172 | Slice &writeableSlice(const AnchorId anchorId, const SliceId sliceId); | |
173 | /// readable slice within an entry chain opened by openForReading() | |
174 | const Slice &readableSlice(const AnchorId anchorId, const SliceId sliceId) const; | |
175 | /// writeable anchor for the entry created by openForWriting() | |
176 | Anchor &writeableEntry(const AnchorId anchorId); | |
ce49546e AR |
177 | /// readable anchor for the entry created by openForReading() |
178 | const Anchor &readableEntry(const AnchorId anchorId) const; | |
44c95fcf | 179 | |
a3023c03 AR |
180 | /// stop writing the entry, freeing its slot for others to use if possible |
181 | void abortWriting(const sfileno fileno); | |
44c95fcf | 182 | |
5bba33c9 | 183 | /// either finds and frees an entry with at least 1 slice or returns false |
50dc81ec | 184 | bool purgeOne(); |
44c95fcf | 185 | |
50dc81ec AR |
186 | /// copies slice to its designated position |
187 | void importSlice(const SliceId sliceId, const Slice &slice); | |
44c95fcf | 188 | |
50dc81ec AR |
189 | bool valid(const int n) const; ///< whether n is a valid slice coordinate |
190 | int entryCount() const; ///< number of writeable and readable entries | |
191 | int entryLimit() const; ///< maximum entryCount() possible | |
44c95fcf AR |
192 | |
193 | /// adds approximate current stats to the supplied ones | |
194 | void updateStats(ReadWriteLockStats &stats) const; | |
195 | ||
7f6748c8 AR |
196 | StoreMapCleaner *cleaner; ///< notified before a readable entry is freed |
197 | ||
44c95fcf | 198 | protected: |
68353d5a | 199 | static Owner *Init(const char *const path, const int limit, const size_t extrasSize); |
44c95fcf | 200 | |
920378ba | 201 | const String path; ///< cache_dir path or similar cache name; for logging |
68353d5a | 202 | Mem::Pointer<Shared> shared; |
44c95fcf AR |
203 | |
204 | private: | |
50dc81ec | 205 | Anchor &anchorByKey(const cache_key *const key); |
44c95fcf | 206 | |
50dc81ec | 207 | Anchor *openForReading(Slice &s); |
50dc81ec AR |
208 | |
209 | void freeChain(const sfileno fileno, Anchor &inode, const bool keepLock); | |
68353d5a | 210 | }; |
44c95fcf | 211 | |
50dc81ec | 212 | /// StoreMap with extra slice data |
68353d5a DK |
213 | /// Note: ExtrasT must be POD, it is initialized with zeroes, no |
214 | /// constructors or destructors are called | |
215 | template <class ExtrasT> | |
216 | class StoreMapWithExtras: public StoreMap | |
217 | { | |
218 | public: | |
219 | typedef ExtrasT Extras; | |
220 | ||
221 | /// initialize shared memory | |
222 | static Owner *Init(const char *const path, const int limit); | |
223 | ||
224 | StoreMapWithExtras(const char *const path); | |
225 | ||
226 | /// write access to the extras; call openForWriting() first! | |
227 | ExtrasT &extras(const sfileno fileno); | |
228 | /// read-only access to the extras; call openForReading() first! | |
229 | const ExtrasT &extras(const sfileno fileno) const; | |
230 | ||
231 | protected: | |
232 | ||
233 | ExtrasT *sharedExtras; ///< pointer to extras in shared memory | |
44c95fcf AR |
234 | }; |
235 | ||
50dc81ec | 236 | /// API for adjusting external state when dirty map slice is being freed |
7f6748c8 AR |
237 | class StoreMapCleaner |
238 | { | |
239 | public: | |
240 | virtual ~StoreMapCleaner() {} | |
241 | ||
50dc81ec AR |
242 | /// adjust slice-linked state before a locked Readable slice is erased |
243 | virtual void noteFreeMapSlice(const sfileno sliceId) = 0; | |
7f6748c8 AR |
244 | }; |
245 | ||
68353d5a DK |
246 | // StoreMapWithExtras implementation |
247 | ||
248 | template <class ExtrasT> | |
249 | StoreMap::Owner * | |
250 | StoreMapWithExtras<ExtrasT>::Init(const char *const path, const int limit) | |
251 | { | |
252 | return StoreMap::Init(path, limit, sizeof(Extras)); | |
253 | } | |
254 | ||
255 | template <class ExtrasT> | |
5f621cd0 AJ |
256 | StoreMapWithExtras<ExtrasT>::StoreMapWithExtras(const char *const aPath): |
257 | StoreMap(aPath) | |
68353d5a DK |
258 | { |
259 | const size_t sharedSizeWithoutExtras = | |
260 | Shared::SharedMemorySize(entryLimit(), 0); | |
261 | sharedExtras = reinterpret_cast<Extras *>(reinterpret_cast<char *>(shared.getRaw()) + sharedSizeWithoutExtras); | |
262 | } | |
263 | ||
264 | template <class ExtrasT> | |
265 | ExtrasT & | |
266 | StoreMapWithExtras<ExtrasT>::extras(const sfileno fileno) | |
267 | { | |
268 | return const_cast<ExtrasT &>(const_cast<const StoreMapWithExtras *>(this)->extras(fileno)); | |
269 | } | |
270 | ||
271 | template <class ExtrasT> | |
272 | const ExtrasT & | |
273 | StoreMapWithExtras<ExtrasT>::extras(const sfileno fileno) const | |
274 | { | |
275 | assert(sharedExtras); | |
276 | assert(valid(fileno)); | |
277 | return sharedExtras[fileno]; | |
278 | } | |
279 | ||
44c95fcf AR |
280 | } // namespace Ipc |
281 | ||
75f8f9a2 | 282 | // We do not reuse FileMap because we cannot control its size, |
44c95fcf AR |
283 | // resulting in sfilenos that are pointing beyond the database. |
284 | ||
285 | #endif /* SQUID_IPC_STORE_MAP_H */ |