mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-27 08:39:28 +08:00
Split up process latch initialization for more-fail-soft behavior.
In the previous coding, new backend processes would attempt to create their self-pipe during the OwnLatch call in InitProcess. However, pipe creation could fail if the kernel is short of resources; and the system does not recover gracefully from a FATAL error right there, since we have armed the dead-man switch for this process and not yet set up the on_shmem_exit callback that would disarm it. The postmaster then forces an unnecessary database-wide crash and restart, as reported by Sean Chittenden. There are various ways we could rearrange the code to fix this, but the simplest and sanest seems to be to split out creation of the self-pipe into a new function InitializeLatchSupport, which must be called from a place where failure is allowed. For most processes that gets called in InitProcess or InitAuxiliaryProcess, but processes that don't call either but still use latches need their own calls. Back-patch to 9.1, which has only a part of the latch logic that 9.2 and HEAD have, but nonetheless includes this bug.
This commit is contained in:
parent
8b728e5c6e
commit
e81e8f9342
@ -60,21 +60,50 @@ static volatile sig_atomic_t waiting = false;
|
||||
static int selfpipe_readfd = -1;
|
||||
static int selfpipe_writefd = -1;
|
||||
|
||||
/* private function prototypes */
|
||||
static void initSelfPipe(void);
|
||||
static void drainSelfPipe(void);
|
||||
/* Private function prototypes */
|
||||
static void sendSelfPipeByte(void);
|
||||
static void drainSelfPipe(void);
|
||||
|
||||
|
||||
/*
|
||||
* Initialize the process-local latch infrastructure.
|
||||
*
|
||||
* This must be called once during startup of any process that can wait on
|
||||
* latches, before it issues any InitLatch() or OwnLatch() calls.
|
||||
*/
|
||||
void
|
||||
InitializeLatchSupport(void)
|
||||
{
|
||||
int pipefd[2];
|
||||
|
||||
Assert(selfpipe_readfd == -1);
|
||||
|
||||
/*
|
||||
* Set up the self-pipe that allows a signal handler to wake up the
|
||||
* select() in WaitLatch. Make the write-end non-blocking, so that
|
||||
* SetLatch won't block if the event has already been set many times
|
||||
* filling the kernel buffer. Make the read-end non-blocking too, so that
|
||||
* we can easily clear the pipe by reading until EAGAIN or EWOULDBLOCK.
|
||||
*/
|
||||
if (pipe(pipefd) < 0)
|
||||
elog(FATAL, "pipe() failed: %m");
|
||||
if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) < 0)
|
||||
elog(FATAL, "fcntl() failed on read-end of self-pipe: %m");
|
||||
if (fcntl(pipefd[1], F_SETFL, O_NONBLOCK) < 0)
|
||||
elog(FATAL, "fcntl() failed on write-end of self-pipe: %m");
|
||||
|
||||
selfpipe_readfd = pipefd[0];
|
||||
selfpipe_writefd = pipefd[1];
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a backend-local latch.
|
||||
*/
|
||||
void
|
||||
InitLatch(volatile Latch *latch)
|
||||
{
|
||||
/* Initialize the self-pipe if this is our first latch in the process */
|
||||
if (selfpipe_readfd == -1)
|
||||
initSelfPipe();
|
||||
/* Assert InitializeLatchSupport has been called in this process */
|
||||
Assert(selfpipe_readfd >= 0);
|
||||
|
||||
latch->is_set = false;
|
||||
latch->owner_pid = MyProcPid;
|
||||
@ -116,11 +145,10 @@ InitSharedLatch(volatile Latch *latch)
|
||||
void
|
||||
OwnLatch(volatile Latch *latch)
|
||||
{
|
||||
Assert(latch->is_shared);
|
||||
/* Assert InitializeLatchSupport has been called in this process */
|
||||
Assert(selfpipe_readfd >= 0);
|
||||
|
||||
/* Initialize the self-pipe if this is our first latch in this process */
|
||||
if (selfpipe_readfd == -1)
|
||||
initSelfPipe();
|
||||
Assert(latch->is_shared);
|
||||
|
||||
/* sanity check */
|
||||
if (latch->owner_pid != 0)
|
||||
@ -514,30 +542,6 @@ latch_sigusr1_handler(void)
|
||||
sendSelfPipeByte();
|
||||
}
|
||||
|
||||
/* initialize the self-pipe */
|
||||
static void
|
||||
initSelfPipe(void)
|
||||
{
|
||||
int pipefd[2];
|
||||
|
||||
/*
|
||||
* Set up the self-pipe that allows a signal handler to wake up the
|
||||
* select() in WaitLatch. Make the write-end non-blocking, so that
|
||||
* SetLatch won't block if the event has already been set many times
|
||||
* filling the kernel buffer. Make the read-end non-blocking too, so that
|
||||
* we can easily clear the pipe by reading until EAGAIN or EWOULDBLOCK.
|
||||
*/
|
||||
if (pipe(pipefd) < 0)
|
||||
elog(FATAL, "pipe() failed: %m");
|
||||
if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) < 0)
|
||||
elog(FATAL, "fcntl() failed on read-end of self-pipe: %m");
|
||||
if (fcntl(pipefd[1], F_SETFL, O_NONBLOCK) < 0)
|
||||
elog(FATAL, "fcntl() failed on write-end of self-pipe: %m");
|
||||
|
||||
selfpipe_readfd = pipefd[0];
|
||||
selfpipe_writefd = pipefd[1];
|
||||
}
|
||||
|
||||
/* Send one byte to the self-pipe, to wake up WaitLatch */
|
||||
static void
|
||||
sendSelfPipeByte(void)
|
||||
|
@ -30,6 +30,12 @@
|
||||
#include "storage/shmem.h"
|
||||
|
||||
|
||||
void
|
||||
InitializeLatchSupport(void)
|
||||
{
|
||||
/* currently, nothing to do here for Windows */
|
||||
}
|
||||
|
||||
void
|
||||
InitLatch(volatile Latch *latch)
|
||||
{
|
||||
|
@ -234,8 +234,6 @@ PgArchiverMain(int argc, char *argv[])
|
||||
|
||||
MyProcPid = getpid(); /* reset MyProcPid */
|
||||
|
||||
InitLatch(&mainloop_latch); /* initialize latch used in main loop */
|
||||
|
||||
MyStartTime = time(NULL); /* record Start Time for logging */
|
||||
|
||||
/*
|
||||
@ -247,6 +245,10 @@ PgArchiverMain(int argc, char *argv[])
|
||||
elog(FATAL, "setsid() failed: %m");
|
||||
#endif
|
||||
|
||||
InitializeLatchSupport(); /* needed for latch waits */
|
||||
|
||||
InitLatch(&mainloop_latch); /* initialize latch used in main loop */
|
||||
|
||||
/*
|
||||
* Ignore all signals usually bound to some action in the postmaster,
|
||||
* except for SIGHUP, SIGTERM, SIGUSR1, SIGUSR2, and SIGQUIT.
|
||||
|
@ -3022,6 +3022,8 @@ PgstatCollectorMain(int argc, char *argv[])
|
||||
elog(FATAL, "setsid() failed: %m");
|
||||
#endif
|
||||
|
||||
InitializeLatchSupport(); /* needed for latch waits */
|
||||
|
||||
/* Initialize private latch for use by signal handlers */
|
||||
InitLatch(&pgStatLatch);
|
||||
|
||||
|
@ -251,6 +251,8 @@ SysLoggerMain(int argc, char *argv[])
|
||||
elog(FATAL, "setsid() failed: %m");
|
||||
#endif
|
||||
|
||||
InitializeLatchSupport(); /* needed for latch waits */
|
||||
|
||||
/* Initialize private latch for use by signal handlers */
|
||||
InitLatch(&sysLoggerLatch);
|
||||
|
||||
|
@ -279,6 +279,13 @@ InitProcess(void)
|
||||
if (MyProc != NULL)
|
||||
elog(ERROR, "you already exist");
|
||||
|
||||
/*
|
||||
* Initialize process-local latch support. This could fail if the kernel
|
||||
* is low on resources, and if so we want to exit cleanly before acquiring
|
||||
* any shared-memory resources.
|
||||
*/
|
||||
InitializeLatchSupport();
|
||||
|
||||
/*
|
||||
* Try to get a proc struct from the free list. If this fails, we must be
|
||||
* out of PGPROC structures (not to mention semaphores).
|
||||
@ -451,6 +458,13 @@ InitAuxiliaryProcess(void)
|
||||
if (MyProc != NULL)
|
||||
elog(ERROR, "you already exist");
|
||||
|
||||
/*
|
||||
* Initialize process-local latch support. This could fail if the kernel
|
||||
* is low on resources, and if so we want to exit cleanly before acquiring
|
||||
* any shared-memory resources.
|
||||
*/
|
||||
InitializeLatchSupport();
|
||||
|
||||
/*
|
||||
* We use the ProcStructLock to protect assignment and releasing of
|
||||
* AuxiliaryProcs entries.
|
||||
|
@ -111,6 +111,7 @@ typedef struct
|
||||
/*
|
||||
* prototypes for functions in latch.c
|
||||
*/
|
||||
extern void InitializeLatchSupport(void);
|
||||
extern void InitLatch(volatile Latch *latch);
|
||||
extern void InitSharedLatch(volatile Latch *latch);
|
||||
extern void OwnLatch(volatile Latch *latch);
|
||||
|
Loading…
Reference in New Issue
Block a user