aboutsummaryrefslogtreecommitdiff
path: root/src/backend/tcop/postgres.c
diff options
context:
space:
mode:
authorAndres Freund <andres@anarazel.de>2015-02-03 22:25:20 +0100
committerAndres Freund <andres@anarazel.de>2015-02-03 22:25:20 +0100
commit4f85fde8eb860f263384fffdca660e16e77c7f76 (patch)
tree9d9d18a368f4e7db987140d6dfd384b9e2e51b0a /src/backend/tcop/postgres.c
parent387da18874afa17156ee3af63766f17efb53c4b9 (diff)
downloadpostgresql-4f85fde8eb860f263384fffdca660e16e77c7f76.tar.gz
postgresql-4f85fde8eb860f263384fffdca660e16e77c7f76.zip
Introduce and use infrastructure for interrupt processing during client reads.
Up to now large swathes of backend code ran inside signal handlers while reading commands from the client, to allow for speedy reaction to asynchronous events. Most prominently shared invalidation and NOTIFY handling. That means that complex code like the starting/stopping of transactions is run in signal handlers... The required code was fragile and verbose, and is likely to contain bugs. That approach also severely limited what could be done while communicating with the client. As the read might be from within openssl it wasn't safely possible to trigger an error, e.g. to cancel a backend in idle-in-transaction state. We did that in some cases, namely fatal errors, nonetheless. Now that FE/BE communication in the backend employs non-blocking sockets and latches to block, we can quite simply interrupt reads from signal handlers by setting the latch. That allows us to signal an interrupted read, which is supposed to be retried after returning from within the ssl library. As signal handlers now only need to set the latch to guarantee timely interrupt processing, remove a fair amount of complicated & fragile code from async.c and sinval.c. We could now actually start to process some kinds of interrupts, like sinval ones, more often that before, but that seems better done separately. This work will hopefully allow to handle cases like being blocked by sending data, interrupting idle transactions and similar to be implemented without too much effort. In addition to allowing getting rid of ImmediateInterruptOK, that is. Author: Andres Freund Reviewed-By: Heikki Linnakangas
Diffstat (limited to 'src/backend/tcop/postgres.c')
-rw-r--r--src/backend/tcop/postgres.c106
1 files changed, 40 insertions, 66 deletions
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 556e5633284..bcc4f243134 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -301,17 +301,25 @@ InteractiveBackend(StringInfo inBuf)
* interactive_getc -- collect one character from stdin
*
* Even though we are not reading from a "client" process, we still want to
- * respond to signals, particularly SIGTERM/SIGQUIT. Hence we must use
- * prepare_for_client_read and client_read_ended.
+ * respond to signals, particularly SIGTERM/SIGQUIT.
*/
static int
interactive_getc(void)
{
int c;
- prepare_for_client_read();
+ /*
+ * This will not process catchup interrupts or notifications while
+ * reading. But those can't really be relevant for a standalone backend
+ * anyway. To properly handle SIGTERM there's a hack in die() that
+ * directly processes interrupts at this stage...
+ */
+ CHECK_FOR_INTERRUPTS();
+
c = getc(stdin);
- client_read_ended();
+
+ ProcessClientReadInterrupt();
+
return c;
}
@@ -513,53 +521,33 @@ ReadCommand(StringInfo inBuf)
}
/*
- * prepare_for_client_read -- set up to possibly block on client input
+ * ProcessClientReadInterrupt() - Process interrupts specific to client reads
*
- * This must be called immediately before any low-level read from the
- * client connection. It is necessary to do it at a sufficiently low level
- * that there won't be any other operations except the read kernel call
- * itself between this call and the subsequent client_read_ended() call.
- * In particular there mustn't be use of malloc() or other potentially
- * non-reentrant libc functions. This restriction makes it safe for us
- * to allow interrupt service routines to execute nontrivial code while
- * we are waiting for input.
- */
-void
-prepare_for_client_read(void)
-{
- if (DoingCommandRead)
- {
- /* Enable immediate processing of asynchronous signals */
- EnableNotifyInterrupt();
- EnableCatchupInterrupt();
-
- /* Allow die interrupts to be processed while waiting */
- ImmediateInterruptOK = true;
-
- /* And don't forget to detect one that already arrived */
- CHECK_FOR_INTERRUPTS();
- }
-}
-
-/*
- * client_read_ended -- get out of the client-input state
+ * This is called just after low-level reads. That might be after the read
+ * finished successfully, or it was interrupted via interrupt.
*
- * This is called just after low-level reads. It must preserve errno!
+ * Must preserve errno!
*/
void
-client_read_ended(void)
+ProcessClientReadInterrupt(void)
{
+ int save_errno = errno;
+
if (DoingCommandRead)
{
- int save_errno = errno;
-
- ImmediateInterruptOK = false;
+ /* Check for general interrupts that arrived while reading */
+ CHECK_FOR_INTERRUPTS();
- DisableNotifyInterrupt();
- DisableCatchupInterrupt();
+ /* Process sinval catchup interrupts that happened while reading */
+ if (catchupInterruptPending)
+ ProcessCatchupInterrupt();
- errno = save_errno;
+ /* Process sinval catchup interrupts that happened while reading */
+ if (notifyInterruptPending)
+ ProcessNotifyInterrupt();
}
+
+ errno = save_errno;
}
@@ -2626,6 +2614,15 @@ die(SIGNAL_ARGS)
/* If we're still here, waken anything waiting on the process latch */
SetLatch(MyLatch);
+ /*
+ * If we're in single user mode, we want to quit immediately - we can't
+ * rely on latches as they wouldn't work when stdin/stdout is a
+ * file. Rather ugly, but it's unlikely to be worthwhile to invest much
+ * more effort just for the benefit of single user mode.
+ */
+ if (DoingCommandRead && whereToSendOutput != DestRemote)
+ ProcessInterrupts();
+
errno = save_errno;
}
@@ -2834,8 +2831,6 @@ ProcessInterrupts(void)
QueryCancelPending = false; /* ProcDie trumps QueryCancel */
ImmediateInterruptOK = false; /* not idle anymore */
LockErrorCleanup();
- DisableNotifyInterrupt();
- DisableCatchupInterrupt();
/* As in quickdie, don't risk sending to client during auth */
if (ClientAuthInProgress && whereToSendOutput == DestRemote)
whereToSendOutput = DestNone;
@@ -2871,8 +2866,6 @@ ProcessInterrupts(void)
QueryCancelPending = false; /* lost connection trumps QueryCancel */
ImmediateInterruptOK = false; /* not idle anymore */
LockErrorCleanup();
- DisableNotifyInterrupt();
- DisableCatchupInterrupt();
/* don't send to client, we already know the connection to be dead. */
whereToSendOutput = DestNone;
ereport(FATAL,
@@ -2892,8 +2885,6 @@ ProcessInterrupts(void)
ImmediateInterruptOK = false; /* not idle anymore */
RecoveryConflictPending = false;
LockErrorCleanup();
- DisableNotifyInterrupt();
- DisableCatchupInterrupt();
pgstat_report_recovery_conflict(RecoveryConflictReason);
ereport(FATAL,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
@@ -2926,8 +2917,6 @@ ProcessInterrupts(void)
{
ImmediateInterruptOK = false; /* not idle anymore */
LockErrorCleanup();
- DisableNotifyInterrupt();
- DisableCatchupInterrupt();
/* As in quickdie, don't risk sending to client during auth */
if (whereToSendOutput == DestRemote)
whereToSendOutput = DestNone;
@@ -2945,8 +2934,6 @@ ProcessInterrupts(void)
ImmediateInterruptOK = false; /* not idle anymore */
(void) get_timeout_indicator(STATEMENT_TIMEOUT, true);
LockErrorCleanup();
- DisableNotifyInterrupt();
- DisableCatchupInterrupt();
ereport(ERROR,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("canceling statement due to lock timeout")));
@@ -2955,8 +2942,6 @@ ProcessInterrupts(void)
{
ImmediateInterruptOK = false; /* not idle anymore */
LockErrorCleanup();
- DisableNotifyInterrupt();
- DisableCatchupInterrupt();
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling statement due to statement timeout")));
@@ -2965,8 +2950,6 @@ ProcessInterrupts(void)
{
ImmediateInterruptOK = false; /* not idle anymore */
LockErrorCleanup();
- DisableNotifyInterrupt();
- DisableCatchupInterrupt();
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling autovacuum task")));
@@ -2976,8 +2959,6 @@ ProcessInterrupts(void)
ImmediateInterruptOK = false; /* not idle anymore */
RecoveryConflictPending = false;
LockErrorCleanup();
- DisableNotifyInterrupt();
- DisableCatchupInterrupt();
pgstat_report_recovery_conflict(RecoveryConflictReason);
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
@@ -2994,13 +2975,12 @@ ProcessInterrupts(void)
{
ImmediateInterruptOK = false; /* not idle anymore */
LockErrorCleanup();
- DisableNotifyInterrupt();
- DisableCatchupInterrupt();
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling statement due to user request")));
}
}
+
/* If we get here, do nothing (probably, QueryCancelPending was reset) */
}
@@ -3843,14 +3823,8 @@ PostgresMain(int argc, char *argv[],
disable_all_timeouts(false);
QueryCancelPending = false; /* second to avoid race condition */
- /*
- * Turn off these interrupts too. This is only needed here and not in
- * other exception-catching places since these interrupts are only
- * enabled while we wait for client input.
- */
+ /* Not reading from the client anymore. */
DoingCommandRead = false;
- DisableNotifyInterrupt();
- DisableCatchupInterrupt();
/* Make sure libpq is in a good state */
pq_comm_reset();