diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c
index 45e012a63a5..c0c4f5d9ca7 100644
--- a/contrib/pg_prewarm/autoprewarm.c
+++ b/contrib/pg_prewarm/autoprewarm.c
@@ -96,6 +96,8 @@ static void apw_start_database_worker(void);
static bool apw_init_shmem(void);
static void apw_detach_shmem(int code, Datum arg);
static int apw_compare_blockinfo(const void *p, const void *q);
+static void autoprewarm_shmem_request(void);
+static shmem_request_hook_type prev_shmem_request_hook = NULL;
/* Pointer to shared-memory state. */
static AutoPrewarmSharedState *apw_state = NULL;
@@ -139,13 +141,26 @@ _PG_init(void)
MarkGUCPrefixReserved("pg_prewarm");
- RequestAddinShmemSpace(MAXALIGN(sizeof(AutoPrewarmSharedState)));
+ prev_shmem_request_hook = shmem_request_hook;
+ shmem_request_hook = autoprewarm_shmem_request;
/* Register autoprewarm worker, if enabled. */
if (autoprewarm)
apw_start_leader_worker();
}
+/*
+ * Requests any additional shared memory required for autoprewarm.
+ */
+static void
+autoprewarm_shmem_request(void)
+{
+ if (prev_shmem_request_hook)
+ prev_shmem_request_hook();
+
+ RequestAddinShmemSpace(MAXALIGN(sizeof(AutoPrewarmSharedState)));
+}
+
/*
* Main entry point for the leader autoprewarm process. Per-database workers
* have a separate entry point.
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 5c8b9ff9430..768cedd91a7 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -252,6 +252,7 @@ static int exec_nested_level = 0;
static int plan_nested_level = 0;
/* Saved hook values in case of unload */
+static shmem_request_hook_type prev_shmem_request_hook = NULL;
static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
static post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL;
static planner_hook_type prev_planner_hook = NULL;
@@ -316,6 +317,7 @@ PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
+static void pgss_shmem_request(void);
static void pgss_shmem_startup(void);
static void pgss_shmem_shutdown(int code, Datum arg);
static void pgss_post_parse_analyze(ParseState *pstate, Query *query,
@@ -451,17 +453,11 @@ _PG_init(void)
MarkGUCPrefixReserved("pg_stat_statements");
- /*
- * Request additional shared resources. (These are no-ops if we're not in
- * the postmaster process.) We'll allocate or attach to the shared
- * resources in pgss_shmem_startup().
- */
- RequestAddinShmemSpace(pgss_memsize());
- RequestNamedLWLockTranche("pg_stat_statements", 1);
-
/*
* Install hooks.
*/
+ prev_shmem_request_hook = shmem_request_hook;
+ shmem_request_hook = pgss_shmem_request;
prev_shmem_startup_hook = shmem_startup_hook;
shmem_startup_hook = pgss_shmem_startup;
prev_post_parse_analyze_hook = post_parse_analyze_hook;
@@ -480,6 +476,20 @@ _PG_init(void)
ProcessUtility_hook = pgss_ProcessUtility;
}
+/*
+ * shmem_request hook: request additional shared resources. We'll allocate or
+ * attach to the shared resources in pgss_shmem_startup().
+ */
+static void
+pgss_shmem_request(void)
+{
+ if (prev_shmem_request_hook)
+ prev_shmem_request_hook();
+
+ RequestAddinShmemSpace(pgss_memsize());
+ RequestNamedLWLockTranche("pg_stat_statements", 1);
+}
+
/*
* shmem_startup hook: allocate or attach to shared memory,
* then load any pre-existing statistics from file.
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index fbe718e3c28..3b0adc0704b 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -3402,22 +3402,30 @@ CREATE FUNCTION make_array(anyelement) RETURNS anyarray
startup. The add-in's shared library must be preloaded by specifying
it in
shared_preload_libraries.
+ The shared library should register a shmem_request_hook
+ in its _PG_init function. This
+ shmem_request_hook can reserve LWLocks or shared memory.
Shared memory is reserved by calling:
void RequestAddinShmemSpace(int size)
- from your _PG_init function.
+ from your shmem_request_hook.
LWLocks are reserved by calling:
void RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
- from _PG_init. This will ensure that an array of
+ from your shmem_request_hook. This will ensure that an array of
num_lwlocks LWLocks is available under the name
tranche_name. Use GetNamedLWLockTranche
to get a pointer to this array.
+
+ An example of a shmem_request_hook can be found in
+ contrib/pg_stat_statements/pg_stat_statements.c in the
+ PostgreSQL source tree.
+
To avoid possible race-conditions, each backend should use the LWLock
AddinShmemInitLock when connecting to and initializing
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index bf591f048d4..3b73e269564 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -1042,6 +1042,11 @@ PostmasterMain(int argc, char *argv[])
*/
InitializeMaxBackends();
+ /*
+ * Give preloaded libraries a chance to request additional shared memory.
+ */
+ process_shmem_requests();
+
/*
* Now that loadable modules have had their chance to request additional
* shared memory, determine the value of any runtime-computed GUCs that
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 75e456360be..26372d95b38 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -55,25 +55,21 @@ int shared_memory_type = DEFAULT_SHARED_MEMORY_TYPE;
shmem_startup_hook_type shmem_startup_hook = NULL;
static Size total_addin_request = 0;
-static bool addin_request_allowed = true;
-
/*
* RequestAddinShmemSpace
* Request that extra shmem space be allocated for use by
* a loadable module.
*
- * This is only useful if called from the _PG_init hook of a library that
- * is loaded into the postmaster via shared_preload_libraries. Once
- * shared memory has been allocated, calls will be ignored. (We could
- * raise an error, but it seems better to make it a no-op, so that
- * libraries containing such calls can be reloaded if needed.)
+ * This may only be called via the shmem_request_hook of a library that is
+ * loaded into the postmaster via shared_preload_libraries. Calls from
+ * elsewhere will fail.
*/
void
RequestAddinShmemSpace(Size size)
{
- if (IsUnderPostmaster || !addin_request_allowed)
- return; /* too late */
+ if (!process_shmem_requests_in_progress)
+ elog(FATAL, "cannot request additional shared memory outside shmem_request_hook");
total_addin_request = add_size(total_addin_request, size);
}
@@ -83,9 +79,6 @@ RequestAddinShmemSpace(Size size)
*
* If num_semaphores is not NULL, it will be set to the number of semaphores
* required.
- *
- * Note that this function freezes the additional shared memory request size
- * from loadable modules.
*/
Size
CalculateShmemSize(int *num_semaphores)
@@ -152,8 +145,7 @@ CalculateShmemSize(int *num_semaphores)
size = add_size(size, ShmemBackendArraySize());
#endif
- /* freeze the addin request size and include it */
- addin_request_allowed = false;
+ /* include additional requested shmem from preload libraries */
size = add_size(size, total_addin_request);
/* might as well round it off to a multiple of a typical page size */
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index fef462b1102..8aef909037d 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -243,8 +243,6 @@ int NamedLWLockTrancheRequests = 0;
/* points to data in shared memory: */
NamedLWLockTranche *NamedLWLockTrancheArray = NULL;
-static bool lock_named_request_allowed = true;
-
static void InitializeLWLocks(void);
static inline void LWLockReportWaitStart(LWLock *lock);
static inline void LWLockReportWaitEnd(void);
@@ -458,9 +456,6 @@ LWLockShmemSize(void)
for (i = 0; i < NamedLWLockTrancheRequests; i++)
size = add_size(size, strlen(NamedLWLockTrancheRequestArray[i].tranche_name) + 1);
- /* Disallow adding any more named tranches. */
- lock_named_request_allowed = false;
-
return size;
}
@@ -691,12 +686,9 @@ LWLockRegisterTranche(int tranche_id, const char *tranche_name)
* Request that extra LWLocks be allocated during postmaster
* startup.
*
- * This is only useful for extensions if called from the _PG_init hook
- * of a library that is loaded into the postmaster via
- * shared_preload_libraries. Once shared memory has been allocated, calls
- * will be ignored. (We could raise an error, but it seems better to make
- * it a no-op, so that libraries containing such calls can be reloaded if
- * needed.)
+ * This may only be called via the shmem_request_hook of a library that is
+ * loaded into the postmaster via shared_preload_libraries. Calls from
+ * elsewhere will fail.
*
* The tranche name will be user-visible as a wait event name, so try to
* use a name that fits the style for those.
@@ -706,8 +698,8 @@ RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
{
NamedLWLockTrancheRequest *request;
- if (IsUnderPostmaster || !lock_named_request_allowed)
- return; /* too late */
+ if (!process_shmem_requests_in_progress)
+ elog(FATAL, "cannot request additional LWLocks outside shmem_request_hook");
if (NamedLWLockTrancheRequestArray == NULL)
{
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 6ed584e1143..ec6a61594a4 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -1618,6 +1618,9 @@ char *local_preload_libraries_string = NULL;
bool process_shared_preload_libraries_in_progress = false;
bool process_shared_preload_libraries_done = false;
+shmem_request_hook_type shmem_request_hook = NULL;
+bool process_shmem_requests_in_progress = false;
+
/*
* load the shared libraries listed in 'libraries'
*
@@ -1701,6 +1704,18 @@ process_session_preload_libraries(void)
true);
}
+/*
+ * process any shared memory requests from preloaded libraries
+ */
+void
+process_shmem_requests(void)
+{
+ process_shmem_requests_in_progress = true;
+ if (shmem_request_hook)
+ shmem_request_hook();
+ process_shmem_requests_in_progress = false;
+}
+
void
pg_bindtextdomain(const char *domain)
{
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 53fd168d93c..0af130fbc5d 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -465,6 +465,7 @@ extern void BaseInit(void);
extern PGDLLIMPORT bool IgnoreSystemIndexes;
extern PGDLLIMPORT bool process_shared_preload_libraries_in_progress;
extern PGDLLIMPORT bool process_shared_preload_libraries_done;
+extern PGDLLIMPORT bool process_shmem_requests_in_progress;
extern PGDLLIMPORT char *session_preload_libraries_string;
extern PGDLLIMPORT char *shared_preload_libraries_string;
extern PGDLLIMPORT char *local_preload_libraries_string;
@@ -478,9 +479,13 @@ extern bool RecheckDataDirLockFile(void);
extern void ValidatePgVersion(const char *path);
extern void process_shared_preload_libraries(void);
extern void process_session_preload_libraries(void);
+extern void process_shmem_requests(void);
extern void pg_bindtextdomain(const char *domain);
extern bool has_rolreplication(Oid roleid);
+typedef void (*shmem_request_hook_type) (void);
+extern PGDLLIMPORT shmem_request_hook_type shmem_request_hook;
+
/* in executor/nodeHash.c */
extern size_t get_hash_memory_limit(void);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index dd1214977a8..4fb746930aa 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3651,6 +3651,7 @@ shm_mq_result
shm_toc
shm_toc_entry
shm_toc_estimator
+shmem_request_hook_type
shmem_startup_hook_type
sig_atomic_t
sigjmp_buf