PostgreSQL Source Code git master
Loading...
Searching...
No Matches
dshash.c File Reference
#include "postgres.h"
#include <limits.h>
#include "common/hashfn.h"
#include "lib/dshash.h"
#include "storage/lwlock.h"
#include "utils/dsa.h"
Include dependency graph for dshash.c:

Go to the source code of this file.

Data Structures

struct  dshash_table_item
 
struct  dshash_partition
 
struct  dshash_table_control
 
struct  dshash_table
 

Macros

#define DSHASH_NUM_PARTITIONS_LOG2   7
 
#define DSHASH_NUM_PARTITIONS   (1 << DSHASH_NUM_PARTITIONS_LOG2)
 
#define DSHASH_MAGIC   0x75ff6a20
 
#define ENTRY_FROM_ITEM(item)    ((char *)(item) + MAXALIGN(sizeof(dshash_table_item)))
 
#define ITEM_FROM_ENTRY(entry)
 
#define NUM_SPLITS(size_log2)    (size_log2 - DSHASH_NUM_PARTITIONS_LOG2)
 
#define NUM_BUCKETS(size_log2)    (((size_t) 1) << (size_log2))
 
#define BUCKETS_PER_PARTITION(size_log2)    (((size_t) 1) << NUM_SPLITS(size_log2))
 
#define MAX_COUNT_PER_PARTITION(hash_table)
 
#define PARTITION_FOR_HASH(hash)    (hash >> ((sizeof(dshash_hash) * CHAR_BIT) - DSHASH_NUM_PARTITIONS_LOG2))
 
#define BUCKET_INDEX_FOR_HASH_AND_SIZE(hash, size_log2)    (hash >> ((sizeof(dshash_hash) * CHAR_BIT) - (size_log2)))
 
#define BUCKET_INDEX_FOR_PARTITION(partition, size_log2)    ((partition) << NUM_SPLITS(size_log2))
 
#define PARTITION_FOR_BUCKET_INDEX(bucket_idx, size_log2)    ((bucket_idx) >> NUM_SPLITS(size_log2))
 
#define BUCKET_FOR_HASH(hash_table, hash)
 
#define PARTITION_LOCK(hash_table, i)    (&(hash_table)->control->partitions[(i)].lock)
 
#define ASSERT_NO_PARTITION_LOCKS_HELD_BY_ME(hash_table)
 

Typedefs

typedef struct dshash_partition dshash_partition
 
typedef struct dshash_table_control dshash_table_control
 

Functions

static void delete_item (dshash_table *hash_table, dshash_table_item *item)
 
static void resize (dshash_table *hash_table, size_t new_size_log2)
 
static void ensure_valid_bucket_pointers (dshash_table *hash_table)
 
static dshash_table_itemfind_in_bucket (dshash_table *hash_table, const void *key, dsa_pointer item_pointer)
 
static void insert_item_into_bucket (dshash_table *hash_table, dsa_pointer item_pointer, dshash_table_item *item, dsa_pointer *bucket)
 
static dshash_table_iteminsert_into_bucket (dshash_table *hash_table, const void *key, dsa_pointer *bucket)
 
static bool delete_key_from_bucket (dshash_table *hash_table, const void *key, dsa_pointer *bucket_head)
 
static bool delete_item_from_bucket (dshash_table *hash_table, dshash_table_item *item, dsa_pointer *bucket_head)
 
static dshash_hash hash_key (dshash_table *hash_table, const void *key)
 
static bool equal_keys (dshash_table *hash_table, const void *a, const void *b)
 
static void copy_key (dshash_table *hash_table, void *dest, const void *src)
 
dshash_tabledshash_create (dsa_area *area, const dshash_parameters *params, void *arg)
 
dshash_tabledshash_attach (dsa_area *area, const dshash_parameters *params, dshash_table_handle handle, void *arg)
 
void dshash_detach (dshash_table *hash_table)
 
void dshash_destroy (dshash_table *hash_table)
 
dshash_table_handle dshash_get_hash_table_handle (dshash_table *hash_table)
 
voiddshash_find (dshash_table *hash_table, const void *key, bool exclusive)
 
voiddshash_find_or_insert (dshash_table *hash_table, const void *key, bool *found)
 
bool dshash_delete_key (dshash_table *hash_table, const void *key)
 
void dshash_delete_entry (dshash_table *hash_table, void *entry)
 
void dshash_release_lock (dshash_table *hash_table, void *entry)
 
int dshash_memcmp (const void *a, const void *b, size_t size, void *arg)
 
dshash_hash dshash_memhash (const void *v, size_t size, void *arg)
 
void dshash_memcpy (void *dest, const void *src, size_t size, void *arg)
 
int dshash_strcmp (const void *a, const void *b, size_t size, void *arg)
 
dshash_hash dshash_strhash (const void *v, size_t size, void *arg)
 
void dshash_strcpy (void *dest, const void *src, size_t size, void *arg)
 
void dshash_seq_init (dshash_seq_status *status, dshash_table *hash_table, bool exclusive)
 
voiddshash_seq_next (dshash_seq_status *status)
 
void dshash_seq_term (dshash_seq_status *status)
 
void dshash_delete_current (dshash_seq_status *status)
 
void dshash_dump (dshash_table *hash_table)
 

Macro Definition Documentation

◆ ASSERT_NO_PARTITION_LOCKS_HELD_BY_ME

