static LRU_CACHE_EVICT_RESULT evict_internal()

in src/lru_cache.c [165:264]


static LRU_CACHE_EVICT_RESULT evict_internal(LRU_CACHE_HANDLE lru_cache, CLDS_HAZARD_POINTERS_THREAD_HANDLE hazard_pointers_thread)
{
    LRU_CACHE_EVICT_RESULT result = LRU_CACHE_EVICT_OK;

    while(result == LRU_CACHE_EVICT_OK)
    {
        /*Codes_SRS_LRU_CACHE_13_040: [ lru_cache_put shall acquire the lock in exclusive. ]*/
        srw_lock_ll_acquire_exclusive(&lru_cache->srw_lock);

        int64_t current_size = interlocked_add_64(&lru_cache->current_size, 0);
        if (current_size <= lru_cache->capacity)
        {
            srw_lock_ll_release_exclusive(&lru_cache->srw_lock);
            break;
        }
        else
        {
            /*Codes_SRS_LRU_CACHE_13_037: [ While the current size of the cache exceeds capacity: ]*/

            if (DList_IsListEmpty(&lru_cache->head))
            {
                /*Codes_SRS_LRU_CACHE_13_050: [ For any other errors, lru_cache_put shall return LRU_CACHE_PUT_ERROR ]*/
                LogError("Something is wrong. The cache is empty but there is no capacity. current_size = %" PRId64 "", current_size);
                srw_lock_ll_release_exclusive(&lru_cache->srw_lock);
                lru_cache->on_error_callback(lru_cache->on_error_context);
                result = LRU_CACHE_EVICT_ERROR;
                break;
            }

            /*Codes_SRS_LRU_CACHE_13_038: [ lru_cache_put shall get the least used node which is Flink of head node. ]*/
            DLIST_ENTRY* least_used_node = lru_cache->head.Flink;
            LRU_NODE* least_used_node_value = CONTAINING_RECORD(least_used_node, LRU_NODE, node);

            if (current_size - least_used_node_value->size < 0)
            {
                /*Codes_SRS_LRU_CACHE_13_050: [ For any other errors, lru_cache_put shall return LRU_CACHE_PUT_ERROR ]*/
                LogError("current_size - least_used_node_value is less than 0. current_size=%" PRId64 ", least_used_node_value->size=%" PRId64 " Failing eviction. ", current_size, least_used_node_value->size);
                srw_lock_ll_release_exclusive(&lru_cache->srw_lock);
                lru_cache->on_error_callback(lru_cache->on_error_context);
                result = LRU_CACHE_EVICT_ERROR;
                break;
            }
            else
            {
                /*Codes_SRS_LRU_CACHE_13_072: [ lru_cache_put shall decrement the least used node size from current_size. ]*/
                if (interlocked_compare_exchange_64(&lru_cache->current_size, current_size - least_used_node_value->size, current_size) != current_size)
                {
                    // something changed
                    srw_lock_ll_release_exclusive(&lru_cache->srw_lock);
                    continue;
                }
                else
                {
                    CLDS_HASH_TABLE_ITEM* entry;
                    /*Codes_SRS_LRU_CACHE_13_039: [ The least used node is removed from clds_hash_table by calling clds_hash_table_remove. ]*/
                    CLDS_HASH_TABLE_REMOVE_RESULT remove_result = clds_hash_table_remove(lru_cache->table, hazard_pointers_thread, least_used_node_value->key, &entry, NULL);

                    switch (remove_result)
                    {
                    case CLDS_HASH_TABLE_REMOVE_OK:
                    {

                        /*Codes_SRS_LRU_CACHE_13_041: [ lru_cache_put shall remove the old node from the list by calling DList_RemoveEntryList. ]*/
                        (void)DList_RemoveEntryList(least_used_node);
                        LogVerbose("Removed DList entry with key=%p and size=%" PRId64 " in order to evict the lru node.", least_used_node_value->key, least_used_node_value->size);

                        /*Codes_SRS_LRU_CACHE_13_043: [ On success, evict_callback is called with the evicted item. ]*/
                        least_used_node_value->evict_callback(least_used_node_value->evict_callback_context, least_used_node_value->value);

                        CLDS_HASH_TABLE_NODE_RELEASE(LRU_NODE, entry);
                        break;
                    }

                    case CLDS_HASH_TABLE_REMOVE_NOT_FOUND:
                    {
                        /*Codes_SRS_LRU_CACHE_13_078: [ If clds_hash_table_remove returns CLDS_HASH_TABLE_REMOVE_NOT_FOUND, then lru_cache_put shall retry eviction. ]*/
                        LogError("item with key =%p has already been evicted.", least_used_node_value->key);
                        (void)interlocked_add_64(&lru_cache->current_size, least_used_node_value->size);
                        break;
                    }
                    default:
                    case CLDS_HASH_TABLE_REMOVE_ERROR:
                    {
                        /*Codes_SRS_LRU_CACHE_13_050: [ For any other errors, lru_cache_put shall return LRU_CACHE_PUT_ERROR ]*/
                        LogError("Error removing item with key =%p from hash table", least_used_node_value->key);
                        result = LRU_CACHE_EVICT_ERROR;
                        lru_cache->on_error_callback(lru_cache->on_error_context);
                        (void)interlocked_add_64(&lru_cache->current_size, least_used_node_value->size);
                        break;
                    }
                    }
                }
            }
        }
        /*Codes_SRS_LRU_CACHE_13_042: [ lru_cache_put shall release the lock in exclusive mode. ]*/
        srw_lock_ll_release_exclusive(&lru_cache->srw_lock);
    }

    return result;
}