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 bool resize (dshash_table *hash_table, size_t new_size_log2, int flags)
 
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, int flags)
 
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_extended (dshash_table *hash_table, const void *key, bool *found, int flags)
 
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:943
#define DSHASH_NUM_PARTITIONS
Definition dshash.c:62
bool LWLockAnyHeldByMe(LWLock *lock, int nlocks, size_t stride)
Definition lwlock.c:1903

Definition at line 199 of file dshash.c.

210{
211 dshash_table *hash_table;
212 dsa_pointer control;
213
214 /* Allocate the backend-local object representing the hash table. */
215 hash_table = palloc_object(dshash_table);
216
217 /* Allocate the control object in shared memory. */
218 control = dsa_allocate(area, sizeof(dshash_table_control));
219
220 /* Set up the local and shared hash table structs. */
221 hash_table->area = area;
222 hash_table->params = *params;
223 hash_table->arg = arg;
224 hash_table->control = dsa_get_address(area, control);
225 hash_table->control->handle = control;
226 hash_table->control->magic = DSHASH_MAGIC;
227 hash_table->control->lwlock_tranche_id = params->tranche_id;
228
229 /* Set up the array of lock partitions. */
230 {
232 int tranche_id = hash_table->control->lwlock_tranche_id;
233 int i;
234
235 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
236 {
237 LWLockInitialize(&partitions[i].lock, tranche_id);
238 partitions[i].count = 0;
239 }
240 }
241
242 /*
243 * Set up the initial array of buckets. Our initial size is the same as
244 * the number of partitions.
245 */
247 hash_table->control->buckets =
251 if (!DsaPointerIsValid(hash_table->control->buckets))
252 {
253 dsa_free(area, control);
256 errmsg("out of memory"),
257 errdetail("Failed on DSA request of size %zu.",
259 }
260 hash_table->buckets = dsa_get_address(area,
261 hash_table->control->buckets);
262 hash_table->size_log2 = hash_table->control->size_log2;
263
264 return hash_table;
265}
266
267/*
268 * Attach to an existing hash table using a handle. The returned object is
269 * allocated in backend-local memory using the current MemoryContext. 'arg'
270 * will be passed through to the compare and hash functions.
271 */
273dshash_attach(dsa_area *area, const dshash_parameters *params,
274 dshash_table_handle handle, void *arg)
275{
276 dshash_table *hash_table;
277 dsa_pointer control;
278
279 /* Allocate the backend-local object representing the hash table. */
280 hash_table = palloc_object(dshash_table);
281
282 /* Find the control object in shared memory. */
283 control = handle;
284
285 /* Set up the local hash table struct. */
286 hash_table->area = area;
287 hash_table->params = *params;
288 hash_table->arg = arg;
289 hash_table->control = dsa_get_address(area, control);
290 Assert(hash_table->control->magic == DSHASH_MAGIC);
291
292 /*
293 * These will later be set to the correct values by
294 * ensure_valid_bucket_pointers(), at which time we'll be holding a
295 * partition lock for interlocking against concurrent resizing.
296 */
297 hash_table->buckets = NULL;
298 hash_table->size_log2 = 0;
299
300 return hash_table;
301}
302
303/*
304 * Detach from a hash table. This frees backend-local resources associated
305 * with the hash table, but the hash table will continue to exist until it is
306 * either explicitly destroyed (by a backend that is still attached to it), or
307 * the area that backs it is returned to the operating system.
308 */
309void
310dshash_detach(dshash_table *hash_table)
311{
313
314 /* The hash table may have been destroyed. Just free local memory. */
315 pfree(hash_table);
316}
317
318/*
319 * Destroy a hash table, returning all memory to the area. The caller must be
320 * certain that no other backend will attempt to access the hash table before
321 * calling this function. Other backend must explicitly call dshash_detach to
322 * free up backend-local memory associated with the hash table. The backend
323 * that calls dshash_destroy must not call dshash_detach.
324 */
325void
326dshash_destroy(dshash_table *hash_table)
327{
328 size_t size;
329 size_t i;
330
331 Assert(hash_table->control->magic == DSHASH_MAGIC);
333
334 /* Free all the entries. */
335 size = NUM_BUCKETS(hash_table->size_log2);
336 for (i = 0; i < size; ++i)
337 {
338 dsa_pointer item_pointer = hash_table->buckets[i];
339
341 {
342 dshash_table_item *item;
344
345 item = dsa_get_address(hash_table->area, item_pointer);
346 next_item_pointer = item->next;
347 dsa_free(hash_table->area, item_pointer);
349 }
350 }
351
352 /*
353 * Vandalize the control block to help catch programming errors where
354 * other backends access the memory formerly occupied by this hash table.
355 */
356 hash_table->control->magic = 0;
357
358 /* Free the active table and control object. */
359 dsa_free(hash_table->area, hash_table->control->buckets);
360 dsa_free(hash_table->area, hash_table->control->handle);
361
362 pfree(hash_table);
363}
364
365/*
366 * Get a handle that can be used by other processes to attach to this hash
367 * table.
368 */
371{
372 Assert(hash_table->control->magic == DSHASH_MAGIC);
373
374 return hash_table->control->handle;
375}
376
377/*
378 * Look up an entry, given a key. Returns a pointer to an entry if one can be
379 * found with the given key. Returns NULL if the key is not found. If a
380 * non-NULL value is returned, the entry is locked and must be released by
381 * calling dshash_release_lock. If an error is raised before
382 * dshash_release_lock is called, the lock will be released automatically, but
383 * the caller must take care to ensure that the entry is not left corrupted.
384 * The lock mode is either shared or exclusive depending on 'exclusive'.
385 *
386 * The caller must not hold a lock already.
387 *
388 * Note that the lock held is in fact an LWLock, so interrupts will be held on
389 * return from this function, and not resumed until dshash_release_lock is
390 * called. It is a very good idea for the caller to release the lock quickly.
391 */
392void *
393dshash_find(dshash_table *hash_table, const void *key, bool exclusive)
394{
396 size_t partition;
397 dshash_table_item *item;
398
399 hash = hash_key(hash_table, key);
401
402 Assert(hash_table->control->magic == DSHASH_MAGIC);
404
406 exclusive ? LW_EXCLUSIVE : LW_SHARED);
408
409 /* Search the active bucket. */
410 item = find_in_bucket(hash_table, key, BUCKET_FOR_HASH(hash_table, hash));
411
412 if (!item)
413 {
414 /* Not found. */
416 return NULL;
417 }
418 else
419 {
420 /* The caller will free the lock by calling dshash_release_lock. */
421 return ENTRY_FROM_ITEM(item);
422 }
423}
424
425/*
426 * Find an existing entry in a dshash_table, or insert a new one.
427 *
428 * DSHASH_INSERT_NO_OOM causes this function to return NULL when no memory is
429 * available for the new entry. Otherwise, such allocations will result in
430 * an ERROR.
431 *
432 * Any entry returned by this function is exclusively locked, and the caller
433 * must release that lock using dshash_release_lock. Notes above dshash_find()
434 * regarding locking and error handling equally apply here.
435 *
436 * On return, *found is set to true if an existing entry was found in the
437 * hash table, and otherwise false.
438 *
439 */
440void *
442 const void *key,
443 bool *found,
444 int flags)
445{
447 size_t partition_index;
449 dshash_table_item *item;
450
451 hash = hash_key(hash_table, key);
454
455 Assert(hash_table->control->magic == DSHASH_MAGIC);
457
458restart:
462
463 /* Search the active bucket. */
464 item = find_in_bucket(hash_table, key, BUCKET_FOR_HASH(hash_table, hash));
465
466 if (item)
467 *found = true;
468 else
469 {
470 *found = false;
471
472 /* Check if we are getting too full. */
473 if (partition->count > MAX_COUNT_PER_PARTITION(hash_table))
474 {
475 /*
476 * The load factor (= keys / buckets) for all buckets protected by
477 * this partition is > 0.75. Presumably the same applies
478 * generally across the whole hash table (though we don't attempt
479 * to track that directly to avoid contention on some kind of
480 * central counter; we just assume that this partition is
481 * representative). This is a good time to resize.
482 *
483 * Give up our existing lock first, because resizing needs to
484 * reacquire all the locks in the right order to avoid deadlocks.
485 */
487 if (!resize(hash_table, hash_table->size_log2 + 1, flags))
488 {
489 Assert((flags & DSHASH_INSERT_NO_OOM) != 0);
490 return NULL;
491 }
492
493 goto restart;
494 }
495
496 /* Finally we can try to insert the new item. */
497 item = insert_into_bucket(hash_table, key,
498 &BUCKET_FOR_HASH(hash_table, hash),
499 flags);
500 if (item == NULL)
501 {
502 Assert((flags & DSHASH_INSERT_NO_OOM) != 0);
504 return NULL;
505 }
506 item->hash = hash;
507 /* Adjust per-lock-partition counter for load factor knowledge. */
508 ++partition->count;
509 }
510
511 /* The caller must release the lock with dshash_release_lock. */
512 return ENTRY_FROM_ITEM(item);
513}
514
515/*
516 * Remove an entry by key. Returns true if the key was found and the
517 * corresponding entry was removed.
518 *
519 * To delete an entry that you already have a pointer to, see
520 * dshash_delete_entry.
521 */
522bool
523dshash_delete_key(dshash_table *hash_table, const void *key)
524{
526 size_t partition;
527 bool found;
528
529 Assert(hash_table->control->magic == DSHASH_MAGIC);
531
532 hash = hash_key(hash_table, key);
534
537
538 if (delete_key_from_bucket(hash_table, key,
539 &BUCKET_FOR_HASH(hash_table, hash)))
540 {
541 Assert(hash_table->control->partitions[partition].count > 0);
542 found = true;
543 --hash_table->control->partitions[partition].count;
544 }
545 else
546 found = false;
547
549
550 return found;
551}
552
553/*
554 * Remove an entry. The entry must already be exclusively locked, and must
555 * have been obtained by dshash_find or dshash_find_or_insert. Note that this
556 * function releases the lock just like dshash_release_lock.
557 *
558 * To delete an entry by key, see dshash_delete_key.
559 */
560void
561dshash_delete_entry(dshash_table *hash_table, void *entry)
562{
563 dshash_table_item *item = ITEM_FROM_ENTRY(entry);
564 size_t partition = PARTITION_FOR_HASH(item->hash);
565
566 Assert(hash_table->control->magic == DSHASH_MAGIC);
568 LW_EXCLUSIVE));
569
570 delete_item(hash_table, item);
572}
573
574/*
575 * Unlock an entry which was locked by dshash_find or dshash_find_or_insert.
576 */
577void
578dshash_release_lock(dshash_table *hash_table, void *entry)
579{
580 dshash_table_item *item = ITEM_FROM_ENTRY(entry);
582
583 Assert(hash_table->control->magic == DSHASH_MAGIC);
584
586}
587
588/*
589 * A compare function that forwards to memcmp.
590 */
591int
592dshash_memcmp(const void *a, const void *b, size_t size, void *arg)
593{
594 return memcmp(a, b, size);
595}
596
597/*
598 * A hash function that forwards to tag_hash.
599 */
601dshash_memhash(const void *v, size_t size, void *arg)
602{
603 return tag_hash(v, size);
604}
605
606/*
607 * A copy function that forwards to memcpy.
608 */
609void
610dshash_memcpy(void *dest, const void *src, size_t size, void *arg)
611{
612 (void) memcpy(dest, src, size);
613}
614
615/*
616 * A compare function that forwards to strcmp.
617 */
618int
619dshash_strcmp(const void *a, const void *b, size_t size, void *arg)
620{
621 Assert(strlen((const char *) a) < size);
622 Assert(strlen((const char *) b) < size);
623
624 return strcmp((const char *) a, (const char *) b);
625}
626
627/*
628 * A hash function that forwards to string_hash.
629 */
631dshash_strhash(const void *v, size_t size, void *arg)
632{
633 Assert(strlen((const char *) v) < size);
634
635 return string_hash((const char *) v, size);
636}
637
638/*
639 * A copy function that forwards to strcpy.
640 */
641void
642dshash_strcpy(void *dest, const void *src, size_t size, void *arg)
643{
644 Assert(strlen((const char *) src) < size);
645
646 (void) strcpy((char *) dest, (const char *) src);
647}
648
649/*
650 * Sequentially scan through dshash table and return all the elements one by
651 * one, return NULL when all elements have been returned.
652 *
653 * dshash_seq_term needs to be called when a scan finished. The caller may
654 * delete returned elements midst of a scan by using dshash_delete_current()
655 * if exclusive = true.
656 */
657void
659 bool exclusive)
660{
661 status->hash_table = hash_table;
662 status->curbucket = 0;
663 status->nbuckets = 0;
664 status->curitem = NULL;
666 status->curpartition = -1;
667 status->exclusive = exclusive;
668}
669
670/*
671 * Returns the next element.
672 *
673 * Returned elements are locked and the caller may not release the lock. It is
674 * released by future calls to dshash_seq_next() or dshash_seq_term().
675 */
676void *
678{
680
681 /*
682 * Not yet holding any partition locks. Need to determine the size of the
683 * hash table, it could have been resized since we were looking last.
684 * Since we iterate in partition order, we can start by unconditionally
685 * lock partition 0.
686 *
687 * Once we hold the lock, no resizing can happen until the scan ends. So
688 * we don't need to repeatedly call ensure_valid_bucket_pointers().
689 */
690 if (status->curpartition == -1)
691 {
692 Assert(status->curbucket == 0);
694
695 status->curpartition = 0;
696
698 status->curpartition),
699 status->exclusive ? LW_EXCLUSIVE : LW_SHARED);
700
702
703 status->nbuckets =
705 next_item_pointer = status->hash_table->buckets[status->curbucket];
706 }
707 else
709
711 status->curpartition),
712 status->exclusive ? LW_EXCLUSIVE : LW_SHARED));
713
714 /* Move to the next bucket if we finished the current bucket */
716 {
717 int next_partition;
718
719 if (++status->curbucket >= status->nbuckets)
720 {
721 /* all buckets have been scanned. finish. */
722 return NULL;
723 }
724
725 /* Check if move to the next partition */
726 next_partition =
728 status->hash_table->size_log2);
729
730 if (status->curpartition != next_partition)
731 {
732 /*
733 * Move to the next partition. Lock the next partition then
734 * release the current, not in the reverse order to avoid
735 * concurrent resizing. Avoid dead lock by taking lock in the
736 * same order with resize().
737 */
739 next_partition),
740 status->exclusive ? LW_EXCLUSIVE : LW_SHARED);
742 status->curpartition));
743 status->curpartition = next_partition;
744 }
745
746 next_item_pointer = status->hash_table->buckets[status->curbucket];
747 }
748
749 status->curitem =
751
752 /*
753 * The caller may delete the item. Store the next item in case of
754 * deletion.
755 */
756 status->pnextitem = status->curitem->next;
757
758 return ENTRY_FROM_ITEM(status->curitem);
759}
760
761/*
762 * Terminates the seqscan and release all locks.
763 *
764 * Needs to be called after finishing or when exiting a seqscan.
765 */
766void
768{
769 if (status->curpartition >= 0)
771}
772
773/*
774 * Remove the current entry of the seq scan.
775 */
776void
778{
779 dshash_table *hash_table = status->hash_table;
780 dshash_table_item *item = status->curitem;
782
784
785 Assert(status->exclusive);
786 Assert(hash_table->control->magic == DSHASH_MAGIC);
788 LW_EXCLUSIVE));
789
790 delete_item(hash_table, item);
791}
792
793/*
794 * Print debugging information about the internal state of the hash table to
795 * stderr. The caller must hold no partition locks.
796 */
797void
798dshash_dump(dshash_table *hash_table)
799{
800 size_t i;
801 size_t j;
802
803 Assert(hash_table->control->magic == DSHASH_MAGIC);
805
806 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
807 {
808 Assert(!LWLockHeldByMe(PARTITION_LOCK(hash_table, i)));
810 }
811
813
815 "hash table size = %zu\n", (size_t) 1 << hash_table->size_log2);
816 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
817 {
819 size_t begin = BUCKET_INDEX_FOR_PARTITION(i, hash_table->size_log2);
820 size_t end = BUCKET_INDEX_FOR_PARTITION(i + 1, hash_table->size_log2);
821
822 fprintf(stderr, " partition %zu\n", i);
824 " active buckets (key count = %zu)\n", partition->count);
825
826 for (j = begin; j < end; ++j)
827 {
828 size_t count = 0;
829 dsa_pointer bucket = hash_table->buckets[j];
830
832 {
833 dshash_table_item *item;
834
835 item = dsa_get_address(hash_table->area, bucket);
836
837 bucket = item->next;
838 ++count;
839 }
840 fprintf(stderr, " bucket %zu (key count = %zu)\n", j, count);
841 }
842 }
843
844 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
845 LWLockRelease(PARTITION_LOCK(hash_table, i));
846}
847
848/*
849 * Delete a locked item to which we have a pointer.
850 */
851static void
853{
854 size_t hash = item->hash;
856
858
859 if (delete_item_from_bucket(hash_table, item,
860 &BUCKET_FOR_HASH(hash_table, hash)))
861 {
862 Assert(hash_table->control->partitions[partition].count > 0);
863 --hash_table->control->partitions[partition].count;
864 }
865 else
866 {
867 Assert(false);
868 }
869}
870
871/*
872 * Grow the hash table if necessary to the requested number of buckets. The
873 * requested size must be double some previously observed size.
874 *
875 * If an out-of-memory condition is observed, this function returns false if
876 * flags includes DSHASH_INSERT_NO_OOM, and otherwise throws an ERROR. In all
877 * other cases, it returns true.
878 *
879 * Must be called without any partition lock held.
880 */
881static bool
882resize(dshash_table *hash_table, size_t new_size_log2, int flags)
883{
887 size_t size;
888 size_t new_size = ((size_t) 1) << new_size_log2;
889 size_t i;
891
892 /*
893 * Acquire the locks for all lock partitions. This is expensive, but we
894 * shouldn't have to do it many times.
895 */
896 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
897 {
898 Assert(!LWLockHeldByMe(PARTITION_LOCK(hash_table, i)));
899
901 if (i == 0 && hash_table->control->size_log2 >= new_size_log2)
902 {
903 /*
904 * Another backend has already increased the size; we can avoid
905 * obtaining all the locks and return early.
906 */
907 LWLockRelease(PARTITION_LOCK(hash_table, 0));
908 return true;
909 }
910 }
911
912 Assert(new_size_log2 == hash_table->control->size_log2 + 1);
913
914 /* Allocate the space for the new table. */
915 if (flags & DSHASH_INSERT_NO_OOM)
918 dsa_allocate_extended(hash_table->area,
919 sizeof(dsa_pointer) * new_size,
920 dsa_flags);
921
922 /* If DSHASH_INSERT_NO_OOM was specified, allocation may have failed. */
924 {
925 /* Release all the locks and return without resizing. */
926 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
927 LWLockRelease(PARTITION_LOCK(hash_table, i));
928 return false;
929 }
930
931 /*
932 * We've allocated the new bucket array; all that remains to do now is to
933 * reinsert all items, which amounts to adjusting all the pointers.
934 */
936 size = ((size_t) 1) << hash_table->control->size_log2;
937 for (i = 0; i < size; ++i)
938 {
939 dsa_pointer item_pointer = hash_table->buckets[i];
940
942 {
943 dshash_table_item *item;
945
946 item = dsa_get_address(hash_table->area, item_pointer);
947 next_item_pointer = item->next;
948 insert_item_into_bucket(hash_table, item_pointer, item,
950 new_size_log2)]);
952 }
953 }
954
955 /* Swap the hash table into place and free the old one. */
956 old_buckets = hash_table->control->buckets;
957 hash_table->control->buckets = new_buckets_shared;
958 hash_table->control->size_log2 = new_size_log2;
959 hash_table->buckets = new_buckets;
960 dsa_free(hash_table->area, old_buckets);
961
962 /* Release all the locks. */
963 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
964 LWLockRelease(PARTITION_LOCK(hash_table, i));
965
966 return true;
967}
968
969/*
970 * Make sure that our backend-local bucket pointers are up to date. The
971 * caller must have locked one lock partition, which prevents resize() from
972 * running concurrently.
973 */
974static inline void
976{
977 if (hash_table->size_log2 != hash_table->control->size_log2)
978 {
979 hash_table->buckets = dsa_get_address(hash_table->area,
980 hash_table->control->buckets);
981 hash_table->size_log2 = hash_table->control->size_log2;
982 }
983}
984
985/*
986 * Scan a locked bucket for a match, using the provided compare function.
987 */
988static inline dshash_table_item *
989find_in_bucket(dshash_table *hash_table, const void *key,
991{
993 {
994 dshash_table_item *item;
995
996 item = dsa_get_address(hash_table->area, item_pointer);
997 if (equal_keys(hash_table, key, ENTRY_FROM_ITEM(item)))
998 return item;
999 item_pointer = item->next;
1000 }
1001 return NULL;
1002}
1003
1004/*
1005 * Insert an already-allocated item into a bucket.
1006 */
1007static void
1010 dshash_table_item *item,
1012{
1013 Assert(item == dsa_get_address(hash_table->area, item_pointer));
1014
1015 item->next = *bucket;
1017}
1018
1019/*
1020 * Allocate space for an entry with the given key and insert it into the
1021 * provided bucket. Returns NULL if out of memory and DSHASH_INSERT_NO_OOM
1022 * was specified in flags.
1023 */
1024static dshash_table_item *
1026 const void *key,
1028 int flags)
1029{
1031 dshash_table_item *item;
1032 int dsa_flags;
1033
1036 hash_table->params.entry_size +
1037 MAXALIGN(sizeof(dshash_table_item)),
1038 dsa_flags);
1040 return NULL;
1041 item = dsa_get_address(hash_table->area, item_pointer);
1042 copy_key(hash_table, ENTRY_FROM_ITEM(item), key);
1043 insert_item_into_bucket(hash_table, item_pointer, item, bucket);
1044 return item;
1045}
1046
1047/*
1048 * Search a bucket for a matching key and delete it.
1049 */
1050static bool
1052 const void *key,
1054{
1056 {
1057 dshash_table_item *item;
1058
1059 item = dsa_get_address(hash_table->area, *bucket_head);
1060
1061 if (equal_keys(hash_table, key, ENTRY_FROM_ITEM(item)))
1062 {
1064
1065 next = item->next;
1066 dsa_free(hash_table->area, *bucket_head);
1067 *bucket_head = next;
1068
1069 return true;
1070 }
1071 bucket_head = &item->next;
1072 }
1073 return false;
1074}
1075
1076/*
1077 * Delete the specified item from the bucket.
1078 */
1079static bool
1081 dshash_table_item *item,
1083{
1085 {
1087
1089
1090 if (bucket_item == item)
1091 {
1093
1094 next = item->next;
1095 dsa_free(hash_table->area, *bucket_head);
1096 *bucket_head = next;
1097 return true;
1098 }
1099 bucket_head = &bucket_item->next;
1100 }
1101 return false;
1102}
1103
1104/*
1105 * Compute the hash value for a key.
1106 */
1107static inline dshash_hash
1108hash_key(dshash_table *hash_table, const void *key)
1109{
1110 return hash_table->params.hash_function(key,
1111 hash_table->params.key_size,
1112 hash_table->arg);
1113}
1114
1115/*
1116 * Check whether two keys compare equal.
1117 */
1118static inline bool
1119equal_keys(dshash_table *hash_table, const void *a, const void *b)
1120{
1121 return hash_table->params.compare_function(a, b,
1122 hash_table->params.key_size,
1123 hash_table->arg) == 0;
1124}
1125
1126/*
1127 * Copy a key.
1128 */
1129static inline void
1130copy_key(dshash_table *hash_table, void *dest, const void *src)
1131{
1132 hash_table->params.copy_function(dest, src,
1133 hash_table->params.key_size,
1134 hash_table->arg);
1135}
static int32 next
Definition blutils.c:225
#define MAXALIGN(LEN)
Definition c.h:896
#define PG_USED_FOR_ASSERTS_ONLY
Definition c.h:249
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
#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:524
void dshash_memcpy(void *dest, const void *src, size_t size, void *arg)
Definition dshash.c:611
void dshash_delete_entry(dshash_table *hash_table, void *entry)
Definition dshash.c:562
void dshash_strcpy(void *dest, const void *src, size_t size, void *arg)
Definition dshash.c:643
static dshash_table_item * insert_into_bucket(dshash_table *hash_table, const void *key, dsa_pointer *bucket, int flags)
Definition dshash.c:1026
void dshash_destroy(dshash_table *hash_table)
Definition dshash.c:327
void dshash_release_lock(dshash_table *hash_table, void *entry)
Definition dshash.c:579
#define PARTITION_LOCK(hash_table, i)
Definition dshash.c:196
void dshash_detach(dshash_table *hash_table)
Definition dshash.c:311
void dshash_seq_init(dshash_seq_status *status, dshash_table *hash_table, bool exclusive)
Definition dshash.c:659
void * dshash_find(dshash_table *hash_table, const void *key, bool exclusive)
Definition dshash.c:394
#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:1052
dshash_hash dshash_strhash(const void *v, size_t size, void *arg)
Definition dshash.c:632
static dshash_hash hash_key(dshash_table *hash_table, const void *key)
Definition dshash.c:1109
#define ASSERT_NO_PARTITION_LOCKS_HELD_BY_ME(hash_table)
Definition dshash.c:199
#define ITEM_FROM_ENTRY(entry)
Definition dshash.c:120
dshash_table_handle dshash_get_hash_table_handle(dshash_table *hash_table)
Definition dshash.c:371
void dshash_dump(dshash_table *hash_table)
Definition dshash.c:799
static bool equal_keys(dshash_table *hash_table, const void *a, const void *b)
Definition dshash.c:1120
dshash_table * dshash_attach(dsa_area *area, const dshash_parameters *params, dshash_table_handle handle, void *arg)
Definition dshash.c:274
void dshash_seq_term(dshash_seq_status *status)
Definition dshash.c:768
#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:1009
static bool delete_item_from_bucket(dshash_table *hash_table, dshash_table_item *item, dsa_pointer *bucket_head)
Definition dshash.c:1081
int dshash_strcmp(const void *a, const void *b, size_t size, void *arg)
Definition dshash.c:620
#define PARTITION_FOR_BUCKET_INDEX(bucket_idx, size_log2)
Definition dshash.c:159
#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:853
dshash_hash dshash_memhash(const void *v, size_t size, void *arg)
Definition dshash.c:602
#define NUM_BUCKETS(size_log2)
Definition dshash.c:129
static void ensure_valid_bucket_pointers(dshash_table *hash_table)
Definition dshash.c:976
static dshash_table_item * find_in_bucket(dshash_table *hash_table, const void *key, dsa_pointer item_pointer)
Definition dshash.c:990
void * dshash_seq_next(dshash_seq_status *status)
Definition dshash.c:678
static void copy_key(dshash_table *hash_table, void *dest, const void *src)
Definition dshash.c:1131
int dshash_memcmp(const void *a, const void *b, size_t size, void *arg)
Definition dshash.c:593
#define PARTITION_FOR_HASH(hash)
Definition dshash.c:142
#define BUCKET_FOR_HASH(hash_table, hash)
Definition dshash.c:163
void * dshash_find_or_insert_extended(dshash_table *hash_table, const void *key, bool *found, int flags)
Definition dshash.c:442
static bool resize(dshash_table *hash_table, size_t new_size_log2, int flags)
Definition dshash.c:883
void dshash_delete_current(dshash_seq_status *status)
Definition dshash.c:778
dsa_pointer dshash_table_handle
Definition dshash.h:24
uint32 dshash_hash
Definition dshash.h:30
#define DSHASH_INSERT_NO_OOM
Definition dshash.h:96
Datum arg
Definition elog.c:1322
int errcode(int sqlerrcode)
Definition elog.c:874
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define ERROR
Definition elog.h:39
#define ereport(elevel,...)
Definition elog.h:151
#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:1885
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition lwlock.c:1150
bool LWLockHeldByMeInMode(LWLock *lock, LWLockMode mode)
Definition lwlock.c:1929
void LWLockRelease(LWLock *lock)
Definition lwlock.c:1767
void LWLockInitialize(LWLock *lock, int tranche_id)
Definition lwlock.c:670
@ LW_SHARED
Definition lwlock.h:105
@ LW_EXCLUSIVE
Definition lwlock.h:104
void pfree(void *pointer)
Definition mcxt.c:1616
static char * errmsg
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 196 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 1131 of file dshash.c.

