Avoid leaking nodes on a failed insert.
authorRobert Haas <rhaas@postgresql.org>
Wed, 25 Jul 2012 14:21:40 +0000 (10:21 -0400)
committerRobert Haas <rhaas@postgresql.org>
Mon, 13 Oct 2014 17:13:54 +0000 (13:13 -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.
  *