PostgreSQL Source Code  git master
cancel.c
Go to the documentation of this file.
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-2020, 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 "fe_utils/cancel.h"
22 #include "fe_utils/connect.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  * CancelRequested tracks if a cancellation request has completed after
47  * a signal interruption. Note that if cancelConn is not set, in short
48  * if SetCancelConn() was never called or if ResetCancelConn() freed
49  * the cancellation object, then CancelRequested is switched to true after
50  * all cancellation attempts.
51  */
52 volatile sig_atomic_t CancelRequested = false;
53 
54 #ifdef WIN32
55 static CRITICAL_SECTION cancelConnLock;
56 #endif
57 
58 /*
59  * Additional callback for cancellations.
60  */
61 static void (*cancel_callback) (void) = NULL;
62 
63 
64 /*
65  * SetCancelConn
66  *
67  * Set cancelConn to point to the current database connection.
68  */
69 void
71 {
72  PGcancel *oldCancelConn;
73 
74 #ifdef WIN32
75  EnterCriticalSection(&cancelConnLock);
76 #endif
77 
78  /* Free the old one if we have one */
79  oldCancelConn = cancelConn;
80 
81  /* be sure handle_sigint doesn't use pointer while freeing */
82  cancelConn = NULL;
83 
84  if (oldCancelConn != NULL)
85  PQfreeCancel(oldCancelConn);
86 
87  cancelConn = PQgetCancel(conn);
88 
89 #ifdef WIN32
90  LeaveCriticalSection(&cancelConnLock);
91 #endif
92 }
93 
94 /*
95  * ResetCancelConn
96  *
97  * Free the current cancel connection, if any, and set to NULL.
98  */
99 void
101 {
102  PGcancel *oldCancelConn;
103 
104 #ifdef WIN32
105  EnterCriticalSection(&cancelConnLock);
106 #endif
107 
108  oldCancelConn = cancelConn;
109 
110  /* be sure handle_sigint doesn't use pointer while freeing */
111  cancelConn = NULL;
112 
113  if (oldCancelConn != NULL)
114  PQfreeCancel(oldCancelConn);
115 
116 #ifdef WIN32
117  LeaveCriticalSection(&cancelConnLock);
118 #endif
119 }
120 
121 
122 /*
123  * Code to support query cancellation
124  *
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
128  * handler.
129  *
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.
135  */
136 
137 #ifndef WIN32
138 
139 /*
140  * handle_sigint
141  *
142  * Handle interrupt signals by canceling the current command, if cancelConn
143  * is set.
144  */
145 static void
147 {
148  int save_errno = errno;
149  char errbuf[256];
150 
151  if (cancel_callback != NULL)
152  cancel_callback();
153 
154  /* Send QueryCancel if we are processing a database query */
155  if (cancelConn != NULL)
156  {
157  if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
158  {
159  CancelRequested = true;
160  write_stderr(_("Cancel request sent\n"));
161  }
162  else
163  {
164  write_stderr(_("Could not send cancel request: "));
165  write_stderr(errbuf);
166  }
167  }
168  else
169  CancelRequested = true;
170 
171  errno = save_errno; /* just in case the write changed it */
172 }
173 
174 /*
175  * setup_cancel_handler
176  *
177  * Register query cancellation callback for SIGINT.
178  */
179 void
181 {
183  pqsignal(SIGINT, handle_sigint);
184 }
185 
186 #else /* WIN32 */
187 
188 static BOOL WINAPI
189 consoleHandler(DWORD dwCtrlType)
190 {
191  char errbuf[256];
192 
193  if (dwCtrlType == CTRL_C_EVENT ||
194  dwCtrlType == CTRL_BREAK_EVENT)
195  {
196  if (cancel_callback != NULL)
197  cancel_callback();
198 
199  /* Send QueryCancel if we are processing a database query */
200  EnterCriticalSection(&cancelConnLock);
201  if (cancelConn != NULL)
202  {
203  if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
204  {
205  write_stderr(_("Cancel request sent\n"));
206  CancelRequested = true;
207  }
208  else
209  {
210  write_stderr(_("Could not send cancel request: %s"));
211  write_stderr(errbuf);
212  }
213  }
214  else
215  CancelRequested = true;
216 
217  LeaveCriticalSection(&cancelConnLock);
218 
219  return TRUE;
220  }
221  else
222  /* Return FALSE for any signals not being handled */
223  return FALSE;
224 }
225 
226 void
227 setup_cancel_handler(void (*callback) (void))
228 {
230 
231  InitializeCriticalSection(&cancelConnLock);
232 
233  SetConsoleCtrlHandler(consoleHandler, TRUE);
234 }
235 
236 #endif /* WIN32 */
static void(* cancel_callback)(void)
Definition: cancel.c:61
void ResetCancelConn(void)
Definition: cancel.c:100
void PQfreeCancel(PGcancel *cancel)
Definition: fe-connect.c:4329
PGconn * conn
Definition: streamutil.c:54
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
Definition: test_ifaddrs.c:48
PGcancel * PQgetCancel(PGconn *conn)
Definition: fe-connect.c:4306
void SetCancelConn(PGconn *conn)
Definition: cancel.c:70
#define write_stderr(str)
Definition: cancel.c:31
pqsigfunc pqsignal(int signum, pqsigfunc handler)
Definition: signal.c:170
#define SIGNAL_ARGS
Definition: c.h:1296
int PQcancel(PGcancel *cancel, char *errbuf, int errbufsize)
Definition: fe-connect.c:4461
volatile sig_atomic_t CancelRequested
Definition: cancel.c:52
static PGcancel *volatile cancelConn
Definition: cancel.c:43
void setup_cancel_handler(void(*callback)(void))
Definition: cancel.c:180
static void handle_sigint(SIGNAL_ARGS)
Definition: cancel.c:146
#define _(x)
Definition: elog.c:88