"scan expunges failed",
"scans restarted",
"cleanup scans",
- "cleanup expunges",
- "cleanup expunges failed",
- "cleanups restarted",
"allocations failed",
"garbage enqueues retried",
"garbage dequeues failed",
uint32 hashcode,
const void *key,
CHashScanResult *res);
-static void CHashBucketCleanup(CHashTable table,
- CHashPtr *start,
- uint32 hashcode);
/*
* First stage of CHashTable initialization. We fill in all the constants
/*
* If we weren't able to remove the deleted item, rescan the bucket
- * to make sure it's really gone.
+ * to make sure it's really gone. This is just like a regular bucket
+ * scan, except that we don't care about the results. We're just doing
+ * it to achieve the side-effect of removing delete-marked nodes from
+ * the bucket chain.
*/
if (cleanup_scan)
{
+ CHashScanResult cleanup_scan;
+
CHashTableIncrementStatistic(table, CHS_Cleanup_Scan);
- CHashBucketCleanup(table, b, hashcode);
+ CHashBucketScan(table, b, hashcode, entry, &cleanup_scan);
}
/* Allow garbage collection for this bucket. */
* the address where the value of target was found. res->target_node will be
* a pointer to the address of the CHashNode referenced by res->target, unless
* res->target is invalid.
- *
- * NB: If you change this function, make sure to adjust CHashBucketCleanup
- * similarly.
*/
static void
CHashBucketScan(CHashTable table,
res->target_node = target_node;
}
-/*
- * Scan one bucket of a concurrent hash table and clean up any delete-marked
- * items by clipping them out of the chain. This is basically the same logic
- * as CHashBucketScan, except that we're not searching for an element; the
- * only purpose is to make sure that there are no residual deleted elements
- * in the chain.
- *
- * In practice this will usually not be necessary, because the next scan of
- * the relevant bucket will clean it up anyway. But we want to make sure we
- * don't have deleted elements eating up our limited storage and making new
- * inserts fail, so let's be safe.
- */
-static void
-CHashBucketCleanup(CHashTable table, CHashPtr *start, uint32 hashcode)
-{
- CHashPtr target;
- CHashPtr *pointer_to_target;
- CHashNode *target_node;
-
-retry:
- pointer_to_target = start;
- target = *pointer_to_target;
- for (;;)
- {
- CHashPtr next;
-
- /*
- * If we've reached the end of the bucket chain, stop; otherwise,
- * figure out the actual address of the next item.
- */
- if (CHashPtrIsInvalid(target))
- break;
- target_node = CHashTableGetNode(table, target);
-
- /*
- * target may have been fetched from an arena entry that could be
- * concurrently modified, so a dependency barrier is required before
- * dereferencing the derived pointer.
- */
- pg_read_barrier_depends();
- next = target_node->next;
-
- /* If element is delete-marked, try to remove it. */
- if (CHashPtrIsMarked(next))
- {
- if (__sync_bool_compare_and_swap(pointer_to_target,
- target,
- CHashPtrUnmark(next)))
- {
- /* We removed the item. */
- CHashTableIncrementStatistic(table, CHS_Cleanup_Expunge);
- CHashAddToGarbage(table, hashcode & table->bucket_mask,
- target);
- target = CHashPtrUnmark(next);
- }
- else
- {
- /* Someone else removed the item first. */
- CHashTableIncrementStatistic(table, CHS_Cleanup_Expunge_Fail);
- target = *pointer_to_target;
- if (CHashPtrIsMarked(target))
- {
- CHashTableIncrementStatistic(table, CHS_Cleanup_Restart);
- goto retry;
- }
- }
- continue;
- }
-
- pointer_to_target = &target_node->next;
- target = next;
- }
-}
-
/*
* Allocate an arena slot for a new item to be inserted into a hash table.
*