Skip to content

Commit b2ccb5f

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 31ba62c commit b2ccb5f

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
const 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);
@@ -499,6 +501,8 @@ shm_mq_receive(shm_mq_handle *mqh, Size *nbytesp, void **datap, bool nowait)
499501
{
500502
if (nowait)
501503
{
504+
if (shm_mq_counterparty_gone(mq, mqh->mqh_handle))
505+
return SHM_MQ_DETACHED;
502506
if (shm_mq_get_sender(mq) == NULL)
503507
return SHM_MQ_WOULD_BLOCK;
504508
}
@@ -794,6 +798,11 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
794798
*/
795799
if (nowait)
796800
{
801+
if (shm_mq_counterparty_gone(mq, mqh->mqh_handle))
802+
{
803+
*bytes_written = sent;
804+
return SHM_MQ_DETACHED;
805+
}
797806
if (shm_mq_get_receiver(mq) == NULL)
798807
{
799808
*bytes_written = sent;
@@ -947,6 +956,45 @@ shm_mq_receive_bytes(shm_mq *mq, Size bytes_needed, bool nowait,
947956
}
948957
}
949958

959+
/*
960+
* Test whether a counterparty who may not even be alive yet is definitely gone.
961+
*/
962+
static bool
963+
shm_mq_counterparty_gone(volatile shm_mq *mq, BackgroundWorkerHandle *handle)
964+
{
965+
bool detached;
966+
pid_t pid;
967+
968+
/* Acquire the lock just long enough to check the pointer. */
969+
SpinLockAcquire(&mq->mq_mutex);
970+
detached = mq->mq_detached;
971+
SpinLockRelease(&mq->mq_mutex);
972+
973+
/* If the queue has been detached, counterparty is definitely gone. */
974+
if (detached)
975+
return true;
976+
977+
/* If there's a handle, check worker status. */
978+
if (handle != NULL)
979+
{
980+
BgwHandleStatus status;
981+
982+
/* Check for unexpected worker death. */
983+
status = GetBackgroundWorkerPid(handle, &pid);
984+
if (status != BGWH_STARTED && status != BGWH_NOT_YET_STARTED)
985+
{
986+
/* Mark it detached, just to make it official. */
987+
SpinLockAcquire(&mq->mq_mutex);
988+
mq->mq_detached = true;
989+
SpinLockRelease(&mq->mq_mutex);
990+
return true;
991+
}
992+
}
993+
994+
/* Counterparty is not definitively gone. */
995+
return false;
996+
}
997+
950998
/*
951999
* This is used when a process is waiting for its counterpart to attach to the
9521000
* queue. We exit when the other process attaches as expected, or, if

0 commit comments

Comments
 (0)