]> granicus.if.org Git - postgresql/commitdiff
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