Avoid leaking nodes on a failed insert.
authorRobert Haas <[email protected]>
Wed, 25 Jul 2012 14:21:40 +0000 (10:21 -0400)
committerRobert Haas <[email protected]>
Wed, 25 Jul 2012 14:21:40 +0000 (10:21 -0400)
contrib/hashtest/hashtest.c
src/backend/utils/hash/chash.c

index 825f65e0cfceb8dd1d0b828e0a719274d2732b05..7c68e982a4bbeb5e963653ebb106f70cbf38bd6c 100644 (file)
@@ -79,6 +79,9 @@ test_chash(PG_FUNCTION_ARGS)
                ok = CHashInsert(chash, &e);
                if (!ok)
                        elog(LOG, "insert %u: failed", i);
+               ok = CHashInsert(chash, &e);
+               if (ok)
+                       elog(LOG, "insert %u: worked twice", i);
        }
 
        for (i = 0; i < 1000000; ++i)
index 9053f4453905649fe25390d67477817db9407d4c..aa9005ca3ad0ea8b0b67a03ac6e0049daf9a9796 100644 (file)
@@ -185,6 +185,7 @@ typedef struct CHashTableData
 
 /* Function prototypes. */
 static CHashPtr CHashAllocate(CHashTable table);
+static void CHashImmediateFree(CHashTable table, CHashPtr c);
 static bool CHashRemoveMarked(CHashTable table, uint32 bucket,
                                  CHashPtr *cp, volatile CHashPtr *p);
 
@@ -548,9 +549,7 @@ retry:
 
        /* If the insert failed, free the entry we allocated. */
        if (found)
-       {
-               /* XXX Need some code here! */
-       }
+               CHashImmediateFree(table, new);
 
        /* The insert succeeded if and only if no duplicate was found. */
        return !found;
@@ -700,6 +699,28 @@ CHashAllocate(CHashTable table)
        }
 }
 
+/*
+ * Free an arena slot immediately.
+ *
+ * When a slot that's actually in use is freed, we can't use this routine,
+ * because a concurrent bucket scan might have a private pointer to the
+ * object.  However, if an insert fails due to a duplicate key, then we need
+ * to put it back on the free list immediately.
+ */
+static void
+CHashImmediateFree(CHashTable table, CHashPtr c)
+{
+       volatile CHashTable vtable = table;
+       volatile CHashNode  *n;
+       uint32          f_home = ((uint32) MyBackendId) % table->nfreelists;
+
+       n = CHashTableGetNode(table, c);
+       SpinLockAcquire(&vtable->freelist[f_home].mutex);
+       n->un.gcnext = vtable->freelist[f_home].head;
+       vtable->freelist[f_home].head = c;
+       SpinLockRelease(&vtable->freelist[f_home].mutex);
+}
+
 /*
  * Attempt to remove marked elements from a bucket chain.
  *