#define ASSERT_NO_PARTITION_LOCKS_HELD_BY_ME (   hash_table)
Value:
Assert(!LWLockAnyHeldByMe(&(hash_table)->control->partitions[0].lock, \
#define Assert(condition)
Definition c.h:873
#define DSHASH_NUM_PARTITIONS
Definition dshash.c:62
bool LWLockAnyHeldByMe(LWLock *lock, int nlocks, size_t stride)
Definition lwlock.c:1929

Definition at line 197 of file dshash.c.

208{
209 dshash_table *hash_table;
210 dsa_pointer control;
211
212 /* Allocate the backend-local object representing the hash table. */
213 hash_table = palloc_object(dshash_table);
214
215 /* Allocate the control object in shared memory. */
216 control = dsa_allocate(area, sizeof(dshash_table_control));
217
218 /* Set up the local and shared hash table structs. */
219 hash_table->area = area;
220 hash_table->params = *params;
221 hash_table->arg = arg;
222 hash_table->control = dsa_get_address(area, control);
223 hash_table->control->handle = control;
224 hash_table->control->magic = DSHASH_MAGIC;
225 hash_table->control->lwlock_tranche_id = params->tranche_id;
226
227 /* Set up the array of lock partitions. */
228 {
230 int tranche_id = hash_table->control->lwlock_tranche_id;
231 int i;
232
233 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
234 {
235 LWLockInitialize(&partitions[i].lock, tranche_id);
236 partitions[i].count = 0;
237 }
238 }
239
240 /*
241 * Set up the initial array of buckets. Our initial size is the same as
242 * the number of partitions.
243 */
245 hash_table->control->buckets =
249 if (!DsaPointerIsValid(hash_table->control->buckets))
250 {
251 dsa_free(area, control);
254 errmsg("out of memory"),
255 errdetail("Failed on DSA request of size %zu.",
257 }
258 hash_table->buckets = dsa_get_address(area,
259 hash_table->control->buckets);
260 hash_table->size_log2 = hash_table->control->size_log2;
261
262 return hash_table;
263}
264
265/*
266 * Attach to an existing hash table using a handle. The returned object is
267 * allocated in backend-local memory using the current MemoryContext. 'arg'
268 * will be passed through to the compare and hash functions.
269 */
271dshash_attach(dsa_area *area, const dshash_parameters *params,
272 dshash_table_handle handle, void *arg)
273{
274 dshash_table *hash_table;
275 dsa_pointer control;
276
277 /* Allocate the backend-local object representing the hash table. */
278 hash_table = palloc_object(dshash_table);
279
280 /* Find the control object in shared memory. */
281 control = handle;
282
283 /* Set up the local hash table struct. */
284 hash_table->area = area;
285 hash_table->params = *params;
286 hash_table->arg = arg;
287 hash_table->control = dsa_get_address(area, control);
288 Assert(hash_table->control->magic == DSHASH_MAGIC);
289
290 /*
291 * These will later be set to the correct values by
292 * ensure_valid_bucket_pointers(), at which time we'll be holding a
293 * partition lock for interlocking against concurrent resizing.
294 */
295 hash_table->buckets = NULL;
296 hash_table->size_log2 = 0;
297
298 return hash_table;
299}
300
301/*
302 * Detach from a hash table. This frees backend-local resources associated
303 * with the hash table, but the hash table will continue to exist until it is
304 * either explicitly destroyed (by a backend that is still attached to it), or
305 * the area that backs it is returned to the operating system.
306 */
307void
308dshash_detach(dshash_table *hash_table)
309{
311
312 /* The hash table may have been destroyed. Just free local memory. */
313 pfree(hash_table);
314}
315
316/*
317 * Destroy a hash table, returning all memory to the area. The caller must be
318 * certain that no other backend will attempt to access the hash table before
319 * calling this function. Other backend must explicitly call dshash_detach to
320 * free up backend-local memory associated with the hash table. The backend
321 * that calls dshash_destroy must not call dshash_detach.
322 */
323void
324dshash_destroy(dshash_table *hash_table)
325{
326 size_t size;
327 size_t i;
328
329 Assert(hash_table->control->magic == DSHASH_MAGIC);
331
332 /* Free all the entries. */
333 size = NUM_BUCKETS(hash_table->size_log2);
334 for (i = 0; i < size; ++i)
335 {
336 dsa_pointer item_pointer = hash_table->buckets[i];
337
339 {
340 dshash_table_item *item;
342
343 item = dsa_get_address(hash_table->area, item_pointer);
344 next_item_pointer = item->next;
345 dsa_free(hash_table->area, item_pointer);
347 }
348 }
349
350 /*
351 * Vandalize the control block to help catch programming errors where
352 * other backends access the memory formerly occupied by this hash table.
353 */
354 hash_table->control->magic = 0;
355
356 /* Free the active table and control object. */
357 dsa_free(hash_table->area, hash_table->control->buckets);
358 dsa_free(hash_table->area, hash_table->control->handle);
359
360 pfree(hash_table);
361}
362
363/*
364 * Get a handle that can be used by other processes to attach to this hash
365 * table.
366 */
369{
370 Assert(hash_table->control->magic == DSHASH_MAGIC);
371
372 return hash_table->control->handle;
373}
374
375/*
376 * Look up an entry, given a key. Returns a pointer to an entry if one can be
377 * found with the given key. Returns NULL if the key is not found. If a
378 * non-NULL value is returned, the entry is locked and must be released by
379 * calling dshash_release_lock. If an error is raised before
380 * dshash_release_lock is called, the lock will be released automatically, but
381 * the caller must take care to ensure that the entry is not left corrupted.
382 * The lock mode is either shared or exclusive depending on 'exclusive'.
383 *
384 * The caller must not hold a lock already.
385 *
386 * Note that the lock held is in fact an LWLock, so interrupts will be held on
387 * return from this function, and not resumed until dshash_release_lock is
388 * called. It is a very good idea for the caller to release the lock quickly.
389 */
390void *
391dshash_find(dshash_table *hash_table, const void *key, bool exclusive)
392{
394 size_t partition;
395 dshash_table_item *item;
396
397 hash = hash_key(hash_table, key);
399
400 Assert(hash_table->control->magic == DSHASH_MAGIC);
402
404 exclusive ? LW_EXCLUSIVE : LW_SHARED);
406
407 /* Search the active bucket. */
408 item = find_in_bucket(hash_table, key, BUCKET_FOR_HASH(hash_table, hash));
409
410 if (!item)
411 {
412 /* Not found. */
414 return NULL;
415 }
416 else
417 {
418 /* The caller will free the lock by calling dshash_release_lock. */
419 return ENTRY_FROM_ITEM(item);
420 }
421}
422
423/*
424 * Returns a pointer to an exclusively locked item which must be released with
425 * dshash_release_lock. If the key is found in the hash table, 'found' is set
426 * to true and a pointer to the existing entry is returned. If the key is not
427 * found, 'found' is set to false, and a pointer to a newly created entry is
428 * returned.
429 *
430 * Notes above dshash_find() regarding locking and error handling equally
431 * apply here.
432 */
433void *
435 const void *key,
436 bool *found)
437{
439 size_t partition_index;
441 dshash_table_item *item;
442
443 hash = hash_key(hash_table, key);
446
447 Assert(hash_table->control->magic == DSHASH_MAGIC);
449
450restart:
454
455 /* Search the active bucket. */
456 item = find_in_bucket(hash_table, key, BUCKET_FOR_HASH(hash_table, hash));
457
458 if (item)
459 *found = true;
460 else
461 {
462 *found = false;
463
464 /* Check if we are getting too full. */
465 if (partition->count > MAX_COUNT_PER_PARTITION(hash_table))
466 {
467 /*
468 * The load factor (= keys / buckets) for all buckets protected by
469 * this partition is > 0.75. Presumably the same applies
470 * generally across the whole hash table (though we don't attempt
471 * to track that directly to avoid contention on some kind of
472 * central counter; we just assume that this partition is
473 * representative). This is a good time to resize.
474 *
475 * Give up our existing lock first, because resizing needs to
476 * reacquire all the locks in the right order to avoid deadlocks.
477 */
479 resize(hash_table, hash_table->size_log2 + 1);
480
481 goto restart;
482 }
483
484 /* Finally we can try to insert the new item. */
485 item = insert_into_bucket(hash_table, key,
486 &BUCKET_FOR_HASH(hash_table, hash));
487 item->hash = hash;
488 /* Adjust per-lock-partition counter for load factor knowledge. */
489 ++partition->count;
490 }
491
492 /* The caller must release the lock with dshash_release_lock. */
493 return ENTRY_FROM_ITEM(item);
494}
495
496/*
497 * Remove an entry by key. Returns true if the key was found and the
498 * corresponding entry was removed.
499 *
500 * To delete an entry that you already have a pointer to, see
501 * dshash_delete_entry.
502 */
503bool
504dshash_delete_key(dshash_table *hash_table, const void *key)
505{
507 size_t partition;
508 bool found;
509
510 Assert(hash_table->control->magic == DSHASH_MAGIC);
512
513 hash = hash_key(hash_table, key);
515
518
519 if (delete_key_from_bucket(hash_table, key,
520 &BUCKET_FOR_HASH(hash_table, hash)))
521 {
522 Assert(hash_table->control->partitions[partition].count > 0);
523 found = true;
524 --hash_table->control->partitions[partition].count;
525 }
526 else
527 found = false;
528
530
531 return found;
532}
533
534/*
535 * Remove an entry. The entry must already be exclusively locked, and must
536 * have been obtained by dshash_find or dshash_find_or_insert. Note that this
537 * function releases the lock just like dshash_release_lock.
538 *
539 * To delete an entry by key, see dshash_delete_key.
540 */
541void
542dshash_delete_entry(dshash_table *hash_table, void *entry)
543{
544 dshash_table_item *item = ITEM_FROM_ENTRY(entry);
545 size_t partition = PARTITION_FOR_HASH(item->hash);
546
547 Assert(hash_table->control->magic == DSHASH_MAGIC);
549 LW_EXCLUSIVE));
550
551 delete_item(hash_table, item);
553}
554
555/*
556 * Unlock an entry which was locked by dshash_find or dshash_find_or_insert.
557 */
558void
559dshash_release_lock(dshash_table *hash_table, void *entry)
560{
561 dshash_table_item *item = ITEM_FROM_ENTRY(entry);
563
564 Assert(hash_table->control->magic == DSHASH_MAGIC);
565
567}
568
569/*
570 * A compare function that forwards to memcmp.
571 */
572int
573dshash_memcmp(const void *a, const void *b, size_t size, void *arg)
574{
575 return memcmp(a, b, size);
576}
577
578/*
579 * A hash function that forwards to tag_hash.
580 */
582dshash_memhash(const void *v, size_t size, void *arg)
583{
584 return tag_hash(v, size);
585}
586
587/*
588 * A copy function that forwards to memcpy.
589 */
590void
591dshash_memcpy(void *dest, const void *src, size_t size, void *arg)
592{
593 (void) memcpy(dest, src, size);
594}
595
596/*
597 * A compare function that forwards to strcmp.
598 */
599int
600dshash_strcmp(const void *a, const void *b, size_t size, void *arg)
601{
602 Assert(strlen((const char *) a) < size);
603 Assert(strlen((const char *) b) < size);
604
605 return strcmp((const char *) a, (const char *) b);
606}
607
608/*
609 * A hash function that forwards to string_hash.
610 */
612dshash_strhash(const void *v, size_t size, void *arg)
613{
614 Assert(strlen((const char *) v) < size);
615
616 return string_hash((const char *) v, size);
617}
618
619/*
620 * A copy function that forwards to strcpy.
621 */
622void
623dshash_strcpy(void *dest, const void *src, size_t size, void *arg)
624{
625 Assert(strlen((const char *) src) < size);
626
627 (void) strcpy((char *) dest, (const char *) src);
628}
629
630/*
631 * Sequentially scan through dshash table and return all the elements one by
632 * one, return NULL when all elements have been returned.
633 *
634 * dshash_seq_term needs to be called when a scan finished. The caller may
635 * delete returned elements midst of a scan by using dshash_delete_current()
636 * if exclusive = true.
637 */
638void
640 bool exclusive)
641{
642 status->hash_table = hash_table;
643 status->curbucket = 0;
644 status->nbuckets = 0;
645 status->curitem = NULL;
647 status->curpartition = -1;
648 status->exclusive = exclusive;
649}
650
651/*
652 * Returns the next element.
653 *
654 * Returned elements are locked and the caller may not release the lock. It is
655 * released by future calls to dshash_seq_next() or dshash_seq_term().
656 */
657void *
659{
661
662 /*
663 * Not yet holding any partition locks. Need to determine the size of the
664 * hash table, it could have been resized since we were looking last.
665 * Since we iterate in partition order, we can start by unconditionally
666 * lock partition 0.
667 *
668 * Once we hold the lock, no resizing can happen until the scan ends. So
669 * we don't need to repeatedly call ensure_valid_bucket_pointers().
670 */
671 if (status->curpartition == -1)
672 {
673 Assert(status->curbucket == 0);
675
676 status->curpartition = 0;
677
679 status->curpartition),
680 status->exclusive ? LW_EXCLUSIVE : LW_SHARED);
681
683
684 status->nbuckets =
686 next_item_pointer = status->hash_table->buckets[status->curbucket];
687 }
688 else
690
692 status->curpartition),
693 status->exclusive ? LW_EXCLUSIVE : LW_SHARED));
694
695 /* Move to the next bucket if we finished the current bucket */
697 {
698 int next_partition;
699
700 if (++status->curbucket >= status->nbuckets)
701 {
702 /* all buckets have been scanned. finish. */
703 return NULL;
704 }
705
706 /* Check if move to the next partition */
707 next_partition =
709 status->hash_table->size_log2);
710
711 if (status->curpartition != next_partition)
712 {
713 /*
714 * Move to the next partition. Lock the next partition then
715 * release the current, not in the reverse order to avoid
716 * concurrent resizing. Avoid dead lock by taking lock in the
717 * same order with resize().
718 */
720 next_partition),
721 status->exclusive ? LW_EXCLUSIVE : LW_SHARED);
723 status->curpartition));
724 status->curpartition = next_partition;
725 }
726
727 next_item_pointer = status->hash_table->buckets[status->curbucket];
728 }
729
730 status->curitem =
732
733 /*
734 * The caller may delete the item. Store the next item in case of
735 * deletion.
736 */
737 status->pnextitem = status->curitem->next;
738
739 return ENTRY_FROM_ITEM(status->curitem);
740}
741
742/*
743 * Terminates the seqscan and release all locks.
744 *
745 * Needs to be called after finishing or when exiting a seqscan.
746 */
747void
749{
750 if (status->curpartition >= 0)
752}
753
754/*
755 * Remove the current entry of the seq scan.
756 */
757void
759{
760 dshash_table *hash_table = status->hash_table;
761 dshash_table_item *item = status->curitem;
763
765
766 Assert(status->exclusive);
767 Assert(hash_table->control->magic == DSHASH_MAGIC);
769 LW_EXCLUSIVE));
770
771 delete_item(hash_table, item);
772}
773
774/*
775 * Print debugging information about the internal state of the hash table to
776 * stderr. The caller must hold no partition locks.
777 */
778void
779dshash_dump(dshash_table *hash_table)
780{
781 size_t i;
782 size_t j;
783
784 Assert(hash_table->control->magic == DSHASH_MAGIC);
786
787 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
788 {
789 Assert(!LWLockHeldByMe(PARTITION_LOCK(hash_table, i)));
791 }
792
794
796 "hash table size = %zu\n", (size_t) 1 << hash_table->size_log2);
797 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
798 {
800 size_t begin = BUCKET_INDEX_FOR_PARTITION(i, hash_table->size_log2);
801 size_t end = BUCKET_INDEX_FOR_PARTITION(i + 1, hash_table->size_log2);
802
803 fprintf(stderr, " partition %zu\n", i);
805 " active buckets (key count = %zu)\n", partition->count);
806
807 for (j = begin; j < end; ++j)
808 {
809 size_t count = 0;
810 dsa_pointer bucket = hash_table->buckets[j];
811
813 {
814 dshash_table_item *item;
815
816 item = dsa_get_address(hash_table->area, bucket);
817
818 bucket = item->next;
819 ++count;
820 }
821 fprintf(stderr, " bucket %zu (key count = %zu)\n", j, count);
822 }
823 }
824
825 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
826 LWLockRelease(PARTITION_LOCK(hash_table, i));
827}
828
829/*
830 * Delete a locked item to which we have a pointer.
831 */
832static void
834{
835 size_t hash = item->hash;
837
839
840 if (delete_item_from_bucket(hash_table, item,
841 &BUCKET_FOR_HASH(hash_table, hash)))
842 {
843 Assert(hash_table->control->partitions[partition].count > 0);
844 --hash_table->control->partitions[partition].count;
845 }
846 else
847 {
848 Assert(false);
849 }
850}
851
852/*
853 * Grow the hash table if necessary to the requested number of buckets. The
854 * requested size must be double some previously observed size.
855 *
856 * Must be called without any partition lock held.
857 */
858static void
859resize(dshash_table *hash_table, size_t new_size_log2)
860{
864 size_t size;
865 size_t new_size = ((size_t) 1) << new_size_log2;
866 size_t i;
867
868 /*
869 * Acquire the locks for all lock partitions. This is expensive, but we
870 * shouldn't have to do it many times.
871 */
872 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
873 {
874 Assert(!LWLockHeldByMe(PARTITION_LOCK(hash_table, i)));
875
877 if (i == 0 && hash_table->control->size_log2 >= new_size_log2)
878 {
879 /*
880 * Another backend has already increased the size; we can avoid
881 * obtaining all the locks and return early.
882 */
883 LWLockRelease(PARTITION_LOCK(hash_table, 0));
884 return;
885 }
886 }
887
888 Assert(new_size_log2 == hash_table->control->size_log2 + 1);
889
890 /* Allocate the space for the new table. */
892 dsa_allocate_extended(hash_table->area,
893 sizeof(dsa_pointer) * new_size,
896
897 /*
898 * We've allocated the new bucket array; all that remains to do now is to
899 * reinsert all items, which amounts to adjusting all the pointers.
900 */
901 size = ((size_t) 1) << hash_table->control->size_log2;
902 for (i = 0; i < size; ++i)
903 {
904 dsa_pointer item_pointer = hash_table->buckets[i];
905
907 {
908 dshash_table_item *item;
910
911 item = dsa_get_address(hash_table->area, item_pointer);
912 next_item_pointer = item->next;
913 insert_item_into_bucket(hash_table, item_pointer, item,
915 new_size_log2)]);
917 }
918 }
919
920 /* Swap the hash table into place and free the old one. */
921 old_buckets = hash_table->control->buckets;
922 hash_table->control->buckets = new_buckets_shared;
923 hash_table->control->size_log2 = new_size_log2;
924 hash_table->buckets = new_buckets;
925 dsa_free(hash_table->area, old_buckets);
926
927 /* Release all the locks. */
928 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
929 LWLockRelease(PARTITION_LOCK(hash_table, i));
930}
931
932/*
933 * Make sure that our backend-local bucket pointers are up to date. The
934 * caller must have locked one lock partition, which prevents resize() from
935 * running concurrently.
936 */
937static inline void
939{
940 if (hash_table->size_log2 != hash_table->control->size_log2)
941 {
942 hash_table->buckets = dsa_get_address(hash_table->area,
943 hash_table->control->buckets);
944 hash_table->size_log2 = hash_table->control->size_log2;
945 }
946}
947
948/*
949 * Scan a locked bucket for a match, using the provided compare function.
950 */
951static inline dshash_table_item *
952find_in_bucket(dshash_table *hash_table, const void *key,
954{
956 {
957 dshash_table_item *item;
958
959 item = dsa_get_address(hash_table->area, item_pointer);
960 if (equal_keys(hash_table, key, ENTRY_FROM_ITEM(item)))
961 return item;
962 item_pointer = item->next;
963 }
964 return NULL;
965}
966
967/*
968 * Insert an already-allocated item into a bucket.
969 */
970static void
973 dshash_table_item *item,
975{
976 Assert(item == dsa_get_address(hash_table->area, item_pointer));
977
978 item->next = *bucket;
980}
981
982/*
983 * Allocate space for an entry with the given key and insert it into the
984 * provided bucket.
985 */
986static dshash_table_item *
988 const void *key,
990{
992 dshash_table_item *item;
993
994 item_pointer = dsa_allocate(hash_table->area,
995 hash_table->params.entry_size +
996 MAXALIGN(sizeof(dshash_table_item)));
997 item = dsa_get_address(hash_table->area, item_pointer);
998 copy_key(hash_table, ENTRY_FROM_ITEM(item), key);
999 insert_item_into_bucket(hash_table, item_pointer, item, bucket);
1000 return item;
1001}
1002
1003/*
1004 * Search a bucket for a matching key and delete it.
1005 */
1006static bool
1008 const void *key,
1010{
1012 {
1013 dshash_table_item *item;
1014
1015 item = dsa_get_address(hash_table->area, *bucket_head);
1016
1017 if (equal_keys(hash_table, key, ENTRY_FROM_ITEM(item)))
1018 {
1020
1021 next = item->next;
1022 dsa_free(hash_table->area, *bucket_head);
1023 *bucket_head = next;
1024
1025 return true;
1026 }
1027 bucket_head = &item->next;
1028 }
1029 return false;
1030}
1031
1032/*
1033 * Delete the specified item from the bucket.
1034 */
1035static bool
1037 dshash_table_item *item,
1039{
1041 {
1043
1045
1046 if (bucket_item == item)
1047 {
1049
1050 next = item->next;
1051 dsa_free(hash_table->area, *bucket_head);
1052 *bucket_head = next;
1053 return true;
1054 }
1055 bucket_head = &bucket_item->next;
1056 }
1057 return false;
1058}
1059
1060/*
1061 * Compute the hash value for a key.
1062 */
1063static inline dshash_hash
1064hash_key(dshash_table *hash_table, const void *key)
1065{
1066 return hash_table->params.hash_function(key,
1067 hash_table->params.key_size,
1068 hash_table->arg);
1069}
1070
1071/*
1072 * Check whether two keys compare equal.
1073 */
1074static inline bool
1075equal_keys(dshash_table *hash_table, const void *a, const void *b)
1076{
1077 return hash_table->params.compare_function(a, b,
1078 hash_table->params.key_size,
1079 hash_table->arg) == 0;
1080}
1081
1082/*
1083 * Copy a key.
1084 */
1085static inline void
1086copy_key(dshash_table *hash_table, void *dest, const void *src)
1087{
1088 hash_table->params.copy_function(dest, src,
1089 hash_table->params.key_size,
1090 hash_table->arg);
1091}
static int32 next
Definition blutils.c:225
#define MAXALIGN(LEN)
Definition c.h:826
#define PG_USED_FOR_ASSERTS_ONLY
Definition c.h:223
#define fprintf(file, fmt, msg)
Definition cubescan.l:21
void * dsa_get_address(dsa_area *area, dsa_pointer dp)
Definition dsa.c:957
dsa_pointer dsa_allocate_extended(dsa_area *area, size_t size, int flags)
Definition dsa.c:686
void dsa_free(dsa_area *area, dsa_pointer dp)
Definition dsa.c:841
uint64 dsa_pointer
Definition dsa.h:62
#define dsa_allocate(area, size)
Definition dsa.h:109
#define InvalidDsaPointer
Definition dsa.h:78
#define DSA_ALLOC_NO_OOM
Definition dsa.h:74
#define DsaPointerIsValid(x)
Definition dsa.h:106
#define DSA_ALLOC_HUGE
Definition dsa.h:73
#define DSA_ALLOC_ZERO
Definition dsa.h:75
bool dshash_delete_key(dshash_table *hash_table, const void *key)
Definition dshash.c:505
void dshash_memcpy(void *dest, const void *src, size_t size, void *arg)
Definition dshash.c:592
void dshash_delete_entry(dshash_table *hash_table, void *entry)
Definition dshash.c:543
void dshash_strcpy(void *dest, const void *src, size_t size, void *arg)
Definition dshash.c:624
void dshash_destroy(dshash_table *hash_table)
Definition dshash.c:325
void dshash_release_lock(dshash_table *hash_table, void *entry)
Definition dshash.c:560
#define PARTITION_LOCK(hash_table, i)
Definition dshash.c:194
void dshash_detach(dshash_table *hash_table)
Definition dshash.c:309
void dshash_seq_init(dshash_seq_status *status, dshash_table *hash_table, bool exclusive)
Definition dshash.c:640
void * dshash_find(dshash_table *hash_table, const void *key, bool exclusive)
Definition dshash.c:392
#define BUCKET_INDEX_FOR_HASH_AND_SIZE(hash, size_log2)
Definition dshash.c:151
static bool delete_key_from_bucket(dshash_table *hash_table, const void *key, dsa_pointer *bucket_head)
Definition dshash.c:1008
dshash_hash dshash_strhash(const void *v, size_t size, void *arg)
Definition dshash.c:613
static dshash_hash hash_key(dshash_table *hash_table, const void *key)
Definition dshash.c:1065
#define ASSERT_NO_PARTITION_LOCKS_HELD_BY_ME(hash_table)
Definition dshash.c:197
#define ITEM_FROM_ENTRY(entry)
Definition dshash.c:120
dshash_table_handle dshash_get_hash_table_handle(dshash_table *hash_table)
Definition dshash.c:369
void dshash_dump(dshash_table *hash_table)
Definition dshash.c:780
static bool equal_keys(dshash_table *hash_table, const void *a, const void *b)
Definition dshash.c:1076
dshash_table * dshash_attach(dsa_area *area, const dshash_parameters *params, dshash_table_handle handle, void *arg)
Definition dshash.c:272
void dshash_seq_term(dshash_seq_status *status)
Definition dshash.c:749
#define DSHASH_MAGIC
Definition dshash.c:65
static void insert_item_into_bucket(dshash_table *hash_table, dsa_pointer item_pointer, dshash_table_item *item, dsa_pointer *bucket)
Definition dshash.c:972
static bool delete_item_from_bucket(dshash_table *hash_table, dshash_table_item *item, dsa_pointer *bucket_head)
Definition dshash.c:1037
int dshash_strcmp(const void *a, const void *b, size_t size, void *arg)
Definition dshash.c:601
#define PARTITION_FOR_BUCKET_INDEX(bucket_idx, size_log2)
Definition dshash.c:159
static dshash_table_item * insert_into_bucket(dshash_table *hash_table, const void *key, dsa_pointer *bucket)
Definition dshash.c:988
void * dshash_find_or_insert(dshash_table *hash_table, const void *key, bool *found)
Definition dshash.c:435
#define BUCKET_INDEX_FOR_PARTITION(partition, size_log2)
Definition dshash.c:155
#define MAX_COUNT_PER_PARTITION(hash_table)
Definition dshash.c:137
#define ENTRY_FROM_ITEM(item)
Definition dshash.c:116
#define DSHASH_NUM_PARTITIONS_LOG2
Definition dshash.c:61
static void delete_item(dshash_table *hash_table, dshash_table_item *item)
Definition dshash.c:834
dshash_hash dshash_memhash(const void *v, size_t size, void *arg)
Definition dshash.c:583
#define NUM_BUCKETS(size_log2)
Definition dshash.c:129
static void ensure_valid_bucket_pointers(dshash_table *hash_table)
Definition dshash.c:939
static dshash_table_item * find_in_bucket(dshash_table *hash_table, const void *key, dsa_pointer item_pointer)
Definition dshash.c:953
void * dshash_seq_next(dshash_seq_status *status)
Definition dshash.c:659
static void resize(dshash_table *hash_table, size_t new_size_log2)
Definition dshash.c:860
static void copy_key(dshash_table *hash_table, void *dest, const void *src)
Definition dshash.c:1087
int dshash_memcmp(const void *a, const void *b, size_t size, void *arg)
Definition dshash.c:574
#define PARTITION_FOR_HASH(hash)
Definition dshash.c:142
#define BUCKET_FOR_HASH(hash_table, hash)
Definition dshash.c:163
void dshash_delete_current(dshash_seq_status *status)
Definition dshash.c:759
dsa_pointer dshash_table_handle
Definition dshash.h:24
uint32 dshash_hash
Definition dshash.h:30
int errdetail(const char *fmt,...)
Definition elog.c:1216
int errcode(int sqlerrcode)
Definition elog.c:863
int errmsg(const char *fmt,...)
Definition elog.c:1080
#define ERROR
Definition elog.h:39
#define ereport(elevel,...)
Definition elog.h:150
#define palloc_object(type)
Definition fe_memutils.h:74
uint32 tag_hash(const void *key, Size keysize)
Definition hashfn.c:677
uint32 string_hash(const void *key, Size keysize)
Definition hashfn.c:660
int b
Definition isn.c:74
int a
Definition isn.c:73
int j
Definition isn.c:78
int i
Definition isn.c:77
bool LWLockHeldByMe(LWLock *lock)
Definition lwlock.c:1911
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition lwlock.c:1176
bool LWLockHeldByMeInMode(LWLock *lock, LWLockMode mode)
Definition lwlock.c:1955
void LWLockRelease(LWLock *lock)
Definition lwlock.c:1793
void LWLockInitialize(LWLock *lock, int tranche_id)
Definition lwlock.c:698
@ LW_SHARED
Definition lwlock.h:113
@ LW_EXCLUSIVE
Definition lwlock.h:112
void pfree(void *pointer)
Definition mcxt.c:1616
void * arg
static int partitions
Definition pgbench.c:224
static int fb(int x)
static unsigned hash(unsigned *uv, int n)
Definition rege_dfa.c:715
dshash_hash_function hash_function
Definition dshash.h:59
size_t key_size
Definition dshash.h:56
dshash_compare_function compare_function
Definition dshash.h:58
dshash_copy_function copy_function
Definition dshash.h:60
size_t entry_size
Definition dshash.h:57
size_t count
Definition dshash.c:78
dshash_table_item * curitem
Definition dshash.h:77
dsa_pointer pnextitem
Definition dshash.h:78
dshash_table * hash_table
Definition dshash.h:74
dshash_partition partitions[DSHASH_NUM_PARTITIONS]
Definition dshash.c:89
dshash_table_handle handle
Definition dshash.c:87
dsa_pointer buckets
Definition dshash.c:99
dshash_hash hash
Definition dshash.c:51
dsa_pointer next
Definition dshash.c:49
dshash_parameters params
Definition dshash.c:108
dsa_pointer * buckets
Definition dshash.c:111
dsa_area * area
Definition dshash.c:107
void * arg
Definition dshash.c:109
size_t size_log2
Definition dshash.c:112
dshash_table_control * control
Definition dshash.c:110

◆ BUCKET_FOR_HASH

#define BUCKET_FOR_HASH (   hash_table,
  hash 
)
Value:
(hash_table->buckets[ \
hash_table->size_log2)])

