Allow WaitLatch() to be used without a latch.
authorThomas Munro <[email protected]>
Wed, 23 Sep 2020 03:17:30 +0000 (15:17 +1200)
committerThomas Munro <[email protected]>
Wed, 23 Sep 2020 03:17:30 +0000 (15:17 +1200)
Due to flaws in commit 3347c982bab, using WaitLatch() without
WL_LATCH_SET could cause an assertion failure or crash.  Repair.

While here, also add a check that the latch we're switching to belongs
to this backend, when changing from one latch to another.

Discussion: https://postgr.es/m/CA%2BhUKGK1607VmtrDUHQXrsooU%3Dap4g4R2yaoByWOOA3m8xevUQ%40mail.gmail.com

src/backend/storage/ipc/latch.c

index 4153cc85579f7ab6372aacb0b3b13e68c6feab16..63c6c97536013a1fd345024d063064c518759bba 100644 (file)
@@ -924,7 +924,22 @@ ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch)
 
    if (events == WL_LATCH_SET)
    {
+       if (latch && latch->owner_pid != MyProcPid)
+           elog(ERROR, "cannot wait on a latch owned by another process");
        set->latch = latch;
+       /*
+        * On Unix, we don't need to modify the kernel object because the
+        * underlying pipe is the same for all latches so we can return
+        * immediately.  On Windows, we need to update our array of handles,
+        * but we leave the old one in place and tolerate spurious wakeups if
+        * the latch is disabled.
+        */
+#if defined(WAIT_USE_WIN32)
+       if (!latch)
+           return;
+#else
+       return;
+#endif
    }
 
 #if defined(WAIT_USE_EPOLL)
@@ -1386,7 +1401,7 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
            /* There's data in the self-pipe, clear it. */
            drainSelfPipe();
 
-           if (set->latch->is_set)
+           if (set->latch && set->latch->is_set)
            {
                occurred_events->fd = PGINVALID_SOCKET;
                occurred_events->events = WL_LATCH_SET;
@@ -1536,7 +1551,7 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
            /* There's data in the self-pipe, clear it. */
            drainSelfPipe();
 
-           if (set->latch->is_set)
+           if (set->latch && set->latch->is_set)
            {
                occurred_events->fd = PGINVALID_SOCKET;
                occurred_events->events = WL_LATCH_SET;
@@ -1645,7 +1660,7 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
            /* There's data in the self-pipe, clear it. */
            drainSelfPipe();
 
-           if (set->latch->is_set)
+           if (set->latch && set->latch->is_set)
            {
                occurred_events->fd = PGINVALID_SOCKET;
                occurred_events->events = WL_LATCH_SET;
@@ -1812,7 +1827,7 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
        if (!ResetEvent(set->latch->event))
            elog(ERROR, "ResetEvent failed: error code %lu", GetLastError());
 
-       if (set->latch->is_set)
+       if (set->latch && set->latch->is_set)
        {
            occurred_events->fd = PGINVALID_SOCKET;
            occurred_events->events = WL_LATCH_SET;