On 21/08/2026 08:36, Ayush Tiwari wrote:
Looking at the after-startup path from 283e823f9dc, I noticed that
RegisterShmemCallbacks() does not do anything useful in single-user mode.
The branch that does the work immediately is guarded by:

     if (shmem_request_state == SRS_DONE && IsUnderPostmaster)

IsUnderPostmaster is false in a standalone backend as well as in the
postmaster, so a standalone backend always falls through to the "remember
the callbacks for later" branch, and startup has already consumed that
list.  An extension loaded at runtime there, e.g. with LOAD or CREATE
EXTENSION, therefore never has its callbacks called, and
RegisterShmemCallbacks() reports nothing:

     LOG:  test_shmem module's _PG_init called
     ERROR:  shmem area not attached or initialized in this process

The LOG line is the only trace of the registration.  The ERROR is
test_shmem's own guard when the area is later used, not the shmem code;
an extension without such a guard would dereference a NULL pointer?

I am unsure whether this was left out intentionally or simply missed, but
it seems worth having: a standalone backend has shared memory and the
same after-startup reserve, and a silent no-op is easy to mistake for
success.

That was an oversight, thanks for the testing!

The attached patch also checks !IsPostmasterEnvironment and adds a
single-user case to the test_shmem TAP test.

Hmm, I think the check should be just "if (shmem_request_state == SRS_DONE)", with an assert that it's not called from the postmaster. The reason it cannot be called from the postmaster after startup is that postmaster cannot acquire lwlocks.

        skip 'single-user mode is not supported on this platform', 1
          if $windows_os;

Huh, we don't support single-user mode on Windows? /me looks around. We do, but apparently there are some issues with it in the CI. All other single-user tests are also skipped on Windows. Per commit 1f2e51e3c7:

These tests are skipped on Windows, as direct calls of
    postgres --single would fail on permission failures.  There is no
    platform-specific behavior that needs to be checked, so living with this
    restriction should be fine.  The CI is OK with that, now let's see what
    the buildfarm tells.

Ok then I guess, but I'll copy the comment from that commit to explain why it's not run on Windows.

See attached.

- Heikki
From c5f5ed2ee4c2219bad6d9079ccb09cc51f2ff7d8 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Wed, 26 Aug 2026 00:39:31 +0300
Subject: [PATCH v2 1/1] Fix registering shmem callbacks in single-user mode

RegisterShmemCallbacks() should not be called from the postmaster
process after postmaster startup. Add an assertion for that, and fix
the check for whether it's being called for "after startup"
allocations to take single-user mode into account.

Reported-by: Ayush Tiwari <[email protected]>
Discussion: https://www.postgresql.org/message-id/cajtyswu7epjul1-xrpyeormkmq20m2q22y_srb8pwqg8knq...@mail.gmail.com
Backpatch-through: 19
---
 src/backend/storage/ipc/shmem.c               | 11 +++++++--
 .../test_shmem/t/001_late_shmem_alloc.pl      | 24 +++++++++++++++++++
 2 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c511395b1f0..0200bcda2cd 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -911,23 +911,30 @@ ShmemAddrIsValid(const void *addr)
 void
 RegisterShmemCallbacks(const ShmemCallbacks *callbacks)
 {
-	if (shmem_request_state == SRS_DONE && IsUnderPostmaster)
+	if (shmem_request_state == SRS_DONE)
 	{
 		/*
 		 * After-startup initialization or attachment.  Call the appropriate
 		 * callbacks immediately.
+		 *
+		 * This is not allowed from the postmaster, because the postmaster
+		 * cannot acquire locks.
 		 */
+		Assert(IsUnderPostmaster || !IsPostmasterEnvironment);
+
 		if ((callbacks->flags & SHMEM_CALLBACKS_ALLOW_AFTER_STARTUP) == 0)
 			elog(ERROR, "cannot request shared memory at this time");
 
 		CallShmemCallbacksAfterStartup(callbacks);
 	}
-	else
+	else if (shmem_request_state == SRS_INITIAL)
 	{
 		/* Remember the callbacks for later */
 		registered_shmem_callbacks = lappend(registered_shmem_callbacks,
 											 (void *) callbacks);
 	}
+	else
+		elog(ERROR, "cannot request shared memory at this time");
 }
 
 /*
diff --git a/src/test/modules/test_shmem/t/001_late_shmem_alloc.pl b/src/test/modules/test_shmem/t/001_late_shmem_alloc.pl
index b09889f4fb0..d2bf32a72fe 100644
--- a/src/test/modules/test_shmem/t/001_late_shmem_alloc.pl
+++ b/src/test/modules/test_shmem/t/001_late_shmem_alloc.pl
@@ -100,4 +100,28 @@ else
 }
 
 $node->stop;
+
+###
+# Test allocating memory after startup in single-user mode
+###
+SKIP:
+{
+	# Skip the test on Windows, as single-user mode would fail on permission
+	# failure with privileged accounts.
+	skip 'single-user mode is not supported on this platform', 1
+	  if $windows_os;
+
+	my $query = "SELECT get_test_shmem_attach_count();\n";
+	my $result = run_log(
+		[
+			'postgres', '--single', '-F',
+			'-c' => 'exit_on_error=true',
+			'-D' => $node->data_dir,
+			'postgres'
+		],
+		'<' => \$query);
+
+	ok($result, "shmem area is initialized in single-user mode");
+}
+
 done_testing();
-- 
2.47.3

Reply via email to