1132{
1133 hash_table->params.copy_function(dest, src,
1134 hash_table->params.key_size,
1135 hash_table->arg);
1136}

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 853 of file dshash.c.

854{
855 size_t hash = item->hash;
857
859
860 if (delete_item_from_bucket(hash_table, item,
861 &BUCKET_FOR_HASH(hash_table, hash)))
862 {
863 Assert(hash_table->control->partitions[partition].count > 0);
864 --hash_table->control->partitions[partition].count;
865 }
866 else
867 {
868 Assert(false);
869 }
870}

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 1081 of file dshash.c.

1084{
1086 {
1088
1090
1091 if (bucket_item == item)
1092 {
1094
1095 next = item->next;
1096 dsa_free(hash_table->area, *bucket_head);
1097 *bucket_head = next;
1098 return true;
1099 }
1100 bucket_head = &bucket_item->next;
1101 }
1102 return false;
1103}

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 1052 of file dshash.c.

1055{
1057 {
1058 dshash_table_item *item;
1059
1060 item = dsa_get_address(hash_table->area, *bucket_head);
1061
1062 if (equal_keys(hash_table, key, ENTRY_FROM_ITEM(item)))
1063 {
1065
1066 next = item->next;
1067 dsa_free(hash_table->area, *bucket_head);
1068 *bucket_head = next;
1069
1070 return true;
1071 }
1072 bucket_head = &item->next;
1073 }
1074 return false;
1075}

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 274 of file dshash.c.

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

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(), pgsa_attach(), pgstat_attach_shmem(), and SharedRecordTypmodRegistryAttach().

