CLDS_HASH_TABLE_SNAPSHOT_RESULT clds_hash_table_snapshot()

in src/clds_hash_table.c [1098:1247]


CLDS_HASH_TABLE_SNAPSHOT_RESULT clds_hash_table_snapshot(CLDS_HASH_TABLE_HANDLE clds_hash_table, CLDS_HAZARD_POINTERS_THREAD_HANDLE clds_hazard_pointers_thread, CLDS_HASH_TABLE_ITEM*** items, uint64_t* item_count, THANDLE(CANCELLATION_TOKEN) cancellation_token)
{
    CLDS_HASH_TABLE_SNAPSHOT_RESULT result;

    if (
        /* Codes_SRS_CLDS_HASH_TABLE_42_013: [ If clds_hash_table is NULL then clds_hash_table_snapshot shall fail and return CLDS_HASH_TABLE_SNAPSHOT_ERROR. ]*/
        (clds_hash_table == NULL) ||
        /* Codes_SRS_CLDS_HASH_TABLE_42_014: [ If clds_hazard_pointers_thread is NULL then clds_hash_table_snapshot shall fail and return CLDS_HASH_TABLE_SNAPSHOT_ERROR. ]*/
        (clds_hazard_pointers_thread == NULL) ||
        /* Codes_SRS_CLDS_HASH_TABLE_42_015: [ If items is NULL then clds_hash_table_snapshot shall fail and return CLDS_HASH_TABLE_SNAPSHOT_ERROR. ]*/
        (items == NULL) ||
        /* Codes_SRS_CLDS_HASH_TABLE_42_016: [ If item_count is NULL then clds_hash_table_snapshot shall fail and return CLDS_HASH_TABLE_SNAPSHOT_ERROR. ]*/
        (item_count == NULL)
        )
    {
        LogError("Invalid arguments: CLDS_HASH_TABLE_HANDLE clds_hash_table=%p, CLDS_HAZARD_POINTERS_THREAD_HANDLE clds_hazard_pointers_thread=%p, CLDS_HASH_TABLE_ITEM*** items=%p, uint64_t* item_count=%p, THANDLE(CANCELLATION_TOKEN) cancellation_token=%p",
            clds_hash_table, clds_hazard_pointers_thread, items, item_count, cancellation_token);
        result = CLDS_HASH_TABLE_SNAPSHOT_ERROR;
    }
    else
    {
        internal_lock_writes(clds_hash_table);

        uint64_t temp_item_count = 0;

        /* Codes_SRS_CLDS_HASH_TABLE_01_114: [ clds_hash_table_snapshot shall determine all the items in the hash table by summing up the item count for all bucket arrays in all levels. ]*/
        BUCKET_ARRAY* current_bucket_array = interlocked_compare_exchange_pointer((void* volatile_atomic*)&clds_hash_table->first_hash_table, NULL, NULL);
        while (current_bucket_array != NULL)
        {
            BUCKET_ARRAY* next_bucket_array = interlocked_compare_exchange_pointer((void* volatile_atomic*)&current_bucket_array->next_bucket, NULL, NULL);

            temp_item_count += interlocked_add(&current_bucket_array->item_count, 0);

            current_bucket_array = next_bucket_array;
        }

        if (temp_item_count == 0)
        {
            /* Codes_SRS_CLDS_HASH_TABLE_42_064: [ If there are no items then clds_hash_table_snapshot shall set items to NULL and item_count to 0 and return CLDS_HASH_TABLE_SNAPSHOT_OK. ]*/
            *items = NULL;
            *item_count = 0;
            result = CLDS_HASH_TABLE_SNAPSHOT_OK;
        }
        else
        {
            /* Codes_SRS_CLDS_HASH_TABLE_42_023: [ clds_hash_table_snapshot shall allocate an array of CLDS_HASH_TABLE_ITEM* ]*/
            CLDS_SORTED_LIST_ITEM** items_to_return = malloc_2((size_t)temp_item_count, sizeof(CLDS_SORTED_LIST_ITEM*));

            if (items_to_return == NULL)
            {
                /* Codes_SRS_CLDS_HASH_TABLE_42_061: [ If there are any other failures then clds_hash_table_snapshot shall fail and return CLDS_HASH_TABLE_SNAPSHOT_ERROR. ]*/
                LogError("malloc_2((size_t)temp_item_count=%zu, sizeof(CLDS_SORTED_LIST_ITEM*)=%zu) failed for the items to return",
                    (size_t)temp_item_count, sizeof(CLDS_SORTED_LIST_ITEM*));
                result = CLDS_HASH_TABLE_SNAPSHOT_ERROR;
            }
            else
            {
                uint64_t result_index = 0;
                bool is_cancelled = false;

                /* Codes_SRS_CLDS_HASH_TABLE_42_024: [ For each bucket in the array: ]*/
                current_bucket_array = interlocked_compare_exchange_pointer((void* volatile_atomic*)&clds_hash_table->first_hash_table, NULL, NULL);
                while (current_bucket_array != NULL)
                {
                    BUCKET_ARRAY* next_bucket_array = interlocked_compare_exchange_pointer((void* volatile_atomic*)&current_bucket_array->next_bucket, NULL, NULL);

                    if (interlocked_add(&current_bucket_array->item_count, 0) != 0)
                    {
                        int32_t bucket_count = interlocked_add(&current_bucket_array->bucket_count, 0);
                        int32_t i;

                        for (i = 0; i < bucket_count; i++)
                        {
                            if ((current_bucket_array->hash_table[i] != NULL) && (temp_item_count > 0))
                            {
                                uint64_t retrieved_item_count;

                                if (
                                    /* Codes_SRS_CLDS_HASH_TABLE_01_115: [ If cancellation_token is non-NULL and cancellation_token_is_cancelled returns true for cancellation_token, clds_hash_table_snapshot shall fail and return CLDS_HASH_TABLE_SNAPSHOT_ABANDONED. ]*/
                                    (cancellation_token != NULL) &&
                                    (cancellation_token_is_canceled(cancellation_token))
                                    )
                                {
                                    LogVerbose("snapshot cancelled");
                                    is_cancelled = true;
                                    break;
                                }

                                /* Codes_SRS_CLDS_HASH_TABLE_42_026: [ clds_hash_table_snapshot shall call clds_sorted_list_get_all with the next portion of the allocated array and false as required_locked_list. ]*/
                                CLDS_SORTED_LIST_GET_ALL_RESULT get_all_result = clds_sorted_list_get_all(current_bucket_array->hash_table[i], clds_hazard_pointers_thread, temp_item_count, items_to_return + result_index, &retrieved_item_count, false);
                                if (get_all_result != CLDS_SORTED_LIST_GET_ALL_OK)
                                {
                                    /* Codes_SRS_CLDS_HASH_TABLE_42_061: [ If there are any other failures then clds_hash_table_snapshot shall fail and return CLDS_HASH_TABLE_SNAPSHOT_ERROR. ]*/
                                    LogError("clds_sorted_list_get_all failed with %" PRI_MU_ENUM, MU_ENUM_VALUE(CLDS_SORTED_LIST_GET_ALL_RESULT, get_all_result));
                                    break;
                                }
                                else
                                {
                                    result_index += retrieved_item_count;
                                    temp_item_count -= retrieved_item_count;
                                }
                            }
                        }

                        if (i < bucket_count)
                        {
                            break;
                        }
                    }

                    current_bucket_array = next_bucket_array;
                }

                if (current_bucket_array != NULL)
                {
                    if (is_cancelled)
                    {
                        result = CLDS_HASH_TABLE_SNAPSHOT_ABANDONED;
                    }
                    else
                    {
                        result = CLDS_HASH_TABLE_SNAPSHOT_ERROR;
                    }

                    for (uint64_t i = 0; i < result_index; i++)
                    {
                        clds_sorted_list_node_release(items_to_return[i]);
                    }
                    free(items_to_return);
                }
                else
                {
                    /* Codes_SRS_CLDS_HASH_TABLE_42_028: [ clds_hash_table_snapshot shall store the allocated array of items in items. ]*/
                    *items = (CLDS_HASH_TABLE_ITEM**)items_to_return;
                    items_to_return = NULL;

                    /* Codes_SRS_CLDS_HASH_TABLE_42_029: [ clds_hash_table_snapshot shall store the count of items in item_count. ]*/
                    *item_count = result_index;

                    /* Codes_SRS_CLDS_HASH_TABLE_42_031: [ clds_hash_table_snapshot shall succeed and return CLDS_HASH_TABLE_SNAPSHOT_OK. ]*/
                    result = CLDS_HASH_TABLE_SNAPSHOT_OK;
                }
            }
        }

        internal_unlock_writes(clds_hash_table);
    }

    return result;
}