Allow DSM allocation to be interrupted.
authorThomas Munro <tmunro@postgresql.org>
Tue, 18 Sep 2018 10:56:36 +0000 (22:56 +1200)
committerThomas Munro <tmunro@postgresql.org>
Tue, 18 Sep 2018 10:56:36 +0000 (22:56 +1200)
Chris Travers reported that the startup process can repeatedly try to
cancel a backend that is in a posix_fallocate()/EINTR loop and cause it
to loop forever.  Teach the retry loop to give up if an interrupt is
pending.  Don't actually check for interrupts in that loop though,
because a non-local exit would skip some clean-up code in the caller.

Back-patch to 9.4 where DSM was added (and posix_fallocate() was later
back-patched).

Author: Chris Travers
Reviewed-by: Ildar Musin, Murat Kabilov, Oleksii Kliukin
Tested-by: Oleksii Kliukin
Discussion: https://postgr.es/m/CAN-RpxB-oeZve_J3SM_6%3DHXPmvEG%3DHX%2B9V9pi8g2YR7YW0rBBg%40mail.gmail.com

src/backend/storage/ipc/dsm_impl.c

index 77e1bab54bef1723f3c11fe0f55fe8ff4a76926b..70f899e7658b34887745d8c8038e6d2b434ffd9a 100644 (file)
@@ -47,6 +47,7 @@
  */
 
 #include "postgres.h"
+#include "miscadmin.h"
 
 #include <fcntl.h>
 #include <unistd.h>
@@ -330,6 +331,14 @@ dsm_impl_posix(dsm_op op, dsm_handle handle, Size request_size,
            shm_unlink(name);
        errno = save_errno;
 
+       /*
+        * If we received a query cancel or termination signal, we will have
+        * EINTR set here.  If the caller said that errors are OK here, check
+        * for interrupts immediately.
+        */
+       if (errno == EINTR && elevel >= ERROR)
+           CHECK_FOR_INTERRUPTS();
+
        ereport(elevel,
                (errcode_for_dynamic_shared_memory(),
                 errmsg("could not resize shared memory segment \"%s\" to %zu bytes: %m",
@@ -419,11 +428,15 @@ dsm_impl_posix_resize(int fd, off_t size)
 #if defined(HAVE_POSIX_FALLOCATE) && defined(__linux__)
    if (rc == 0)
    {
-       /* We may get interrupted, if so just retry. */
+       /*
+        * We may get interrupted.  If so, just retry unless there is an
+        * interrupt pending.  This avoids the possibility of looping forever
+        * if another backend is repeatedly trying to interrupt us.
+        */
        do
        {
            rc = posix_fallocate(fd, 0, size);
-       } while (rc == EINTR);
+       } while (rc == EINTR && !(ProcDiePending || QueryCancelPending));
 
        /*
         * The caller expects errno to be set, but posix_fallocate() doesn't