Age Owner TLA 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-2023, 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
1224 michael 77 CBC 169576 : 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 169576 : oldCancelConn = cancelConn;
87 :
88 : /* be sure handle_sigint doesn't use pointer while freeing */
89 169576 : cancelConn = NULL;
90 :
91 169576 : if (oldCancelConn != NULL)
92 640 : PQfreeCancel(oldCancelConn);
93 :
94 169576 : cancelConn = PQgetCancel(conn);
95 :
96 : #ifdef WIN32
97 : LeaveCriticalSection(&cancelConnLock);
98 : #endif
99 169576 : }
100 :
101 : /*
102 : * ResetCancelConn
103 : *
104 : * Free the current cancel connection, if any, and set to NULL.
105 : */
106 : void
107 169569 : ResetCancelConn(void)
108 : {
109 : PGcancel *oldCancelConn;
110 :
111 : #ifdef WIN32
112 : EnterCriticalSection(&cancelConnLock);
113 : #endif
114 :
115 169569 : oldCancelConn = cancelConn;
116 :
117 : /* be sure handle_sigint doesn't use pointer while freeing */
118 169569 : cancelConn = NULL;
119 :
120 169569 : if (oldCancelConn != NULL)
121 168907 : PQfreeCancel(oldCancelConn);
122 :
123 : #ifdef WIN32
124 : LeaveCriticalSection(&cancelConnLock);
125 : #endif
126 169569 : }
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 1 : int save_errno = errno;
156 : char errbuf[256];
157 :
1036 tgl 158 1 : CancelRequested = true;
159 :
1224 michael 160 1 : if (cancel_callback != NULL)
161 1 : cancel_callback();
162 :
163 : /* Send QueryCancel if we are processing a database query */
164 1 : if (cancelConn != NULL)
165 : {
166 1 : if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
167 : {
447 tgl 168 1 : write_stderr(cancel_sent_msg);
169 : }
170 : else
171 : {
447 tgl 172 UBC 0 : write_stderr(cancel_not_sent_msg);
1224 michael 173 0 : write_stderr(errbuf);
174 : }
175 : }
176 :
1224 michael 177 CBC 1 : errno = save_errno; /* just in case the write changed it */
178 1 : }
179 :
180 : /*
181 : * setup_cancel_handler
182 : *
183 : * Register query cancellation callback for SIGINT.
184 : */
185 : void
201 pg 186 GNC 6290 : setup_cancel_handler(void (*query_cancel_callback) (void))
187 : {
188 6290 : cancel_callback = query_cancel_callback;
447 tgl 189 CBC 6290 : cancel_sent_msg = _("Cancel request sent\n");
190 6290 : cancel_not_sent_msg = _("Could not send cancel request: ");
191 :
1224 michael 192 6290 : pqsignal(SIGINT, handle_sigint);
193 6290 : }
194 :
195 : #else /* WIN32 */
196 :
197 : static BOOL WINAPI
198 : consoleHandler(DWORD dwCtrlType)
199 : {
200 : char errbuf[256];
201 :
202 : if (dwCtrlType == CTRL_C_EVENT ||
203 : dwCtrlType == CTRL_BREAK_EVENT)
204 : {
205 : CancelRequested = true;
206 :
207 : if (cancel_callback != NULL)
208 : cancel_callback();
209 :
210 : /* Send QueryCancel if we are processing a database query */
211 : EnterCriticalSection(&cancelConnLock);
212 : if (cancelConn != NULL)
213 : {
214 : if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
215 : {
216 : write_stderr(cancel_sent_msg);
217 : }
218 : else
219 : {
220 : write_stderr(cancel_not_sent_msg);
221 : write_stderr(errbuf);
222 : }
223 : }
224 :
225 : LeaveCriticalSection(&cancelConnLock);
226 :
227 : return TRUE;
228 : }
229 : else
230 : /* Return FALSE for any signals not being handled */
231 : return FALSE;
232 : }
233 :
234 : void
235 : setup_cancel_handler(void (*callback) (void))
236 : {
237 : cancel_callback = callback;
238 : cancel_sent_msg = _("Cancel request sent\n");
239 : cancel_not_sent_msg = _("Could not send cancel request: ");
240 :
241 : InitializeCriticalSection(&cancelConnLock);
242 :
243 : SetConsoleCtrlHandler(consoleHandler, TRUE);
244 : }
245 :
246 : #endif /* WIN32 */
|