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.
+ */
if (cleanup_scan)
- {
- /* XXX */
- }
+ CHashBucketCleanup(table, &table->bucket[bucket], hashcode);
/* Allow garbage collection for this bucket. */
CHashTableUnsuppressGC();
int cmp;
/*
- * Read arena offset of target. If we've reached the end of the
- * bucket chain, stop; otherwise, figure out the actual address of
- * the next item.
+ * If we've reached the end of the bucket chain, stop; otherwise,
+ * figure out the actual address of the next item.
*/
if (target == InvalidCHashPtr)
{
n->un.gcnext = f;
} while (!__sync_bool_compare_and_swap(free, f, c));
}
+
+/*
+ * 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 (target == InvalidCHashPtr)
+ 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, next))
+ {
+ /* We removed the item. */
+ CHashAddToGarbage(table, hashcode & table->bucket_mask,
+ target);
+ target = next;
+ continue;
+ }
+ else
+ {
+ /* Someone else removed the item first. */
+ target = *pointer_to_target;
+ if (CHashPtrIsMarked(target))
+ goto retry;
+ continue;
+ }
+ }
+
+ pointer_to_target = &target_node->next;
+ target = next;
+ }
+}