char *arena; /* arena */
/*
- * This field will be different in each backend; the shared copy is
+ * These fields will be different in each backend; the shared copy is
* unused.
*/
uint32 gc_next; /* next garbage list to reclaim */
+ uint64 s_insert_retry; /* insert point concurrently updated */
+ uint64 s_delete_retry; /* delete point concurrently updated */
+ uint64 s_cleanup_scan; /* expunge-after-delete failed */
+ uint64 s_scan_expunge_ok; /* scan did expunge */
+ uint64 s_scan_expunge_fail; /* expunge failed */
+ uint64 s_scan_restart; /* scan restarted */
+ uint64 s_cleanup_expunge_ok; /* cleanup scan did expunge */
+ uint64 s_cleanup_expunge_fail; /* cleanup scan expunge failed */
+ uint64 s_cleanup_restart; /* cleanup scan restarted */
+ uint64 s_allocate_pop_fail; /* freelist pop failed */
+ uint64 s_gc_pop_fail; /* garbage list pop failed */
+ uint64 s_gc_reclaim_retry; /* failed to return GC'd nodes */
+ uint64 s_garbage_retry; /* failed to enqueue garbage */
+ uint64 s_free_retry; /* failed to perform immediate free */
} CHashTableData;
#define CHashTableGetRaw(table, offset) \
uint32 bucket_shift;
/* Allocate table and copy descriptor. */
- table = MemoryContextAlloc(TopMemoryContext, sizeof(CHashTableData));
+ table = MemoryContextAllocZero(TopMemoryContext, sizeof(CHashTableData));
memcpy(&table->desc, desc, sizeof(CHashDescriptor));
/* Sanity checks. */
nnew->next = scan.target;
if (!__sync_bool_compare_and_swap(scan.pointer_to_target,
scan.target, new))
+ {
+ table->s_insert_retry++;
goto retry;
+ }
}
/* Allow garbage collection for this bucket. */
cleanup_scan = true;
break;
}
+ table->s_delete_retry++;
}
/*
* to make sure it's really gone.
*/
if (cleanup_scan)
+ {
+ table->s_cleanup_scan++;
CHashBucketCleanup(table, &table->bucket[bucket], hashcode);
+ }
/* Allow garbage collection for this bucket. */
CHashTableUnsuppressGC();
* all non-deleted items (and possibly some deleted items)
* that were present at the time we began the scan.
*/
+ table->s_scan_expunge_ok++;
CHashAddToGarbage(table, hashcode & table->bucket_mask,
target);
target = CHashPtrUnmark(next);
* have to be pretty unlucky to have it happen even twice in
* a row.
*/
+ table->s_scan_expunge_fail++;
target = *pointer_to_target;
if (CHashPtrIsMarked(target))
+ {
+ table->s_scan_restart++;
goto retry;
+ }
continue;
}
}
CHashPtrUnmark(next)))
{
/* We removed the item. */
+ table->s_cleanup_expunge_ok++;
CHashAddToGarbage(table, hashcode & table->bucket_mask,
target);
target = CHashPtrUnmark(next);
pg_read_barrier_depends();
if (__sync_bool_compare_and_swap(b, new, n->un.gcnext))
return new;
+ table->s_allocate_pop_fail++;
}
/* If next garbage list is non-empty, empty it via compare-and-swap. */
table->gc_next = (table->gc_next + 1) % table->ngarbage;
b = &table->garbage[table->gc_next];
garbage = *b;
- if (!CHashPtrIsInvalid(garbage) &&
- __sync_bool_compare_and_swap(b, garbage, InvalidCHashPtr))
+ if (CHashPtrIsInvalid(garbage))
+ ;
+ else if (!__sync_bool_compare_and_swap(b, garbage, InvalidCHashPtr))
+ ++table->s_gc_pop_fail;
+ else
{
uint64 chash_bucket;
uint32 i;
/* Push reclaimed elements onto home free list. */
b = &table->freelist[f_home];
- do
+ for (;;)
{
oldhead = *b;
n->un.gcnext = oldhead;
- } while (!__sync_bool_compare_and_swap(b, oldhead, fhead));
+ if (__sync_bool_compare_and_swap(b, oldhead, fhead))
+ break;
+ ++table->s_gc_reclaim_retry;
+ }
}
/* Return the element we saved for ourselves. */
n = CHashTableGetNode(table, c);
garbage = &table->garbage[garbage_bucket];
- do
+ while (1)
{
g = *garbage;
n->un.gcnext = g;
- } while (!__sync_bool_compare_and_swap(garbage, g, c));
+ if (__sync_bool_compare_and_swap(garbage, g, c))
+ break;
+ ++table->s_garbage_retry;
+ }
}
/*
n = CHashTableGetNode(table, c);
free = &table->freelist[f_home];
- do
+ for (;;)
{
f = *free;
n->un.gcnext = f;
- } while (!__sync_bool_compare_and_swap(free, f, c));
+ if (__sync_bool_compare_and_swap(free, f, c))
+ break;
+ ++table->s_free_retry;
+ }
}