=== Applying patches on top of PostgreSQL commit ID 5a6c39b6df3313e5c2d3aed714a56f5a5c6be3f2 === /etc/rc.d/jail: WARNING: Per-jail configuration via jail_* variables is obsolete. Please consider migrating to /etc/jail.conf. Fri Jul 4 07:50:21 UTC 2025 On branch cf/5654 nothing to commit, working tree clean === using 'git am' to apply patch ./v4-0001-add-detach-and-destroy-for-dsm_registry.patch === Applying: Add detach and destroy feature to dsm_registry Using index info to reconstruct a base tree... M src/backend/storage/ipc/dsm_registry.c M src/include/storage/dsm_registry.h M src/test/modules/test_dsm_registry/expected/test_dsm_registry.out M src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql M src/test/modules/test_dsm_registry/test_dsm_registry--1.0.sql M src/test/modules/test_dsm_registry/test_dsm_registry.c Falling back to patching base and 3-way merge... Auto-merging src/test/modules/test_dsm_registry/test_dsm_registry.c Auto-merging src/test/modules/test_dsm_registry/test_dsm_registry--1.0.sql CONFLICT (content): Merge conflict in src/test/modules/test_dsm_registry/test_dsm_registry--1.0.sql Auto-merging src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql CONFLICT (content): Merge conflict in src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql Auto-merging src/test/modules/test_dsm_registry/expected/test_dsm_registry.out CONFLICT (content): Merge conflict in src/test/modules/test_dsm_registry/expected/test_dsm_registry.out Auto-merging src/include/storage/dsm_registry.h CONFLICT (content): Merge conflict in src/include/storage/dsm_registry.h Auto-merging src/backend/storage/ipc/dsm_registry.c CONFLICT (content): Merge conflict in src/backend/storage/ipc/dsm_registry.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 Add detach and destroy feature to dsm_registry 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". Unstaged changes after reset: M src/backend/storage/ipc/dsm.c M src/backend/storage/ipc/dsm_registry.c M src/include/storage/dsm.h M src/include/storage/dsm_registry.h M src/test/modules/test_dsm_registry/expected/test_dsm_registry.out M src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql M src/test/modules/test_dsm_registry/test_dsm_registry--1.0.sql M src/test/modules/test_dsm_registry/test_dsm_registry.c === using patch(1) to apply patch ./v4-0001-add-detach-and-destroy-for-dsm_registry.patch === patching file src/backend/storage/ipc/dsm.c patching file src/backend/storage/ipc/dsm_registry.c Hunk #1 succeeded at 100 with fuzz 2 (offset 53 lines). Hunk #2 succeeded at 180 (offset 53 lines). Hunk #3 FAILED at 215. Hunk #4 succeeded at 288 with fuzz 2 (offset 60 lines). Hunk #5 succeeded at 481 (offset 237 lines). 1 out of 5 hunks FAILED -- saving rejects to file src/backend/storage/ipc/dsm_registry.c.rej patching file src/include/storage/dsm.h patching file src/include/storage/dsm_registry.h Hunk #1 FAILED at 17. 1 out of 1 hunk FAILED -- saving rejects to file src/include/storage/dsm_registry.h.rej patching file src/test/modules/test_dsm_registry/expected/test_dsm_registry.out Hunk #1 succeeded at 24 with fuzz 1 (offset 12 lines). patching file src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql Hunk #1 FAILED at 2. 1 out of 1 hunk FAILED -- saving rejects to file src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql.rej patching file src/test/modules/test_dsm_registry/test_dsm_registry--1.0.sql Hunk #1 succeeded at 14 with fuzz 2 (offset 6 lines). patching file src/test/modules/test_dsm_registry/test_dsm_registry.c Hunk #1 succeeded at 52 with fuzz 1 (offset 17 lines). Hunk #2 succeeded at 151 with fuzz 2 (offset 71 lines). Unstaged changes after reset: M src/backend/storage/ipc/dsm.c M src/backend/storage/ipc/dsm_registry.c M src/include/storage/dsm.h M src/test/modules/test_dsm_registry/expected/test_dsm_registry.out M src/test/modules/test_dsm_registry/test_dsm_registry--1.0.sql M src/test/modules/test_dsm_registry/test_dsm_registry.c Removing src/backend/storage/ipc/dsm_registry.c.rej Removing src/include/storage/dsm_registry.h.rej Removing src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql.rej === using 'git apply' to apply patch ./v4-0001-add-detach-and-destroy-for-dsm_registry.patch === Applied patch to 'src/backend/storage/ipc/dsm.c' cleanly. Applied patch to 'src/backend/storage/ipc/dsm_registry.c' with conflicts. Applied patch to 'src/include/storage/dsm.h' cleanly. Applied patch to 'src/include/storage/dsm_registry.h' with conflicts. Applied patch to 'src/test/modules/test_dsm_registry/expected/test_dsm_registry.out' with conflicts. Applied patch to 'src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql' with conflicts. Applied patch to 'src/test/modules/test_dsm_registry/test_dsm_registry--1.0.sql' with conflicts. Applied patch to 'src/test/modules/test_dsm_registry/test_dsm_registry.c' cleanly. U src/backend/storage/ipc/dsm_registry.c U src/include/storage/dsm_registry.h U src/test/modules/test_dsm_registry/expected/test_dsm_registry.out U src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql U src/test/modules/test_dsm_registry/test_dsm_registry--1.0.sql diff --cc src/backend/storage/ipc/dsm_registry.c index 828c2ff0c7f,8bc3bd28a50..00000000000 --- a/src/backend/storage/ipc/dsm_registry.c +++ b/src/backend/storage/ipc/dsm_registry.c @@@ -60,48 -40,21 +60,54 @@@ typedef struct DSMRegistryCtxStruc static DSMRegistryCtxStruct *DSMRegistryCtx; -typedef struct DSMRegistryEntry +typedef struct NamedDSMState { - char name[64]; dsm_handle handle; size_t size; +} NamedDSMState; + +typedef struct NamedDSAState +{ + dsa_handle handle; + int tranche; + char tranche_name[DSMR_DSA_TRANCHE_NAME_LEN]; +} NamedDSAState; + +typedef struct NamedDSHState +{ + NamedDSAState dsa; + dshash_table_handle handle; + int tranche; + char tranche_name[DSMR_NAME_LEN]; +} NamedDSHState; + +typedef enum DSMREntryType +{ + DSMR_ENTRY_TYPE_DSM, + DSMR_ENTRY_TYPE_DSA, + DSMR_ENTRY_TYPE_DSH, +} DSMREntryType; + +typedef struct DSMRegistryEntry +{ + char name[DSMR_NAME_LEN]; + DSMREntryType type; + union + { + NamedDSMState dsm; + NamedDSAState dsa; + NamedDSHState dsh; + } data; } DSMRegistryEntry; + typedef struct DSMDetachCallbackContext + { + void (*on_detach_callback) (void *); + void *arg; + } DSMDetachCallbackContext; + static const dshash_parameters dsh_params = { - offsetof(DSMRegistryEntry, handle), + offsetof(DSMRegistryEntry, type), sizeof(DSMRegistryEntry), dshash_strcmp, dshash_strhash, @@@ -228,25 -213,28 +271,35 @@@ GetNamedDSMSegment(const char *name, si if (init_callback) (*init_callback) (ret); } ++<<<<<<< ours + else if (entry->type != DSMR_ENTRY_TYPE_DSM) ++======= + else if (entry->size != size) + { + dshash_release_lock(dsm_registry_table, entry); + MemoryContextSwitchTo(oldcontext); ++>>>>>>> theirs ereport(ERROR, - (errmsg("requested DSM segment size does not match size of " - "existing segment"))); - } + (errmsg("requested DSM segment does not match type of existing entry"))); + else if (entry->data.dsm.size != size) + ereport(ERROR, + (errmsg("requested DSM segment size does not match size of existing segment"))); else { - dsm_segment *seg = dsm_find_mapping(entry->handle); + NamedDSMState *state = &entry->data.dsm; + dsm_segment *seg; /* If the existing segment is not already attached, attach it now. */ + seg = dsm_find_mapping(state->handle); if (seg == NULL) { - seg = dsm_attach(entry->handle); + seg = dsm_attach(state->handle); if (seg == NULL) + { + dshash_release_lock(dsm_registry_table, entry); + MemoryContextSwitchTo(oldcontext); elog(ERROR, "could not map dynamic shared memory segment"); - + } dsm_pin_mapping(seg); } @@@ -260,29 -248,31 +313,57 @@@ } /* ++<<<<<<< ours + * Initialize or attach a named DSA. + * + * This routine returns a pointer to the DSA. A new LWLock tranche ID will be + * generated if needed. Note that the lock tranche will be registered with the + * provided name. Also note that this should be called at most once for a + * given DSA in each backend. + */ +dsa_area * +GetNamedDSA(const char *name, bool *found) +{ + DSMRegistryEntry *entry; + MemoryContext oldcontext; + dsa_area *ret; + + Assert(found); + + if (!name || *name == '\0') + ereport(ERROR, + (errmsg("DSA name cannot be empty"))); + + if (strlen(name) >= offsetof(DSMRegistryEntry, type)) + ereport(ERROR, + (errmsg("DSA name too long"))); ++======= + * Detach a named DSM segment + * + * This routine detaches the DSM segment. If the DSM segment was not attached + * by this process, then the routine just returns. on_detach_callback is passed + * on to dsm_segment by calling on_dsm_detach for the corresponding dsm_segment + * before actually detaching. + */ + void + DetachNamedDSMSegment(const char *name, size_t size, + void (*on_detach_callback) (void *), void *arg) + { + DSMRegistryEntry *entry; + MemoryContext oldcontext; + + if (!name || *name == '\0') + ereport(ERROR, + (errmsg("DSM segment name cannot be empty"))); + + if (strlen(name) >= offsetof(DSMRegistryEntry, handle)) + ereport(ERROR, + (errmsg("DSM segment name too long"))); + + if (size == 0) + ereport(ERROR, + (errmsg("DSM segment size must be nonzero"))); ++>>>>>>> theirs /* Be sure any local memory allocated by DSM/DSA routines is persistent. */ oldcontext = MemoryContextSwitchTo(TopMemoryContext); @@@ -290,78 -280,61 +371,136 @@@ /* Connect to the registry. */ init_dsm_registry(); ++<<<<<<< ours + entry = dshash_find_or_insert(dsm_registry_table, name, found); + if (!(*found)) + { + NamedDSAState *state = &entry->data.dsa; + + entry->type = DSMR_ENTRY_TYPE_DSA; + + /* Initialize the LWLock tranche for the DSA. */ + state->tranche = LWLockNewTrancheId(); + strcpy(state->tranche_name, name); + LWLockRegisterTranche(state->tranche, state->tranche_name); + + /* Initialize the DSA. */ + ret = dsa_create(state->tranche); + dsa_pin(ret); + dsa_pin_mapping(ret); + + /* Store handle for other backends to use. */ + state->handle = dsa_get_handle(ret); + } + else if (entry->type != DSMR_ENTRY_TYPE_DSA) + ereport(ERROR, + (errmsg("requested DSA does not match type of existing entry"))); + else + { + NamedDSAState *state = &entry->data.dsa; + + if (dsa_is_attached(state->handle)) + ereport(ERROR, + (errmsg("requested DSA already attached to current process"))); + + /* Initialize existing LWLock tranche for the DSA. */ + LWLockRegisterTranche(state->tranche, state->tranche_name); + + /* Attach to existing DSA. */ + ret = dsa_attach(state->handle); + dsa_pin_mapping(ret); + } + + dshash_release_lock(dsm_registry_table, entry); + MemoryContextSwitchTo(oldcontext); + + return ret; +} + +/* + * Initialize or attach a named dshash table. + * + * This routine returns the address of the table. The tranche_id member of + * params is ignored; new tranche IDs will be generated if needed. Note that + * the DSA lock tranche will be registered with the provided name with " DSA" + * appended. The dshash lock tranche will be registered with the provided + * name. Also note that this should be called at most once for a given table + * in each backend. + */ +dshash_table * +GetNamedDSHash(const char *name, const dshash_parameters *params, bool *found) +{ + DSMRegistryEntry *entry; + MemoryContext oldcontext; + dshash_table *ret; + + Assert(params); + Assert(found); + + if (!name || *name == '\0') + ereport(ERROR, + (errmsg("DSHash name cannot be empty"))); + + if (strlen(name) >= offsetof(DSMRegistryEntry, type)) + ereport(ERROR, + (errmsg("DSHash name too long"))); ++======= + entry = dshash_find(dsm_registry_table, name, true); + + if (entry == NULL) + { + MemoryContextSwitchTo(oldcontext); + ereport(ERROR, + (errmsg("cannot detach a DSM segment that does not exist"))); + } + + if (entry->size != size) + { + dshash_release_lock(dsm_registry_table, entry); + MemoryContextSwitchTo(oldcontext); + ereport(ERROR, + (errmsg("requested DSM segment size does not match size of " + "existing segment"))); + } + + detach_dsm_segment(entry->handle, on_detach_callback, arg); + + dshash_release_lock(dsm_registry_table, entry); + MemoryContextSwitchTo(oldcontext); + } + + /* + * Attempt to destroy a named DSM segment + * + * This routine attempts to destroy the DSM segment. We unpin the dsm_segment + * and delete the entry from dsm_registry_table. This may not destroy the + * dsm_segment instantly, but it would die out once all the other processes + * attached to this dsm_segment either exit or manually detach from the + * dsm_segment. + * + * Because we deleted the key from dsm_registry_table, calling + * GetNamedDSMSegment with the same key would result into creating a new + * dsm_segment instead of retrieving the old unpinned dsm_segment. + */ + void + DestroyNamedDSMSegment(const char *name, size_t size, + void (*on_detach_callback) (void *), void *arg) + { + DSMRegistryEntry *entry; + MemoryContext oldcontext; + + if (!name || *name == '\0') + ereport(ERROR, + (errmsg("DSM segment name cannot be empty"))); + + if (strlen(name) >= offsetof(DSMRegistryEntry, handle)) + ereport(ERROR, + (errmsg("DSM segment name too long"))); + + if (size == 0) + ereport(ERROR, + (errmsg("DSM segment size must be nonzero"))); ++>>>>>>> theirs /* Be sure any local memory allocated by DSM/DSA routines is persistent. */ oldcontext = MemoryContextSwitchTo(TopMemoryContext); @@@ -369,69 -342,39 +508,108 @@@ /* Connect to the registry. */ init_dsm_registry(); ++<<<<<<< ours + entry = dshash_find_or_insert(dsm_registry_table, name, found); + if (!(*found)) + { + NamedDSAState *dsa_state = &entry->data.dsh.dsa; + NamedDSHState *dsh_state = &entry->data.dsh; + dshash_parameters params_copy; + dsa_area *dsa; + + entry->type = DSMR_ENTRY_TYPE_DSH; + + /* Initialize the LWLock tranche for the DSA. */ + dsa_state->tranche = LWLockNewTrancheId(); + sprintf(dsa_state->tranche_name, "%s%s", name, DSMR_DSA_TRANCHE_SUFFIX); + LWLockRegisterTranche(dsa_state->tranche, dsa_state->tranche_name); + + /* Initialize the LWLock tranche for the dshash table. */ + dsh_state->tranche = LWLockNewTrancheId(); + strcpy(dsh_state->tranche_name, name); + LWLockRegisterTranche(dsh_state->tranche, dsh_state->tranche_name); + + /* Initialize the DSA for the hash table. */ + dsa = dsa_create(dsa_state->tranche); + dsa_pin(dsa); + dsa_pin_mapping(dsa); + + /* Initialize the dshash table. */ + memcpy(¶ms_copy, params, sizeof(dshash_parameters)); + params_copy.tranche_id = dsh_state->tranche; + ret = dshash_create(dsa, ¶ms_copy, NULL); + + /* Store handles for other backends to use. */ + dsa_state->handle = dsa_get_handle(dsa); + dsh_state->handle = dshash_get_hash_table_handle(ret); + } + else if (entry->type != DSMR_ENTRY_TYPE_DSH) + ereport(ERROR, + (errmsg("requested DSHash does not match type of existing entry"))); + else + { + NamedDSAState *dsa_state = &entry->data.dsh.dsa; + NamedDSHState *dsh_state = &entry->data.dsh; + dsa_area *dsa; + + /* XXX: Should we verify params matches what table was created with? */ + + if (dsa_is_attached(dsa_state->handle)) + ereport(ERROR, + (errmsg("requested DSHash already attached to current process"))); + + /* Initialize existing LWLock tranches for the DSA and dshash table. */ + LWLockRegisterTranche(dsa_state->tranche, dsa_state->tranche_name); + LWLockRegisterTranche(dsh_state->tranche, dsh_state->tranche_name); + + /* Attach to existing DSA for the hash table. */ + dsa = dsa_attach(dsa_state->handle); + dsa_pin_mapping(dsa); + + /* Attach to existing dshash table. */ + ret = dshash_attach(dsa, params, dsh_state->handle, NULL); + } + + dshash_release_lock(dsm_registry_table, entry); + MemoryContextSwitchTo(oldcontext); + + return ret; +} ++======= + entry = dshash_find(dsm_registry_table, name, true); + + if (entry == NULL) + { + MemoryContextSwitchTo(oldcontext); + ereport(ERROR, + (errmsg("cannot destroy a DSM segment that does not exist"))); + } + + if (entry->size != size) + { + dshash_release_lock(dsm_registry_table, entry); + MemoryContextSwitchTo(oldcontext); + ereport(ERROR, + (errmsg("requested DSM segment size does not match size of " + "existing segment"))); + } + + if (is_dsm_segment_in_use(entry->handle)) + { + dshash_release_lock(dsm_registry_table, entry); + MemoryContextSwitchTo(oldcontext); + ereport(ERROR, + (errmsg("cannot destroy a DSM segment that is still in use"))); + } + + detach_dsm_segment(entry->handle, on_detach_callback, arg); + dsm_unpin_segment(entry->handle); + + /* dshash_delete_entry calls LWLockRelease internally. We shouldn't + * release lock twice */ + dshash_delete_entry(dsm_registry_table, entry); + dshash_delete_key(dsm_registry_table, name); + + MemoryContextSwitchTo(oldcontext); -} ++} ++>>>>>>> theirs diff --cc src/include/storage/dsm_registry.h index 4871ed509eb,2bd20b82ffd..00000000000 --- a/src/include/storage/dsm_registry.h +++ b/src/include/storage/dsm_registry.h @@@ -18,10 -16,15 +18,22 @@@ extern void *GetNamedDSMSegment(const char *name, size_t size, void (*init_callback) (void *ptr), bool *found); ++<<<<<<< ours +extern dsa_area *GetNamedDSA(const char *name, bool *found); +extern dshash_table *GetNamedDSHash(const char *name, + const dshash_parameters *params, + bool *found); ++======= + + extern void DetachNamedDSMSegment(const char *name, size_t size, + void (*on_detach_callback) (void *), + void *arg); + + extern void DestroyNamedDSMSegment(const char *name, size_t size, + void (*on_detach_callback) (void *), + void *arg); + ++>>>>>>> theirs extern Size DSMRegistryShmemSize(void); extern void DSMRegistryShmemInit(void); diff --cc src/test/modules/test_dsm_registry/expected/test_dsm_registry.out index 8ded82e59d6,3764aa31bf6..00000000000 --- a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out +++ b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out @@@ -18,9 -12,15 +18,22 @@@ SELECT get_val_in_shmem() 1236 (1 row) ++<<<<<<< ours +SELECT get_val_in_hash('test'); + get_val_in_hash +----------------- + 1414 ++======= + SELECT detach_from_tdr_segment(); + detach_from_tdr_segment + ------------------------- + t + (1 row) + + SELECT destroy_tdr_segment(); + destroy_tdr_segment + --------------------- + ++>>>>>>> theirs (1 row) diff --cc src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql index c2e25cddaae,24f90658f83..00000000000 --- a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql +++ b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql @@@ -1,6 -1,6 +1,11 @@@ CREATE EXTENSION test_dsm_registry; SELECT set_val_in_shmem(1236); +SELECT set_val_in_hash('test', '1414'); \c SELECT get_val_in_shmem(); ++<<<<<<< ours +SELECT get_val_in_hash('test'); ++======= + SELECT detach_from_tdr_segment(); -SELECT destroy_tdr_segment(); ++SELECT destroy_tdr_segment(); ++>>>>>>> theirs diff --cc src/test/modules/test_dsm_registry/test_dsm_registry--1.0.sql index 5da45155be9,74d8dc50d8a..00000000000 --- a/src/test/modules/test_dsm_registry/test_dsm_registry--1.0.sql +++ b/src/test/modules/test_dsm_registry/test_dsm_registry--1.0.sql @@@ -9,8 -9,8 +9,16 @@@ CREATE FUNCTION set_val_in_shmem(val IN CREATE FUNCTION get_val_in_shmem() RETURNS INT AS 'MODULE_PATHNAME' LANGUAGE C; ++<<<<<<< ours +CREATE FUNCTION set_val_in_hash(key TEXT, val TEXT) RETURNS VOID + AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION get_val_in_hash(key TEXT) RETURNS TEXT + AS 'MODULE_PATHNAME' LANGUAGE C; ++======= + CREATE FUNCTION detach_from_tdr_segment() RETURNS BOOL + AS 'MODULE_PATHNAME' LANGUAGE C; + + CREATE FUNCTION destroy_tdr_segment() RETURNS VOID - AS 'MODULE_PATHNAME' LANGUAGE C; ++ AS 'MODULE_PATHNAME' LANGUAGE C; ++>>>>>>> theirs