@@ -127,6 +127,10 @@ struct dshash_table
127
127
#define NUM_SPLITS (size_log2 ) \
128
128
(size_log2 - DSHASH_NUM_PARTITIONS_LOG2)
129
129
130
+ /* How many buckets are there in a given size? */
131
+ #define NUM_BUCKETS (size_log2 ) \
132
+ (((size_t) 1) << (size_log2))
133
+
130
134
/* How many buckets are there in each partition at a given size? */
131
135
#define BUCKETS_PER_PARTITION (size_log2 ) \
132
136
(((size_t) 1) << NUM_SPLITS(size_log2))
@@ -153,6 +157,10 @@ struct dshash_table
153
157
#define BUCKET_INDEX_FOR_PARTITION (partition , size_log2 ) \
154
158
((partition) << NUM_SPLITS(size_log2))
155
159
160
+ /* Choose partition based on bucket index. */
161
+ #define PARTITION_FOR_BUCKET_INDEX (bucket_idx , size_log2 ) \
162
+ ((bucket_idx) >> NUM_SPLITS(size_log2))
163
+
156
164
/* The head of the active bucket for a given hash value (lvalue). */
157
165
#define BUCKET_FOR_HASH (hash_table , hash ) \
158
166
(hash_table->buckets[ \
@@ -324,7 +332,7 @@ dshash_destroy(dshash_table *hash_table)
324
332
ensure_valid_bucket_pointers (hash_table );
325
333
326
334
/* Free all the entries. */
327
- size = (( size_t ) 1 ) << hash_table -> size_log2 ;
335
+ size = NUM_BUCKETS ( hash_table -> size_log2 ) ;
328
336
for (i = 0 ; i < size ; ++ i )
329
337
{
330
338
dsa_pointer item_pointer = hash_table -> buckets [i ];
@@ -592,6 +600,159 @@ dshash_memhash(const void *v, size_t size, void *arg)
592
600
return tag_hash (v , size );
593
601
}
594
602
603
+ /*
604
+ * dshash_seq_init/_next/_term
605
+ * Sequentially scan through dshash table and return all the
606
+ * elements one by one, return NULL when no more.
607
+ *
608
+ * dshash_seq_term should always be called when a scan finished.
609
+ * The caller may delete returned elements midst of a scan by using
610
+ * dshash_delete_current(). exclusive must be true to delete elements.
611
+ */
612
+ void
613
+ dshash_seq_init (dshash_seq_status * status , dshash_table * hash_table ,
614
+ bool exclusive )
615
+ {
616
+ status -> hash_table = hash_table ;
617
+ status -> curbucket = 0 ;
618
+ status -> nbuckets = 0 ;
619
+ status -> curitem = NULL ;
620
+ status -> pnextitem = InvalidDsaPointer ;
621
+ status -> curpartition = -1 ;
622
+ status -> exclusive = exclusive ;
623
+ }
624
+
625
+ /*
626
+ * Returns the next element.
627
+ *
628
+ * Returned elements are locked and the caller must not explicitly release
629
+ * it. It is released at the next call to dshash_next().
630
+ */
631
+ void *
632
+ dshash_seq_next (dshash_seq_status * status )
633
+ {
634
+ dsa_pointer next_item_pointer ;
635
+
636
+ if (status -> curitem == NULL )
637
+ {
638
+ int partition ;
639
+
640
+ Assert (status -> curbucket == 0 );
641
+ Assert (!status -> hash_table -> find_locked );
642
+
643
+ /* first shot. grab the first item. */
644
+ partition =
645
+ PARTITION_FOR_BUCKET_INDEX (status -> curbucket ,
646
+ status -> hash_table -> size_log2 );
647
+ LWLockAcquire (PARTITION_LOCK (status -> hash_table , partition ),
648
+ status -> exclusive ? LW_EXCLUSIVE : LW_SHARED );
649
+ status -> curpartition = partition ;
650
+
651
+ /* resize doesn't happen from now until seq scan ends */
652
+ status -> nbuckets =
653
+ NUM_BUCKETS (status -> hash_table -> control -> size_log2 );
654
+ ensure_valid_bucket_pointers (status -> hash_table );
655
+
656
+ next_item_pointer = status -> hash_table -> buckets [status -> curbucket ];
657
+ }
658
+ else
659
+ next_item_pointer = status -> pnextitem ;
660
+
661
+ Assert (LWLockHeldByMeInMode (PARTITION_LOCK (status -> hash_table ,
662
+ status -> curpartition ),
663
+ status -> exclusive ? LW_EXCLUSIVE : LW_SHARED ));
664
+
665
+ /* Move to the next bucket if we finished the current bucket */
666
+ while (!DsaPointerIsValid (next_item_pointer ))
667
+ {
668
+ int next_partition ;
669
+
670
+ if (++ status -> curbucket >= status -> nbuckets )
671
+ {
672
+ /* all buckets have been scanned. finish. */
673
+ return NULL ;
674
+ }
675
+
676
+ /* Check if move to the next partition */
677
+ next_partition =
678
+ PARTITION_FOR_BUCKET_INDEX (status -> curbucket ,
679
+ status -> hash_table -> size_log2 );
680
+
681
+ if (status -> curpartition != next_partition )
682
+ {
683
+ /*
684
+ * Move to the next partition. Lock the next partition then
685
+ * release the current, not in the reverse order to avoid
686
+ * concurrent resizing. Avoid dead lock by taking lock in the
687
+ * same order with resize().
688
+ */
689
+ LWLockAcquire (PARTITION_LOCK (status -> hash_table ,
690
+ next_partition ),
691
+ status -> exclusive ? LW_EXCLUSIVE : LW_SHARED );
692
+ LWLockRelease (PARTITION_LOCK (status -> hash_table ,
693
+ status -> curpartition ));
694
+ status -> curpartition = next_partition ;
695
+ }
696
+
697
+ next_item_pointer = status -> hash_table -> buckets [status -> curbucket ];
698
+ }
699
+
700
+ status -> curitem =
701
+ dsa_get_address (status -> hash_table -> area , next_item_pointer );
702
+ status -> hash_table -> find_locked = true;
703
+ status -> hash_table -> find_exclusively_locked = status -> exclusive ;
704
+
705
+ /*
706
+ * The caller may delete the item. Store the next item in case of
707
+ * deletion.
708
+ */
709
+ status -> pnextitem = status -> curitem -> next ;
710
+
711
+ return ENTRY_FROM_ITEM (status -> curitem );
712
+ }
713
+
714
+ /*
715
+ * Terminates the seqscan and release all locks.
716
+ *
717
+ * Should be always called when finishing or exiting a seqscan.
718
+ */
719
+ void
720
+ dshash_seq_term (dshash_seq_status * status )
721
+ {
722
+ status -> hash_table -> find_locked = false;
723
+ status -> hash_table -> find_exclusively_locked = false;
724
+
725
+ if (status -> curpartition >= 0 )
726
+ LWLockRelease (PARTITION_LOCK (status -> hash_table , status -> curpartition ));
727
+ }
728
+
729
+ /* Remove the current entry while a seq scan. */
730
+ void
731
+ dshash_delete_current (dshash_seq_status * status )
732
+ {
733
+ dshash_table * hash_table = status -> hash_table ;
734
+ dshash_table_item * item = status -> curitem ;
735
+ size_t partition PG_USED_FOR_ASSERTS_ONLY ;
736
+
737
+ partition = PARTITION_FOR_HASH (item -> hash );
738
+
739
+ Assert (status -> exclusive );
740
+ Assert (hash_table -> control -> magic == DSHASH_MAGIC );
741
+ Assert (hash_table -> find_locked );
742
+ Assert (hash_table -> find_exclusively_locked );
743
+ Assert (LWLockHeldByMeInMode (PARTITION_LOCK (hash_table , partition ),
744
+ LW_EXCLUSIVE ));
745
+
746
+ delete_item (hash_table , item );
747
+ }
748
+
749
+ /* Get the current entry while a seq scan. */
750
+ void *
751
+ dshash_get_current (dshash_seq_status * status )
752
+ {
753
+ return ENTRY_FROM_ITEM (status -> curitem );
754
+ }
755
+
595
756
/*
596
757
* Print debugging information about the internal state of the hash table to
597
758
* stderr. The caller must hold no partition locks.
0 commit comments