Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vNx6k-002q7h-1a for pgsql-hackers@arkaria.postgresql.org; Tue, 25 Nov 2025 17:45:42 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vNx6i-00AQED-2Q for pgsql-hackers@arkaria.postgresql.org; Tue, 25 Nov 2025 17:45:41 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vNx6i-00AQE4-0e for pgsql-hackers@lists.postgresql.org; Tue, 25 Nov 2025 17:45:40 +0000 Received: from mail-io1-xd31.google.com ([2607:f8b0:4864:20::d31]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1vNx6f-001QoC-0V for pgsql-hackers@lists.postgresql.org; Tue, 25 Nov 2025 17:45:39 +0000 Received: by mail-io1-xd31.google.com with SMTP id ca18e2360f4ac-94903ea3766so218101839f.3 for ; Tue, 25 Nov 2025 09:45:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1764092737; x=1764697537; darn=lists.postgresql.org; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=7xo55WGp3nCFrbPSP2qm/FCpRrs+K5esJbPDgdGdoto=; b=mdr/qT2zMeilPjt0iaOGkNW9KgScCqA2ba4octEc8UngSyKsWgzEpfGwDWz359J5WI Tg5YAqGlS664lzARie6tpdMOwooDb2QsHt9gpOv30LG/TpBX0ER4y4gTx+qcBQpzokWv H9moBWW4uA0i4i8RjlbtqOi/7Eza/ZZqVZ2/pD1/BdhmheM8fmYFG6Sqg160SKE1IkNW 1YHMaZIxsYZlCdeQ7WeyAsj+e+zQX4xYPvFZn435YFh1lgaYnqK0jD15tDQ9PKHi0QcW 36UCbN0XpPkIqqy7/VfYVWWqWnAtpollwQQvw7cLkTlWIS9buSd8lfuNTpuHxrwpkF+p /0BA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764092737; x=1764697537; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:x-gm-gg:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=7xo55WGp3nCFrbPSP2qm/FCpRrs+K5esJbPDgdGdoto=; b=bRZ12GmO6FhnMKX9bzk7UDtlqsihAmK2y2gUwgjphDBGwzDZSz+QYBhUFsZcKOdK5e eSyPbaONgbTKyxs53+6RtcNeJmuAsgR1be9kQNCkUz19QTdMgVVKAOadj9ofiMRbJy39 iNsBG9CwRx6SMgjA8zbSpujLpkZKZNvBbDDsgu/SpBS5x/hB3TGrFrJblsz7qvPsRIz4 PGEc2oQhEb7akRF9/xsfGr6Ps3DsYd+L57K3igjmWNJ+T7xcX7z9wLOkhb6D5sepet7W FkonbhiVlsWJI3xcLPayTf1RP6SJruDOD8of854otIXcxeEi3RKStpO3t7VrigXxPb5H kI4w== X-Gm-Message-State: AOJu0Yxdrzkwy1V+fQSDOdCyuTiFZ+pUhIsKqfk/7P2l9V+A4d280A/A GmyhrD42uU4CT+tQV416pvwtEh8G95Bn6WbPrKuRkcjPSgxy5NmolzG5 X-Gm-Gg: ASbGncu0tvmrnRzo/NDsZ2vvWwLPegPRL8OILL9CQmtFJRltlOvlGtOGtm5US8mWVSF hK7sitgnybpG6sU22FeCb9llpT/rhmpS2QDKqnXQS+xWRke85UgbqUXT4Holze2XVVLHygBbLkE vns53FNmWQAwVPOEOPxSBwSvg+riXG85B6yNJ7KbaIA8nuSWKK/yPVq5S87oH6gJBWpW13hm5XD BQpAxnT6RpA1rbZh719oAtNtZoy6hbsLJ5t/sE8sdIJrT+BHTct/FLxZABsYtx6xJaU1iughisk GO5dictdWT96pG4qh0jqEUJmH1ea9B08DaGmO497ImemyB3vzyvvq3xRYoVsAGlwHHjI0byGUVZ BIg82u3Kt1Ps2+44H6Um7qV2miw2Ims83y/ne9sgVGUf4clrU2khRxEzgOL0yDtnuI+wUapmj3C 1PG8RA7rGWgFSr/iiC4LYJzVPAA9ljAmOJxClnffndKfj/+FdHxayQAALfr8RX70zeKZKh4N4Kv b/O X-Google-Smtp-Source: AGHT+IHiQAlNZbCfGzxAuQQblKG1ejkK7+AODyj4gF0CSRUT2QM3vy2uRaBT+bj/L4QRG89CFjVzWA== X-Received: by 2002:a05:6638:9283:b0:5ac:8391:5bd0 with SMTP id 8926c6da1cb9f-5b99971afc0mr2405499173.15.1764092737212; Tue, 25 Nov 2025 09:45:37 -0800 (PST) Received: from nathan (162-195-168-172.lightspeed.stlsmo.sbcglobal.net. [162.195.168.172]) by smtp.gmail.com with ESMTPSA id 8926c6da1cb9f-5b954a50255sm7225049173.14.2025.11.25.09.45.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 25 Nov 2025 09:45:36 -0800 (PST) Date: Tue, 25 Nov 2025 11:45:34 -0600 From: Nathan Bossart To: Robert Haas Cc: PostgreSQL Hackers Subject: Re: pgsql: Teach DSM registry to ERROR if attaching to an uninitialized ent Message-ID: References: MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="c08aH/hgV4/mNMsI" Content-Disposition: inline In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --c08aH/hgV4/mNMsI Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Tue, Nov 25, 2025 at 11:25:09AM -0500, Robert Haas wrote: > The downside is that then we have to rely on PG_CATCH() to make things > whole. I am not sure that there's any problem with that, but I'm also > not sure that there isn't. The timing of PG_CATCH() block execution > often creates bugs, because it runs before (sub)transaction abort. > That means that there's a real risk that you try to acquire an LWLock > you already hold, for example. It's a lot easier to be confident that > cleanup actions will reliably succeed when they run inside the > transaction abort path that knows the order in which various resources > should be released. Generally, I think it's PG_CATCH() is fine if > you're releasing a resource that is decoupled from everything else, > like using a third-party library's free function to free memory > allocated by that library. But if you're releasing PostgreSQL > resources that are layered on top of other PostgreSQL resources, like > a DSA that depends on DSM and LWLock, I think it's a lot more > difficult to be certain that you aren't going to end up trying to > release the same stuff multiple times or in the wrong order. I'm not > saying you can't make it work, but I've banged my head on this > particular doorframe enough times that my reflex is to duck. I gave your idea a try, and I like the result a lot. IMHO it makes the code much easier to reason about. -- nathan --c08aH/hgV4/mNMsI Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=v3-0001-Revert-Teach-DSM-registry-to-ERROR-if-attaching-t.patch From 3fddb959db906ff6560d73c08ba7695812020275 Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Mon, 24 Nov 2025 11:03:52 -0600 Subject: [PATCH v3 1/2] Revert "Teach DSM registry to ERROR if attaching to an uninitialized entry." This reverts commit 1165a933aab1355757a43cfd9193b6cce06f573b. --- src/backend/storage/ipc/dsm_registry.c | 34 +++----------------------- 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c index 7eba8a8cffb..a926b9c3f32 100644 --- a/src/backend/storage/ipc/dsm_registry.c +++ b/src/backend/storage/ipc/dsm_registry.c @@ -93,7 +93,6 @@ typedef struct DSMRegistryEntry { char name[NAMEDATALEN]; DSMREntryType type; - bool initialized; union { NamedDSMState dsm; @@ -217,7 +216,6 @@ GetNamedDSMSegment(const char *name, size_t size, dsm_segment *seg; entry->type = DSMR_ENTRY_TYPE_DSM; - entry->initialized = false; /* Initialize the segment. */ seg = dsm_create(size, 0); @@ -230,21 +228,13 @@ GetNamedDSMSegment(const char *name, size_t size, if (init_callback) (*init_callback) (ret); - - entry->initialized = true; } else if (entry->type != DSMR_ENTRY_TYPE_DSM) ereport(ERROR, - (errmsg("requested DSM segment \"%s\" does not match type of existing entry", - name))); - else if (!entry->initialized) - ereport(ERROR, - (errmsg("requested DSM segment \"%s\" failed initialization", - name))); + (errmsg("requested DSM segment does not match type of existing entry"))); else if (entry->dsm.size != size) ereport(ERROR, - (errmsg("requested DSM segment \"%s\" does not match size of existing entry", - name))); + (errmsg("requested DSM segment size does not match size of existing segment"))); else { NamedDSMState *state = &entry->dsm; @@ -307,7 +297,6 @@ GetNamedDSA(const char *name, bool *found) NamedDSAState *state = &entry->dsa; entry->type = DSMR_ENTRY_TYPE_DSA; - entry->initialized = false; /* Initialize the LWLock tranche for the DSA. */ state->tranche = LWLockNewTrancheId(name); @@ -319,17 +308,10 @@ GetNamedDSA(const char *name, bool *found) /* Store handle for other backends to use. */ state->handle = dsa_get_handle(ret); - - entry->initialized = true; } else if (entry->type != DSMR_ENTRY_TYPE_DSA) ereport(ERROR, - (errmsg("requested DSA \"%s\" does not match type of existing entry", - name))); - else if (!entry->initialized) - ereport(ERROR, - (errmsg("requested DSA \"%s\" failed initialization", - name))); + (errmsg("requested DSA does not match type of existing entry"))); else { NamedDSAState *state = &entry->dsa; @@ -390,7 +372,6 @@ GetNamedDSHash(const char *name, const dshash_parameters *params, bool *found) dsa_area *dsa; entry->type = DSMR_ENTRY_TYPE_DSH; - entry->initialized = false; /* Initialize the LWLock tranche for the hash table. */ dsh_state->tranche = LWLockNewTrancheId(name); @@ -408,17 +389,10 @@ GetNamedDSHash(const char *name, const dshash_parameters *params, bool *found) /* Store handles for other backends to use. */ dsh_state->dsa_handle = dsa_get_handle(dsa); dsh_state->dsh_handle = dshash_get_hash_table_handle(ret); - - entry->initialized = true; } else if (entry->type != DSMR_ENTRY_TYPE_DSH) ereport(ERROR, - (errmsg("requested DSHash \"%s\" does not match type of existing entry", - name))); - else if (!entry->initialized) - ereport(ERROR, - (errmsg("requested DSHash \"%s\" failed initialization", - name))); + (errmsg("requested DSHash does not match type of existing entry"))); else { NamedDSHState *dsh_state = &entry->dsh; -- 2.39.5 (Apple Git-154) --c08aH/hgV4/mNMsI Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=v3-0002-handle-ERRORs-in-DSM-registry-functions.patch From c40884c967755b3630d0419a7d6585d3f4aae170 Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Mon, 24 Nov 2025 12:28:23 -0600 Subject: [PATCH v3 2/2] handle ERRORs in DSM registry functions --- src/backend/storage/ipc/dsm_registry.c | 110 +++++++++++++++---------- 1 file changed, 67 insertions(+), 43 deletions(-) diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c index a926b9c3f32..5290cc1c8e3 100644 --- a/src/backend/storage/ipc/dsm_registry.c +++ b/src/backend/storage/ipc/dsm_registry.c @@ -155,9 +155,10 @@ init_dsm_registry(void) { /* Initialize dynamic shared hash table for registry. */ dsm_registry_dsa = dsa_create(LWTRANCHE_DSM_REGISTRY_DSA); + dsm_registry_table = dshash_create(dsm_registry_dsa, &dsh_params, NULL); + dsa_pin(dsm_registry_dsa); dsa_pin_mapping(dsm_registry_dsa); - dsm_registry_table = dshash_create(dsm_registry_dsa, &dsh_params, NULL); /* Store handles in shared memory for other backends to use. */ DSMRegistryCtx->dsah = dsa_get_handle(dsm_registry_dsa); @@ -188,6 +189,8 @@ GetNamedDSMSegment(const char *name, size_t size, DSMRegistryEntry *entry; MemoryContext oldcontext; void *ret; + NamedDSMState *state; + dsm_segment *seg; Assert(found); @@ -210,36 +213,36 @@ GetNamedDSMSegment(const char *name, size_t size, init_dsm_registry(); entry = dshash_find_or_insert(dsm_registry_table, name, found); + state = &entry->dsm; if (!(*found)) { - NamedDSMState *state = &entry->dsm; - dsm_segment *seg; - entry->type = DSMR_ENTRY_TYPE_DSM; + state->handle = DSM_HANDLE_INVALID; + state->size = size; + } + else if (entry->type != DSMR_ENTRY_TYPE_DSM) + ereport(ERROR, + (errmsg("requested DSM segment does not match type of existing entry"))); + else if (state->size != size) + ereport(ERROR, + (errmsg("requested DSM segment size does not match size of existing segment"))); + + if (state->handle == DSM_HANDLE_INVALID) + { + *found = false; /* Initialize the segment. */ seg = dsm_create(size, 0); + if (init_callback) + (*init_callback) (dsm_segment_address(seg)); + dsm_pin_segment(seg); dsm_pin_mapping(seg); state->handle = dsm_segment_handle(seg); - state->size = size; - ret = dsm_segment_address(seg); - - if (init_callback) - (*init_callback) (ret); } - else if (entry->type != DSMR_ENTRY_TYPE_DSM) - ereport(ERROR, - (errmsg("requested DSM segment does not match type of existing entry"))); - else if (entry->dsm.size != size) - ereport(ERROR, - (errmsg("requested DSM segment size does not match size of existing segment"))); else { - NamedDSMState *state = &entry->dsm; - dsm_segment *seg; - /* If the existing segment is not already attached, attach it now. */ seg = dsm_find_mapping(state->handle); if (seg == NULL) @@ -250,10 +253,9 @@ GetNamedDSMSegment(const char *name, size_t size, dsm_pin_mapping(seg); } - - ret = dsm_segment_address(seg); } + ret = dsm_segment_address(seg); dshash_release_lock(dsm_registry_table, entry); MemoryContextSwitchTo(oldcontext); @@ -274,6 +276,7 @@ GetNamedDSA(const char *name, bool *found) DSMRegistryEntry *entry; MemoryContext oldcontext; dsa_area *ret; + NamedDSAState *state; Assert(found); @@ -292,14 +295,28 @@ GetNamedDSA(const char *name, bool *found) init_dsm_registry(); entry = dshash_find_or_insert(dsm_registry_table, name, found); + state = &entry->dsa; if (!(*found)) { - NamedDSAState *state = &entry->dsa; - entry->type = DSMR_ENTRY_TYPE_DSA; + state->handle = DSA_HANDLE_INVALID; + state->tranche = -1; + } + else if (entry->type != DSMR_ENTRY_TYPE_DSA) + ereport(ERROR, + (errmsg("requested DSA does not match type of existing entry"))); + + if (state->tranche == -1) + { + *found = false; /* Initialize the LWLock tranche for the DSA. */ state->tranche = LWLockNewTrancheId(name); + } + + if (state->handle == DSA_HANDLE_INVALID) + { + *found = false; /* Initialize the DSA. */ ret = dsa_create(state->tranche); @@ -309,17 +326,11 @@ GetNamedDSA(const char *name, bool *found) /* Store handle for other backends to use. */ state->handle = dsa_get_handle(ret); } - else if (entry->type != DSMR_ENTRY_TYPE_DSA) + else if (dsa_is_attached(state->handle)) ereport(ERROR, - (errmsg("requested DSA does not match type of existing entry"))); + (errmsg("requested DSA already attached to current process"))); else { - NamedDSAState *state = &entry->dsa; - - if (dsa_is_attached(state->handle)) - ereport(ERROR, - (errmsg("requested DSA already attached to current process"))); - /* Attach to existing DSA. */ ret = dsa_attach(state->handle); dsa_pin_mapping(ret); @@ -346,6 +357,7 @@ GetNamedDSHash(const char *name, const dshash_parameters *params, bool *found) DSMRegistryEntry *entry; MemoryContext oldcontext; dshash_table *ret; + NamedDSHState *dsh_state; Assert(params); Assert(found); @@ -365,45 +377,57 @@ GetNamedDSHash(const char *name, const dshash_parameters *params, bool *found) init_dsm_registry(); entry = dshash_find_or_insert(dsm_registry_table, name, found); + dsh_state = &entry->dsh; if (!(*found)) { - NamedDSHState *dsh_state = &entry->dsh; - dshash_parameters params_copy; - dsa_area *dsa; - entry->type = DSMR_ENTRY_TYPE_DSH; + dsh_state->dsa_handle = DSA_HANDLE_INVALID; + dsh_state->dsh_handle = DSHASH_HANDLE_INVALID; + dsh_state->tranche = -1; + } + else if (entry->type != DSMR_ENTRY_TYPE_DSH) + ereport(ERROR, + (errmsg("requested DSHash does not match type of existing entry"))); + + if (dsh_state->tranche == -1) + { + *found = false; /* Initialize the LWLock tranche for the hash table. */ dsh_state->tranche = LWLockNewTrancheId(name); + } + + if (dsh_state->dsa_handle == DSA_HANDLE_INVALID) + { + dshash_parameters params_copy; + dsa_area *dsa; + + *found = false; /* Initialize the DSA for the hash table. */ dsa = dsa_create(dsh_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); + dsa_pin(dsa); + dsa_pin_mapping(dsa); + /* Store handles for other backends to use. */ dsh_state->dsa_handle = dsa_get_handle(dsa); dsh_state->dsh_handle = dshash_get_hash_table_handle(ret); } - else if (entry->type != DSMR_ENTRY_TYPE_DSH) + else if (dsa_is_attached(dsh_state->dsa_handle)) ereport(ERROR, - (errmsg("requested DSHash does not match type of existing entry"))); + (errmsg("requested DSHash already attached to current process"))); else { - NamedDSHState *dsh_state = &entry->dsh; dsa_area *dsa; /* XXX: Should we verify params matches what table was created with? */ - if (dsa_is_attached(dsh_state->dsa_handle)) - ereport(ERROR, - (errmsg("requested DSHash already attached to current process"))); - /* Attach to existing DSA for the hash table. */ dsa = dsa_attach(dsh_state->dsa_handle); dsa_pin_mapping(dsa); -- 2.39.5 (Apple Git-154) --c08aH/hgV4/mNMsI--