static void internal_reclaim()

in src/clds_hazard_pointers.c [164:285]


static void internal_reclaim(CLDS_HAZARD_POINTERS_THREAD_HANDLE clds_hazard_pointers_thread)
{
    CLDS_HAZARD_POINTERS_HANDLE clds_hazard_pointers = clds_hazard_pointers_thread->clds_hazard_pointers;

    (void)interlocked_increment(&clds_hazard_pointers->pending_reclaim_calls);

    CLDS_ST_HASH_SET_HANDLE all_hps_set = clds_st_hash_set_create(hp_key_hash, hp_key_compare, clds_hazard_pointers_thread->clds_hazard_pointers->reclaim_threshold);
    if (all_hps_set == NULL)
    {
        // oops, panic now!
        LogError("Cannot allocate hazard pointers hash set");
    }
    else
    {
        // go through all hazard pointers of all threads, no thread should be able to get a hazard pointer after this point
        CLDS_HAZARD_POINTERS_THREAD_HANDLE current_thread = interlocked_compare_exchange_pointer((void* volatile_atomic*)&clds_hazard_pointers->head, NULL, NULL);
        while (current_thread != NULL)
        {
            CLDS_HAZARD_POINTERS_THREAD_HANDLE next_thread = interlocked_compare_exchange_pointer((void* volatile_atomic*)&current_thread->next, NULL, NULL);
            if (interlocked_add(&current_thread->active, 0) == 1)
            {
                // look at the pointers of this thread
                // if it gets unregistered in the meanwhile we won't care
                // if it gets registered again we also don't care as for sure it does not have our hazard pointer anymore
                CLDS_HAZARD_POINTER_RECORD_HANDLE clds_hazard_pointer = interlocked_compare_exchange_pointer((void* volatile_atomic*)&current_thread->pointers, NULL, NULL);
                while (clds_hazard_pointer != NULL)
                {
                    CLDS_HAZARD_POINTER_RECORD_HANDLE next_hazard_pointer = interlocked_compare_exchange_pointer((void* volatile_atomic*)&clds_hazard_pointer->next, NULL, NULL);
                    void* node = interlocked_compare_exchange_pointer((void* volatile_atomic*)&clds_hazard_pointer->node, NULL, NULL);
                    if (node != NULL)
                    {
                        CLDS_ST_HASH_SET_INSERT_RESULT insert_result = clds_st_hash_set_insert(all_hps_set, node);
                        if ((insert_result == CLDS_ST_HASH_SET_INSERT_OK) ||
                            (insert_result == CLDS_ST_HASH_SET_INSERT_KEY_ALREADY_EXISTS))
                        {
                            // all ok
                        }
                        else
                        {
                            LogError("Cannot insert hazard pointer in set");
                            break;
                        }
                    }

                    clds_hazard_pointer = next_hazard_pointer;
                }

                if (clds_hazard_pointer != NULL)
                {
                    break;
                }
            }

            current_thread = next_thread;
        }

        if (current_thread != NULL)
        {
            LogError("Error collecting hazard pointers");
        }
        else
        {
            // go through all pointers in the reclaim list
            CLDS_RECLAIM_LIST_ENTRY* current_reclaim_entry = clds_hazard_pointers_thread->reclaim_list;
            CLDS_RECLAIM_LIST_ENTRY* prev_reclaim_entry = NULL;
            while (current_reclaim_entry != NULL)
            {
                // this is the scan for the pointers
                CLDS_ST_HASH_SET_FIND_RESULT find_result = clds_st_hash_set_find(all_hps_set, current_reclaim_entry->node);

                if (find_result == CLDS_ST_HASH_SET_FIND_ERROR)
                {
                    LogError("Error finding pointer");
                    break;
                }
                else if (find_result == CLDS_ST_HASH_SET_FIND_NOT_FOUND)
                {
                    // node is safe to be reclaimed
                    current_reclaim_entry->reclaim(current_reclaim_entry->node);

                    // now remove it from the reclaim list
                    if (prev_reclaim_entry == NULL)
                    {
                        // this is the head of the reclaim list
                        clds_hazard_pointers_thread->reclaim_list = current_reclaim_entry->next;
                        free(current_reclaim_entry);
                        current_reclaim_entry = clds_hazard_pointers_thread->reclaim_list;
                    }
                    else
                    {
                        prev_reclaim_entry->next = current_reclaim_entry->next;
                        free(current_reclaim_entry);
                        current_reclaim_entry = prev_reclaim_entry->next;
                    }

                    clds_hazard_pointers_thread->reclaim_list_entry_count--;
                }
                else
                {
                    // not safe, sorry, shall still have it around, move to next reclaim entry
                    prev_reclaim_entry = current_reclaim_entry;
                    current_reclaim_entry = current_reclaim_entry->next;
                }
            }
        }

        clds_st_hash_set_destroy(all_hps_set);
    }

    int64_t current_epoch = interlocked_add_64(&clds_hazard_pointers->epoch, 0);
    if (interlocked_decrement(&clds_hazard_pointers->pending_reclaim_calls) == 0)
    {
        if (interlocked_compare_exchange_64(&clds_hazard_pointers->epoch, current_epoch + 1, current_epoch) != current_epoch)
        {
            // epoch already incremented by other thread, let them do the cleanup
        }
        else
        {
            free_inactive_threads_in_previous_epochs(clds_hazard_pointers, current_epoch + 1);
        }
    }
}