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*)¤t_thread->next, NULL, NULL);
if (interlocked_add(¤t_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*)¤t_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);
}
}
}