◆ dshash_create()

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

Definition at line 210 of file dshash.c.

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

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(), pgsa_attach(), 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 327 of file dshash.c.

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

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 311 of file dshash.c.

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

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 799 of file dshash.c.

800{
801 size_t i;
802 size_t j;
803
804 Assert(hash_table->control->magic == DSHASH_MAGIC);
806
807 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
808 {
809 Assert(!LWLockHeldByMe(PARTITION_LOCK(hash_table, i)));
811 }
812
814
816 "hash table size = %zu\n", (size_t) 1 << hash_table->size_log2);
817 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
818 {
820 size_t begin = BUCKET_INDEX_FOR_PARTITION(i, hash_table->size_log2);
821 size_t end = BUCKET_INDEX_FOR_PARTITION(i + 1, hash_table->size_log2);
822
823 fprintf(stderr, " partition %zu\n", i);
825 " active buckets (key count = %zu)\n", partition->count);
826
827 for (j = begin; j < end; ++j)
828 {
829 size_t count = 0;
830 dsa_pointer bucket = hash_table->buckets[j];
831
833 {
834 dshash_table_item *item;
835
836 item = dsa_get_address(hash_table->area, bucket);
837
838 bucket = item->next;
839 ++count;
840 }
841 fprintf(stderr, " bucket %zu (key count = %zu)\n", j, count);
842 }
843 }
844
845 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
846 LWLockRelease(PARTITION_LOCK(hash_table, i));
847}

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 394 of file dshash.c.

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

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(), pgsa_advisor(), pgsa_clear_advice_string(), pgsa_drop_stash(), pgsa_lookup_stash_id(), pgstat_drop_entry(), pgstat_get_entry_ref(), pgstat_release_entry_ref(), and SignalBackends().

