diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml index 89116ae74c..ede2a5dea6 100644 --- a/doc/src/sgml/xfunc.sgml +++ b/doc/src/sgml/xfunc.sgml @@ -3397,90 +3397,136 @@ CREATE FUNCTION make_array(anyelement) RETURNS anyarray - Shared Memory and LWLocks + Shared Memory - - Add-ins can reserve LWLocks and an allocation of shared memory on server - 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: + + Requesting Shared Memory at Startup + + + Add-ins can reserve shared memory on server startup. To do so, the + add-in's shared library must be preloaded by specifying it in + shared_preload_libraries. + The shared library should also register a + shmem_request_hook in its + _PG_init function. This + shmem_request_hook can reserve shared memory by + calling: -void RequestAddinShmemSpace(int size) +void RequestAddinShmemSpace(Size size) - from your shmem_request_hook. - - - LWLocks are reserved by calling: + Each backend should obtain a pointer to the reserved shared memory by + calling: + +void *ShmemInitStruct(const char *name, Size size, bool *foundPtr) + + If this function sets foundPtr to + false, the caller should proceed to initialize the + contents of the reserved shared memory. If foundPtr + is set to true, the shared memory was already + initialized by another backend, and the caller need not initialize + further. + + + + To avoid race conditions, each backend should use the LWLock + AddinShmemInitLock when initializing its allocation + of shared memory, as shown here: + +static mystruct *ptr = NULL; +bool found; + +LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE); +ptr = ShmemInitStruct("my struct name", size, &found); +if (!found) +{ + ... initialize contents of shared memory ... + ptr->locks = GetNamedLWLockTranche("my tranche name"); +} +LWLockRelease(AddinShmemInitLock); + + shmem_startup_hook provides a convenient place for the + initialization code, but it is not strictly required that all such code + be placed in this hook. Each backend will execute the registered + shmem_startup_hook shortly after it attaches to shared + memory. Note that add-ins should still acquire + AddinShmemInitLock within this hook, as shown in the + example above. + + + + An example of a shmem_request_hook and + shmem_startup_hook can be found in + contrib/pg_stat_statements/pg_stat_statements.c in + the PostgreSQL source tree. + + + + + + LWLocks + + + Requesting LWLocks at Startup + + + Add-ins can reserve LWLocks on server startup. As with shared memory, + the add-in's shared library must be preloaded by specifying it in + shared_preload_libraries, + and the shared library should register a + shmem_request_hook in its + _PG_init function. This + shmem_request_hook can reserve LWLocks by calling: void RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks) - 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. - - - There is another, more flexible method of obtaining LWLocks. First, - allocate a tranche_id from a shared counter by - calling: + This ensures that an array of num_lwlocks LWLocks is + available under the name tranche_name. A pointer to + this array can be obtained by calling: + +LWLockPadded *GetNamedLWLockTranche(const char *tranche_name) + + + + + + Requesting LWLocks After Startup + + + There is another, more flexible method of obtaining LWLocks that can be + done after server startup and outside a + shmem_request_hook. To do so, first allocate a + tranche_id by calling: int LWLockNewTrancheId(void) - Next, each individual process using the tranche_id - should associate it with a tranche_name by calling: - -void LWLockRegisterTranche(int tranche_id, const char *tranche_name) - - It is also required to call LWLockInitialize once - per LWLock, passing the tranche_id as argument: + Next, initialize each LWLock, passing the new + tranche_id as an argument: void LWLockInitialize(LWLock *lock, int tranche_id) - A complete usage example of LWLockNewTrancheId, - LWLockInitialize and - LWLockRegisterTranche can be found in - contrib/pg_prewarm/autoprewarm.c in the - PostgreSQL source tree. - - - To avoid possible race-conditions, each backend should use the LWLock - AddinShmemInitLock when connecting to and initializing - its allocation of shared memory, as shown here: + Similar to shared memory, each backend should ensure that only one + process allocates a new tranche_id and initializes + each new LWLock. One way to do this is to only call these functions in + your shared memory initialization code with the + AddinShmemInitLock held exclusively. + + + + Finally, each backend using the tranche_id should + associate it with a tranche_name by calling: -static mystruct *ptr = NULL; - -if (!ptr) -{ - bool found; - - LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE); - ptr = ShmemInitStruct("my struct name", size, &found); - if (!found) - { - initialize contents of shmem area; - acquire any requested LWLocks using: - ptr->locks = GetNamedLWLockTranche("my tranche name"); - } - LWLockRelease(AddinShmemInitLock); -} +void LWLockRegisterTranche(int tranche_id, const char *tranche_name) - - - It is convenient to use shmem_startup_hook which allows - placing all the code responsible for initializing shared memory in one - place. When using shmem_startup_hook the extension - still needs to acquire AddinShmemInitLock in order to - work properly on all the supported platforms. - + + + + A complete usage example of LWLockNewTrancheId, + LWLockInitialize, and + LWLockRegisterTranche can be found in + contrib/pg_prewarm/autoprewarm.c in the + PostgreSQL source tree. + +