/* finish setting up bufmgr.c */
InitBufferPoolBackend();
- /* register a shutdown callback for LWLock cleanup */
- on_shmem_exit(ShutdownAuxiliaryProcess, 0);
+ /* register a before-shutdown callback for LWLock cleanup */
+ before_shmem_exit(ShutdownAuxiliaryProcess, 0);
}
/*
if (myTempNamespaceSubID != InvalidSubTransactionId)
{
if (isCommit)
- on_shmem_exit(RemoveTempRelationsCallback, 0);
+ before_shmem_exit(RemoveTempRelationsCallback, 0);
else
{
myTempNamespace = InvalidOid;
*/
if (!unlistenExitRegistered)
{
- on_shmem_exit(Async_UnlistenOnExit, 0);
+ before_shmem_exit(Async_UnlistenOnExit, 0);
unlistenExitRegistered = true;
}
#define INVALID_CONTROL_SLOT ((uint32) -1)
+/* Backend-local tracking for on-detach callbacks. */
+typedef struct dsm_segment_detach_callback
+{
+ on_dsm_detach_callback function;
+ Datum arg;
+ slist_node node;
+} dsm_segment_detach_callback;
+
/* Backend-local state for a dynamic shared memory segment. */
struct dsm_segment
{
void *impl_private; /* Implementation-specific private data. */
void *mapped_address; /* Mapping address, or NULL if unmapped. */
Size mapped_size; /* Size of our mapping. */
+ slist_head on_detach; /* On-detach callbacks. */
};
/* Shared-memory state for a dynamic shared memory segment. */
static bool dsm_read_state_file(dsm_handle *h);
static void dsm_write_state_file(dsm_handle h);
static void dsm_postmaster_shutdown(int code, Datum arg);
-static void dsm_backend_shutdown(int code, Datum arg);
static dsm_segment *dsm_create_descriptor(void);
static bool dsm_control_segment_sane(dsm_control_header *control,
Size mapped_size);
}
#endif
- /* Arrange to detach segments on exit. */
- on_shmem_exit(dsm_backend_shutdown, 0);
-
dsm_init_done = true;
}
/*
* At backend shutdown time, detach any segments that are still attached.
*/
-static void
-dsm_backend_shutdown(int code, Datum arg)
+void
+dsm_backend_shutdown(void)
{
while (!dlist_is_empty(&dsm_segment_list))
{
void
dsm_detach(dsm_segment *seg)
{
+ /*
+ * Invoke registered callbacks. Just in case one of those callbacks
+ * throws a further error that brings us back here, pop the callback
+ * before invoking it, to avoid infinite error recursion.
+ */
+ while (!slist_is_empty(&seg->on_detach))
+ {
+ slist_node *node;
+ dsm_segment_detach_callback *cb;
+ on_dsm_detach_callback function;
+ Datum arg;
+
+ node = slist_pop_head_node(&seg->on_detach);
+ cb = slist_container(dsm_segment_detach_callback, node, node);
+ function = cb->function;
+ arg = cb->arg;
+ pfree(cb);
+
+ function(seg, arg);
+ }
+
/*
* Try to remove the mapping, if one exists. Normally, there will be,
* but maybe not, if we failed partway through a create or attach
return seg->handle;
}
+/*
+ * Register an on-detach callback for a dynamic shared memory segment.
+ */
+void
+on_dsm_detach(dsm_segment *seg, on_dsm_detach_callback function, Datum arg)
+{
+ dsm_segment_detach_callback *cb;
+
+ cb = MemoryContextAlloc(TopMemoryContext,
+ sizeof(dsm_segment_detach_callback));
+ cb->function = function;
+ cb->arg = arg;
+ slist_push_head(&seg->on_detach, &cb->node);
+}
+
+/*
+ * Unregister an on-detach callback for a dynamic shared memory segment.
+ */
+void
+cancel_on_dsm_detach(dsm_segment *seg, on_dsm_detach_callback function,
+ Datum arg)
+{
+ slist_mutable_iter iter;
+
+ slist_foreach_modify(iter, &seg->on_detach)
+ {
+ dsm_segment_detach_callback *cb;
+
+ cb = slist_container(dsm_segment_detach_callback, node, iter.cur);
+ if (cb->function == function && cb->arg == arg)
+ {
+ slist_delete_current(&iter);
+ pfree(cb);
+ break;
+ }
+ }
+}
+
/*
* Create a segment descriptor.
*/
seg->resowner = CurrentResourceOwner;
ResourceOwnerRememberDSM(CurrentResourceOwner, seg);
+ slist_init(&seg->on_detach);
+
return seg;
}
#ifdef PROFILE_PID_DIR
#include "postmaster/autovacuum.h"
#endif
+#include "storage/dsm.h"
#include "storage/ipc.h"
#include "tcop/tcopprot.h"
#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;
/* ----------------------------------------------------------------
/* ------------------
* 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;
}
}
}
+/* ----------------------------------------------------------------
+ * 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
}
/* ----------------------------------------------------------------
- * 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;
}
/* ----------------------------------------------------------------
void
on_exit_reset(void)
{
+ before_shmem_exit_index = 0;
on_shmem_exit_index = 0;
on_proc_exit_index = 0;
}
RelationCacheInitializePhase2();
/*
- * Set up process-exit callback to do pre-shutdown cleanup. This has to
- * be after we've initialized all the low-level modules like the buffer
- * manager, because during shutdown this has to run before the low-level
- * modules start to close down. On the other hand, we want it in place
- * before we begin our first transaction --- if we fail during the
- * initialization transaction, as is entirely possible, we need the
- * AbortTransaction call to clean up.
+ * Set up process-exit callback to do pre-shutdown cleanup. This is the
+ * first before_shmem_exit callback we register; thus, this will be the
+ * last thing we do before low-level modules like the buffer manager begin
+ * to close down. We need to have this in place before we begin our first
+ * transaction --- if we fail during the initialization transaction, as is
+ * entirely possible, we need the AbortTransaction call to clean up.
*/
- on_shmem_exit(ShutdownPostgres, 0);
+ before_shmem_exit(ShutdownPostgres, 0);
/* The autovacuum launcher is done here */
if (IsAutoVacuumLauncherProcess())
typedef struct dsm_segment dsm_segment;
-/* Initialization function. */
+/* Startup and shutdown functions. */
extern void dsm_postmaster_startup(void);
+extern void dsm_backend_shutdown(void);
/* Functions that create, update, or remove mappings. */
extern dsm_segment *dsm_create(Size size);
extern Size dsm_segment_map_length(dsm_segment *seg);
extern dsm_handle dsm_segment_handle(dsm_segment *seg);
+/* Cleanup hooks. */
+typedef void (*on_dsm_detach_callback) (dsm_segment *, Datum arg);
+extern void on_dsm_detach(dsm_segment *seg,
+ on_dsm_detach_callback function, Datum arg);
+extern void cancel_on_dsm_detach(dsm_segment *seg,
+ on_dsm_detach_callback function, Datum arg);
+
#endif /* DSM_H */
*/
#define PG_ENSURE_ERROR_CLEANUP(cleanup_function, arg) \
do { \
- on_shmem_exit(cleanup_function, arg); \
+ before_shmem_exit(cleanup_function, arg); \
PG_TRY()
#define PG_END_ENSURE_ERROR_CLEANUP(cleanup_function, arg) \
- cancel_shmem_exit(cleanup_function, arg); \
+ cancel_before_shmem_exit(cleanup_function, arg); \
PG_CATCH(); \
{ \
- cancel_shmem_exit(cleanup_function, arg); \
+ cancel_before_shmem_exit(cleanup_function, arg); \
cleanup_function (0, arg); \
PG_RE_THROW(); \
} \
extern void shmem_exit(int code);
extern void on_proc_exit(pg_on_exit_callback function, Datum arg);
extern void on_shmem_exit(pg_on_exit_callback function, Datum arg);
-extern void cancel_shmem_exit(pg_on_exit_callback function, Datum arg);
+extern void before_shmem_exit(pg_on_exit_callback function, Datum arg);
+extern void cancel_before_shmem_exit(pg_on_exit_callback function, Datum arg);
extern void on_exit_reset(void);
/* ipci.c */