Skip to content

Commit 87abcb4

Browse files
committed
shm_mq: Fix failure to notice a dead counterparty when nowait is used.
The shm_mq mechanism was intended to optionally notice when the process on the other end of the queue fails to attach to the queue. It does this by allowing the user to pass a BackgroundWorkerHandle; if the background worker in question is launched and dies without attaching to the queue, then we know it never will. This logic works OK in blocking mode, but when called with nowait = true we fail to notice that this has happened due to an asymmetry in the logic. Repair. Reported off-list by Rushabh Lathia. Patch by me.
1 parent 4f33572 commit 87abcb4

File tree

1 file changed

+48
-0
lines changed

1 file changed

+48
-0
lines changed

src/backend/storage/ipc/shm_mq.c

+48
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ static shm_mq_result shm_mq_send_bytes(shm_mq_handle *mq, Size nbytes,
142142
void *data, bool nowait, Size *bytes_written);
143143
static shm_mq_result shm_mq_receive_bytes(shm_mq *mq, Size bytes_needed,
144144
bool nowait, Size *nbytesp, void **datap);
145+
static bool shm_mq_counterparty_gone(volatile shm_mq *mq,
146+
BackgroundWorkerHandle *handle);
145147
static bool shm_mq_wait_internal(volatile shm_mq *mq, PGPROC *volatile * ptr,
146148
BackgroundWorkerHandle *handle);
147149
static uint64 shm_mq_get_bytes_read(volatile shm_mq *mq, bool *detached);
@@ -403,6 +405,8 @@ shm_mq_receive(shm_mq_handle *mqh, Size *nbytesp, void **datap, bool nowait)
403405
{
404406
if (nowait)
405407
{
408+
if (shm_mq_counterparty_gone(mq, mqh->mqh_handle))
409+
return SHM_MQ_DETACHED;
406410
if (shm_mq_get_sender(mq) == NULL)
407411
return SHM_MQ_WOULD_BLOCK;
408412
}
@@ -689,6 +693,11 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, void *data, bool nowait,
689693
*/
690694
if (nowait)
691695
{
696+
if (shm_mq_counterparty_gone(mq, mqh->mqh_handle))
697+
{
698+
*bytes_written = sent;
699+
return SHM_MQ_DETACHED;
700+
}
692701
if (shm_mq_get_receiver(mq) == NULL)
693702
{
694703
*bytes_written = sent;
@@ -842,6 +851,45 @@ shm_mq_receive_bytes(shm_mq *mq, Size bytes_needed, bool nowait,
842851
}
843852
}
844853

854+
/*
855+
* Test whether a counterparty who may not even be alive yet is definitely gone.
856+
*/
857+
static bool
858+
shm_mq_counterparty_gone(volatile shm_mq *mq, BackgroundWorkerHandle *handle)
859+
{
860+
bool detached;
861+
pid_t pid;
862+
863+
/* Acquire the lock just long enough to check the pointer. */
864+
SpinLockAcquire(&mq->mq_mutex);
865+
detached = mq->mq_detached;
866+
SpinLockRelease(&mq->mq_mutex);
867+
868+
/* If the queue has been detached, counterparty is definitely gone. */
869+
if (detached)
870+
return true;
871+
872+
/* If there's a handle, check worker status. */
873+
if (handle != NULL)
874+
{
875+
BgwHandleStatus status;
876+
877+
/* Check for unexpected worker death. */
878+
status = GetBackgroundWorkerPid(handle, &pid);
879+
if (status != BGWH_STARTED && status != BGWH_NOT_YET_STARTED)
880+
{
881+
/* Mark it detached, just to make it official. */
882+
SpinLockAcquire(&mq->mq_mutex);
883+
mq->mq_detached = true;
884+
SpinLockRelease(&mq->mq_mutex);
885+
return true;
886+
}
887+
}
888+
889+
/* Counterparty is not definitively gone. */
890+
return false;
891+
}
892+
845893
/*
846894
* This is used when a process is waiting for its counterpart to attach to the
847895
* queue. We exit when the other process attaches as expected, or, if

0 commit comments

Comments
 (0)