Definition at line 163 of file dshash.c.

◆ BUCKET_INDEX_FOR_HASH_AND_SIZE

#define BUCKET_INDEX_FOR_HASH_AND_SIZE (   hash,
  size_log2 
)     (hash >> ((sizeof(dshash_hash) * CHAR_BIT) - (size_log2)))

Definition at line 151 of file dshash.c.

◆ BUCKET_INDEX_FOR_PARTITION

#define BUCKET_INDEX_FOR_PARTITION (   partition,
  size_log2 
)     ((partition) << NUM_SPLITS(size_log2))

Definition at line 155 of file dshash.c.

◆ BUCKETS_PER_PARTITION

#define BUCKETS_PER_PARTITION (   size_log2)     (((size_t) 1) << NUM_SPLITS(size_log2))

Definition at line 133 of file dshash.c.

◆ DSHASH_MAGIC

#define DSHASH_MAGIC   0x75ff6a20

Definition at line 65 of file dshash.c.

◆ DSHASH_NUM_PARTITIONS

#define DSHASH_NUM_PARTITIONS   (1 << DSHASH_NUM_PARTITIONS_LOG2)

Definition at line 62 of file dshash.c.

◆ DSHASH_NUM_PARTITIONS_LOG2

#define DSHASH_NUM_PARTITIONS_LOG2   7

