Skip to content

Commit 4fe384b

Browse files
committed
Process 'die' interrupts while reading/writing from the client socket.
Up to now it was impossible to terminate a backend that was trying to send/recv data to/from the client when the socket's buffer was already full/empty. While the send/recv calls itself might have gotten interrupted by signals on some platforms, we just immediately retried. That could lead to situations where a backend couldn't be terminated , after a client died without the connection being closed, because it was blocked in send/recv. The problem was far more likely to be hit when sending data than when reading. That's because while reading a command from the client, and during authentication, we processed interrupts immediately . That primarily left COPY FROM STDIN as being problematic for recv. Change things so that that we process 'die' events immediately when the appropriate signal arrives. We can't sensibly react to query cancels at that point, because we might loose sync with the client as we could be in the middle of writing a message. We don't interrupt writes if the write buffer isn't full, as indicated by write() returning EWOULDBLOCK, as that would lead to fewer error messages reaching clients. Per discussion with Kyotaro HORIGUCHI and Heikki Linnakangas Discussion: 20140927191243.GD5423@alap3.anarazel.de
1 parent 4f85fde commit 4fe384b

File tree

4 files changed

+105
-30
lines changed

4 files changed

+105
-30
lines changed

src/backend/libpq/be-secure-openssl.c

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@ be_tls_read(Port *port, void *ptr, size_t len)
553553
if (latchret & WL_LATCH_SET)
554554
{
555555
ResetLatch(MyLatch);
556-
ProcessClientReadInterrupt(); /* preserves errno */
556+
ProcessClientReadInterrupt(true); /* preserves errno */
557557
}
558558
goto rloop;
559559
case SSL_ERROR_SYSCALL:
@@ -595,6 +595,7 @@ be_tls_write(Port *port, void *ptr, size_t len)
595595
ssize_t n;
596596
int err;
597597
int waitfor;
598+
int latchret;
598599

599600
/*
600601
* If SSL renegotiations are enabled and we're getting close to the
@@ -659,16 +660,27 @@ be_tls_write(Port *port, void *ptr, size_t len)
659660
case SSL_ERROR_WANT_READ:
660661
case SSL_ERROR_WANT_WRITE:
661662

663+
waitfor = WL_LATCH_SET;
664+
662665
if (err == SSL_ERROR_WANT_READ)
663-
waitfor = WL_SOCKET_READABLE;
666+
waitfor |= WL_SOCKET_READABLE;
664667
else
665-
waitfor = WL_SOCKET_WRITEABLE;
668+
waitfor |= WL_SOCKET_WRITEABLE;
669+
670+
latchret = WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
666671

667-
WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
668672
/*
669-
* XXX: We'll, at some later point, likely want to add interrupt
670-
* processing here.
673+
* Check for interrupts here, in addition to secure_write(),
674+
* because an interrupted write in secure_raw_write() will return
675+
* here, and we cannot return to secure_write() until we've
676+
* written something.
671677
*/
678+
if (latchret & WL_LATCH_SET)
679+
{
680+
ResetLatch(MyLatch);
681+
ProcessClientWriteInterrupt(true); /* preserves errno */
682+
}
683+
672684
goto wloop;
673685
case SSL_ERROR_SYSCALL:
674686
/* leave it to caller to ereport the value of errno */

src/backend/libpq/be-secure.c

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -140,14 +140,27 @@ secure_read(Port *port, void *ptr, size_t len)
140140
n = secure_raw_read(port, ptr, len);
141141
}
142142

143-
/* Process interrupts that happened while (or before) receiving. */
144-
ProcessClientReadInterrupt(); /* preserves errno */
145-
146143
/* retry after processing interrupts */
147144
if (n < 0 && errno == EINTR)
148145
{
146+
/*
147+
* We tried to read data, the socket was empty, and we were
148+
* interrupted while waiting for readability. We only process
149+
* interrupts if we got interrupted while reading and when in blocking
150+
* mode. In other cases it's better to allow the interrupts to be
151+
* handled at higher layers.
152+
*/
153+
ProcessClientReadInterrupt(!port->noblock); /* preserves errno */
149154
goto retry;
150155
}
156+
157+
/*
158+
* Process interrupts that happened while (or before) receiving. Note that
159+
* we signal that we're not blocking, which will prevent some types of
160+
* interrupts from being processed.
161+
*/
162+
ProcessClientReadInterrupt(false);
163+
151164
return n;
152165
}
153166

@@ -224,18 +237,17 @@ secure_write(Port *port, void *ptr, size_t len)
224237
n = secure_raw_write(port, ptr, len);
225238
}
226239

