Line data Source code
1 : /*------------------------------------------------------------------------
2 : *
3 : * Query cancellation support for frontend code
4 : *
5 : * Assorted utility functions to control query cancellation with signal
6 : * handler for SIGINT.
7 : *
8 : *
9 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
10 : * Portions Copyright (c) 1994, Regents of the University of California
11 : *
12 : * src/fe_utils/cancel.c
13 : *
14 : *------------------------------------------------------------------------
15 : */
16 :
17 : #include "postgres_fe.h"
18 :
19 : #include <unistd.h>
20 :
21 : #include "common/connect.h"
22 : #include "fe_utils/cancel.h"
23 : #include "fe_utils/string_utils.h"
24 :
25 :
26 : /*
27 : * Write a simple string to stderr --- must be safe in a signal handler.
28 : * We ignore the write() result since there's not much we could do about it.
29 : * Certain compilers make that harder than it ought to be.
30 : */
31 : #define write_stderr(str) \
32 : do { \
33 : const char *str_ = (str); \
34 : int rc_; \
35 : rc_ = write(fileno(stderr), str_, strlen(str_)); \
36 : (void) rc_; \
37 : } while (0)
38 :
39 : /*
40 : * Contains all the information needed to cancel a query issued from
41 : * a database connection to the backend.
42 : */
43 : static PGcancel *volatile cancelConn = NULL;
44 :
45 : /*
46 : * Predetermined localized error strings --- needed to avoid trying
47 : * to call gettext() from a signal handler.
48 : */
49 : static const char *cancel_sent_msg = NULL;
50 : static const char *cancel_not_sent_msg = NULL;
51 :
52 : /*
53 : * CancelRequested is set when we receive SIGINT (or local equivalent).
54 : * There is no provision in this module for resetting it; but applications
55 : * might choose to clear it after successfully recovering from a cancel.
56 : * Note that there is no guarantee that we successfully sent a Cancel request,
57 : * or that the request will have any effect if we did send it.
58 : */
59 : volatile sig_atomic_t CancelRequested = false;
60 :
61 : #ifdef WIN32
62 : static CRITICAL_SECTION cancelConnLock;
63 : #endif
64 :
65 : /*
66 : * Additional callback for cancellations.
67 : */
68 : static void (*cancel_callback) (void) = NULL;
69 :
70 :
71 : /*
72 : * SetCancelConn
73 : *
74 : * Set cancelConn to point to the current database connection.
75 : */
76 : void
77 229119 : SetCancelConn(PGconn *conn)
78 : {
79 : PGcancel *oldCancelConn;
80 :
81 : #ifdef WIN32
82 : EnterCriticalSection(&cancelConnLock);
83 : #endif
84 :
85 : /* Free the old one if we have one */
86 229119 : oldCancelConn = cancelConn;
87 :
88 : /* be sure handle_sigint doesn't use pointer while freeing */
89 229119 : cancelConn = NULL;
90 :
91 229119 : if (oldCancelConn != NULL)
92 786 : PQfreeCancel(oldCancelConn);
93 :
94 229119 : cancelConn = PQgetCancel(conn);
95 :
96 : #ifdef WIN32
97 : LeaveCriticalSection(&cancelConnLock);
98 : #endif
99 229119 : }
100 :
101 : /*
102 : * ResetCancelConn
103 : *
104 : * Free the current cancel connection, if any, and set to NULL.
105 : */
106 : void
107 229102 : ResetCancelConn(void)
108 : {
109 : PGcancel *oldCancelConn;
110 :
111 : #ifdef WIN32
112 : EnterCriticalSection(&cancelConnLock);
113 : #endif
114 :
115 229102 : oldCancelConn = cancelConn;
116 :
117 : /* be sure handle_sigint doesn't use pointer while freeing */
118 229102 : cancelConn = NULL;
119 :
120 229102 : if (oldCancelConn != NULL)
121 228294 : PQfreeCancel(oldCancelConn);
122 :
123 : #ifdef WIN32
124 : LeaveCriticalSection(&cancelConnLock);
125 : #endif
126 229102 : }
127 :
128 :
129 : /*
130 : * Code to support query cancellation
131 : *
132 : * Note that sending the cancel directly from the signal handler is safe
133 : * because PQcancel() is written to make it so. We use write() to report
134 : * to stderr because it's better to use simple facilities in a signal
135 : * handler.
136 : *
137 : * On Windows, the signal canceling happens on a separate thread, because
138 : * that's how SetConsoleCtrlHandler works. The PQcancel function is safe
139 : * for this (unlike PQrequestCancel). However, a CRITICAL_SECTION is required
140 : * to protect the PGcancel structure against being changed while the signal
141 : * thread is using it.
142 : */
143 :
144 : #ifndef WIN32
145 :
146 : /*
147 : * handle_sigint
148 : *
149 : * Handle interrupt signals by canceling the current command, if cancelConn
150 : * is set.
151 : */
152 : static void
153 1 : handle_sigint(SIGNAL_ARGS)
154 : {
155 : char errbuf[256];
156 :
157 1 : CancelRequested = true;
158 :
159 1 : if (cancel_callback != NULL)
160 1 : cancel_callback();
161 :
162 : /* Send QueryCancel if we are processing a database query */
163 1 : if (cancelConn != NULL)
164 : {
165 1 : if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
166 : {
167 1 : write_stderr(cancel_sent_msg);
168 : }
169 : else
170 : {
171 0 : write_stderr(cancel_not_sent_msg);
172 0 : write_stderr(errbuf);
173 : }
174 : }
175 1 : }
176 :
177 : /*
178 : * setup_cancel_handler
179 : *
180 : * Register query cancellation callback for SIGINT.
181 : */
182 : void
183 9807 : setup_cancel_handler(void (*query_cancel_callback) (void))
184 : {
185 9807 : cancel_callback = query_cancel_callback;
186 9807 : cancel_sent_msg = _("Cancel request sent\n");
187 9807 : cancel_not_sent_msg = _("Could not send cancel request: ");
188 :
189 9807 : pqsignal(SIGINT, handle_sigint);
190 9807 : }
191 :
192 : #else /* WIN32 */
193 :
194 : static BOOL WINAPI
195 : consoleHandler(DWORD dwCtrlType)
196 : {
197 : char errbuf[256];
198 :
199 : if (dwCtrlType == CTRL_C_EVENT ||
200 : dwCtrlType == CTRL_BREAK_EVENT)
201 : {
202 : CancelRequested = true;
203 :
204 : if (cancel_callback != NULL)
205 : cancel_callback();
206 :
207 : /* Send QueryCancel if we are processing a database query */
208 : EnterCriticalSection(&cancelConnLock);
209 : if (cancelConn != NULL)
210 : {
211 : if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
212 : {
213 : write_stderr(cancel_sent_msg);
214 : }
215 : else
216 : {
217 : write_stderr(cancel_not_sent_msg);
218 : write_stderr(errbuf);
219 : }
220 : }
221 :
222 : LeaveCriticalSection(&cancelConnLock);
223 :
224 : return TRUE;
225 : }
226 : else
227 : /* Return FALSE for any signals not being handled */
228 : return FALSE;
229 : }
230 :
231 : void
232 : setup_cancel_handler(void (*callback) (void))
233 : {
234 : cancel_callback = callback;
235 : cancel_sent_msg = _("Cancel request sent\n");
236 : cancel_not_sent_msg = _("Could not send cancel request: ");
237 :
238 : InitializeCriticalSection(&cancelConnLock);
239 :
240 : SetConsoleCtrlHandler(consoleHandler, TRUE);
241 : }
242 :
243 : #endif /* WIN32 */
|