Definition at line 61 of file dshash.c.

◆ ENTRY_FROM_ITEM

#define ENTRY_FROM_ITEM (   item)     ((char *)(item) + MAXALIGN(sizeof(dshash_table_item)))

Definition at line 116 of file dshash.c.

◆ ITEM_FROM_ENTRY

#define ITEM_FROM_ENTRY (   entry)
Value:
((dshash_table_item *)((char *)(entry) - \

Definition at line 120 of file dshash.c.

◆ MAX_COUNT_PER_PARTITION

#define MAX_COUNT_PER_PARTITION (   hash_table)
Value:
(BUCKETS_PER_PARTITION(hash_table->size_log2) / 2 + \
BUCKETS_PER_PARTITION(hash_table->size_log2) / 4)
#define BUCKETS_PER_PARTITION(size_log2)
Definition dshash.c:133

Definition at line 137 of file dshash.c.

◆ NUM_BUCKETS

#define NUM_BUCKETS (   size_log2)     (((size_t) 1) << (size_log2))

Definition at line 129 of file dshash.c.

◆ NUM_SPLITS

#define NUM_SPLITS (   size_log2)     (size_log2 - DSHASH_NUM_PARTITIONS_LOG2)

Definition at line 125 of file dshash.c.

◆ PARTITION_FOR_BUCKET_INDEX

#define PARTITION_FOR_BUCKET_INDEX (   bucket_idx,
  size_log2 
)     ((bucket_idx) >> NUM_SPLITS(size_log2))

Definition at line 159 of file dshash.c.

◆ PARTITION_FOR_HASH

#define PARTITION_FOR_HASH (   hash)     (hash >> ((sizeof(dshash_hash) * CHAR_BIT) - DSHASH_NUM_PARTITIONS_LOG2))

Definition at line 142 of file dshash.c.

◆ PARTITION_LOCK

#define PARTITION_LOCK (   hash_table,
  i 
)     (&(hash_table)->control->partitions[(i)].lock)

