I spent some time cleaning up the new registration machinery. I didn't
look at the "resizeable" part yet, but it should fit in nicely, as
Ashutosh demonstrated.
I added a lot of comments, changed the existing docs on how to allocate
shared memory in extensions to use the machinery, and tons of other
cleanups. I'm getting pretty happy with this, but there are a couple of
weak spots:
Firstly, I'm not sure what to do with ShmemRegisterHash() and the
'HASHCTL *infoP' argument to it. I feel it'd be nicer if the HASHCTL was
just part of the ShmemHashDesc struct, but I'm not sure if that fits all
the callers. I'll have to try that out I guess.
Secondly, I'm not 100% happy with the facilities we provide to
extensions. The lifecycle of _PG_init() and shmem_request/startup_hooks
is a little messy. The status quo is that a shared library gets control
in three different places:
1. _PG_init() gets called early at postmaster startup, if the library is
in shared_preload_libraries. If it's not in shared_preload_libraries, it
gets called whenever the module is loaded.
2. The library can install a shmem_request_hook, which gets called early
at postmaster startup, but after initializing the MaxBackends GUC. It
only gets called when the library is loaded via shared_preload_libraries.
3. The library can install a shmem_startup_hook. It gets called later at
postmaster startup, after the shared memory segment has been allocated.
In EXEC_BACKEND mode it also gets called at backend startup. It does not
get called if the library is not listed in shared_preload_libraries.
None of these is quite the right moment to call the new
ShmemRegisterStruct() function. _PG_init() is too early if the extension
needs MaxBackends for sizing the shared memory area. shmem_request_hook
is otherwise good, but in EXEC_BACKEND mode, the ShmemRegisterStruct()
function needs to also be called backend startup and shmem_request_hook
is not called at backend startup. shmem_startup_hook() is too late.
For now, I documented that an extension should call
ShmemRegisterStruct() from _PG_init(), but may adjust the size in the
shmem_request_hook, if needed.
Another wrinkle here is that you still need the shmem_request_hook, if
you want to call RequestNamedLWLockTranche(). It cannot be called from
_PG_init(). I'm not sure why that is.
So I think that requires a little more refactoring, I think an extension
should need to use shmem_request/startup_hook with the new APIs anymore.
It should provide more ergonomic callbacks or other mechanisms to
accomplish the same things.
- Heikki
From f78982315977b7e6a103351a961a5f02f4ca5171 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Thu, 5 Mar 2026 22:09:35 +0200
Subject: [PATCH v2 1/4] Fix pointer type of ShmemAllocatorData->index
Cosmetic.
---
src/backend/storage/ipc/shmem.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 9f362ce8641..cfdd92bb2b5 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -87,7 +87,7 @@
typedef struct ShmemAllocatorData
{
Size free_offset; /* offset to first free space from ShmemBase */
- HTAB *index; /* copy of ShmemIndex */
+ HASHHDR *index; /* location of ShmemIndex */
/* protects shared memory and LWLock allocation */
slock_t shmem_lock;
--
2.47.3
From 4f18086d7849cad65a4451a1f01d3252108965be Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Fri, 6 Mar 2026 12:34:49 +0200
Subject: [PATCH v2 2/4] Introduce a new mechanism for registering shared
memory areas
Each shared memory area is registered with a "descriptor struct" that
contains the parameters like name and size. A struct makes it easier
to add optional fields in the future; the additional fields can just
be left as zeros.
This merges the separate [Subsystem]ShmemSize() and
[Subsystem]ShmemInit() phases at postmaster startup. Each subsystem is
now called into just once, before the shared memory segment has been
allocated, to register the subsystems shared memory areas. The
registration includes the size, which replaces the
[Subsystem]ShmemSize() calls, and a pointer to an initialization
callback function, which replaces the [Subsystem]ShmemInit()
calls. This is more ergonomic, as you only need to calculate the size
once, when you register the struct.
This replaces ShmemInitStruct() and ShmemInitHash(), which become just
backwards-compatibility wrappers around the new functions. In future
commits, I plan to replace all ShmemInitStruct() and ShmemInitHash()
calls with the new functions, although we'll still need to keep them
around for extensions.
---
doc/src/sgml/system-views.sgml | 4 +-
doc/src/sgml/xfunc.sgml | 119 ++--
src/backend/bootstrap/bootstrap.c | 2 +
src/backend/postmaster/launch_backend.c | 4 +
src/backend/postmaster/postmaster.c | 5 +
src/backend/storage/ipc/ipci.c | 57 +-
src/backend/storage/ipc/shmem.c | 821 +++++++++++++++++-------
src/backend/tcop/postgres.c | 3 +
src/include/storage/ipc.h | 1 +
src/include/storage/shmem.h | 136 +++-
10 files changed, 831 insertions(+), 321 deletions(-)
diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index e5fe423fc61..a0baed339fe 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -4254,8 +4254,8 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
<para>
Anonymous allocations are allocations that have been made
with <literal>ShmemAlloc()</literal> directly, rather than via
- <literal>ShmemInitStruct()</literal> or
- <literal>ShmemInitHash()</literal>.
+ <literal>ShmemRegisterStruct()</literal> or
+ <literal>ShmemRegisterHash()</literal>.
</para>
<para>
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index 70e815b8a2c..dfe03557fa9 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -3628,58 +3628,91 @@ CREATE FUNCTION make_array(anyelement) RETURNS anyarray
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
<xref linkend="guc-shared-preload-libraries"/><indexterm><primary>shared_preload_libraries</primary></indexterm>.
- The shared library should also register a
- <literal>shmem_request_hook</literal> in its
- <function>_PG_init</function> function. This
- <literal>shmem_request_hook</literal> can reserve shared memory by
- calling:
-<programlisting>
-void RequestAddinShmemSpace(Size size)
-</programlisting>
- Each backend should obtain a pointer to the reserved shared memory by
- calling:
+ The shared library should register the shared memory allocation in
+ its <function>_PG_init</function> function. Here is an example:
<programlisting>
-void *ShmemInitStruct(const char *name, Size size, bool *foundPtr)
-</programlisting>
- If this function sets <literal>foundPtr</literal> to
- <literal>false</literal>, the caller should proceed to initialize the
- contents of the reserved shared memory. If <literal>foundPtr</literal>
- is set to <literal>true</literal>, the shared memory was already
- initialized by another backend, and the caller need not initialize
- further.
- </para>
+typedef struct MyShmemData {
+ ... shared memory contents ...
+ LWLock *lock;
+} MyShmemData;
+
+static MyShmemData *MyShmem; /* pointer to the struct in shared memory */
+
+static void my_shmem_request(void);
+static void my_shmem_init(void *arg);
+
+static ShmemStructDesc MyShmemDesc = {
+ .name = "My shmem area",
+ .size = sizeof(MyShmemData),
+ .init_fn = my_shmem_init,
+ .ptr = &MyShmem,
+};
+
+/*
+ * Module load callback
+ */
+void
+_PG_init(void)
+{
+ /*
+ * In order to create our shared memory area, we have to be loaded via
+ * shared_preload_libraries.
+ */
+ if (!process_shared_preload_libraries_in_progress)
+ return;
- <para>
- To avoid race conditions, each backend should use the LWLock
- <function>AddinShmemInitLock</function> when initializing its allocation
- of shared memory, as shown here:
-<programlisting>
-static mystruct *ptr = NULL;
-bool found;
+ /* Register our shared memory needs */
+ ShmemRegisterStruct(&MyShmemDesc);
+
+ /* Set up a hook for requesting additional resources */
+ prev_shmem_request_hook = shmem_request_hook;
+ shmem_request_hook = my_shmem_request;
+}
-LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
-ptr = ShmemInitStruct("my struct name", size, &found);
-if (!found)
+static void
+my_shmem_request(void *arg)
+{
+ if (prev_shmem_request_hook)
+ prev_shmem_request_hook();
+
+ RequestNamedLWLockTranche("my tranche name", 1);
+}
+
+/* callback to initialize the contents of the MyShmem area at startup */
+static void
+my_shmem_init(void *arg)
{
... initialize contents of shared memory ...
- ptr->locks = GetNamedLWLockTranche("my tranche name");
+ MyShmem->locks = GetNamedLWLockTranche("my tranche name");
}
-LWLockRelease(AddinShmemInitLock);
+
</programlisting>
- <literal>shmem_startup_hook</literal> provides a convenient place for the
- initialization code, but it is not strictly required that all such code
- be placed in this hook. On Windows (and anywhere else where
- <literal>EXEC_BACKEND</literal> is defined), each backend executes the
- registered <literal>shmem_startup_hook</literal> shortly after it
- attaches to shared memory, so add-ins should still acquire
- <function>AddinShmemInitLock</function> within this hook, as shown in the
- example above. On other platforms, only the postmaster process executes
- the <literal>shmem_startup_hook</literal>, and each backend automatically
- inherits the pointers to shared memory.
+ The <function>ShmemRegisterStruct()</function> call doesn't immediately
+ allocate or initialize the memory, it merely reserves the space for when
+ the shared memory segment is allocated later in the startup sequence.
+ When the memory is allocated, the registered
+ <function>init_fn</function> callback is called to initialize it.
+ </para>
+ <para>
+ The <function>init_fn()</function> callback is normally called at
+ postmaster startup, when no other processes are running yet and no
+ locking is required. However, if shared memory area is registered
+ after system start, e.g. in an extension that is not in
+ <xref linkend="guc-shared-preload-libraries"/><indexterm><primary>shared_preload_libraries</primary></indexterm>,
+ <function>ShmemRegisterStruct()</function> will immediately call
+ the <function>init_fn</function> callback. In that case, it holds a
+ lock internally that prevents concurrent shmem allocations.
+ </para>
+ <para>
+ On Windows, the <literal>attach_fn</literal> callback is additionally
+ called at every backend startup. It can be used for initializing
+ additional per-backend state related to the shared memory area that is
+ inherited via <function>fork()</function> on other systems. On other
+ platforms, the <literal>attach_fn</literal> callback is only called for
+ structs that are registered after system startup.
</para>
-
<para>
- An example of a <literal>shmem_request_hook</literal> and
+ An complete example of a <literal>shmem_request_hook</literal> and
<literal>shmem_startup_hook</literal> can be found in
<filename>contrib/pg_stat_statements/pg_stat_statements.c</filename> in
the <productname>PostgreSQL</productname> source tree.
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e7699be55aa..42322f9a9e1 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -329,6 +329,8 @@ BootstrapModeMain(int argc, char *argv[], bool check_only)
InitializeFastPathLocks();
+ RegisterShmemStructs();
+
CreateSharedMemoryAndSemaphores();
/*
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 30357845729..fecae827e5b 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -49,6 +49,7 @@
#include "replication/walreceiver.h"
#include "storage/dsm.h"
#include "storage/io_worker.h"
+#include "storage/ipc.h"
#include "storage/pg_shmem.h"
#include "tcop/backend_startup.h"
#include "utils/memutils.h"
@@ -677,7 +678,10 @@ SubPostmasterMain(int argc, char *argv[])
/* Restore basic shared memory pointers */
if (UsedShmemSegAddr != NULL)
+ {
InitShmemAllocator(UsedShmemSegAddr);
+ RegisterShmemStructs();
+ }
/*
* Run the appropriate Main function
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 3fac46c402b..702c646f0d7 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -958,6 +958,9 @@ PostmasterMain(int argc, char *argv[])
*/
InitializeFastPathLocks();
+ /* Register the shared memory needs of all core subsystems. */
+ RegisterShmemStructs();
+
/*
* Give preloaded libraries a chance to request additional shared memory.
*/
@@ -3236,6 +3239,8 @@ PostmasterStateMachine(void)
LocalProcessControlFile(true);
/* re-create shared memory and semaphores */
+ ResetShmemAllocator();
+ RegisterShmemStructs();
CreateSharedMemoryAndSemaphores();
UpdatePMState(PM_STARTUP);
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 1f7e933d500..97b2de994f2 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -98,10 +98,12 @@ CalculateShmemSize(void)
* during the actual allocation phase.
*/
size = 100000;
- size = add_size(size, hash_estimate_size(SHMEM_INDEX_SIZE,
- sizeof(ShmemIndexEnt)));
+ size = add_size(size, ShmemRegisteredSize());
+
size = add_size(size, dsm_estimate_size());
size = add_size(size, DSMRegistryShmemSize());
+
+ /* legacy subsystems */
size = add_size(size, BufferManagerShmemSize());
size = add_size(size, LockManagerShmemSize());
size = add_size(size, PredicateLockShmemSize());
@@ -217,6 +219,10 @@ CreateSharedMemoryAndSemaphores(void)
*/
InitShmemAllocator(seghdr);
+ /* Reserve space for semaphores. */
+ if (!IsUnderPostmaster)
+ PGReserveSemaphores(ProcGlobalSemas());
+
/* Initialize subsystems */
CreateOrAttachShmemStructs();
@@ -230,6 +236,22 @@ CreateSharedMemoryAndSemaphores(void)
shmem_startup_hook();
}
+/*
+ * Early initialization of various subsystems, giving them a change to
+ * register their shared memory needs before the shared memory segment is
+ * allocated.
+ */
+void
+RegisterShmemStructs(void)
+{
+ /*
+ * TODO: Not used in any built-in subsystems yet. In the future, most of
+ * the calls *ShmemInit() calls in CreateOrAttachShmemStructs(), and
+ * *ShmemSize() calls in CalculateShmemSize() will be replaced by calls
+ * into the subsystems from here.
+ */
+}
+
/*
* Initialize various subsystems, setting up their data structures in
* shared memory.
@@ -248,16 +270,27 @@ CreateSharedMemoryAndSemaphores(void)
static void
CreateOrAttachShmemStructs(void)
{
- /*
- * Now initialize LWLocks, which do shared memory allocation and are
- * needed for InitShmemIndex.
- */
- CreateLWLocks();
-
- /*
- * Set up shmem.c index hashtable
- */
- InitShmemIndex();
+#ifdef EXEC_BACKEND
+ if (IsUnderPostmaster)
+ {
+ /*
+ * ShmemAttachRegistered() uses LWLocks. Fortunately, LWLocks don't
+ * need any special attaching.
+ */
+ ShmemAttachRegistered();
+ }
+ else
+#endif
+ {
+ /*
+ * Initialize LWLocks first, in case any of the shmem init function
+ * use LWLocks. (Nothing else can be running during startup though,
+ * so it's pretty useless for them to do any locking, but we still
+ * allow it.)
+ */
+ CreateLWLocks();
+ ShmemInitRegistered();
+ }
dsm_shmem_init();
DSMRegistryShmemInit();
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index cfdd92bb2b5..c20bec2bafb 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -19,48 +19,95 @@
* methods). The routines in this file are used for allocating and
* binding to shared memory data structures.
*
- * NOTES:
- * (a) There are three kinds of shared memory data structures
- * available to POSTGRES: fixed-size structures, queues and hash
- * tables. Fixed-size structures contain things like global variables
- * for a module and should never be allocated after the shared memory
- * initialization phase. Hash tables have a fixed maximum size, but
- * their actual size can vary dynamically. When entries are added
- * to the table, more space is allocated. Queues link data structures
- * that have been allocated either within fixed-size structures or as hash
- * buckets. Each shared data structure has a string name to identify
- * it (assigned in the module that declares it).
- *
- * (b) During initialization, each module looks for its
- * shared data structures in a hash table called the "Shmem Index".
- * If the data structure is not present, the caller can allocate
- * a new one and initialize it. If the data structure is present,
- * the caller "attaches" to the structure by initializing a pointer
- * in the local address space.
- * The shmem index has two purposes: first, it gives us
- * a simple model of how the world looks when a backend process
- * initializes. If something is present in the shmem index,
- * it is initialized. If it is not, it is uninitialized. Second,
- * the shmem index allows us to allocate shared memory on demand
- * instead of trying to preallocate structures and hard-wire the
- * sizes and locations in header files. If you are using a lot
- * of shared memory in a lot of different places (and changing
- * things during development), this is important.
- *
- * (c) In standard Unix-ish environments, individual backends do not
- * need to re-establish their local pointers into shared memory, because
- * they inherit correct values of those variables via fork() from the
- * postmaster. However, this does not work in the EXEC_BACKEND case.
- * In ports using EXEC_BACKEND, new backends have to set up their local
- * pointers using the method described in (b) above.
- *
- * (d) memory allocation model: shared memory can never be
- * freed, once allocated. Each hash table has its own free list,
- * so hash buckets can be reused when an item is deleted. However,
- * if one hash table grows very large and then shrinks, its space
- * cannot be redistributed to other tables. We could build a simple
- * hash bucket garbage collector if need be. Right now, it seems
- * unnecessary.
+ * There are two kinds of shared memory data structures: fixed-size structures
+ * and hash tables. Fixed-size structures contain things like global
+ * variables for a module and should never be allocated after the shared
+ * memory initialization phase. Hash tables have a fixed maximum size, but
+ * their actual size can vary dynamically. When entries are added to the
+ * table, more space is allocated. Each shared data structure and hash has a
+ * string name to identify it, specified in the descriptor when its
+ * registered.
+ *
+ * Shared memory structures (and hash table entries) are mapped to the address
+ * in each backend process, so you can safely use pointers to other parts of
+ * shared memory in the shared memory structures.
+ *
+ * Shared memory can never be freed, once allocated. Each hash table has its
+ * own free list, so hash buckets can be reused when an item is deleted.
+ * However, if one hash table grows very large and then shrinks, its space
+ * cannot be redistributed to other tables. We could build a simple hash
+ * bucket garbage collector if need be. Right now, it seems unnecessary.
+ *
+ * Usage
+ * -----
+ *
+ * To allocate a shared memory area, fill in the name, size, and any other
+ * options in ShmemStructDesc, and call ShmemRegisterStruct(). Leave any
+ * unused fields as zeros.
+ *
+ * typedef struct MyShmemData {
+ * ...
+ * } MyShmemData;
+ *
+ * static MyShmemData *MyShmem;
+ *
+ * static void my_shmem_init(void *arg);
+ *
+ * static ShmemStructDesc MyShmemDesc = {
+ * .name = "My shmem area",
+ * .size = sizeof(MyShmemData),
+ * .init_fn = my_shmem_init,
+ * .ptr = &MyShmem,
+ * };
+ *
+ * In the subsystem's initialization code, or in _PG_init() or shmem_request_hook
+ * in extensions, call ShmemRegisterStruct():
+ *
+ * ShmemRegisterStruct(&MyShmemDesc)
+ *
+ *
+ * Lifecycle
+ * ---------
+ *
+ * RegisterShmemStructs() is called at postmaster startup, before deciding the
+ * size of the global shared memory segment. To add a new shared memory area,
+ * call its Register function from RegisterShmemStructs().
+ *
+ * Once all the registrations have been done, postmaster calls
+ * ShmemRegisteredSize() to add up the sizes of all the registered areas
+ *
+ * After allocating the shared memory segment, postmaster calls
+ * ShmemInitRegistered(), which calls the init_fn callbacks of each registered
+ * area, in the order that they were registered.
+ *
+ * In standard Unix-ish environments, individual backends do not need to
+ * re-establish their local pointers into shared memory, because they inherit
+ * correct values of those variables via fork() from the postmaster. However,
+ * this does not work in the EXEC_BACKEND case. In ports using EXEC_BACKEND,
+ * backend startup also calls RegisterShmemStructs(), followed by
+ * ShmemAttachRegistered(), which re-establishes the pointer variables
+ * (*ShmemStructDesc->ptr), and calls the attach_fn callback, if any, for
+ * additional per-backend setup.
+ *
+ * Legacy ShmemInitStruct()/ShmemInitHash() functions
+ * --------------------------------------------------
+ *
+ * ShmemInitStruct()/ShmemInitHash() is another way of registring shmem
+ * areas. It pre-dates the ShmemRegisterStruct()/ShmemRegisterHash()
+ * functions, and should not be used in new code, but as of this writing it is
+ * still widely used in extensions.
+ *
+ * To allocate a shmem area with ShmemInitStruct(), you need to separately
+ * register the size needed for the area by calling RequestAddinShmemSpace()
+ * from the extension's shmem_request_hook, and allocate the area by calling
+ * ShmemInitStruct() from the extension's shmem_startup_hook. There are no
+ * init/attach callbacks; the caller of ShmemInitStruct() must check the
+ * return status of ShmemInitStruct() and initialize the struct if it was not
+ * previously initialized.
+ *
+ *
+ * More legacy: Calling ShmemAlloc() directly
+ * ------------------------------------------
*/
#include "postgres.h"
@@ -76,6 +123,24 @@
#include "storage/spin.h"
#include "utils/builtins.h"
+/*
+ * Array of registered shared memory areas.
+ *
+ * This is in process private memory, although on Unix-like systems, we expect
+ * all the registrations to happen at postmaster startup time, and be
+ * inherited by all the child processes. Extensions may register additional
+ * areas after startup, but only areas registered at postmaster startup are
+ * included in the estimate for the total memory needed for shared memory. If
+ * any non-trivial allocations are made after startup, there might not be
+ * enough shared memory available.
+ */
+static ShmemStructDesc **registered_shmem_areas;
+static int num_registered_shmem_areas = 0;
+static int max_registered_shmem_areas = 0; /* allocated size of the array */
+
+/* estimated size of registered_shmem_areas (not a hard limit) */
+#define INITIAL_REGISTRY_SIZE (64)
+
/*
* This is the first data structure stored in the shared memory segment, at
* the offset that PGShmemHeader->content_offset points to. Allocations by
@@ -95,6 +160,9 @@ typedef struct ShmemAllocatorData
static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void shmem_hash_init(void *arg);
+static void shmem_hash_attach(void *arg);
+
/* shared memory global variables */
static PGShmemHeader *ShmemSegHdr; /* shared mem segment header */
@@ -103,24 +171,328 @@ static void *ShmemEnd; /* end+1 address of shared memory */
static ShmemAllocatorData *ShmemAllocator;
slock_t *ShmemLock; /* points to ShmemAllocator->shmem_lock */
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+
+
+/*
+ * ShmemIndex is a global directory of shmem areas, itself also stored in the
+ * shared memory.
+ */
+static HTAB *ShmemIndex;
+
+ /* max size of data structure string name */
+#define SHMEM_INDEX_KEYSIZE (48)
+
+/*
+ * # of additional entries to reserve in the shmem index table, for allocations
+ * after postmaster startup (not a hard limit)
+ */
+#define SHMEM_INDEX_ADDITIONAL_SIZE (64)
+
+/* this is a hash bucket in the shmem index table */
+typedef struct
+{
+ char key[SHMEM_INDEX_KEYSIZE]; /* string name */
+ void *location; /* location in shared mem */
+ Size size; /* # bytes requested for the structure */
+ Size allocated_size; /* # bytes actually allocated */
+} ShmemIndexEnt;
/* To get reliable results for NUMA inquiry we need to "touch pages" once */
static bool firstNumaTouch = true;
Datum pg_numa_available(PG_FUNCTION_ARGS);
+static bool shmem_initialized = false;
+
+/*
+ * ShmemRegisterStruct() --- register a shared memory struct
+ *
+ * Subsystems call this to register their shared memory needs. That should be
+ * done early in postmaster startup, before the shared memory segment has been
+ * created, so that the size can be included in the estimate for total amount
+ * of shared memory needed. We set aside a small amount of memory for
+ * allocations that happen later, for the benefit of non-preloaded extensions,
+ * but that should not be relied upon.
+ *
+ * In core subsystems, each subsystem's registration functions is called from
+ * RegisterShmemStructs(). In extensions, this should be called from the
+ * _PG_init() initializer or the 'shmem_request_hook'. In EXEC_BACKEND mode,
+ * this also needs to be called in each child process, to reattach and set the
+ * pointer to the shared memory area, usually in a global variable. Calling
+ * this from the _PG_init() initializer or the 'shmem_request_hook' takes
+ * care of that too.
+ *
+ * Returns true if the struct was already initialized in shared memory, and
+ * we merely attached to it.
+ */
+bool
+ShmemRegisterStruct(ShmemStructDesc *desc)
+{
+ bool found;
+
+ /* Check that it's not already registered in this process */
+ for (int i = 0; i < num_registered_shmem_areas; i++)
+ {
+ ShmemStructDesc *existing = registered_shmem_areas[i];
+
+ if (strcmp(existing->name, desc->name) == 0)
+ elog(ERROR, "shared memory struct \"%s\" is already registered",
+ desc->name);
+ }
+
+ /* desc->ptr can be non-NULL when re-initializing after crash */
+ if (desc->ptr)
+ *desc->ptr = NULL;
+
+ /* Add the descriptor to the array, growing the array if needed */
+ if (num_registered_shmem_areas == max_registered_shmem_areas)
+ {
+ int new_size;
+
+ if (registered_shmem_areas)
+ {
+ new_size = max_registered_shmem_areas * 2;
+ registered_shmem_areas = repalloc(registered_shmem_areas,
+ new_size * sizeof(ShmemStructDesc *));
+ }
+ else
+ {
+ new_size = INITIAL_REGISTRY_SIZE;
+ registered_shmem_areas = MemoryContextAlloc(TopMemoryContext,
+ new_size * sizeof(ShmemStructDesc *));
+ }
+ max_registered_shmem_areas = new_size;
+ }
+ registered_shmem_areas[num_registered_shmem_areas++] = desc;
+
+ /*
+ * If called after postmaster startup, we need to immediately also
+ * initialize or attach to the area.
+ */
+ if (shmem_initialized)
+ {
+ ShmemIndexEnt *index_entry;
+
+ LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
+
+ /* look it up in the shmem index */
+ index_entry = (ShmemIndexEnt *)
+ hash_search(ShmemIndex, desc->name, HASH_ENTER_NULL, &found);
+ if (!index_entry)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("could not create ShmemIndex entry for data structure \"%s\"",
+ desc->name)));
+ }
+ if (found)
+ {
+ /* Already present, just attach to it */
+ if (index_entry->size != desc->size)
+ elog(ERROR, "shared memory struct \"%s\" is already registered with different size",
+ desc->name);
+ if (desc->ptr)
+ *desc->ptr = index_entry->location;
+ if (desc->attach_fn)
+ desc->attach_fn(desc->attach_fn_arg);
+ }
+ else
+ {
+ /* This is the first time. Initialize it like ShmemInitRegistered() would */
+ size_t allocated_size;
+ void *structPtr;
+
+ structPtr = ShmemAllocRaw(desc->size, &allocated_size);
+ if (structPtr == NULL)
+ {
+ /* out of memory; remove the failed ShmemIndex entry */
+ hash_search(ShmemIndex, desc->name, HASH_REMOVE, NULL);
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("not enough shared memory for data structure"
+ " \"%s\" (%zu bytes requested)",
+ desc->name, desc->size)));
+ }
+ index_entry->size = desc->size;
+ index_entry->allocated_size = allocated_size;
+ index_entry->location = structPtr;
+ if (desc->ptr)
+ *desc->ptr = index_entry->location;
+
+ /* XXX: if this errors out, the areas is left in a half-initialized state */
+ if (desc->init_fn)
+ desc->init_fn(desc->init_fn_arg);
+ }
+
+ LWLockRelease(ShmemIndexLock);
+ }
+ else
+ found = false;
+
+ return found;
+}
+
+/*
+ * ShmemRegisteredSize() --- estimate the total size of all pre-registered
+ * shared memory structures.
+ *
+ * This runs once at postmaster startup, before the shared memory segment has
+ * been created.
+ */
+size_t
+ShmemRegisteredSize(void)
+{
+ size_t size;
+
+ /* memory needed for the ShmemIndex */
+ size = hash_estimate_size(num_registered_shmem_areas + SHMEM_INDEX_ADDITIONAL_SIZE,
+ sizeof(ShmemIndexEnt));
+
+ /* memory needed for all the registered areas */
+ for (int i = 0; i < num_registered_shmem_areas; i++)
+ {
+ ShmemStructDesc *desc = registered_shmem_areas[i];
+
+ size = add_size(size, desc->size);
+ size = add_size(size, desc->extra_size);
+ }
+
+ return size;
+}
+
+/*
+ * ShmemInitRegistered() --- allocate and initialize pre-registered shared
+ * memory structures.
+ *
+ * This runs once at postmaster startup, after the shared memory segment has
+ * been created.
+ */
+void
+ShmemInitRegistered(void)
+{
+ /* Should be called only by the postmaster or a standalone backend. */
+ Assert(!IsUnderPostmaster);
+ Assert(!shmem_initialized);
+
+ /*
+ * Initialize all the registered memory areas. There are no concurrent
+ * processes yet, so no need for locking.
+ */
+ for (int i = 0; i < num_registered_shmem_areas; i++)
+ {
+ ShmemStructDesc *desc = registered_shmem_areas[i];
+ size_t allocated_size;
+ void *structPtr;
+ bool found;
+ ShmemIndexEnt *index_entry;
+
+ /* look it up in the shmem index */
+ index_entry = (ShmemIndexEnt *)
+ hash_search(ShmemIndex, desc->name, HASH_ENTER_NULL, &found);
+ if (!index_entry)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("could not create ShmemIndex entry for data structure \"%s\"",
+ desc->name)));
+ }
+ if (found)
+ elog(ERROR, "shmem struct \"%s\" is already initialized", desc->name);
+
+ /* allocate and initialize it */
+ structPtr = ShmemAllocRaw(desc->size, &allocated_size);
+ if (structPtr == NULL)
+ {
+ /* out of memory; remove the failed ShmemIndex entry */
+ hash_search(ShmemIndex, desc->name, HASH_REMOVE, NULL);
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("not enough shared memory for data structure"
+ " \"%s\" (%zu bytes requested)",
+ desc->name, desc->size)));
+ }
+ index_entry->size = desc->size;
+ index_entry->allocated_size = allocated_size;
+ index_entry->location = structPtr;
+
+ *(desc->ptr) = structPtr;
+ if (desc->init_fn)
+ desc->init_fn(desc->init_fn_arg);
+ }
+
+ shmem_initialized = true;
+}
+
+/*
+ * Call the attach_fn callbacks of all registered
+ */
+#ifdef EXEC_BACKEND
+void
+ShmemAttachRegistered(void)
+{
+ /* Must be initializing a (non-standalone) backend */
+ Assert(IsUnderPostmaster);
+ Assert(ShmemAllocator->index != NULL);
+
+ /* XXX: document this locking with attach_fn */
+ LWLockAcquire(ShmemIndexLock, LW_SHARED);
+
+ for (int i = 0; i < num_registered_shmem_areas; i++)
+ {
+ ShmemStructDesc *desc = registered_shmem_areas[i];
+ bool found;
+ ShmemIndexEnt *result;
+
+ /* look it up in the shmem index */
+ result = (ShmemIndexEnt *)
+ hash_search(ShmemIndex, desc->name, HASH_FIND, &found);
+ if (!found)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("could not find ShmemIndex entry for data structure \"%s\"",
+ desc->name)));
+ }
+
+ if (desc->ptr)
+ *desc->ptr = result->location;
+ if (desc->attach_fn)
+ desc->attach_fn(desc->attach_fn_arg);
+ }
+
+ LWLockRelease(ShmemIndexLock);
+
+ shmem_initialized = true;
+}
+#endif
+
+void
+ResetShmemAllocator(void)
+{
+ shmem_initialized = false;
+ num_registered_shmem_areas = 0;
+
+ /* FIXME: this leaks the allocations in TopMemoryContext */
+}
+
/*
* InitShmemAllocator() --- set up basic pointers to shared memory.
*
* Called at postmaster or stand-alone backend startup, to initialize the
* allocator's data structure in the shared memory segment. In EXEC_BACKEND,
- * this is also called at backend startup, to set up pointers to the shared
- * memory areas.
+ * also called at backend startup, to set up pointers to the
+ * already-initialized data structure.
*/
void
InitShmemAllocator(PGShmemHeader *seghdr)
{
+ Size offset;
+ int hash_size;
+ HASHCTL info;
+ int hash_flags;
+ size_t size;
+
+ Assert(!shmem_initialized);
Assert(seghdr != NULL);
/*
@@ -134,41 +506,47 @@ InitShmemAllocator(PGShmemHeader *seghdr)
ShmemBase = seghdr;
ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
-#ifndef EXEC_BACKEND
- Assert(!IsUnderPostmaster);
-#endif
- if (IsUnderPostmaster)
- {
- PGShmemHeader *shmhdr = ShmemSegHdr;
-
- ShmemAllocator = (ShmemAllocatorData *) ((char *) shmhdr + shmhdr->content_offset);
- ShmemLock = &ShmemAllocator->shmem_lock;
- }
- else
- {
- Size offset;
-
- /*
- * Allocations after this point should go through ShmemAlloc, which
- * expects to allocate everything on cache line boundaries. Make sure
- * the first allocation begins on a cache line boundary.
- */
- offset = CACHELINEALIGN(seghdr->content_offset + sizeof(ShmemAllocatorData));
- if (offset > seghdr->totalsize)
- ereport(ERROR,
- (errcode(ERRCODE_OUT_OF_MEMORY),
- errmsg("out of shared memory (%zu bytes requested)",
- offset)));
+ /*
+ * Allocations after this point should go through ShmemAlloc, which
+ * expects to allocate everything on cache line boundaries. Make sure
+ * the first allocation begins on a cache line boundary.
+ */
+ offset = CACHELINEALIGN(seghdr->content_offset + sizeof(ShmemAllocatorData));
+ if (offset > seghdr->totalsize)
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("out of shared memory (%zu bytes requested)",
+ offset)));
- ShmemAllocator = (ShmemAllocatorData *) ((char *) seghdr + seghdr->content_offset);
+ ShmemAllocator = (ShmemAllocatorData *) ((char *) seghdr + seghdr->content_offset);
+ ShmemLock = &ShmemAllocator->shmem_lock;
+ if (!IsUnderPostmaster)
+ {
SpinLockInit(&ShmemAllocator->shmem_lock);
- ShmemLock = &ShmemAllocator->shmem_lock;
ShmemAllocator->free_offset = offset;
- /* ShmemIndex can't be set up yet (need LWLocks first) */
- ShmemAllocator->index = NULL;
- ShmemIndex = (HTAB *) NULL;
}
+
+ /*
+ * Create (or attach to) the shared memory index of shmem areas.
+ */
+ hash_size = num_registered_shmem_areas + SHMEM_INDEX_ADDITIONAL_SIZE;
+
+ info.keysize = SHMEM_INDEX_KEYSIZE;
+ info.entrysize = sizeof(ShmemIndexEnt);
+ info.dsize = info.max_dsize = hash_select_dirsize(hash_size);
+ info.alloc = ShmemAllocNoError;
+ hash_flags = HASH_ELEM | HASH_STRINGS | HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
+ if (!IsUnderPostmaster)
+ {
+ size = hash_get_shared_size(&info, hash_flags);
+ ShmemAllocator->index = (HASHHDR *) ShmemAlloc(size);
+ }
+ else
+ hash_flags |= HASH_ATTACH;
+ info.hctl = ShmemAllocator->index;
+ ShmemIndex = hash_create("ShmemIndex", hash_size, &info, hash_flags);
+ Assert(ShmemIndex != NULL);
}
/*
@@ -268,67 +646,18 @@ ShmemAddrIsValid(const void *addr)
}
/*
- * InitShmemIndex() --- set up or attach to shmem index table.
- */
-void
-InitShmemIndex(void)
-{
- HASHCTL info;
-
- /*
- * Create the shared memory shmem index.
- *
- * Since ShmemInitHash calls ShmemInitStruct, which expects the ShmemIndex
- * hashtable to exist already, we have a bit of a circularity problem in
- * initializing the ShmemIndex itself. The special "ShmemIndex" hash
- * table name will tell ShmemInitStruct to fake it.
- */
- info.keysize = SHMEM_INDEX_KEYSIZE;
- info.entrysize = sizeof(ShmemIndexEnt);
-
- ShmemIndex = ShmemInitHash("ShmemIndex",
- SHMEM_INDEX_SIZE, SHMEM_INDEX_SIZE,
- &info,
- HASH_ELEM | HASH_STRINGS);
-}
-
-/*
- * ShmemInitHash -- Create and initialize, or attach to, a
- * shared memory hash table.
- *
- * We assume caller is doing some kind of synchronization
- * so that two processes don't try to create/initialize the same
- * table at once. (In practice, all creations are done in the postmaster
- * process; child processes should always be attaching to existing tables.)
- *
- * max_size is the estimated maximum number of hashtable entries. This is
- * not a hard limit, but the access efficiency will degrade if it is
- * exceeded substantially (since it's used to compute directory size and
- * the hash table buckets will get overfull).
- *
- * init_size is the number of hashtable entries to preallocate. For a table
- * whose maximum size is certain, this should be equal to max_size; that
- * ensures that no run-time out-of-shared-memory failures can occur.
+ * ShmemRegisterHash -- Register a shared memory hash table.
*
* *infoP and hash_flags must specify at least the entry sizes and key
* comparison semantics (see hash_create()). Flag bits and values specific
* to shared-memory hash tables are added here, except that callers may
* choose to specify HASH_PARTITION and/or HASH_FIXED_SIZE.
- *
- * Note: before Postgres 9.0, this function returned NULL for some failure
- * cases. Now, it always throws error instead, so callers need not check
- * for NULL.
*/
-HTAB *
-ShmemInitHash(const char *name, /* table string name for shmem index */
- int64 init_size, /* initial table size */
- int64 max_size, /* max size of the table */
- HASHCTL *infoP, /* info about key and bucket size */
- int hash_flags) /* info about infoP */
+bool
+ShmemRegisterHash(ShmemHashDesc *desc, /* configuration */
+ HASHCTL *infoP, /* info about key and bucket size */
+ int hash_flags) /* info about infoP */
{
- bool found;
- void *location;
-
/*
* Hash tables allocated in shared memory have a fixed directory; it can't
* grow or other backends wouldn't be able to find it. So, make sure we
@@ -336,145 +665,62 @@ ShmemInitHash(const char *name, /* table string name for shmem index */
*
* The shared memory allocator must be specified too.
*/
- infoP->dsize = infoP->max_dsize = hash_select_dirsize(max_size);
+ infoP->dsize = infoP->max_dsize = hash_select_dirsize(desc->max_size);
infoP->alloc = ShmemAllocNoError;
hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
/* look it up in the shmem index */
- location = ShmemInitStruct(name,
- hash_get_shared_size(infoP, hash_flags),
- &found);
+ memset(&desc->base_desc, 0, sizeof(desc->base_desc));
+ desc->base_desc.name = desc->name;
+ desc->base_desc.size = hash_get_shared_size(infoP, hash_flags);
+ desc->base_desc.init_fn = shmem_hash_init;
+ desc->base_desc.init_fn_arg = desc;
+ desc->base_desc.attach_fn = shmem_hash_attach;
+ desc->base_desc.attach_fn_arg = desc;
/*
- * if it already exists, attach to it rather than allocate and initialize
- * new space
+ * We need a stable pointer to hold the pointer to the shared memory. Use
+ * the one passed in the descriptor now. It will be replaced with the hash
+ * table header by init or attach function.
*/
- if (found)
- hash_flags |= HASH_ATTACH;
+ desc->base_desc.ptr = (void **) desc->ptr;
- /* Pass location of hashtable header to hash_create */
- infoP->hctl = (HASHHDR *) location;
+ desc->base_desc.extra_size = hash_estimate_size(desc->max_size, infoP->entrysize) - desc->base_desc.size;
+
+ desc->hash_flags = hash_flags;
+ desc->infoP = MemoryContextAlloc(TopMemoryContext, sizeof(HASHCTL));
+ memcpy(desc->infoP, infoP, sizeof(HASHCTL));
- return hash_create(name, init_size, infoP, hash_flags);
+ return ShmemRegisterStruct(&desc->base_desc);
}
-/*
- * ShmemInitStruct -- Create/attach to a structure in shared memory.
- *
- * This is called during initialization to find or allocate
- * a data structure in shared memory. If no other process
- * has created the structure, this routine allocates space
- * for it. If it exists already, a pointer to the existing
- * structure is returned.
- *
- * Returns: pointer to the object. *foundPtr is set true if the object was
- * already in the shmem index (hence, already initialized).
- *
- * Note: before Postgres 9.0, this function returned NULL for some failure
- * cases. Now, it always throws error instead, so callers need not check
- * for NULL.
- */
-void *
-ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+static void
+shmem_hash_init(void *arg)
{
- ShmemIndexEnt *result;
- void *structPtr;
-
- LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
+ ShmemHashDesc *desc = (ShmemHashDesc *) arg;
+ int hash_flags = desc->hash_flags;
- if (!ShmemIndex)
- {
- /* Must be trying to create/attach to ShmemIndex itself */
- Assert(strcmp(name, "ShmemIndex") == 0);
-
- if (IsUnderPostmaster)
- {
- /* Must be initializing a (non-standalone) backend */
- Assert(ShmemAllocator->index != NULL);
- structPtr = ShmemAllocator->index;
- *foundPtr = true;
- }
- else
- {
- /*
- * If the shmem index doesn't exist, we are bootstrapping: we must
- * be trying to init the shmem index itself.
- *
- * Notice that the ShmemIndexLock is released before the shmem
- * index has been initialized. This should be OK because no other
- * process can be accessing shared memory yet.
- */
- Assert(ShmemAllocator->index == NULL);
- structPtr = ShmemAlloc(size);
- ShmemAllocator->index = structPtr;
- *foundPtr = false;
- }
- LWLockRelease(ShmemIndexLock);
- return structPtr;
- }
-
- /* look it up in the shmem index */
- result = (ShmemIndexEnt *)
- hash_search(ShmemIndex, name, HASH_ENTER_NULL, foundPtr);
-
- if (!result)
- {
- LWLockRelease(ShmemIndexLock);
- ereport(ERROR,
- (errcode(ERRCODE_OUT_OF_MEMORY),
- errmsg("could not create ShmemIndex entry for data structure \"%s\"",
- name)));
- }
-
- if (*foundPtr)
- {
- /*
- * Structure is in the shmem index so someone else has allocated it
- * already. The size better be the same as the size we are trying to
- * initialize to, or there is a name conflict (or worse).
- */
- if (result->size != size)
- {
- LWLockRelease(ShmemIndexLock);
- ereport(ERROR,
- (errmsg("ShmemIndex entry size is wrong for data structure"
- " \"%s\": expected %zu, actual %zu",
- name, size, result->size)));
- }
- structPtr = result->location;
- }
- else
- {
- Size allocated_size;
+ /* Pass location of hashtable header to hash_create */
+ desc->infoP->hctl = (HASHHDR *) *desc->base_desc.ptr;
- /* It isn't in the table yet. allocate and initialize it */
- structPtr = ShmemAllocRaw(size, &allocated_size);
- if (structPtr == NULL)
- {
- /* out of memory; remove the failed ShmemIndex entry */
- hash_search(ShmemIndex, name, HASH_REMOVE, NULL);
- LWLockRelease(ShmemIndexLock);
- ereport(ERROR,
- (errcode(ERRCODE_OUT_OF_MEMORY),
- errmsg("not enough shared memory for data structure"
- " \"%s\" (%zu bytes requested)",
- name, size)));
- }
- result->size = size;
- result->allocated_size = allocated_size;
- result->location = structPtr;
- }
+ *desc->ptr = hash_create(desc->name, desc->init_size, desc->infoP, hash_flags);
+}
- LWLockRelease(ShmemIndexLock);
+static void
+shmem_hash_attach(void *arg)
+{
+ ShmemHashDesc *desc = (ShmemHashDesc *) arg;
+ int hash_flags = desc->hash_flags;
- Assert(ShmemAddrIsValid(structPtr));
+ /* attach to it rather than allocate and initialize new space */
+ hash_flags |= HASH_ATTACH;
- Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
+ /* Pass location of hashtable header to hash_create */
+ desc->infoP->hctl = (HASHHDR *) *desc->base_desc.ptr;
- return structPtr;
+ *desc->ptr = hash_create(desc->name, desc->init_size, desc->infoP, hash_flags);
}
-
/*
* Add two Size values, checking for overflow
*/
@@ -761,3 +1007,82 @@ pg_numa_available(PG_FUNCTION_ARGS)
{
PG_RETURN_BOOL(pg_numa_init() != -1);
}
+
+/*
+ * ShmemInitStruct -- Create/attach to a structure in shared memory.
+ *
+ * This is called during initialization to find or allocate
+ * a data structure in shared memory. If no other process
+ * has created the structure, this routine allocates space
+ * for it. If it exists already, a pointer to the existing
+ * structure is returned.
+ *
+ * Returns: pointer to the object. *foundPtr is set true if the object was
+ * already in the shmem index (hence, already initialized).
+ *
+ * Note: This is a legacy interface, kept for backwards compatibility with
+ * extensions. Use ShmemRegisterStruct() in new code!
+ */
+void *
+ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+ ShmemStructDesc *desc;
+
+ Assert(shmem_initialized);
+
+ desc = MemoryContextAllocZero(TopMemoryContext, sizeof(ShmemStructDesc) + sizeof(void *));
+ desc->name = name;
+ desc->size = size;
+ desc->ptr = (void *) (((char *) desc) + sizeof(ShmemStructDesc));
+
+ *foundPtr = ShmemRegisterStruct(desc);
+ Assert(*desc->ptr != NULL);
+ return *desc->ptr;
+}
+
+/*
+ * ShmemInitHash -- Create and initialize, or attach to, a
+ * shared memory hash table.
+ *
+ * We assume caller is doing some kind of synchronization
+ * so that two processes don't try to create/initialize the same
+ * table at once. (In practice, all creations are done in the postmaster
+ * process; child processes should always be attaching to existing tables.)
+ *
+ * max_size is the estimated maximum number of hashtable entries. This is
+ * not a hard limit, but the access efficiency will degrade if it is
+ * exceeded substantially (since it's used to compute directory size and
+ * the hash table buckets will get overfull).
+ *
+ * init_size is the number of hashtable entries to preallocate. For a table
+ * whose maximum size is certain, this should be equal to max_size; that
+ * ensures that no run-time out-of-shared-memory failures can occur.
+ *
+ * *infoP and hash_flags must specify at least the entry sizes and key
+ * comparison semantics (see hash_create()). Flag bits and values specific
+ * to shared-memory hash tables are added here, except that callers may
+ * choose to specify HASH_PARTITION and/or HASH_FIXED_SIZE.
+ *
+ * Note: This is a legacy interface, kept for backwards compatibility with
+ * extensions. Use ShmemRegisterHash() in new code!
+ */
+HTAB *
+ShmemInitHash(const char *name, /* table string name for shmem index */
+ int64 init_size, /* initial table size */
+ int64 max_size, /* max size of the table */
+ HASHCTL *infoP, /* info about key and bucket size */
+ int hash_flags) /* info about infoP */
+{
+ ShmemHashDesc *desc;
+
+ Assert(shmem_initialized);
+
+ desc = MemoryContextAllocZero(TopMemoryContext, sizeof(ShmemHashDesc) + sizeof(HTAB *));
+ desc->name = name;
+ desc->init_size = init_size;
+ desc->max_size = max_size;
+ desc->ptr = (HTAB **) (((char *) desc) + sizeof(ShmemHashDesc));
+
+ ShmemRegisterHash(desc, infoP, hash_flags);
+ return *desc->ptr;
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index d01a09dd0c4..fa074c419a8 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -4162,6 +4162,9 @@ PostgresSingleUserMain(int argc, char *argv[],
/* Initialize size of fast-path lock cache. */
InitializeFastPathLocks();
+ /* Register the shared memory needs of all core subsystems. */
+ RegisterShmemStructs();
+
/*
* Give preloaded libraries a chance to request additional shared memory.
*/
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index da32787ab51..8a3b71ad5d3 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,6 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
/* ipci.c */
extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
+extern void RegisterShmemStructs(void);
extern Size CalculateShmemSize(void);
extern void CreateSharedMemoryAndSemaphores(void);
#ifdef EXEC_BACKEND
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 89d45287c17..ea1884a8778 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -24,19 +24,138 @@
#include "storage/spin.h"
#include "utils/hsearch.h"
+typedef void (*ShmemInitCallback) (void *arg);
+typedef void (*ShmemAttachCallback) (void *arg);
+
+/*
+ * ShmemStructDesc describes a named area or struct in shared memory.
+ *
+ * Shared memory is reserved and allocated in a few phases at postmaster
+ * startup, and in EXEC_BACKEND mode, there's some extra work done to "attach"
+ * to them at backend startup. ShmemStructDesc contains all the information
+ * needed to manage the lifecycle.
+ *
+ * 'name', 'size' and the callback functions are filled in by the
+ * ShmemRegisterStruct() caller. After registration, the shmem machinery
+ * reserves the memory for the area, sets *ptr to point to the allocation, and
+ * calls the callbacks at the right moments.
+ */
+typedef struct ShmemStructDesc
+{
+ /* Name of the shared memory area. Must be unique across the system */
+ const char *name;
+
+ /* Size of the shared memory area */
+ size_t size;
+
+ /*
+ * Initialization callback function. This is called when the shared
+ * memory area is allocated, usually at postmaster startup. 'init_fn_arg'
+ * is an opaque argument passed to the callback.
+ */
+ ShmemInitCallback init_fn;
+ void *init_fn_arg;
+
+ /*
+ * Attachment callback function. In EXEC_BACKEND mode, this is called at
+ * startup of each backend. In !EXEC_BACKEND mode, this is only called if
+ * the shared memory area is registered after postmaster startup. We
+ * never do that in core code, but extensions might.
+ */
+ ShmemInitCallback attach_fn;
+ void *attach_fn_arg;
+
+ /*
+ * Extra space to reserve for the shared memory segment, but it's not part
+ * of the struct itself. This is used for shared memory hash tables that
+ * can grow beyond the initial size when more buckets are allocated.
+ */
+ size_t extra_size;
+
+ /*
+ * When the shmem area is initialized or attached to, pointer to it is
+ * stored in *ptr. It usually points to a global variable, used to access
+ * the shared memory area later. *ptr is set before the init_fn or
+ * attach_fn callback is called.
+ */
+ void **ptr;
+} ShmemStructDesc;
+
+/*
+ * Descriptor for named shared memory hash table.
+ *
+ * Similar to ShmemStructDesc, but describes a shared memory hash table. Each
+ * hash table is backed by an allocated area, described by 'base_desc', but if
+ * 'max_size' is greater than 'init_size', it can also grow beyond the initial
+ * allocated area by allocating more hash entries from the global unreserved
+ * space.
+ */
+typedef struct ShmemHashDesc
+{
+ /* Name of the shared memory area. Must be unique across the system */
+ const char *name;
+
+ /*
+ * max_size is the estimated maximum number of hashtable entries. This is
+ * not a hard limit, but the access efficiency will degrade if it is
+ * exceeded substantially (since it's used to compute directory size and
+ * the hash table buckets will get overfull).
+ */
+ size_t max_size;
+
+ /*
+ * init_size is the number of hashtable entries to preallocate. For a table
+ * whose maximum size is certain, this should be equal to max_size; that
+ * ensures that no run-time out-of-shared-memory failures can occur.
+ */
+ size_t init_size;
+
+ /* Hash table options passed to hash_create() */
+ HASHCTL *infoP;
+ int hash_flags;
+
+ /*
+ * When the hash table is initialized or attached to, pointer to it is
+ * stored in *ptr. It usually points to a global variable, used to access
+ * the shared hash table later.
+ */
+ HTAB **ptr;
+
+ /*
+ * Descriptor for the underlying "area". Callers of ShmemRegisterHash()
+ * do not need to touch this, it is filled in by ShmemRegisterHash() based
+ * on the hash table parameters.
+ */
+ ShmemStructDesc base_desc;
+} ShmemHashDesc;
/* shmem.c */
extern PGDLLIMPORT slock_t *ShmemLock;
typedef struct PGShmemHeader PGShmemHeader; /* avoid including
* storage/pg_shmem.h here */
+extern void ResetShmemAllocator(void);
extern void InitShmemAllocator(PGShmemHeader *seghdr);
+#ifdef EXEC_BACKEND
+extern void AttachShmemAllocator(PGShmemHeader *seghdr);
+#endif
extern void *ShmemAlloc(Size size);
extern void *ShmemAllocNoError(Size size);
extern bool ShmemAddrIsValid(const void *addr);
-extern void InitShmemIndex(void);
+
+extern bool ShmemRegisterHash(ShmemHashDesc *desc, HASHCTL *infoP, int hash_flags);
+extern bool ShmemRegisterStruct(ShmemStructDesc *desc);
+
+/* legacy shmem allocation functions */
extern HTAB *ShmemInitHash(const char *name, int64 init_size, int64 max_size,
HASHCTL *infoP, int hash_flags);
extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+
+extern size_t ShmemRegisteredSize(void);
+extern void ShmemInitRegistered(void);
+#ifdef EXEC_BACKEND
+extern void ShmemAttachRegistered(void);
+#endif
+
extern Size add_size(Size s1, Size s2);
extern Size mul_size(Size s1, Size s2);
@@ -45,19 +164,4 @@ extern PGDLLIMPORT Size pg_get_shmem_pagesize(void);
/* ipci.c */
extern void RequestAddinShmemSpace(Size size);
-/* size constants for the shmem index table */
- /* max size of data structure string name */
-#define SHMEM_INDEX_KEYSIZE (48)
- /* estimated size of the shmem index table (not a hard limit) */
-#define SHMEM_INDEX_SIZE (64)
-
-/* this is a hash bucket in the shmem index table */
-typedef struct
-{
- char key[SHMEM_INDEX_KEYSIZE]; /* string name */
- void *location; /* location in shared mem */
- Size size; /* # bytes requested for the structure */
- Size allocated_size; /* # bytes actually allocated */
-} ShmemIndexEnt;
-
#endif /* SHMEM_H */
--
2.47.3
From f4ea30ca38e7fc5ba22e092a27341077b6f0dcc0 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Tue, 24 Feb 2026 15:45:51 +0200
Subject: [PATCH v2 3/4] Convert pg_stat_statements to use the new interface
---
.../pg_stat_statements/pg_stat_statements.c | 135 ++++++++----------
1 file changed, 62 insertions(+), 73 deletions(-)
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 4a427533bd8..a8c7f7b2441 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -258,6 +258,29 @@ typedef struct pgssSharedState
pgssGlobalStats stats; /* global statistics for pgss */
} pgssSharedState;
+/* Links to shared memory state */
+static pgssSharedState *pgss;
+static HTAB *pgss_hash;
+
+static void pgss_shmem_init(void *arg);
+
+static ShmemStructDesc pgssSharedStateShmemDesc =
+{
+ .name = "pg_stat_statements",
+ .size = sizeof(pgssSharedState),
+ .init_fn = pgss_shmem_init,
+ .ptr = (void *) &pgss,
+};
+
+static ShmemHashDesc pgssSharedHashDesc =
+{
+ .name = "pg_stat_statements hash",
+ .init_size = 0, /* set from 'pgss_max' */
+ .max_size = 0, /* set from 'pgss_max' */
+ .ptr = &pgss_hash,
+};
+
+
/*---- Local variables ----*/
/* Current nesting depth of planner/ExecutorRun/ProcessUtility calls */
@@ -274,10 +297,6 @@ static ExecutorFinish_hook_type prev_ExecutorFinish = NULL;
static ExecutorEnd_hook_type prev_ExecutorEnd = NULL;
static ProcessUtility_hook_type prev_ProcessUtility = NULL;
-/* Links to shared memory state */
-static pgssSharedState *pgss = NULL;
-static HTAB *pgss_hash = NULL;
-
/*---- GUC variables ----*/
typedef enum
@@ -365,7 +384,6 @@ static void pgss_store(const char *query, int64 queryId,
static void pg_stat_statements_internal(FunctionCallInfo fcinfo,
pgssVersion api_version,
bool showtext);
-static Size pgss_memsize(void);
static pgssEntry *entry_alloc(pgssHashKey *key, Size query_offset, int query_len,
int encoding, bool sticky);
static void entry_dealloc(void);
@@ -390,6 +408,8 @@ static int comp_location(const void *a, const void *b);
void
_PG_init(void)
{
+ HASHCTL info;
+
/*
* In order to create our shared memory area, we have to be loaded via
* shared_preload_libraries. If not, fall out without hooking into any of
@@ -470,6 +490,19 @@ _PG_init(void)
MarkGUCPrefixReserved("pg_stat_statements");
+ /*
+ * Register our shared memory needs, including hash table
+ */
+ ShmemRegisterStruct(&pgssSharedStateShmemDesc);
+
+ info.keysize = sizeof(pgssHashKey);
+ info.entrysize = sizeof(pgssEntry);
+ pgssSharedHashDesc.init_size = pgss_max;
+ pgssSharedHashDesc.max_size = pgss_max;
+ ShmemRegisterHash(&pgssSharedHashDesc,
+ &info,
+ HASH_ELEM | HASH_BLOBS);
+
/*
* Install hooks.
*/
@@ -494,8 +527,8 @@ _PG_init(void)
}
/*
- * shmem_request hook: request additional shared resources. We'll allocate or
- * attach to the shared resources in pgss_shmem_startup().
+ * shmem_request hook: request additional shared resources. We'll initialize
+ * other resources in pgss_shmem_startup().
*/
static void
pgss_shmem_request(void)
@@ -503,21 +536,31 @@ pgss_shmem_request(void)
if (prev_shmem_request_hook)
prev_shmem_request_hook();
- RequestAddinShmemSpace(pgss_memsize());
RequestNamedLWLockTranche("pg_stat_statements", 1);
}
+static void
+pgss_shmem_init(void *arg)
+{
+ pgss->lock = &(GetNamedLWLockTranche("pg_stat_statements"))->lock;
+ pgss->cur_median_usage = ASSUMED_MEDIAN_INIT;
+ pgss->mean_query_len = ASSUMED_LENGTH_INIT;
+ SpinLockInit(&pgss->mutex);
+ pgss->extent = 0;
+ pgss->n_writers = 0;
+ pgss->gc_count = 0;
+ pgss->stats.dealloc = 0;
+ pgss->stats.stats_reset = GetCurrentTimestamp();
+}
+
/*
- * shmem_startup hook: allocate or attach to shared memory,
- * then load any pre-existing statistics from file.
- * Also create and load the query-texts file, which is expected to exist
- * (even if empty) while the module is enabled.
+ * shmem_startup hook: Load any pre-existing statistics from file at
+ * postmaster startup. Also create and load the query-texts file, which is
+ * expected to exist (even if empty) while the module is enabled.
*/
static void
pgss_shmem_startup(void)
{
- bool found;
- HASHCTL info;
FILE *file = NULL;
FILE *qfile = NULL;
uint32 header;
@@ -530,54 +573,14 @@ pgss_shmem_startup(void)
if (prev_shmem_startup_hook)
prev_shmem_startup_hook();
- /* reset in case this is a restart within the postmaster */
- pgss = NULL;
- pgss_hash = NULL;
-
- /*
- * Create or attach to the shared memory state, including hash table
- */
- LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
-
- pgss = ShmemInitStruct("pg_stat_statements",
- sizeof(pgssSharedState),
- &found);
-
- if (!found)
- {
- /* First time through ... */
- pgss->lock = &(GetNamedLWLockTranche("pg_stat_statements"))->lock;
- pgss->cur_median_usage = ASSUMED_MEDIAN_INIT;
- pgss->mean_query_len = ASSUMED_LENGTH_INIT;
- SpinLockInit(&pgss->mutex);
- pgss->extent = 0;
- pgss->n_writers = 0;
- pgss->gc_count = 0;
- pgss->stats.dealloc = 0;
- pgss->stats.stats_reset = GetCurrentTimestamp();
- }
-
- info.keysize = sizeof(pgssHashKey);
- info.entrysize = sizeof(pgssEntry);
- pgss_hash = ShmemInitHash("pg_stat_statements hash",
- pgss_max, pgss_max,
- &info,
- HASH_ELEM | HASH_BLOBS);
-
- LWLockRelease(AddinShmemInitLock);
+ if (IsUnderPostmaster)
+ return; /* nothing to do in backends */
/*
- * If we're in the postmaster (or a standalone backend...), set up a shmem
- * exit hook to dump the statistics to disk.
+ * Set up a shmem exit hook to dump the statistics to disk on postmaster
+ * (or standalone backend) exit.
*/
- if (!IsUnderPostmaster)
- on_shmem_exit(pgss_shmem_shutdown, (Datum) 0);
-
- /*
- * Done if some other process already completed our initialization.
- */
- if (found)
- return;
+ on_shmem_exit(pgss_shmem_shutdown, (Datum) 0);
/*
* Note: we don't bother with locks here, because there should be no other
@@ -2082,20 +2085,6 @@ pg_stat_statements_info(PG_FUNCTION_ARGS)
PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
}
-/*
- * Estimate shared memory space needed.
- */
-static Size
-pgss_memsize(void)
-{
- Size size;
-
- size = MAXALIGN(sizeof(pgssSharedState));
- size = add_size(size, hash_estimate_size(pgss_max, sizeof(pgssEntry)));
-
- return size;
-}
-
/*
* Allocate a new hashtable entry.
* caller must hold an exclusive lock on pgss->lock
--
2.47.3
From 5f9eb1b577e16eb8764d05cba9e770cac2d4bfea Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Fri, 6 Mar 2026 14:55:38 +0200
Subject: [PATCH v2 4/4] Use the new mechanism in a few core subsystems
I chose these subsystems specifically because they have some
complicating properties, making them slightly harder to convert than
most:
- The initialization callbacks of some of these subsystems have
dependencies, i.e. they need to be initialized in the right order.
- The ProgGlobal pointer still needs to be inherited by the
BackendParameters mechanism on EXEC_BACKEND builds, because
ProcGlobal is required by InitProcess() to get a PGPROC entry, and
the PGPROC entry is required to use LWLocks, and usually attaching
to shared memory areas requires the use of LWLocks.
- Similarly, ProcSignal pointer still needs to be handled by
BackendParameters, because query cancellation connections access it
without calling InitProcess
I'm believe converting all the rest of the subsystems after this will
be pretty mechanic.
---
src/backend/access/transam/varsup.c | 33 +++--
src/backend/storage/ipc/dsm.c | 45 ++++---
src/backend/storage/ipc/dsm_registry.c | 34 +++---
src/backend/storage/ipc/ipci.c | 37 +++---
src/backend/storage/ipc/pmsignal.c | 54 +++++----
src/backend/storage/ipc/procarray.c | 126 +++++++++----------
src/backend/storage/ipc/procsignal.c | 63 +++++-----
src/backend/storage/ipc/shmem.c | 4 +-
src/backend/storage/ipc/sinvaladt.c | 37 +++---
src/backend/storage/lmgr/proc.c | 162 +++++++++++++------------
src/include/access/transam.h | 10 +-
src/include/storage/dsm_registry.h | 3 +-
src/include/storage/pmsignal.h | 3 +-
src/include/storage/proc.h | 2 +-
src/include/storage/procarray.h | 3 +-
src/include/storage/procsignal.h | 3 +-
src/include/storage/sinvaladt.h | 3 +-
17 files changed, 312 insertions(+), 310 deletions(-)
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index 3e95d4cfd16..3dfda875e80 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -30,35 +30,32 @@
/* Number of OIDs to prefetch (preallocate) per XLOG write */
#define VAR_OID_PREFETCH 8192
+static void VarsupShmemInit(void *arg);
+
/* pointer to variables struct in shared memory */
TransamVariablesData *TransamVariables = NULL;
+ShmemStructDesc TransamVariablesShmemDesc = {
+ .name = "TransamVariables",
+ .size = sizeof(TransamVariablesData),
+ .init_fn = VarsupShmemInit,
+ .ptr = (void **) &TransamVariables,
+};
/*
* Initialization of shared memory for TransamVariables.
*/
-Size
-VarsupShmemSize(void)
+void
+VarsupShmemRegister(void)
{
- return sizeof(TransamVariablesData);
+ ShmemRegisterStruct(&TransamVariablesShmemDesc);
}
-void
-VarsupShmemInit(void)
-{
- bool found;
+static void
+VarsupShmemInit(void *arg)
- /* Initialize our shared state struct */
- TransamVariables = ShmemInitStruct("TransamVariables",
- sizeof(TransamVariablesData),
- &found);
- if (!IsUnderPostmaster)
- {
- Assert(!found);
- memset(TransamVariables, 0, sizeof(TransamVariablesData));
- }
- else
- Assert(found);
+{
+ memset(TransamVariables, 0, sizeof(TransamVariablesData));
}
/*
diff --git a/src/backend/storage/ipc/dsm.c b/src/backend/storage/ipc/dsm.c
index 6a5b16392f7..73644ec3bbb 100644
--- a/src/backend/storage/ipc/dsm.c
+++ b/src/backend/storage/ipc/dsm.c
@@ -108,8 +108,17 @@ static inline bool is_main_region_dsm_handle(dsm_handle handle);
static bool dsm_init_done = false;
/* Preallocated DSM space in the main shared memory region. */
+static void dsm_main_space_init(void *);
+
static void *dsm_main_space_begin = NULL;
+static ShmemStructDesc dsm_main_space_shmem_desc = {
+ .name = "Preallocated DSM",
+ .size = 0, /* dynamic */
+ .init_fn = dsm_main_space_init,
+ .ptr = &dsm_main_space_begin,
+};
+
/*
* List of dynamic shared memory segments used by this backend.
*
@@ -479,27 +488,29 @@ void
dsm_shmem_init(void)
{
size_t size = dsm_estimate_size();
- bool found;
if (size == 0)
return;
- dsm_main_space_begin = ShmemInitStruct("Preallocated DSM", size, &found);
- if (!found)
- {
- FreePageManager *fpm = (FreePageManager *) dsm_main_space_begin;
- size_t first_page = 0;
- size_t pages;
-
- /* Reserve space for the FreePageManager. */
- while (first_page * FPM_PAGE_SIZE < sizeof(FreePageManager))
- ++first_page;
-
- /* Initialize it and give it all the rest of the space. */
- FreePageManagerInitialize(fpm, dsm_main_space_begin);
- pages = (size / FPM_PAGE_SIZE) - first_page;
- FreePageManagerPut(fpm, first_page, pages);
- }
+ ShmemRegisterStruct(&dsm_main_space_shmem_desc);
+}
+
+static void
+dsm_main_space_init(void *arg)
+{
+ size_t size = dsm_main_space_shmem_desc.size;
+ FreePageManager *fpm = (FreePageManager *) dsm_main_space_begin;
+ size_t first_page = 0;
+ size_t pages;
+
+ /* Reserve space for the FreePageManager. */
+ while (first_page * FPM_PAGE_SIZE < sizeof(FreePageManager))
+ ++first_page;
+
+ /* Initialize it and give it all the rest of the space. */
+ FreePageManagerInitialize(fpm, dsm_main_space_begin);
+ pages = (size / FPM_PAGE_SIZE) - first_page;
+ FreePageManagerPut(fpm, first_page, pages);
}
/*
diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c
index 068c1577b12..efa6b0dc01f 100644
--- a/src/backend/storage/ipc/dsm_registry.c
+++ b/src/backend/storage/ipc/dsm_registry.c
@@ -54,8 +54,18 @@ typedef struct DSMRegistryCtxStruct
dshash_table_handle dshh;
} DSMRegistryCtxStruct;
+static void DSMRegistryCtxShmemInit(void *arg);
+
static DSMRegistryCtxStruct *DSMRegistryCtx;
+static ShmemStructDesc DSMRegistryCtxShmemDesc = {
+ .name = "DSM Registry Data",
+ .size = sizeof(DSMRegistryCtxStruct),
+ .init_fn = DSMRegistryCtxShmemInit,
+ .ptr = (void **) &DSMRegistryCtx,
+};
+
+
typedef struct NamedDSMState
{
dsm_handle handle;
@@ -113,27 +123,17 @@ static const dshash_parameters dsh_params = {
static dsa_area *dsm_registry_dsa;
static dshash_table *dsm_registry_table;
-Size
-DSMRegistryShmemSize(void)
+void
+DSMRegistryShmemRegister(void)
{
- return MAXALIGN(sizeof(DSMRegistryCtxStruct));
+ ShmemRegisterStruct(&DSMRegistryCtxShmemDesc);
}
-void
-DSMRegistryShmemInit(void)
+static void
+DSMRegistryCtxShmemInit(void *)
{
- bool found;
-
- DSMRegistryCtx = (DSMRegistryCtxStruct *)
- ShmemInitStruct("DSM Registry Data",
- DSMRegistryShmemSize(),
- &found);
-
- if (!found)
- {
- DSMRegistryCtx->dsah = DSA_HANDLE_INVALID;
- DSMRegistryCtx->dshh = DSHASH_HANDLE_INVALID;
- }
+ DSMRegistryCtx->dsah = DSA_HANDLE_INVALID;
+ DSMRegistryCtx->dshh = DSHASH_HANDLE_INVALID;
}
/*
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 97b2de994f2..dba518e7bf9 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -101,15 +101,14 @@ CalculateShmemSize(void)
size = add_size(size, ShmemRegisteredSize());
size = add_size(size, dsm_estimate_size());
- size = add_size(size, DSMRegistryShmemSize());
+
+ size = add_size(size, ShmemRegisteredSize());
/* legacy subsystems */
size = add_size(size, BufferManagerShmemSize());
size = add_size(size, LockManagerShmemSize());
size = add_size(size, PredicateLockShmemSize());
- size = add_size(size, ProcGlobalShmemSize());
size = add_size(size, XLogPrefetchShmemSize());
- size = add_size(size, VarsupShmemSize());
size = add_size(size, XLOGShmemSize());
size = add_size(size, XLogRecoveryShmemSize());
size = add_size(size, CLOGShmemSize());
@@ -119,11 +118,7 @@ CalculateShmemSize(void)
size = add_size(size, BackgroundWorkerShmemSize());
size = add_size(size, MultiXactShmemSize());
size = add_size(size, LWLockShmemSize());
- size = add_size(size, ProcArrayShmemSize());
size = add_size(size, BackendStatusShmemSize());
- size = add_size(size, SharedInvalShmemSize());
- size = add_size(size, PMSignalShmemSize());
- size = add_size(size, ProcSignalShmemSize());
size = add_size(size, CheckpointerShmemSize());
size = add_size(size, AutoVacuumShmemSize());
size = add_size(size, ReplicationSlotsShmemSize());
@@ -245,11 +240,18 @@ void
RegisterShmemStructs(void)
{
/*
- * TODO: Not used in any built-in subsystems yet. In the future, most of
- * the calls *ShmemInit() calls in CreateOrAttachShmemStructs(), and
- * *ShmemSize() calls in CalculateShmemSize() will be replaced by calls
- * into the subsystems from here.
+ * TODO: In the future, most of the calls *ShmemInit() calls in
+ * CreateOrAttachShmemStructs(), and *ShmemSize() calls in
+ * CalculateShmemSize() will be replaced by calls into the subsystems from
+ * here.
*/
+ DSMRegistryShmemRegister();
+ ProcGlobalShmemRegister();
+ VarsupShmemRegister();
+ ProcArrayShmemRegister();
+ SharedInvalShmemRegister();
+ PMSignalShmemRegister();
+ ProcSignalShmemRegister();
}
/*
@@ -293,13 +295,12 @@ CreateOrAttachShmemStructs(void)
}
dsm_shmem_init();
- DSMRegistryShmemInit();
/*
* Set up xlog, clog, and buffers
*/
- VarsupShmemInit();
XLOGShmemInit();
+
XLogPrefetchShmemInit();
XLogRecoveryShmemInit();
CLOGShmemInit();
@@ -321,23 +322,13 @@ CreateOrAttachShmemStructs(void)
/*
* Set up process table
*/
- if (!IsUnderPostmaster)
- InitProcGlobal();
- ProcArrayShmemInit();
BackendStatusShmemInit();
TwoPhaseShmemInit();
BackgroundWorkerShmemInit();
- /*
- * Set up shared-inval messaging
- */
- SharedInvalShmemInit();
-
/*
* Set up interprocess signaling mechanisms
*/
- PMSignalShmemInit();
- ProcSignalShmemInit();
CheckpointerShmemInit();
AutoVacuumShmemInit();
ReplicationSlotsShmemInit();
diff --git a/src/backend/storage/ipc/pmsignal.c b/src/backend/storage/ipc/pmsignal.c
index 4618820b337..c840d5a8fb8 100644
--- a/src/backend/storage/ipc/pmsignal.c
+++ b/src/backend/storage/ipc/pmsignal.c
@@ -80,9 +80,20 @@ struct PMSignalData
sig_atomic_t PMChildFlags[FLEXIBLE_ARRAY_MEMBER];
};
-/* PMSignalState pointer is valid in both postmaster and child processes */
+static void PMSignalShmemInit(void *);
+
+/*
+ * PMSignalState pointer is valid in both postmaster and child processes
+ */
NON_EXEC_STATIC volatile PMSignalData *PMSignalState = NULL;
+static ShmemStructDesc PMSignalShmemDesc = {
+ .name = "PMSignalState",
+ .size = 0, /* dynamic */
+ .init_fn = PMSignalShmemInit,
+ .ptr = (void **) &PMSignalState,
+};
+
/*
* Local copy of PMSignalState->num_child_flags, only valid in the
* postmaster. Postmaster keeps a local copy so that it doesn't need to
@@ -123,39 +134,29 @@ postmaster_death_handler(SIGNAL_ARGS)
static void MarkPostmasterChildInactive(int code, Datum arg);
/*
- * PMSignalShmemSize
- * Compute space needed for pmsignal.c's shared memory
+ * PMSignalShmemRegister - Register our shared memory
*/
-Size
-PMSignalShmemSize(void)
+void
+PMSignalShmemRegister(void)
{
Size size;
- size = offsetof(PMSignalData, PMChildFlags);
- size = add_size(size, mul_size(MaxLivePostmasterChildren(),
- sizeof(sig_atomic_t)));
+ num_child_flags = MaxLivePostmasterChildren();
- return size;
+ size = offsetof(PMSignalData, PMChildFlags);
+ size = add_size(size, mul_size(num_child_flags, sizeof(sig_atomic_t)));
+ PMSignalShmemDesc.size = size;
+ ShmemRegisterStruct(&PMSignalShmemDesc);
}
-/*
- * PMSignalShmemInit - initialize during shared-memory creation
- */
-void
-PMSignalShmemInit(void)
+static void
+PMSignalShmemInit(void *arg)
{
- bool found;
-
- PMSignalState = (PMSignalData *)
- ShmemInitStruct("PMSignalState", PMSignalShmemSize(), &found);
-
- if (!found)
- {
- /* initialize all flags to zeroes */
- MemSet(unvolatize(PMSignalData *, PMSignalState), 0, PMSignalShmemSize());
- num_child_flags = MaxLivePostmasterChildren();
- PMSignalState->num_child_flags = num_child_flags;
- }
+ /* initialize all flags to zeroes */
+ Assert(PMSignalState);
+ MemSet(unvolatize(PMSignalData *, PMSignalState), 0, PMSignalShmemDesc.size);
+ Assert(num_child_flags > 0);
+ PMSignalState->num_child_flags = num_child_flags;
}
/*
@@ -291,6 +292,7 @@ RegisterPostmasterChildActive(void)
{
int slot = MyPMChildSlot;
+ Assert(PMSignalState);
Assert(slot > 0 && slot <= PMSignalState->num_child_flags);
slot--;
Assert(PMSignalState->PMChildFlags[slot] == PM_CHILD_ASSIGNED);
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 40312df2cac..2d8800ef02f 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -102,6 +102,19 @@ typedef struct ProcArrayStruct
int pgprocnos[FLEXIBLE_ARRAY_MEMBER];
} ProcArrayStruct;
+static void ProcArrayShmemInit(void *arg);
+static void ProcArrayShmemAttach(void *arg);
+
+static ProcArrayStruct *procArray;
+
+static ShmemStructDesc ProcArrayShmemDesc = {
+ .name = "Proc Array",
+ .size = 0, /* dynamic */
+ .init_fn = ProcArrayShmemInit,
+ .attach_fn = ProcArrayShmemAttach,
+ .ptr = (void **) &procArray,
+};
+
/*
* State for the GlobalVisTest* family of functions. Those functions can
* e.g. be used to decide if a deleted row can be removed without violating
@@ -268,9 +281,6 @@ typedef enum KAXCompressReason
KAX_STARTUP_PROCESS_IDLE, /* startup process is about to sleep */
} KAXCompressReason;
-
-static ProcArrayStruct *procArray;
-
static PGPROC *allProcs;
/*
@@ -281,8 +291,25 @@ static TransactionId cachedXidIsNotInProgress = InvalidTransactionId;
/*
* Bookkeeping for tracking emulated transactions in recovery
*/
+
static TransactionId *KnownAssignedXids;
+
+static ShmemStructDesc KnownAssignedXidsShmemDesc = {
+ .name = "KnownAssignedXids",
+ .size = 0, /* dynamic */
+ .init_fn = NULL,
+ .ptr = (void **) &KnownAssignedXids,
+};
+
static bool *KnownAssignedXidsValid;
+
+static ShmemStructDesc KnownAssignedXidsValidShmemDesc = {
+ .name = "KnownAssignedXidsValid",
+ .size = 0, /* dynamic */
+ .init_fn = NULL,
+ .ptr = (void **) &KnownAssignedXidsValid,
+};
+
static TransactionId latestObservedXid = InvalidTransactionId;
/*
@@ -373,18 +400,19 @@ static inline FullTransactionId FullXidRelativeTo(FullTransactionId rel,
static void GlobalVisUpdateApply(ComputeXidHorizonsResult *horizons);
/*
- * Report shared-memory space needed by ProcArrayShmemInit
+ * Register the shared PGPROC array during postmaster startup.
*/
-Size
-ProcArrayShmemSize(void)
+void
+ProcArrayShmemRegister(void)
{
- Size size;
-
- /* Size of the ProcArray structure itself */
#define PROCARRAY_MAXPROCS (MaxBackends + max_prepared_xacts)
- size = offsetof(ProcArrayStruct, pgprocnos);
- size = add_size(size, mul_size(sizeof(int), PROCARRAY_MAXPROCS));
+ /* Create or attach to the ProcArray shared structure */
+ ProcArrayShmemDesc.size =
+ add_size(offsetof(ProcArrayStruct, pgprocnos),
+ mul_size(sizeof(int),
+ PROCARRAY_MAXPROCS));
+ ShmemRegisterStruct(&ProcArrayShmemDesc);
/*
* During Hot Standby processing we have a data structure called
@@ -404,64 +432,38 @@ ProcArrayShmemSize(void)
if (EnableHotStandby)
{
- size = add_size(size,
- mul_size(sizeof(TransactionId),
- TOTAL_MAX_CACHED_SUBXIDS));
- size = add_size(size,
- mul_size(sizeof(bool), TOTAL_MAX_CACHED_SUBXIDS));
+ KnownAssignedXidsShmemDesc.size =
+ mul_size(sizeof(TransactionId),
+ TOTAL_MAX_CACHED_SUBXIDS);
+ ShmemRegisterStruct(&KnownAssignedXidsShmemDesc);
+
+ KnownAssignedXidsValidShmemDesc.size =
+ mul_size(sizeof(bool), TOTAL_MAX_CACHED_SUBXIDS);
+ ShmemRegisterStruct(&KnownAssignedXidsValidShmemDesc);
}
-
- return size;
}
-/*
- * Initialize the shared PGPROC array during postmaster startup.
- */
-void
-ProcArrayShmemInit(void)
+static void
+ProcArrayShmemInit(void *arg)
{
- bool found;
-
- /* Create or attach to the ProcArray shared structure */
- procArray = (ProcArrayStruct *)
- ShmemInitStruct("Proc Array",
- add_size(offsetof(ProcArrayStruct, pgprocnos),
- mul_size(sizeof(int),
- PROCARRAY_MAXPROCS)),
- &found);
-
- if (!found)
- {
- /*
- * We're the first - initialize.
- */
- procArray->numProcs = 0;
- procArray->maxProcs = PROCARRAY_MAXPROCS;
- procArray->maxKnownAssignedXids = TOTAL_MAX_CACHED_SUBXIDS;
- procArray->numKnownAssignedXids = 0;
- procArray->tailKnownAssignedXids = 0;
- procArray->headKnownAssignedXids = 0;
- procArray->lastOverflowedXid = InvalidTransactionId;
- procArray->replication_slot_xmin = InvalidTransactionId;
- procArray->replication_slot_catalog_xmin = InvalidTransactionId;
- TransamVariables->xactCompletionCount = 1;
- }
+ procArray->numProcs = 0;
+ procArray->maxProcs = PROCARRAY_MAXPROCS;
+ procArray->maxKnownAssignedXids = TOTAL_MAX_CACHED_SUBXIDS;
+ procArray->numKnownAssignedXids = 0;
+ procArray->tailKnownAssignedXids = 0;
+ procArray->headKnownAssignedXids = 0;
+ procArray->lastOverflowedXid = InvalidTransactionId;
+ procArray->replication_slot_xmin = InvalidTransactionId;
+ procArray->replication_slot_catalog_xmin = InvalidTransactionId;
+ TransamVariables->xactCompletionCount = 1;
allProcs = ProcGlobal->allProcs;
+}
- /* Create or attach to the KnownAssignedXids arrays too, if needed */
- if (EnableHotStandby)
- {
- KnownAssignedXids = (TransactionId *)
- ShmemInitStruct("KnownAssignedXids",
- mul_size(sizeof(TransactionId),
- TOTAL_MAX_CACHED_SUBXIDS),
- &found);
- KnownAssignedXidsValid = (bool *)
- ShmemInitStruct("KnownAssignedXidsValid",
- mul_size(sizeof(bool), TOTAL_MAX_CACHED_SUBXIDS),
- &found);
- }
+static void
+ProcArrayShmemAttach(void *arg)
+{
+ allProcs = ProcGlobal->allProcs;
}
/*
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index d47d180a32f..6bfc0126d96 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -104,7 +104,18 @@ struct ProcSignalHeader
#define BARRIER_CLEAR_BIT(flags, type) \
((flags) &= ~(((uint32) 1) << (uint32) (type)))
+static void ProcSignalShmemInit(void *arg);
+
NON_EXEC_STATIC ProcSignalHeader *ProcSignal = NULL;
+
+static ShmemStructDesc ProcSignalShmemDesc = {
+ .name = "ProcSignal",
+ .size = 0, /* dynamic */
+ .init_fn = ProcSignalShmemInit,
+ .ptr = (void **) &ProcSignal,
+};
+
+
static ProcSignalSlot *MyProcSignalSlot = NULL;
static bool CheckProcSignal(ProcSignalReason reason);
@@ -112,51 +123,37 @@ static void CleanupProcSignalState(int status, Datum arg);
static void ResetProcSignalBarrierBits(uint32 flags);
/*
- * ProcSignalShmemSize
- * Compute space needed for ProcSignal's shared memory
+ * ProcSignalShmemRegister
+ * Register ProcSignal's shared memory needs at postmaster startup
*/
-Size
-ProcSignalShmemSize(void)
+void
+ProcSignalShmemRegister(void)
{
Size size;
size = mul_size(NumProcSignalSlots, sizeof(ProcSignalSlot));
size = add_size(size, offsetof(ProcSignalHeader, psh_slot));
- return size;
+
+ ProcSignalShmemDesc.size = size;
+ ShmemRegisterStruct(&ProcSignalShmemDesc);
}
-/*
- * ProcSignalShmemInit
- * Allocate and initialize ProcSignal's shared memory
- */
-void
-ProcSignalShmemInit(void)
+static void
+ProcSignalShmemInit(void *arg)
{
- Size size = ProcSignalShmemSize();
- bool found;
+ pg_atomic_init_u64(&ProcSignal->psh_barrierGeneration, 0);
- ProcSignal = (ProcSignalHeader *)
- ShmemInitStruct("ProcSignal", size, &found);
-
- /* If we're first, initialize. */
- if (!found)
+ for (int i = 0; i < NumProcSignalSlots; ++i)
{
- int i;
-
- pg_atomic_init_u64(&ProcSignal->psh_barrierGeneration, 0);
+ ProcSignalSlot *slot = &ProcSignal->psh_slot[i];
- for (i = 0; i < NumProcSignalSlots; ++i)
- {
- ProcSignalSlot *slot = &ProcSignal->psh_slot[i];
-
- SpinLockInit(&slot->pss_mutex);
- pg_atomic_init_u32(&slot->pss_pid, 0);
- slot->pss_cancel_key_len = 0;
- MemSet(slot->pss_signalFlags, 0, sizeof(slot->pss_signalFlags));
- pg_atomic_init_u64(&slot->pss_barrierGeneration, PG_UINT64_MAX);
- pg_atomic_init_u32(&slot->pss_barrierCheckMask, 0);
- ConditionVariableInit(&slot->pss_barrierCV);
- }
+ SpinLockInit(&slot->pss_mutex);
+ pg_atomic_init_u32(&slot->pss_pid, 0);
+ slot->pss_cancel_key_len = 0;
+ MemSet(slot->pss_signalFlags, 0, sizeof(slot->pss_signalFlags));
+ pg_atomic_init_u64(&slot->pss_barrierGeneration, PG_UINT64_MAX);
+ pg_atomic_init_u32(&slot->pss_barrierCheckMask, 0);
+ ConditionVariableInit(&slot->pss_barrierCV);
}
}
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c20bec2bafb..2d487ef79eb 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -241,7 +241,7 @@ ShmemRegisterStruct(ShmemStructDesc *desc)
}
/* desc->ptr can be non-NULL when re-initializing after crash */
- if (desc->ptr)
+ if (!IsUnderPostmaster && desc->ptr)
*desc->ptr = NULL;
/* Add the descriptor to the array, growing the array if needed */
@@ -319,7 +319,7 @@ ShmemRegisterStruct(ShmemStructDesc *desc)
if (desc->ptr)
*desc->ptr = index_entry->location;
- /* XXX: if this errors out, the areas is left in a half-initialized state */
+ /* XXX: if this errors out, the area is left in a half-initialized state */
if (desc->init_fn)
desc->init_fn(desc->init_fn_arg);
}
diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c
index a7a7cc4f0a9..61c4e3aa93e 100644
--- a/src/backend/storage/ipc/sinvaladt.c
+++ b/src/backend/storage/ipc/sinvaladt.c
@@ -203,8 +203,17 @@ typedef struct SISeg
*/
#define NumProcStateSlots (MaxBackends + NUM_AUXILIARY_PROCS)
+static void SharedInvalShmemInit(void *arg);
+
static SISeg *shmInvalBuffer; /* pointer to the shared inval buffer */
+static ShmemStructDesc SharedInvalShmemDesc = {
+ .name = "shmInvalBuffer",
+ .size = 0, /* dynamic */
+ .init_fn = SharedInvalShmemInit,
+ .ptr = (void **) &shmInvalBuffer,
+};
+
static LocalTransactionId nextLocalTransactionId;
@@ -212,10 +221,11 @@ static void CleanupInvalidationState(int status, Datum arg);
/*
- * SharedInvalShmemSize --- return shared-memory space needed
+ * SharedInvalShmemRegister
+ * Register shared memory needs for the SI message buffer
*/
-Size
-SharedInvalShmemSize(void)
+void
+SharedInvalShmemRegister(void)
{
Size size;
@@ -223,26 +233,17 @@ SharedInvalShmemSize(void)
size = add_size(size, mul_size(sizeof(ProcState), NumProcStateSlots)); /* procState */
size = add_size(size, mul_size(sizeof(int), NumProcStateSlots)); /* pgprocnos */
- return size;
+ /* Allocate space in shared memory */
+ SharedInvalShmemDesc.size = size;
+ ShmemRegisterStruct(&SharedInvalShmemDesc);
}
-/*
- * SharedInvalShmemInit
- * Create and initialize the SI message buffer
- */
-void
-SharedInvalShmemInit(void)
+static void
+SharedInvalShmemInit(void *arg)
{
int i;
- bool found;
-
- /* Allocate space in shared memory */
- shmInvalBuffer = (SISeg *)
- ShmemInitStruct("shmInvalBuffer", SharedInvalShmemSize(), &found);
- if (found)
- return;
- /* Clear message counters, save size of procState array, init spinlock */
+ /* Clear message counters, save size of procState array FIXME, init spinlock */
shmInvalBuffer->minMsgNum = 0;
shmInvalBuffer->maxMsgNum = 0;
shmInvalBuffer->nextThreshold = CLEANUP_MIN;
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index ccf9de0e67c..188b4bfef4e 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -66,11 +66,37 @@ bool log_lock_waits = true;
/* Pointer to this process's PGPROC struct, if any */
PGPROC *MyProc = NULL;
+static void ProcGlobalShmemInit(void *arg);
+
/* Pointers to shared-memory structures */
PROC_HDR *ProcGlobal = NULL;
+static void *tmpAllProcs;
+static void *tmpFastPathLockArray;
NON_EXEC_STATIC PGPROC *AuxiliaryProcs = NULL;
PGPROC *PreparedXactProcs = NULL;
+
+static ShmemStructDesc ProcGlobalShmemDesc = {
+ .name = "Proc Header",
+ .size = sizeof(PROC_HDR),
+ .init_fn = ProcGlobalShmemInit,
+ .ptr = (void **) &ProcGlobal,
+};
+
+static ShmemStructDesc ProcGlobalAllProcsShmemDesc = {
+ .name = "PGPROC structures",
+ .size = 0, /* dynamic */
+ .ptr = (void **) &tmpAllProcs,
+};
+
+static ShmemStructDesc FastPathLockArrayShmemDesc = {
+ .name = "Fast-Path Lock Array",
+ .size = 0, /* dynamic */
+ .ptr = (void **) &tmpFastPathLockArray,
+};
+
+static uint32 TotalProcs;
+
/* Is a deadlock check pending? */
static volatile sig_atomic_t got_deadlock_timeout;
@@ -80,24 +106,6 @@ static void AuxiliaryProcKill(int code, Datum arg);
static DeadLockState CheckDeadLock(void);
-/*
- * Report shared-memory space needed by PGPROC.
- */
-static Size
-PGProcShmemSize(void)
-{
- Size size = 0;
- Size TotalProcs =
- add_size(MaxBackends, add_size(NUM_AUXILIARY_PROCS, max_prepared_xacts));
-
- size = add_size(size, mul_size(TotalProcs, sizeof(PGPROC)));
- size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->xids)));
- size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->subxidStates)));
- size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->statusFlags)));
-
- return size;
-}
-
/*
* Report shared-memory space needed by Fast-Path locks.
*/
@@ -105,8 +113,6 @@ static Size
FastPathLockShmemSize(void)
{
Size size = 0;
- Size TotalProcs =
- add_size(MaxBackends, add_size(NUM_AUXILIARY_PROCS, max_prepared_xacts));
Size fpLockBitsSize,
fpRelIdSize;
@@ -122,25 +128,6 @@ FastPathLockShmemSize(void)
return size;
}
-/*
- * Report shared-memory space needed by InitProcGlobal.
- */
-Size
-ProcGlobalShmemSize(void)
-{
- Size size = 0;
-
- /* ProcGlobal */
- size = add_size(size, sizeof(PROC_HDR));
- size = add_size(size, sizeof(slock_t));
-
- size = add_size(size, PGSemaphoreShmemSize(ProcGlobalSemas()));
- size = add_size(size, PGProcShmemSize());
- size = add_size(size, FastPathLockShmemSize());
-
- return size;
-}
-
/*
* Report number of semaphores needed by InitProcGlobal.
*/
@@ -175,35 +162,68 @@ ProcGlobalSemas(void)
* implementation typically requires us to create semaphores in the
* postmaster, not in backends.
*
- * Note: this is NOT called by individual backends under a postmaster,
+ * Note: this is NOT called by individual backends under a postmaster, XXX
* not even in the EXEC_BACKEND case. The ProcGlobal and AuxiliaryProcs
* pointers must be propagated specially for EXEC_BACKEND operation.
*/
void
-InitProcGlobal(void)
+ProcGlobalShmemRegister(void)
{
+ Size size = 0;
+
+ /*
+ * Reserve all the PGPROC structures we'll need. There are
+ * six separate consumers: (1) normal backends, (2) autovacuum workers and
+ * special workers, (3) background workers, (4) walsenders, (5) auxiliary
+ * processes, and (6) prepared transactions. (For largely-historical
+ * reasons, we combine autovacuum and special workers into one category
+ * with a single freelist.) Each PGPROC structure is dedicated to exactly
+ * one of these purposes, and they do not move between groups.
+ */
+ TotalProcs =
+ add_size(MaxBackends, add_size(NUM_AUXILIARY_PROCS, max_prepared_xacts));
+
+ size = add_size(size, mul_size(TotalProcs, sizeof(PGPROC)));
+
+ /* FIXME: the sizeofs look dangerous because ProcGlobal is not initialized yet */
+ size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->xids)));
+ size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->subxidStates)));
+ size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->statusFlags)));
+
+ ProcGlobalAllProcsShmemDesc.size = size;
+ ShmemRegisterStruct(&ProcGlobalAllProcsShmemDesc);
+
+ FastPathLockArrayShmemDesc.size = FastPathLockShmemSize();
+ ShmemRegisterStruct(&FastPathLockArrayShmemDesc);
+
+ /*
+ * Create the ProcGlobal shared structure last. Its init callback
+ * initializes the others too.
+ */
+ ShmemRegisterStruct(&ProcGlobalShmemDesc);
+
+ if (IsUnderPostmaster)
+ {
+ Assert(ProcGlobal != NULL);
+ AuxiliaryProcs = &ProcGlobal->allProcs[MaxBackends];
+ }
+}
+
+static void
+ProcGlobalShmemInit(void *arg)
+{
+ char *ptr;
+ size_t requestSize;
PGPROC *procs;
int i,
j;
- bool found;
- uint32 TotalProcs = MaxBackends + NUM_AUXILIARY_PROCS + max_prepared_xacts;
-
/* Used for setup of per-backend fast-path slots. */
char *fpPtr,
*fpEndPtr PG_USED_FOR_ASSERTS_ONLY;
Size fpLockBitsSize,
fpRelIdSize;
- Size requestSize;
- char *ptr;
-
- /* Create the ProcGlobal shared structure */
- ProcGlobal = (PROC_HDR *)
- ShmemInitStruct("Proc Header", sizeof(PROC_HDR), &found);
- Assert(!found);
- /*
- * Initialize the data structures.
- */
+ Assert(ProcGlobal);
ProcGlobal->spins_per_delay = DEFAULT_SPINS_PER_DELAY;
SpinLockInit(&ProcGlobal->freeProcsLock);
dlist_init(&ProcGlobal->freeProcs);
@@ -216,22 +236,10 @@ InitProcGlobal(void)
pg_atomic_init_u32(&ProcGlobal->procArrayGroupFirst, INVALID_PROC_NUMBER);
pg_atomic_init_u32(&ProcGlobal->clogGroupFirst, INVALID_PROC_NUMBER);
- /*
- * Create and initialize all the PGPROC structures we'll need. There are
- * six separate consumers: (1) normal backends, (2) autovacuum workers and
- * special workers, (3) background workers, (4) walsenders, (5) auxiliary
- * processes, and (6) prepared transactions. (For largely-historical
- * reasons, we combine autovacuum and special workers into one category
- * with a single freelist.) Each PGPROC structure is dedicated to exactly
- * one of these purposes, and they do not move between groups.
- */
- requestSize = PGProcShmemSize();
-
- ptr = ShmemInitStruct("PGPROC structures",
- requestSize,
- &found);
-
- MemSet(ptr, 0, requestSize);
+ Assert(tmpAllProcs);
+ ptr = tmpAllProcs;
+ requestSize = ProcGlobalAllProcsShmemDesc.size;
+ memset(ptr, 0, requestSize);
procs = (PGPROC *) ptr;
ptr = ptr + TotalProcs * sizeof(PGPROC);
@@ -267,20 +275,14 @@ InitProcGlobal(void)
fpLockBitsSize = MAXALIGN(FastPathLockGroupsPerBackend * sizeof(uint64));
fpRelIdSize = MAXALIGN(FastPathLockSlotsPerBackend() * sizeof(Oid));
- requestSize = FastPathLockShmemSize();
-
- fpPtr = ShmemInitStruct("Fast-Path Lock Array",
- requestSize,
- &found);
-
- MemSet(fpPtr, 0, requestSize);
+ Assert(tmpFastPathLockArray);
+ fpPtr = tmpFastPathLockArray;
+ requestSize = FastPathLockArrayShmemDesc.size;
+ memset(fpPtr, 0, requestSize);
/* For asserts checking we did not overflow. */
fpEndPtr = fpPtr + requestSize;
- /* Reserve space for semaphores. */
- PGReserveSemaphores(ProcGlobalSemas());
-
for (i = 0; i < TotalProcs; i++)
{
PGPROC *proc = &procs[i];
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 6fa91bfcdc0..6e5a546f411 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -15,7 +15,9 @@
#define TRANSAM_H
#include "access/xlogdefs.h"
-
+#ifndef FRONTEND
+#include "storage/shmem.h"
+#endif
/* ----------------
* Special transaction ID values
@@ -330,7 +332,10 @@ TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
extern bool TransactionStartedDuringRecovery(void);
/* in transam/varsup.c */
+#ifndef FRONTEND
+extern PGDLLIMPORT struct ShmemStructDesc TransamVariablesShmemDesc;
extern PGDLLIMPORT TransamVariablesData *TransamVariables;
+#endif
/*
* prototypes for functions in transam/transam.c
@@ -345,8 +350,7 @@ extern TransactionId TransactionIdLatest(TransactionId mainxid,
extern XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid);
/* in transam/varsup.c */
-extern Size VarsupShmemSize(void);
-extern void VarsupShmemInit(void);
+extern void VarsupShmemRegister(void);
extern FullTransactionId GetNewTransactionId(bool isSubXact);
extern void AdvanceNextFullTransactionIdPastXid(TransactionId xid);
extern FullTransactionId ReadNextFullTransactionId(void);
diff --git a/src/include/storage/dsm_registry.h b/src/include/storage/dsm_registry.h
index 506fae2c9ca..9a1b4d982af 100644
--- a/src/include/storage/dsm_registry.h
+++ b/src/include/storage/dsm_registry.h
@@ -22,7 +22,6 @@ extern dsa_area *GetNamedDSA(const char *name, bool *found);
extern dshash_table *GetNamedDSHash(const char *name,
const dshash_parameters *params,
bool *found);
-extern Size DSMRegistryShmemSize(void);
-extern void DSMRegistryShmemInit(void);
+extern void DSMRegistryShmemRegister(void);
#endif /* DSM_REGISTRY_H */
diff --git a/src/include/storage/pmsignal.h b/src/include/storage/pmsignal.h
index 206fb78f8a5..7cdc4852334 100644
--- a/src/include/storage/pmsignal.h
+++ b/src/include/storage/pmsignal.h
@@ -66,8 +66,7 @@ extern PGDLLIMPORT volatile PMSignalData *PMSignalState;
/*
* prototypes for functions in pmsignal.c
*/
-extern Size PMSignalShmemSize(void);
-extern void PMSignalShmemInit(void);
+extern void PMSignalShmemRegister(void);
extern void SendPostmasterSignal(PMSignalReason reason);
extern bool CheckPostmasterSignal(PMSignalReason reason);
extern void SetQuitSignalReason(QuitSignalReason reason);
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 3f89450c216..1d1e0881af2 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -552,7 +552,7 @@ extern PGDLLIMPORT PGPROC *AuxiliaryProcs;
* Function Prototypes
*/
extern int ProcGlobalSemas(void);
-extern Size ProcGlobalShmemSize(void);
+extern void ProcGlobalShmemRegister(void);
extern void InitProcGlobal(void);
extern void InitProcess(void);
extern void InitProcessPhase2(void);
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index c5ab1574fe3..572516c4e21 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -20,8 +20,7 @@
#include "utils/snapshot.h"
-extern Size ProcArrayShmemSize(void);
-extern void ProcArrayShmemInit(void);
+extern void ProcArrayShmemRegister(void);
extern void ProcArrayAdd(PGPROC *proc);
extern void ProcArrayRemove(PGPROC *proc, TransactionId latestXid);
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 348fba53a93..d2344b1cbb3 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -63,8 +63,7 @@ typedef enum
/*
* prototypes for functions in procsignal.c
*/
-extern Size ProcSignalShmemSize(void);
-extern void ProcSignalShmemInit(void);
+extern void ProcSignalShmemRegister(void);
extern void ProcSignalInit(const uint8 *cancel_key, int cancel_key_len);
extern int SendProcSignal(pid_t pid, ProcSignalReason reason,
diff --git a/src/include/storage/sinvaladt.h b/src/include/storage/sinvaladt.h
index a1694500a85..4edba2936e6 100644
--- a/src/include/storage/sinvaladt.h
+++ b/src/include/storage/sinvaladt.h
@@ -28,8 +28,7 @@
/*
* prototypes for functions in sinvaladt.c
*/
-extern Size SharedInvalShmemSize(void);
-extern void SharedInvalShmemInit(void);
+extern void SharedInvalShmemRegister(void);
extern void SharedInvalBackendInit(bool sendOnly);
extern void SIInsertDataEntries(const SharedInvalidationMessage *data, int n);
--
2.47.3