in src/lru_cache.c [277:419]
LRU_CACHE_PUT_RESULT lru_cache_put(LRU_CACHE_HANDLE lru_cache, void* key, void* value, int64_t size, LRU_CACHE_EVICT_CALLBACK_FUNC evict_callback, void* context, LRU_CACHE_KEY_VALUE_COPY copy_key_value_function, LRU_CACHE_KEY_VALUE_FREE free_key_value_function)
{
LRU_CACHE_PUT_RESULT result = LRU_CACHE_PUT_OK;
if (/*Codes_SRS_LRU_CACHE_13_023: [ If lru_handle is NULL, then lru_cache_put shall fail and return LRU_CACHE_PUT_ERROR. ]*/
lru_cache == NULL ||
/*Codes_SRS_LRU_CACHE_13_024: [ If key is NULL, then lru_cache_put shall fail and return LRU_CACHE_PUT_ERROR. ]*/
key == NULL ||
/*Codes_SRS_LRU_CACHE_13_025: [ If value is NULL, then lru_cache_put shall fail and return LRU_CACHE_PUT_ERROR. ]*/
value == NULL ||
/*Codes_SRS_LRU_CACHE_13_026: [ If size is less than or equals to 0, then lru_cache_put shall fail and return LRU_CACHE_PUT_ERROR. ]*/
size <= 0 ||
/*Codes_SRS_LRU_CACHE_13_075: [ If evict_callback is NULL, then lru_cache_put shall fail and return LRU_CACHE_PUT_ERROR. ]*/
evict_callback == NULL ||
/*Codes_SRS_LRU_CACHE_13_081: [ If either of copy_key_value_function or free_key_value_function is NULL and the other is not NULL, then lru_cache_put shall fail and return LRU_CACHE_PUT_ERROR. ]*/
((copy_key_value_function == NULL) ^ (free_key_value_function == NULL)))
{
LogError("Invalid arguments: LRU_CACHE_HANDLE lru_cache=%p, void* key=%p, CLDS_HASH_TABLE_ITEM* value=%p, int64_t size=%" PRId64 "", lru_cache, key, value, size);
result = LRU_CACHE_PUT_ERROR;
}
else
{
/*Codes_SRS_LRU_CACHE_13_027: [ If size is greater than capacity of lru cache, then lru_cache_put shall fail and return LRU_CACHE_PUT_VALUE_INVALID_SIZE. ]*/
if (lru_cache->capacity < size)
{
LogError("value size is larger than capacity.");
result = LRU_CACHE_PUT_VALUE_INVALID_SIZE;
}
else
{
/*Codes_SRS_LRU_CACHE_13_076: [ context may be NULL. ]*/
/*Codes_SRS_LRU_CACHE_13_028: [ lru_cache_put shall get CLDS_HAZARD_POINTERS_THREAD_HANDLE by calling clds_hazard_pointers_thread_helper_get_thread. ]*/
CLDS_HAZARD_POINTERS_THREAD_HANDLE hazard_pointers_thread = clds_hazard_pointers_thread_helper_get_thread(lru_cache->clds_hazard_pointers_thread_helper);
if (hazard_pointers_thread == NULL)
{
/*Codes_SRS_LRU_CACHE_13_050: [ For any other errors, lru_cache_put shall return LRU_CACHE_PUT_ERROR ]*/
LogError("clds_hazard_pointers_thread_helper_get_thread failed.");
result = LRU_CACHE_PUT_ERROR;
}
else
{
/*Codes_SRS_LRU_CACHE_13_033: [ lru_cache_put shall acquire the lock in exclusive mode. ]*/
srw_lock_ll_acquire_exclusive(&lru_cache->srw_lock);
int64_t current_size = interlocked_add_64(&lru_cache->current_size, 0);
if (INT64_MAX - size < current_size)
{
/*Codes_SRS_LRU_CACHE_13_080: [ If current_size with size exceeds INT64_MAX, then lru_cache_put shall fail and return LRU_CACHE_PUT_VALUE_INVALID_SIZE. ]*/
LogError("Invalid sizes: Key=%p, size=%" PRId64 ", current_size = %" PRId64 ". Failing the call due to integer overflow", key, size, current_size);
result = LRU_CACHE_PUT_VALUE_INVALID_SIZE;
}
else
{
/*Codes_SRS_LRU_CACHE_13_064: [ lru_cache_put shall create LRU Node item to be updated in the hash table. ]*/
CLDS_HASH_TABLE_ITEM* item = CLDS_HASH_TABLE_NODE_CREATE(LRU_NODE, lru_node_cleanup, NULL);
LRU_NODE* new_node = CLDS_HASH_TABLE_GET_VALUE(LRU_NODE, item);
new_node->size = size;
new_node->evict_callback = evict_callback;
new_node->evict_callback_context = context;
new_node->copy_func = copy_key_value_function;
new_node->free_func = free_key_value_function;
/*Codes_SRS_LRU_CACHE_13_082: [ lru_cache_put shall call copy_key_value_function if not NULL to copy the value, otherwise assigns value to LRU Node item. ]*/
if(new_node->copy_func != NULL)
{
if (new_node->copy_func(&(new_node->key), key, &(new_node->value), value) != 0)
{
LogError("copy function failed. Returning with LRU_CACHE_PUT_VALUE_COPY_FUNCTION_FAILED");
CLDS_HASH_TABLE_NODE_RELEASE(LRU_NODE, item);
/*Codes_SRS_LRU_CACHE_13_084: [ If copy_key_value_function returns non zero value, then lru_cache_put shall release the exclusive lock and fail with LRU_CACHE_PUT_VALUE_COPY_FUNCTION_FAILED. ]*/
result = LRU_CACHE_PUT_VALUE_COPY_FUNCTION_FAILED;
}
}
else
{
new_node->key = key;
new_node->value = value;
}
if (result == LRU_CACHE_PUT_OK)
{
CLDS_HASH_TABLE_ITEM* old_item;
/*Codes_SRS_LRU_CACHE_13_065: [ lru_cache_put shall update the LRU Node item in the hash table by calling clds_hash_table_set_value. ]*/
CLDS_HASH_TABLE_SET_VALUE_RESULT set_value_result = clds_hash_table_set_value(lru_cache->table, hazard_pointers_thread, new_node->key, item, NULL, NULL, &old_item, NULL);
if (set_value_result != CLDS_HASH_TABLE_SET_VALUE_OK)
{
/*Codes_SRS_LRU_CACHE_13_050: [ For any other errors, lru_cache_put shall return LRU_CACHE_PUT_ERROR ]*/
LogError("Cannot set old key=%p to the clds_hash_table failed with error (%" PRI_MU_ENUM ")", new_node->key, MU_ENUM_VALUE(CLDS_HASH_TABLE_SET_VALUE_RESULT, set_value_result));
CLDS_HASH_TABLE_NODE_RELEASE(LRU_NODE, item);
result = LRU_CACHE_PUT_ERROR;
}
else
{
if (old_item != NULL)
{
/*Codes_SRS_LRU_CACHE_13_030: [ If the key is found: ]*/
LRU_NODE* current_item = CLDS_HASH_TABLE_GET_VALUE(LRU_NODE, old_item);
PDLIST_ENTRY node = &(current_item->node);
/*Codes_SRS_LRU_CACHE_13_070: [ lru_cache_put shall update the current_size with the new size and removes the old value size. ]*/
(void)interlocked_add_64(&lru_cache->current_size, -current_item->size + size);
/*Codes_SRS_LRU_CACHE_13_077: [ lru_cache_put shall remove the old node from the list by calling DList_RemoveEntryList. ]*/
DList_RemoveEntryList(node);
LogVerbose("Removed DList entry with key=%p and size=%" PRId64 " in order to reposition the node.", current_item->key, current_item->size);
/*Codes_SRS_LRU_CACHE_13_067: [ lru_cache_put shall free the old value. ]*/
CLDS_HASH_TABLE_NODE_RELEASE(LRU_NODE, old_item);
}
else
{
/*Codes_SRS_LRU_CACHE_13_071: [ Otherwise, if the key is not found: ]*/
/*Codes_SRS_LRU_CACHE_13_062: [ lru_cache_put shall add the item size to the current_size. ]*/
(void)interlocked_add_64(&lru_cache->current_size, size);
}
/*Codes_SRS_LRU_CACHE_13_066: [ lru_cache_put shall append the updated node to the tail to maintain the order. ]*/
DList_InsertTailList(&(lru_cache->head), &(new_node->node));
/*Codes_SRS_LRU_CACHE_13_068: [ lru_cache_put shall return with LRU_CACHE_PUT_OK. ]*/
result = LRU_CACHE_PUT_OK;
}
}
}
/*Codes_SRS_LRU_CACHE_13_036: [ lru_cache_put shall release the lock in exclusive mode. ]*/
srw_lock_ll_release_exclusive(&lru_cache->srw_lock);
if (result != LRU_CACHE_PUT_OK)
{
LogError("Put failed for key=%p, with result (%" PRI_MU_ENUM ").", key, MU_ENUM_VALUE(LRU_CACHE_PUT_RESULT, result));
}
// Evict if the current size overflows capacity of the cache.
else if (evict_internal(lru_cache, hazard_pointers_thread) != LRU_CACHE_EVICT_OK)
{
LogError("Eviction failed.");
result = LRU_CACHE_PUT_EVICT_ERROR;
}
}
}
}
/*Codes_SRS_LRU_CACHE_13_049: [ On success, lru_cache_put shall return LRU_CACHE_PUT_OK. ]*/
return result;
}