目录

Using ConcurrentHashMap to Query and Maintain Key-Value Information in a Multithreaded Environment

ConcurrentHashMap provided by FAST Kit is specifically designed for key-value data management in high-concurrency scenarios. It is primarily designed for secure storage, fast access, and efficient updates in a multithreaded environment. It is suitable for create, read, update, and delete (CRUD) operations that require high concurrent throughput and data consistency. Typical scenarios include single-point insertion, deletion, query, and concurrent modifications.

API Description

For details about the APIs, please refer to the API Reference.

API Description
FAST_ErrorCode HMS_FAST_ConcurrentHashmap_Create (FAST_ConcurrentHashmapHandle* handle, HMS_FAST_ConcurrentHashmap_HashFunc hasher, HMS_FAST_ConcurrentHashmap_KeyEqualFunc equaler, float maxLoadFac, size_t numShards) Creates a ConcurrentHashMap with the given configuration.
void HMS_FAST_ConcurrentHashmap_Destroy (FAST_ConcurrentHashmapHandle* handle) Destroys a specified ConcurrentHashMap.
FAST_ErrorCode HMS_FAST_ConcurrentHashmap_Insert (FAST_ConcurrentHashmapHandle* handle, const FAST_ConcurrentHashmapKeyPtr key, const FAST_ConcurrentHashmapValuePtr value, FAST_ConcurrentHashmapValuePtr* originValue) Inserts a given key-value pair into a ConcurrentHashMap. If the key already exists, the existing value is overwritten with value, and the address of the original value is stored in originValue.
FAST_ErrorCode HMS_FAST_ConcurrentHashmap_Find (FAST_ConcurrentHashmapHandle* handle, const FAST_ConcurrentHashmapKeyPtr key, FAST_ConcurrentHashmapValuePtr* value) Searches for the input key in a given ConcurrentHashMap and saves the corresponding value into value.
FAST_ErrorCode HMS_FAST_ConcurrentHashmap_Erase (FAST_ConcurrentHashmapHandle* handle, const FAST_ConcurrentHashmapKeyPtr key, FAST_ConcurrentHashmapKeyPtr* originKey, FAST_ConcurrentHashmapValuePtr* originValue) Deletes the input key from the given HashMap and saves the addresses of the corresponding key and value into originKey and originValue, respectively.
FAST_ErrorCode HMS_FAST_ConcurrentHashmap_TryInsert (FAST_ConcurrentHashmapHandle* handle, const FAST_ConcurrentHashmapKeyPtr key, const FAST_ConcurrentHashmapValuePtr value) Inserts a given key-value pair into a ConcurrentHashMap. If the key already exists, no operation is performed.
size_t HMS_FAST_ConcurrentHashmap_Size (FAST_ConcurrentHashmapHandle* handle) Returns the number of elements in a given HashMap.
void HMS_FAST_ConcurrentHashmap_Clear (FAST_ConcurrentHashmapHandle* handle) Clears all elements maintained in a given HashMap.
size_t HMS_FAST_ConcurrentHashmap_EraseIf (FAST_ConcurrentHashmapHandle* handle, HMS_FAST_ConcurrentHashmap_HookFunc condFunc, void* condCtx, HMS_FAST_ConcurrentHashmap_HookFunc freeFunc, void* freeCtx) Deletes all elements that meet the developer-defined conditions from the HashMap and releases the memory in the developer-defined manner.
void HMS_FAST_ConcurrentHashmap_Traverse (FAST_ConcurrentHashmapHandle* handle, HMS_FAST_ConcurrentHashmap_HookFunc condFunc, void* condCtx, HMS_FAST_ConcurrentHashmap_HookFunc workFunc, void* workCtx) Traverses the HashMap and modifies all key-value pairs that meet the developer-defined conditions in the developer-defined manner.