◆ dshash_find_or_insert_extended()

void * dshash_find_or_insert_extended ( dshash_table hash_table,
const void key,
bool found,
int  flags 
)

Definition at line 442 of file dshash.c.

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

References Assert, ASSERT_NO_PARTITION_LOCKS_HELD_BY_ME, BUCKET_FOR_HASH, dshash_table::control, DSHASH_INSERT_NO_OOM, 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 pgsa_set_advice_string().

◆ dshash_get_hash_table_handle()

◆ dshash_memcmp()

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

Definition at line 593 of file dshash.c.

594{
595 return memcmp(a, b, size);
596}

References a, b, and fb().

◆ dshash_memcpy()

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

Definition at line 611 of file dshash.c.

612{
613 (void) memcpy(dest, src, size);
614}

References fb(), and memcpy().

◆ dshash_memhash()

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

Definition at line 602 of file dshash.c.

603{
604 return tag_hash(v, size);
605}

References tag_hash().

◆ dshash_release_lock()

◆ dshash_seq_init()

◆ dshash_seq_next()

void * dshash_seq_next ( dshash_seq_status status)

Definition at line 678 of file dshash.c.

679{
681
682 /*
683 * Not yet holding any partition locks. Need to determine the size of the
684 * hash table, it could have been resized since we were looking last.
685 * Since we iterate in partition order, we can start by unconditionally
686 * lock partition 0.
687 *
688 * Once we hold the lock, no resizing can happen until the scan ends. So
689 * we don't need to repeatedly call ensure_valid_bucket_pointers().
690 */
691 if (status->curpartition == -1)
692 {
693 Assert(status->curbucket == 0);
695
696 status->curpartition = 0;
697
699 status->curpartition),
700 status->exclusive ? LW_EXCLUSIVE : LW_SHARED);
701
703
704 status->nbuckets =
706 next_item_pointer = status->hash_table->buckets[status->curbucket];
707 }
708 else
710
712 status->curpartition),
713 status->exclusive ? LW_EXCLUSIVE : LW_SHARED));
714
715 /* Move to the next bucket if we finished the current bucket */
717 {
718 int next_partition;
719
720 if (++status->curbucket >= status->nbuckets)
721 {
722 /* all buckets have been scanned. finish. */
723 return NULL;
724 }
725
726 /* Check if move to the next partition */
727 next_partition =
729 status->hash_table->size_log2);
730
731 if (status->curpartition != next_partition)
732 {
733 /*
734 * Move to the next partition. Lock the next partition then
735 * release the current, not in the reverse order to avoid
736 * concurrent resizing. Avoid dead lock by taking lock in the
737 * same order with resize().
738 */
740 next_partition),
741 status->exclusive ? LW_EXCLUSIVE : LW_SHARED);
743 status->curpartition));
744 status->curpartition = next_partition;
745 }
746
747 next_item_pointer = status->hash_table->buckets[status->curbucket];
748 }
749
750 status->curitem =
752
753 /*
754 * The caller may delete the item. Store the next item in case of
755 * deletion.
756 */
757 status->pnextitem = status->curitem->next;
758
759 return ENTRY_FROM_ITEM(status->curitem);
760}

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_advice_stash_contents(), pg_get_advice_stashes(), pg_get_dsm_registry_allocations(), pgsa_drop_stash(), 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 620 of file dshash.c.

