LRU_CACHE_PUT_RESULT lru_cache_put()

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;
}