More cache code.

git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@12953 27425a3e-05d8-49a3-a47f-9c15f0e5edd8
This commit is contained in:
Giovanni Di Sirio 2019-08-23 12:51:13 +00:00
parent 80b3141de4
commit ca2c602919
2 changed files with 185 additions and 16 deletions

View File

@ -38,10 +38,11 @@
* @name Cached objects flags
* @{
*/
#define OC_FLAG_INVALID 0x00000001U
#define OC_FLAG_INLRU 0x00000002U
#define OC_FLAG_INLRU 0x00000001U
#define OC_FLAG_INHASH 0x00000002U
#define OC_FLAG_CACHEHIT 0x00000004U
#define OC_FLAG_ERROR 0x00000008U
#define OC_FLAG_MODIFIED 0x00000010U
/** @} */
/**

View File

@ -75,7 +75,13 @@
(hhp)->hash_next = (objp); \
}
/* Insertion on LRU head (newer objects).*/
/* Removal of an object from the hash.*/
#define HASH_REMOVE(objp) { \
(objp)->hash_prev->hash_next = (objp)->hash_next; \
(objp)->hash_next->hash_prev = (objp)->hash_prev; \
}
/* Insertion on LRU list head (newer objects).*/
#define LRU_INSERT_HEAD(ocp, objp) { \
(objp)->lru_next = (ocp)->lru.lru_next; \
(objp)->lru_prev = (oc_object_t *)&(ocp)->lru; \
@ -83,6 +89,20 @@
(ocp)->lru.lru_next = (objp); \
}
/* Insertion on LRU list head (newer objects).*/
#define LRU_INSERT_TAIL(ocp, objp) { \
(objp)->lru_prev = (ocp)->lru.lru_prev; \
(objp)->lru_next = (oc_object_t *)&(ocp)->lru; \
(ocp)->lru.lru_prev->lru_next = (objp); \
(ocp)->lru.lru_prev = (objp); \
}
/* Removal of an object from the LRU list.*/
#define LRU_REMOVE(objp) { \
(objp)->lru_prev->lru_next = (objp)->lru_next; \
(objp)->lru_next->lru_prev = (objp)->lru_prev; \
}
/*===========================================================================*/
/* Module exported variables. */
/*===========================================================================*/
@ -99,10 +119,37 @@
/* Module local functions. */
/*===========================================================================*/
static oc_object_t *oc_hash_get(objects_cache_t *ocp,
uint32_t group,
uint32_t key) {
/**
* @brief Returns an object pointer from the cache, if present.
*
* @param[out] ocp pointer to the @p objects_cache_t structure to be
* @param[in] group object group identifier
* @param[in] key object identifier within the group
* initialized
* @return The pointer to the retrieved object.
* @retval NULL if the object is not in cache.
*/
static oc_object_t *hash_get(objects_cache_t *ocp,
uint32_t group,
uint32_t key) {
oc_hash_header_t *hhp;
oc_object_t *objp;
/* Hash slot where to search for an hit.*/
hhp = &ocp->hashp[OC_HASH_FUNCTION(ocp, group, key)];
objp = hhp->hash_next;
/* Scanning the siblings collision list.*/
while (objp != (oc_object_t *)hhp) {
if ((objp->obj_key == key) && (objp->obj_group == group)) {
/* Cache hit.*/
return objp;
}
objp = objp->hash_next;
}
return NULL;
}
/*===========================================================================*/
@ -124,6 +171,16 @@ static oc_object_t *oc_hash_get(objects_cache_t *ocp,
* @param[in] readf pointer to an object reader function
* @param[in] writef pointer to an object writer function
*
* Object records states:
* - Invalid not owned:
* (OC_FLAG_INLRU, cnt==1).
* - Caching an object not owned:
* (OC_FLAG_INLRU, OC_FLAG_INHASH, OC_FLAG_CACHEHIT, cnt==1).
* - Representing an object owned:
* (OC_FLAG_INHASH, cnt<=0).
* - Caching an object owned:
* (OC_FLAG_INHASH, OC_FLAG_CACHEHIT, cnt<=0).
*
* @init
*/
void chCacheObjectInit(objects_cache_t *ocp,
@ -133,10 +190,9 @@ void chCacheObjectInit(objects_cache_t *ocp,
oc_object_t *objp,
oc_readf_t readf,
oc_writef_t writef) {
unsigned i;
chDbgCheck((ocp != NULL) && (hashp != NULL) && (objp != NULL) &&
(hashn & (hashn - 1U) == 0U) &&
((hashn & (hashn - 1U)) == 0U) &&
(objn > (size_t)0) && (hashn >= objn));
chSemObjectInit(&ocp->cache_sem, (cnt_t)1);
@ -161,42 +217,154 @@ void chCacheObjectInit(objects_cache_t *ocp,
/* Object headers initialization.*/
while (objp < &ocp->objp[ocp->objn]) {
chSemObjectInit(&objp->obj_sem, (cnt_t)1);
HASH_INSERT(ocp, objp, OC_NO_GROUP, (uint32_t)i);
LRU_INSERT_HEAD(ocp, objp);
objp->obj_group = OC_NO_GROUP;
objp->obj_group = 0U;
objp->obj_key = 0U;
objp->obj_flags = OC_FLAG_INLRU | OC_FLAG_INVALID;
objp->obj_flags = OC_FLAG_INLRU;
objp->data = NULL;
}
}
/**
* @brief Retrieves an object from the cache.
* @note If the object is not in cache then the returned object does
* not specify the flag @p OC_FLAG_CACHEHIT meaning its data contains
* garbage and must be initialized.
* @note If the object is not in cache then the returned object is marked
* as @p OC_FLAG_INVALID meaning its data contains garbage and must
* be initialized.
*
* @param[in] ocp pointer to the @p objects_cache_t structure
* @param[in] group object group identifier
* @param[in] key object identifier within the group
* @return The pointer to the retrieved object.
* @retval NULL is a reserved value.
*
* @api
*/
oc_object_t *chCacheGetObject(objects_cache_t *ocp,
uint32_t group,
uint32_t key) {
/* If the requested object is not a generic one.*/
if (group == OC_NO_GROUP) {
/* Any buffer will do.*/
}
else {
while (true) {
oc_object_t *objp;
/* Critical section enter, the hash check operation is fast.*/
chSysLock();
/* Checking the cache for a hit.*/
objp = hash_get(ocp, group, key);
chDbgAssert((objp->obj_flags & OC_FLAG_INHASH) != 0U, "not in hash");
if (objp != NULL) {
/* Cache hit, checking if the buffer is owned by some
other thread.*/
if (chSemGetCounterI(&objp->obj_sem) > (cnt_t)0) {
/* Not owned case.*/
chDbgAssert((objp->obj_flags & OC_FLAG_INLRU) != 0U, "not in LRU");
/* Removing the object from LRU, now it is "owned".*/
LRU_REMOVE(objp);
objp->obj_flags &= ~OC_FLAG_INLRU;
/* Getting the object semaphore, we know there is no wait so
using the "fast" variant.*/
chSemFastWaitI(&objp->obj_sem);
}
else {
/* Owned case.*/
msg_t msg;
chDbgAssert((objp->obj_flags & OC_FLAG_INLRU) == 0U, "in LRU");
/* Getting the buffer semaphore, note it could have been
invalidated by the previous owner, in this case the
semaphore has been reset.*/
msg = chSemWaitS(&objp->obj_sem);
/* Out of critical section and retry.*/
chSysUnlock();
/* The semaphore has been signaled, the object is OK.*/
if (msg == MSG_OK) {
return objp;
}
}
}
else {
/* Cache miss, waiting for an object to become available
in the LRU.*/
chSemWaitS(&ocp->lru_sem);
/* Now a buffer is in the LRU for sure, taking it from the
LRU tail.*/
objp = ocp->lru.lru_prev;
chDbgAssert((objp->obj_flags & OC_FLAG_INLRU) != 0U, "not in LRU");
chDbgAssert(chSemGetCounterI(&objp->obj_sem) == (cnt_t)1,
"semaphore counter not 1");
LRU_REMOVE(objp);
objp->obj_flags &= ~OC_FLAG_INLRU;
/* Getting the object semaphore, we know there is no wait so
using the "fast" variant.*/
chSemFastWaitI(&objp->obj_sem);
/* Naming this object and publishing it in the hash table.*/
objp->obj_group = group;
objp->obj_key = key;
objp->obj_flags |= OC_FLAG_INHASH;
HASH_INSERT(ocp, objp, group, key);
}
}
}
}
/**
* @brief Releases an object into the cache.
* @note This function gives a meaning to the following flags:
* - @p OC_FLAG_INLRU should not happen, it is caught by an
* assertion.
* - @p OC_FLAG_ERROR the object is invalidated and queued on
* the LRU tail.
* - @p OC_FLAG_MODIFIED is ignored and kept.
* .
*
* @param[in] ocp pointer to the @p objects_cache_t structure
* @param[in] objp pointer to the @p oc_object_t structure
*
* @iclass
*/
void chCacheReleaseObject(objects_cache_t *ocp,
oc_object_t *objp) {
void chCacheReleaseObjectI(objects_cache_t *ocp,
oc_object_t *objp) {
chDbgAssert((objp->obj_flags & OC_FLAG_INLRU) == 0U, "in LRU");
chDbgAssert((objp->obj_flags & OC_FLAG_INHASH) != 0U, "not in hash");
chDbgAssert(chSemGetCounterI(&objp->obj_sem) <= (cnt_t)0,
"semaphore counter greater than 0");
/* Cases where the object should be invalidated and discarded.*/
if ((objp->obj_flags & OC_FLAG_ERROR) != 0U) {
HASH_REMOVE(objp);
LRU_INSERT_TAIL(ocp, objp);
objp->obj_flags = OC_FLAG_INLRU;
objp->obj_group = 0U;
objp->obj_key = 0U;
return;
}
/* If some thread is waiting for this specific buffer then it is
released directly without going in the LRU.*/
if (chSemGetCounterI(&objp->obj_sem) < (cnt_t)0) {
chSemSignalI(&objp->obj_sem);
return;
}
}
#endif /* CH_CFG_USE_OBJ_CACHES == TRUE */