227-
/*
228-
* XXX: We'll, at some later point, likely want to add interrupt
229-
* processing here.
230-
*/
231-
232-
/*
233-
* Retry after processing interrupts. This can be triggered even though we
234-
* don't check for latch set's during writing yet, because SSL
235-
* renegotiations might have required reading from the socket.
236-
*/
240+
/* retry after processing interrupts */
237241
if (n < 0 && errno == EINTR)
238242
{
243+
/*
244+
* We tried to send data, the socket was full, and we were interrupted
245+
* while waiting for writability. We only process interrupts if we got
246+
* interrupted while writing and when in blocking mode. In other cases
247+
* it's better to allow the interrupts to be handled at higher layers.
248+
*/
249+
ProcessClientWriteInterrupt(!port->noblock);
250+
239251
goto retry;
240252
}
241253

@@ -262,17 +274,21 @@ secure_raw_write(Port *port, const void *ptr, size_t len)
262274
int w;
263275
int save_errno = errno;
264276

265-
/*
266-
* We probably want to check for latches being set at some point
267-
* here. That'd allow us to handle interrupts while blocked on
268-
* writes. If set we'd not retry directly, but return. That way we
269-
* don't do anything while (possibly) inside a ssl library.
270-
*/
271277
w = WaitLatchOrSocket(MyLatch,
272-
WL_SOCKET_WRITEABLE,
278+
WL_LATCH_SET | WL_SOCKET_WRITEABLE,
273279
port->sock, 0);
274280

275-
if (w & WL_SOCKET_WRITEABLE)
281+
if (w & WL_LATCH_SET)
282+
{
283+
ResetLatch(MyLatch);
284+
/*
285+
* Force a return, so interrupts can be processed when not
286+
* (possibly) underneath a ssl library.
287+
*/
288+
errno = EINTR;
289+
return -1;
290+
}
291+
else if (w & WL_SOCKET_WRITEABLE)
276292
{
277293
goto wloop;
278294
}

src/backend/tcop/postgres.c

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ interactive_getc(void)
318318

319319
c = getc(stdin);
320320

321-
ProcessClientReadInterrupt();
321+
ProcessClientReadInterrupt(true);
322322

323323
return c;
324324
}
@@ -529,7 +529,7 @@ ReadCommand(StringInfo inBuf)
529529
* Must preserve errno!
530530
*/
531531
void
532-
ProcessClientReadInterrupt(void)
532+
ProcessClientReadInterrupt(bool blocked)
533533
{
534534
int save_errno = errno;
535535

@@ -546,10 +546,56 @@ ProcessClientReadInterrupt(void)
546546
if (notifyInterruptPending)
547547
ProcessNotifyInterrupt();
548548
}
549+
else if (ProcDiePending && blocked)
550+
{
551+
/*
552+
* We're dying. It's safe (and sane) to handle that now.
553+
*/
554+
CHECK_FOR_INTERRUPTS();
555+
}
549556

550557
errno = save_errno;
551558
}
552559

560+
/*
561+
* ProcessClientWriteInterrupt() - Process interrupts specific to client writes
562+
*
563+
* This is called just after low-level writes. That might be after the read
564+
* finished successfully, or it was interrupted via interrupt. 'blocked' tells
565+
* us whether the
566+
*
567+
* Must preserve errno!
568+
*/
569+
void
570+
ProcessClientWriteInterrupt(bool blocked)
571+
{
572+
int save_errno = errno;
573+
574+
Assert(InterruptHoldoffCount == 0 && CritSectionCount == 0);
575+
576+
/*
577+
* We only want to process the interrupt here if socket writes are
578+
* blocking to increase the chance to get an error message to the
579+
* client. If we're not blocked there'll soon be a
580+
* CHECK_FOR_INTERRUPTS(). But if we're blocked we'll never get out of
581+
* that situation if the client has died.
582+
*/
583+
if (ProcDiePending && blocked)
584+
{
585+
/*
586+
* We're dying. It's safe (and sane) to handle that now. But we don't
587+
* want to send the client the error message as that a) would possibly
588+
* block again b) would possibly lead to sending an error message to
589+
* the client, while we already started to send something else.
590+
*/
591+
if (whereToSendOutput == DestRemote)
592+
whereToSendOutput = DestNone;
593+
594+
CHECK_FOR_INTERRUPTS();
595+
}
596+
597+
errno = save_errno;
598+
}
553599

554600
/*
555601
* Do raw parsing (only).

src/include/tcop/tcopprot.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ extern void StatementCancelHandler(SIGNAL_ARGS);
6767
extern void FloatExceptionHandler(SIGNAL_ARGS) __attribute__((noreturn));
6868
extern void RecoveryConflictInterrupt(ProcSignalReason reason); /* called from SIGUSR1
6969
* handler */
70-
extern void ProcessClientReadInterrupt(void);
70+
extern void ProcessClientReadInterrupt(bool blocked);
71+
extern void ProcessClientWriteInterrupt(bool blocked);
7172

7273
extern void process_postgres_switches(int argc, char *argv[],
7374
GucContext ctx, const char **dbname);

0 commit comments

Comments
 (0)