summaryrefslogtreecommitdiff
path: root/src/bin/psql/common.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2004-10-30 23:11:27 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2004-10-30 23:11:27 +0000
commit44e8a968e33f04974edc8ac9f184d009b036bbb7 (patch)
treedb4ddd8e717a63555273096289f4fa56f7a817ed /src/bin/psql/common.c
parent80559fa9e9657ecfdb92a210971b8d0aa6e82e39 (diff)
Invent a new, more thread-safe version of PQrequestCancel, called PQcancel.
Use this new function in psql. Implement query cancellation in psql for Windows. Code by Magnus Hagander, documentation and minor editorialization by Tom Lane.
Diffstat (limited to 'src/bin/psql/common.c')
-rw-r--r--src/bin/psql/common.c99
1 files changed, 87 insertions, 12 deletions
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index 0ca6fa4f557..0089d5d2c81 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2004, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.92 2004/10/10 23:37:40 neilc Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.93 2004/10/30 23:10:50 tgl Exp $
*/
#include "postgres_fe.h"
#include "common.h"
@@ -223,23 +223,33 @@ NoticeProcessor(void *arg, const char *message)
*
* Before we start a query, we enable a SIGINT signal catcher that sends a
* cancel request to the backend. Note that sending the cancel directly from
- * the signal handler is safe because PQrequestCancel() is written to make it
- * so. We use write() to print to stdout because it's better to use simple
+ * the signal handler is safe because PQcancel() is written to make it
+ * so. We use write() to print to stderr because it's better to use simple
* facilities in a signal handler.
+ *
+ * On win32, the signal cancelling happens on a separate thread, because
+ * that's how SetConsoleCtrlHandler works. The PQcancel function is safe
+ * for this (unlike PQrequestCancel). However, a CRITICAL_SECTION is required
+ * to protect the PGcancel structure against being changed while the other
+ * thread is using it.
*/
-static PGconn *volatile cancelConn = NULL;
+static PGcancel *cancelConn = NULL;
+#ifdef WIN32
+static CRITICAL_SECTION cancelConnLock;
+#endif
volatile bool cancel_pressed = false;
+#define write_stderr(str) write(fileno(stderr), str, strlen(str))
-#ifndef WIN32
-#define write_stderr(String) write(fileno(stderr), String, strlen(String))
+#ifndef WIN32
void
handle_sigint(SIGNAL_ARGS)
{
int save_errno = errno;
+ char errbuf[256];
/* Don't muck around if prompting for a password. */
if (prompt_state)
@@ -250,17 +260,60 @@ handle_sigint(SIGNAL_ARGS)
cancel_pressed = true;
- if (PQrequestCancel(cancelConn))
+ if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
write_stderr("Cancel request sent\n");
else
{
write_stderr("Could not send cancel request: ");
- write_stderr(PQerrorMessage(cancelConn));
+ write_stderr(errbuf);
}
errno = save_errno; /* just in case the write changed it */
}
-#endif /* not WIN32 */
+#else /* WIN32 */
+
+static BOOL WINAPI
+consoleHandler(DWORD dwCtrlType)
+{
+ char errbuf[256];
+
+ if (dwCtrlType == CTRL_C_EVENT ||
+ dwCtrlType == CTRL_BREAK_EVENT)
+ {
+ if (prompt_state)
+ return TRUE;
+
+ /* Perform query cancel */
+ EnterCriticalSection(&cancelConnLock);
+ if (cancelConn != NULL)
+ {
+ cancel_pressed = true;
+
+ if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
+ write_stderr("Cancel request sent\n");
+ else
+ {
+ write_stderr("Could not send cancel request: ");
+ write_stderr(errbuf);
+ }
+ }
+ LeaveCriticalSection(&cancelConnLock);
+
+ return TRUE;
+ }
+ else
+ /* Return FALSE for any signals not being handled */
+ return FALSE;
+}
+
+void
+setup_cancel_handler(void)
+{
+ InitializeCriticalSection(&cancelConnLock);
+ SetConsoleCtrlHandler(consoleHandler, TRUE);
+}
+
+#endif /* WIN32 */
/* ConnectionUp
@@ -327,20 +380,42 @@ CheckConnection(void)
static void
SetCancelConn(void)
{
- cancelConn = pset.db;
+#ifdef WIN32
+ EnterCriticalSection(&cancelConnLock);
+#endif
+
+ /* Free the old one if we have one */
+ if (cancelConn != NULL)
+ PQfreeCancel(cancelConn);
+
+ cancelConn = PQgetCancel(pset.db);
+
+#ifdef WIN32
+ LeaveCriticalSection(&cancelConnLock);
+#endif
}
/*
* ResetCancelConn
*
- * Set cancelConn to NULL. I don't know what this means exactly, but it saves
- * having to export the variable.
+ * Free the current cancel connection, if any, and set to NULL.
*/
void
ResetCancelConn(void)
{
+#ifdef WIN32
+ EnterCriticalSection(&cancelConnLock);
+#endif
+
+ if (cancelConn)
+ PQfreeCancel(cancelConn);
+
cancelConn = NULL;
+
+#ifdef WIN32
+ LeaveCriticalSection(&cancelConnLock);
+#endif
}