621{
622 Assert(strlen((const char *) a) < size);
623 Assert(strlen((const char *) b) < size);
624
625 return strcmp((const char *) a, (const char *) b);
626}

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

◆ dshash_strcpy()

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

Definition at line 643 of file dshash.c.

644{
645 Assert(strlen((const char *) src) < size);
646
647 (void) strcpy((char *) dest, (const char *) src);
648}

References Assert, and fb().

◆ dshash_strhash()

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

Definition at line 632 of file dshash.c.

633{
634 Assert(strlen((const char *) v) < size);
635
636 return string_hash((const char *) v, size);
637}

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

◆ ensure_valid_bucket_pointers()

static void ensure_valid_bucket_pointers ( dshash_table hash_table)
inlinestatic

◆ equal_keys()

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

Definition at line 1120 of file dshash.c.

1121{
1122 return hash_table->params.compare_function(a, b,
1123 hash_table->params.key_size,
1124 hash_table->arg) == 0;
1125}

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 990 of file dshash.c.

992{
994 {
995 dshash_table_item *item;
996
997 item = dsa_get_address(hash_table->area, item_pointer);
998 if (equal_keys(hash_table, key, ENTRY_FROM_ITEM(item)))
999 return item;
1000 item_pointer = item->next;
1001 }
1002 return NULL;
1003}

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_extended().

