From 9f5dddb330f7a5bec9a2233df7ad2d4e269ffc55 Mon Sep 17 00:00:00 2001 From: Mark Dilger Date: Mon, 11 Jan 2021 16:04:31 -0800 Subject: [PATCH v33 1/8] Moving exit_nicely and fatal into fe_utils Various frontend executables in src/bin, src/bin/scripts, and contrib/ have logic for logging and exiting under error conditions. The logging code itself is already under common/, but executables differ in their calls to exit() vs. exit_nicely(), with exit_nicely() not uniformly defined, and sometimes all of this wrapped up under a macro named fatal(), the definition of that macro also not uniformly defined. This makes it harder to move code out of these executables into a shared library under fe_utils/. Standardizing all executables to define these things the same way or to use a single fe_utils/ library is beyond the scope of this patch, but this patch should get the ball rolling in that direction. For pg_amcheck purposes, we really just need to get scripts_parallel moved into fe_utils, which doesn't require changing pg_dump at all. But by creating a new fe_utils/exit_utils which is compatible with pg_dump along with everything else, we avoid creating yet another compatibility problem. So we move the functions "on_exit_nicely" and "exit_nicely", and the macro "fatal" from pg_dump into fe_utils, and we refactor the on_exit_nicely() implementation to no longer contain pg_dump specific logic for exiting threads on Windows. Instead, we create a new on_exit_nicely_final() function for registering a final callback, and use that from within pg_dump to register a final callback encapsulating knowledge of threading variables. The on_exit_nicely_final() function, and associated final_on_exit_nicely variable, are not strictly necessary. The same behavior could be achieved by registering an exiting callback via the on_exit_nicely() function prior to any other callbacks, but that would place a larger burden on scripts authors to make certain that (a) no other callback gets registered before the exiting callback, and (b) that only one exiting callback gets registered. --- src/bin/pg_dump/parallel.c | 16 ++++ src/bin/pg_dump/parallel.h | 5 -- src/bin/pg_dump/pg_backup_archiver.h | 1 + src/bin/pg_dump/pg_backup_utils.c | 59 --------------- src/bin/pg_dump/pg_backup_utils.h | 8 -- src/fe_utils/Makefile | 1 + src/fe_utils/exit_utils.c | 105 +++++++++++++++++++++++++++ src/include/fe_utils/exit_utils.h | 26 +++++++ src/tools/msvc/Mkvcbuild.pm | 2 +- 9 files changed, 150 insertions(+), 73 deletions(-) create mode 100644 src/fe_utils/exit_utils.c create mode 100644 src/include/fe_utils/exit_utils.h diff --git a/src/bin/pg_dump/parallel.c b/src/bin/pg_dump/parallel.c index c7351a43fd..e619f126f5 100644 --- a/src/bin/pg_dump/parallel.c +++ b/src/bin/pg_dump/parallel.c @@ -228,6 +228,19 @@ static char *readMessageFromPipe(int fd); #define messageStartsWith(msg, prefix) \ (strncmp(msg, prefix, strlen(prefix)) == 0) +#ifdef WIN32 + +/* + * An on_exit_nicely_callback which, if run in a parallel worker thread on + * Windows, will only exit the thread, not the whole process. + */ +static void +end_threads(int code, void *arg) +{ + if (parallel_init_done && GetCurrentThreadId() != mainThreadId) + _endthreadex(code); +} +#endif /* * Initialize parallel dump support --- should be called early in process @@ -255,6 +268,9 @@ init_parallel_dump_utils(void) exit_nicely(1); } + /* install callback to close threads on exit */ + on_exit_nicely_final(end_threads, NULL); + parallel_init_done = true; } #endif diff --git a/src/bin/pg_dump/parallel.h b/src/bin/pg_dump/parallel.h index 0fbf736c81..48c48e12e6 100644 --- a/src/bin/pg_dump/parallel.h +++ b/src/bin/pg_dump/parallel.h @@ -45,11 +45,6 @@ typedef struct ParallelState ParallelSlot *parallelSlot; /* private info about each worker */ } ParallelState; -#ifdef WIN32 -extern bool parallel_init_done; -extern DWORD mainThreadId; -#endif - extern void init_parallel_dump_utils(void); extern bool IsEveryWorkerIdle(ParallelState *pstate); diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h index a8ea5c7eae..37d157b7ad 100644 --- a/src/bin/pg_dump/pg_backup_archiver.h +++ b/src/bin/pg_dump/pg_backup_archiver.h @@ -26,6 +26,7 @@ #include +#include "fe_utils/exit_utils.h" #include "libpq-fe.h" #include "pg_backup.h" #include "pqexpbuffer.h" diff --git a/src/bin/pg_dump/pg_backup_utils.c b/src/bin/pg_dump/pg_backup_utils.c index c709a40e06..631e88f7db 100644 --- a/src/bin/pg_dump/pg_backup_utils.c +++ b/src/bin/pg_dump/pg_backup_utils.c @@ -19,16 +19,6 @@ /* Globals exported by this file */ const char *progname = NULL; -#define MAX_ON_EXIT_NICELY 20 - -static struct -{ - on_exit_nicely_callback function; - void *arg; -} on_exit_nicely_list[MAX_ON_EXIT_NICELY]; - -static int on_exit_nicely_index; - /* * Parse a --section=foo command line argument. * @@ -57,52 +47,3 @@ set_dump_section(const char *arg, int *dumpSections) exit_nicely(1); } } - - -/* Register a callback to be run when exit_nicely is invoked. */ -void -on_exit_nicely(on_exit_nicely_callback function, void *arg) -{ - if (on_exit_nicely_index >= MAX_ON_EXIT_NICELY) - { - pg_log_fatal("out of on_exit_nicely slots"); - exit_nicely(1); - } - on_exit_nicely_list[on_exit_nicely_index].function = function; - on_exit_nicely_list[on_exit_nicely_index].arg = arg; - on_exit_nicely_index++; -} - -/* - * Run accumulated on_exit_nicely callbacks in reverse order and then exit - * without printing any message. - * - * If running in a parallel worker thread on Windows, we only exit the thread, - * not the whole process. - * - * Note that in parallel operation on Windows, the callback(s) will be run - * by each thread since the list state is necessarily shared by all threads; - * each callback must contain logic to ensure it does only what's appropriate - * for its thread. On Unix, callbacks are also run by each process, but only - * for callbacks established before we fork off the child processes. (It'd - * be cleaner to reset the list after fork(), and let each child establish - * its own callbacks; but then the behavior would be completely inconsistent - * between Windows and Unix. For now, just be sure to establish callbacks - * before forking to avoid inconsistency.) - */ -void -exit_nicely(int code) -{ - int i; - - for (i = on_exit_nicely_index - 1; i >= 0; i--) - on_exit_nicely_list[i].function(code, - on_exit_nicely_list[i].arg); - -#ifdef WIN32 - if (parallel_init_done && GetCurrentThreadId() != mainThreadId) - _endthreadex(code); -#endif - - exit(code); -} diff --git a/src/bin/pg_dump/pg_backup_utils.h b/src/bin/pg_dump/pg_backup_utils.h index 306798f9ac..ee4409c274 100644 --- a/src/bin/pg_dump/pg_backup_utils.h +++ b/src/bin/pg_dump/pg_backup_utils.h @@ -15,22 +15,14 @@ #ifndef PG_BACKUP_UTILS_H #define PG_BACKUP_UTILS_H -#include "common/logging.h" - /* bits returned by set_dump_section */ #define DUMP_PRE_DATA 0x01 #define DUMP_DATA 0x02 #define DUMP_POST_DATA 0x04 #define DUMP_UNSECTIONED 0xff -typedef void (*on_exit_nicely_callback) (int code, void *arg); - extern const char *progname; extern void set_dump_section(const char *arg, int *dumpSections); -extern void on_exit_nicely(on_exit_nicely_callback function, void *arg); -extern void exit_nicely(int code) pg_attribute_noreturn(); - -#define fatal(...) do { pg_log_error(__VA_ARGS__); exit_nicely(1); } while(0) #endif /* PG_BACKUP_UTILS_H */ diff --git a/src/fe_utils/Makefile b/src/fe_utils/Makefile index 10d6838cf9..d6c328faf1 100644 --- a/src/fe_utils/Makefile +++ b/src/fe_utils/Makefile @@ -23,6 +23,7 @@ OBJS = \ archive.o \ cancel.o \ conditional.o \ + exit_utils.o \ mbprint.o \ print.o \ psqlscan.o \ diff --git a/src/fe_utils/exit_utils.c b/src/fe_utils/exit_utils.c new file mode 100644 index 0000000000..8c2760a78c --- /dev/null +++ b/src/fe_utils/exit_utils.c @@ -0,0 +1,105 @@ +/*------------------------------------------------------------------------- + * + * Exiting with cleanup callback facilities for frontend code + * + * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/fe_utils/exit_utils.c + * + *------------------------------------------------------------------------- + */ + +#include "fe_utils/exit_utils.h" + +#define MAX_ON_EXIT_NICELY 20 + +typedef struct +{ + on_exit_nicely_callback function; + void *arg; +} OnExitCallback; + +/* + * Storage for registered on_exit_nicely_callbacks to be run by exit_nicely() + * before exit()ing. The callbacks in the on_exit_nicely_list[] are run in + * reverse of the order that they were registered. The final_on_exit_nicely + * callback is run after all the ones from the list, regardless of when it was + * registered. + * + * Note that in parallel operation on Windows, the callback(s) will be run + * by each thread since the list state is necessarily shared by all + * threads; each callback must contain logic to ensure it does only what's + * appropriate for its thread. On Unix, callbacks are also run by each + * process, but only for callbacks established before we fork off the child + * processes. (It'd be cleaner to reset the list after fork(), and let + * each child establish its own callbacks; but then the behavior would be + * completely inconsistent between Windows and Unix. For now, just be sure + * to establish callbacks before forking to avoid inconsistency.) + */ +static OnExitCallback final_on_exit_nicely; +static OnExitCallback on_exit_nicely_list[MAX_ON_EXIT_NICELY]; +static int on_exit_nicely_index; + +/* + * Register a callback to be run when exit_nicely is invoked. + * + * If you wish to register a callback which itself exits the application or + * thread, see on_exit_nicely_final() instead. + */ +void +on_exit_nicely(on_exit_nicely_callback function, void *arg) +{ + if (on_exit_nicely_index >= MAX_ON_EXIT_NICELY) + { + pg_log_fatal("out of on_exit_nicely slots"); + exit_nicely(1); + } + on_exit_nicely_list[on_exit_nicely_index].function = function; + on_exit_nicely_list[on_exit_nicely_index].arg = arg; + on_exit_nicely_index++; +} + +/* + * Register a final callback to be run after all other on_exit_nicely callbacks + * have been processed. + * + * Only one final callback may be registered. Attempts to subsequently register + * additional final callbacks are an error. + * + * A callback registered via on_exit_nicely_final() may choose to exit the + * application or thread without risk of skipping any other callback logic. If + * the callback does not itself exit, exit_nicely() will exit() immediately + * after the final callback returns. + */ +void +on_exit_nicely_final(on_exit_nicely_callback function, void *arg) +{ + if (final_on_exit_nicely.function) + { + pg_log_fatal("multiple on_exit_nicely_final callbacks"); + exit_nicely(1); + } + final_on_exit_nicely.function = function; + final_on_exit_nicely.arg = arg; +} + +/* + * Run accumulated on_exit_nicely callbacks in reverse order followed by the + * final on_exit_nicely callback, if any, and then exit() without printing any + * message. + */ +void +exit_nicely(int code) +{ + int i; + + for (i = on_exit_nicely_index - 1; i >= 0; i--) + on_exit_nicely_list[i].function(code, + on_exit_nicely_list[i].arg); + + if (final_on_exit_nicely.function) + final_on_exit_nicely.function(code, final_on_exit_nicely.arg); + + exit(code); +} diff --git a/src/include/fe_utils/exit_utils.h b/src/include/fe_utils/exit_utils.h new file mode 100644 index 0000000000..6326404133 --- /dev/null +++ b/src/include/fe_utils/exit_utils.h @@ -0,0 +1,26 @@ +/*------------------------------------------------------------------------- + * + * Exiting with cleanup callback facilities for frontend code + * + * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/fe_utils/exit_utils.h + * + *------------------------------------------------------------------------- + */ +#ifndef EXIT_UTILS_H +#define EXIT_UTILS_H + +#include "postgres_fe.h" +#include "common/logging.h" + +typedef void (*on_exit_nicely_callback) (int code, void *arg); + +extern void on_exit_nicely(on_exit_nicely_callback function, void *arg); +extern void on_exit_nicely_final(on_exit_nicely_callback function, void *arg); +extern void exit_nicely(int code) pg_attribute_noreturn(); + +#define fatal(...) do { pg_log_error(__VA_ARGS__); exit_nicely(1); } while(0) + +#endif /* EXIT_UTILS_H */ diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index 7213e65e08..0c6c1c996f 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -147,7 +147,7 @@ sub mkvcbuild our @pgcommonbkndfiles = @pgcommonallfiles; our @pgfeutilsfiles = qw( - archive.c cancel.c conditional.c mbprint.c print.c psqlscan.l + archive.c cancel.c conditional.c exit_utils.c mbprint.c print.c psqlscan.l psqlscan.c simple_list.c string_utils.c recovery_gen.c); $libpgport = $solution->AddProject('libpgport', 'lib', 'misc'); -- 2.21.1 (Apple Git-122.3)