Skip to content

Commit 21786db

Browse files
committed
Fix cache flush hazard in event trigger cache.
Bug spotted by Jeff Davis using -DCLOBBER_CACHE_ALWAYS.
1 parent 2751740 commit 21786db

File tree

1 file changed

+39
-16
lines changed

1 file changed

+39
-16
lines changed

src/backend/utils/cache/evtcache.c

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@
2929
#include "utils/snapmgr.h"
3030
#include "utils/syscache.h"
3131

32+
typedef enum
33+
{
34+
ETCS_NEEDS_REBUILD,
35+
ETCS_REBUILD_STARTED,
36+
ETCS_VALID
37+
} EventTriggerCacheStateType;
38+
3239
typedef struct
3340
{
3441
EventTriggerEvent event;
@@ -37,6 +44,7 @@ typedef struct
3744

3845
static HTAB *EventTriggerCache;
3946
static MemoryContext EventTriggerCacheContext;
47+
static EventTriggerCacheStateType EventTriggerCacheState = ETCS_NEEDS_REBUILD;
4048

4149
static void BuildEventTriggerCache(void);
4250
static void InvalidateEventCacheCallback(Datum arg,
@@ -55,7 +63,7 @@ EventCacheLookup(EventTriggerEvent event)
5563
{
5664
EventTriggerCacheEntry *entry;
5765

58-
if (EventTriggerCache == NULL)
66+
if (EventTriggerCacheState != ETCS_VALID)
5967
BuildEventTriggerCache();
6068
entry = hash_search(EventTriggerCache, &event, HASH_FIND, NULL);
6169
return entry != NULL ? entry->triggerlist : NULL;
@@ -77,12 +85,9 @@ BuildEventTriggerCache(void)
7785
if (EventTriggerCacheContext != NULL)
7886
{
7987
/*
80-
* The cache has been previously built, and subsequently invalidated,
81-
* and now we're trying to rebuild it. Normally, there won't be
82-
* anything in the context at this point, because the invalidation
83-
* will have already reset it. But if the previous attempt to rebuild
84-
* the cache failed, then there might be some junk lying around
85-
* that we want to reclaim.
88+
* Free up any memory already allocated in EventTriggerCacheContext.
89+
* This can happen either because a previous rebuild failed, or
90+
* because an invalidation happened before the rebuild was complete.
8691
*/
8792
MemoryContextResetAndDeleteChildren(EventTriggerCacheContext);
8893
}
@@ -109,12 +114,10 @@ BuildEventTriggerCache(void)
109114
/* Switch to correct memory context. */
110115
oldcontext = MemoryContextSwitchTo(EventTriggerCacheContext);
111116

112-
/*
113-
* Create a new hash table, but don't assign it to the global variable
114-
* until it accurately represents the state of the catalogs, so that
115-
* if we fail midway through this we won't end up with incorrect cache
116-
* contents.
117-
*/
117+
/* Prevent the memory context from being nuked while we're rebuilding. */
118+
EventTriggerCacheState = ETCS_REBUILD_STARTED;
119+
120+
/* Create new hash table. */
118121
MemSet(&ctl, 0, sizeof(ctl));
119122
ctl.keysize = sizeof(EventTriggerEvent);
120123
ctl.entrysize = sizeof(EventTriggerCacheEntry);
@@ -195,8 +198,17 @@ BuildEventTriggerCache(void)
195198
/* Restore previous memory context. */
196199
MemoryContextSwitchTo(oldcontext);
197200

198-
/* Cache is now valid. */
201+
/* Install new cache. */
199202
EventTriggerCache = cache;
203+
204+
/*
205+
* If the cache has been invalidated since we entered this routine, we
206+
* still use and return the cache we just finished constructing, to avoid
207+
* infinite loops, but we leave the cache marked stale so that we'll
208+
* rebuild it again on next access. Otherwise, we mark the cache valid.
209+
*/
210+
if (EventTriggerCacheState == ETCS_REBUILD_STARTED)
211+
EventTriggerCacheState = ETCS_VALID;
200212
}
201213

202214
/*
@@ -238,6 +250,17 @@ DecodeTextArrayToCString(Datum array, char ***cstringp)
238250
static void
239251
InvalidateEventCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
240252
{
241-
MemoryContextResetAndDeleteChildren(EventTriggerCacheContext);
242-
EventTriggerCache = NULL;
253+
/*
254+
* If the cache isn't valid, then there might be a rebuild in progress,
255+
* so we can't immediately blow it away. But it's advantageous to do
256+
* this when possible, so as to immediately free memory.
257+
*/
258+
if (EventTriggerCacheState == ETCS_VALID)
259+
{
260+
MemoryContextResetAndDeleteChildren(EventTriggerCacheContext);
261+
EventTriggerCache = NULL;
262+
}
263+
264+
/* Mark cache for rebuild. */
265+
EventTriggerCacheState = ETCS_NEEDS_REBUILD;
243266
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy