diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c index 45e012a63a..c0c4f5d9ca 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 5c8b9ff943..768cedd91a 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 fbe718e3c2..3b0adc0704 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 bf591f048d..3b73e26956 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 75e456360b..26372d95b3 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 fef462b110..8aef909037 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 6ed584e114..ec6a61594a 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 53fd168d93..0af130fbc5 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 dd1214977a..4fb746930a 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