Definition at line 194 of file dshash.c.

Typedef Documentation

◆ dshash_partition

◆ dshash_table_control

Function Documentation

◆ copy_key()

static void copy_key ( dshash_table hash_table,
void dest,
const void src 
)
inlinestatic

Definition at line 1087 of file dshash.c.

1088{
1089 hash_table->params.copy_function(dest, src,
1090 hash_table->params.key_size,
1091 hash_table->arg);
1092}

References dshash_table::arg, dshash_parameters::copy_function, dshash_parameters::key_size, and dshash_table::params.

Referenced by insert_into_bucket().

◆ delete_item()

static void delete_item ( dshash_table hash_table,
dshash_table_item item 
)
static

Definition at line 834 of file dshash.c.

835{
836 size_t hash = item->hash;
838
840
841 if (delete_item_from_bucket(hash_table, item,
842 &BUCKET_FOR_HASH(hash_table, hash)))
843 {
844 Assert(hash_table->control->partitions[partition].count > 0);
845 --hash_table->control->partitions[partition].count;
846 }
847 else
848 {
849 Assert(false);
850 }
851}

References Assert, BUCKET_FOR_HASH, dshash_table::control, dshash_partition::count, delete_item_from_bucket(), fb(), dshash_table_item::hash, hash(), LWLockHeldByMe(), PARTITION_FOR_HASH, PARTITION_LOCK, and dshash_table_control::partitions.

Referenced by dshash_delete_current(), and dshash_delete_entry().

◆ delete_item_from_bucket()

static bool delete_item_from_bucket ( dshash_table hash_table,
dshash_table_item item,
dsa_pointer bucket_head 
)
static

Definition at line 1037 of file dshash.c.

1040{
1042 {
1044
1046
1047 if (bucket_item == item)
1048 {
1050
1051 next = item->next;
1052 dsa_free(hash_table->area, *bucket_head);
1053 *bucket_head = next;
1054 return true;
1055 }
1056 bucket_head = &bucket_item->next;
1057 }
1058 return false;
1059}

References dshash_table::area, dsa_free(), dsa_get_address(), DsaPointerIsValid, fb(), next, and dshash_table_item::next.

