mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-21 08:29:39 +08:00
Install an atexit(2) callback that ensures that proc_exit's cleanup processing
will still be performed if something in a backend process calls exit() directly, instead of going through proc_exit() as we prefer. This is a second response to the issue that we might load third-party code that doesn't know it should not call exit(). Such a call will now cause a reasonably graceful backend shutdown, if possible. (Of course, if the reason for the exit() call is out-of-memory or some such, we might not be able to recover, but at least we will try.)
This commit is contained in:
parent
969d7cd431
commit
249a899f73
@ -13,7 +13,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/storage/ipc/ipc.c,v 1.102 2009/01/01 17:23:47 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/storage/ipc/ipc.c,v 1.103 2009/05/05 20:06:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -37,6 +37,15 @@
|
||||
*/
|
||||
bool proc_exit_inprogress = false;
|
||||
|
||||
/*
|
||||
* This flag tracks whether we've called atexit(2) in the current process
|
||||
* (or in the parent postmaster).
|
||||
*/
|
||||
static bool atexit_callback_setup = false;
|
||||
|
||||
/* local functions */
|
||||
static void proc_exit_prepare(int code);
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* exit() handling stuff
|
||||
@ -69,51 +78,21 @@ static int on_proc_exit_index,
|
||||
*
|
||||
* this function calls all the callbacks registered
|
||||
* for it (to free resources) and then calls exit.
|
||||
*
|
||||
* This should be the only function to call exit().
|
||||
* -cim 2/6/90
|
||||
*
|
||||
* Unfortunately, we can't really guarantee that add-on code
|
||||
* obeys the rule of not calling exit() directly. So, while
|
||||
* this is the preferred way out of the system, we also register
|
||||
* an atexit callback that will make sure cleanup happens.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
proc_exit(int code)
|
||||
{
|
||||
/*
|
||||
* Once we set this flag, we are committed to exit. Any ereport() will
|
||||
* NOT send control back to the main loop, but right back here.
|
||||
*/
|
||||
proc_exit_inprogress = true;
|
||||
|
||||
/*
|
||||
* Forget any pending cancel or die requests; we're doing our best to
|
||||
* close up shop already. Note that the signal handlers will not set
|
||||
* these flags again, now that proc_exit_inprogress is set.
|
||||
*/
|
||||
InterruptPending = false;
|
||||
ProcDiePending = false;
|
||||
QueryCancelPending = false;
|
||||
/* And let's just make *sure* we're not interrupted ... */
|
||||
ImmediateInterruptOK = false;
|
||||
InterruptHoldoffCount = 1;
|
||||
CritSectionCount = 0;
|
||||
|
||||
elog(DEBUG3, "proc_exit(%d)", code);
|
||||
|
||||
/* do our shared memory exits first */
|
||||
shmem_exit(code);
|
||||
|
||||
/*
|
||||
* call all the callbacks registered before calling exit().
|
||||
*
|
||||
* Note that since we decrement on_proc_exit_index each time, if a
|
||||
* callback calls ereport(ERROR) or ereport(FATAL) then it won't be
|
||||
* invoked again when control comes back here (nor will the
|
||||
* previously-completed callbacks). So, an infinite loop should not be
|
||||
* possible.
|
||||
*/
|
||||
while (--on_proc_exit_index >= 0)
|
||||
(*on_proc_exit_list[on_proc_exit_index].function) (code,
|
||||
on_proc_exit_list[on_proc_exit_index].arg);
|
||||
|
||||
elog(DEBUG3, "exit(%d)", code);
|
||||
/* Clean up everything that must be cleaned up */
|
||||
proc_exit_prepare(code);
|
||||
|
||||
#ifdef PROFILE_PID_DIR
|
||||
{
|
||||
@ -134,7 +113,10 @@ proc_exit(int code)
|
||||
*
|
||||
* Note that we do this here instead of in an on_proc_exit() callback
|
||||
* because we want to ensure that this code executes last - we don't
|
||||
* want to interfere with any other on_proc_exit() callback.
|
||||
* want to interfere with any other on_proc_exit() callback. For
|
||||
* the same reason, we do not include it in proc_exit_prepare ...
|
||||
* so if you are exiting in the "wrong way" you won't drop your profile
|
||||
* in a nice place.
|
||||
*/
|
||||
char gprofDirName[32];
|
||||
|
||||
@ -149,9 +131,59 @@ proc_exit(int code)
|
||||
}
|
||||
#endif
|
||||
|
||||
elog(DEBUG3, "exit(%d)", code);
|
||||
|
||||
exit(code);
|
||||
}
|
||||
|
||||
/*
|
||||
* Code shared between proc_exit and the atexit handler. Note that in
|
||||
* normal exit through proc_exit, this will actually be called twice ...
|
||||
* but the second call will have nothing to do.
|
||||
*/
|
||||
static void
|
||||
proc_exit_prepare(int code)
|
||||
{
|
||||
/*
|
||||
* Once we set this flag, we are committed to exit. Any ereport() will
|
||||
* NOT send control back to the main loop, but right back here.
|
||||
*/
|
||||
proc_exit_inprogress = true;
|
||||
|
||||
/*
|
||||
* Forget any pending cancel or die requests; we're doing our best to
|
||||
* close up shop already. Note that the signal handlers will not set
|
||||
* these flags again, now that proc_exit_inprogress is set.
|
||||
*/
|
||||
InterruptPending = false;
|
||||
ProcDiePending = false;
|
||||
QueryCancelPending = false;
|
||||
/* And let's just make *sure* we're not interrupted ... */
|
||||
ImmediateInterruptOK = false;
|
||||
InterruptHoldoffCount = 1;
|
||||
CritSectionCount = 0;
|
||||
|
||||
/* do our shared memory exits first */
|
||||
shmem_exit(code);
|
||||
|
||||
elog(DEBUG3, "proc_exit(%d)", code);
|
||||
|
||||
/*
|
||||
* call all the registered callbacks.
|
||||
*
|
||||
* Note that since we decrement on_proc_exit_index each time, if a
|
||||
* callback calls ereport(ERROR) or ereport(FATAL) then it won't be
|
||||
* invoked again when control comes back here (nor will the
|
||||
* previously-completed callbacks). So, an infinite loop should not be
|
||||
* possible.
|
||||
*/
|
||||
while (--on_proc_exit_index >= 0)
|
||||
(*on_proc_exit_list[on_proc_exit_index].function) (code,
|
||||
on_proc_exit_list[on_proc_exit_index].arg);
|
||||
|
||||
on_proc_exit_index = 0;
|
||||
}
|
||||
|
||||
/* ------------------
|
||||
* Run all of the on_shmem_exit routines --- but don't actually exit.
|
||||
* This is used by the postmaster to re-initialize shared memory and
|
||||
@ -176,6 +208,37 @@ shmem_exit(int code)
|
||||
on_shmem_exit_index = 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* atexit_callback
|
||||
*
|
||||
* Backstop to ensure that direct calls of exit() don't mess us up.
|
||||
*
|
||||
* Somebody who was being really uncooperative could call _exit(),
|
||||
* but for that case we have a "dead man switch" that will make the
|
||||
* postmaster treat it as a crash --- see pmsignal.c.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
#ifdef HAVE_ATEXIT
|
||||
|
||||
static void
|
||||
atexit_callback(void)
|
||||
{
|
||||
/* Clean up everything that must be cleaned up */
|
||||
/* ... too bad we don't know the real exit code ... */
|
||||
proc_exit_prepare(-1);
|
||||
}
|
||||
|
||||
#else /* assume we have on_exit instead */
|
||||
|
||||
static void
|
||||
atexit_callback(int exitstatus, void *arg)
|
||||
{
|
||||
/* Clean up everything that must be cleaned up */
|
||||
proc_exit_prepare(exitstatus);
|
||||
}
|
||||
|
||||
#endif /* HAVE_ATEXIT */
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* on_proc_exit
|
||||
*
|
||||
@ -195,6 +258,16 @@ on_proc_exit(pg_on_exit_callback function, Datum arg)
|
||||
on_proc_exit_list[on_proc_exit_index].arg = arg;
|
||||
|
||||
++on_proc_exit_index;
|
||||
|
||||
if (!atexit_callback_setup)
|
||||
{
|
||||
#ifdef HAVE_ATEXIT
|
||||
atexit(atexit_callback);
|
||||
#else
|
||||
on_exit(atexit_callback, NULL);
|
||||
#endif
|
||||
atexit_callback_setup = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
@ -216,6 +289,16 @@ on_shmem_exit(pg_on_exit_callback function, Datum arg)
|
||||
on_shmem_exit_list[on_shmem_exit_index].arg = arg;
|
||||
|
||||
++on_shmem_exit_index;
|
||||
|
||||
if (!atexit_callback_setup)
|
||||
{
|
||||
#ifdef HAVE_ATEXIT
|
||||
atexit(atexit_callback);
|
||||
#else
|
||||
on_exit(atexit_callback, NULL);
|
||||
#endif
|
||||
atexit_callback_setup = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user