Development Procedure

  1. Link the related dynamic library in the CMake script.

     find_library(
         lib_fast_ads
         NAMES fast_ads
     )
    target_link_libraries(entry PRIVATE ${lib_fast_ads})
  2. Call related APIs to manage key-value information.

    #include "FASTKit/fast_ads_concurrent_hashmap.h"
    
    // Define how to calculate hash values and compare keys.
    uint64_t custom_hash_int(const FAST_ConcurrentHashmapKeyPtr key) {
        static std::hash<int> hasher;
        int* intKey = (int*)key;
        return hasher(*intKey);
    }
    
    int32_t custom_equal_int(const FAST_ConcurrentHashmapKeyPtr key1, const FAST_ConcurrentHashmapKeyPtr key2) {
        int* intKey1 = (int*)key1;
        int* intKey2 = (int*)key2;
        return (*intKey1) == (*intKey2);
    }
    
    // Customize the deletion condition.
    int32_t custom_erase_cond(FAST_ConcurrentHashmapKeyPtr key, FAST_ConcurrentHashmapValuePtr val, void* context) {
        return 1;
    }
    
    // Free the memory held by the key and value pointers.
    int32_t custom_free(FAST_ConcurrentHashmapKeyPtr key, FAST_ConcurrentHashmapValuePtr val, void* context) {
        int* intKey = (int*)key;
        int* intVal = (int*)val;
        delete intKey;
        delete intVal;
        return 0;
    }
    
    // Customize the modification condition. You can also pass nullptr to modify all elements.
    int32_t custom_modify_cond(FAST_ConcurrentHashmapKeyPtr key, FAST_ConcurrentHashmapValuePtr val, void* context) {
        return 1;
    }
    
    int32_t custom_work(FAST_ConcurrentHashmapKeyPtr key, FAST_ConcurrentHashmapValuePtr val, void* context) {
        int* intVal = (int*)val;
        int* intCtx = (int*)context;
        *intVal += (*intCtx);
        return 1;
    }
    
    static napi_value RunConcurrentHashmap(napi_env env, napi_callback_info info)
    {
        // Create a ConcurrentHashMap with proper configurations. The typical values of the load factor (loadfac) and the number of segments (numShards) are 0.8 and 64, respectively.
        // The load factor mainly affects operations within each segment. A larger load factor usually results in lower memory consumption but higher operational overhead.
        // The number of segments mainly affects the ConcurrentHashMap as a whole. A larger number of segments generally means better concurrency performance but greater memory consumption.
        FAST_ConcurrentHashmapHandle handle;
        HMS_FAST_ConcurrentHashmap_HashFunc hasher = &custom_hash_int;
        HMS_FAST_ConcurrentHashmap_KeyEqualFunc equaler = &custom_equal_int;
        float loadfac = 0.8;
        size_t numShards = 64;
        int ret = HMS_FAST_ConcurrentHashmap_Create(&handle, hasher, equaler, loadfac, numShards);
    
        // Initialize an empty HashMap and insert elements into it.
        const int size = 10;
        int keys[size] = {1,2,3,4,5,6,7,8,9,10};
        int vals[size] = {1,2,3,4,5,6,7,8,9,10};
        for (int i = 0; i < size; ++i) {
            ret = HMS_FAST_ConcurrentHashmap_Insert(
                handle,
                (FAST_ConcurrentHashmapKeyPtr)&(keys[i]),
                (FAST_ConcurrentHashmapValuePtr)&(vals[i]),
                nullptr
            );
        } // After the insertion, the HashMap should contain {1: 1, 2: 2, ..., 10: 10}.
        
        // Use insert to overwrite the existing key. If tryInsert is used, the existing key will not be overwritten.
        int key2 = 1;
        int val2 = 2;
        int* originVal0;
        ret = HMS_FAST_ConcurrentHashmap_Insert(
            handle,
            (FAST_ConcurrentHashmapKeyPtr)&key2,
            (FAST_ConcurrentHashmapValuePtr)&val2,
            (FAST_ConcurrentHashmapValuePtr*)&originVal0
        ); // {1: 2, ...}, and originVal0 == &vals[0]
    
        // Use the key to search for the corresponding value and save the result in the input pointer. When using this API, you need to verify the return value ret.
        // If the value of ret is not FAST_ERROR_CODE_SUCCESS, the value obtained by res is invalid.
        int targetKey = 1;
        int* res;
        ret = HMS_FAST_ConcurrentHashmap_Find(
            handle,
            (FAST_ConcurrentHashmapKeyPtr)&targetKey,
            (FAST_ConcurrentHashmapValuePtr*)&res
        ); // (*res) == 2
    
        // Remove the key-value pair associated with the key from the ConcurrentHashMap and retrieve the associated memory addresses.
        // You can use originKey/Val to obtain the address of the pre-inserted element for memory management. You can also use nullptr as the input parameter.
        int* originKey1;
        int* originVal1;
        int deleteKey = 1;
        ret = HMS_FAST_ConcurrentHashmap_Erase(
            handle,
            (FAST_ConcurrentHashmapKeyPtr)&deleteKey,
            (FAST_ConcurrentHashmapKeyPtr*)&originKey1,
            (FAST_ConcurrentHashmapValuePtr*)&originVal1
        ); // originKey1 == &keys[0] && originVal1 == &val2
    
        // Query the number of elements in the ConcurrentHashMap.
        size_t curSize = HMS_FAST_ConcurrentHashmap_Size(handle); // curSize == 9
    
        // Clear all elements in the ConcurrentHashMap.
        HMS_FAST_ConcurrentHashmap_Clear(handle);
        curSize = HMS_FAST_ConcurrentHashmap_Size(handle); // curSize == 0
    
        for (int i = 0; i < 6; i++) {
            int* key = new int{i};
            int* val = new int{i};
            ret = HMS_FAST_ConcurrentHashmap_Insert(
                handle,
                (FAST_ConcurrentHashmapKeyPtr)key,
                (FAST_ConcurrentHashmapValuePtr)val,
                nullptr
            );
        } // {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5}
    
        // Use the Traverse API to perform the custom_work operation on the elements that meet the custom_modify_cond condition.
        int context = 10;
        HMS_FAST_ConcurrentHashmap_Traverse(
            handle,
            (HMS_FAST_ConcurrentHashmap_HookFunc)&custom_modify_cond,
            nullptr,
            (HMS_FAST_ConcurrentHashmap_HookFunc)&custom_work,
            (void*)&context
        ); // {0: 10, 1: 11, 2: 12, 3: 13, 4: 14, 5: 15}
    
        // Use the EraseIf API to delete all key-value pairs that meet the custom_erase_cond condition and use custom_free to clear the corresponding memory.
        ret = HMS_FAST_ConcurrentHashmap_EraseIf(
            handle,
            (HMS_FAST_ConcurrentHashmap_HookFunc)&custom_erase_cond,
            nullptr,
            (HMS_FAST_ConcurrentHashmap_HookFunc)&custom_free,
            nullptr
        ); // size == 0 && ret == 6
    
        // Destroy the ConcurrentHashMap.
        HMS_FAST_ConcurrentHashmap_Destroy(handle);
        return 0;
    }