Referenced by delete_item().

◆ delete_key_from_bucket()

static bool delete_key_from_bucket ( dshash_table hash_table,
const void key,
dsa_pointer bucket_head 
)
static

Definition at line 1008 of file dshash.c.

1011{
1013 {
1014 dshash_table_item *item;
1015
1016 item = dsa_get_address(hash_table->area, *bucket_head);
1017
1018 if (equal_keys(hash_table, key, ENTRY_FROM_ITEM(item)))
1019 {
1021
1022 next = item->next;
1023 dsa_free(hash_table->area, *bucket_head);
1024 *bucket_head = next;
1025
1026 return true;
1027 }
1028 bucket_head = &item->next;
1029 }
1030 return false;
1031}

References dshash_table::area, dsa_free(), dsa_get_address(), DsaPointerIsValid, ENTRY_FROM_ITEM, equal_keys(), fb(), next, and dshash_table_item::next.

Referenced by dshash_delete_key().

◆ dshash_attach()

dshash_table * dshash_attach ( dsa_area area,
const dshash_parameters params,
dshash_table_handle  handle,
void arg 
)

Definition at line 272 of file dshash.c.

274{
275 dshash_table *hash_table;
276 dsa_pointer control;
277
278 /* Allocate the backend-local object representing the hash table. */
279 hash_table = palloc_object(dshash_table);
280
281 /* Find the control object in shared memory. */
282 control = handle;
283
284 /* Set up the local hash table struct. */
285 hash_table->area = area;
286 hash_table->params = *params;
287 hash_table->arg = arg;
288 hash_table->control = dsa_get_address(area, control);
289 Assert(hash_table->control->magic == DSHASH_MAGIC);
290
291 /*
292 * These will later be set to the correct values by
293 * ensure_valid_bucket_pointers(), at which time we'll be holding a
294 * partition lock for interlocking against concurrent resizing.
295 */
296 hash_table->buckets = NULL;
297 hash_table->size_log2 = 0;
298
299 return hash_table;
300}

References dshash_table::area, dshash_table::arg, arg, Assert, dshash_table::buckets, dshash_table::control, dsa_get_address(), DSHASH_MAGIC, fb(), dshash_table_control::magic, palloc_object, dshash_table::params, and dshash_table::size_log2.

Referenced by GetNamedDSHash(), init_dsm_registry(), initGlobalChannelTable(), logicalrep_launcher_attach_dshmem(), pgstat_attach_shmem(), and SharedRecordTypmodRegistryAttach().

◆ dshash_create()

dshash_table * dshash_create ( dsa_area area,
const dshash_parameters params,
void arg 
)

Definition at line 208 of file dshash.c.

209{
210 dshash_table *hash_table;
211 dsa_pointer control;
212
213 /* Allocate the backend-local object representing the hash table. */
214 hash_table = palloc_object(dshash_table);
215
216 /* Allocate the control object in shared memory. */
217 control = dsa_allocate(area, sizeof(dshash_table_control));
218
219 /* Set up the local and shared hash table structs. */
220 hash_table->area = area;
221 hash_table->params = *params;
222 hash_table->arg = arg;
223 hash_table->control = dsa_get_address(area, control);
224 hash_table->control->handle = control;
225 hash_table->control->magic = DSHASH_MAGIC;
226 hash_table->control->lwlock_tranche_id = params->tranche_id;
227
228 /* Set up the array of lock partitions. */
229 {
231 int tranche_id = hash_table->control->lwlock_tranche_id;
232 int i;
233
234 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
235 {
236 LWLockInitialize(&partitions[i].lock, tranche_id);
237 partitions[i].count = 0;
238 }
239 }
240
241 /*
242 * Set up the initial array of buckets. Our initial size is the same as
243 * the number of partitions.
244 */
246 hash_table->control->buckets =
250 if (!DsaPointerIsValid(hash_table->control->buckets))
251 {
252 dsa_free(area, control);
255 errmsg("out of memory"),
256 errdetail("Failed on DSA request of size %zu.",
258 }
259 hash_table->buckets = dsa_get_address(area,
260 hash_table->control->buckets);
261 hash_table->size_log2 = hash_table->control->size_log2;
262
263 return hash_table;
264}

References dshash_table::area, dshash_table::arg, arg, dshash_table_control::buckets, dshash_table::buckets, dshash_table::control, DSA_ALLOC_NO_OOM, DSA_ALLOC_ZERO, dsa_allocate, dsa_allocate_extended(), dsa_free(), dsa_get_address(), DsaPointerIsValid, DSHASH_MAGIC, DSHASH_NUM_PARTITIONS, DSHASH_NUM_PARTITIONS_LOG2, ereport, errcode(), errdetail(), errmsg(), ERROR, fb(), dshash_table_control::handle, i, dshash_table_control::lwlock_tranche_id, LWLockInitialize(), dshash_table_control::magic, palloc_object, dshash_table::params, dshash_table_control::partitions, partitions, dshash_table_control::size_log2, dshash_table::size_log2, and dshash_parameters::tranche_id.

Referenced by GetNamedDSHash(), init_dsm_registry(), initGlobalChannelTable(), logicalrep_launcher_attach_dshmem(), SharedRecordTypmodRegistryInit(), and StatsShmemInit().

◆ dshash_delete_current()

◆ dshash_delete_entry()

◆ dshash_delete_key()

bool dshash_delete_key ( dshash_table hash_table,
const void key 
)

◆ dshash_destroy()

void dshash_destroy ( dshash_table hash_table)

Definition at line 325 of file dshash.c.

326{
327 size_t size;
328 size_t i;
329
330 Assert(hash_table->control->magic == DSHASH_MAGIC);
332
333 /* Free all the entries. */
334 size = NUM_BUCKETS(hash_table->size_log2);
335 for (i = 0; i < size; ++i)
336 {
337 dsa_pointer item_pointer = hash_table->buckets[i];
338
340 {
341 dshash_table_item *item;
343
344 item = dsa_get_address(hash_table->area, item_pointer);
345 next_item_pointer = item->next;
346 dsa_free(hash_table->area, item_pointer);
348 }
349 }
350
351 /*
352 * Vandalize the control block to help catch programming errors where
353 * other backends access the memory formerly occupied by this hash table.
354 */
355 hash_table->control->magic = 0;
356
357 /* Free the active table and control object. */
358 dsa_free(hash_table->area, hash_table->control->buckets);
359 dsa_free(hash_table->area, hash_table->control->handle);
360
361 pfree(hash_table);
362}

References dshash_table::area, Assert, dshash_table_control::buckets, dshash_table::buckets, dshash_table::control, dsa_free(), dsa_get_address(), DsaPointerIsValid, DSHASH_MAGIC, ensure_valid_bucket_pointers(), fb(), dshash_table_control::handle, i, dshash_table_control::magic, dshash_table_item::next, NUM_BUCKETS, pfree(), and dshash_table::size_log2.

◆ dshash_detach()

void dshash_detach ( dshash_table hash_table)

Definition at line 309 of file dshash.c.

310{
312
313 /* The hash table may have been destroyed. Just free local memory. */
314 pfree(hash_table);
315}

References ASSERT_NO_PARTITION_LOCKS_HELD_BY_ME, and pfree().

Referenced by pgstat_detach_shmem(), shared_record_typmod_registry_detach(), and StatsShmemInit().

◆ dshash_dump()

void dshash_dump ( dshash_table hash_table)

Definition at line 780 of file dshash.c.

781{
782 size_t i;
783 size_t j;
784
785 Assert(hash_table->control->magic == DSHASH_MAGIC);
787
788 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
789 {
790 Assert(!LWLockHeldByMe(PARTITION_LOCK(hash_table, i)));
792 }
793
795
797 "hash table size = %zu\n", (size_t) 1 << hash_table->size_log2);
798 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
799 {
801 size_t begin = BUCKET_INDEX_FOR_PARTITION(i, hash_table->size_log2);
802 size_t end = BUCKET_INDEX_FOR_PARTITION(i + 1, hash_table->size_log2);
803
804 fprintf(stderr, " partition %zu\n", i);
806 " active buckets (key count = %zu)\n", partition->count);
807
808 for (j = begin; j < end; ++j)
809 {
810 size_t count = 0;
811 dsa_pointer bucket = hash_table->buckets[j];
812
814 {
815 dshash_table_item *item;
816
817 item = dsa_get_address(hash_table->area, bucket);
818
819 bucket = item->next;
820 ++count;
821 }
822 fprintf(stderr, " bucket %zu (key count = %zu)\n", j, count);
823 }
824 }
825
826 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
827 LWLockRelease(PARTITION_LOCK(hash_table, i));
828}

