@@ -284,6 +284,9 @@ typedef struct hashtableBucket {
284284/* A key property is that the bucket size is one cache line. */
285285static_assert (sizeof (bucket ) == HASHTABLE_BUCKET_SIZE , "Bucket size mismatch" );
286286
287+ /* Forward declaration for iter type */
288+ typedef struct iter iter ;
289+
287290struct hashtable {
288291 hashtableType * type ;
289292 ssize_t rehash_idx ; /* -1 = rehashing not in progress. */
@@ -293,10 +296,11 @@ struct hashtable {
293296 int16_t pause_rehash ; /* Non-zero = rehashing is paused */
294297 int16_t pause_auto_shrink ; /* Non-zero = automatic resizing disallowed. */
295298 size_t child_buckets [2 ]; /* Number of allocated child buckets. */
299+ iter * safe_iterators ; /* Head of linked list of safe iterators */
296300 void * metadata [];
297301};
298302
299- typedef struct {
303+ struct iter {
300304 hashtable * hashtable ;
301305 bucket * bucket ;
302306 long index ;
@@ -309,7 +313,8 @@ typedef struct {
309313 /* Safe iterator temporary storage for bucket chain compaction. */
310314 uint64_t last_seen_size ;
311315 };
312- } iter ;
316+ iter * next_safe_iter ; /* Next safe iterator in hashtable's list */
317+ };
313318
314319/* The opaque hashtableIterator is defined as a blob of bytes. */
315320static_assert (sizeof (hashtableIterator ) >= sizeof (iter ),
@@ -1084,6 +1089,37 @@ static inline int shouldPrefetchValues(iter *iter) {
10841089 return (iter -> flags & HASHTABLE_ITER_PREFETCH_VALUES );
10851090}
10861091
1092+ /* Add a safe iterator to the hashtable's tracking list */
1093+ static void trackSafeIterator (iter * it ) {
1094+ assert (it -> next_safe_iter == NULL );
1095+ hashtable * ht = it -> hashtable ;
1096+ it -> next_safe_iter = ht -> safe_iterators ;
1097+ ht -> safe_iterators = it ;
1098+ }
1099+
1100+ /* Remove a safe iterator from the hashtable's tracking list */
1101+ static void untrackSafeIterator (iter * it ) {
1102+ hashtable * ht = it -> hashtable ;
1103+ if (ht -> safe_iterators == it ) {
1104+ ht -> safe_iterators = it -> next_safe_iter ;
1105+ } else {
1106+ iter * current = ht -> safe_iterators ;
1107+ assert (current != NULL );
1108+ while (current -> next_safe_iter != it ) {
1109+ current = current -> next_safe_iter ;
1110+ assert (current != NULL );
1111+ }
1112+ current -> next_safe_iter = it -> next_safe_iter ;
1113+ }
1114+ it -> next_safe_iter = NULL ;
1115+ it -> hashtable = NULL ; /* Mark as invalid */
1116+ }
1117+
1118+ /* Invalidate all safe iterators by setting hashtable = NULL */
1119+ static void invalidateAllSafeIterators (hashtable * ht ) {
1120+ while (ht -> safe_iterators ) untrackSafeIterator (ht -> safe_iterators );
1121+ }
1122+
10871123/* --- API functions --- */
10881124
10891125/* Allocates and initializes a new hashtable specified by the given type. */
@@ -1098,6 +1134,7 @@ hashtable *hashtableCreate(hashtableType *type) {
10981134 ht -> rehash_idx = -1 ;
10991135 ht -> pause_rehash = 0 ;
11001136 ht -> pause_auto_shrink = 0 ;
1137+ ht -> safe_iterators = NULL ;
11011138 resetTable (ht , 0 );
11021139 resetTable (ht , 1 );
11031140 if (type -> trackMemUsage ) type -> trackMemUsage (ht , alloc_size );
@@ -1153,6 +1190,7 @@ void hashtableEmpty(hashtable *ht, void(callback)(hashtable *)) {
11531190
11541191/* Deletes all the entries and frees the table. */
11551192void hashtableRelease (hashtable * ht ) {
1193+ invalidateAllSafeIterators (ht );
11561194 hashtableEmpty (ht , NULL );
11571195 /* Call trackMemUsage before zfree, so trackMemUsage can access ht. */
11581196 if (ht -> type -> trackMemUsage ) {
@@ -1242,6 +1280,7 @@ static void hashtablePauseRehashing(hashtable *ht) {
12421280/* Resumes incremental rehashing, after pausing it. */
12431281static void hashtableResumeRehashing (hashtable * ht ) {
12441282 ht -> pause_rehash -- ;
1283+ assert (ht -> pause_rehash >= 0 );
12451284 hashtableResumeAutoShrink (ht );
12461285}
12471286
@@ -1962,7 +2001,9 @@ size_t hashtableScanDefrag(hashtable *ht, size_t cursor, hashtableScanFunction f
19622001 * rehashing to prevent entries from moving around. It's allowed to insert and
19632002 * replace entries. Deleting entries is only allowed for the entry that was just
19642003 * returned by hashtableNext. Deleting other entries is possible, but doing so
1965- * can cause internal fragmentation, so don't.
2004+ * can cause internal fragmentation, so don't. The hash table itself can be
2005+ * safely deleted while safe iterators exist - they will be invalidated and
2006+ * subsequent calls to hashtableNext will return false.
19662007 *
19672008 * Guarantees for safe iterators:
19682009 *
@@ -1978,7 +2019,7 @@ size_t hashtableScanDefrag(hashtable *ht, size_t cursor, hashtableScanFunction f
19782019 * - Entries that are inserted during the iteration may or may not be returned
19792020 * by the iterator.
19802021 *
1981- * Call hashtableNext to fetch each entry. You must call hashtableResetIterator
2022+ * Call hashtableNext to fetch each entry. You must call hashtableCleanupIterator
19822023 * when you are done with the iterator.
19832024 */
19842025void hashtableInitIterator (hashtableIterator * iterator , hashtable * ht , uint8_t flags ) {
@@ -1988,22 +2029,31 @@ void hashtableInitIterator(hashtableIterator *iterator, hashtable *ht, uint8_t f
19882029 iter -> table = 0 ;
19892030 iter -> index = -1 ;
19902031 iter -> flags = flags ;
2032+ iter -> next_safe_iter = NULL ;
2033+ if (isSafe (iter ) && ht != NULL ) {
2034+ trackSafeIterator (iter );
2035+ }
19912036}
19922037
1993- /* Reinitializes the iterator for the provided hashtable while
1994- * preserving the flags from its previous initialization. */
1995- void hashtableReinitIterator (hashtableIterator * iterator , hashtable * ht ) {
2038+ /* Reinitializes the iterator to begin a new iteration of the provided hashtable
2039+ * while preserving the flags from its previous initialization. */
2040+ void hashtableRetargetIterator (hashtableIterator * iterator , hashtable * ht ) {
19962041 iter * iter = iteratorFromOpaque (iterator );
1997- hashtableInitIterator (iterator , ht , iter -> flags );
2042+ uint8_t flags = iter -> flags ;
2043+
2044+ hashtableCleanupIterator (iterator );
2045+ hashtableInitIterator (iterator , ht , flags );
19982046}
19992047
2000- /* Resets a stack-allocated iterator. */
2001- void hashtableResetIterator (hashtableIterator * iterator ) {
2048+ /* Performs required cleanup for a stack-allocated iterator. */
2049+ void hashtableCleanupIterator (hashtableIterator * iterator ) {
20022050 iter * iter = iteratorFromOpaque (iterator );
2051+ if (iter -> hashtable == NULL ) return ;
2052+
20032053 if (!(iter -> index == -1 && iter -> table == 0 )) {
20042054 if (isSafe (iter )) {
20052055 hashtableResumeRehashing (iter -> hashtable );
2006- assert (iter -> hashtable -> pause_rehash >= 0 );
2056+ untrackSafeIterator (iter );
20072057 } else {
20082058 assert (iter -> fingerprint == hashtableFingerprint (iter -> hashtable ));
20092059 }
@@ -2021,7 +2071,7 @@ hashtableIterator *hashtableCreateIterator(hashtable *ht, uint8_t flags) {
20212071/* Resets and frees the memory of an allocated iterator, i.e. one created using
20222072 * hashtableCreate(Safe)Iterator. */
20232073void hashtableReleaseIterator (hashtableIterator * iterator ) {
2024- hashtableResetIterator (iterator );
2074+ hashtableCleanupIterator (iterator );
20252075 iter * iter = iteratorFromOpaque (iterator );
20262076 zfree (iter );
20272077}
@@ -2030,6 +2080,9 @@ void hashtableReleaseIterator(hashtableIterator *iterator) {
20302080 * Returns false if there are no more entries. */
20312081bool hashtableNext (hashtableIterator * iterator , void * * elemptr ) {
20322082 iter * iter = iteratorFromOpaque (iterator );
2083+ /* Check if iterator has been invalidated */
2084+ if (iter -> hashtable == NULL ) return false;
2085+
20332086 while (1 ) {
20342087 if (iter -> index == -1 && iter -> table == 0 ) {
20352088 /* It's the first call to next. */
0 commit comments