=== Applying patches on top of PostgreSQL commit ID 66ad764c8d517f59577d41ac3dad786729c9e10e === /etc/rc.d/jail: WARNING: Per-jail configuration via jail_* variables is obsolete. Please consider migrating to /etc/jail.conf. Tue Apr 14 20:37:31 UTC 2026 On branch cf/5319 nothing to commit, working tree clean === using 'git am' to apply patch ./0001-wip-Introduce-a-new-way-of-registering-shar-20260210.patch === Applying: wip: Introduce a new way of registering shared memory structs Using index info to reconstruct a base tree... M contrib/pg_stat_statements/pg_stat_statements.c M src/backend/access/transam/varsup.c M src/backend/bootstrap/bootstrap.c M src/backend/postmaster/launch_backend.c M src/backend/postmaster/postmaster.c M src/backend/storage/ipc/dsm.c M src/backend/storage/ipc/dsm_registry.c M src/backend/storage/ipc/ipci.c M src/backend/storage/ipc/pmsignal.c M src/backend/storage/ipc/procarray.c M src/backend/storage/ipc/procsignal.c M src/backend/storage/ipc/shmem.c M src/backend/storage/ipc/sinvaladt.c M src/backend/storage/lmgr/proc.c M src/backend/tcop/postgres.c M src/include/access/transam.h M src/include/storage/dsm_registry.h M src/include/storage/ipc.h M src/include/storage/pmsignal.h M src/include/storage/proc.h M src/include/storage/procarray.h M src/include/storage/procsignal.h M src/include/storage/shmem.h M src/include/storage/sinvaladt.h Falling back to patching base and 3-way merge... Auto-merging src/include/storage/sinvaladt.h CONFLICT (content): Merge conflict in src/include/storage/sinvaladt.h Auto-merging src/include/storage/shmem.h CONFLICT (content): Merge conflict in src/include/storage/shmem.h Auto-merging src/include/storage/procsignal.h CONFLICT (content): Merge conflict in src/include/storage/procsignal.h Auto-merging src/include/storage/procarray.h CONFLICT (content): Merge conflict in src/include/storage/procarray.h Auto-merging src/include/storage/proc.h CONFLICT (content): Merge conflict in src/include/storage/proc.h Auto-merging src/include/storage/pmsignal.h CONFLICT (content): Merge conflict in src/include/storage/pmsignal.h Auto-merging src/include/storage/ipc.h CONFLICT (content): Merge conflict in src/include/storage/ipc.h Auto-merging src/include/storage/dsm_registry.h CONFLICT (content): Merge conflict in src/include/storage/dsm_registry.h Auto-merging src/include/access/transam.h CONFLICT (content): Merge conflict in src/include/access/transam.h Auto-merging src/backend/tcop/postgres.c Auto-merging src/backend/storage/lmgr/proc.c CONFLICT (content): Merge conflict in src/backend/storage/lmgr/proc.c Auto-merging src/backend/storage/ipc/sinvaladt.c CONFLICT (content): Merge conflict in src/backend/storage/ipc/sinvaladt.c Auto-merging src/backend/storage/ipc/shmem.c CONFLICT (content): Merge conflict in src/backend/storage/ipc/shmem.c Auto-merging src/backend/storage/ipc/procsignal.c CONFLICT (content): Merge conflict in src/backend/storage/ipc/procsignal.c Auto-merging src/backend/storage/ipc/procarray.c CONFLICT (content): Merge conflict in src/backend/storage/ipc/procarray.c Auto-merging src/backend/storage/ipc/pmsignal.c CONFLICT (content): Merge conflict in src/backend/storage/ipc/pmsignal.c Auto-merging src/backend/storage/ipc/ipci.c CONFLICT (content): Merge conflict in src/backend/storage/ipc/ipci.c Auto-merging src/backend/storage/ipc/dsm_registry.c CONFLICT (content): Merge conflict in src/backend/storage/ipc/dsm_registry.c Auto-merging src/backend/storage/ipc/dsm.c CONFLICT (content): Merge conflict in src/backend/storage/ipc/dsm.c Auto-merging src/backend/postmaster/postmaster.c Auto-merging src/backend/postmaster/launch_backend.c CONFLICT (content): Merge conflict in src/backend/postmaster/launch_backend.c Auto-merging src/backend/bootstrap/bootstrap.c CONFLICT (content): Merge conflict in src/backend/bootstrap/bootstrap.c Auto-merging src/backend/access/transam/varsup.c CONFLICT (content): Merge conflict in src/backend/access/transam/varsup.c Auto-merging contrib/pg_stat_statements/pg_stat_statements.c CONFLICT (content): Merge conflict in contrib/pg_stat_statements/pg_stat_statements.c error: Failed to merge in the changes. hint: Use 'git am --show-current-patch=diff' to see the failed patch Patch failed at 0001 wip: Introduce a new way of registering shared memory structs When you have resolved this problem, run "git am --continue". If you prefer to skip this patch, run "git am --skip" instead. To restore the original branch and stop patching, run "git am --abort". === using patch(1) to apply patch ./0001-wip-Introduce-a-new-way-of-registering-shar-20260210.patch === patching file contrib/pg_stat_statements/pg_stat_statements.c Hunk #1 succeeded at 269 with fuzz 2 (offset 11 lines). Hunk #2 FAILED at 293. Hunk #3 FAILED at 384. Hunk #4 FAILED at 519. Hunk #5 FAILED at 535. Hunk #6 FAILED at 549. Hunk #7 FAILED at 592. Hunk #8 FAILED at 2101. 7 out of 8 hunks FAILED -- saving rejects to file contrib/pg_stat_statements/pg_stat_statements.c.rej patching file src/backend/access/transam/varsup.c Hunk #1 FAILED at 30. 1 out of 1 hunk FAILED -- saving rejects to file src/backend/access/transam/varsup.c.rej patching file src/backend/bootstrap/bootstrap.c Hunk #1 FAILED at 337. 1 out of 1 hunk FAILED -- saving rejects to file src/backend/bootstrap/bootstrap.c.rej patching file src/backend/postmaster/launch_backend.c Hunk #1 FAILED at 49. Hunk #2 FAILED at 104. Hunk #3 FAILED at 678. Hunk #4 FAILED at 735. Hunk #5 FAILED at 995. 5 out of 5 hunks FAILED -- saving rejects to file src/backend/postmaster/launch_backend.c.rej patching file src/backend/postmaster/postmaster.c Hunk #1 succeeded at 979 (offset 11 lines). patching file src/backend/storage/ipc/dsm.c Hunk #1 FAILED at 108. Hunk #2 FAILED at 479. 2 out of 2 hunks FAILED -- saving rejects to file src/backend/storage/ipc/dsm.c.rej patching file src/backend/storage/ipc/dsm_registry.c Hunk #1 succeeded at 56 with fuzz 2 (offset 2 lines). Hunk #2 FAILED at 121. 1 out of 2 hunks FAILED -- saving rejects to file src/backend/storage/ipc/dsm_registry.c.rej patching file src/backend/storage/ipc/ipci.c Hunk #1 FAILED at 101. Hunk #2 FAILED at 117. Hunk #3 FAILED at 217. Hunk #4 succeeded at 179 with fuzz 2 (offset -51 lines). Hunk #5 FAILED at 272. Hunk #6 FAILED at 301. 5 out of 6 hunks FAILED -- saving rejects to file src/backend/storage/ipc/ipci.c.rej patching file src/backend/storage/ipc/pmsignal.c Hunk #1 FAILED at 80. Hunk #2 FAILED at 123. Hunk #3 succeeded at 290 (offset -1 lines). 2 out of 3 hunks FAILED -- saving rejects to file src/backend/storage/ipc/pmsignal.c.rej patching file src/backend/storage/ipc/procarray.c Hunk #1 succeeded at 116 with fuzz 2 (offset 15 lines). Hunk #2 FAILED at 279. Hunk #3 FAILED at 292. Hunk #4 FAILED at 384. Hunk #5 FAILED at 415. 4 out of 5 hunks FAILED -- saving rejects to file src/backend/storage/ipc/procarray.c.rej patching file src/backend/storage/ipc/procsignal.c Hunk #1 FAILED at 102. Hunk #2 FAILED at 110. 2 out of 2 hunks FAILED -- saving rejects to file src/backend/storage/ipc/procsignal.c.rej patching file src/backend/storage/ipc/shmem.c Hunk #1 FAILED at 19. Hunk #2 succeeded at 145 with fuzz 2 (offset 69 lines). Hunk #3 succeeded at 248 with fuzz 2 (offset 143 lines). Hunk #4 FAILED at 116. Hunk #5 succeeded at 467 with fuzz 2 (offset 162 lines). 2 out of 5 hunks FAILED -- saving rejects to file src/backend/storage/ipc/shmem.c.rej patching file src/backend/storage/ipc/sinvaladt.c Hunk #1 succeeded at 204 with fuzz 2 (offset 1 line). Hunk #2 FAILED at 221. Hunk #3 FAILED at 232. 2 out of 3 hunks FAILED -- saving rejects to file src/backend/storage/ipc/sinvaladt.c.rej patching file src/backend/storage/lmgr/proc.c Hunk #1 FAILED at 73. Hunk #2 FAILED at 91. Hunk #3 FAILED at 116. Hunk #4 FAILED at 133. Hunk #5 FAILED at 186. Hunk #6 FAILED at 225. Hunk #7 FAILED at 277. Hunk #8 FAILED at 380. 8 out of 8 hunks FAILED -- saving rejects to file src/backend/storage/lmgr/proc.c.rej patching file src/backend/tcop/postgres.c Hunk #1 succeeded at 4207 (offset 90 lines). patching file src/include/access/transam.h Hunk #3 FAILED at 350. 1 out of 3 hunks FAILED -- saving rejects to file src/include/access/transam.h.rej patching file src/include/storage/dsm_registry.h Hunk #1 FAILED at 22. 1 out of 1 hunk FAILED -- saving rejects to file src/include/storage/dsm_registry.h.rej patching file src/include/storage/ipc.h Hunk #1 FAILED at 77. 1 out of 1 hunk FAILED -- saving rejects to file src/include/storage/ipc.h.rej patching file src/include/storage/pmsignal.h Hunk #1 FAILED at 66. 1 out of 1 hunk FAILED -- saving rejects to file src/include/storage/pmsignal.h.rej patching file src/include/storage/proc.h Hunk #1 FAILED at 418. Hunk #2 FAILED at 488. 2 out of 2 hunks FAILED -- saving rejects to file src/include/storage/proc.h.rej patching file src/include/storage/procarray.h Hunk #1 FAILED at 20. 1 out of 1 hunk FAILED -- saving rejects to file src/include/storage/procarray.h.rej patching file src/include/storage/procsignal.h Hunk #1 FAILED at 71. 1 out of 1 hunk FAILED -- saving rejects to file src/include/storage/procsignal.h.rej patching file src/include/storage/shmem.h Hunk #1 FAILED at 24. Hunk #2 FAILED at 34. 2 out of 2 hunks FAILED -- saving rejects to file src/include/storage/shmem.h.rej patching file src/include/storage/sinvaladt.h Hunk #1 FAILED at 28. 1 out of 1 hunk FAILED -- saving rejects to file src/include/storage/sinvaladt.h.rej Unstaged changes after reset: M contrib/pg_stat_statements/pg_stat_statements.c M src/backend/postmaster/postmaster.c M src/backend/storage/ipc/dsm_registry.c M src/backend/storage/ipc/ipci.c M src/backend/storage/ipc/pmsignal.c M src/backend/storage/ipc/procarray.c M src/backend/storage/ipc/shmem.c M src/backend/storage/ipc/sinvaladt.c M src/backend/tcop/postgres.c M src/include/access/transam.h Removing contrib/pg_stat_statements/pg_stat_statements.c.rej Removing src/backend/access/transam/varsup.c.rej Removing src/backend/bootstrap/bootstrap.c.rej Removing src/backend/postmaster/launch_backend.c.rej Removing src/backend/storage/ipc/dsm.c.rej Removing src/backend/storage/ipc/dsm_registry.c.rej Removing src/backend/storage/ipc/ipci.c.rej Removing src/backend/storage/ipc/pmsignal.c.rej Removing src/backend/storage/ipc/procarray.c.rej Removing src/backend/storage/ipc/procsignal.c.rej Removing src/backend/storage/ipc/shmem.c.rej Removing src/backend/storage/ipc/sinvaladt.c.rej Removing src/backend/storage/lmgr/proc.c.rej Removing src/include/access/transam.h.rej Removing src/include/storage/dsm_registry.h.rej Removing src/include/storage/ipc.h.rej Removing src/include/storage/pmsignal.h.rej Removing src/include/storage/proc.h.rej Removing src/include/storage/procarray.h.rej Removing src/include/storage/procsignal.h.rej Removing src/include/storage/shmem.h.rej Removing src/include/storage/sinvaladt.h.rej === using 'git apply' to apply patch ./0001-wip-Introduce-a-new-way-of-registering-shar-20260210.patch === Applied patch to 'contrib/pg_stat_statements/pg_stat_statements.c' with conflicts. Applied patch to 'src/backend/access/transam/varsup.c' with conflicts. Applied patch to 'src/backend/bootstrap/bootstrap.c' with conflicts. Applied patch to 'src/backend/postmaster/launch_backend.c' with conflicts. Applied patch to 'src/backend/postmaster/postmaster.c' cleanly. Applied patch to 'src/backend/storage/ipc/dsm.c' with conflicts. Applied patch to 'src/backend/storage/ipc/dsm_registry.c' with conflicts. Applied patch to 'src/backend/storage/ipc/ipci.c' with conflicts. Applied patch to 'src/backend/storage/ipc/pmsignal.c' with conflicts. Applied patch to 'src/backend/storage/ipc/procarray.c' with conflicts. Applied patch to 'src/backend/storage/ipc/procsignal.c' with conflicts. Applied patch to 'src/backend/storage/ipc/shmem.c' with conflicts. Applied patch to 'src/backend/storage/ipc/sinvaladt.c' with conflicts. Applied patch to 'src/backend/storage/lmgr/proc.c' with conflicts. Applied patch to 'src/backend/tcop/postgres.c' cleanly. Applied patch to 'src/include/access/transam.h' with conflicts. Applied patch to 'src/include/storage/dsm_registry.h' with conflicts. Applied patch to 'src/include/storage/ipc.h' with conflicts. Applied patch to 'src/include/storage/pmsignal.h' with conflicts. Applied patch to 'src/include/storage/proc.h' with conflicts. Applied patch to 'src/include/storage/procarray.h' with conflicts. Applied patch to 'src/include/storage/procsignal.h' with conflicts. Applied patch to 'src/include/storage/shmem.h' with conflicts. Applied patch to 'src/include/storage/sinvaladt.h' with conflicts. U contrib/pg_stat_statements/pg_stat_statements.c U src/backend/access/transam/varsup.c U src/backend/bootstrap/bootstrap.c U src/backend/postmaster/launch_backend.c U src/backend/storage/ipc/dsm.c U src/backend/storage/ipc/dsm_registry.c U src/backend/storage/ipc/ipci.c U src/backend/storage/ipc/pmsignal.c U src/backend/storage/ipc/procarray.c U src/backend/storage/ipc/procsignal.c U src/backend/storage/ipc/shmem.c U src/backend/storage/ipc/sinvaladt.c U src/backend/storage/lmgr/proc.c U src/include/access/transam.h U src/include/storage/dsm_registry.h U src/include/storage/ipc.h U src/include/storage/pmsignal.h U src/include/storage/proc.h U src/include/storage/procarray.h U src/include/storage/procsignal.h U src/include/storage/shmem.h U src/include/storage/sinvaladt.h diff --cc contrib/pg_stat_statements/pg_stat_statements.c index 95a5411a39d,71debc8b47f..00000000000 --- a/contrib/pg_stat_statements/pg_stat_statements.c +++ b/contrib/pg_stat_statements/pg_stat_statements.c @@@ -257,18 -258,25 +257,40 @@@ typedef struct pgssSharedStat pgssGlobalStats stats; /* global statistics for pgss */ } pgssSharedState; ++<<<<<<< ours +/* Links to shared memory state */ +static pgssSharedState *pgss; +static HTAB *pgss_hash; + +static void pgss_shmem_request(void *arg); +static void pgss_shmem_init(void *arg); + +static const ShmemCallbacks pgss_shmem_callbacks = { + .request_fn = pgss_shmem_request, + .init_fn = pgss_shmem_init, +}; + ++======= + static void pgss_shmem_init(void *arg); + + static ShmemStructDesc pgssSharedStateShmemDesc = { + .name = "pg_stat_statements", + .size = sizeof(pgssSharedState), + .init_fn = pgss_shmem_init, + }; + + static ShmemHashDesc pgssSharedHashDesc = { + .name = "pg_stat_statements hash", + .init_size = 0, /* set from 'pgss_max' */ + .max_size = 0, /* set from 'pgss_max' */ + }; + + /* Links to shared memory state */ + #define pgss ((pgssSharedState *) pgssSharedStateShmemDesc.ptr) + #define pgss_hash (pgssSharedHashDesc.ptr) + + ++>>>>>>> theirs /*---- Local variables ----*/ /* Current nesting depth of planner/ExecutorRun/ProcessUtility calls */ @@@ -494,42 -508,56 +516,81 @@@ _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 callback: Request shared memory resources. + * + * This is called at postmaster startup. Note that the shared memory isn't + * allocated here yet, this merely register our needs. + * + * In EXEC_BACKEND mode, this is also called in each backend, to re-attach to + * the shared memory area that was already initialized. */ static void -pgss_shmem_request(void) +pgss_shmem_request(void *arg) { ++<<<<<<< ours + ShmemRequestHash(.name = "pg_stat_statements hash", + .nelems = pgss_max, + .hash_info.keysize = sizeof(pgssHashKey), + .hash_info.entrysize = sizeof(pgssEntry), + .hash_flags = HASH_ELEM | HASH_BLOBS, + .ptr = &pgss_hash, + ); + ShmemRequestStruct(.name = "pg_stat_statements", + .size = sizeof(pgssSharedState), + .ptr = (void **) &pgss, + ); ++======= + HASHCTL info; + + if (prev_shmem_request_hook) + prev_shmem_request_hook(); + + RequestNamedLWLockTranche("pg_stat_statements", 1); + + /* + * Register our shared memory state, 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); + } + + 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(); ++>>>>>>> theirs } /* - * 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 init callback: Initialize our shared memory data structures at + * postmaster startup. + * + * 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. */ static void -pgss_shmem_startup(void) +pgss_shmem_init(void *arg) { ++<<<<<<< ours + int tranche_id; ++======= ++>>>>>>> theirs FILE *file = NULL; FILE *qfile = NULL; uint32 header; @@@ -539,38 -567,17 +600,46 @@@ int buffer_size; char *buffer = NULL; ++<<<<<<< ours + /* + * We already checked that we're loaded from shared_preload_libraries in + * _PG_init(), so we should not get here after postmaster startup. + */ + Assert(!IsUnderPostmaster); ++======= + if (prev_shmem_startup_hook) + prev_shmem_startup_hook(); ++>>>>>>> theirs + + /* + * Initialize the shmem area with no statistics. + */ + tranche_id = LWLockNewTrancheId("pg_stat_statements"); + LWLockInitialize(&pgss->lock.lock, tranche_id); + 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(); + + /* The hash table must've also been initialized by now */ + Assert(pgss_hash != NULL); /* - * If we're in the postmaster (or a standalone backend...), set up a shmem - * exit hook to dump the statistics to disk. ++<<<<<<< ours + * 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); + on_shmem_exit(pgss_shmem_shutdown, (Datum) 0); /* + * Load any pre-existing statistics from file. + * ++======= ++>>>>>>> theirs * Note: we don't bother with locks here, because there should be no other * processes running when this code is reached. */ diff --cc src/backend/access/transam/varsup.c index dc5e32d86f3,11ad90e7372..00000000000 --- a/src/backend/access/transam/varsup.c +++ b/src/backend/access/transam/varsup.c @@@ -31,25 -30,27 +31,47 @@@ /* Number of OIDs to prefetch (preallocate) per XLOG write */ #define VAR_OID_PREFETCH 8192 ++<<<<<<< ours +static void VarsupShmemRequest(void *arg); + +/* pointer to variables struct in shared memory */ +TransamVariablesData *TransamVariables = NULL; + +const ShmemCallbacks VarsupShmemCallbacks = { + .request_fn = VarsupShmemRequest, ++======= + static void VarsupShmemInit(void *arg); + + ShmemStructDesc TransamVariablesShmemDesc = { + .name = "TransamVariables", + .size = sizeof(TransamVariablesData), + .init_fn = VarsupShmemInit, ++>>>>>>> theirs }; /* - * Initialization of shared memory for TransamVariables. + * Request shared memory for TransamVariables. */ ++<<<<<<< ours +static void +VarsupShmemRequest(void *arg) +{ + ShmemRequestStruct(.name = "TransamVariables", + .size = sizeof(TransamVariablesData), + .ptr = (void **) &TransamVariables, + ); ++======= + void + VarsupShmemRegister(void) + { + ShmemRegisterStruct(&TransamVariablesShmemDesc); + } + + static void + VarsupShmemInit(void *arg) + { + memset(TransamVariables, 0, sizeof(TransamVariablesData)); ++>>>>>>> theirs } /* diff --cc src/backend/bootstrap/bootstrap.c index a4af7bf8fad,0ded7018e86..00000000000 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@@ -376,7 -337,8 +376,12 @@@ BootstrapModeMain(int argc, char *argv[ InitializeFastPathLocks(); ++<<<<<<< ours + ShmemCallRequestCallbacks(); ++======= + RegisterShmemStructs(); + ++>>>>>>> theirs CreateSharedMemoryAndSemaphores(); /* diff --cc src/backend/postmaster/launch_backend.c index 8f3cfea880c,8f638118cdf..00000000000 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@@ -102,6 -100,11 +102,14 @@@ typedef struc #ifdef USE_INJECTION_POINTS struct InjectionPointsCtl *ActiveInjectionPoints; #endif ++<<<<<<< ours ++======= + int NamedLWLockTrancheRequests; + NamedLWLockTrancheRequest *NamedLWLockTrancheRequestArray; + char **LWLockTrancheNames; + int *LWLockCounter; + LWLockPadded *MainLWLockArray; ++>>>>>>> theirs PROC_HDR *ProcGlobal; PGPROC *AuxiliaryProcs; PGPROC *PreparedXactProcs; @@@ -681,9 -679,10 +688,12 @@@ SubPostmasterMain(int argc, char *argv[ if (UsedShmemSegAddr != NULL) { InitShmemAllocator(UsedShmemSegAddr); + ShmemCallRequestCallbacks(); + } + RegisterShmemStructs(); + } + /* * Run the appropriate Main function */ @@@ -734,6 -733,11 +744,14 @@@ save_backend_variables(BackendParameter param->ActiveInjectionPoints = ActiveInjectionPoints; #endif ++<<<<<<< ours ++======= + param->NamedLWLockTrancheRequests = NamedLWLockTrancheRequests; + param->NamedLWLockTrancheRequestArray = NamedLWLockTrancheRequestArray; + param->LWLockTrancheNames = LWLockTrancheNames; + param->LWLockCounter = LWLockCounter; + param->MainLWLockArray = MainLWLockArray; ++>>>>>>> theirs param->ProcGlobal = ProcGlobal; param->AuxiliaryProcs = AuxiliaryProcs; param->PreparedXactProcs = PreparedXactProcs; @@@ -990,6 -991,11 +1007,14 @@@ restore_backend_variables(BackendParame ActiveInjectionPoints = param->ActiveInjectionPoints; #endif ++<<<<<<< ours ++======= + NamedLWLockTrancheRequests = param->NamedLWLockTrancheRequests; + NamedLWLockTrancheRequestArray = param->NamedLWLockTrancheRequestArray; + LWLockTrancheNames = param->LWLockTrancheNames; + LWLockCounter = param->LWLockCounter; + MainLWLockArray = param->MainLWLockArray; ++>>>>>>> theirs ProcGlobal = param->ProcGlobal; AuxiliaryProcs = param->AuxiliaryProcs; PreparedXactProcs = param->PreparedXactProcs; diff --cc src/backend/storage/ipc/dsm.c index 8b69df4ff26,55f46c7687e..00000000000 --- a/src/backend/storage/ipc/dsm.c +++ b/src/backend/storage/ipc/dsm.c @@@ -109,16 -108,15 +109,28 @@@ static inline bool is_main_region_dsm_h static bool dsm_init_done = false; /* Preallocated DSM space in the main shared memory region. */ ++<<<<<<< ours +static void *dsm_main_space_begin = NULL; +static size_t dsm_main_space_size; + +static void dsm_main_space_request(void *arg); +static void dsm_main_space_init(void *arg); + +const ShmemCallbacks dsm_shmem_callbacks = { + .request_fn = dsm_main_space_request, + .init_fn = dsm_main_space_init, +}; ++======= + static void dsm_main_space_init(void *); + + static ShmemStructDesc dsm_main_space_shmem_desc = { + .name = "Preallocated DSM", + .size = 0, /* dynamic */ + .init_fn = dsm_main_space_init, + }; + + #define dsm_main_space_begin (dsm_main_space_shmem_desc.ptr) ++>>>>>>> theirs /* * List of dynamic shared memory segments used by this backend. @@@ -474,39 -472,43 +486,61 @@@ dsm_set_control_handle(dsm_handle h #endif /* - * Reserve some space in the main shared memory segment for DSM segments. + * Reserve space in the main shared memory segment for DSM segments. */ -size_t -dsm_estimate_size(void) +static void +dsm_main_space_request(void *arg) { - return 1024 * 1024 * (size_t) min_dynamic_shared_memory; + dsm_main_space_size = 1024 * 1024 * (size_t) min_dynamic_shared_memory; + + if (dsm_main_space_size == 0) + return; + + ShmemRequestStruct(.name = "Preallocated DSM", + .size = dsm_main_space_size, + .ptr = &dsm_main_space_begin, + ); } -/* - * Initialize space in the main shared memory segment for DSM segments. - */ -void -dsm_shmem_init(void) +static void +dsm_main_space_init(void *arg) { ++<<<<<<< ours + FreePageManager *fpm = (FreePageManager *) dsm_main_space_begin; + size_t first_page = 0; + size_t pages; ++======= + size_t size = dsm_estimate_size(); ++>>>>>>> theirs - if (size == 0) + if (dsm_main_space_size == 0) return; ++<<<<<<< ours ++======= + 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; + ++>>>>>>> theirs /* 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); ++<<<<<<< ours + pages = (dsm_main_space_size / FPM_PAGE_SIZE) - first_page; ++======= + pages = (size / FPM_PAGE_SIZE) - first_page; ++>>>>>>> theirs FreePageManagerPut(fpm, first_page, pages); } diff --cc src/backend/storage/ipc/dsm_registry.c index 2b56977659b,882af83b7b2..00000000000 --- a/src/backend/storage/ipc/dsm_registry.c +++ b/src/backend/storage/ipc/dsm_registry.c @@@ -56,16 -54,16 +56,24 @@@ typedef struct DSMRegistryCtxStruc dshash_table_handle dshh; } DSMRegistryCtxStruct; - static DSMRegistryCtxStruct *DSMRegistryCtx; + static void DSMRegistryCtxShmemInit(void *arg); + + static ShmemStructDesc DSMRegistryCtxShmemDesc = { + .name = "DSM Registry Data", + .size = sizeof(DSMRegistryCtxStruct), + .init_fn = DSMRegistryCtxShmemInit, + }; + + #define DSMRegistryCtx ((DSMRegistryCtxStruct *) DSMRegistryCtxShmemDesc.ptr) +static void DSMRegistryShmemRequest(void *arg); +static void DSMRegistryShmemInit(void *arg); + +const ShmemCallbacks DSMRegistryShmemCallbacks = { + .request_fn = DSMRegistryShmemRequest, + .init_fn = DSMRegistryShmemInit, +}; + typedef struct NamedDSMState { dsm_handle handle; @@@ -123,17 -121,14 +131,28 @@@ static const dshash_parameters dsh_para static dsa_area *dsm_registry_dsa; static dshash_table *dsm_registry_table; ++<<<<<<< ours +static void +DSMRegistryShmemRequest(void *arg) +{ + ShmemRequestStruct(.name = "DSM Registry Data", + .size = sizeof(DSMRegistryCtxStruct), + .ptr = (void **) &DSMRegistryCtx, + ); +} + +static void +DSMRegistryShmemInit(void *arg) ++======= + void + DSMRegistryShmemRegister(void) + { + ShmemRegisterStruct(&DSMRegistryCtxShmemDesc); + } + + static void + DSMRegistryCtxShmemInit(void *) ++>>>>>>> theirs { DSMRegistryCtx->dsah = DSA_HANDLE_INVALID; DSMRegistryCtx->dshh = DSHASH_HANDLE_INVALID; diff --cc src/backend/storage/ipc/ipci.c index bf6b81e621b,952988645d0..00000000000 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@@ -68,7 -98,46 +68,50 @@@ CalculateShmemSize(void * during the actual allocation phase. */ size = 100000; ++<<<<<<< ours + size = add_size(size, ShmemGetRequestedSize()); ++======= + size = add_size(size, hash_estimate_size(SHMEM_INDEX_SIZE, + sizeof(ShmemIndexEnt))); + size = add_size(size, dsm_estimate_size()); + + size = add_size(size, ShmemRegisteredSize()); + + /* legacy subsystmes */ + size = add_size(size, BufferManagerShmemSize()); + size = add_size(size, LockManagerShmemSize()); + size = add_size(size, PredicateLockShmemSize()); + size = add_size(size, XLogPrefetchShmemSize()); + size = add_size(size, XLOGShmemSize()); + size = add_size(size, XLogRecoveryShmemSize()); + size = add_size(size, CLOGShmemSize()); + size = add_size(size, CommitTsShmemSize()); + size = add_size(size, SUBTRANSShmemSize()); + size = add_size(size, TwoPhaseShmemSize()); + size = add_size(size, BackgroundWorkerShmemSize()); + size = add_size(size, MultiXactShmemSize()); + size = add_size(size, LWLockShmemSize()); + size = add_size(size, BackendStatusShmemSize()); + size = add_size(size, CheckpointerShmemSize()); + size = add_size(size, AutoVacuumShmemSize()); + size = add_size(size, ReplicationSlotsShmemSize()); + size = add_size(size, ReplicationOriginShmemSize()); + size = add_size(size, WalSndShmemSize()); + size = add_size(size, WalRcvShmemSize()); + size = add_size(size, WalSummarizerShmemSize()); + size = add_size(size, PgArchShmemSize()); + size = add_size(size, ApplyLauncherShmemSize()); + size = add_size(size, BTreeShmemSize()); + size = add_size(size, SyncScanShmemSize()); + size = add_size(size, AsyncShmemSize()); + size = add_size(size, StatsShmemSize()); + size = add_size(size, WaitEventCustomShmemSize()); + size = add_size(size, InjectionPointShmemSize()); + size = add_size(size, SlotSyncShmemSize()); + size = add_size(size, AioShmemSize()); + size = add_size(size, WaitLSNShmemSize()); + size = add_size(size, LogicalDecodingCtlShmemSize()); ++>>>>>>> theirs /* include additional requested shmem from preload libraries */ size = add_size(size, total_addin_request); @@@ -146,8 -214,12 +189,17 @@@ CreateSharedMemoryAndSemaphores(void */ InitShmemAllocator(seghdr); ++<<<<<<< ours + /* Initialize all shmem areas */ + ShmemInitRequested(); ++======= + /* Reserve space for semaphores. */ + if (!IsUnderPostmaster) + PGReserveSemaphores(ProcGlobalSemas()); + + /* Initialize subsystems */ + CreateOrAttachShmemStructs(); ++>>>>>>> theirs /* Initialize dynamic shared memory facilities. */ dsm_postmaster_startup(shim); @@@ -159,24 -231,116 +211,113 @@@ shmem_startup_hook(); } + void + RegisterShmemStructs(void) + { + DSMRegistryShmemRegister(); + + ProcGlobalShmemRegister(); + VarsupShmemRegister(); + ProcArrayShmemRegister(); + SharedInvalShmemRegister(); + PMSignalShmemRegister(); + ProcSignalShmemRegister(); + } + /* - * Initialize various subsystems, setting up their data structures in - * shared memory. - * - * This is called by the postmaster or by a standalone backend. - * It is also called by a backend forked from the postmaster in the - * EXEC_BACKEND case. In the latter case, the shared memory segment - * already exists and has been physically attached to, but we have to - * initialize pointers in local memory that reference the shared structures, - * because we didn't inherit the correct pointer values from the postmaster - * as we do in the fork() scenario. The easiest way to do that is to run - * through the same code as before. (Note that the called routines mostly - * check IsUnderPostmaster, rather than EXEC_BACKEND, to detect this case. - * This is a bit code-wasteful and could be cleaned up.) + * Early initialization of various subsystems, giving them a chance to + * register their shared memory needs before the shared memory segment is + * allocated. */ -static void -CreateOrAttachShmemStructs(void) +void +RegisterBuiltinShmemCallbacks(void) { /* ++<<<<<<< ours + * Call RegisterShmemCallbacks(...) on each subsystem listed in + * subsystemslist.h ++======= + * 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(); + else + #endif + { + ShmemInitRegistered(); + } + + dsm_shmem_init(); + //DSMRegistryShmemInit(); + + /* + * Set up xlog, clog, and buffers + */ + XLOGShmemInit(); + + XLogPrefetchShmemInit(); + XLogRecoveryShmemInit(); + CLOGShmemInit(); + CommitTsShmemInit(); + SUBTRANSShmemInit(); + MultiXactShmemInit(); + BufferManagerShmemInit(); + + /* + * Set up lock manager ++>>>>>>> theirs */ - LockManagerShmemInit(); +#define PG_SHMEM_SUBSYSTEM(subsystem_callbacks) \ + RegisterShmemCallbacks(&(subsystem_callbacks)); - /* - * Set up predicate lock manager - */ - PredicateLockShmemInit(); +#include "storage/subsystemlist.h" ++<<<<<<< ours +#undef PG_SHMEM_SUBSYSTEM ++======= + /* + * Set up process table + */ + BackendStatusShmemInit(); + TwoPhaseShmemInit(); + BackgroundWorkerShmemInit(); + + /* + * Set up interprocess signaling mechanisms + */ + CheckpointerShmemInit(); + AutoVacuumShmemInit(); + ReplicationSlotsShmemInit(); + ReplicationOriginShmemInit(); + WalSndShmemInit(); + WalRcvShmemInit(); + WalSummarizerShmemInit(); + PgArchShmemInit(); + ApplyLauncherShmemInit(); + SlotSyncShmemInit(); + + /* + * Set up other modules that need some shared memory space + */ + BTreeShmemInit(); + SyncScanShmemInit(); + AsyncShmemInit(); + StatsShmemInit(); + WaitEventCustomShmemInit(); + InjectionPointShmemInit(); + AioShmemInit(); + WaitLSNShmemInit(); + LogicalDecodingCtlShmemInit(); ++>>>>>>> theirs } /* diff --cc src/backend/storage/ipc/pmsignal.c index bdad5fdd043,23752500d16..00000000000 --- a/src/backend/storage/ipc/pmsignal.c +++ b/src/backend/storage/ipc/pmsignal.c @@@ -81,16 -80,23 +81,33 @@@ struct PMSignalDat sig_atomic_t PMChildFlags[FLEXIBLE_ARRAY_MEMBER]; }; - /* PMSignalState pointer is valid in both postmaster and child processes */ + static void PMSignalShmemInit(void *); + + static ShmemStructDesc PMSignalShmemDesc = { + .name = "PMSignalState", + .size = 0, /* dynamic */ + .init_fn = PMSignalShmemInit, + }; + + /* + * PMSignalState pointer is valid in both postmaster and child processes + * + * This is a stand-alone variable rather than just a #define over + * PMSignalShmemDesc.ptr because it is needed early at backend startup and + * passed as a backend parameter in EXEC_BACKEND mode + */ NON_EXEC_STATIC volatile PMSignalData *PMSignalState = NULL; ++<<<<<<< ours +static void PMSignalShmemRequest(void *); +static void PMSignalShmemInit(void *); + +const ShmemCallbacks PMSignalShmemCallbacks = { + .request_fn = PMSignalShmemRequest, + .init_fn = PMSignalShmemInit, +}; ++======= ++>>>>>>> theirs /* * Local copy of PMSignalState->num_child_flags, only valid in the @@@ -132,28 -138,27 +149,50 @@@ postmaster_death_handler(SIGNAL_ARGS static void MarkPostmasterChildInactive(int code, Datum arg); /* ++<<<<<<< ours + * PMSignalShmemRequest - Register pmsignal.c's shared memory needs + */ +static void +PMSignalShmemRequest(void *arg) ++======= + * PMSignalShmemRegister - Register our shared memory + */ + void + PMSignalShmemRegister(void) ++>>>>>>> theirs { - Size size; + size_t size; + ++<<<<<<< ours + num_child_flags = MaxLivePostmasterChildren(); + size = add_size(offsetof(PMSignalData, PMChildFlags), + mul_size(num_child_flags, sizeof(sig_atomic_t))); + ShmemRequestStruct(.name = "PMSignalState", + .size = size, + .ptr = (void **) &PMSignalState, + ); ++======= + size = offsetof(PMSignalData, PMChildFlags); + size = add_size(size, mul_size(MaxLivePostmasterChildren(), + sizeof(sig_atomic_t))); + PMSignalShmemDesc.size = size; + ShmemRegisterStruct(&PMSignalShmemDesc); ++>>>>>>> theirs } static void PMSignalShmemInit(void *arg) { ++<<<<<<< ours + Assert(PMSignalState); + Assert(num_child_flags > 0); ++======= + /* initialize all flags to zeroes */ + PMSignalState = PMSignalShmemDesc.ptr; + MemSet(unvolatize(PMSignalData *, PMSignalState), 0, PMSignalShmemDesc.size); + num_child_flags = MaxLivePostmasterChildren(); ++>>>>>>> theirs PMSignalState->num_child_flags = num_child_flags; } diff --cc src/backend/storage/ipc/procarray.c index 9299bcebbda,08c63bcb2a7..00000000000 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@@ -104,18 -101,18 +104,32 @@@ typedef struct ProcArrayStruc int pgprocnos[FLEXIBLE_ARRAY_MEMBER]; } ProcArrayStruct; ++<<<<<<< ours +static void ProcArrayShmemRequest(void *arg); +static void ProcArrayShmemInit(void *arg); +static void ProcArrayShmemAttach(void *arg); + +static ProcArrayStruct *procArray; + +const struct ShmemCallbacks ProcArrayShmemCallbacks = { + .request_fn = ProcArrayShmemRequest, ++======= + static void ProcArrayShmemInit(void *arg); + static void ProcArrayShmemAttach(void *arg); + + static ShmemStructDesc ProcArrayShmemDesc = { + .name = "Proc Array", + .size = 0, /* dynamic */ ++>>>>>>> theirs .init_fn = ProcArrayShmemInit, .attach_fn = ProcArrayShmemAttach, }; ++<<<<<<< ours ++======= + #define procArray ((ProcArrayStruct *) ProcArrayShmemDesc.ptr) + ++>>>>>>> theirs /* * 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 @@@ -293,9 -290,21 +307,27 @@@ static TransactionId cachedXidIsNotInPr * Bookkeeping for tracking emulated transactions in recovery */ ++<<<<<<< ours +static TransactionId *KnownAssignedXids; + +static bool *KnownAssignedXidsValid; ++======= + static ShmemStructDesc KnownAssignedXidsShmemDesc = { + .name = "KnownAssignedXids", + .size = 0, /* dynamic */ + .init_fn = NULL, + }; + + #define KnownAssignedXids ((TransactionId *) KnownAssignedXidsShmemDesc.ptr) + + static ShmemStructDesc KnownAssignedXidsValidShmemDesc = { + .name = "KnownAssignedXidsValid", + .size = 0, /* dynamic */ + .init_fn = NULL, + }; + + #define KnownAssignedXidsValid ((bool *) KnownAssignedXidsValidShmemDesc.ptr) ++>>>>>>> theirs static TransactionId latestObservedXid = InvalidTransactionId; @@@ -389,11 -398,18 +421,26 @@@ static void GlobalVisUpdateApply(Comput /* * Register the shared PGPROC array during postmaster startup. */ ++<<<<<<< ours +static void +ProcArrayShmemRequest(void *arg) +{ +#define PROCARRAY_MAXPROCS (MaxBackends + max_prepared_xacts) + ++======= + void + ProcArrayShmemRegister(void) + { + #define PROCARRAY_MAXPROCS (MaxBackends + max_prepared_xacts) + + /* Create or attach to the ProcArray shared structure */ + ProcArrayShmemDesc.size = + add_size(offsetof(ProcArrayStruct, pgprocnos), + mul_size(sizeof(int), + PROCARRAY_MAXPROCS)); + ShmemRegisterStruct(&ProcArrayShmemDesc); + ++>>>>>>> theirs /* * During Hot Standby processing we have a data structure called * KnownAssignedXids, created in shared memory. Local data structures are @@@ -412,28 -428,17 +459,42 @@@ if (EnableHotStandby) { ++<<<<<<< ours + ShmemRequestStruct(.name = "KnownAssignedXids", + .size = mul_size(sizeof(TransactionId), TOTAL_MAX_CACHED_SUBXIDS), + .ptr = (void **) &KnownAssignedXids, + ); + + ShmemRequestStruct(.name = "KnownAssignedXidsValid", + .size = mul_size(sizeof(bool), TOTAL_MAX_CACHED_SUBXIDS), + .ptr = (void **) &KnownAssignedXidsValid, + ); + } + + /* Register the ProcArray shared structure */ + ShmemRequestStruct(.name = "Proc Array", + .size = add_size(offsetof(ProcArrayStruct, pgprocnos), + mul_size(sizeof(int), PROCARRAY_MAXPROCS)), + .ptr = (void **) &procArray, + ); +} + +/* + * Initialize the shared PGPROC array during postmaster startup. + */ ++======= + KnownAssignedXidsShmemDesc.size = + mul_size(sizeof(TransactionId), + TOTAL_MAX_CACHED_SUBXIDS); + ShmemRegisterStruct(&KnownAssignedXidsShmemDesc); + + KnownAssignedXidsValidShmemDesc.size = + mul_size(sizeof(bool), TOTAL_MAX_CACHED_SUBXIDS); + ShmemRegisterStruct(&KnownAssignedXidsValidShmemDesc); + } + } + ++>>>>>>> theirs static void ProcArrayShmemInit(void *arg) { diff --cc src/backend/storage/ipc/procsignal.c index 264e4c22ca6,5743f088324..00000000000 --- a/src/backend/storage/ipc/procsignal.c +++ b/src/backend/storage/ipc/procsignal.c @@@ -109,15 -102,15 +109,27 @@@ struct ProcSignalHeade #define BARRIER_CLEAR_BIT(flags, type) \ ((flags) &= ~(((uint32) 1) << (uint32) (type))) ++<<<<<<< ours +static void ProcSignalShmemRequest(void *arg); +static void ProcSignalShmemInit(void *arg); + +const ShmemCallbacks ProcSignalShmemCallbacks = { + .request_fn = ProcSignalShmemRequest, + .init_fn = ProcSignalShmemInit, +}; + +NON_EXEC_STATIC ProcSignalHeader *ProcSignal = NULL; ++======= + static void ProcSignalShmemInit(void *arg); + + static ShmemStructDesc ProcSignalShmemDesc = { + .name = "ProcSignal", + .size = 0, /* dynamic */ + .init_fn = ProcSignalShmemInit, + }; + + #define ProcSignal ((ProcSignalHeader *) ProcSignalShmemDesc.ptr) ++>>>>>>> theirs static ProcSignalSlot *MyProcSignalSlot = NULL; @@@ -126,21 -119,19 +138,34 @@@ static void CleanupProcSignalState(int static void ResetProcSignalBarrierBits(uint32 flags); /* ++<<<<<<< ours + * ProcSignalShmemRequest + * Register ProcSignal's shared memory needs at postmaster startup + */ +static void +ProcSignalShmemRequest(void *arg) ++======= + * ProcSignalShmemRegister + * Register ProcSignal's shared memory needs at postmaster startup + */ + void + ProcSignalShmemRegister(void) ++>>>>>>> theirs { Size size; size = mul_size(NumProcSignalSlots, sizeof(ProcSignalSlot)); size = add_size(size, offsetof(ProcSignalHeader, psh_slot)); ++<<<<<<< ours + ShmemRequestStruct(.name = "ProcSignal", + .size = size, + .ptr = (void **) &ProcSignal, + ); ++======= + ProcSignalShmemDesc.size = size; + ShmemRegisterStruct(&ProcSignalShmemDesc); ++>>>>>>> theirs } static void diff --cc src/backend/storage/ipc/shmem.c index 1ebffe5a32a,faa0fcbd21e..00000000000 --- a/src/backend/storage/ipc/shmem.c +++ b/src/backend/storage/ipc/shmem.c @@@ -19,111 -19,50 +19,158 @@@ * methods). The routines in this file are used for allocating and * binding to shared memory data structures. * ++<<<<<<< ours + * This module provides facilities to allocate fixed-size structures in shared + * memory, for things like variables shared between all backend processes. + * Each such structure has a string name to identify it, specified when it is + * requested. shmem_hash.c provides a shared hash table implementation on top + * of that. + * + * Shared memory areas should usually not be allocated after postmaster + * startup, although we do allow small allocations later for the benefit of + * extension modules that are loaded after startup. Despite that allowance, + * extensions that need shared memory should be added in + * shared_preload_libraries, because the allowance is quite small and there is + * no guarantee that any memory is available after startup. + * + * Nowadays, there is also another way to allocate shared memory called + * Dynamic Shared Memory. See dsm.c for that facility. One big difference + * between traditional shared memory handled by shmem.c and dynamic shared + * memory is that traditional shared memory areas are mapped to the same + * address in all processes, so you can use normal pointers in shared memory + * structs. With Dynamic Shared Memory, you must use offsets or DSA pointers + * instead. + * + * Shared memory managed by shmem.c 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. + * + * Usage + * ----- + * + * To allocate shared memory, you need to register a set of callback functions + * which handle the lifecycle of the allocation. In the request_fn callback, + * call ShmemRequestStruct() with the desired name and size. When the area is + * later allocated or attached to, the global variable pointed to by the .ptr + * option is set to the shared memory location of the allocation. The init_fn + * callback can perform additional initialization. + * + * typedef struct MyShmemData { + * ... + * } MyShmemData; + * + * static MyShmemData *MyShmem; + * + * static void my_shmem_request(void *arg); + * static void my_shmem_init(void *arg); + * + * const ShmemCallbacks MyShmemCallbacks = { + * .request_fn = my_shmem_request, + * .init_fn = my_shmem_init, + * }; + * + * static void + * my_shmem_request(void *arg) + * { + * ShmemRequestStruct(.name = "My shmem area", + * .size = sizeof(MyShmemData), + * .ptr = (void **) &MyShmem, + * ); + * } + * + * In builtin PostgreSQL code, add the callbacks to the list in + * src/include/storage/subsystemlist.h. In an add-in module, you can register + * the callbacks by calling RegisterShmemCallbacks(&MyShmemCallbacks) in the + * extension's _PG_init() function. + * + * Lifecycle + * --------- + * + * Initializing shared memory happens in multiple phases. In the first phase, + * during postmaster startup, all the request_fn callbacks are called. Only + * after all the request_fn callbacks have been called and all the shmem areas + * have been requested by the ShmemRequestStruct() calls we know how much + * shared memory we need in total. After that, postmaster allocates global + * shared memory segment, and calls all the init_fn callbacks to initialize + * all the requested shmem areas. + * + * 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 the shmem_request callbacks to re-establish the + * knowledge about each shared memory area, sets the pointer variables + * (*options->ptr), and calls the attach_fn callback, if any, for additional + * per-backend setup. + * + * Legacy ShmemInitStruct()/ShmemInitHash() functions + * -------------------------------------------------- + * + * ShmemInitStruct()/ShmemInitHash() is another way of registering shmem + * areas. It pre-dates the ShmemRequestStruct()/ShmemRequestHash() 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. Instead, the caller of ShmemInitStruct() must check + * the return status of ShmemInitStruct() and initialize the struct if it was + * not previously initialized. + * + * Calling ShmemAlloc() directly + * ----------------------------- + * + * There's a more low-level way of allocating shared memory too: you can call + * ShmemAlloc() directly. It's used to implement the higher level mechanisms, + * and should generally not be called directly. ++======= + * FIXME: NOTES below are outdated + * + * 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. ++>>>>>>> theirs */ #include "postgres.h" @@@ -140,80 -75,19 +187,90 @@@ #include "storage/lwlock.h" #include "storage/pg_shmem.h" #include "storage/shmem.h" +#include "storage/shmem_internal.h" #include "storage/spin.h" #include "utils/builtins.h" +#include "utils/tuplestore.h" + +/* + * Registered callbacks. + * + * During postmaster startup, we accumulate the callbacks from all subsystems + * in this list. + * + * 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 via fork(). + */ +static List *registered_shmem_callbacks; + +/* + * In the shmem request phase, all the shmem areas requested with the + * ShmemRequest*() functions are accumulated here. + */ +typedef struct +{ + ShmemStructOpts *options; + ShmemRequestKind kind; +} ShmemRequest; + +static List *pending_shmem_requests; + +/* + * Per-process state machine, for sanity checking that we do things in the + * right order. + * + * Postmaster: + * INITIAL -> REQUESTING -> INITIALIZING -> DONE + * + * Backends in EXEC_BACKEND mode: + * INITIAL -> REQUESTING -> ATTACHING -> DONE + * + * Late request: + * DONE -> REQUESTING -> AFTER_STARTUP_ATTACH_OR_INIT -> DONE + */ +enum shmem_request_state +{ + /* Initial state */ + SRS_INITIAL, + + /* + * When we start calling the shmem_request callbacks, we enter the + * SRS_REQUESTING phase. All ShmemRequestStruct calls happen in this + * state. + */ + SRS_REQUESTING, + + /* + * Postmaster has finished all shmem requests, and is now initializing the + * shared memory segment. init_fn callbacks are called in this state. + */ + SRS_INITIALIZING, + + /* + * A postmaster child process is starting up. attach_fn callbacks are + * called in this state. + */ + SRS_ATTACHING, + + /* An after-startup allocation or attachment is in progress */ + SRS_AFTER_STARTUP_ATTACH_OR_INIT, + + /* Normal state after shmem initialization / attachment */ + SRS_DONE, +}; +static enum shmem_request_state shmem_request_state = SRS_INITIAL; + /* 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) + + /* these are in postmaster private memory */ + static ShmemStructDesc *registry[SHMEM_INDEX_SIZE]; + static int num_registrations = 0; + /* * This is the first data structure stored in the shared memory segment, at * the offset that PGShmemHeader->content_offset points to. Allocations by @@@ -225,19 -99,17 +282,22 @@@ typedef struct ShmemAllocatorData { Size free_offset; /* offset to first free space from ShmemBase */ - HTAB *index; /* copy of ShmemIndex */ - /* protects shared memory and LWLock allocation */ + /* protects 'free_offset' */ slock_t shmem_lock; + + HASHHDR *index; /* location of ShmemIndex */ + size_t index_size; /* size of shmem region holding ShmemIndex */ + LWLock index_lock; /* protects ShmemIndex */ } ShmemAllocatorData; -static void *ShmemAllocRaw(Size size, Size *allocated_size); +#define ShmemIndexLock (&ShmemAllocator->index_lock) + +static void *ShmemAllocRaw(Size size, Size alignment, 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 */ @@@ -245,387 -117,138 +305,516 @@@ static void *ShmemBase; /* start addr static void *ShmemEnd; /* end+1 address of shared memory */ static ShmemAllocatorData *ShmemAllocator; ++<<<<<<< ours + +/* + * 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. (This is not a hard limit, the hash + * table can grow larger than that if there is shared memory available) + */ +#define SHMEM_INDEX_ADDITIONAL_SIZE (128) + +/* 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; ++======= + slock_t *ShmemLock; /* points to ShmemAllocator->shmem_lock */ + + + static ShmemHashDesc ShmemIndexHashDesc = { + .name = "ShmemIndex", + .init_size = SHMEM_INDEX_SIZE, + .max_size = SHMEM_INDEX_SIZE, + }; + + /* primary index hashtable for shmem */ + #define ShmemIndex (ShmemIndexHashDesc.ptr) + ++>>>>>>> theirs /* To get reliable results for NUMA inquiry we need to "touch pages" once */ static bool firstNumaTouch = true; +static void CallShmemCallbacksAfterStartup(const ShmemCallbacks *callbacks); +static void InitShmemIndexEntry(ShmemRequest *request); +static bool AttachShmemIndexEntry(ShmemRequest *request, bool missing_ok); + Datum pg_numa_available(PG_FUNCTION_ARGS); + + void + ShmemRegisterStruct(ShmemStructDesc *desc) + { + elog(DEBUG2, "REGISTER: %s with size %zd", desc->name, desc->size); + + registry[num_registrations++] = desc; + } + + size_t + ShmemRegisteredSize(void) + { + size_t size; + + size = 0; + for (int i = 0; i < num_registrations; i++) + { + size = add_size(size, registry[i]->size); + size = add_size(size, registry[i]->extra_size); + } + + elog(DEBUG2, "SIZE: total %zd", size); + + return size; + } + + void + ShmemInitRegistered(void) + { + /* Should be called only by the postmaster or a standalone backend. */ + Assert(!IsUnderPostmaster); + + for (int i = 0; i < num_registrations; i++) + { + size_t allocated_size; + void *structPtr; + bool found; + ShmemIndexEnt *result; + + elog(DEBUG2, "INIT [%d/%d]: %s", i, num_registrations, registry[i]->name); + + /* look it up in the shmem index */ + result = (ShmemIndexEnt *) + hash_search(ShmemIndex, registry[i]->name, HASH_ENTER_NULL, &found); + if (!result) + { + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("could not create ShmemIndex entry for data structure \"%s\"", + registry[i]->name))); + } + if (found) + elog(ERROR, "shmem struct \"%s\" is already initialized", registry[i]->name); + + /* allocate and initialize it */ + structPtr = ShmemAllocRaw(registry[i]->size, &allocated_size); + if (structPtr == NULL) + { + /* out of memory; remove the failed ShmemIndex entry */ + hash_search(ShmemIndex, registry[i]->name, HASH_REMOVE, NULL); + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("not enough shared memory for data structure" + " \"%s\" (%zu bytes requested)", + registry[i]->name, registry[i]->size))); + } + result->size = registry[i]->size; + result->allocated_size = allocated_size; + result->location = structPtr; + + registry[i]->ptr = structPtr; + if (registry[i]->init_fn) + registry[i]->init_fn(registry[i]->init_fn_arg); + } + } + + #ifdef EXEC_BACKEND + void + ShmemAttachRegistered(void) + { + /* Must be initializing a (non-standalone) backend */ + Assert(IsUnderPostmaster); + Assert(ShmemAllocator->index != NULL); + + LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE); + + for (int i = 0; i < num_registrations; i++) + { + bool found; + ShmemIndexEnt *result; + + elog(LOG, "ATTACH [%d/%d]: %s", i, num_registrations, registry[i]->name); + + /* look it up in the shmem index */ + result = (ShmemIndexEnt *) + hash_search(ShmemIndex, registry[i]->name, HASH_FIND, &found); + if (!found) + { + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("could not find ShmemIndex entry for data structure \"%s\"", + registry[i]->name))); + } + + registry[i]->ptr = result->location; + + if (registry[i]->attach_fn) + registry[i]->attach_fn(registry[i]->attach_fn_arg); + } + + LWLockRelease(ShmemIndexLock); + } + #endif + +/* + * ShmemRequestStruct() --- request a named shared memory area + * + * Subsystems call this to register their shared memory needs. This is + * usually 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. + * + * This does not yet allocate the memory, but merely registers the need for + * it. The actual allocation happens later in the postmaster startup + * sequence. + * + * This must be called from a shmem_request callback function, registered with + * RegisterShmemCallbacks(). This enforces a coding pattern that works the + * same in normal Unix systems and with EXEC_BACKEND. On Unix systems, the + * shmem_request callbacks are called once, early in postmaster startup, and + * the child processes inherit the struct descriptors and any other + * per-process state from the postmaster. In EXEC_BACKEND mode, shmem_request + * callbacks are *also* called in each backend, at backend startup, to + * re-establish the struct descriptors. By calling the same function in both + * cases, we ensure that all the shmem areas are registered the same way in + * all processes. + * + * 'options' defines the name and size of the area, and any other optional + * features. Leave unused options as zeros. The options are copied to + * longer-lived memory, so it doesn't need to live after the + * ShmemRequestStruct() call and can point to a local variable in the calling + * function. The 'name' must point to a long-lived string though, only the + * pointer to it is copied. + */ +void +ShmemRequestStructWithOpts(const ShmemStructOpts *options) +{ + ShmemStructOpts *options_copy; + + options_copy = MemoryContextAlloc(TopMemoryContext, + sizeof(ShmemStructOpts)); + memcpy(options_copy, options, sizeof(ShmemStructOpts)); + + ShmemRequestInternal(options_copy, SHMEM_KIND_STRUCT); +} + +/* + * Internal workhorse of ShmemRequestStruct() and ShmemRequestHash(). + * + * Note: Unlike in the public ShmemRequestStruct() and ShmemRequestHash() + * functions, 'options' is *not* copied. It must be allocated in + * TopMemoryContext by the caller, and will be freed after the init/attach + * callbacks have been called. This allows ShmemRequestHash() to pass a + * pointer to the extended ShmemHashOpts struct instead. + */ +void +ShmemRequestInternal(ShmemStructOpts *options, ShmemRequestKind kind) +{ + ShmemRequest *request; + + /* Check the options */ + if (options->name == NULL) + elog(ERROR, "shared memory request is missing 'name' option"); + + if (IsUnderPostmaster) + { + if (options->size <= 0 && options->size != SHMEM_ATTACH_UNKNOWN_SIZE) + elog(ERROR, "invalid size %zd for shared memory request for \"%s\"", + options->size, options->name); + } + else + { + if (options->size == SHMEM_ATTACH_UNKNOWN_SIZE) + elog(ERROR, "SHMEM_ATTACH_UNKNOWN_SIZE cannot be used during startup"); + if (options->size <= 0) + elog(ERROR, "invalid size %zd for shared memory request for \"%s\"", + options->size, options->name); + } + + if (options->alignment != 0 && pg_nextpower2_size_t(options->alignment) != options->alignment) + elog(ERROR, "invalid alignment %zu for shared memory request for \"%s\"", + options->alignment, options->name); + + /* Check that we're in the right state */ + if (shmem_request_state != SRS_REQUESTING) + elog(ERROR, "ShmemRequestStruct can only be called from a shmem_request callback"); + + /* Check that it's not already registered in this process */ + foreach_ptr(ShmemRequest, existing, pending_shmem_requests) + { + if (strcmp(existing->options->name, options->name) == 0) + ereport(ERROR, + (errmsg("shared memory struct \"%s\" is already registered", + options->name))); + } + + /* Request looks valid, remember it */ + request = palloc(sizeof(ShmemRequest)); + request->options = options; + request->kind = kind; + pending_shmem_requests = lappend(pending_shmem_requests, request); +} + +/* + * ShmemGetRequestedSize() --- estimate the total size of all registered shared + * memory structures. + * + * This is called at postmaster startup, before the shared memory segment has + * been created. + */ +size_t +ShmemGetRequestedSize(void) +{ + size_t size; + + /* memory needed for the ShmemIndex */ + size = hash_estimate_size(list_length(pending_shmem_requests) + SHMEM_INDEX_ADDITIONAL_SIZE, + sizeof(ShmemIndexEnt)); + size = CACHELINEALIGN(size); + + /* memory needed for all the requested areas */ + foreach_ptr(ShmemRequest, request, pending_shmem_requests) + { + size_t alignment = request->options->alignment; + + /* pad the start address for alignment like ShmemAllocRaw() does */ + if (alignment < PG_CACHE_LINE_SIZE) + alignment = PG_CACHE_LINE_SIZE; + size = TYPEALIGN(alignment, size); + + size = add_size(size, request->options->size); + } + + return size; +} + +/* + * ShmemInitRequested() --- allocate and initialize requested shared memory + * structures. + * + * This is called once at postmaster startup, after the shared memory segment + * has been created. + */ +void +ShmemInitRequested(void) +{ + /* should be called only by the postmaster or a standalone backend */ + Assert(!IsUnderPostmaster); + Assert(shmem_request_state == SRS_INITIALIZING); + + /* + * Initialize the ShmemIndex entries and perform basic initialization of + * all the requested memory areas. There are no concurrent processes yet, + * so no need for locking. + */ + foreach_ptr(ShmemRequest, request, pending_shmem_requests) + { + InitShmemIndexEntry(request); + pfree(request->options); + } + list_free_deep(pending_shmem_requests); + pending_shmem_requests = NIL; + + /* + * Call the subsystem-specific init callbacks to finish initialization of + * all the areas. + */ + foreach_ptr(const ShmemCallbacks, callbacks, registered_shmem_callbacks) + { + if (callbacks->init_fn) + callbacks->init_fn(callbacks->opaque_arg); + } + + shmem_request_state = SRS_DONE; +} + +/* + * Re-establish process private state related to shmem areas. + * + * This is called at backend startup in EXEC_BACKEND mode, in every backend. + */ +#ifdef EXEC_BACKEND +void +ShmemAttachRequested(void) +{ + ListCell *lc; + + /* Must be initializing a (non-standalone) backend */ + Assert(IsUnderPostmaster); + Assert(ShmemAllocator->index != NULL); + Assert(shmem_request_state == SRS_REQUESTING); + shmem_request_state = SRS_ATTACHING; + + LWLockAcquire(ShmemIndexLock, LW_SHARED); + + /* + * Attach to all the requested memory areas. + */ + foreach_ptr(ShmemRequest, request, pending_shmem_requests) + { + AttachShmemIndexEntry(request, false); + pfree(request->options); + } + list_free_deep(pending_shmem_requests); + pending_shmem_requests = NIL; + + /* Call attach callbacks */ + foreach(lc, registered_shmem_callbacks) + { + const ShmemCallbacks *callbacks = (const ShmemCallbacks *) lfirst(lc); + + if (callbacks->attach_fn) + callbacks->attach_fn(callbacks->opaque_arg); + } + + LWLockRelease(ShmemIndexLock); + + shmem_request_state = SRS_DONE; +} +#endif + +/* + * Insert requested shmem area into the shared memory index and initialize it. + * + * Note that this only does performs basic initialization depending on + * ShmemRequestKind, like setting the global pointer variable to the area for + * SHMEM_KIND_STRUCT or setting up the backend-private HTAB control struct. + * This does *not* call the subsystem-specific init callbacks. That's done + * later after all the shmem areas have been initialized or attached to. + */ +static void +InitShmemIndexEntry(ShmemRequest *request) +{ + const char *name = request->options->name; + ShmemIndexEnt *index_entry; + bool found; + size_t allocated_size; + void *structPtr; + + /* look it up in the shmem index */ + index_entry = (ShmemIndexEnt *) + hash_search(ShmemIndex, name, HASH_ENTER_NULL, &found); + if (found) + elog(ERROR, "shared memory struct \"%s\" is already initialized", name); + if (!index_entry) + { + /* tried to add it to the hash table, but there was no space */ + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("could not create ShmemIndex entry for data structure \"%s\"", + name))); + } + + /* + * We inserted the entry to the shared memory index. Allocate requested + * amount of shared memory for it, and initialize the index entry. + */ + structPtr = ShmemAllocRaw(request->options->size, + request->options->alignment, + &allocated_size); + if (structPtr == NULL) + { + /* out of memory; remove the failed ShmemIndex entry */ + hash_search(ShmemIndex, name, HASH_REMOVE, NULL); + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("not enough shared memory for data structure" + " \"%s\" (%zu bytes requested)", + name, request->options->size))); + } + index_entry->size = request->options->size; + index_entry->allocated_size = allocated_size; + index_entry->location = structPtr; + + /* Initialize depending on the kind of shmem area it is */ + switch (request->kind) + { + case SHMEM_KIND_STRUCT: + if (request->options->ptr) + *(request->options->ptr) = index_entry->location; + break; + case SHMEM_KIND_HASH: + shmem_hash_init(structPtr, request->options); + break; + case SHMEM_KIND_SLRU: + shmem_slru_init(structPtr, request->options); + break; + } +} + +/* + * Look up a named shmem area in the shared memory index and attach to it. + * + * Note that this only performs the basic attachment actions depending on + * ShmemRequestKind, like setting the global pointer variable to the area for + * SHMEM_KIND_STRUCT or setting up the backend-private HTAB control struct. + * This does *not* call the subsystem-specific attach callbacks. That's done + * later after all the shmem areas have been initialized or attached to. + */ +static bool +AttachShmemIndexEntry(ShmemRequest *request, bool missing_ok) +{ + const char *name = request->options->name; + ShmemIndexEnt *index_entry; + + /* Look it up in the shmem index */ + index_entry = (ShmemIndexEnt *) + hash_search(ShmemIndex, name, HASH_FIND, NULL); + if (!index_entry) + { + if (!missing_ok) + ereport(ERROR, + (errmsg("could not find ShmemIndex entry for data structure \"%s\"", + request->options->name))); + return false; + } + + /* Check that the size in the index matches the request */ + if (index_entry->size != request->options->size && + request->options->size != SHMEM_ATTACH_UNKNOWN_SIZE) + { + ereport(ERROR, + (errmsg("shared memory struct \"%s\" was created with" + " different size: existing %zu, requested %zu", + name, index_entry->size, request->options->size))); + } + + /* + * Re-establish the caller's pointer variable, or do other actions to + * attach depending on the kind of shmem area it is. + */ + switch (request->kind) + { + case SHMEM_KIND_STRUCT: + if (request->options->ptr) + *(request->options->ptr) = index_entry->location; + break; + case SHMEM_KIND_HASH: + shmem_hash_attach(index_entry->location, request->options); + break; + case SHMEM_KIND_SLRU: + shmem_slru_attach(index_entry->location, request->options); + break; + } + + return true; +} + /* * InitShmemAllocator() --- set up basic pointers to shared memory. * @@@ -893,103 -432,161 +1082,204 @@@ RegisterShmemCallbacks(const ShmemCallb } /* - * 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: 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. + * Register a shmem area (or multiple areas) after startup. */ ++<<<<<<< ours +static void +CallShmemCallbacksAfterStartup(const ShmemCallbacks *callbacks) ++======= + void + ShmemRegisterHash(ShmemHashDesc *desc, /* configuration */ + HASHCTL *infoP, /* info about key and bucket size */ + int hash_flags) /* info about infoP */ + { + /* + * 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 + * make it big enough to start with. + * + * The shared memory allocator must be specified too. + */ + 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 */ + 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; + + 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)); + + ShmemRegisterStruct(&desc->base_desc); + } + + static void + shmem_hash_init(void *arg) + { + ShmemHashDesc *desc = (ShmemHashDesc *) arg; + int hash_flags = desc->hash_flags; + + /* Pass location of hashtable header to hash_create */ + desc->ptr = desc->base_desc.ptr; + desc->infoP->hctl = (HASHHDR *) desc->ptr; + + desc->ptr = hash_create(desc->name, desc->init_size, desc->infoP, hash_flags); + } + + static void + shmem_hash_attach(void *arg) + { + ShmemHashDesc *desc = (ShmemHashDesc *) arg; + int hash_flags = desc->hash_flags; + + /* + * if it already exists, attach to it rather than allocate and initialize + * new space + */ + hash_flags |= HASH_ATTACH; + + /* Pass location of hashtable header to hash_create */ + desc->infoP->hctl = (HASHHDR *) desc->ptr; + + desc->ptr = hash_create(desc->name, desc->init_size, desc->infoP, hash_flags); + } + + /* + * 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: 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 */ ++>>>>>>> theirs { - bool found; - void *location; + bool found_any; + bool notfound_any; + + Assert(shmem_request_state == SRS_DONE); + shmem_request_state = SRS_REQUESTING; /* - * 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 - * make it big enough to start with. - * - * The shared memory allocator must be specified too. + * Call the request callback first. The callback makes ShmemRequest*() + * calls for each shmem area, adding them to pending_shmem_requests. */ - infoP->dsize = infoP->max_dsize = hash_select_dirsize(max_size); - infoP->alloc = ShmemAllocNoError; - hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE; + Assert(pending_shmem_requests == NIL); + if (callbacks->request_fn) + callbacks->request_fn(callbacks->opaque_arg); + shmem_request_state = SRS_AFTER_STARTUP_ATTACH_OR_INIT; - /* look it up in the shmem index */ - location = ShmemInitStruct(name, - hash_get_shared_size(infoP, hash_flags), - &found); + if (pending_shmem_requests == NIL) + { + shmem_request_state = SRS_DONE; + return; + } + + /* Hold ShmemIndexLock while we allocate all the shmem entries */ + LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE); /* - * if it already exists, attach to it rather than allocate and initialize - * new space + * Check if the requested shared memory areas have already been + * initialized. We assume all the areas requested by the request callback + * to form a coherent unit such that they're all already initialized or + * none. Otherwise it would be ambiguous which callback, init or attach, + * to callback afterwards. */ - if (found) - hash_flags |= HASH_ATTACH; + found_any = notfound_any = false; + foreach_ptr(ShmemRequest, request, pending_shmem_requests) + { + if (hash_search(ShmemIndex, request->options->name, HASH_FIND, NULL)) + found_any = true; + else + notfound_any = true; + } + if (found_any && notfound_any) + elog(ERROR, "found some but not all"); - /* Pass location of hashtable header to hash_create */ - infoP->hctl = (HASHHDR *) location; + /* + * Allocate or attach all the shmem areas requested by the request_fn + * callback. + */ + foreach_ptr(ShmemRequest, request, pending_shmem_requests) + { + if (found_any) + AttachShmemIndexEntry(request, false); + else + InitShmemIndexEntry(request); + + pfree(request->options); + } + list_free_deep(pending_shmem_requests); + pending_shmem_requests = NIL; + + /* Finish by calling the appropriate subsystem-specific callback */ + if (found_any) + { + if (callbacks->attach_fn) + callbacks->attach_fn(callbacks->opaque_arg); + } + else + { + if (callbacks->init_fn) + callbacks->init_fn(callbacks->opaque_arg); + } + + LWLockRelease(ShmemIndexLock); + shmem_request_state = SRS_DONE; +} + +/* + * Call all shmem request callbacks. + */ +void +ShmemCallRequestCallbacks(void) +{ + ListCell *lc; + + Assert(shmem_request_state == SRS_INITIAL); + shmem_request_state = SRS_REQUESTING; + + foreach(lc, registered_shmem_callbacks) + { + const ShmemCallbacks *callbacks = (const ShmemCallbacks *) lfirst(lc); - return hash_create(name, init_size, infoP, hash_flags); + if (callbacks->request_fn) + callbacks->request_fn(callbacks->opaque_arg); + } } /* diff --cc src/backend/storage/ipc/sinvaladt.c index 37a21ffaf1a,0fe0f256971..00000000000 --- a/src/backend/storage/ipc/sinvaladt.c +++ b/src/backend/storage/ipc/sinvaladt.c @@@ -204,16 -203,17 +204,25 @@@ typedef struct SISe */ #define NumProcStateSlots (MaxBackends + NUM_AUXILIARY_PROCS) - static SISeg *shmInvalBuffer; /* pointer to the shared inval buffer */ + static void SharedInvalShmemInit(void *arg); + + static ShmemStructDesc SharedInvalShmemDesc = { + .name = "shmInvalBuffer", + .size = 0, /* dynamic */ + .init_fn = SharedInvalShmemInit, + }; + + /* pointer to the shared inval buffer */ + #define shmInvalBuffer ((SISeg *) SharedInvalShmemDesc.ptr) +static void SharedInvalShmemRequest(void *arg); +static void SharedInvalShmemInit(void *arg); + +const ShmemCallbacks SharedInvalShmemCallbacks = { + .request_fn = SharedInvalShmemRequest, + .init_fn = SharedInvalShmemInit, +}; + static LocalTransactionId nextLocalTransactionId; @@@ -221,11 -221,11 +230,19 @@@ static void CleanupInvalidationState(in /* ++<<<<<<< ours + * SharedInvalShmemRequest + * Register shared memory needs for the SI message buffer + */ +static void +SharedInvalShmemRequest(void *arg) ++======= + * SharedInvalShmemRegister + * Register shared memory needs for the SI message buffer + */ + void + SharedInvalShmemRegister(void) ++>>>>>>> theirs { Size size; @@@ -233,10 -233,9 +250,16 @@@ size = add_size(size, mul_size(sizeof(ProcState), NumProcStateSlots)); /* procState */ size = add_size(size, mul_size(sizeof(int), NumProcStateSlots)); /* pgprocnos */ ++<<<<<<< ours + ShmemRequestStruct(.name = "shmInvalBuffer", + .size = size, + .ptr = (void **) &shmInvalBuffer, + ); ++======= + /* Allocate space in shared memory */ + SharedInvalShmemDesc.size = size; + ShmemRegisterStruct(&SharedInvalShmemDesc); ++>>>>>>> theirs } static void @@@ -244,7 -243,7 +267,11 @@@ SharedInvalShmemInit(void *arg { int i; ++<<<<<<< ours + /* Clear message counters, init spinlock */ ++======= + /* Clear message counters, save size of procState array FIXME, init spinlock */ ++>>>>>>> theirs shmInvalBuffer->minMsgNum = 0; shmInvalBuffer->maxMsgNum = 0; shmInvalBuffer->nextThreshold = CLEANUP_MIN; diff --cc src/backend/storage/lmgr/proc.c index 1ac25068d62,85375b5195e..00000000000 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@@ -70,24 -66,41 +70,60 @@@ bool log_lock_waits = true /* Pointer to this process's PGPROC struct, if any */ PGPROC *MyProc = NULL; ++<<<<<<< ours ++======= + /* + * This spinlock protects the freelist of recycled PGPROC structures. + * We cannot use an LWLock because the LWLock manager depends on already + * having a PGPROC and a wait semaphore! But these structures are touched + * relatively infrequently (only at backend startup or shutdown) and not for + * very long, so a spinlock is okay. + */ + #define ProcStructLock (&ProcGlobal->freeProcsLock) + + static void ProcGlobalShmemInit(void *arg); + + static ShmemStructDesc ProcGlobalShmemDesc = { + .name = "Proc Header", + .size = sizeof(PROC_HDR), + .init_fn = ProcGlobalShmemInit, + }; + + static ShmemStructDesc ProcGlobalAllProcsShmemDesc = { + .name = "PGPROC structures", + .size = 0, /* dynamic */ + }; + + static ShmemStructDesc FastPathLockArrayShmemDesc = { + .name = "Fast-Path Lock Array", + .size = 0, /* dynamic */ + }; + ++>>>>>>> theirs /* Pointers to shared-memory structures */ PROC_HDR *ProcGlobal = NULL; +static void *AllProcsShmemPtr; +static void *FastPathLockArrayShmemPtr; NON_EXEC_STATIC PGPROC *AuxiliaryProcs = NULL; PGPROC *PreparedXactProcs = NULL; ++<<<<<<< ours +static void ProcGlobalShmemRequest(void *arg); +static void ProcGlobalShmemInit(void *arg); + +const ShmemCallbacks ProcGlobalShmemCallbacks = { + .request_fn = ProcGlobalShmemRequest, + .init_fn = ProcGlobalShmemInit, +}; + +static uint32 TotalProcs; +static size_t ProcGlobalAllProcsShmemSize; +static size_t FastPathLockArrayShmemSize; ++======= + static uint32 TotalProcs; + + static DeadLockState deadlock_state = DS_NOT_YET_CHECKED; ++>>>>>>> theirs /* Is a deadlock check pending? */ static volatile sig_atomic_t got_deadlock_timeout; @@@ -99,10 -112,10 +135,17 @@@ static DeadLockState CheckDeadLock(void /* ++<<<<<<< ours + * Calculate shared-memory space needed by Fast-Path locks. + */ +static Size +CalculateFastPathLockShmemSize(void) ++======= + * Report shared-memory space needed by Fast-Path locks. + */ + static Size + FastPathLockShmemSize(void) ++>>>>>>> theirs { Size size = 0; Size fpLockBitsSize, @@@ -117,14 -130,11 +160,21 @@@ size = add_size(size, mul_size(TotalProcs, (fpLockBitsSize + fpRelIdSize))); ++<<<<<<< ours + Assert(TotalProcs > 0); + Assert(size > 0); + ++======= ++>>>>>>> theirs return size; } /* ++<<<<<<< ours + * Report number of semaphores needed by ProcGlobalShmemInit. ++======= + * Report number of semaphores needed by InitProcGlobal. ++>>>>>>> theirs */ int ProcGlobalSemas(void) @@@ -216,7 -166,48 +266,52 @@@ ProcGlobalShmemRequest(void *arg * Another reason for creating semaphores here is that the semaphore * implementation typically requires us to create semaphores in the * postmaster, not in backends. ++<<<<<<< ours ++ */ ++======= + * + * 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 + 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); + } + ++>>>>>>> theirs static void ProcGlobalShmemInit(void *arg) { @@@ -225,16 -216,15 +320,25 @@@ PGPROC *procs; int i, j; ++<<<<<<< ours + ++======= ++>>>>>>> theirs /* Used for setup of per-backend fast-path slots. */ char *fpPtr, *fpEndPtr PG_USED_FOR_ASSERTS_ONLY; Size fpLockBitsSize, fpRelIdSize; ++<<<<<<< ours + + Assert(ProcGlobal); ++======= + + ProcGlobal = ProcGlobalShmemDesc.ptr; + ++>>>>>>> theirs ProcGlobal->spins_per_delay = DEFAULT_SPINS_PER_DELAY; + SpinLockInit(&ProcGlobal->freeProcsLock); dlist_init(&ProcGlobal->freeProcs); dlist_init(&ProcGlobal->autovacFreeProcs); dlist_init(&ProcGlobal->bgworkerFreeProcs); @@@ -244,12 -234,12 +348,19 @@@ ProcGlobal->checkpointerProc = INVALID_PROC_NUMBER; pg_atomic_init_u32(&ProcGlobal->procArrayGroupFirst, INVALID_PROC_NUMBER); pg_atomic_init_u32(&ProcGlobal->clogGroupFirst, INVALID_PROC_NUMBER); + SpinLockInit(ProcStructLock); ++<<<<<<< ours + ptr = AllProcsShmemPtr; + requestSize = ProcGlobalAllProcsShmemSize; + MemSet(ptr, 0, requestSize); ++======= + ptr = ProcGlobalAllProcsShmemDesc.ptr; + requestSize = ProcGlobalAllProcsShmemDesc.size; + memset(ptr, 0, requestSize); ++>>>>>>> theirs + /* Carve out the allProcs array from the shared memory area */ procs = (PGPROC *) ptr; ptr = ptr + TotalProcs * sizeof(PGPROC); @@@ -284,16 -274,13 +395,24 @@@ fpLockBitsSize = MAXALIGN(FastPathLockGroupsPerBackend * sizeof(uint64)); fpRelIdSize = MAXALIGN(FastPathLockSlotsPerBackend() * sizeof(Oid)); ++<<<<<<< ours + fpPtr = FastPathLockArrayShmemPtr; + requestSize = FastPathLockArrayShmemSize; ++======= + fpPtr = FastPathLockArrayShmemDesc.ptr; + requestSize = FastPathLockArrayShmemDesc.size; ++>>>>>>> theirs memset(fpPtr, 0, requestSize); /* For asserts checking we did not overflow. */ fpEndPtr = fpPtr + requestSize; ++<<<<<<< ours + /* Initialize semaphores */ + PGSemaphoreInit(ProcGlobalSemas()); + ++======= ++>>>>>>> theirs for (i = 0; i < TotalProcs; i++) { PGPROC *proc = &procs[i]; @@@ -382,7 -369,7 +501,11 @@@ * processes and prepared transactions. */ AuxiliaryProcs = &procs[MaxBackends]; ++<<<<<<< ours + PreparedXactProcs = &procs[FIRST_PREPARED_XACT_PROC_NUMBER]; ++======= + PreparedXactProcs = &procs[MaxBackends + NUM_AUXILIARY_PROCS]; ++>>>>>>> theirs } /* diff --cc src/include/access/transam.h index 55a4ab26b34,49d476e9d5c..00000000000 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@@ -345,6 -350,7 +350,10 @@@ extern TransactionId TransactionIdLates extern XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid); /* in transam/varsup.c */ ++<<<<<<< ours ++======= + extern void VarsupShmemRegister(void); ++>>>>>>> theirs extern FullTransactionId GetNewTransactionId(bool isSubXact); extern void AdvanceNextFullTransactionIdPastXid(TransactionId xid); extern FullTransactionId ReadNextFullTransactionId(void); diff --cc src/include/storage/dsm_registry.h index a2269c89f01,9a1b4d982af..00000000000 --- a/src/include/storage/dsm_registry.h +++ b/src/include/storage/dsm_registry.h @@@ -22,5 -22,6 +22,9 @@@ extern dsa_area *GetNamedDSA(const cha extern dshash_table *GetNamedDSHash(const char *name, const dshash_parameters *params, bool *found); ++<<<<<<< ours ++======= + extern void DSMRegistryShmemRegister(void); ++>>>>>>> theirs #endif /* DSM_REGISTRY_H */ diff --cc src/include/storage/ipc.h index b205b00e7a1,8a3b71ad5d3..00000000000 --- a/src/include/storage/ipc.h +++ b/src/include/storage/ipc.h @@@ -77,7 -77,7 +77,11 @@@ extern void check_on_shmem_exit_lists_a /* ipci.c */ extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook; ++<<<<<<< ours +extern void RegisterBuiltinShmemCallbacks(void); ++======= + extern void RegisterShmemStructs(void); ++>>>>>>> theirs extern Size CalculateShmemSize(void); extern void CreateSharedMemoryAndSemaphores(void); #ifdef EXEC_BACKEND diff --cc src/include/storage/pmsignal.h index bcce4011790,7cdc4852334..00000000000 --- a/src/include/storage/pmsignal.h +++ b/src/include/storage/pmsignal.h @@@ -67,6 -66,7 +67,10 @@@ extern PGDLLIMPORT volatile PMSignalDat /* * prototypes for functions in pmsignal.c */ ++<<<<<<< ours ++======= + extern void PMSignalShmemRegister(void); ++>>>>>>> theirs extern void SendPostmasterSignal(PMSignalReason reason); extern bool CheckPostmasterSignal(PMSignalReason reason); extern void SetQuitSignalReason(QuitSignalReason reason); diff --cc src/include/storage/proc.h index 3e1d1fad5f9,37023e1a93f..00000000000 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@@ -482,6 -419,8 +482,11 @@@ typedef struct PROC_HD /* Head of list of walsender free PGPROC structures */ dlist_head walsenderFreeProcs; ++<<<<<<< ours ++======= + slock_t freeProcsLock; + ++>>>>>>> theirs /* First pgproc waiting for group XID clear */ pg_atomic_uint32 procArrayGroupFirst; /* First pgproc waiting for group transaction status update */ @@@ -552,6 -491,8 +557,11 @@@ extern PGDLLIMPORT PGPROC *AuxiliaryPro * Function Prototypes */ extern int ProcGlobalSemas(void); ++<<<<<<< ours ++======= + extern void ProcGlobalShmemRegister(void); + extern void InitProcGlobal(void); ++>>>>>>> theirs extern void InitProcess(void); extern void InitProcessPhase2(void); extern void InitAuxiliaryProcess(void); diff --cc src/include/storage/procarray.h index ec89c448220,41753c3a630..00000000000 --- a/src/include/storage/procarray.h +++ b/src/include/storage/procarray.h @@@ -19,6 -20,7 +19,10 @@@ #include "utils/snapshot.h" ++<<<<<<< ours ++======= + extern void ProcArrayShmemRegister(void); ++>>>>>>> theirs extern void ProcArrayAdd(PGPROC *proc); extern void ProcArrayRemove(PGPROC *proc, TransactionId latestXid); diff --cc src/include/storage/procsignal.h index aaa158bfd66,f2df1f30c5f..00000000000 --- a/src/include/storage/procsignal.h +++ b/src/include/storage/procsignal.h @@@ -69,6 -71,8 +69,11 @@@ typedef enu /* * prototypes for functions in procsignal.c */ ++<<<<<<< ours ++======= + extern void ProcSignalShmemRegister(void); + ++>>>>>>> theirs extern void ProcSignalInit(const uint8 *cancel_key, int cancel_key_len); extern int SendProcSignal(pid_t pid, ProcSignalReason reason, ProcNumber procNumber); diff --cc src/include/storage/shmem.h index af7fe893bc4,40e2fc17056..00000000000 --- a/src/include/storage/shmem.h +++ b/src/include/storage/shmem.h @@@ -26,168 -21,78 +26,234 @@@ #ifndef SHMEM_H #define SHMEM_H -#include "storage/spin.h" #include "utils/hsearch.h" ++<<<<<<< ours +/* + * Options for ShmemRequestStruct() + * + * 'name' and 'size' are required. Initialize any optional fields that you + * don't use to zeros. + * + * After registration, the shmem machinery reserves memory for the area, sets + * '*ptr' to point to the allocation, and calls the callbacks at the right + * moments. + */ +typedef struct ShmemStructOpts +{ + const char *name; ++======= + typedef void (*ShmemInitCallback) (void *arg); + typedef void (*ShmemAttachCallback) (void *arg); + + /* + * Descriptor for a named area or struct in shared memory + */ + typedef struct ShmemStructDesc + { + /* Name of the shared memory area. Must be unique across the system */ + const char *name; + + size_t size; + + size_t alignment; + ShmemInitCallback init_fn; + ShmemInitCallback attach_fn; + void *init_fn_arg; + void *attach_fn_arg; + + /* + * Extra space to allocated in 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; + + /* Pointer to the shared memory area, when it's allocated. */ + void *ptr; + } ShmemStructDesc; + + /* + * Descriptor for shared memory hash table + */ + typedef struct ShmemHashDesc + { + const char *name; + + int hash_flags; + + size_t init_size; /* initial number of entries */ + size_t max_size; /* max number of entries */ + HASHCTL *infoP; + + HTAB *ptr; + + ShmemStructDesc base_desc; + } ShmemHashDesc; ++>>>>>>> theirs + + /* + * Requested size of the shmem allocation. + * + * When attaching to an existing allocation, the size must match the size + * given when the shmem region was allocated. This cross-check can be + * disabled specifying SHMEM_ATTACH_UNKNOWN_SIZE. + */ + ssize_t size; + + /* + * Alignment of the starting address. If not set, defaults to cacheline + * boundary. Must be a power of two. + */ + size_t alignment; + + /* + * 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; +} ShmemStructOpts; + +#define SHMEM_ATTACH_UNKNOWN_SIZE (-1) + +/* + * Options for ShmemRequestHash() + * + * Each hash table is backed by a contiguous shmem area. + */ +typedef struct ShmemHashOpts +{ + /* Options for allocating the underlying shmem area; do not touch directly */ + ShmemStructOpts base; + + /* + * Name of the shared memory area. Required. Must be unique across the + * system. + */ + const char *name; + + /* + * 'nelems' is the max number of elements for the hash table. + */ + int64 nelems; + + /* + * Hash table options passed to hash_create() + * + * hash_info 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 implicitly in + * ShmemRequestHash(), except that callers may choose to specify + * HASH_PARTITION and/or HASH_FIXED_SIZE. + */ + HASHCTL hash_info; + int hash_flags; + + /* + * When the hash table is initialized or attached to, pointer to its + * backend-private handle is stored in *ptr. It usually points to a + * global variable, used to access the hash table later. + */ + HTAB **ptr; +} ShmemHashOpts; + +typedef void (*ShmemRequestCallback) (void *opaque_arg); +typedef void (*ShmemInitCallback) (void *opaque_arg); +typedef void (*ShmemAttachCallback) (void *opaque_arg); + +/* + * Shared memory is reserved and allocated in stages at postmaster startup, + * and in EXEC_BACKEND mode, there's some extra work done to "attach" to them + * at backend startup. ShmemCallbacks holds callback functions that are + * called at different stages. + */ +typedef struct ShmemCallbacks +{ + /* SHMEM_CALLBACKS_* flags */ + int flags; + + /* + * 'request_fn' is called during postmaster startup, before the shared + * memory has been allocated. The function should call + * ShmemRequestStruct() and ShmemRequestHash() to register the subsystem's + * shared memory needs. + */ + ShmemRequestCallback request_fn; + + /* + * Initialization callback function. This is called after the shared + * memory area has been allocated, usually at postmaster startup. + */ + ShmemInitCallback init_fn; + + /* + * 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 (see + * SHMEM_CALLBACKS_ALLOW_AFTER_STARTUP). + */ + ShmemAttachCallback attach_fn; + + /* + * Argument passed to the callbacks. This is opaque to the shmem system, + * callbacks can use it for their own purposes. + */ + void *opaque_arg; +} ShmemCallbacks; + +/* + * Flags to control the behavior of RegisterShmemCallbacks(). + * + * SHMEM_CALLBACKS_ALLOW_AFTER_STARTUP: Normally, calling + * RegisterShmemCallbacks() after postmaster startup, e.g. in an add-in + * library loaded on-demand in a backend, results in an error, because shared + * memory should generally be requested at postmaster startup time. But if + * this flag is set, it is allowed and the callbacks are called immediately to + * initialize or attach to the requested shared memory areas. This is not + * used by any built-in subsystems, but extensions may find it useful. + */ +#define SHMEM_CALLBACKS_ALLOW_AFTER_STARTUP 0x00000001 + +extern void RegisterShmemCallbacks(const ShmemCallbacks *callbacks); +extern bool ShmemAddrIsValid(const void *addr); ++<<<<<<< ours + +/* + * These macros provide syntactic sugar for calling the underlying functions + * with named arguments -like syntax. + */ +#define ShmemRequestStruct(...) \ + ShmemRequestStructWithOpts(&(ShmemStructOpts){__VA_ARGS__}) + +#define ShmemRequestHash(...) \ + ShmemRequestHashWithOpts(&(ShmemHashOpts){__VA_ARGS__}) + +extern void ShmemRequestStructWithOpts(const ShmemStructOpts *options); +extern void ShmemRequestHashWithOpts(const ShmemHashOpts *options); -/* shmem.c */ -extern PGDLLIMPORT slock_t *ShmemLock; -typedef struct PGShmemHeader PGShmemHeader; /* avoid including - * storage/pg_shmem.h here */ -extern void InitShmemAllocator(PGShmemHeader *seghdr); +/* legacy shmem allocation functions */ +extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr); +extern HTAB *ShmemInitHash(const char *name, int64 nelems, + HASHCTL *infoP, int hash_flags); extern void *ShmemAlloc(Size size); extern void *ShmemAllocNoError(Size size); -extern bool ShmemAddrIsValid(const void *addr); ++======= + extern void InitShmemIndex(void); + + extern void ShmemRegisterHash(ShmemHashDesc *desc, HASHCTL *infoP, int hash_flags); + extern void ShmemRegisterStruct(ShmemStructDesc *desc); + + /* Legacy 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); + extern void ShmemAttachRegistered(void); ++>>>>>>> theirs extern Size add_size(Size s1, Size s2); extern Size mul_size(Size s1, Size s2); diff --cc src/include/storage/sinvaladt.h index 208ea9d051e,4edba2936e6..00000000000 --- a/src/include/storage/sinvaladt.h +++ b/src/include/storage/sinvaladt.h @@@ -27,6 -28,7 +27,10 @@@ /* * prototypes for functions in sinvaladt.c */ ++<<<<<<< ours ++======= + extern void SharedInvalShmemRegister(void); ++>>>>>>> theirs extern void SharedInvalBackendInit(bool sendOnly); extern void SIInsertDataEntries(const SharedInvalidationMessage *data, int n);