References dshash_table::area, Assert, ASSERT_NO_PARTITION_LOCKS_HELD_BY_ME, BUCKET_INDEX_FOR_PARTITION, dshash_table::buckets, dshash_table::control, dsa_get_address(), DsaPointerIsValid, DSHASH_MAGIC, DSHASH_NUM_PARTITIONS, ensure_valid_bucket_pointers(), fb(), fprintf, i, j, LW_SHARED, LWLockAcquire(), LWLockHeldByMe(), LWLockRelease(), dshash_table_control::magic, dshash_table_item::next, PARTITION_LOCK, dshash_table_control::partitions, and dshash_table::size_log2.

◆ dshash_find()

void * dshash_find ( dshash_table hash_table,
const void key,
bool  exclusive 
)

Definition at line 392 of file dshash.c.

393{
395 size_t partition;
396 dshash_table_item *item;
397
398 hash = hash_key(hash_table, key);
400
401 Assert(hash_table->control->magic == DSHASH_MAGIC);
403
405 exclusive ? LW_EXCLUSIVE : LW_SHARED);
407
408 /* Search the active bucket. */
409 item = find_in_bucket(hash_table, key, BUCKET_FOR_HASH(hash_table, hash));
410
411 if (!item)
412 {
413 /* Not found. */
415 return NULL;
416 }
417 else
418 {
419 /* The caller will free the lock by calling dshash_release_lock. */
420 return ENTRY_FROM_ITEM(item);
421 }
422}

References Assert, ASSERT_NO_PARTITION_LOCKS_HELD_BY_ME, BUCKET_FOR_HASH, dshash_table::control, DSHASH_MAGIC, ensure_valid_bucket_pointers(), ENTRY_FROM_ITEM, fb(), find_in_bucket(), hash(), hash_key(), LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), dshash_table_control::magic, PARTITION_FOR_HASH, and PARTITION_LOCK.

Referenced by ApplyLauncherGetWorkerStartTime(), ApplyPendingListenActions(), find_or_make_matching_shared_tupledesc(), get_val_in_hash(), lookup_rowtype_tupdesc_internal(), pgstat_drop_entry(), pgstat_get_entry_ref(), pgstat_release_entry_ref(), and SignalBackends().

◆ dshash_find_or_insert()

void * dshash_find_or_insert ( dshash_table hash_table,
const void key,
bool found 
)

Definition at line 435 of file dshash.c.

438{
440 size_t partition_index;
442 dshash_table_item *item;
443
444 hash = hash_key(hash_table, key);
447
448 Assert(hash_table->control->magic == DSHASH_MAGIC);
450
451restart:
455
456 /* Search the active bucket. */
457 item = find_in_bucket(hash_table, key, BUCKET_FOR_HASH(hash_table, hash));
458
459 if (item)
460 *found = true;
461 else
462 {
463 *found = false;
464
465 /* Check if we are getting too full. */
466 if (partition->count > MAX_COUNT_PER_PARTITION(hash_table))
467 {
468 /*
469 * The load factor (= keys / buckets) for all buckets protected by
470 * this partition is > 0.75. Presumably the same applies
471 * generally across the whole hash table (though we don't attempt
472 * to track that directly to avoid contention on some kind of
473 * central counter; we just assume that this partition is
474 * representative). This is a good time to resize.
475 *
476 * Give up our existing lock first, because resizing needs to
477 * reacquire all the locks in the right order to avoid deadlocks.
478 */
480 resize(hash_table, hash_table->size_log2 + 1);
481
482 goto restart;
483 }
484
485 /* Finally we can try to insert the new item. */
486 item = insert_into_bucket(hash_table, key,
487 &BUCKET_FOR_HASH(hash_table, hash));
488 item->hash = hash;
489 /* Adjust per-lock-partition counter for load factor knowledge. */
490 ++partition->count;
491 }
492
493 /* The caller must release the lock with dshash_release_lock. */
494 return ENTRY_FROM_ITEM(item);
495}

References Assert, ASSERT_NO_PARTITION_LOCKS_HELD_BY_ME, BUCKET_FOR_HASH, dshash_table::control, DSHASH_MAGIC, ensure_valid_bucket_pointers(), ENTRY_FROM_ITEM, fb(), find_in_bucket(), dshash_table_item::hash, hash(), hash_key(), insert_into_bucket(), LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), dshash_table_control::magic, MAX_COUNT_PER_PARTITION, PARTITION_FOR_HASH, PARTITION_LOCK, dshash_table_control::partitions, resize(), and dshash_table::size_log2.

Referenced by ApplyLauncherSetWorkerStartTime(), find_or_make_matching_shared_tupledesc(), GetNamedDSA(), GetNamedDSHash(), GetNamedDSMSegment(), pgstat_get_entry_ref(), pgstat_read_statsfile(), PrepareTableEntriesForListen(), set_val_in_hash(), and SharedRecordTypmodRegistryInit().

◆ dshash_get_hash_table_handle()

◆ dshash_memcmp()

int dshash_memcmp ( const void a,
const void b,
size_t  size,
void arg 
)

Definition at line 574 of file dshash.c.

575{
576 return memcmp(a, b, size);
577}

References a, b, and fb().

◆ dshash_memcpy()

void dshash_memcpy ( void dest,
const void src,
size_t  size,
void arg 
)

Definition at line 592 of file dshash.c.

593{
594 (void) memcpy(dest, src, size);
595}

References fb().

◆ dshash_memhash()

dshash_hash dshash_memhash ( const void v,
size_t  size,
void arg 
)

Definition at line 583 of file dshash.c.

584{
585 return tag_hash(v, size);
586}

References tag_hash().

◆ dshash_release_lock()

◆ dshash_seq_init()

◆ dshash_seq_next()

void * dshash_seq_next ( dshash_seq_status status)

Definition at line 659 of file dshash.c.

660{
662
663 /*
664 * Not yet holding any partition locks. Need to determine the size of the
665 * hash table, it could have been resized since we were looking last.
666 * Since we iterate in partition order, we can start by unconditionally
667 * lock partition 0.
668 *
669 * Once we hold the lock, no resizing can happen until the scan ends. So
670 * we don't need to repeatedly call ensure_valid_bucket_pointers().
671 */
672 if (status->curpartition == -1)
673 {
674 Assert(status->curbucket == 0);
676
677 status->curpartition = 0;
678
680 status->curpartition),
681 status->exclusive ? LW_EXCLUSIVE : LW_SHARED);
682
684
685 status->nbuckets =
687 next_item_pointer = status->hash_table->buckets[status->curbucket];
688 }
689 else
691
693 status->curpartition),
694 status->exclusive ? LW_EXCLUSIVE : LW_SHARED));
695
696 /* Move to the next bucket if we finished the current bucket */
698 {
699 int next_partition;
700
701 if (++status->curbucket >= status->nbuckets)
702 {
703 /* all buckets have been scanned. finish. */
704 return NULL;
705 }
706
707 /* Check if move to the next partition */
708 next_partition =
710 status->hash_table->size_log2);
711
712 if (status->curpartition != next_partition)
713 {
714 /*
715 * Move to the next partition. Lock the next partition then
716 * release the current, not in the reverse order to avoid
717 * concurrent resizing. Avoid dead lock by taking lock in the
718 * same order with resize().
719 */
721 next_partition),
722 status->exclusive ? LW_EXCLUSIVE : LW_SHARED);
724 status->curpartition));
725 status->curpartition = next_partition;
726 }
727
728 next_item_pointer = status->hash_table->buckets[status->curbucket];
729 }
730
731 status->curitem =
733
734 /*
735 * The caller may delete the item. Store the next item in case of
736 * deletion.
737 */
738 status->pnextitem = status->curitem->next;
739
740 return ENTRY_FROM_ITEM(status->curitem);
741}

References dshash_table::area, Assert, ASSERT_NO_PARTITION_LOCKS_HELD_BY_ME, dshash_table::buckets, dshash_table::control, dshash_seq_status::curbucket, dshash_seq_status::curitem, dshash_seq_status::curpartition, dsa_get_address(), DsaPointerIsValid, ensure_valid_bucket_pointers(), ENTRY_FROM_ITEM, dshash_seq_status::exclusive, fb(), dshash_seq_status::hash_table, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockHeldByMeInMode(), LWLockRelease(), dshash_seq_status::nbuckets, dshash_table_item::next, NUM_BUCKETS, PARTITION_FOR_BUCKET_INDEX, PARTITION_LOCK, dshash_seq_status::pnextitem, dshash_table_control::size_log2, and dshash_table::size_log2.

