diff options
author | Robert Haas <rhaas@postgresql.org> | 2013-12-18 12:57:20 -0500 |
---|---|---|
committer | Robert Haas <rhaas@postgresql.org> | 2013-12-18 13:09:09 -0500 |
commit | 001a573a2011d605f2a6e10aee9996de8581d099 (patch) | |
tree | 5bec5fa0c42508d9d17ae324191f8c6d3ca7e05d /src/backend/storage/ipc/ipc.c | |
parent | 613c6d26bd42dd8c2dd0664315be9551475b8864 (diff) | |
download | postgresql-001a573a2011d605f2a6e10aee9996de8581d099.tar.gz postgresql-001a573a2011d605f2a6e10aee9996de8581d099.zip |
Allow on-detach callbacks for dynamic shared memory segments.
Just as backends must clean up their shared memory state (releasing
lwlocks, buffer pins, etc.) before exiting, they must also perform
any similar cleanups related to dynamic shared memory segments they
have mapped before unmapping those segments. So add a mechanism to
ensure that.
Existing on_shmem_exit hooks include both "user level" cleanup such
as transaction abort and removal of leftover temporary relations and
also "low level" cleanup that forcibly released leftover shared
memory resources. On-detach callbacks should run after the first
group but before the second group, so create a new before_shmem_exit
function for registering the early callbacks and keep on_shmem_exit
for the regular callbacks. (An earlier draft of this patch added an
additional argument to on_shmem_exit, but that had a much larger
footprint and probably a substantially higher risk of breaking third
party code for no real gain.)
Patch by me, reviewed by KaiGai Kohei and Andres Freund.
Diffstat (limited to 'src/backend/storage/ipc/ipc.c')
-rw-r--r-- | src/backend/storage/ipc/ipc.c | 118 |
1 files changed, 95 insertions, 23 deletions
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c index c339e9c780b..9297292fc5e 100644 --- a/src/backend/storage/ipc/ipc.c +++ b/src/backend/storage/ipc/ipc.c @@ -27,6 +27,7 @@ #ifdef PROFILE_PID_DIR #include "postmaster/autovacuum.h" #endif +#include "storage/dsm.h" #include "storage/ipc.h" #include "tcop/tcopprot.h" @@ -64,14 +65,19 @@ static void proc_exit_prepare(int code); #define MAX_ON_EXITS 20 -static struct ONEXIT +struct ONEXIT { pg_on_exit_callback function; Datum arg; -} on_proc_exit_list[MAX_ON_EXITS], on_shmem_exit_list[MAX_ON_EXITS]; +}; + +static struct ONEXIT on_proc_exit_list[MAX_ON_EXITS]; +static struct ONEXIT on_shmem_exit_list[MAX_ON_EXITS]; +static struct ONEXIT before_shmem_exit_list[MAX_ON_EXITS]; static int on_proc_exit_index, - on_shmem_exit_index; + on_shmem_exit_index, + before_shmem_exit_index; /* ---------------------------------------------------------------- @@ -202,25 +208,60 @@ proc_exit_prepare(int code) /* ------------------ * 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 - * semaphores after a backend dies horribly. + * semaphores after a backend dies horribly. As with proc_exit(), we + * remove each callback from the list before calling it, to avoid + * infinite loop in case of error. * ------------------ */ void shmem_exit(int code) { - elog(DEBUG3, "shmem_exit(%d): %d callbacks to make", - code, on_shmem_exit_index); + /* + * Call before_shmem_exit callbacks. + * + * These should be things that need most of the system to still be + * up and working, such as cleanup of temp relations, which requires + * catalog access; or things that need to be completed because later + * cleanup steps depend on them, such as releasing lwlocks. + */ + elog(DEBUG3, "shmem_exit(%d): %d before_shmem_exit callbacks to make", + code, before_shmem_exit_index); + while (--before_shmem_exit_index >= 0) + (*before_shmem_exit_list[before_shmem_exit_index].function) (code, + before_shmem_exit_list[before_shmem_exit_index].arg); + before_shmem_exit_index = 0; /* - * call all the registered callbacks. + * Call dynamic shared memory callbacks. + * + * These serve the same purpose as late callbacks, but for dynamic shared + * memory segments rather than the main shared memory segment. + * dsm_backend_shutdown() has the same kind of progressive logic we use + * for the main shared memory segment; namely, it unregisters each + * callback before invoking it, so that we don't get stuck in an infinite + * loop if one of those callbacks itself throws an ERROR or FATAL. + * + * Note that explicitly calling this function here is quite different + * from registering it as an on_shmem_exit callback for precisely this + * reason: if one dynamic shared memory callback errors out, the remaining + * callbacks will still be invoked. Thus, hard-coding this call puts it + * equal footing with callbacks for the main shared memory segment. + */ + dsm_backend_shutdown(); + + /* + * Call on_shmem_exit callbacks. * - * As with proc_exit(), we remove each callback from the list before - * calling it, to avoid infinite loop in case of error. + * These are generally releasing low-level shared memory resources. In + * some cases, this is a backstop against the possibility that the early + * callbacks might themselves fail, leading to re-entry to this routine; + * in other cases, it's cleanup that only happens at process exit. */ + elog(DEBUG3, "shmem_exit(%d): %d on_shmem_exit callbacks to make", + code, on_shmem_exit_index); while (--on_shmem_exit_index >= 0) (*on_shmem_exit_list[on_shmem_exit_index].function) (code, - on_shmem_exit_list[on_shmem_exit_index].arg); - + on_shmem_exit_list[on_shmem_exit_index].arg); on_shmem_exit_index = 0; } @@ -270,10 +311,39 @@ on_proc_exit(pg_on_exit_callback function, Datum arg) } /* ---------------------------------------------------------------- + * before_shmem_exit + * + * Register early callback to perform user-level cleanup, + * e.g. transaction abort, before we begin shutting down + * low-level subsystems. + * ---------------------------------------------------------------- + */ +void +before_shmem_exit(pg_on_exit_callback function, Datum arg) +{ + if (before_shmem_exit_index >= MAX_ON_EXITS) + ereport(FATAL, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg_internal("out of before_shmem_exit slots"))); + + before_shmem_exit_list[before_shmem_exit_index].function = function; + before_shmem_exit_list[before_shmem_exit_index].arg = arg; + + ++before_shmem_exit_index; + + if (!atexit_callback_setup) + { + atexit(atexit_callback); + atexit_callback_setup = true; + } +} + +/* ---------------------------------------------------------------- * on_shmem_exit * - * this function adds a callback function to the list of - * functions invoked by shmem_exit(). -cim 2/6/90 + * Register ordinary callback to perform low-level shutdown + * (e.g. releasing our PGPROC); run after before_shmem_exit + * callbacks and before on_proc_exit callbacks. * ---------------------------------------------------------------- */ void @@ -297,21 +367,22 @@ on_shmem_exit(pg_on_exit_callback function, Datum arg) } /* ---------------------------------------------------------------- - * cancel_shmem_exit + * cancel_before_shmem_exit * - * this function removes an entry, if present, from the list of - * functions to be invoked by shmem_exit(). For simplicity, - * only the latest entry can be removed. (We could work harder - * but there is no need for current uses.) + * this function removes a previously-registed before_shmem_exit + * callback. For simplicity, only the latest entry can be + * removed. (We could work harder but there is no need for + * current uses.) * ---------------------------------------------------------------- */ void -cancel_shmem_exit(pg_on_exit_callback function, Datum arg) +cancel_before_shmem_exit(pg_on_exit_callback function, Datum arg) { - if (on_shmem_exit_index > 0 && - on_shmem_exit_list[on_shmem_exit_index - 1].function == function && - on_shmem_exit_list[on_shmem_exit_index - 1].arg == arg) - --on_shmem_exit_index; + if (before_shmem_exit_index > 0 && + before_shmem_exit_list[before_shmem_exit_index - 1].function + == function && + before_shmem_exit_list[before_shmem_exit_index - 1].arg == arg) + --before_shmem_exit_index; } /* ---------------------------------------------------------------- @@ -326,6 +397,7 @@ cancel_shmem_exit(pg_on_exit_callback function, Datum arg) void on_exit_reset(void) { + before_shmem_exit_index = 0; on_shmem_exit_index = 0; on_proc_exit_index = 0; } |