From: Robert Haas Date: Fri, 27 Jul 2012 04:14:08 +0000 (-0400) Subject: Add cleanup scan logic. X-Git-Url: http://git.postgresql.org/gitweb/static/developers.postgresql.org?a=commitdiff_plain;h=a41f8ea3192377259e9fd9e2a85994c67c61afdb;p=users%2Frhaas%2Fpostgres.git Add cleanup scan logic. --- diff --git a/src/backend/utils/hash/chash.c b/src/backend/utils/hash/chash.c index 52c5cc5b07..d9067a5892 100644 --- a/src/backend/utils/hash/chash.c +++ b/src/backend/utils/hash/chash.c @@ -202,6 +202,9 @@ static void CHashBucketScan(CHashTable table, 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 @@ -525,10 +528,12 @@ CHashDelete(CHashTable table, void *entry) } } + /* + * 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(); @@ -573,9 +578,8 @@ retry: 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) { @@ -888,3 +892,71 @@ CHashImmediateFree(CHashTable table, CHashPtr c) 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; + } +}