1 /*------------------------------------------------------------------------
3 * Query cancellation support for frontend code
5 * Assorted utility functions to control query cancellation with signal
9 * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
10 * Portions Copyright (c) 1994, Regents of the University of California
12 * src/fe-utils/cancel.c
14 *------------------------------------------------------------------------
17 #include "postgres_fe.h"
21 #include "common/connect.h"
22 #include "fe_utils/cancel.h"
23 #include "fe_utils/string_utils.h"
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.
31 #define write_stderr(str) \
33 const char *str_ = (str); \
35 rc_ = write(fileno(stderr), str_, strlen(str_)); \
40 * Contains all the information needed to cancel a query issued from
41 * a database connection to the backend.
43 static PGcancel
*volatile cancelConn
= NULL
;
46 * CancelRequested is set when we receive SIGINT (or local equivalent).
47 * There is no provision in this module for resetting it; but applications
48 * might choose to clear it after successfully recovering from a cancel.
49 * Note that there is no guarantee that we successfully sent a Cancel request,
50 * or that the request will have any effect if we did send it.
52 volatile sig_atomic_t CancelRequested
= false;
55 static CRITICAL_SECTION cancelConnLock
;
59 * Additional callback for cancellations.
61 static void (*cancel_callback
) (void) = NULL
;
67 * Set cancelConn to point to the current database connection.
70 SetCancelConn(PGconn
*conn
)
72 PGcancel
*oldCancelConn
;
75 EnterCriticalSection(&cancelConnLock
);
78 /* Free the old one if we have one */
79 oldCancelConn
= cancelConn
;
81 /* be sure handle_sigint doesn't use pointer while freeing */
84 if (oldCancelConn
!= NULL
)
85 PQfreeCancel(oldCancelConn
);
87 cancelConn
= PQgetCancel(conn
);
90 LeaveCriticalSection(&cancelConnLock
);
97 * Free the current cancel connection, if any, and set to NULL.
100 ResetCancelConn(void)
102 PGcancel
*oldCancelConn
;
105 EnterCriticalSection(&cancelConnLock
);
108 oldCancelConn
= cancelConn
;
110 /* be sure handle_sigint doesn't use pointer while freeing */
113 if (oldCancelConn
!= NULL
)
114 PQfreeCancel(oldCancelConn
);
117 LeaveCriticalSection(&cancelConnLock
);
123 * Code to support query cancellation
125 * Note that sending the cancel directly from the signal handler is safe
126 * because PQcancel() is written to make it so. We use write() to report
127 * to stderr because it's better to use simple facilities in a signal
130 * On Windows, the signal canceling happens on a separate thread, because
131 * that's how SetConsoleCtrlHandler works. The PQcancel function is safe
132 * for this (unlike PQrequestCancel). However, a CRITICAL_SECTION is required
133 * to protect the PGcancel structure against being changed while the signal
134 * thread is using it.
142 * Handle interrupt signals by canceling the current command, if cancelConn
146 handle_sigint(SIGNAL_ARGS
)
148 int save_errno
= errno
;
151 CancelRequested
= true;
153 if (cancel_callback
!= NULL
)
156 /* Send QueryCancel if we are processing a database query */
157 if (cancelConn
!= NULL
)
159 if (PQcancel(cancelConn
, errbuf
, sizeof(errbuf
)))
161 write_stderr(_("Cancel request sent\n"));
165 write_stderr(_("Could not send cancel request: "));
166 write_stderr(errbuf
);
170 errno
= save_errno
; /* just in case the write changed it */
174 * setup_cancel_handler
176 * Register query cancellation callback for SIGINT.
179 setup_cancel_handler(void (*callback
) (void))
181 cancel_callback
= callback
;
182 pqsignal(SIGINT
, handle_sigint
);
188 consoleHandler(DWORD dwCtrlType
)
192 if (dwCtrlType
== CTRL_C_EVENT
||
193 dwCtrlType
== CTRL_BREAK_EVENT
)
195 CancelRequested
= true;
197 if (cancel_callback
!= NULL
)
200 /* Send QueryCancel if we are processing a database query */
201 EnterCriticalSection(&cancelConnLock
);
202 if (cancelConn
!= NULL
)
204 if (PQcancel(cancelConn
, errbuf
, sizeof(errbuf
)))
206 write_stderr(_("Cancel request sent\n"));
210 write_stderr(_("Could not send cancel request: "));
211 write_stderr(errbuf
);
215 LeaveCriticalSection(&cancelConnLock
);
220 /* Return FALSE for any signals not being handled */
225 setup_cancel_handler(void (*callback
) (void))
227 cancel_callback
= callback
;
229 InitializeCriticalSection(&cancelConnLock
);
231 SetConsoleCtrlHandler(consoleHandler
, TRUE
);