summaryrefslogtreecommitdiff
path: root/src/include
diff options
context:
space:
mode:
authorHeikki Linnakangas2024-01-31 10:31:02 +0000
committerHeikki Linnakangas2024-01-31 10:31:02 +0000
commit21d9c3ee4ef74e2229341d39811c97f85071c90a (patch)
tree5400cf27276fae4ffa9566f107bb27f084da119c /src/include
parent6a8ffe812d194ba6f4f26791b6388a4837d17d6c (diff)
Give SMgrRelation pointers a well-defined lifetime.
After calling smgropen(), it was not clear how long you could continue to use the result, because various code paths including cache invalidation could call smgrclose(), which freed the memory. Guarantee that the object won't be destroyed until the end of the current transaction, or in recovery, the commit/abort record that destroys the underlying storage. smgrclose() is now just an alias for smgrrelease(). It closes files and forgets all state except the rlocator, but keeps the SMgrRelation object valid. A new smgrdestroy() function is used by rare places that know there should be no other references to the SMgrRelation. The short version: * smgrclose() is now just an alias for smgrrelease(). It releases resources, but doesn't destroy until EOX * smgrdestroy() now frees memory, and should rarely be used. Existing code should be unaffected, but it is now possible for code that has an SMgrRelation object to use it repeatedly during a transaction as long as the storage hasn't been physically dropped. Such code would normally hold a lock on the relation. This also replaces the "ownership" mechanism of SMgrRelations with a pin counter. An SMgrRelation can now be "pinned", which prevents it from being destroyed at end of transaction. There can be multiple pins on the same SMgrRelation. In practice, the pin mechanism is only used by the relcache, so there cannot be more than one pin on the same SMgrRelation. Except with swap_relation_files XXX Author: Thomas Munro, Heikki Linnakangas Reviewed-by: Robert Haas <robertmhaas@gmail.com> Discussion: https://www.postgresql.org/message-id/CA%2BhUKGJ8NTvqLHz6dqbQnt2c8XCki4r2QvXjBQcXpVwxTY_pvA@mail.gmail.com
Diffstat (limited to 'src/include')
-rw-r--r--src/include/storage/smgr.h36
-rw-r--r--src/include/utils/rel.h18
-rw-r--r--src/include/utils/relcache.h2
3 files changed, 24 insertions, 32 deletions
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index 527cd2a0568..8d90d1f8e22 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -21,29 +21,21 @@
/*
* smgr.c maintains a table of SMgrRelation objects, which are essentially
* cached file handles. An SMgrRelation is created (if not already present)
- * by smgropen(), and destroyed by smgrclose(). Note that neither of these
- * operations imply I/O, they just create or destroy a hashtable entry.
- * (But smgrclose() may release associated resources, such as OS-level file
+ * by smgropen(), and destroyed by smgrdestroy(). Note that neither of these
+ * operations imply I/O, they just create or destroy a hashtable entry. (But
+ * smgrdestroy() may release associated resources, such as OS-level file
* descriptors.)
*
- * An SMgrRelation may have an "owner", which is just a pointer to it from
- * somewhere else; smgr.c will clear this pointer if the SMgrRelation is
- * closed. We use this to avoid dangling pointers from relcache to smgr
- * without having to make the smgr explicitly aware of relcache. There
- * can't be more than one "owner" pointer per SMgrRelation, but that's
- * all we need.
- *
- * SMgrRelations that do not have an "owner" are considered to be transient,
- * and are deleted at end of transaction.
+ * An SMgrRelation may be "pinned", to prevent it from being destroyed while
+ * it's in use. We use this to prevent pointers relcache to smgr from being
+ * invalidated. SMgrRelations that are not pinned are deleted at end of
+ * transaction.
*/
typedef struct SMgrRelationData
{
/* rlocator is the hashtable lookup key, so it must be first! */
RelFileLocatorBackend smgr_rlocator; /* relation physical identifier */
- /* pointer to owning pointer, or NULL if none */
- struct SMgrRelationData **smgr_owner;
-
/*
* The following fields are reset to InvalidBlockNumber upon a cache flush
* event, and hold the last known size for each fork. This information is
@@ -68,7 +60,11 @@ typedef struct SMgrRelationData
int md_num_open_segs[MAX_FORKNUM + 1];
struct _MdfdVec *md_seg_fds[MAX_FORKNUM + 1];
- /* if unowned, list link in list of all unowned SMgrRelations */
+ /*
+ * Pinning support. If unpinned (ie. pincount == 0), 'node' is a list
+ * link in list of all unpinned SMgrRelations.
+ */
+ int pincount;
dlist_node node;
} SMgrRelationData;
@@ -80,13 +76,13 @@ typedef SMgrRelationData *SMgrRelation;
extern void smgrinit(void);
extern SMgrRelation smgropen(RelFileLocator rlocator, BackendId backend);
extern bool smgrexists(SMgrRelation reln, ForkNumber forknum);
-extern void smgrsetowner(SMgrRelation *owner, SMgrRelation reln);
-extern void smgrclearowner(SMgrRelation *owner, SMgrRelation reln);
+extern void smgrpin(SMgrRelation reln);
+extern void smgrunpin(SMgrRelation reln);
extern void smgrclose(SMgrRelation reln);
-extern void smgrcloseall(void);
-extern void smgrcloserellocator(RelFileLocatorBackend rlocator);
+extern void smgrdestroyall(void);
extern void smgrrelease(SMgrRelation reln);
extern void smgrreleaseall(void);
+extern void smgrreleaserellocator(RelFileLocatorBackend rlocator);
extern void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo);
extern void smgrdosyncall(SMgrRelation *rels, int nrels);
extern void smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index a584b1ddff3..ab9fa4faf9f 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -561,18 +561,15 @@ typedef struct ViewOptions
*
* Very little code is authorized to touch rel->rd_smgr directly. Instead
* use this function to fetch its value.
- *
- * Note: since a relcache flush can cause the file handle to be closed again,
- * it's unwise to hold onto the pointer returned by this function for any
- * long period. Recommended practice is to just re-execute RelationGetSmgr
- * each time you need to access the SMgrRelation. It's quite cheap in
- * comparison to whatever an smgr function is going to do.
*/
static inline SMgrRelation
RelationGetSmgr(Relation rel)
{
if (unlikely(rel->rd_smgr == NULL))
- smgrsetowner(&(rel->rd_smgr), smgropen(rel->rd_locator, rel->rd_backend));
+ {
+ rel->rd_smgr = smgropen(rel->rd_locator, rel->rd_backend);
+ smgrpin(rel->rd_smgr);
+ }
return rel->rd_smgr;
}
@@ -584,10 +581,11 @@ static inline void
RelationCloseSmgr(Relation relation)
{
if (relation->rd_smgr != NULL)
+ {
+ smgrunpin(relation->rd_smgr);
smgrclose(relation->rd_smgr);
-
- /* smgrclose should unhook from owner pointer */
- Assert(relation->rd_smgr == NULL);
+ relation->rd_smgr = NULL;
+ }
}
#endif /* !FRONTEND */
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 058da62a726..18c32ea7008 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -129,8 +129,6 @@ extern void RelationCacheInvalidateEntry(Oid relationId);
extern void RelationCacheInvalidate(bool debug_discard);
-extern void RelationCloseSmgrByOid(Oid relationId);
-
#ifdef USE_ASSERT_CHECKING
extern void AssertPendingSyncs_RelationCache(void);
#else