Add cleanup scan logic.
authorRobert Haas <rhaas@postgresql.org>
Fri, 27 Jul 2012 04:14:08 +0000 (00:14 -0400)
committerRobert Haas <rhaas@postgresql.org>
Fri, 27 Jul 2012 04:14:08 +0000 (00:14 -0400)
src/backend/utils/hash/chash.c

index 52c5cc5b0735dce3c74bbebe307c1003ddf3260d..d9067a589225ee62cf6b7ac8857bbc3a522d7dd1 100644 (file)
@@ -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;
+       }
+}