Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 1b8a219

Browse files
committed
Clean up non-reentrant interface for hash_seq/HashTableWalk, so that
starting a new hashtable search no longer clobbers any other search active anywhere in the system. Fix RelationCacheInvalidate() so that it will not crash or go into an infinite loop if invoked recursively, as for example by a second SI Reset message arriving while we are still processing a prior one.
1 parent 25d88e4 commit 1b8a219

File tree

8 files changed

+148
-157
lines changed

8 files changed

+148
-157
lines changed

src/backend/access/transam/xlogutils.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ _xl_init_rel_cache(void)
262262
}
263263

264264
static void
265-
_xl_remove_hash_entry(XLogRelDesc **edata, int dummy)
265+
_xl_remove_hash_entry(XLogRelDesc **edata, Datum dummy)
266266
{
267267
XLogRelCacheEntry *hentry;
268268
bool found;
@@ -328,7 +328,7 @@ XLogCloseRelationCache(void)
328328
if (!_xlrelarr)
329329
return;
330330

331-
HashTableWalk(_xlrelcache, (HashtFunc)_xl_remove_hash_entry, 0);
331+
HashTableWalk(_xlrelcache, (HashtFunc) _xl_remove_hash_entry, 0);
332332
hash_destroy(_xlrelcache);
333333

334334
free(_xlrelarr);

src/backend/lib/hasht.c

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $Header: /cvsroot/pgsql/src/backend/lib/Attic/hasht.c,v 1.13 2000/01/31 04:35:51 tgl Exp $
12+
* $Header: /cvsroot/pgsql/src/backend/lib/Attic/hasht.c,v 1.14 2001/01/02 04:33:15 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -21,23 +21,32 @@
2121
/* -----------------------------------
2222
* HashTableWalk
2323
*
24-
* call function on every element in hashtable
25-
* one extra argument (arg) may be supplied
24+
* call given function on every element in hashtable
25+
*
26+
* one extra argument (arg) may be supplied
27+
*
28+
* NOTE: it is allowed for the given function to delete the hashtable entry
29+
* it is passed. However, deleting any other element while the scan is
30+
* in progress is UNDEFINED (see hash_seq functions). Also, if elements are
31+
* added to the table while the scan is in progress, it is unspecified
32+
* whether they will be visited by the scan or not.
2633
* -----------------------------------
2734
*/
2835
void
29-
HashTableWalk(HTAB *hashtable, HashtFunc function, int arg)
36+
HashTableWalk(HTAB *hashtable, HashtFunc function, Datum arg)
3037
{
38+
HASH_SEQ_STATUS status;
3139
long *hashent;
3240
void *data;
3341
int keysize;
3442

43+
hash_seq_init(&status, hashtable);
3544
keysize = hashtable->hctl->keysize;
36-
hash_seq((HTAB *) NULL);
37-
while ((hashent = hash_seq(hashtable)) != (long *) TRUE)
45+
46+
while ((hashent = hash_seq_search(&status)) != (long *) TRUE)
3847
{
3948
if (hashent == NULL)
40-
elog(FATAL, "error in HashTableWalk.");
49+
elog(FATAL, "error in HashTableWalk");
4150

4251
/*
4352
* XXX the corresponding hash table insertion does NOT LONGALIGN

src/backend/storage/lmgr/lock.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.74 2000/12/22 00:51:54 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.75 2001/01/02 04:33:16 tgl Exp $
1212
*
1313
* NOTES
1414
* Outside modules can create a lock table and acquire/release
@@ -1772,6 +1772,7 @@ DumpAllLocks(void)
17721772
int lockmethod = DEFAULT_LOCKMETHOD;
17731773
LOCKMETHODTABLE *lockMethodTable;
17741774
HTAB *holderTable;
1775+
HASH_SEQ_STATUS status;
17751776

17761777
pid = getpid();
17771778
ShmemPIDLookup(pid, &location);
@@ -1791,8 +1792,8 @@ DumpAllLocks(void)
17911792
if (proc->waitLock)
17921793
LOCK_PRINT("DumpAllLocks: waiting on", proc->waitLock, 0);
17931794

1794-
hash_seq(NULL);
1795-
while ((holder = (HOLDER *) hash_seq(holderTable)) &&
1795+
hash_seq_init(&status, holderTable);
1796+
while ((holder = (HOLDER *) hash_seq_search(&status)) &&
17961797
(holder != (HOLDER *) TRUE))
17971798
{
17981799
HOLDER_PRINT("DumpAllLocks", holder);

src/backend/utils/cache/relcache.c

Lines changed: 60 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.121 2000/12/22 23:12:06 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.122 2001/01/02 04:33:19 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -253,10 +253,10 @@ static void RelationClearRelation(Relation relation, bool rebuildIt);
253253
#ifdef ENABLE_REINDEX_NAILED_RELATIONS
254254
static void RelationReloadClassinfo(Relation relation);
255255
#endif /* ENABLE_REINDEX_NAILED_RELATIONS */
256-
static void RelationFlushRelation(Relation *relationPtr,
257-
int skipLocalRelations);
256+
static void RelationFlushRelation(Relation relation);
258257
static Relation RelationNameCacheGetRelation(const char *relationName);
259-
static void RelationCacheAbortWalker(Relation *relationPtr, int dummy);
258+
static void RelationCacheInvalidateWalker(Relation *relationPtr, Datum listp);
259+
static void RelationCacheAbortWalker(Relation *relationPtr, Datum dummy);
260260
static void init_irels(void);
261261
static void write_irels(void);
262262

@@ -1639,14 +1639,12 @@ RelationClearRelation(Relation relation, bool rebuildIt)
16391639
* we'd be unable to recover.
16401640
*/
16411641
if (relation->rd_isnailed)
1642-
#ifdef ENABLE_REINDEX_NAILED_RELATIONS
16431642
{
1643+
#ifdef ENABLE_REINDEX_NAILED_RELATIONS
16441644
RelationReloadClassinfo(relation);
16451645
#endif /* ENABLE_REINDEX_NAILED_RELATIONS */
16461646
return;
1647-
#ifdef ENABLE_REINDEX_NAILED_RELATIONS
16481647
}
1649-
#endif /* ENABLE_REINDEX_NAILED_RELATIONS */
16501648

16511649
/*
16521650
* Remove relation from hash tables
@@ -1774,26 +1772,15 @@ RelationClearRelation(Relation relation, bool rebuildIt)
17741772
* RelationFlushRelation
17751773
*
17761774
* Rebuild the relation if it is open (refcount > 0), else blow it away.
1777-
* If skipLocalRelations is TRUE, xact-local relations are ignored
1778-
* (which is useful when processing SI cache reset, since xact-local
1779-
* relations could not be targets of notifications from other backends).
1780-
*
1781-
* The peculiar calling convention (pointer to pointer to relation)
1782-
* is needed so that we can use this routine as a hash table walker.
17831775
* --------------------------------
17841776
*/
17851777
static void
1786-
RelationFlushRelation(Relation *relationPtr,
1787-
int skipLocalRelations)
1778+
RelationFlushRelation(Relation relation)
17881779
{
1789-
Relation relation = *relationPtr;
17901780
bool rebuildIt;
17911781

17921782
if (relation->rd_myxactonly)
17931783
{
1794-
if (skipLocalRelations)
1795-
return; /* don't touch local rels if so commanded */
1796-
17971784
/*
17981785
* Local rels should always be rebuilt, not flushed; the relcache
17991786
* entry must live until RelationPurgeLocalRelation().
@@ -1878,7 +1865,7 @@ RelationIdInvalidateRelationCacheByRelationId(Oid relationId)
18781865
RelationIdCacheLookup(relationId, relation);
18791866

18801867
if (PointerIsValid(relation))
1881-
RelationFlushRelation(&relation, false);
1868+
RelationFlushRelation(relation);
18821869
}
18831870

18841871
#if NOT_USED
@@ -1900,35 +1887,12 @@ RelationFlushIndexes(Relation *r,
19001887
if (relation->rd_rel->relkind == RELKIND_INDEX && /* XXX style */
19011888
(!OidIsValid(accessMethodId) ||
19021889
relation->rd_rel->relam == accessMethodId))
1903-
RelationFlushRelation(&relation, false);
1890+
RelationFlushRelation(relation);
19041891
}
19051892

19061893
#endif
19071894

19081895

1909-
#if NOT_USED
1910-
void
1911-
RelationIdInvalidateRelationCacheByAccessMethodId(Oid accessMethodId)
1912-
{
1913-
1914-
/*
1915-
* 25 aug 1992: mao commented out the ht walk below. it should be
1916-
* doing the right thing, in theory, but flushing reldescs for index
1917-
* relations apparently doesn't work. we want to cut 4.0.1, and i
1918-
* don't want to introduce new bugs. this code never executed before,
1919-
* so i'm turning it off for now. after the release is cut, i'll fix
1920-
* this up.
1921-
*
1922-
* 20 nov 1999: this code has still never done anything, so I'm cutting
1923-
* the routine out of the system entirely. tgl
1924-
*/
1925-
1926-
HashTableWalk(RelationNameCache, (HashtFunc) RelationFlushIndexes,
1927-
accessMethodId);
1928-
}
1929-
1930-
#endif
1931-
19321896
/*
19331897
* RelationCacheInvalidate
19341898
* Blow away cached relation descriptors that have zero reference counts,
@@ -1938,12 +1902,59 @@ RelationIdInvalidateRelationCacheByAccessMethodId(Oid accessMethodId)
19381902
* so we do not touch transaction-local relations; they cannot be targets
19391903
* of cross-backend SI updates (and our own updates now go through a
19401904
* separate linked list that isn't limited by the SI message buffer size).
1905+
*
1906+
* We do this in two phases: the first pass deletes deletable items, and
1907+
* the second one rebuilds the rebuildable items. This is essential for
1908+
* safety, because HashTableWalk only copes with concurrent deletion of
1909+
* the element it is currently visiting. If a second SI overflow were to
1910+
* occur while we are walking the table, resulting in recursive entry to
1911+
* this routine, we could crash because the inner invocation blows away
1912+
* the entry next to be visited by the outer scan. But this way is OK,
1913+
* because (a) during the first pass we won't process any more SI messages,
1914+
* so HashTableWalk will complete safely; (b) during the second pass we
1915+
* only hold onto pointers to nondeletable entries.
19411916
*/
19421917
void
19431918
RelationCacheInvalidate(void)
19441919
{
1945-
HashTableWalk(RelationNameCache, (HashtFunc) RelationFlushRelation,
1946-
(int) true);
1920+
List *rebuildList = NIL;
1921+
List *l;
1922+
1923+
/* Phase 1 */
1924+
HashTableWalk(RelationNameCache,
1925+
(HashtFunc) RelationCacheInvalidateWalker,
1926+
PointerGetDatum(&rebuildList));
1927+
1928+
/* Phase 2: rebuild the items found to need rebuild in phase 1 */
1929+
foreach (l, rebuildList)
1930+
{
1931+
Relation relation = (Relation) lfirst(l);
1932+
1933+
RelationClearRelation(relation, true);
1934+
}
1935+
freeList(rebuildList);
1936+
}
1937+
1938+
static void
1939+
RelationCacheInvalidateWalker(Relation *relationPtr, Datum listp)
1940+
{
1941+
Relation relation = *relationPtr;
1942+
List **rebuildList = (List **) DatumGetPointer(listp);
1943+
1944+
/* We can ignore xact-local relations, since they are never SI targets */
1945+
if (relation->rd_myxactonly)
1946+
return;
1947+
1948+
if (RelationHasReferenceCountZero(relation))
1949+
{
1950+
/* Delete this entry immediately */
1951+
RelationClearRelation(relation, false);
1952+
}
1953+
else
1954+
{
1955+
/* Add entry to list of stuff to rebuild in second pass */
1956+
*rebuildList = lcons(relation, *rebuildList);
1957+
}
19471958
}
19481959

19491960
/*
@@ -1962,12 +1973,13 @@ RelationCacheInvalidate(void)
19621973
void
19631974
RelationCacheAbort(void)
19641975
{
1965-
HashTableWalk(RelationNameCache, (HashtFunc) RelationCacheAbortWalker,
1976+
HashTableWalk(RelationNameCache,
1977+
(HashtFunc) RelationCacheAbortWalker,
19661978
0);
19671979
}
19681980

19691981
static void
1970-
RelationCacheAbortWalker(Relation *relationPtr, int dummy)
1982+
RelationCacheAbortWalker(Relation *relationPtr, Datum dummy)
19711983
{
19721984
Relation relation = *relationPtr;
19731985

src/backend/utils/hash/dynahash.c

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/utils/hash/dynahash.c,v 1.32 2000/06/28 03:32:34 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/utils/hash/dynahash.c,v 1.33 2001/01/02 04:33:20 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -681,50 +681,54 @@ hash_search(HTAB *hashp,
681681
}
682682

683683
/*
684-
* hash_seq -- sequentially search through hash table and return
685-
* all the elements one by one, return NULL on error and
686-
* return TRUE in the end.
684+
* hash_seq_init/_search
685+
* Sequentially search through hash table and return
686+
* all the elements one by one, return NULL on error and
687+
* return (long *) TRUE in the end.
687688
*
689+
* NOTE: caller may delete the returned element before continuing the scan.
690+
* However, deleting any other element while the scan is in progress is
691+
* UNDEFINED (it might be the one that curIndex is pointing at!). Also,
692+
* if elements are added to the table while the scan is in progress, it is
693+
* unspecified whether they will be visited by the scan or not.
688694
*/
695+
void
696+
hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
697+
{
698+
status->hashp = hashp;
699+
status->curBucket = 0;
700+
status->curIndex = INVALID_INDEX;
701+
}
702+
689703
long *
690-
hash_seq(HTAB *hashp)
704+
hash_seq_search(HASH_SEQ_STATUS *status)
691705
{
692-
static long curBucket = 0;
693-
static BUCKET_INDEX curIndex;
694-
ELEMENT *curElem;
695-
long segment_num;
696-
long segment_ndx;
697-
SEGMENT segp;
698-
HHDR *hctl;
706+
HTAB *hashp = status->hashp;
707+
HHDR *hctl = hashp->hctl;
699708

700-
if (hashp == NULL)
709+
while (status->curBucket <= hctl->max_bucket)
701710
{
711+
long segment_num;
712+
long segment_ndx;
713+
SEGMENT segp;
702714

703-
/*
704-
* reset static state
705-
*/
706-
curBucket = 0;
707-
curIndex = INVALID_INDEX;
708-
return (long *) NULL;
709-
}
710-
711-
hctl = hashp->hctl;
712-
while (curBucket <= hctl->max_bucket)
713-
{
714-
if (curIndex != INVALID_INDEX)
715+
if (status->curIndex != INVALID_INDEX)
715716
{
716-
curElem = GET_BUCKET(hashp, curIndex);
717-
curIndex = curElem->next;
718-
if (curIndex == INVALID_INDEX) /* end of this bucket */
719-
++curBucket;
717+
/* Continuing scan of curBucket... */
718+
ELEMENT *curElem;
719+
720+
curElem = GET_BUCKET(hashp, status->curIndex);
721+
status->curIndex = curElem->next;
722+
if (status->curIndex == INVALID_INDEX) /* end of this bucket */
723+
++status->curBucket;
720724
return &(curElem->key);
721725
}
722726

723727
/*
724728
* initialize the search within this bucket.
725729
*/
726-
segment_num = curBucket >> hctl->sshift;
727-
segment_ndx = MOD(curBucket, hctl->ssize);
730+
segment_num = status->curBucket >> hctl->sshift;
731+
segment_ndx = MOD(status->curBucket, hctl->ssize);
728732

729733
/*
730734
* first find the right segment in the table directory.
@@ -742,10 +746,10 @@ hash_seq(HTAB *hashp)
742746
* directory of valid stuff. if there are elements in the bucket
743747
* chains that point to the freelist we're in big trouble.
744748
*/
745-
curIndex = segp[segment_ndx];
749+
status->curIndex = segp[segment_ndx];
746750

747-
if (curIndex == INVALID_INDEX) /* empty bucket */
748-
++curBucket;
751+
if (status->curIndex == INVALID_INDEX) /* empty bucket */
752+
++status->curBucket;
749753
}
750754

751755
return (long *) TRUE; /* out of buckets */

0 commit comments

Comments
 (0)