static bool _makeRoomInStore(AthenaLRUContentStore *impl, size_t sizeNeeded) { bool result = false; if (sizeNeeded > impl->maxSizeInBytes) { return false; // not possible. } uint64_t nowInMillis = parcClock_GetTime(impl->wallClock); // Evict expired items until we have enough room, or don't have any expired items. while (sizeNeeded > (impl->maxSizeInBytes - impl->currentSizeInBytes)) { _AthenaLRUContentStoreEntry *entry = _getEarliestExpiryTime(impl); if (entry == NULL) { break; } if (nowInMillis > entry->expiryTime) { _athenaLRUContentStore_PurgeContentStoreEntry(impl, entry); } else { break; } } // Evict items past their recommended cache time until we have enough room, or don't have any items. while (sizeNeeded > (impl->maxSizeInBytes - impl->currentSizeInBytes)) { _AthenaLRUContentStoreEntry *entry = _getEarliestRecommendedCacheTime(impl); if (entry == NULL) { break; } if (nowInMillis > entry->recommendedCacheTime) { _athenaLRUContentStore_PurgeContentStoreEntry(impl, entry); } else { break; } } while (sizeNeeded > (impl->maxSizeInBytes - impl->currentSizeInBytes)) { _AthenaLRUContentStoreEntry *entry = _getLeastUsedFromLRU(impl); if (entry == NULL) { break; } _athenaLRUContentStore_PurgeContentStoreEntry(impl, entry); } if (impl->maxSizeInBytes - impl->currentSizeInBytes > sizeNeeded) { return true; } return result; }
static CCNxContentObject * //_athenaLRUContentStore_GetMatch(AthenaContentStoreImplementation *store, const CCNxName *name, const PARCBuffer *keyIdRestriction, // const PARCBuffer *contentObjectHash) _athenaLRUContentStore_GetMatch(AthenaContentStoreImplementation *store, const CCNxInterest *interest) { CCNxContentObject *result = NULL; AthenaLRUContentStore *impl = (AthenaLRUContentStore *) store; _AthenaLRUContentStoreEntry *entry = NULL; CCNxName *name = ccnxInterest_GetName(interest); PARCBuffer *contentObjectHashRestriction = ccnxInterest_GetContentObjectHashRestriction(interest); PARCBuffer *keyIdRestriction = ccnxInterest_GetKeyIdRestriction(interest); if (contentObjectHashRestriction != NULL) { PARCObject *nameAndHashKey = _createHashableKey(name, NULL, contentObjectHashRestriction); entry = (_AthenaLRUContentStoreEntry *) parcHashMap_Get(impl->tableByNameAndObjectHash, nameAndHashKey); parcObject_Release((PARCObject **) &nameAndHashKey); } if ((entry == NULL) && (keyIdRestriction != NULL)) { PARCObject *nameAndKeyIdKey = _createHashableKey(name, keyIdRestriction, NULL); entry = (_AthenaLRUContentStoreEntry *) parcHashMap_Get(impl->tableByNameAndKeyId, nameAndKeyIdKey); parcObject_Release((PARCObject **) &nameAndKeyIdKey); } if (entry == NULL) { PARCObject *nameKey = _createHashableKey(name, NULL, NULL); entry = (_AthenaLRUContentStoreEntry *) parcHashMap_Get(impl->tableByName, nameKey); parcObject_Release((PARCObject **) &nameKey); } // Matching is done. Now check for validity, if necessary. if (entry != NULL) { // We found matching content. Now make sure it's not expired before returning it. If it is expired, // remove it from the store and don't return anything. if (entry->hasExpiryTime && (entry->expiryTime < parcClock_GetTime(impl->wallClock))) { _athenaLRUContentStore_PurgeContentStoreEntry(impl, entry); entry = NULL; } // XXX: TODO: Check that the KeyId, if any, was verified. } // At this point, the cached content is considered valid for responding with. Return it. if (entry != NULL) { result = entry->contentObject; // Update LRU so that the matched entry is at the top of the list. _moveContentStoreEntryToLRUHead(impl, entry); impl->stats.numMatchHits++; } else { impl->stats.numMatchMisses++; } return result; }
static bool _athenaLRUContentStore_RemoveMatch(AthenaContentStoreImplementation *store, const CCNxName *name, const PARCBuffer *keyIdRestriction, const PARCBuffer *contentObjectHash) { AthenaLRUContentStore *impl = (AthenaLRUContentStore *) store; bool wasRemoved = false; if (contentObjectHash != NULL) { PARCObject *nameAndHashKey = _createHashableKey(name, NULL, contentObjectHash); _AthenaLRUContentStoreEntry *entry = (_AthenaLRUContentStoreEntry *) parcHashMap_Get(impl->tableByNameAndObjectHash, nameAndHashKey); parcObject_Release((PARCObject **) &nameAndHashKey); if (entry != NULL) { _athenaLRUContentStore_PurgeContentStoreEntry(store, entry); wasRemoved = true; } } if (!wasRemoved && keyIdRestriction != NULL) { PARCObject *nameAndKeyIdKey = _createHashableKey(name, keyIdRestriction, NULL); _AthenaLRUContentStoreEntry *entry = (_AthenaLRUContentStoreEntry *) parcHashMap_Get(impl->tableByNameAndKeyId, nameAndKeyIdKey); parcObject_Release((PARCObject **) &nameAndKeyIdKey); if (entry != NULL) { _athenaLRUContentStore_PurgeContentStoreEntry(store, entry); wasRemoved = true; } } if (!wasRemoved) { PARCObject *nameKey = _createHashableKey(name, NULL, NULL); _AthenaLRUContentStoreEntry *entry = (_AthenaLRUContentStoreEntry *) parcHashMap_Get(impl->tableByName, nameKey); parcObject_Release((PARCObject **) &nameKey); if (entry != NULL) { _athenaLRUContentStore_PurgeContentStoreEntry(store, entry); wasRemoved = true; } } return wasRemoved; }
LONGBOW_TEST_CASE(Local, _getLeastUsedFromLRU) { AthenaLRUContentStore *impl = _createLRUContentStore(); CCNxName *name1 = ccnxName_CreateFromURI("lci:/first/entry"); CCNxContentObject *contentObject1 = ccnxContentObject_CreateWithDataPayload(name1, NULL); CCNxName *name2 = ccnxName_CreateFromURI("lci:/second/entry"); CCNxContentObject *contentObject2 = ccnxContentObject_CreateWithDataPayload(name2, NULL); CCNxName *name3 = ccnxName_CreateFromURI("lci:/third/entry"); CCNxContentObject *contentObject3 = ccnxContentObject_CreateWithDataPayload(name3, NULL); bool status = _athenaLRUContentStore_PutContentObject(impl, contentObject1); assertTrue(status, "Exepected to insert content"); status = _athenaLRUContentStore_PutContentObject(impl, contentObject2); assertTrue(status, "Exepected to insert content"); status = _athenaLRUContentStore_PutContentObject(impl, contentObject3); assertTrue(status, "Exepected to insert content"); athenaLRUContentStore_Display(impl, 2); _AthenaLRUContentStoreEntry *entry1 = _getLeastUsedFromLRU(impl); assertTrue(ccnxContentObject_Equals(entry1->contentObject, contentObject1), "Expected to retrieve contentObject1"); _athenaLRUContentStore_PurgeContentStoreEntry(impl, entry1); _AthenaLRUContentStoreEntry *entry2 = _getLeastUsedFromLRU(impl); assertTrue(ccnxContentObject_Equals(entry2->contentObject, contentObject2), "Expected to retrieve contentObject1"); _athenaLRUContentStore_PurgeContentStoreEntry(impl, entry2); _AthenaLRUContentStoreEntry *entry3 = _getLeastUsedFromLRU(impl); assertTrue(ccnxContentObject_Equals(entry3->contentObject, contentObject3), "Expected to retrieve contentObject1"); _athenaLRUContentStore_PurgeContentStoreEntry(impl, entry3); ccnxContentObject_Release(&contentObject1); ccnxContentObject_Release(&contentObject2); ccnxContentObject_Release(&contentObject3); ccnxName_Release(&name1); ccnxName_Release(&name2); ccnxName_Release(&name3); _athenaLRUContentStore_Release((AthenaContentStoreImplementation *) &impl); }
LONGBOW_TEST_CASE(Local, _athenaLRUContentStore_PutLRUContentStoreEntry) { AthenaLRUContentStore *impl = _createLRUContentStore(); PARCBuffer *payload = parcBuffer_Allocate(1200); CCNxContentObject *contentObject = _createContentObject("lci:/boose/roo/pie", 10, NULL); parcBuffer_Release(&payload); _AthenaLRUContentStoreEntry *entry = _athenaLRUContentStoreEntry_Create(contentObject); ccnxContentObject_Release(&contentObject); entry->expiryTime = 10000; entry->contentObjectHash = parcBuffer_WrapCString("object hash buffer"); entry->keyId = parcBuffer_WrapCString("key id buffer"); entry->hasContentObjectHash = true; entry->hasKeyId = true; bool status = _athenaLRUContentStore_PutLRUContentStoreEntry(impl, entry); assertTrue(status, "Expected to put content into the store"); assertTrue(status, "Expected to put content into the store a second time"); assertTrue(impl->numEntries == 1, "Expected 1 entry in the store"); assertTrue(impl->stats.numAdds == 1, "Expected stats to show 1 adds"); _athenaLRUContentStore_PurgeContentStoreEntry(impl, entry); assertTrue(impl->numEntries == 0, "Expected 0 entries in the store"); parcBuffer_Release(&entry->keyId); parcBuffer_Release(&entry->contentObjectHash); _athenaLRUContentStoreEntry_Release(&entry); _athenaLRUContentStore_Release((AthenaContentStoreImplementation *) &impl); }
static bool _athenaLRUContentStore_PutLRUContentStoreEntry(AthenaContentStoreImplementation *store, const _AthenaLRUContentStoreEntry *entry) { bool result = false; AthenaLRUContentStore *impl = (AthenaLRUContentStore *) store; // Enforce capacity limit. If adding the next store item would put us over the limit, we have to remove // entrie(s) until there is room. bool isEnoughRoomInStore = true; if ((entry->sizeInBytes + impl->currentSizeInBytes) > impl->maxSizeInBytes) { isEnoughRoomInStore = _makeRoomInStore(impl, entry->sizeInBytes); } if (isEnoughRoomInStore) { _AthenaLRUContentStoreEntry *newEntry = _athenaLRUContentStoreEntry_Acquire(entry); // New entries go to the HEAD of the LRU. _addContentStoreEntryToLRUHead(impl, newEntry); // DO NOT RELEASE the newEntry after adding it to the containers. We will let the LRU be responsible for the // final release. At this point, the reference count of the new LRUContentStoreEntry is 1 and the LRU // points to it. The other containers (hashmap indices, sorted lists, etc) can acquire and release on their // own, but the LRU holds the final reference. _AthenaLRUContentStoreEntry *existingEntry = NULL; CCNxName *name = ccnxContentObject_GetName(newEntry->contentObject); if (name != NULL) { PARCObject *nameKey = _createHashableKey(name, NULL, NULL); existingEntry = _addEntryToIndexTableIfNotAlreadyInIt(impl->tableByName, nameKey, newEntry); parcObject_Release((PARCObject **) &nameKey); } if (newEntry->hasKeyId) { PARCObject *nameAndKeyIdKey = _createHashableKey(name, newEntry->keyId, NULL); existingEntry = _addEntryToIndexTableIfNotAlreadyInIt(impl->tableByNameAndKeyId, nameAndKeyIdKey, newEntry); parcObject_Release((PARCObject **) &nameAndKeyIdKey); } if (newEntry->hasContentObjectHash) { PARCObject *nameAndObjectHashKey = _createHashableKey(name, NULL, newEntry->contentObjectHash); existingEntry = _addEntryToIndexTableIfNotAlreadyInIt(impl->tableByNameAndObjectHash, nameAndObjectHashKey, newEntry); parcObject_Release((PARCObject **) &nameAndObjectHashKey); } if (existingEntry != NULL && existingEntry->indexCount < 1) { // The existing entry is in no indexes, which means it cannot be matched and serves no further purpose. // Remove it completely from all containers. //printf("Releasing previously held store entry\n"); _athenaLRUContentStore_PurgeContentStoreEntry(impl, existingEntry); } // Add the new entry to the time-ordered lists, if it has an RCT or ExpiryTime. if (newEntry->hasExpiryTime) { parcSortedList_Add(impl->listByExpiryTime, newEntry); } if (newEntry->hasRecommendedCacheTime) { parcSortedList_Add(impl->listByRecommendedCacheTime, newEntry); } impl->stats.numAdds++; impl->numEntries++; impl->currentSizeInBytes += newEntry->sizeInBytes; result = true; } return result; }