v1-0013-fe_utils-Provide-cancel_set-for-fast-quit-paths.patch
text/x-patch
Filename: v1-0013-fe_utils-Provide-cancel_set-for-fast-quit-paths.patch
Type: text/x-patch
Part: 12
Message:
pg_threads.h take II
Patch
Format: format-patch
Series: patch v1-0013
Subject: fe_utils: Provide cancel_set for fast quit paths.
| File | + | − |
|---|---|---|
| src/fe_utils/cancel_set.c | 158 | 0 |
| src/fe_utils/Makefile | 1 | 0 |
| src/fe_utils/meson.build | 1 | 0 |
| src/include/fe_utils/cancel_set.h | 20 | 0 |
| src/tools/pgindent/typedefs.list | 2 | 0 |
From 7e897c4c8cd99cce7b475b8339e50079c984a5b1 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Mon, 6 Jul 2026 16:53:46 +1200
Subject: [PATCH v1 13/14] fe_utils: Provide cancel_set for fast quit paths.
Provide a simpler way for frontend programs managing many connections to
arrange cleanup. Now that we can run more complicated code on eg SIGINT
via pg_signal_processor, this API provides an easy way to manage a set
of connections that need to be canceled. We couldn't have done this
before, because it requires locks. A follow-up patch will use this for
pg_dump/pg_restore.
XXX Experimental!
Discussion:
Reviewed-by:
---
src/fe_utils/Makefile | 1 +
src/fe_utils/cancel_set.c | 158 ++++++++++++++++++++++++++++++
src/fe_utils/meson.build | 1 +
src/include/fe_utils/cancel_set.h | 20 ++++
src/tools/pgindent/typedefs.list | 2 +
5 files changed, 182 insertions(+)
create mode 100644 src/fe_utils/cancel_set.c
create mode 100644 src/include/fe_utils/cancel_set.h
diff --git a/src/fe_utils/Makefile b/src/fe_utils/Makefile
index cbfbf93ac69..6ca39b7cf9a 100644
--- a/src/fe_utils/Makefile
+++ b/src/fe_utils/Makefile
@@ -27,6 +27,7 @@ OBJS = \
astreamer_tar.o \
astreamer_zstd.o \
cancel.o \
+ cancel_set.o \
conditional.o \
connect_utils.o \
mbprint.o \
diff --git a/src/fe_utils/cancel_set.c b/src/fe_utils/cancel_set.c
new file mode 100644
index 00000000000..cba1cb02f06
--- /dev/null
+++ b/src/fe_utils/cancel_set.c
@@ -0,0 +1,158 @@
+#include "c.h"
+
+#include "fe_utils/cancel_set.h"
+#include "lib/ilist.h"
+#include "port/pg_threads.h"
+
+typedef struct cancel_set_entry
+{
+ dlist_node node;
+ PGconn *connection;
+ PGcancelConn *cancel;
+} cancel_set_entry;
+
+struct cancel_set
+{
+ pg_mtx_t mutex;
+ dlist_head entries;
+};
+
+cancel_set *
+cancel_set_alloc(void)
+{
+ cancel_set *cs;
+
+ if (!(cs = malloc(sizeof(*cs))))
+ return NULL;
+
+ pg_mtx_init(&cs->mutex, pg_mtx_plain);
+ dlist_init(&cs->entries);
+
+ return cs;
+}
+
+void
+cancel_set_free(cancel_set *cs)
+{
+ cancel_set_remove_all(cs);
+ pg_mtx_destroy(&cs->mutex);
+ free(cs);
+}
+
+/*
+ * Add a connection to the set. Returns false on failure.
+ */
+bool
+cancel_set_add(cancel_set *cs, PGconn *connection)
+{
+ bool result;
+ cancel_set_entry *entry;
+ PGcancelConn *cancel;
+
+ cancel = PQcancelCreate(connection);
+ if (cancel == NULL)
+ return false;
+
+ pg_mtx_lock(&cs->mutex);
+ entry = malloc(sizeof(*entry));
+ if (entry)
+ {
+ entry->connection = connection;
+ entry->cancel = cancel;
+ dlist_push_tail(&cs->entries, &entry->node);
+ result = true;
+ }
+ else
+ {
+ PQcancelFinish(cancel);
+ result = false;
+ }
+ pg_mtx_unlock(&cs->mutex);
+
+ return result;
+}
+
+static int
+cancel_set_scan(cancel_set *cs, PGconn *connection, bool cancel, bool remove)
+{
+ dlist_mutable_iter iter;
+ int count = 0;
+
+ pg_mtx_lock(&cs->mutex);
+ dlist_foreach_modify(iter, &cs->entries)
+ {
+ cancel_set_entry *entry;
+
+ entry = dlist_container(cancel_set_entry, node, iter.cur);
+ if (connection == NULL || entry->connection == connection)
+ {
+ count++;
+ if (cancel)
+ {
+ /*
+ * XXX Pin these so they can't be removed yet, and then
+ * perform the network request without holding the lock, and
+ * then go back and unpin them?
+ *
+ * XXX Would it be possible to detect connections that match,
+ * and collect multiple cancel packets that we send together
+ * in one network request?
+ */
+ PQcancelBlocking(entry->cancel);
+ }
+ if (remove)
+ {
+ dlist_delete(&entry->node);
+ PQcancelFinish(entry->cancel);
+ free(entry);
+ }
+ if (connection)
+ break;
+ }
+ }
+ pg_mtx_unlock(&cs->mutex);
+
+ return count;
+}
+
+/* Cancel a connection, without removing it. */
+bool
+cancel_set_cancel(cancel_set *cs, PGconn *connection)
+{
+ return cancel_set_scan(cs, connection, true, false) != 0;
+}
+
+/* Cancel and remove a connection. */
+bool
+cancel_set_cancel_and_remove(cancel_set *cs, PGconn *connection)
+{
+ return cancel_set_scan(cs, connection, true, true) != 0;
+}
+
+/* Remove a connection from the set. */
+bool
+cancel_set_remove(cancel_set *cs, PGconn *connection)
+{
+ return cancel_set_scan(cs, connection, false, true) != 0;
+}
+
+/* Cancel all connections in the set, without removing them. */
+int
+cancel_set_cancel_all(cancel_set *cs)
+{
+ return cancel_set_scan(cs, NULL, true, false);
+}
+
+/* Cancel and remove all connections in the set. */
+int
+cancel_set_cancel_and_remove_all(cancel_set *cs)
+{
+ return cancel_set_scan(cs, NULL, true, true);
+}
+
+/* Remove all connections from the set. */
+int
+cancel_set_remove_all(cancel_set *cs)
+{
+ return cancel_set_scan(cs, NULL, false, true);
+}
diff --git a/src/fe_utils/meson.build b/src/fe_utils/meson.build
index 86befca192e..b84edde2dfd 100644
--- a/src/fe_utils/meson.build
+++ b/src/fe_utils/meson.build
@@ -8,6 +8,7 @@ fe_utils_sources = files(
'astreamer_tar.c',
'astreamer_zstd.c',
'cancel.c',
+ 'cancel_set.c',
'conditional.c',
'connect_utils.c',
'mbprint.c',
diff --git a/src/include/fe_utils/cancel_set.h b/src/include/fe_utils/cancel_set.h
new file mode 100644
index 00000000000..b75f0b7cd04
--- /dev/null
+++ b/src/include/fe_utils/cancel_set.h
@@ -0,0 +1,20 @@
+#ifndef CANCEL_SET
+#define CANCEL_SET
+
+#include "libpq-fe.h"
+
+typedef struct cancel_set cancel_set;
+
+extern cancel_set *cancel_set_alloc(void);
+extern void cancel_set_free(cancel_set *e);
+
+extern bool cancel_set_add(cancel_set *e, PGconn *connection);
+extern bool cancel_set_remove(cancel_set *e, PGconn *connection);
+extern bool cancel_set_cancel(cancel_set *e, PGconn *connection);
+extern bool cancel_set_cancel_and_remove(cancel_set *e, PGconn *connection);
+
+extern int cancel_set_remove_all(cancel_set *e);
+extern int cancel_set_cancel_all(cancel_set *e);
+extern int cancel_set_cancel_and_remove_all(cancel_set *e);
+
+#endif
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index e830092ee62..2ece9b8a92e 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3667,6 +3667,8 @@ btree_gin_leftmost_function
build_simple_rel_hook_type
bytea
cached_re_str
+cancel_set
+cancel_set_entry
canonicalize_state
cashKEY
catalogid_hash
--
2.47.3