◆ hash_key()

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

Definition at line 1109 of file dshash.c.

1110{
1111 return hash_table->params.hash_function(key,
1112 hash_table->params.key_size,
1113 hash_table->arg);
1114}

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_extended().

◆ insert_into_bucket()

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

◆ 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 1009 of file dshash.c.

1013{
1014 Assert(item == dsa_get_address(hash_table->area, item_pointer));
1015
1016 item->next = *bucket;
1018}

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

Referenced by insert_into_bucket(), and resize().

◆ resize()

static bool resize ( dshash_table hash_table,
size_t  new_size_log2,
int  flags 
)
static

Definition at line 883 of file dshash.c.

884{
888 size_t size;
889 size_t new_size = ((size_t) 1) << new_size_log2;
890 size_t i;
892
893 /*
894 * Acquire the locks for all lock partitions. This is expensive, but we
895 * shouldn't have to do it many times.
896 */
897 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
898 {
899 Assert(!LWLockHeldByMe(PARTITION_LOCK(hash_table, i)));
900
902 if (i == 0 && hash_table->control->size_log2 >= new_size_log2)
903 {
904 /*
905 * Another backend has already increased the size; we can avoid
906 * obtaining all the locks and return early.
907 */
908 LWLockRelease(PARTITION_LOCK(hash_table, 0));
909 return true;
910 }
911 }
912
913 Assert(new_size_log2 == hash_table->control->size_log2 + 1);
914
915 /* Allocate the space for the new table. */
916 if (flags & DSHASH_INSERT_NO_OOM)
919 dsa_allocate_extended(hash_table->area,
920 sizeof(dsa_pointer) * new_size,
921 dsa_flags);
922
923 /* If DSHASH_INSERT_NO_OOM was specified, allocation may have failed. */
925 {
926 /* Release all the locks and return without resizing. */
927 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
928 LWLockRelease(PARTITION_LOCK(hash_table, i));
929 return false;
930 }
931
932 /*
933 * We've allocated the new bucket array; all that remains to do now is to
934 * reinsert all items, which amounts to adjusting all the pointers.
935 */
937 size = ((size_t) 1) << hash_table->control->size_log2;
938 for (i = 0; i < size; ++i)
939 {
940 dsa_pointer item_pointer = hash_table->buckets[i];
941
943 {
944 dshash_table_item *item;
946
947 item = dsa_get_address(hash_table->area, item_pointer);
948 next_item_pointer = item->next;
949 insert_item_into_bucket(hash_table, item_pointer, item,
951 new_size_log2)]);
953 }
954 }
955
956 /* Swap the hash table into place and free the old one. */
957 old_buckets = hash_table->control->buckets;
958 hash_table->control->buckets = new_buckets_shared;
959 hash_table->control->size_log2 = new_size_log2;
960 hash_table->buckets = new_buckets;
961 dsa_free(hash_table->area, old_buckets);
962
963 /* Release all the locks. */
964 for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i)
965 LWLockRelease(PARTITION_LOCK(hash_table, i));
966
967 return true;
968}

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_NO_OOM, DSA_ALLOC_ZERO, dsa_allocate_extended(), dsa_free(), dsa_get_address(), DsaPointerIsValid, DSHASH_INSERT_NO_OOM, 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_extended().