Referenced by CleanupListenersOnExit(), pg_get_dsm_registry_allocations(), pgstat_build_snapshot(), pgstat_drop_database_and_contents(), pgstat_drop_matching_entries(), pgstat_reset_matching_entries(), and pgstat_write_statsfile().

◆ dshash_seq_term()

◆ dshash_strcmp()

int dshash_strcmp ( const void a,
const void b,
size_t  size,
void arg 
)

Definition at line 601 of file dshash.c.

602{
603 Assert(strlen((const char *) a) < size);
604 Assert(strlen((const char *) b) < size);
605
606 return strcmp((const char *) a, (const char *) b);
607}

References a, Assert, b, and fb().

◆ dshash_strcpy()

void dshash_strcpy ( void dest,
const void src,
size_t  size,
void arg 
)

Definition at line 624 of file dshash.c.

625{
626 Assert(strlen((const char *) src) < size);
627
628 (void) strcpy((char *) dest, (const char *) src);
629}

References Assert, and fb().

◆ dshash_strhash()

dshash_hash dshash_strhash ( const void v,
size_t  size,
void arg 
)

Definition at line 613 of file dshash.c.

614{
615 Assert(strlen((const char *) v) < size);
616
617 return string_hash((const char *) v, size);
618}

References Assert, fb(), and string_hash().

◆ ensure_valid_bucket_pointers()

static void ensure_valid_bucket_pointers ( dshash_table hash_table)
inlinestatic

Definition at line 939 of file dshash.c.

940{
941 if (hash_table->size_log2 != hash_table->control->size_log2)
942 {
943 hash_table->buckets = dsa_get_address(hash_table->area,
944 hash_table->control->buckets);
945 hash_table->size_log2 = hash_table->control->size_log2;
946 }
947}

References dshash_table::area, dshash_table_control::buckets, dshash_table::buckets, dshash_table::control, dsa_get_address(), dshash_table_control::size_log2, and dshash_table::size_log2.

Referenced by dshash_delete_key(), dshash_destroy(), dshash_dump(), dshash_find(), dshash_find_or_insert(), and dshash_seq_next().

◆ equal_keys()

static bool equal_keys ( dshash_table hash_table,
const void a,
const void b 
)
inlinestatic

Definition at line 1076 of file dshash.c.

1077{
1078 return hash_table->params.compare_function(a, b,
1079 hash_table->params.key_size,
1080 hash_table->arg) == 0;
1081}

References a, dshash_table::arg, b, dshash_parameters::compare_function, dshash_parameters::key_size, and dshash_table::params.

Referenced by delete_key_from_bucket(), and find_in_bucket().

◆ find_in_bucket()

static dshash_table_item * find_in_bucket ( dshash_table hash_table,
const void key,
dsa_pointer  item_pointer 
)
inlinestatic

Definition at line 953 of file dshash.c.

955{
957 {
958 dshash_table_item *item;
959
960 item = dsa_get_address(hash_table->area, item_pointer);
961 if (equal_keys(hash_table, key, ENTRY_FROM_ITEM(item)))
962 return item;
963 item_pointer = item->next;
964 }
965 return NULL;
966}

References dshash_table::area, dsa_get_address(), DsaPointerIsValid, ENTRY_FROM_ITEM, equal_keys(), fb(), and dshash_table_item::next.

Referenced by dshash_find(), and dshash_find_or_insert().

◆ hash_key()

static dshash_hash hash_key ( dshash_table hash_table,
const void key 
)
inlinestatic

Definition at line 1065 of file dshash.c.

1066{
1067 return hash_table->params.hash_function(key,
1068 hash_table->params.key_size,
1069 hash_table->arg);
1070}

References dshash_table::arg, dshash_parameters::hash_function, dshash_parameters::key_size, and dshash_table::params.

Referenced by compute_tsvector_stats(), dshash_delete_key(), dshash_find(), and dshash_find_or_insert().

◆ insert_into_bucket()

static dshash_table_item * insert_into_bucket ( dshash_table hash_table,
const void key,
dsa_pointer bucket 
)
static

Definition at line 988 of file dshash.c.

991{
993 dshash_table_item *item;
994
995 item_pointer = dsa_allocate(hash_table->area,
996 hash_table->params.entry_size +
997 MAXALIGN(sizeof(dshash_table_item)));
998 item = dsa_get_address(hash_table->area, item_pointer);
999 copy_key(hash_table, ENTRY_FROM_ITEM(item), key);
1000 insert_item_into_bucket(hash_table, item_pointer, item, bucket);
1001 return item;
1002}

References dshash_table::area, copy_key(), dsa_allocate, dsa_get_address(), ENTRY_FROM_ITEM, dshash_parameters::entry_size, fb(), insert_item_into_bucket(), MAXALIGN, and dshash_table::params.

Referenced by dshash_find_or_insert().

◆ insert_item_into_bucket()

static void insert_item_into_bucket ( dshash_table hash_table,
dsa_pointer  item_pointer,
dshash_table_item item,
dsa_pointer bucket 
)
static

Definition at line 972 of file dshash.c.

976{
977 Assert(item == dsa_get_address(hash_table->area, item_pointer));
978
979 item->next = *bucket;
981}

References dshash_table::area, Assert, dsa_get_address(), fb(), and dshash_table_item::next.

Referenced by insert_into_bucket(), and resize().

◆ resize()

static void resize ( dshash_table hash_table,
size_t  new_size_log2 
)
static

Definition at line 860 of file dshash.c.

861{
865 size_t size;
866 size_t new_size = ((size_t) 1) << new_size_log2;
867 size_t i;
868
869 /*
870 * Acquire the locks for all lock partitions. This is expensive, but we
871 * shouldn't have to do it many times.
872 */
873 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
874 {
875 Assert(!LWLockHeldByMe(PARTITION_LOCK(hash_table, i)));
876
878 if (i == 0 && hash_table->control->size_log2 >= new_size_log2)
879 {
880 /*
881 * Another backend has already increased the size; we can avoid
882 * obtaining all the locks and return early.
883 */
884 LWLockRelease(PARTITION_LOCK(hash_table, 0));
885 return;
886 }
887 }
888
889 Assert(new_size_log2 == hash_table->control->size_log2 + 1);
890
891 /* Allocate the space for the new table. */
893 dsa_allocate_extended(hash_table->area,
894 sizeof(dsa_pointer) * new_size,
897
898 /*
899 * We've allocated the new bucket array; all that remains to do now is to
900 * reinsert all items, which amounts to adjusting all the pointers.
901 */
902 size = ((size_t) 1) << hash_table->control->size_log2;
903 for (i = 0; i < size; ++i)
904 {
905 dsa_pointer item_pointer = hash_table->buckets[i];
906
908 {
909 dshash_table_item *item;
911
912 item = dsa_get_address(hash_table->area, item_pointer);
913 next_item_pointer = item->next;
914 insert_item_into_bucket(hash_table, item_pointer, item,
916 new_size_log2)]);
918 }
919 }
920
921 /* Swap the hash table into place and free the old one. */
922 old_buckets = hash_table->control->buckets;
923 hash_table->control->buckets = new_buckets_shared;
924 hash_table->control->size_log2 = new_size_log2;
925 hash_table->buckets = new_buckets;
926 dsa_free(hash_table->area, old_buckets);
927
928 /* Release all the locks. */
929 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
930 LWLockRelease(PARTITION_LOCK(hash_table, i));
931}

References dshash_table::area, Assert, BUCKET_INDEX_FOR_HASH_AND_SIZE, dshash_table_control::buckets, dshash_table::buckets, dshash_table::control, DSA_ALLOC_HUGE, DSA_ALLOC_ZERO, dsa_allocate_extended(), dsa_free(), dsa_get_address(), DsaPointerIsValid, DSHASH_NUM_PARTITIONS, fb(), dshash_table_item::hash, i, insert_item_into_bucket(), LW_EXCLUSIVE, LWLockAcquire(), LWLockHeldByMe(), LWLockRelease(), dshash_table_item::next, PARTITION_LOCK, and dshash_table_control::size_log2.

Referenced by dshash_find_or_insert().