From b791a6af0c01673e7e6d982cb9b679cdfcd8e7a7 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Wed, 20 Jan 2021 23:16:07 +0200 Subject: [PATCH v8 2/3] Make resowners more easily extensible. Use a single array and hash, instead of one for each object kind. --- src/backend/access/common/tupdesc.c | 42 +- src/backend/jit/jit.c | 2 - src/backend/jit/llvm/llvmjit.c | 46 +- src/backend/storage/buffer/bufmgr.c | 47 +- src/backend/storage/buffer/localbuf.c | 4 +- src/backend/storage/file/fd.c | 44 +- src/backend/storage/ipc/dsm.c | 44 +- src/backend/storage/lmgr/lock.c | 2 +- src/backend/utils/cache/catcache.c | 74 +- src/backend/utils/cache/plancache.c | 45 +- src/backend/utils/cache/relcache.c | 41 +- src/backend/utils/resowner/README | 19 +- src/backend/utils/resowner/resowner.c | 1312 +++++++------------------ src/backend/utils/time/snapmgr.c | 38 +- src/common/cryptohash_openssl.c | 49 +- src/common/hmac_openssl.c | 47 +- src/include/storage/buf_internals.h | 9 + src/include/utils/catcache.h | 3 - src/include/utils/plancache.h | 2 + src/include/utils/resowner.h | 45 +- src/include/utils/resowner_private.h | 112 --- src/tools/pgindent/typedefs.list | 4 +- 22 files changed, 894 insertions(+), 1137 deletions(-) delete mode 100644 src/include/utils/resowner_private.h diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index 4c63bd4dc64..f2d4442c938 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -30,9 +30,27 @@ #include "utils/acl.h" #include "utils/builtins.h" #include "utils/datum.h" -#include "utils/resowner_private.h" +#include "utils/resowner.h" #include "utils/syscache.h" +/* ResourceOwner callbacks to hold tupledesc references */ +static void ResOwnerReleaseTupleDesc(Datum res); +static void ResOwnerPrintTupleDescLeakWarning(Datum res); + +static ResourceOwnerFuncs tupdesc_resowner_funcs = +{ + /* relcache references */ + .name = "tupdesc reference", + .phase = RESOURCE_RELEASE_AFTER_LOCKS, + .ReleaseResource = ResOwnerReleaseTupleDesc, + .PrintLeakWarning = ResOwnerPrintTupleDescLeakWarning +}; + +/* Convenience wrappers over ResourceOwnerRemember/Forget */ +#define ResourceOwnerRememberTupleDesc(owner, tupdesc) \ + ResourceOwnerRemember(owner, PointerGetDatum(tupdesc), &tupdesc_resowner_funcs) +#define ResourceOwnerForgetTupleDesc(owner, tupdesc) \ + ResourceOwnerForget(owner, PointerGetDatum(tupdesc), &tupdesc_resowner_funcs) /* * CreateTemplateTupleDesc @@ -367,7 +385,7 @@ IncrTupleDescRefCount(TupleDesc tupdesc) { Assert(tupdesc->tdrefcount >= 0); - ResourceOwnerEnlargeTupleDescs(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); tupdesc->tdrefcount++; ResourceOwnerRememberTupleDesc(CurrentResourceOwner, tupdesc); } @@ -910,3 +928,23 @@ BuildDescFromLists(List *names, List *types, List *typmods, List *collations) return desc; } + + +/* + * ResourceOwner callbacks + */ +static void +ResOwnerReleaseTupleDesc(Datum res) +{ + DecrTupleDescRefCount((TupleDesc) DatumGetPointer(res)); +} + +static void +ResOwnerPrintTupleDescLeakWarning(Datum res) +{ + TupleDesc tupdesc = (TupleDesc) DatumGetPointer(res); + + elog(WARNING, + "TupleDesc reference leak: TupleDesc %p (%u,%d) still referenced", + tupdesc, tupdesc->tdtypeid, tupdesc->tdtypmod); +} diff --git a/src/backend/jit/jit.c b/src/backend/jit/jit.c index 2da300e000d..1aa04d173b4 100644 --- a/src/backend/jit/jit.c +++ b/src/backend/jit/jit.c @@ -26,7 +26,6 @@ #include "jit/jit.h" #include "miscadmin.h" #include "utils/fmgrprotos.h" -#include "utils/resowner_private.h" /* GUCs */ bool jit_enabled = true; @@ -140,7 +139,6 @@ jit_release_context(JitContext *context) if (provider_successfully_loaded) provider.release_context(context); - ResourceOwnerForgetJIT(context->resowner, PointerGetDatum(context)); pfree(context); } diff --git a/src/backend/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c index df691cbf1c5..57e3144fae7 100644 --- a/src/backend/jit/llvm/llvmjit.c +++ b/src/backend/jit/llvm/llvmjit.c @@ -40,7 +40,7 @@ #include "portability/instr_time.h" #include "storage/ipc.h" #include "utils/memutils.h" -#include "utils/resowner_private.h" +#include "utils/resowner.h" /* Handle of a module emitted via ORC JIT */ typedef struct LLVMJitHandle @@ -121,8 +121,26 @@ static LLVMOrcLLJITRef llvm_create_jit_instance(LLVMTargetMachineRef tm); static char *llvm_error_message(LLVMErrorRef error); #endif /* LLVM_VERSION_MAJOR > 11 */ -PG_MODULE_MAGIC; +/* ResourceOwner callbacks to hold JitContexts */ +static void ResOwnerReleaseJitContext(Datum res); +static void ResOwnerPrintJitContextLeakWarning(Datum res); + +static ResourceOwnerFuncs jit_resowner_funcs = +{ + /* relcache references */ + .name = "LLVM JIT context", + .phase = RESOURCE_RELEASE_BEFORE_LOCKS, + .ReleaseResource = ResOwnerReleaseJitContext, + .PrintLeakWarning = ResOwnerPrintJitContextLeakWarning +}; + +/* Convenience wrappers over ResourceOwnerRemember/Forget */ +#define ResourceOwnerRememberJIT(owner, handle) \ + ResourceOwnerRemember(owner, PointerGetDatum(handle), &jit_resowner_funcs) +#define ResourceOwnerForgetJIT(owner, handle) \ + ResourceOwnerForget(owner, PointerGetDatum(handle), &jit_resowner_funcs) +PG_MODULE_MAGIC; /* * Initialize LLVM JIT provider. @@ -151,7 +169,7 @@ llvm_create_context(int jitFlags) llvm_session_initialize(); - ResourceOwnerEnlargeJIT(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); context = MemoryContextAllocZero(TopMemoryContext, sizeof(LLVMJitContext)); @@ -159,7 +177,7 @@ llvm_create_context(int jitFlags) /* ensure cleanup */ context->base.resowner = CurrentResourceOwner; - ResourceOwnerRememberJIT(CurrentResourceOwner, PointerGetDatum(context)); + ResourceOwnerRememberJIT(CurrentResourceOwner, context); return context; } @@ -221,6 +239,8 @@ llvm_release_context(JitContext *context) pfree(jit_handle); } + + ResourceOwnerForgetJIT(context->resowner, context); } /* @@ -1234,3 +1254,21 @@ llvm_error_message(LLVMErrorRef error) } #endif /* LLVM_VERSION_MAJOR > 11 */ + +/* + * ResourceOwner callbacks + */ +static void +ResOwnerReleaseJitContext(Datum res) +{ + jit_release_context((JitContext *) PointerGetDatum(res)); +} + +static void +ResOwnerPrintJitContextLeakWarning(Datum res) +{ + /* XXX: We used to not print these. Was that intentional? */ + JitContext *context = (JitContext *) PointerGetDatum(res); + + elog(WARNING, "JIT context leak: context %p still referenced", context); +} diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 2383e4cac08..9e55ad3f743 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -52,7 +52,7 @@ #include "utils/memdebug.h" #include "utils/ps_status.h" #include "utils/rel.h" -#include "utils/resowner_private.h" +#include "utils/resowner.h" #include "utils/timestamp.h" @@ -206,6 +206,18 @@ static PrivateRefCountEntry *GetPrivateRefCountEntry(Buffer buffer, bool do_move static inline int32 GetPrivateRefCount(Buffer buffer); static void ForgetPrivateRefCountEntry(PrivateRefCountEntry *ref); +/* ResourceOwner callbacks to hold buffer pins */ +static void ResOwnerReleaseBuffer(Datum res); +static void ResOwnerPrintBufferLeakWarning(Datum res); + +ResourceOwnerFuncs buffer_resowner_funcs = +{ + .name = "buffer", + .phase = RESOURCE_RELEASE_BEFORE_LOCKS, + .ReleaseResource = ResOwnerReleaseBuffer, + .PrintLeakWarning = ResOwnerPrintBufferLeakWarning +}; + /* * Ensure that the PrivateRefCountArray has sufficient space to store one more * entry. This has to be called before using NewPrivateRefCountEntry() to fill @@ -625,7 +637,7 @@ ReadRecentBuffer(RelFileNode rnode, ForkNumber forkNum, BlockNumber blockNum, Assert(BufferIsValid(recent_buffer)); - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); ReservePrivateRefCountEntry(); INIT_BUFFERTAG(tag, rnode, forkNum, blockNum); @@ -1166,7 +1178,7 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, * pin. */ ReservePrivateRefCountEntry(); - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1677,7 +1689,7 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); ref = GetPrivateRefCountEntry(b, true); @@ -1760,7 +1772,7 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * * As this function is called with the spinlock held, the caller has to * previously call ReservePrivateRefCountEntry() and - * ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + * ResourceOwnerEnlarge(CurrentResourceOwner); * * Currently, no callers of this function want to modify the buffer's * usage_count at all, so there's no need for a strategy parameter. @@ -2501,7 +2513,7 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3568,7 +3580,7 @@ FlushRelationBuffers(Relation rel) /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3663,7 +3675,7 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3718,7 +3730,7 @@ FlushDatabaseBuffers(Oid dbid) /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && @@ -3801,7 +3813,7 @@ void IncrBufferRefCount(Buffer buffer) { Assert(BufferIsPinned(buffer)); - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); if (BufferIsLocal(buffer)) LocalRefCount[-buffer - 1]++; else @@ -4848,3 +4860,18 @@ TestForOldSnapshot_impl(Snapshot snapshot, Relation relation) (errcode(ERRCODE_SNAPSHOT_TOO_OLD), errmsg("snapshot too old"))); } + +/* + * ResourceOwner callbacks + */ +static void +ResOwnerReleaseBuffer(Datum res) +{ + ReleaseBuffer(DatumGetInt32(res)); +} + +static void +ResOwnerPrintBufferLeakWarning(Datum res) +{ + PrintBufferLeakWarning(DatumGetInt32(res)); +} diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index f7c15ea8a44..3f22d7127b9 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -22,7 +22,7 @@ #include "storage/bufmgr.h" #include "utils/guc.h" #include "utils/memutils.h" -#include "utils/resowner_private.h" +#include "utils/resowner.h" /*#define LBDEBUG*/ @@ -124,7 +124,7 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, InitLocalBuffers(); /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c index a340a5f6afe..6a9ee97ea1e 100644 --- a/src/backend/storage/file/fd.c +++ b/src/backend/storage/file/fd.c @@ -99,7 +99,7 @@ #include "storage/fd.h" #include "storage/ipc.h" #include "utils/guc.h" -#include "utils/resowner_private.h" +#include "utils/resowner.h" /* Define PG_FLUSH_DATA_WORKS if we have an implementation for pg_flush_data */ #if defined(HAVE_SYNC_FILE_RANGE) @@ -345,6 +345,24 @@ static void unlink_if_exists_fname(const char *fname, bool isdir, int elevel); static int fsync_parent_path(const char *fname, int elevel); +/* ResourceOwner callbacks to hold virtual file descriptors */ +static void ResOwnerReleaseFile(Datum res); +static void ResOwnerPrintFileLeakWarning(Datum res); + +static ResourceOwnerFuncs file_resowner_funcs = +{ + .name = "File", + .phase = RESOURCE_RELEASE_AFTER_LOCKS, + .ReleaseResource = ResOwnerReleaseFile, + .PrintLeakWarning = ResOwnerPrintFileLeakWarning +}; + +/* Convenience wrappers over ResourceOwnerRemember/Forget */ +#define ResourceOwnerRememberFile(owner, file) \ + ResourceOwnerRemember(owner, Int32GetDatum(file), &file_resowner_funcs) +#define ResourceOwnerForgetFile(owner, file) \ + ResourceOwnerForget(owner, Int32GetDatum(file), &file_resowner_funcs) + /* * pg_fsync --- do fsync with or without writethrough */ @@ -1449,7 +1467,7 @@ ReportTemporaryFileUsage(const char *path, off_t size) /* * Called to register a temporary file for automatic close. - * ResourceOwnerEnlargeFiles(CurrentResourceOwner) must have been called + * ResourceOwnerEnlarge(CurrentResourceOwner) must have been called * before the file was opened. */ static void @@ -1631,7 +1649,7 @@ OpenTemporaryFile(bool interXact) * open it, if we'll be registering it below. */ if (!interXact) - ResourceOwnerEnlargeFiles(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); /* * If some temp tablespace(s) have been given to us, try to use the next @@ -1761,7 +1779,7 @@ PathNameCreateTemporaryFile(const char *path, bool error_on_failure) { File file; - ResourceOwnerEnlargeFiles(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); /* * Open the file. Note: we don't use O_EXCL, in case there is an orphaned @@ -1799,7 +1817,7 @@ PathNameOpenTemporaryFile(const char *path, int mode) { File file; - ResourceOwnerEnlargeFiles(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); file = PathNameOpenFile(path, mode | PG_BINARY); @@ -3779,3 +3797,19 @@ pg_pwritev_with_retry(int fd, const struct iovec *iov, int iovcnt, off_t offset) return sum; } + +/* + * ResourceOwner callbacks + */ +static void +ResOwnerReleaseFile(Datum res) +{ + FileClose((File) DatumGetInt32(res)); +} + +static void +ResOwnerPrintFileLeakWarning(Datum res) +{ + elog(WARNING, "temporary file leak: File %d still referenced", + DatumGetInt32(res)); +} diff --git a/src/backend/storage/ipc/dsm.c b/src/backend/storage/ipc/dsm.c index b461a5f7e96..77b6e58fd9d 100644 --- a/src/backend/storage/ipc/dsm.c +++ b/src/backend/storage/ipc/dsm.c @@ -37,13 +37,15 @@ #include "miscadmin.h" #include "port/pg_bitutils.h" #include "storage/dsm.h" +#include "storage/fd.h" #include "storage/ipc.h" #include "storage/lwlock.h" #include "storage/pg_shmem.h" +#include "storage/shmem.h" #include "utils/freepage.h" #include "utils/guc.h" #include "utils/memutils.h" -#include "utils/resowner_private.h" +#include "utils/resowner.h" #define PG_DYNSHMEM_CONTROL_MAGIC 0x9a503d32 @@ -139,6 +141,25 @@ static dsm_control_header *dsm_control; static Size dsm_control_mapped_size = 0; static void *dsm_control_impl_private = NULL; + +/* ResourceOwner callbacks to hold DSM segments */ +static void ResOwnerReleaseDSM(Datum res); +static void ResOwnerPrintDSMLeakWarning(Datum res); + +static ResourceOwnerFuncs dsm_resowner_funcs = +{ + .name = "dynamic shared memory segment", + .phase = RESOURCE_RELEASE_BEFORE_LOCKS, + .ReleaseResource = ResOwnerReleaseDSM, + .PrintLeakWarning = ResOwnerPrintDSMLeakWarning +}; + +/* Convenience wrappers over ResourceOwnerRemember/Forget */ +#define ResourceOwnerRememberDSM(owner, seg) \ + ResourceOwnerRemember(owner, PointerGetDatum(seg), &dsm_resowner_funcs) +#define ResourceOwnerForgetDSM(owner, seg) \ + ResourceOwnerForget(owner, PointerGetDatum(seg), &dsm_resowner_funcs) + /* * Start up the dynamic shared memory system. * @@ -900,7 +921,7 @@ void dsm_unpin_mapping(dsm_segment *seg) { Assert(seg->resowner == NULL); - ResourceOwnerEnlargeDSMs(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); seg->resowner = CurrentResourceOwner; ResourceOwnerRememberDSM(seg->resowner, seg); } @@ -1167,7 +1188,7 @@ dsm_create_descriptor(void) dsm_segment *seg; if (CurrentResourceOwner) - ResourceOwnerEnlargeDSMs(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); seg = MemoryContextAlloc(TopMemoryContext, sizeof(dsm_segment)); dlist_push_head(&dsm_segment_list, &seg->node); @@ -1246,3 +1267,20 @@ is_main_region_dsm_handle(dsm_handle handle) { return handle & 1; } + +/* + * ResourceOwner callbacks + */ +static void +ResOwnerReleaseDSM(Datum res) +{ + dsm_detach((dsm_segment *) DatumGetPointer(res)); +} +static void +ResOwnerPrintDSMLeakWarning(Datum res) +{ + dsm_segment *seg = (dsm_segment *) res; + + elog(WARNING, "dynamic shared memory leak: segment %u still referenced", + dsm_segment_handle(seg)); +} diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c index 108b4d90238..719612da04d 100644 --- a/src/backend/storage/lmgr/lock.c +++ b/src/backend/storage/lmgr/lock.c @@ -47,7 +47,7 @@ #include "storage/standby.h" #include "utils/memutils.h" #include "utils/ps_status.h" -#include "utils/resowner_private.h" +#include "utils/resowner.h" /* This configuration variable is used to set the lock table size */ diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 13eed587601..ed046db57ec 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -31,12 +31,13 @@ #endif #include "storage/lmgr.h" #include "utils/builtins.h" +#include "utils/catcache.h" #include "utils/datum.h" #include "utils/fmgroids.h" #include "utils/inval.h" #include "utils/memutils.h" #include "utils/rel.h" -#include "utils/resowner_private.h" +#include "utils/resowner.h" #include "utils/syscache.h" @@ -104,6 +105,42 @@ static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos, * internal support functions */ +/* ResourceOwner callbacks to hold catcache references */ + +static void ResOwnerReleaseCatCache(Datum res); +static void ResOwnerPrintCatCacheLeakWarning(Datum res); +static void ResOwnerReleaseCatCacheList(Datum res); +static void ResOwnerPrintCatCacheListLeakWarning(Datum res); + +static ResourceOwnerFuncs catcache_resowner_funcs = +{ + /* catcache references */ + .name = "catcache reference", + .phase = RESOURCE_RELEASE_AFTER_LOCKS, + .ReleaseResource = ResOwnerReleaseCatCache, + .PrintLeakWarning = ResOwnerPrintCatCacheLeakWarning +}; + +static ResourceOwnerFuncs catlistref_resowner_funcs = +{ + /* catcache-list pins */ + .name = "catcache list reference", + .phase = RESOURCE_RELEASE_AFTER_LOCKS, + .ReleaseResource = ResOwnerReleaseCatCacheList, + .PrintLeakWarning = ResOwnerPrintCatCacheListLeakWarning +}; + +/* Convenience wrappers over ResourceOwnerRemember/Forget */ +#define ResourceOwnerRememberCatCacheRef(owner, tuple) \ + ResourceOwnerRemember(owner, PointerGetDatum(tuple), &catcache_resowner_funcs) +#define ResourceOwnerForgetCatCacheRef(owner, tuple) \ + ResourceOwnerForget(owner, PointerGetDatum(tuple), &catcache_resowner_funcs) +#define ResourceOwnerRememberCatCacheListRef(owner, list) \ + ResourceOwnerRemember(owner, PointerGetDatum(list), &catlistref_resowner_funcs) +#define ResourceOwnerForgetCatCacheListRef(owner, list) \ + ResourceOwnerForget(owner, PointerGetDatum(list), &catlistref_resowner_funcs) + + /* * Hash and equality functions for system types that are used as cache key * fields. In some cases, we just call the regular SQL-callable functions for @@ -1272,7 +1309,7 @@ SearchCatCacheInternal(CatCache *cache, */ if (!ct->negative) { - ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); ct->refcount++; ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple); @@ -1373,7 +1410,7 @@ SearchCatCacheMiss(CatCache *cache, hashValue, hashIndex, false); /* immediately set the refcount to 1 */ - ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); ct->refcount++; ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple); break; /* assume only one match */ @@ -1585,7 +1622,7 @@ SearchCatCacheList(CatCache *cache, dlist_move_head(&cache->cc_lists, &cl->cache_elem); /* Bump the list's refcount and return it */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); cl->refcount++; ResourceOwnerRememberCatCacheListRef(CurrentResourceOwner, cl); @@ -1697,7 +1734,7 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); /* Make sure the resource owner has room to remember this entry. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); @@ -2071,14 +2108,19 @@ PrepareToInvalidateCacheTuple(Relation relation, } } - /* - * Subroutines for warning about reference leaks. These are exported so - * that resowner.c can call them. + * ResourceOwner callbacks */ -void -PrintCatCacheLeakWarning(HeapTuple tuple) +static void +ResOwnerReleaseCatCache(Datum res) +{ + ReleaseCatCache((HeapTuple) DatumGetPointer(res)); +} + +static void +ResOwnerPrintCatCacheLeakWarning(Datum res) { + HeapTuple tuple = (HeapTuple) DatumGetPointer(res); CatCTup *ct = (CatCTup *) (((char *) tuple) - offsetof(CatCTup, tuple)); @@ -2092,9 +2134,17 @@ PrintCatCacheLeakWarning(HeapTuple tuple) ct->refcount); } -void -PrintCatCacheListLeakWarning(CatCList *list) +static void +ResOwnerReleaseCatCacheList(Datum res) { + ReleaseCatCacheList((CatCList *) DatumGetPointer(res)); +} + +static void +ResOwnerPrintCatCacheListLeakWarning(Datum res) +{ + CatCList *list = (CatCList *) DatumGetPointer(res); + elog(WARNING, "cache reference leak: cache %s (%d), list %p has count %d", list->my_cache->cc_relname, list->my_cache->id, list, list->refcount); diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c index 6767eae8f20..a10362629f8 100644 --- a/src/backend/utils/cache/plancache.c +++ b/src/backend/utils/cache/plancache.c @@ -69,7 +69,7 @@ #include "tcop/utility.h" #include "utils/inval.h" #include "utils/memutils.h" -#include "utils/resowner_private.h" +#include "utils/resowner.h" #include "utils/rls.h" #include "utils/snapmgr.h" #include "utils/syscache.h" @@ -115,6 +115,26 @@ static void PlanCacheRelCallback(Datum arg, Oid relid); static void PlanCacheObjectCallback(Datum arg, int cacheid, uint32 hashvalue); static void PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue); +/* ResourceOwner callbacks to track plancache references */ +static void ResOwnerReleaseCachedPlan(Datum res); +static void ResOwnerPrintPlanCacheLeakWarning(Datum res); + +/* this is exported for ResourceOwnerReleaseAllPlanCacheRefs() */ +ResourceOwnerFuncs planref_resowner_funcs = +{ + .name = "plancache reference", + .phase = RESOURCE_RELEASE_AFTER_LOCKS, + .ReleaseResource = ResOwnerReleaseCachedPlan, + .PrintLeakWarning = ResOwnerPrintPlanCacheLeakWarning +}; + +/* Convenience wrappers over ResourceOwnerRemember/Forget */ +#define ResourceOwnerRememberPlanCacheRef(owner, plan) \ + ResourceOwnerRemember(owner, PointerGetDatum(plan), &planref_resowner_funcs) +#define ResourceOwnerForgetPlanCacheRef(owner, plan) \ + ResourceOwnerForget(owner, PointerGetDatum(plan), &planref_resowner_funcs) + + /* GUC parameter */ int plan_cache_mode; @@ -1229,7 +1249,7 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams, /* Flag the plan as in use by caller */ if (owner) - ResourceOwnerEnlargePlanCacheRefs(owner); + ResourceOwnerEnlarge(owner); plan->refcount++; if (owner) ResourceOwnerRememberPlanCacheRef(owner, plan); @@ -1392,7 +1412,7 @@ CachedPlanAllowsSimpleValidityCheck(CachedPlanSource *plansource, /* Bump refcount if requested. */ if (owner) { - ResourceOwnerEnlargePlanCacheRefs(owner); + ResourceOwnerEnlarge(owner); plan->refcount++; ResourceOwnerRememberPlanCacheRef(owner, plan); } @@ -1451,7 +1471,7 @@ CachedPlanIsSimplyValid(CachedPlanSource *plansource, CachedPlan *plan, /* It's still good. Bump refcount if requested. */ if (owner) { - ResourceOwnerEnlargePlanCacheRefs(owner); + ResourceOwnerEnlarge(owner); plan->refcount++; ResourceOwnerRememberPlanCacheRef(owner, plan); } @@ -2205,3 +2225,20 @@ ResetPlanCache(void) cexpr->is_valid = false; } } + +/* + * ResourceOwner callbacks + */ + +static void +ResOwnerReleaseCachedPlan(Datum res) +{ + ReleaseCachedPlan((CachedPlan *) DatumGetPointer(res), CurrentResourceOwner); +} + +static void +ResOwnerPrintPlanCacheLeakWarning(Datum res) +{ + elog(WARNING, "plancache reference leak: plan %p not closed", + DatumGetPointer(res)); +} diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 13d9994af3e..90d2892489f 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -77,13 +77,14 @@ #include "storage/smgr.h" #include "utils/array.h" #include "utils/builtins.h" +#include "utils/catcache.h" #include "utils/datum.h" #include "utils/fmgroids.h" #include "utils/inval.h" #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/relmapper.h" -#include "utils/resowner_private.h" +#include "utils/resowner.h" #include "utils/snapmgr.h" #include "utils/syscache.h" @@ -2056,6 +2057,24 @@ RelationIdGetRelation(Oid relationId) * ---------------------------------------------------------------- */ +/* ResourceOwner callbacks to track relcache references */ +static void ResOwnerReleaseRelation(Datum res); +static void ResOwnerPrintRelCacheLeakWarning(Datum res); + +static ResourceOwnerFuncs relref_resowner_funcs = +{ + .name = "relcache reference", + .phase = RESOURCE_RELEASE_BEFORE_LOCKS, + .ReleaseResource = ResOwnerReleaseRelation, + .PrintLeakWarning = ResOwnerPrintRelCacheLeakWarning +}; + +/* Convenience wrappers over ResourceOwnerRemember/Forget */ +#define ResourceOwnerRememberRelationRef(owner, rel) \ + ResourceOwnerRemember(owner, PointerGetDatum(rel), &relref_resowner_funcs) +#define ResourceOwnerForgetRelationRef(owner, rel) \ + ResourceOwnerForget(owner, PointerGetDatum(rel), &relref_resowner_funcs) + /* * RelationIncrementReferenceCount * Increments relation reference count. @@ -2067,7 +2086,7 @@ RelationIdGetRelation(Oid relationId) void RelationIncrementReferenceCount(Relation rel) { - ResourceOwnerEnlargeRelationRefs(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); rel->rd_refcnt += 1; if (!IsBootstrapProcessingMode()) ResourceOwnerRememberRelationRef(CurrentResourceOwner, rel); @@ -6546,3 +6565,21 @@ unlink_initfile(const char *initfilename, int elevel) initfilename))); } } + +/* + * ResourceOwner callbacks + */ +static void +ResOwnerPrintRelCacheLeakWarning(Datum res) +{ + Relation rel = (Relation) res; + + elog(WARNING, "relcache reference leak: relation \"%s\" not closed", + RelationGetRelationName(rel)); +} + +static void +ResOwnerReleaseRelation(Datum res) +{ + RelationClose((Relation) res); +} diff --git a/src/backend/utils/resowner/README b/src/backend/utils/resowner/README index 2998f6bb362..4ed13afa101 100644 --- a/src/backend/utils/resowner/README +++ b/src/backend/utils/resowner/README @@ -60,13 +60,18 @@ subtransaction or portal. Therefore, the "release" operation on a child ResourceOwner transfers lock ownership to the parent instead of actually releasing the lock, if isCommit is true. -Currently, ResourceOwners contain direct support for recording ownership of -buffer pins, lmgr locks, and catcache, relcache, plancache, tupdesc, and -snapshot references. Other objects can be associated with a ResourceOwner by -recording the address of the owning ResourceOwner in such an object. There is -an API for other modules to get control during ResourceOwner release, so that -they can scan their own data structures to find the objects that need to be -deleted. +ResourceOwner can record ownership of many different kinds of resources. +As of this writing, it's used internally for buffer pins, lmgr locks, and +catcache, relcache, plancache, tupdesc, snapshot, DSM and JIT context +references. ResourceOwner treats all resources the same, and extensions +can define new kinds of resources by filling in a ResourceOwnerFuncs +struct with a suitable callback functions. + +There is also an API for other modules to get control during ResourceOwner +release, so that they can scan their own data structures to find the objects +that need to be deleted. This used to be the only way to register new kinds +of objects with a resource owner; nowadays it easier to write custom +ResourceOwnerFuncs callbacks. Whenever we are inside a transaction, the global variable CurrentResourceOwner shows which resource owner should be assigned diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c index e24f00f0601..d62b1495dd3 100644 --- a/src/backend/utils/resowner/resowner.c +++ b/src/backend/utils/resowner/resowner.c @@ -20,76 +20,45 @@ */ #include "postgres.h" -#include "common/cryptohash.h" #include "common/hashfn.h" -#include "common/hmac.h" -#include "jit/jit.h" -#include "storage/bufmgr.h" #include "storage/ipc.h" #include "storage/predicate.h" #include "storage/proc.h" #include "utils/memutils.h" -#include "utils/rel.h" -#include "utils/resowner_private.h" -#include "utils/snapmgr.h" - - -/* - * All resource IDs managed by this code are required to fit into a Datum, - * which is fine since they are generally pointers or integers. - * - * Provide Datum conversion macros for a couple of things that are really - * just "int". - */ -#define FileGetDatum(file) Int32GetDatum(file) -#define DatumGetFile(datum) ((File) DatumGetInt32(datum)) -#define BufferGetDatum(buffer) Int32GetDatum(buffer) -#define DatumGetBuffer(datum) ((Buffer) DatumGetInt32(datum)) +#include "utils/plancache.h" +#include "utils/resowner.h" /* - * ResourceArray is a common structure for storing all types of resource IDs. - * - * We manage small sets of resource IDs by keeping them in a simple array: - * itemsarr[k] holds an ID, for 0 <= k < nitems <= maxitems = capacity. - * - * If a set grows large, we switch over to using open-addressing hashing. - * Then, itemsarr[] is a hash table of "capacity" slots, with each - * slot holding either an ID or "invalidval". nitems is the number of valid - * items present; if it would exceed maxitems, we enlarge the array and - * re-hash. In this mode, maxitems should be rather less than capacity so - * that we don't waste too much time searching for empty slots. + * ResourceElem represents a reference associated with a resource owner. * - * In either mode, lastidx remembers the location of the last item inserted - * or returned by GetAny; this speeds up searches in ResourceArrayRemove. + * All objects managed by this code are required to fit into a Datum, + * which is fine since they are generally pointers or integers. */ -typedef struct ResourceArray +typedef struct ResourceElem { - Datum *itemsarr; /* buffer for storing values */ - Datum invalidval; /* value that is considered invalid */ - uint32 capacity; /* allocated length of itemsarr[] */ - uint32 nitems; /* how many items are stored in items array */ - uint32 maxitems; /* current limit on nitems before enlarging */ - uint32 lastidx; /* index of last item returned by GetAny */ -} ResourceArray; + Datum item; + ResourceOwnerFuncs *kind; +} ResourceElem; /* - * Initially allocated size of a ResourceArray. Must be power of two since - * we'll use (arraysize - 1) as mask for hashing. + * Size of the small fixed-size array to hold most-recently remembered resources. */ -#define RESARRAY_INIT_SIZE 16 +#define RESOWNER_ARRAY_SIZE 8 /* - * When to switch to hashing vs. simple array logic in a ResourceArray. + * Initially allocated size of a ResourceOwner's hash. Must be power of two since + * we'll use (capacity - 1) as mask for hashing. */ -#define RESARRAY_MAX_ARRAY 64 -#define RESARRAY_IS_ARRAY(resarr) ((resarr)->capacity <= RESARRAY_MAX_ARRAY) +#define RESOWNER_HASH_INIT_SIZE 32 /* - * How many items may be stored in a resource array of given capacity. + * How many items may be stored in a hash of given capacity. * When this number is reached, we must resize. */ -#define RESARRAY_MAX_ITEMS(capacity) \ - ((capacity) <= RESARRAY_MAX_ARRAY ? (capacity) : (capacity)/4 * 3) +#define RESOWNER_HASH_MAX_ITEMS(capacity) ((capacity)/4 * 3) + +StaticAssertDecl(RESOWNER_HASH_MAX_ITEMS(RESOWNER_HASH_INIT_SIZE) > RESOWNER_ARRAY_SIZE, + "initial hash size too small compared to array size"); /* * To speed up bulk releasing or reassigning locks from a resource owner to @@ -119,24 +88,33 @@ typedef struct ResourceOwnerData ResourceOwner nextchild; /* next child of same parent */ const char *name; /* name (just for debugging) */ - /* We have built-in support for remembering: */ - ResourceArray bufferarr; /* owned buffers */ - ResourceArray catrefarr; /* catcache references */ - ResourceArray catlistrefarr; /* catcache-list pins */ - ResourceArray relrefarr; /* relcache references */ - ResourceArray planrefarr; /* plancache references */ - ResourceArray tupdescarr; /* tupdesc references */ - ResourceArray snapshotarr; /* snapshot references */ - ResourceArray filearr; /* open temporary files */ - ResourceArray dsmarr; /* dynamic shmem segments */ - ResourceArray jitarr; /* JIT contexts */ - ResourceArray cryptohasharr; /* cryptohash contexts */ - ResourceArray hmacarr; /* HMAC contexts */ + /* + * These structs keep track of the objects registered with this owner. + * + * We manage a small set of references by keeping them in a simple array. + * When the array gets full, all the elements in the array are moved to a + * hash table. This way, the array always contains a few most recently + * remembered references. To find a particular reference, you need to + * search both the array and the hash table. + */ + ResourceElem arr[RESOWNER_ARRAY_SIZE]; + uint32 narr; /* how many items are stored in the array */ + + /* + * The hash table. Uses open-addressing. 'nhash' is the number of items + * present; if it would exceed 'grow_at', we enlarge it and re-hash. + * 'grow_at' should be rather less than 'capacity' so that we don't waste + * too much time searching for empty slots. + */ + ResourceElem *hash; + uint32 nhash; /* how many items are stored in the hash */ + uint32 capacity; /* allocated length of hash[] */ + uint32 grow_at; /* grow hash when reach this */ /* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */ int nlocks; /* number of owned locks */ LOCALLOCK *locks[MAX_RESOWNER_LOCKS]; /* list of owned locks */ -} ResourceOwnerData; +} ResourceOwnerData; /***************************************************************************** @@ -148,6 +126,18 @@ ResourceOwner CurTransactionResourceOwner = NULL; ResourceOwner TopTransactionResourceOwner = NULL; ResourceOwner AuxProcessResourceOwner = NULL; +/* #define RESOWNER_STATS */ +/* #define RESOWNER_TRACE */ + +#ifdef RESOWNER_STATS +static int narray_lookups = 0; +static int nhash_lookups = 0; +#endif + +#ifdef RESOWNER_TRACE +static int resowner_trace_counter = 0; +#endif + /* * List of add-on callbacks for resource releasing */ @@ -162,298 +152,315 @@ static ResourceReleaseCallbackItem *ResourceRelease_callbacks = NULL; /* Internal routines */ -static void ResourceArrayInit(ResourceArray *resarr, Datum invalidval); -static void ResourceArrayEnlarge(ResourceArray *resarr); -static void ResourceArrayAdd(ResourceArray *resarr, Datum value); -static bool ResourceArrayRemove(ResourceArray *resarr, Datum value); -static bool ResourceArrayGetAny(ResourceArray *resarr, Datum *value); -static void ResourceArrayFree(ResourceArray *resarr); static void ResourceOwnerReleaseInternal(ResourceOwner owner, ResourceReleasePhase phase, bool isCommit, bool isTopLevel); static void ReleaseAuxProcessResourcesCallback(int code, Datum arg); -static void PrintRelCacheLeakWarning(Relation rel); -static void PrintPlanCacheLeakWarning(CachedPlan *plan); -static void PrintTupleDescLeakWarning(TupleDesc tupdesc); -static void PrintSnapshotLeakWarning(Snapshot snapshot); -static void PrintFileLeakWarning(File file); -static void PrintDSMLeakWarning(dsm_segment *seg); -static void PrintCryptoHashLeakWarning(Datum handle); -static void PrintHMACLeakWarning(Datum handle); /***************************************************************************** * INTERNAL ROUTINES * *****************************************************************************/ +static inline uint32 +hash_resource_elem(Datum value, ResourceOwnerFuncs *kind) +{ + Datum data[2]; + + data[0] = value; + data[1] = PointerGetDatum(kind); + + return hash_bytes((unsigned char *) &data, 2 * SIZEOF_DATUM); +} -/* - * Initialize a ResourceArray - */ static void -ResourceArrayInit(ResourceArray *resarr, Datum invalidval) +ResourceOwnerAddToHash(ResourceOwner owner, Datum value, ResourceOwnerFuncs *kind) { - /* Assert it's empty */ - Assert(resarr->itemsarr == NULL); - Assert(resarr->capacity == 0); - Assert(resarr->nitems == 0); - Assert(resarr->maxitems == 0); - /* Remember the appropriate "invalid" value */ - resarr->invalidval = invalidval; - /* We don't allocate any storage until needed */ + /* Insert into first free slot at or after hash location. */ + uint32 mask = owner->capacity - 1; + uint32 idx; + + idx = hash_resource_elem(value, kind) & mask; + for (;;) + { + if (owner->hash[idx].kind == NULL) + break; + idx = (idx + 1) & mask; + } + owner->hash[idx].item = value; + owner->hash[idx].kind = kind; + owner->nhash++; } /* - * Make sure there is room for at least one more resource in an array. - * - * This is separate from actually inserting a resource because if we run out - * of memory, it's critical to do so *before* acquiring the resource. + * Call the ReleaseResource callback on entries with given 'phase'. */ static void -ResourceArrayEnlarge(ResourceArray *resarr) +ResourceOwnerReleaseAll(ResourceOwner owner, ResourceReleasePhase phase, + bool printLeakWarnings) { - uint32 i, - oldcap, - newcap; - Datum *olditemsarr; - Datum *newitemsarr; - - if (resarr->nitems < resarr->maxitems) - return; /* no work needed */ + bool found; + int capacity; - olditemsarr = resarr->itemsarr; - oldcap = resarr->capacity; - - /* Double the capacity of the array (capacity must stay a power of 2!) */ - newcap = (oldcap > 0) ? oldcap * 2 : RESARRAY_INIT_SIZE; - newitemsarr = (Datum *) MemoryContextAlloc(TopMemoryContext, - newcap * sizeof(Datum)); - for (i = 0; i < newcap; i++) - newitemsarr[i] = resarr->invalidval; + /* First handle all the entries in the array. */ + do + { + found = false; + for (int i = 0; i < owner->narr; i++) + { + if (owner->arr[i].kind->phase == phase) + { + Datum value = owner->arr[i].item; + ResourceOwnerFuncs *kind = owner->arr[i].kind; - /* We assume we can't fail below this point, so OK to scribble on resarr */ - resarr->itemsarr = newitemsarr; - resarr->capacity = newcap; - resarr->maxitems = RESARRAY_MAX_ITEMS(newcap); - resarr->nitems = 0; + if (printLeakWarnings) + kind->PrintLeakWarning(value); + kind->ReleaseResource(value); + found = true; + } + } - if (olditemsarr != NULL) - { /* - * Transfer any pre-existing entries into the new array; they don't - * necessarily go where they were before, so this simple logic is the - * best way. Note that if we were managing the set as a simple array, - * the entries after nitems are garbage, but that shouldn't matter - * because we won't get here unless nitems was equal to oldcap. + * If any resources were released, check again because some of the + * elements might have been moved by the callbacks. We don't want to + * miss them. */ - for (i = 0; i < oldcap; i++) + } while (found && owner->narr > 0); + + /* Ok, the array has now been handled. Then the hash */ + do + { + capacity = owner->capacity; + for (int idx = 0; idx < capacity; idx++) { - if (olditemsarr[i] != resarr->invalidval) - ResourceArrayAdd(resarr, olditemsarr[i]); + while (owner->hash[idx].kind != NULL && + owner->hash[idx].kind->phase == phase) + { + Datum value = owner->hash[idx].item; + ResourceOwnerFuncs *kind = owner->hash[idx].kind; + + if (printLeakWarnings) + kind->PrintLeakWarning(value); + kind->ReleaseResource(value); + + /* + * If the resource is remembered more than once in this + * resource owner, the ReleaseResource callback might've + * released a different copy of it. Because of that, loop to + * check the same index again. + */ + } } - /* And release old array. */ - pfree(olditemsarr); - } - - Assert(resarr->nitems < resarr->maxitems); + /* + * It's possible that the callbacks acquired more resources, causing + * the hash table to grow and the existing entries to be moved around. + * If that happened, scan the hash table again, so that we don't miss + * entries that were moved. (XXX: I'm not sure if any of the callbacks + * actually do that, but this is cheap to check, and better safe than + * sorry.) + */ + Assert(owner->capacity >= capacity); + } while (capacity != owner->capacity); } + +/***************************************************************************** + * EXPORTED ROUTINES * + *****************************************************************************/ + + /* - * Add a resource to ResourceArray + * ResourceOwnerCreate + * Create an empty ResourceOwner. * - * Caller must have previously done ResourceArrayEnlarge() + * All ResourceOwner objects are kept in TopMemoryContext, since they should + * only be freed explicitly. */ -static void -ResourceArrayAdd(ResourceArray *resarr, Datum value) +ResourceOwner +ResourceOwnerCreate(ResourceOwner parent, const char *name) { - uint32 idx; + ResourceOwner owner; - Assert(value != resarr->invalidval); - Assert(resarr->nitems < resarr->maxitems); + owner = (ResourceOwner) MemoryContextAllocZero(TopMemoryContext, + sizeof(ResourceOwnerData)); + owner->name = name; - if (RESARRAY_IS_ARRAY(resarr)) + if (parent) { - /* Append to linear array. */ - idx = resarr->nitems; + owner->parent = parent; + owner->nextchild = parent->firstchild; + parent->firstchild = owner; } - else - { - /* Insert into first free slot at or after hash location. */ - uint32 mask = resarr->capacity - 1; - idx = DatumGetUInt32(hash_any((void *) &value, sizeof(value))) & mask; - for (;;) - { - if (resarr->itemsarr[idx] == resarr->invalidval) - break; - idx = (idx + 1) & mask; - } - } - resarr->lastidx = idx; - resarr->itemsarr[idx] = value; - resarr->nitems++; +#ifdef RESOWNER_TRACE + elog(LOG, "CREATE %d: %p %s", + resowner_trace_counter++, owner, name); +#endif + + return owner; } /* - * Remove a resource from ResourceArray - * - * Returns true on success, false if resource was not found. + * Make sure there is room for at least one more resource in an array. * - * Note: if same resource ID appears more than once, one instance is removed. + * This is separate from actually inserting a resource because if we run out + * of memory, it's critical to do so *before* acquiring the resource. */ -static bool -ResourceArrayRemove(ResourceArray *resarr, Datum value) +void +ResourceOwnerEnlarge(ResourceOwner owner) { - uint32 i, - idx, - lastidx = resarr->lastidx; - - Assert(value != resarr->invalidval); + if (owner->narr < RESOWNER_ARRAY_SIZE) + return; /* no work needed */ - /* Search through all items, but try lastidx first. */ - if (RESARRAY_IS_ARRAY(resarr)) + /* Is there space in the hash? If not, enlarge it. */ + if (owner->narr + owner->nhash >= owner->grow_at) { - if (lastidx < resarr->nitems && - resarr->itemsarr[lastidx] == value) - { - resarr->itemsarr[lastidx] = resarr->itemsarr[resarr->nitems - 1]; - resarr->nitems--; - /* Update lastidx to make reverse-order removals fast. */ - resarr->lastidx = resarr->nitems - 1; - return true; - } - for (i = 0; i < resarr->nitems; i++) + uint32 i, + oldcap, + newcap; + ResourceElem *oldhash; + ResourceElem *newhash; + + oldhash = owner->hash; + oldcap = owner->capacity; + + /* Double the capacity (it must stay a power of 2!) */ + newcap = (oldcap > 0) ? oldcap * 2 : RESOWNER_HASH_INIT_SIZE; + newhash = (ResourceElem *) MemoryContextAllocZero(TopMemoryContext, + newcap * sizeof(ResourceElem)); + + /* + * We assume we can't fail below this point, so OK to scribble on the + * owner + */ + owner->hash = newhash; + owner->capacity = newcap; + owner->grow_at = RESOWNER_HASH_MAX_ITEMS(newcap); + owner->nhash = 0; + + if (oldhash != NULL) { - if (resarr->itemsarr[i] == value) + /* + * Transfer any pre-existing entries into the new hash table; they + * don't necessarily go where they were before, so this simple + * logic is the best way. + */ + for (i = 0; i < oldcap; i++) { - resarr->itemsarr[i] = resarr->itemsarr[resarr->nitems - 1]; - resarr->nitems--; - /* Update lastidx to make reverse-order removals fast. */ - resarr->lastidx = resarr->nitems - 1; - return true; + if (oldhash[i].kind != NULL) + ResourceOwnerAddToHash(owner, oldhash[i].item, oldhash[i].kind); } + + /* And release old hash table. */ + pfree(oldhash); } } - else - { - uint32 mask = resarr->capacity - 1; - if (lastidx < resarr->capacity && - resarr->itemsarr[lastidx] == value) - { - resarr->itemsarr[lastidx] = resarr->invalidval; - resarr->nitems--; - return true; - } - idx = DatumGetUInt32(hash_any((void *) &value, sizeof(value))) & mask; - for (i = 0; i < resarr->capacity; i++) - { - if (resarr->itemsarr[idx] == value) - { - resarr->itemsarr[idx] = resarr->invalidval; - resarr->nitems--; - return true; - } - idx = (idx + 1) & mask; - } + /* Move items from the array to the hash */ + Assert(owner->narr == RESOWNER_ARRAY_SIZE); + for (int i = 0; i < owner->narr; i++) + { + ResourceOwnerAddToHash(owner, owner->arr[i].item, owner->arr[i].kind); } + owner->narr = 0; - return false; + Assert(owner->nhash < owner->grow_at); } /* - * Get any convenient entry in a ResourceArray. + * Remember that an object is owner by a ReourceOwner * - * "Convenient" is defined as "easy for ResourceArrayRemove to remove"; - * we help that along by setting lastidx to match. This avoids O(N^2) cost - * when removing all ResourceArray items during ResourceOwner destruction. - * - * Returns true if we found an element, or false if the array is empty. + * Caller must have previously done ResourceOwnerEnlarge() */ -static bool -ResourceArrayGetAny(ResourceArray *resarr, Datum *value) +void +ResourceOwnerRemember(ResourceOwner owner, Datum value, ResourceOwnerFuncs *kind) { - if (resarr->nitems == 0) - return false; + uint32 idx; - if (RESARRAY_IS_ARRAY(resarr)) - { - /* Linear array: just return the first element. */ - resarr->lastidx = 0; - } - else - { - /* Hash: search forward from wherever we were last. */ - uint32 mask = resarr->capacity - 1; +#ifdef RESOWNER_TRACE + elog(LOG, "REMEMBER %d: owner %p value " UINT64_FORMAT ", kind: %s", + resowner_trace_counter++, owner, DatumGetUInt64(value), kind->name); +#endif - for (;;) - { - resarr->lastidx &= mask; - if (resarr->itemsarr[resarr->lastidx] != resarr->invalidval) - break; - resarr->lastidx++; - } - } + Assert(owner->narr < RESOWNER_ARRAY_SIZE); - *value = resarr->itemsarr[resarr->lastidx]; - return true; + /* Append to linear array. */ + idx = owner->narr; + owner->arr[idx].item = value; + owner->arr[idx].kind = kind; + owner->narr++; } /* - * Trash a ResourceArray (we don't care about its state after this) + * Forget that an object is owned by a ResourceOwner + * + * Returns true on success. If the resource was not found, returns false, + * and calls kind->ForgetError callback. + * + * Note: if same resource ID is associated with the ResourceOwner more than once, + * one instance is removed. */ -static void -ResourceArrayFree(ResourceArray *resarr) +void +ResourceOwnerForget(ResourceOwner owner, Datum value, ResourceOwnerFuncs *kind) { - if (resarr->itemsarr) - pfree(resarr->itemsarr); -} - + uint32 i, + idx; -/***************************************************************************** - * EXPORTED ROUTINES * - *****************************************************************************/ +#ifdef RESOWNER_TRACE + elog(LOG, "FORGET %d: owner %p value " UINT64_FORMAT ", kind: %s", + resowner_trace_counter++, owner, DatumGetUInt64(value), kind->name); +#endif + /* Search through all items, but check the array first. */ + for (i = 0; i < owner->narr; i++) + { + if (owner->arr[i].item == value && + owner->arr[i].kind == kind) + { + owner->arr[i] = owner->arr[owner->narr - 1]; + owner->narr--; -/* - * ResourceOwnerCreate - * Create an empty ResourceOwner. - * - * All ResourceOwner objects are kept in TopMemoryContext, since they should - * only be freed explicitly. - */ -ResourceOwner -ResourceOwnerCreate(ResourceOwner parent, const char *name) -{ - ResourceOwner owner; +#ifdef RESOWNER_STATS + narray_lookups++; +#endif - owner = (ResourceOwner) MemoryContextAllocZero(TopMemoryContext, - sizeof(ResourceOwnerData)); - owner->name = name; + return; + } + } - if (parent) + /* Search hash */ + if (owner->nhash > 0) { - owner->parent = parent; - owner->nextchild = parent->firstchild; - parent->firstchild = owner; - } + uint32 mask = owner->capacity - 1; - ResourceArrayInit(&(owner->bufferarr), BufferGetDatum(InvalidBuffer)); - ResourceArrayInit(&(owner->catrefarr), PointerGetDatum(NULL)); - ResourceArrayInit(&(owner->catlistrefarr), PointerGetDatum(NULL)); - ResourceArrayInit(&(owner->relrefarr), PointerGetDatum(NULL)); - ResourceArrayInit(&(owner->planrefarr), PointerGetDatum(NULL)); - ResourceArrayInit(&(owner->tupdescarr), PointerGetDatum(NULL)); - ResourceArrayInit(&(owner->snapshotarr), PointerGetDatum(NULL)); - ResourceArrayInit(&(owner->filearr), FileGetDatum(-1)); - ResourceArrayInit(&(owner->dsmarr), PointerGetDatum(NULL)); - ResourceArrayInit(&(owner->jitarr), PointerGetDatum(NULL)); - ResourceArrayInit(&(owner->cryptohasharr), PointerGetDatum(NULL)); - ResourceArrayInit(&(owner->hmacarr), PointerGetDatum(NULL)); + idx = hash_resource_elem(value, kind) & mask; + for (i = 0; i < owner->capacity; i++) + { + if (owner->hash[idx].item == value && + owner->hash[idx].kind == kind) + { + owner->hash[idx].item = (Datum) 0; + owner->hash[idx].kind = NULL; + owner->nhash--; + +#ifdef RESOWNER_STATS + nhash_lookups++; +#endif + return; + } + idx = (idx + 1) & mask; + } + } - return owner; + /* + * Use %p to print the reference, since most objects tracked by a resource + * owner are pointers. It's a bit misleading if it's not a pointer, but + * this is a programmer error, anyway. + */ + elog(ERROR, "%s %p is not owned by resource owner %s", + kind->name, DatumGetPointer(value), owner->name); } /* @@ -490,6 +497,15 @@ ResourceOwnerRelease(ResourceOwner owner, { /* There's not currently any setup needed before recursing */ ResourceOwnerReleaseInternal(owner, phase, isCommit, isTopLevel); + +#ifdef RESOWNER_STATS + if (isTopLevel) + { + elog(LOG, "RESOWNER STATS: lookups: array %d, hash %d", narray_lookups, nhash_lookups); + narray_lookups = 0; + nhash_lookups = 0; + } +#endif } static void @@ -501,7 +517,6 @@ ResourceOwnerReleaseInternal(ResourceOwner owner, ResourceOwner child; ResourceOwner save; ResourceReleaseCallbackItem *item; - Datum foundres; /* Recurse to handle descendants */ for (child = owner->firstchild; child != NULL; child = child->nextchild) @@ -517,71 +532,13 @@ ResourceOwnerReleaseInternal(ResourceOwner owner, if (phase == RESOURCE_RELEASE_BEFORE_LOCKS) { /* - * Release buffer pins. Note that ReleaseBuffer will remove the - * buffer entry from our array, so we just have to iterate till there - * are none. + * Release all references that need to be released before the locks. * - * During a commit, there shouldn't be any remaining pins --- that - * would indicate failure to clean up the executor correctly --- so - * issue warnings. In the abort case, just clean up quietly. + * During a commit, there shouldn't be any remaining references --- + * that would indicate failure to clean up the executor correctly --- + * so issue warnings. In the abort case, just clean up quietly. */ - while (ResourceArrayGetAny(&(owner->bufferarr), &foundres)) - { - Buffer res = DatumGetBuffer(foundres); - - if (isCommit) - PrintBufferLeakWarning(res); - ReleaseBuffer(res); - } - - /* Ditto for relcache references */ - while (ResourceArrayGetAny(&(owner->relrefarr), &foundres)) - { - Relation res = (Relation) DatumGetPointer(foundres); - - if (isCommit) - PrintRelCacheLeakWarning(res); - RelationClose(res); - } - - /* Ditto for dynamic shared memory segments */ - while (ResourceArrayGetAny(&(owner->dsmarr), &foundres)) - { - dsm_segment *res = (dsm_segment *) DatumGetPointer(foundres); - - if (isCommit) - PrintDSMLeakWarning(res); - dsm_detach(res); - } - - /* Ditto for JIT contexts */ - while (ResourceArrayGetAny(&(owner->jitarr), &foundres)) - { - JitContext *context = (JitContext *) PointerGetDatum(foundres); - - jit_release_context(context); - } - - /* Ditto for cryptohash contexts */ - while (ResourceArrayGetAny(&(owner->cryptohasharr), &foundres)) - { - pg_cryptohash_ctx *context = - (pg_cryptohash_ctx *) PointerGetDatum(foundres); - - if (isCommit) - PrintCryptoHashLeakWarning(foundres); - pg_cryptohash_free(context); - } - - /* Ditto for HMAC contexts */ - while (ResourceArrayGetAny(&(owner->hmacarr), &foundres)) - { - pg_hmac_ctx *context = (pg_hmac_ctx *) PointerGetDatum(foundres); - - if (isCommit) - PrintHMACLeakWarning(foundres); - pg_hmac_free(context); - } + ResourceOwnerReleaseAll(owner, phase, isCommit); } else if (phase == RESOURCE_RELEASE_LOCKS) { @@ -590,7 +547,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner, /* * For a top-level xact we are going to release all locks (or at * least all non-session locks), so just do a single lmgr call at - * the top of the recursion. + * the top of the recursion */ if (owner == TopTransactionResourceOwner) { @@ -634,70 +591,9 @@ ResourceOwnerReleaseInternal(ResourceOwner owner, else if (phase == RESOURCE_RELEASE_AFTER_LOCKS) { /* - * Release catcache references. Note that ReleaseCatCache will remove - * the catref entry from our array, so we just have to iterate till - * there are none. - * - * As with buffer pins, warn if any are left at commit time. + * Release all references that need to be released after the locks. */ - while (ResourceArrayGetAny(&(owner->catrefarr), &foundres)) - { - HeapTuple res = (HeapTuple) DatumGetPointer(foundres); - - if (isCommit) - PrintCatCacheLeakWarning(res); - ReleaseCatCache(res); - } - - /* Ditto for catcache lists */ - while (ResourceArrayGetAny(&(owner->catlistrefarr), &foundres)) - { - CatCList *res = (CatCList *) DatumGetPointer(foundres); - - if (isCommit) - PrintCatCacheListLeakWarning(res); - ReleaseCatCacheList(res); - } - - /* Ditto for plancache references */ - while (ResourceArrayGetAny(&(owner->planrefarr), &foundres)) - { - CachedPlan *res = (CachedPlan *) DatumGetPointer(foundres); - - if (isCommit) - PrintPlanCacheLeakWarning(res); - ReleaseCachedPlan(res, owner); - } - - /* Ditto for tupdesc references */ - while (ResourceArrayGetAny(&(owner->tupdescarr), &foundres)) - { - TupleDesc res = (TupleDesc) DatumGetPointer(foundres); - - if (isCommit) - PrintTupleDescLeakWarning(res); - DecrTupleDescRefCount(res); - } - - /* Ditto for snapshot references */ - while (ResourceArrayGetAny(&(owner->snapshotarr), &foundres)) - { - Snapshot res = (Snapshot) DatumGetPointer(foundres); - - if (isCommit) - PrintSnapshotLeakWarning(res); - UnregisterSnapshot(res); - } - - /* Ditto for temporary files */ - while (ResourceArrayGetAny(&(owner->filearr), &foundres)) - { - File res = DatumGetFile(foundres); - - if (isCommit) - PrintFileLeakWarning(res); - FileClose(res); - } + ResourceOwnerReleaseAll(owner, phase, isCommit); } /* Let add-on modules get a chance too */ @@ -717,13 +613,42 @@ ResourceOwnerReleaseInternal(ResourceOwner owner, void ResourceOwnerReleaseAllPlanCacheRefs(ResourceOwner owner) { - Datum foundres; + /* array first */ + for (int i = 0; i < owner->narr; i++) + { + if (owner->arr[i].kind == &planref_resowner_funcs) + { + CachedPlan *planref = (CachedPlan *) DatumGetPointer(owner->arr[i].item); + + owner->arr[i] = owner->arr[owner->narr - 1]; + owner->narr--; + i--; + + /* + * pass 'NULL' because we already removed the entry from the + * resowner + */ + ReleaseCachedPlan(planref, NULL); + } + } - while (ResourceArrayGetAny(&(owner->planrefarr), &foundres)) + /* Then hash */ + for (int i = 0; i < owner->capacity; i++) { - CachedPlan *res = (CachedPlan *) DatumGetPointer(foundres); + if (owner->hash[i].kind == &planref_resowner_funcs) + { + CachedPlan *planref = (CachedPlan *) DatumGetPointer(owner->hash[i].item); + + owner->hash[i].item = (Datum) 0; + owner->hash[i].kind = NULL; + owner->nhash--; - ReleaseCachedPlan(res, owner); + /* + * pass 'NULL' because we already removed the entry from the + * resowner + */ + ReleaseCachedPlan(planref, NULL); + } } } @@ -740,20 +665,15 @@ ResourceOwnerDelete(ResourceOwner owner) Assert(owner != CurrentResourceOwner); /* And it better not own any resources, either */ - Assert(owner->bufferarr.nitems == 0); - Assert(owner->catrefarr.nitems == 0); - Assert(owner->catlistrefarr.nitems == 0); - Assert(owner->relrefarr.nitems == 0); - Assert(owner->planrefarr.nitems == 0); - Assert(owner->tupdescarr.nitems == 0); - Assert(owner->snapshotarr.nitems == 0); - Assert(owner->filearr.nitems == 0); - Assert(owner->dsmarr.nitems == 0); - Assert(owner->jitarr.nitems == 0); - Assert(owner->cryptohasharr.nitems == 0); - Assert(owner->hmacarr.nitems == 0); + Assert(owner->narr == 0); + Assert(owner->nhash == 0); Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1); +#ifdef RESOWNER_TRACE + elog(LOG, "DELETE %d: %p %s", + resowner_trace_counter++, owner, owner->name); +#endif + /* * Delete children. The recursive call will delink the child from me, so * just iterate as long as there is a child. @@ -769,19 +689,8 @@ ResourceOwnerDelete(ResourceOwner owner) ResourceOwnerNewParent(owner, NULL); /* And free the object. */ - ResourceArrayFree(&(owner->bufferarr)); - ResourceArrayFree(&(owner->catrefarr)); - ResourceArrayFree(&(owner->catlistrefarr)); - ResourceArrayFree(&(owner->relrefarr)); - ResourceArrayFree(&(owner->planrefarr)); - ResourceArrayFree(&(owner->tupdescarr)); - ResourceArrayFree(&(owner->snapshotarr)); - ResourceArrayFree(&(owner->filearr)); - ResourceArrayFree(&(owner->dsmarr)); - ResourceArrayFree(&(owner->jitarr)); - ResourceArrayFree(&(owner->cryptohasharr)); - ResourceArrayFree(&(owner->hmacarr)); - + if (owner->hash) + pfree(owner->hash); pfree(owner); } @@ -839,11 +748,10 @@ ResourceOwnerNewParent(ResourceOwner owner, /* * Register or deregister callback functions for resource cleanup * - * These functions are intended for use by dynamically loaded modules. - * For built-in modules we generally just hardwire the appropriate calls. - * - * Note that the callback occurs post-commit or post-abort, so the callback - * functions can only do noncritical cleanup. + * These functions can be used by dynamically loaded modules. These used + * to be the only way for an extension to register custom resource types + * with a resource owner, but nowadays it is easier to define a new + * ResourceOwnerFuncs instance with custom callbacks. */ void RegisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg) @@ -934,58 +842,20 @@ ReleaseAuxProcessResourcesCallback(int code, Datum arg) ReleaseAuxProcessResources(isCommit); } - /* - * Make sure there is room for at least one more entry in a ResourceOwner's - * buffer array. + * Remember that a Local Lock is owned by a ResourceOwner * - * This is separate from actually inserting an entry because if we run out - * of memory, it's critical to do so *before* acquiring the resource. + * This is different from the other Remember functions in that the list of + * locks is only a lossy cache. It can hold up to MAX_RESOWNER_LOCKS entries, + * and when it overflows, we stop tracking locks. The point of only remembering + * only up to MAX_RESOWNER_LOCKS entries is that if a lot of locks are held, + * ResourceOwnerForgetLock doesn't need to scan through a large array to find + * the entry. */ void -ResourceOwnerEnlargeBuffers(ResourceOwner owner) +ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock) { - /* We used to allow pinning buffers without a resowner, but no more */ - Assert(owner != NULL); - ResourceArrayEnlarge(&(owner->bufferarr)); -} - -/* - * Remember that a buffer pin is owned by a ResourceOwner - * - * Caller must have previously done ResourceOwnerEnlargeBuffers() - */ -void -ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer) -{ - ResourceArrayAdd(&(owner->bufferarr), BufferGetDatum(buffer)); -} - -/* - * Forget that a buffer pin is owned by a ResourceOwner - */ -void -ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer) -{ - if (!ResourceArrayRemove(&(owner->bufferarr), BufferGetDatum(buffer))) - elog(ERROR, "buffer %d is not owned by resource owner %s", - buffer, owner->name); -} - -/* - * Remember that a Local Lock is owned by a ResourceOwner - * - * This is different from the other Remember functions in that the list of - * locks is only a lossy cache. It can hold up to MAX_RESOWNER_LOCKS entries, - * and when it overflows, we stop tracking locks. The point of only remembering - * only up to MAX_RESOWNER_LOCKS entries is that if a lot of locks are held, - * ResourceOwnerForgetLock doesn't need to scan through a large array to find - * the entry. - */ -void -ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock) -{ - Assert(locallock != NULL); + Assert(locallock != NULL); if (owner->nlocks > MAX_RESOWNER_LOCKS) return; /* we have already overflowed */ @@ -1023,469 +893,3 @@ ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock) elog(ERROR, "lock reference %p is not owned by resource owner %s", locallock, owner->name); } - -/* - * Make sure there is room for at least one more entry in a ResourceOwner's - * catcache reference array. - * - * This is separate from actually inserting an entry because if we run out - * of memory, it's critical to do so *before* acquiring the resource. - */ -void -ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner) -{ - ResourceArrayEnlarge(&(owner->catrefarr)); -} - -/* - * Remember that a catcache reference is owned by a ResourceOwner - * - * Caller must have previously done ResourceOwnerEnlargeCatCacheRefs() - */ -void -ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple) -{ - ResourceArrayAdd(&(owner->catrefarr), PointerGetDatum(tuple)); -} - -/* - * Forget that a catcache reference is owned by a ResourceOwner - */ -void -ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple) -{ - if (!ResourceArrayRemove(&(owner->catrefarr), PointerGetDatum(tuple))) - elog(ERROR, "catcache reference %p is not owned by resource owner %s", - tuple, owner->name); -} - -/* - * Make sure there is room for at least one more entry in a ResourceOwner's - * catcache-list reference array. - * - * This is separate from actually inserting an entry because if we run out - * of memory, it's critical to do so *before* acquiring the resource. - */ -void -ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner) -{ - ResourceArrayEnlarge(&(owner->catlistrefarr)); -} - -/* - * Remember that a catcache-list reference is owned by a ResourceOwner - * - * Caller must have previously done ResourceOwnerEnlargeCatCacheListRefs() - */ -void -ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list) -{ - ResourceArrayAdd(&(owner->catlistrefarr), PointerGetDatum(list)); -} - -/* - * Forget that a catcache-list reference is owned by a ResourceOwner - */ -void -ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list) -{ - if (!ResourceArrayRemove(&(owner->catlistrefarr), PointerGetDatum(list))) - elog(ERROR, "catcache list reference %p is not owned by resource owner %s", - list, owner->name); -} - -/* - * Make sure there is room for at least one more entry in a ResourceOwner's - * relcache reference array. - * - * This is separate from actually inserting an entry because if we run out - * of memory, it's critical to do so *before* acquiring the resource. - */ -void -ResourceOwnerEnlargeRelationRefs(ResourceOwner owner) -{ - ResourceArrayEnlarge(&(owner->relrefarr)); -} - -/* - * Remember that a relcache reference is owned by a ResourceOwner - * - * Caller must have previously done ResourceOwnerEnlargeRelationRefs() - */ -void -ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel) -{ - ResourceArrayAdd(&(owner->relrefarr), PointerGetDatum(rel)); -} - -/* - * Forget that a relcache reference is owned by a ResourceOwner - */ -void -ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel) -{ - if (!ResourceArrayRemove(&(owner->relrefarr), PointerGetDatum(rel))) - elog(ERROR, "relcache reference %s is not owned by resource owner %s", - RelationGetRelationName(rel), owner->name); -} - -/* - * Debugging subroutine - */ -static void -PrintRelCacheLeakWarning(Relation rel) -{ - elog(WARNING, "relcache reference leak: relation \"%s\" not closed", - RelationGetRelationName(rel)); -} - -/* - * Make sure there is room for at least one more entry in a ResourceOwner's - * plancache reference array. - * - * This is separate from actually inserting an entry because if we run out - * of memory, it's critical to do so *before* acquiring the resource. - */ -void -ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner) -{ - ResourceArrayEnlarge(&(owner->planrefarr)); -} - -/* - * Remember that a plancache reference is owned by a ResourceOwner - * - * Caller must have previously done ResourceOwnerEnlargePlanCacheRefs() - */ -void -ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan) -{ - ResourceArrayAdd(&(owner->planrefarr), PointerGetDatum(plan)); -} - -/* - * Forget that a plancache reference is owned by a ResourceOwner - */ -void -ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan) -{ - if (!ResourceArrayRemove(&(owner->planrefarr), PointerGetDatum(plan))) - elog(ERROR, "plancache reference %p is not owned by resource owner %s", - plan, owner->name); -} - -/* - * Debugging subroutine - */ -static void -PrintPlanCacheLeakWarning(CachedPlan *plan) -{ - elog(WARNING, "plancache reference leak: plan %p not closed", plan); -} - -/* - * Make sure there is room for at least one more entry in a ResourceOwner's - * tupdesc reference array. - * - * This is separate from actually inserting an entry because if we run out - * of memory, it's critical to do so *before* acquiring the resource. - */ -void -ResourceOwnerEnlargeTupleDescs(ResourceOwner owner) -{ - ResourceArrayEnlarge(&(owner->tupdescarr)); -} - -/* - * Remember that a tupdesc reference is owned by a ResourceOwner - * - * Caller must have previously done ResourceOwnerEnlargeTupleDescs() - */ -void -ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc) -{ - ResourceArrayAdd(&(owner->tupdescarr), PointerGetDatum(tupdesc)); -} - -/* - * Forget that a tupdesc reference is owned by a ResourceOwner - */ -void -ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc) -{ - if (!ResourceArrayRemove(&(owner->tupdescarr), PointerGetDatum(tupdesc))) - elog(ERROR, "tupdesc reference %p is not owned by resource owner %s", - tupdesc, owner->name); -} - -/* - * Debugging subroutine - */ -static void -PrintTupleDescLeakWarning(TupleDesc tupdesc) -{ - elog(WARNING, - "TupleDesc reference leak: TupleDesc %p (%u,%d) still referenced", - tupdesc, tupdesc->tdtypeid, tupdesc->tdtypmod); -} - -/* - * Make sure there is room for at least one more entry in a ResourceOwner's - * snapshot reference array. - * - * This is separate from actually inserting an entry because if we run out - * of memory, it's critical to do so *before* acquiring the resource. - */ -void -ResourceOwnerEnlargeSnapshots(ResourceOwner owner) -{ - ResourceArrayEnlarge(&(owner->snapshotarr)); -} - -/* - * Remember that a snapshot reference is owned by a ResourceOwner - * - * Caller must have previously done ResourceOwnerEnlargeSnapshots() - */ -void -ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot) -{ - ResourceArrayAdd(&(owner->snapshotarr), PointerGetDatum(snapshot)); -} - -/* - * Forget that a snapshot reference is owned by a ResourceOwner - */ -void -ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snapshot) -{ - if (!ResourceArrayRemove(&(owner->snapshotarr), PointerGetDatum(snapshot))) - elog(ERROR, "snapshot reference %p is not owned by resource owner %s", - snapshot, owner->name); -} - -/* - * Debugging subroutine - */ -static void -PrintSnapshotLeakWarning(Snapshot snapshot) -{ - elog(WARNING, "Snapshot reference leak: Snapshot %p still referenced", - snapshot); -} - - -/* - * Make sure there is room for at least one more entry in a ResourceOwner's - * files reference array. - * - * This is separate from actually inserting an entry because if we run out - * of memory, it's critical to do so *before* acquiring the resource. - */ -void -ResourceOwnerEnlargeFiles(ResourceOwner owner) -{ - ResourceArrayEnlarge(&(owner->filearr)); -} - -/* - * Remember that a temporary file is owned by a ResourceOwner - * - * Caller must have previously done ResourceOwnerEnlargeFiles() - */ -void -ResourceOwnerRememberFile(ResourceOwner owner, File file) -{ - ResourceArrayAdd(&(owner->filearr), FileGetDatum(file)); -} - -/* - * Forget that a temporary file is owned by a ResourceOwner - */ -void -ResourceOwnerForgetFile(ResourceOwner owner, File file) -{ - if (!ResourceArrayRemove(&(owner->filearr), FileGetDatum(file))) - elog(ERROR, "temporary file %d is not owned by resource owner %s", - file, owner->name); -} - -/* - * Debugging subroutine - */ -static void -PrintFileLeakWarning(File file) -{ - elog(WARNING, "temporary file leak: File %d still referenced", - file); -} - -/* - * Make sure there is room for at least one more entry in a ResourceOwner's - * dynamic shmem segment reference array. - * - * This is separate from actually inserting an entry because if we run out - * of memory, it's critical to do so *before* acquiring the resource. - */ -void -ResourceOwnerEnlargeDSMs(ResourceOwner owner) -{ - ResourceArrayEnlarge(&(owner->dsmarr)); -} - -/* - * Remember that a dynamic shmem segment is owned by a ResourceOwner - * - * Caller must have previously done ResourceOwnerEnlargeDSMs() - */ -void -ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg) -{ - ResourceArrayAdd(&(owner->dsmarr), PointerGetDatum(seg)); -} - -/* - * Forget that a dynamic shmem segment is owned by a ResourceOwner - */ -void -ResourceOwnerForgetDSM(ResourceOwner owner, dsm_segment *seg) -{ - if (!ResourceArrayRemove(&(owner->dsmarr), PointerGetDatum(seg))) - elog(ERROR, "dynamic shared memory segment %u is not owned by resource owner %s", - dsm_segment_handle(seg), owner->name); -} - -/* - * Debugging subroutine - */ -static void -PrintDSMLeakWarning(dsm_segment *seg) -{ - elog(WARNING, "dynamic shared memory leak: segment %u still referenced", - dsm_segment_handle(seg)); -} - -/* - * Make sure there is room for at least one more entry in a ResourceOwner's - * JIT context reference array. - * - * This is separate from actually inserting an entry because if we run out of - * memory, it's critical to do so *before* acquiring the resource. - */ -void -ResourceOwnerEnlargeJIT(ResourceOwner owner) -{ - ResourceArrayEnlarge(&(owner->jitarr)); -} - -/* - * Remember that a JIT context is owned by a ResourceOwner - * - * Caller must have previously done ResourceOwnerEnlargeJIT() - */ -void -ResourceOwnerRememberJIT(ResourceOwner owner, Datum handle) -{ - ResourceArrayAdd(&(owner->jitarr), handle); -} - -/* - * Forget that a JIT context is owned by a ResourceOwner - */ -void -ResourceOwnerForgetJIT(ResourceOwner owner, Datum handle) -{ - if (!ResourceArrayRemove(&(owner->jitarr), handle)) - elog(ERROR, "JIT context %p is not owned by resource owner %s", - DatumGetPointer(handle), owner->name); -} - -/* - * Make sure there is room for at least one more entry in a ResourceOwner's - * cryptohash context reference array. - * - * This is separate from actually inserting an entry because if we run out of - * memory, it's critical to do so *before* acquiring the resource. - */ -void -ResourceOwnerEnlargeCryptoHash(ResourceOwner owner) -{ - ResourceArrayEnlarge(&(owner->cryptohasharr)); -} - -/* - * Remember that a cryptohash context is owned by a ResourceOwner - * - * Caller must have previously done ResourceOwnerEnlargeCryptoHash() - */ -void -ResourceOwnerRememberCryptoHash(ResourceOwner owner, Datum handle) -{ - ResourceArrayAdd(&(owner->cryptohasharr), handle); -} - -/* - * Forget that a cryptohash context is owned by a ResourceOwner - */ -void -ResourceOwnerForgetCryptoHash(ResourceOwner owner, Datum handle) -{ - if (!ResourceArrayRemove(&(owner->cryptohasharr), handle)) - elog(ERROR, "cryptohash context %p is not owned by resource owner %s", - DatumGetPointer(handle), owner->name); -} - -/* - * Debugging subroutine - */ -static void -PrintCryptoHashLeakWarning(Datum handle) -{ - elog(WARNING, "cryptohash context reference leak: context %p still referenced", - DatumGetPointer(handle)); -} - -/* - * Make sure there is room for at least one more entry in a ResourceOwner's - * hmac context reference array. - * - * This is separate from actually inserting an entry because if we run out of - * memory, it's critical to do so *before* acquiring the resource. - */ -void -ResourceOwnerEnlargeHMAC(ResourceOwner owner) -{ - ResourceArrayEnlarge(&(owner->hmacarr)); -} - -/* - * Remember that a HMAC context is owned by a ResourceOwner - * - * Caller must have previously done ResourceOwnerEnlargeHMAC() - */ -void -ResourceOwnerRememberHMAC(ResourceOwner owner, Datum handle) -{ - ResourceArrayAdd(&(owner->hmacarr), handle); -} - -/* - * Forget that a HMAC context is owned by a ResourceOwner - */ -void -ResourceOwnerForgetHMAC(ResourceOwner owner, Datum handle) -{ - if (!ResourceArrayRemove(&(owner->hmacarr), handle)) - elog(ERROR, "HMAC context %p is not owned by resource owner %s", - DatumGetPointer(handle), owner->name); -} - -/* - * Debugging subroutine - */ -static void -PrintHMACLeakWarning(Datum handle) -{ - elog(WARNING, "HMAC context reference leak: context %p still referenced", - DatumGetPointer(handle)); -} diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c index 2968c7f7b7d..1f46cbe58c9 100644 --- a/src/backend/utils/time/snapmgr.c +++ b/src/backend/utils/time/snapmgr.c @@ -66,7 +66,7 @@ #include "utils/memutils.h" #include "utils/old_snapshot.h" #include "utils/rel.h" -#include "utils/resowner_private.h" +#include "utils/resowner.h" #include "utils/snapmgr.h" #include "utils/syscache.h" #include "utils/timestamp.h" @@ -174,6 +174,24 @@ static Snapshot CopySnapshot(Snapshot snapshot); static void FreeSnapshot(Snapshot snapshot); static void SnapshotResetXmin(void); +/* ResourceOwner callbacks to track snapshot references */ +static void ResOwnerReleaseSnapshot(Datum res); +static void ResOwnerPrintSnapshotLeakWarning(Datum res); + +static ResourceOwnerFuncs snapshot_resowner_funcs = +{ + .name = "snapshot reference", + .phase = RESOURCE_RELEASE_AFTER_LOCKS, + .ReleaseResource = ResOwnerReleaseSnapshot, + .PrintLeakWarning = ResOwnerPrintSnapshotLeakWarning +}; + +/* Convenience wrappers over ResourceOwnerRemember/Forget */ +#define ResourceOwnerRememberSnapshot(owner, snap) \ + ResourceOwnerRemember(owner, PointerGetDatum(snap), &snapshot_resowner_funcs) +#define ResourceOwnerForgetSnapshot(owner, snap) \ + ResourceOwnerForget(owner, PointerGetDatum(snap), &snapshot_resowner_funcs) + /* * Snapshot fields to be serialized. * @@ -831,7 +849,7 @@ RegisterSnapshotOnOwner(Snapshot snapshot, ResourceOwner owner) snap = snapshot->copied ? snapshot : CopySnapshot(snapshot); /* and tell resowner.c about it */ - ResourceOwnerEnlargeSnapshots(owner); + ResourceOwnerEnlarge(owner); snap->regd_count++; ResourceOwnerRememberSnapshot(owner, snap); @@ -2349,3 +2367,19 @@ XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot) return false; } + +/* + * ResourceOwner callbacks + */ +static void +ResOwnerReleaseSnapshot(Datum res) +{ + UnregisterSnapshot((Snapshot) DatumGetPointer(res)); +} + +static void +ResOwnerPrintSnapshotLeakWarning(Datum res) +{ + elog(WARNING, "Snapshot reference leak: Snapshot %p still referenced", + DatumGetPointer(res)); +} diff --git a/src/common/cryptohash_openssl.c b/src/common/cryptohash_openssl.c index 643cc7aea2c..cf5fc9eacb4 100644 --- a/src/common/cryptohash_openssl.c +++ b/src/common/cryptohash_openssl.c @@ -30,7 +30,6 @@ #ifndef FRONTEND #include "utils/memutils.h" #include "utils/resowner.h" -#include "utils/resowner_private.h" #endif /* @@ -63,6 +62,27 @@ struct pg_cryptohash_ctx #endif }; +/* ResourceOwner callbacks to hold cryptohash contexts */ +#ifndef FRONTEND +static void ResOwnerReleaseCryptoHash(Datum res); +static void ResOwnerPrintCryptoHashLeakWarning(Datum res); + +static ResourceOwnerFuncs cryptohash_resowner_funcs = +{ + /* relcache references */ + .name = "OpenSSL cryptohash context", + .phase = RESOURCE_RELEASE_BEFORE_LOCKS, + .ReleaseResource = ResOwnerReleaseCryptoHash, + .PrintLeakWarning = ResOwnerPrintCryptoHashLeakWarning, +}; + +/* Convenience wrappers over ResourceOwnerRemember/Forget */ +#define ResourceOwnerRememberCryptoHash(owner, ctx) \ + ResourceOwnerRemember(owner, PointerGetDatum(ctx), &cryptohash_resowner_funcs) +#define ResourceOwnerForgetCryptoHash(owner, ctx) \ + ResourceOwnerForget(owner, PointerGetDatum(ctx), &cryptohash_resowner_funcs) +#endif + /* * pg_cryptohash_create * @@ -80,7 +100,7 @@ pg_cryptohash_create(pg_cryptohash_type type) * allocation to avoid leaking. */ #ifndef FRONTEND - ResourceOwnerEnlargeCryptoHash(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); #endif ctx = ALLOC(sizeof(pg_cryptohash_ctx)); @@ -109,8 +129,7 @@ pg_cryptohash_create(pg_cryptohash_type type) #ifndef FRONTEND ctx->resowner = CurrentResourceOwner; - ResourceOwnerRememberCryptoHash(CurrentResourceOwner, - PointerGetDatum(ctx)); + ResourceOwnerRememberCryptoHash(CurrentResourceOwner, ctx); #endif return ctx; @@ -241,10 +260,28 @@ pg_cryptohash_free(pg_cryptohash_ctx *ctx) EVP_MD_CTX_destroy(ctx->evpctx); #ifndef FRONTEND - ResourceOwnerForgetCryptoHash(ctx->resowner, - PointerGetDatum(ctx)); + ResourceOwnerForgetCryptoHash(ctx->resowner, ctx); #endif explicit_bzero(ctx, sizeof(pg_cryptohash_ctx)); FREE(ctx); } + + +/* + * ResourceOwner callbacks + */ +#ifndef FRONTEND +static void +ResOwnerReleaseCryptoHash(Datum res) +{ + pg_cryptohash_free((pg_cryptohash_ctx *) DatumGetPointer(res)); +} + +static void +ResOwnerPrintCryptoHashLeakWarning(Datum res) +{ + elog(WARNING, "cryptohash context reference leak: context %p still referenced", + DatumGetPointer(res)); +} +#endif diff --git a/src/common/hmac_openssl.c b/src/common/hmac_openssl.c index 1acf59476eb..0a65284bae9 100644 --- a/src/common/hmac_openssl.c +++ b/src/common/hmac_openssl.c @@ -29,7 +29,6 @@ #ifndef FRONTEND #include "utils/memutils.h" #include "utils/resowner.h" -#include "utils/resowner_private.h" #endif /* @@ -63,6 +62,27 @@ struct pg_hmac_ctx #endif }; +/* ResourceOwner callbacks to hold HMAC contexts */ +#ifndef FRONTEND +static void ResOwnerReleaseHMAC(Datum res); +static void ResOwnerPrintHMACLeakWarning(Datum res); + +static ResourceOwnerFuncs hmac_resowner_funcs = +{ + /* relcache references */ + .name = "OpenSSL HMAC context", + .phase = RESOURCE_RELEASE_BEFORE_LOCKS, + .ReleaseResource = ResOwnerReleaseHMAC, + .PrintLeakWarning = ResOwnerPrintHMACLeakWarning, +}; + +/* Convenience wrappers over ResourceOwnerRemember/Forget */ +#define ResourceOwnerRememberHMAC(owner, ctx) \ + ResourceOwnerRemember(owner, PointerGetDatum(ctx), &hmac_resowner_funcs) +#define ResourceOwnerForgetHMAC(owner, ctx) \ + ResourceOwnerForget(owner, PointerGetDatum(ctx), &hmac_resowner_funcs) +#endif + /* * pg_hmac_create * @@ -86,7 +106,7 @@ pg_hmac_create(pg_cryptohash_type type) */ #ifdef HAVE_HMAC_CTX_NEW #ifndef FRONTEND - ResourceOwnerEnlargeHMAC(CurrentResourceOwner); + ResourceOwnerEnlarge(CurrentResourceOwner); #endif ctx->hmacctx = HMAC_CTX_new(); #else @@ -108,7 +128,7 @@ pg_hmac_create(pg_cryptohash_type type) #ifdef HAVE_HMAC_CTX_NEW #ifndef FRONTEND ctx->resowner = CurrentResourceOwner; - ResourceOwnerRememberHMAC(CurrentResourceOwner, PointerGetDatum(ctx)); + ResourceOwnerRememberHMAC(CurrentResourceOwner, ctx); #endif #else memset(ctx->hmacctx, 0, sizeof(HMAC_CTX)); @@ -244,7 +264,7 @@ pg_hmac_free(pg_hmac_ctx *ctx) #ifdef HAVE_HMAC_CTX_FREE HMAC_CTX_free(ctx->hmacctx); #ifndef FRONTEND - ResourceOwnerForgetHMAC(ctx->resowner, PointerGetDatum(ctx)); + ResourceOwnerForgetHMAC(ctx->resowner, ctx); #endif #else explicit_bzero(ctx->hmacctx, sizeof(HMAC_CTX)); @@ -254,3 +274,22 @@ pg_hmac_free(pg_hmac_ctx *ctx) explicit_bzero(ctx, sizeof(pg_hmac_ctx)); FREE(ctx); } + + +/* + * ResourceOwner callbacks + */ +#ifndef FRONTEND +static void +ResOwnerReleaseHMAC(Datum res) +{ + pg_hmac_free((pg_hmac_ctx *) DatumGetPointer(res)); +} + +static void +ResOwnerPrintHMACLeakWarning(Datum res) +{ + elog(WARNING, "HMAC context reference leak: context %p still referenced", + DatumGetPointer(res)); +} +#endif diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h index 33fcaf5c9a8..10ef8d850e4 100644 --- a/src/include/storage/buf_internals.h +++ b/src/include/storage/buf_internals.h @@ -300,6 +300,15 @@ typedef struct CkptSortItem extern CkptSortItem *CkptBufferIds; +/* ResourceOwner callbacks to hold buffer pins */ +extern ResourceOwnerFuncs buffer_resowner_funcs; + +/* Convenience wrappers over ResourceOwnerRemember/Forget */ +#define ResourceOwnerRememberBuffer(owner, buffer) \ + ResourceOwnerRemember(owner, Int32GetDatum(buffer), &buffer_resowner_funcs) +#define ResourceOwnerForgetBuffer(owner, buffer) \ + ResourceOwnerForget(owner, Int32GetDatum(buffer), &buffer_resowner_funcs) + /* * Internal buffer management routines */ diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h index ddc2762eb3f..2bf95c22e84 100644 --- a/src/include/utils/catcache.h +++ b/src/include/utils/catcache.h @@ -225,7 +225,4 @@ extern void PrepareToInvalidateCacheTuple(Relation relation, HeapTuple newtuple, void (*function) (int, uint32, Oid)); -extern void PrintCatCacheLeakWarning(HeapTuple tuple); -extern void PrintCatCacheListLeakWarning(CatCList *list); - #endif /* CATCACHE_H */ diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h index ff09c63a02f..f3e3e50fb21 100644 --- a/src/include/utils/plancache.h +++ b/src/include/utils/plancache.h @@ -233,4 +233,6 @@ extern bool CachedPlanIsSimplyValid(CachedPlanSource *plansource, extern CachedExpression *GetCachedExpression(Node *expr); extern void FreeCachedExpression(CachedExpression *cexpr); +extern ResourceOwnerFuncs planref_resowner_funcs; + #endif /* PLANCACHE_H */ diff --git a/src/include/utils/resowner.h b/src/include/utils/resowner.h index 109ac31b248..d59d14c0d01 100644 --- a/src/include/utils/resowner.h +++ b/src/include/utils/resowner.h @@ -50,6 +50,36 @@ typedef enum RESOURCE_RELEASE_AFTER_LOCKS } ResourceReleasePhase; +/* + * In order to track an object, resowner.c needs a few callbacks for it. + * The callbacks for an resource of a specific kind are encapsulated in + * ResourceOwnerFuncs. + * + * Note that the callback occurs post-commit or post-abort, so these callback + * functions can only do noncritical cleanup. + */ +typedef struct ResourceOwnerFuncs +{ + const char *name; /* name for the object kind, for debugging */ + + ResourceReleasePhase phase; /* when are these objects released? */ + + /* + * Release resource. + * + * NOTE: this must call ResourceOwnerForget to disassociate it with the + * resource owner. + */ + void (*ReleaseResource)(Datum res); + + /* + * Print a warning, when a resource has not been properly released before + * commit. + */ + void (*PrintLeakWarning)(Datum res); + +} ResourceOwnerFuncs; + /* * Dynamically loaded modules can get control during ResourceOwnerRelease * by providing a callback of this form. @@ -71,16 +101,29 @@ extern void ResourceOwnerRelease(ResourceOwner owner, ResourceReleasePhase phase, bool isCommit, bool isTopLevel); -extern void ResourceOwnerReleaseAllPlanCacheRefs(ResourceOwner owner); extern void ResourceOwnerDelete(ResourceOwner owner); extern ResourceOwner ResourceOwnerGetParent(ResourceOwner owner); extern void ResourceOwnerNewParent(ResourceOwner owner, ResourceOwner newparent); + +extern void ResourceOwnerEnlarge(ResourceOwner owner); +extern void ResourceOwnerRemember(ResourceOwner owner, Datum res, ResourceOwnerFuncs *kind); +extern void ResourceOwnerForget(ResourceOwner owner, Datum res, ResourceOwnerFuncs *kind); + extern void RegisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg); extern void UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg); + extern void CreateAuxProcessResourceOwner(void); extern void ReleaseAuxProcessResources(bool isCommit); +/* special support for local lock management */ +struct LOCALLOCK; +extern void ResourceOwnerRememberLock(ResourceOwner owner, struct LOCALLOCK *locallock); +extern void ResourceOwnerForgetLock(ResourceOwner owner, struct LOCALLOCK *locallock); + +/* special function to relase all plancache references */ +extern void ResourceOwnerReleaseAllPlanCacheRefs(ResourceOwner owner); + #endif /* RESOWNER_H */ diff --git a/src/include/utils/resowner_private.h b/src/include/utils/resowner_private.h deleted file mode 100644 index 6dafc87e28c..00000000000 --- a/src/include/utils/resowner_private.h +++ /dev/null @@ -1,112 +0,0 @@ -/*------------------------------------------------------------------------- - * - * resowner_private.h - * POSTGRES resource owner private definitions. - * - * See utils/resowner/README for more info. - * - * - * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * src/include/utils/resowner_private.h - * - *------------------------------------------------------------------------- - */ -#ifndef RESOWNER_PRIVATE_H -#define RESOWNER_PRIVATE_H - -#include "storage/dsm.h" -#include "storage/fd.h" -#include "storage/lock.h" -#include "utils/catcache.h" -#include "utils/plancache.h" -#include "utils/resowner.h" -#include "utils/snapshot.h" - - -/* support for buffer refcount management */ -extern void ResourceOwnerEnlargeBuffers(ResourceOwner owner); -extern void ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer); -extern void ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer); - -/* support for local lock management */ -extern void ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock); -extern void ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock); - -/* support for catcache refcount management */ -extern void ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner); -extern void ResourceOwnerRememberCatCacheRef(ResourceOwner owner, - HeapTuple tuple); -extern void ResourceOwnerForgetCatCacheRef(ResourceOwner owner, - HeapTuple tuple); -extern void ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner); -extern void ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, - CatCList *list); -extern void ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, - CatCList *list); - -/* support for relcache refcount management */ -extern void ResourceOwnerEnlargeRelationRefs(ResourceOwner owner); -extern void ResourceOwnerRememberRelationRef(ResourceOwner owner, - Relation rel); -extern void ResourceOwnerForgetRelationRef(ResourceOwner owner, - Relation rel); - -/* support for plancache refcount management */ -extern void ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner); -extern void ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, - CachedPlan *plan); -extern void ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, - CachedPlan *plan); - -/* support for tupledesc refcount management */ -extern void ResourceOwnerEnlargeTupleDescs(ResourceOwner owner); -extern void ResourceOwnerRememberTupleDesc(ResourceOwner owner, - TupleDesc tupdesc); -extern void ResourceOwnerForgetTupleDesc(ResourceOwner owner, - TupleDesc tupdesc); - -/* support for snapshot refcount management */ -extern void ResourceOwnerEnlargeSnapshots(ResourceOwner owner); -extern void ResourceOwnerRememberSnapshot(ResourceOwner owner, - Snapshot snapshot); -extern void ResourceOwnerForgetSnapshot(ResourceOwner owner, - Snapshot snapshot); - -/* support for temporary file management */ -extern void ResourceOwnerEnlargeFiles(ResourceOwner owner); -extern void ResourceOwnerRememberFile(ResourceOwner owner, - File file); -extern void ResourceOwnerForgetFile(ResourceOwner owner, - File file); - -/* support for dynamic shared memory management */ -extern void ResourceOwnerEnlargeDSMs(ResourceOwner owner); -extern void ResourceOwnerRememberDSM(ResourceOwner owner, - dsm_segment *); -extern void ResourceOwnerForgetDSM(ResourceOwner owner, - dsm_segment *); - -/* support for JITContext management */ -extern void ResourceOwnerEnlargeJIT(ResourceOwner owner); -extern void ResourceOwnerRememberJIT(ResourceOwner owner, - Datum handle); -extern void ResourceOwnerForgetJIT(ResourceOwner owner, - Datum handle); - -/* support for cryptohash context management */ -extern void ResourceOwnerEnlargeCryptoHash(ResourceOwner owner); -extern void ResourceOwnerRememberCryptoHash(ResourceOwner owner, - Datum handle); -extern void ResourceOwnerForgetCryptoHash(ResourceOwner owner, - Datum handle); - -/* support for HMAC context management */ -extern void ResourceOwnerEnlargeHMAC(ResourceOwner owner); -extern void ResourceOwnerRememberHMAC(ResourceOwner owner, - Datum handle); -extern void ResourceOwnerForgetHMAC(ResourceOwner owner, - Datum handle); - -#endif /* RESOWNER_PRIVATE_H */ diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 37cf4b2f76b..1a0e05df62b 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2223,8 +2223,10 @@ ReplicationStateOnDisk ResTarget ReservoirState ReservoirStateData -ResourceArray +ResourceElem ResourceOwner +ResourceOwnerData +ResourceOwnerFuncs ResourceReleaseCallback ResourceReleaseCallbackItem ResourceReleasePhase -- 2.30.2