From 2ad5c27bb565c26a4b12ea3343331c80f121f269 Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Fri, 16 Oct 2015 09:42:33 -0400 Subject: [PATCH] Don't send protocol messages to a shm_mq that no longer exists. Commit 2bd9e412f92bc6a68f3e8bcb18e04955cc35001d introduced a mechanism for relaying protocol messages from a background worker to another backend via a shm_mq. However, there was no provision for shutting down the communication channel. Therefore, a protocol message sent late in the shutdown sequence, such as a DEBUG message resulting from cranking up log_min_messages, could crash the server. To fix, install an on_dsm_detach callback that disables sending messages to the shm_mq when the associated DSM is detached. --- src/backend/access/transam/parallel.c | 2 +- src/backend/libpq/pqmq.c | 28 +++++++++++++++++++++++++-- src/include/libpq/pqmq.h | 2 +- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c index 29d6ed57cc..0b94c0f87e 100644 --- a/src/backend/access/transam/parallel.c +++ b/src/backend/access/transam/parallel.c @@ -867,7 +867,7 @@ ParallelWorkerMain(Datum main_arg) ParallelWorkerNumber * PARALLEL_ERROR_QUEUE_SIZE); shm_mq_set_sender(mq, MyProc); mqh = shm_mq_attach(mq, seg, NULL); - pq_redirect_to_shm_mq(mq, mqh); + pq_redirect_to_shm_mq(seg, mqh); pq_set_parallel_master(fps->parallel_master_pid, fps->parallel_master_backend_id); diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c index 9ca6b7ce0d..0a3c2b70cb 100644 --- a/src/backend/libpq/pqmq.c +++ b/src/backend/libpq/pqmq.c @@ -26,6 +26,7 @@ static bool pq_mq_busy = false; static pid_t pq_mq_parallel_master_pid = 0; static pid_t pq_mq_parallel_master_backend_id = InvalidBackendId; +static void pq_cleanup_redirect_to_shm_mq(dsm_segment *seg, Datum arg); static void mq_comm_reset(void); static int mq_flush(void); static int mq_flush_if_writable(void); @@ -51,13 +52,26 @@ static PQcommMethods PqCommMqMethods = { * message queue. */ void -pq_redirect_to_shm_mq(shm_mq *mq, shm_mq_handle *mqh) +pq_redirect_to_shm_mq(dsm_segment *seg, shm_mq_handle *mqh) { PqCommMethods = &PqCommMqMethods; - pq_mq = mq; + pq_mq = shm_mq_get_queue(mqh); pq_mq_handle = mqh; whereToSendOutput = DestRemote; FrontendProtocol = PG_PROTOCOL_LATEST; + on_dsm_detach(seg, pq_cleanup_redirect_to_shm_mq, (Datum) 0); +} + +/* + * When the DSM that contains our shm_mq goes away, we need to stop sending + * messages to it. + */ +static void +pq_cleanup_redirect_to_shm_mq(dsm_segment *seg, Datum arg) +{ + pq_mq = NULL; + pq_mq_handle = NULL; + whereToSendOutput = DestNone; } /* @@ -123,9 +137,19 @@ mq_putmessage(char msgtype, const char *s, size_t len) if (pq_mq != NULL) shm_mq_detach(pq_mq); pq_mq = NULL; + pq_mq_handle = NULL; return EOF; } + /* + * If the message queue is already gone, just ignore the message. This + * doesn't necessarily indicate a problem; for example, DEBUG messages + * can be generated late in the shutdown sequence, after all DSMs have + * already been detached. + */ + if (pq_mq == NULL) + return 0; + pq_mq_busy = true; iov[0].data = &msgtype; diff --git a/src/include/libpq/pqmq.h b/src/include/libpq/pqmq.h index 901756596a..97f17da89d 100644 --- a/src/include/libpq/pqmq.h +++ b/src/include/libpq/pqmq.h @@ -16,7 +16,7 @@ #include "lib/stringinfo.h" #include "storage/shm_mq.h" -extern void pq_redirect_to_shm_mq(shm_mq *, shm_mq_handle *); +extern void pq_redirect_to_shm_mq(dsm_segment *seg, shm_mq_handle *mqh); extern void pq_set_parallel_master(pid_t pid, BackendId backend_id); extern void pq_parse_errornotice(StringInfo str, ErrorData *edata);