/* Function prototypes. */
static CHashPtr CHashAllocate(CHashTable table);
+static void CHashAddToGarbage(CHashTable table, uint32 bucket, CHashPtr c);
static void CHashImmediateFree(CHashTable table, CHashPtr c);
static bool CHashRemoveMarked(CHashTable table, uint32 bucket,
CHashPtr *cp, volatile CHashPtr *p);
if (found)
{
CHashPtr cc;
- bool needs_cleanup = false;
+ bool removed = false;
/*
* Really do the deletion. Since we've held no lock up to this
{
n->next = CHashPtrMark(cc);
if (*p == c)
+ {
*p = cc;
- else
- needs_cleanup = true;
+ removed = true;
+ }
}
SpinLockRelease(&table->bucket[bucket].mutex);
/*
* At this point the deletion is done. However, it's possible that
- * we weren't able to redirect the pointer that formerly addressed the
- * deleted item. If so, rescan the bucket chain and excise any deleted
- * items in the chain. We don't have to worry about not finding it;
- * that just means someone else did the work for us.
+ * someone else did it before we did it, in which case we need to
+ * return false rather than true. Also, we may have successfully
+ * removed the deleted from the linked list, in which case we need to
+ * add it to the garbage list; or that we failed to do so, in which
+ * case we need to rescan the list and remove any deleted items we
+ * find.
*/
- if (needs_cleanup)
+ if (removed)
+ CHashAddToGarbage(table, bucket, c);
+ else
{
- /* XXX */
+ /* XXX Rescan bucket. */
}
}
}
}
+/*
+ * Add an arena slot to the appropriate garbage list.
+ *
+ * The next garbage collection cycle for the affected bucket will move it
+ * to the free list. We can't do that immediately because there might be
+ * someone traversing the list who is counting on being able to follow the
+ * next pointer. It's OK to clobber the hash value, though, since a spurious
+ * failure to match an already-deleted item shouldn't cause any problems;
+ * this is why gcnext can share space with the hash value.
+ */
+static void
+CHashAddToGarbage(CHashTable table, uint32 bucket, CHashPtr c)
+{
+ uint32 garbage_bucket;
+ volatile CHashNode *n;
+ volatile CHashBucket *garbage;
+
+ garbage_bucket = bucket >> table->garbage_shift;
+ n = CHashTableGetNode(table, c);
+ garbage = &table->garbage[garbage_bucket];
+
+ SpinLockAcquire(&garbage->mutex);
+ n->un.gcnext = garbage->head;
+ garbage->head = c;
+ SpinLockRelease(&garbage->mutex);
+}
+
/*
* Free an arena slot immediately.
*
CHashImmediateFree(CHashTable table, CHashPtr c)
{
volatile CHashTable vtable = table;
- volatile CHashNode *n;
+ volatile CHashNode *n;
uint32 f_home = ((uint32) MyBackendId) % table->nfreelists;
n = CHashTableGetNode(table, c);
if (retry_needed)
return true;
+ /* Add c to garbage list. */
+ CHashAddToGarbage(table, bucket, c);
+
/* The new target of the pointer may also be delete-marked, so loop. */
c = cc;
} while (CHashPtrIsMarked(c));