ChibiOS/RT 7.0.5
chobjcaches.c
Go to the documentation of this file.
1/*
2 ChibiOS - Copyright (C) 2006,2007,2008,2009,2010,2011,2012,2013,2014,
3 2015,2016,2017,2018,2019,2020,2021 Giovanni Di Sirio.
4
5 This file is part of ChibiOS.
6
7 ChibiOS is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation version 3 of the License.
10
11 ChibiOS is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20/**
21 * @file oslib/src/chobjcaches.c
22 * @brief Objects Caches code.
23 * @details Objects caches.
24 * <h2>Operation mode</h2>
25 * An object cache allows to retrieve and release objects from a
26 * slow media, for example a disk or flash.<br>
27 * The most recently used objects are kept in a series of RAM
28 * buffers making access faster. Objects are identified by a
29 * pair <group, key> which could be mapped, for example, to a
30 * disk drive identifier and sector identifier.<br>
31 * Read and write operations are performed using externally-supplied
32 * functions, the cache is device-agnostic.<br>
33 * The cache uses internally an hash table, the size of the table
34 * should be dimensioned to minimize the risk of hash collisions,
35 * a factor of two is usually acceptable, it depends on the specific
36 * application requirements.<br>
37 * Operations defined for caches:
38 * - <b>Get Object</b>: Retrieves an object from cache, if not
39 * present then an empty buffer is returned.
40 * - <b>Read Object</b>: Retrieves an object from cache, if not
41 * present a buffer is allocated and the object is read from the
42 * media.
43 * - <b>Release Object</b>: Releases an object to the cache handling
44 * the media update, if required.
45 * .
46 * @pre In order to use the pipes APIs the @p CH_CFG_USE_OBJ_CACHES
47 * option must be enabled in @p chconf.h.
48 * @note Compatible with RT and NIL.
49 *
50 * @addtogroup oslib_objchaches
51 * @{
52 */
53
54#include "ch.h"
55
56#if (CH_CFG_USE_OBJ_CACHES == TRUE) || defined(__DOXYGEN__)
57
58/*===========================================================================*/
59/* Module local definitions. */
60/*===========================================================================*/
61
62/* Default hash function.*/
63#if !defined(OC_HASH_FUNCTION) || defined(__DOXYGEN__)
64#define OC_HASH_FUNCTION(ocp, group, key) \
65 (((unsigned)(group) + (unsigned)(key)) & ((unsigned)(ocp)->hashn - 1U))
66#endif
67
68/* Insertion into an hash slot list.*/
69#define HASH_INSERT(ocp, objp, group, key) { \
70 oc_hash_header_t *hhp; \
71 (hhp) = &(ocp)->hashp[OC_HASH_FUNCTION(ocp, group, key)]; \
72 (objp)->hash_next = (hhp)->hash_next; \
73 (objp)->hash_prev = (oc_object_t *)(hhp); \
74 (hhp)->hash_next->hash_prev = (objp); \
75 (hhp)->hash_next = (objp); \
76}
77
78/* Removal of an object from the hash.*/
79#define HASH_REMOVE(objp) { \
80 (objp)->hash_prev->hash_next = (objp)->hash_next; \
81 (objp)->hash_next->hash_prev = (objp)->hash_prev; \
82}
83
84/* Insertion on LRU list head (newer objects).*/
85#define LRU_INSERT_HEAD(ocp, objp) { \
86 (objp)->lru_next = (ocp)->lru.lru_next; \
87 (objp)->lru_prev = (oc_object_t *)&(ocp)->lru; \
88 (ocp)->lru.lru_next->lru_prev = (objp); \
89 (ocp)->lru.lru_next = (objp); \
90}
91
92/* Insertion on LRU list head (newer objects).*/
93#define LRU_INSERT_TAIL(ocp, objp) { \
94 (objp)->lru_prev = (ocp)->lru.lru_prev; \
95 (objp)->lru_next = (oc_object_t *)&(ocp)->lru; \
96 (ocp)->lru.lru_prev->lru_next = (objp); \
97 (ocp)->lru.lru_prev = (objp); \
98}
99
100/* Removal of an object from the LRU list.*/
101#define LRU_REMOVE(objp) { \
102 (objp)->lru_prev->lru_next = (objp)->lru_next; \
103 (objp)->lru_next->lru_prev = (objp)->lru_prev; \
104}
105
106/*===========================================================================*/
107/* Module exported variables. */
108/*===========================================================================*/
109
110/*===========================================================================*/
111/* Module local types. */
112/*===========================================================================*/
113
114/*===========================================================================*/
115/* Module local variables. */
116/*===========================================================================*/
117
118/*===========================================================================*/
119/* Module local functions. */
120/*===========================================================================*/
121
122/**
123 * @brief Returns an object pointer from the cache, if present.
124 *
125 * @param[out] ocp pointer to the @p objects_cache_t structure to be
126 * @param[in] group object group identifier
127 * @param[in] key object identifier within the group
128 * initialized
129 * @return The pointer to the retrieved object.
130 * @retval NULL if the object is not in cache.
131 *
132 * @notapi
133 */
135 uint32_t group,
136 uint32_t key) {
137 oc_hash_header_t *hhp;
138 oc_object_t *objp;
139
140 /* Hash slot where to search for an hit.*/
141 hhp = &ocp->hashp[OC_HASH_FUNCTION(ocp, group, key)];
142 objp = hhp->hash_next;
143
144 /* Scanning the siblings collision list.*/
145 while (objp != (oc_object_t *)hhp) {
146 if ((objp->obj_key == key) && (objp->obj_group == group)) {
147
148 /* Cache hit.*/
149 return objp;
150 }
151 objp = objp->hash_next;
152 }
153
154 return NULL;
155}
156
157/**
158 * @brief Gets the least recently used object buffer from the LRU list.
159 *
160 * @param[out] ocp pointer to the @p objects_cache_t structure to be
161 * @return The pointer to the retrieved object.
162 *
163 * @notapi
164 */
166 oc_object_t *objp;
167
168 while (true) {
169 /* Waiting for an object buffer to become available in the LRU.*/
170 (void) chSemWaitS(&ocp->lru_sem);
171
172 /* Now an object buffer is in the LRU for sure, taking it from the
173 LRU tail.*/
174 objp = ocp->lru.lru_prev;
175
177 "not in LRU");
179 "semaphore counter not 1");
180
181 LRU_REMOVE(objp);
182 objp->obj_flags &= ~OC_FLAG_INLRU;
183
184 /* Getting the object semaphore, we know there is no wait so
185 using the "fast" variant.*/
186 chSemFastWaitI(&objp->obj_sem);
187
188 /* If it is a buffer not needing (lazy) write then it can be used
189 right away.*/
190 if ((objp->obj_flags & OC_FLAG_LAZYWRITE) == 0U) {
191
192 /* Removing from hash table if required.*/
193 if ((objp->obj_flags & OC_FLAG_INHASH) != 0U) {
194 HASH_REMOVE(objp);
195 }
196
197 /* Removing all flags, it is "new" now.*/
198 objp->obj_flags = 0U;
199
200 return objp;
201 }
202
203 /* Out of critical section.*/
204 chSysUnlock();
205
206 /* Invoking the writer asynchronously, it will release the buffer once it
207 is written. It is responsibility of the write function to release
208 the buffer.*/
210 (void) ocp->writef(ocp, objp, true);
211
212 /* Critical section enter again.*/
213 chSysLock();
214 }
215}
216
217/*===========================================================================*/
218/* Module exported functions. */
219/*===========================================================================*/
220
221/**
222 * @brief Initializes a @p objects_cache_t object.
223 *
224 * @param[out] ocp pointer to the @p objects_cache_t structure to be
225 * initialized
226 * @param[in] hashn number of elements in the hash table array, must be
227 * a power of two and not lower than @p objn
228 * @param[in] hashp pointer to the hash table as an array of
229 * @p oc_hash_header_t
230 * @param[in] objn number of elements in the objects table array
231 * @param[in] objsz size of elements in the objects table array, the
232 * minimum value is <tt>sizeof (oc_object_t)</tt>.
233 * @param[in] objvp pointer to the hash objects as an array of structures
234 * starting with an @p oc_object_t
235 * @param[in] readf pointer to an object reader function
236 * @param[in] writef pointer to an object writer function
237 *
238 * @init
239 */
241 ucnt_t hashn,
242 oc_hash_header_t *hashp,
243 ucnt_t objn,
244 size_t objsz,
245 void *objvp,
246 oc_readf_t readf,
247 oc_writef_t writef) {
248
249 chDbgCheck((ocp != NULL) && (hashp != NULL) && (objvp != NULL) &&
250 ((hashn & (hashn - (ucnt_t)1)) == (ucnt_t)0) &&
251 (objn > (ucnt_t)0) && (hashn >= objn) &&
252 (objsz >= sizeof (oc_object_t)) &&
253 ((objsz & (PORT_NATURAL_ALIGN - 1U)) == 0U));
254
256 chSemObjectInit(&ocp->lru_sem, (cnt_t)objn);
257 ocp->hashn = hashn;
258 ocp->hashp = hashp;
259 ocp->objn = objn;
260 ocp->objvp = objvp;
261 ocp->readf = readf;
262 ocp->writef = writef;
263 ocp->lru.hash_next = NULL;
264 ocp->lru.hash_prev = NULL;
265 ocp->lru.lru_next = (oc_object_t *)&ocp->lru;
266 ocp->lru.lru_prev = (oc_object_t *)&ocp->lru;
267
268 /* Hash headers initialization.*/
269 do {
270 hashp->hash_next = (oc_object_t *)hashp;
271 hashp->hash_prev = (oc_object_t *)hashp;
272 hashp++;
273 } while (hashp < &ocp->hashp[ocp->hashn]);
274
275 /* Object headers initialization.*/
276 do {
277 oc_object_t *objp = (oc_object_t *)objvp;
278
279 chSemObjectInit(&objp->obj_sem, (cnt_t)1);
280 LRU_INSERT_HEAD(ocp, objp);
281 objp->obj_group = 0U;
282 objp->obj_key = 0U;
283 objp->obj_flags = OC_FLAG_INLRU;
284 objp->dptr = NULL;
285 objvp = (void *)((uint8_t *)objvp + objsz);
286 objn--;
287 } while (objn > (ucnt_t)0);
288}
289
290/**
291 * @brief Retrieves an object from the cache.
292 * @note If the object is not in cache then the returned object is marked
293 * as @p OC_FLAG_NOTSYNC meaning that its data contains garbage and
294 * must be initialized.
295 *
296 * @param[in] ocp pointer to the @p objects_cache_t structure
297 * @param[in] group object group identifier
298 * @param[in] key object identifier within the group
299 * @return The pointer to the retrieved object.
300 *
301 * @api
302 */
304 uint32_t group,
305 uint32_t key) {
306 oc_object_t *objp;
307
308 /* Critical section enter, the hash check operation is fast.*/
309 chSysLock();
310
311 /* Checking the cache for a hit.*/
312 objp = hash_get_s(ocp, group, key);
313 if (objp != NULL) {
314
316 "not in hash");
317
318 /* Cache hit, checking if the buffer is owned by some
319 other thread.*/
320 if (chSemGetCounterI(&objp->obj_sem) > (cnt_t)0) {
321 /* Not owned case, it is in the LRU list.*/
322
324 "not in LRU");
325
326 /* Removing the object from LRU, now it is "owned".*/
327 LRU_REMOVE(objp);
328 objp->obj_flags &= ~OC_FLAG_INLRU;
329
330 /* Getting the object semaphore, we know there is no wait so
331 using the "fast" variant.*/
332 chSemFastWaitI(&objp->obj_sem);
333 }
334 else {
335 /* Owned case, some other thread is playing with this object, we
336 need to wait.*/
337
338 chDbgAssert((objp->obj_flags & OC_FLAG_INLRU) == 0U, "in LRU");
339
340 /* Waiting on the buffer semaphore.*/
341 (void) chSemWaitS(&objp->obj_sem);
342 }
343 }
344 else {
345 /* Cache miss, getting an object buffer from the LRU list.*/
346 objp = lru_get_last_s(ocp);
347
348 /* Naming this object and publishing it in the hash table.*/
349 objp->obj_group = group;
350 objp->obj_key = key;
352 HASH_INSERT(ocp, objp, group, key);
353 }
354
355 /* Out of critical section and returning the object.*/
356 chSysUnlock();
357
358 return objp;
359}
360
361/**
362 * @brief Releases an object into the cache.
363 * @note This function gives a meaning to the following flags:
364 * - @p OC_FLAG_INLRU must be cleared.
365 * - @p OC_FLAG_INHASH must be set.
366 * - @p OC_FLAG_SHARED must be cleared.
367 * - @p OC_FLAG_NOTSYNC invalidates the object and queues it on
368 * the LRU tail.
369 * - @p OC_FLAG_LAZYWRITE is ignored and kept, a write will occur
370 * when the object is removed from the LRU list (lazy write).
371 * .
372 *
373 * @param[in] ocp pointer to the @p objects_cache_t structure
374 * @param[in] objp pointer to the @p oc_object_t structure
375 *
376 * @iclass
377 */
379 oc_object_t *objp) {
380
381 /* Checking initial conditions of the object to be released.*/
385 "invalid object state");
387 "semaphore counter greater than 0");
388
389 /* If some thread is waiting for this specific buffer then it is
390 handed directly without going through the LRU.*/
391 if (chSemGetCounterI(&objp->obj_sem) < (cnt_t)0) {
392 /* Clearing all flags except those that are still meaningful, note,
393 OC_FLAG_NOTSYNC and OC_FLAG_LAZYWRITE are passed, the other thread
394 will handle them.*/
396 chSemSignalI(&objp->obj_sem);
397 return;
398 }
399
400 /* If the object specifies OC_FLAG_NOTSYNC then it must be invalidated
401 and removed from the hash table.*/
402 if ((objp->obj_flags & OC_FLAG_NOTSYNC) != 0U) {
403 HASH_REMOVE(objp);
404 LRU_INSERT_TAIL(ocp, objp);
405 objp->obj_group = 0U;
406 objp->obj_key = 0U;
407 objp->obj_flags = OC_FLAG_INLRU;
408 }
409 else {
410 /* LRU insertion point depends on the OC_FLAG_FORGET flag.*/
411 if ((objp->obj_flags & OC_FLAG_FORGET) == 0U) {
412 /* Placing it on head.*/
413 LRU_INSERT_HEAD(ocp, objp);
414 }
415 else {
416 /* Low priority data, placing it on tail.*/
417 LRU_INSERT_TAIL(ocp, objp);
418 }
420 objp->obj_flags |= OC_FLAG_INLRU;
421 }
422
423 /* Increasing the LRU counter semaphore.*/
424 chSemSignalI(&ocp->lru_sem);
425
426 /* Releasing the object, we know there are no threads waiting so
427 using the "fast" signal variant.*/
429}
430
431/**
432 * @brief Reads object data from the storage.
433 * @note In case of asynchronous operation an error condition is not
434 * reported by this function.
435 *
436 * @param[in] ocp pointer to the @p objects_cache_t structure
437 * @param[in] objp pointer to the @p oc_object_t structure
438 * @param[in] async requests an asynchronous operation if supported, the
439 * function is then responsible for releasing the
440 * object
441 * @return The operation status. In case of asynchronous
442 * operation @p false is always returned.
443 * @retval false if the operation succeeded.
444 * @retval true if the synchronous read operation failed.
445 *
446 * @api
447 */
449 oc_object_t *objp,
450 bool async) {
451
452 /* Marking it as OC_FLAG_NOTSYNC because the read operation is going
453 to corrupt it in case of failure. It is responsibility of the read
454 implementation to clear it if the operation succeeds.*/
455 objp->obj_flags |= OC_FLAG_NOTSYNC;
456
457 return ocp->readf(ocp, objp, async);
458}
459
460/**
461 * @brief Writes the object data back to storage.
462 * @note In case of asynchronous operation an error condition is not
463 * reported by this function.
464 *
465 * @param[in] ocp pointer to the @p objects_cache_t structure
466 * @param[in] objp pointer to the @p oc_object_t structure
467 * @param[in] async requests an asynchronous operation if supported, the
468 * function is then responsible for releasing the
469 * object
470 * @return The operation status. In case of asynchronous
471 * operation @p false is always returned.
472 * @retval false if the operation succeeded.
473 * @retval true if the synchronous write operation failed.
474 *
475 * @api
476 */
478 oc_object_t *objp,
479 bool async) {
480
481 /* Resetting the OC_FLAG_LAZYWRITE flag in order to prevent multiple
482 writes.*/
484
485 return ocp->writef(ocp, objp, async);
486}
487
488#endif /* CH_CFG_USE_OBJ_CACHES == TRUE */
489
490/** @} */
ChibiOS/RT main include file.
#define chDbgAssert(c, r)
Condition assertion.
Definition chdebug.h:144
#define chDbgCheck(c)
Function parameters check.
Definition chdebug.h:118
int32_t cnt_t
Definition chearly.h:92
uint32_t ucnt_t
Definition chearly.h:93
bool chCacheReadObject(objects_cache_t *ocp, oc_object_t *objp, bool async)
Reads object data from the storage.
#define OC_FLAG_LAZYWRITE
Definition chobjcaches.h:45
void chCacheObjectInit(objects_cache_t *ocp, ucnt_t hashn, oc_hash_header_t *hashp, ucnt_t objn, size_t objsz, void *objvp, oc_readf_t readf, oc_writef_t writef)
Initializes a objects_cache_t object.
#define OC_FLAG_INLRU
Definition chobjcaches.h:41
struct ch_oc_object oc_object_t
Type of a cached object.
Definition chobjcaches.h:83
bool chCacheWriteObject(objects_cache_t *ocp, oc_object_t *objp, bool async)
Writes the object data back to storage.
struct ch_oc_hash_header oc_hash_header_t
Type of an hash element header.
Definition chobjcaches.h:73
bool(* oc_readf_t)(objects_cache_t *ocp, oc_object_t *objp, bool async)
Object read function.
Definition chobjcaches.h:98
#define OC_FLAG_FORGET
Definition chobjcaches.h:46
oc_object_t * chCacheGetObject(objects_cache_t *ocp, uint32_t group, uint32_t key)
Retrieves an object from the cache.
#define LRU_INSERT_HEAD(ocp, objp)
Definition chobjcaches.c:85
static oc_object_t * lru_get_last_s(objects_cache_t *ocp)
Gets the least recently used object buffer from the LRU list.
struct ch_objects_cache objects_cache_t
Type of a cache object.
Definition chobjcaches.h:88
void chCacheReleaseObjectI(objects_cache_t *ocp, oc_object_t *objp)
Releases an object into the cache.
#define OC_FLAG_SHARED
Definition chobjcaches.h:43
bool(* oc_writef_t)(objects_cache_t *ocp, oc_object_t *objp, bool async)
Object write function.
#define HASH_REMOVE(objp)
Definition chobjcaches.c:79
#define LRU_INSERT_TAIL(ocp, objp)
Definition chobjcaches.c:93
#define OC_FLAG_NOTSYNC
Definition chobjcaches.h:44
#define OC_HASH_FUNCTION(ocp, group, key)
Definition chobjcaches.c:64
#define OC_FLAG_INHASH
Definition chobjcaches.h:42
#define HASH_INSERT(ocp, objp, group, key)
Definition chobjcaches.c:69
#define LRU_REMOVE(objp)
static oc_object_t * hash_get_s(objects_cache_t *ocp, uint32_t group, uint32_t key)
Returns an object pointer from the cache, if present.
static cnt_t chSemGetCounterI(const semaphore_t *sp)
Returns the semaphore counter current value.
Definition chsem.h:189
void chSemObjectInit(semaphore_t *sp, cnt_t n)
Initializes a semaphore with the specified counter value.
Definition chsem.c:97
msg_t chSemWaitS(semaphore_t *sp)
Performs a wait operation on a semaphore.
Definition chsem.c:191
static void chSemFastWaitI(semaphore_t *sp)
Decreases the semaphore counter.
Definition chsem.h:158
void chSemSignalI(semaphore_t *sp)
Performs a signal operation on a semaphore.
Definition chsem.c:315
static void chSemFastSignalI(semaphore_t *sp)
Increases the semaphore counter.
Definition chsem.h:174
static void chSysLock(void)
Enters the kernel lock state.
Definition chsys.h:407
static void chSysUnlock(void)
Leaves the kernel lock state.
Definition chsys.h:421
oc_writef_t writef
Writer functions for cached objects.
void * objvp
Pointer to the objects table.
semaphore_t cache_sem
Semaphore for cache access.
ucnt_t objn
Number of elements in the objects table.
oc_hash_header_t * hashp
Pointer to the hash table.
ucnt_t hashn
Number of elements in the hash table.
semaphore_t lru_sem
Semaphore for LRU access.
oc_readf_t readf
Reader functions for cached objects.
oc_lru_header_t lru
LRU list header.
oc_object_t * hash_prev
Previous in the collisions list.
oc_object_t * hash_next
Next in the collisions list.
oc_object_t * lru_next
Next in the LRU list.
oc_object_t * hash_next
Next in the collisions list.
oc_object_t * lru_prev
Previous in the LRU list.
oc_object_t * hash_prev
Previous in the collisions list.
uint32_t obj_key
Object key.
oc_flags_t obj_flags
Object flags.
oc_object_t * hash_next
Next in the collisions list.
void * dptr
User pointer.
semaphore_t obj_sem
Semaphore for object access.
uint32_t obj_group
Object group.