On Wed, Jun 21, 2017 at 06:44:09PM -0400, Tom Lane wrote:
> Today, lorikeet failed with a new variant on the bgworker start crash:
> 
> https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=lorikeet&dt=2017-06-21%2020%3A29%3A10
> 
> This one is even more exciting than the last one, because it sure looks
> like the crashing bgworker took the postmaster down with it.  That is
> Not Supposed To Happen.
> 
> Wondering if we broke something here recently, I tried to reproduce it
> on a Linux machine by adding a randomized Assert failure in
> shm_mq_set_sender.  I don't see any such problem, even with EXEC_BACKEND;
> we recover from the crash as-expected.
> 
> So I'm starting to get a distinct feeling that there's something wrong
> with the cygwin port.  But I dunno what.

I think signal blocking broke on Cygwin.

On a system (gcc 5.4.0, CYGWIN_NT-10.0 2.7.0(0.306/5/3) 2017-02-12 13:18
x86_64) that reproduces lorikeet's symptoms, I instrumented the postmaster as
attached.  The patch's small_parallel.sql is a subset of select_parallel.sql
sufficient to reproduce the mq_sender Assert failure and the postmaster silent
exit.  (It occasionally needed hundreds of iterations to do so.)  The parallel
query normally starts four bgworkers; when the mq_sender Assert fired, the
test had started five workers in response to four registrations.

The postmaster.c instrumentation regularly detects sigusr1_handler() calls
while another sigusr1_handler() is already on the stack:

6328 2017-08-02 07:25:42.788 GMT LOG:  forbid signals @ sigusr1_handler
6328 2017-08-02 07:25:42.788 GMT DEBUG:  saw slot-0 registration, want 0
6328 2017-08-02 07:25:42.788 GMT DEBUG:  saw slot-0 registration, want 1
6328 2017-08-02 07:25:42.788 GMT DEBUG:  slot 1 not yet registered
6328 2017-08-02 07:25:42.789 GMT DEBUG:  registering background worker 
"parallel worker for PID 4776" (slot 1)
6328 2017-08-02 07:25:42.789 GMT DEBUG:  saw slot-1 registration, want 2
6328 2017-08-02 07:25:42.789 GMT DEBUG:  saw slot-0 registration, want 2
6328 2017-08-02 07:25:42.789 GMT DEBUG:  slot 2 not yet registered
6328 2017-08-02 07:25:42.789 GMT DEBUG:  registering background worker 
"parallel worker for PID 4776" (slot 2)
6328 2017-08-02 07:25:42.789 GMT DEBUG:  saw slot-2 registration, want 3
6328 2017-08-02 07:25:42.789 GMT DEBUG:  saw slot-1 registration, want 3
6328 2017-08-02 07:25:42.789 GMT DEBUG:  saw slot-0 registration, want 3
6328 2017-08-02 07:25:42.789 GMT DEBUG:  slot 3 not yet registered
6328 2017-08-02 07:25:42.789 GMT DEBUG:  registering background worker 
"parallel worker for PID 4776" (slot 3)
6328 2017-08-02 07:25:42.789 GMT DEBUG:  saw slot-3 registration, want 4
6328 2017-08-02 07:25:42.789 GMT DEBUG:  saw slot-2 registration, want 4
6328 2017-08-02 07:25:42.789 GMT DEBUG:  saw slot-1 registration, want 4
6328 2017-08-02 07:25:42.789 GMT DEBUG:  saw slot-0 registration, want 4
6328 2017-08-02 07:25:42.789 GMT DEBUG:  slot 4 not yet registered
6328 2017-08-02 07:25:42.789 GMT DEBUG:  registering background worker 
"parallel worker for PID 4776" (slot 4)
6328 2017-08-02 07:25:42.789 GMT DEBUG:  starting background worker process 
"parallel worker for PID 4776"
6328 2017-08-02 07:25:42.790 GMT LOG:  forbid signals @ sigusr1_handler
6328 2017-08-02 07:25:42.790 GMT WARNING:  signals already forbidden @ 
sigusr1_handler
6328 2017-08-02 07:25:42.790 GMT LOG:  permit signals @ sigusr1_handler

postmaster algorithms rely on the PG_SETMASK() calls preventing that.  Without
such protection, duplicate bgworkers are an understandable result.  I caught
several other assertions; the PMChildFlags failure is another case of
duplicate postmaster children:

      6 TRAP: FailedAssertion("!(entry->trans == ((void *)0))", File: 
"pgstat.c", Line: 871)
      3 TRAP: FailedAssertion("!(PMSignalState->PMChildFlags[slot] == 1)", 
File: "pmsignal.c", Line: 229)
     20 TRAP: FailedAssertion("!(RefCountErrors == 0)", File: "bufmgr.c", Line: 
2523)
     21 TRAP: FailedAssertion("!(vmq->mq_sender == ((void *)0))", File: 
"shm_mq.c", Line: 221)
     Also, got a few "select() failed in postmaster: Bad address"

I suspect a Cygwin signals bug.  I'll try to distill a self-contained test
case for the Cygwin hackers.  The lack of failures on buildfarm member brolga
argues that older Cygwin is not affected.
diff --git a/src/backend/access/transam/parallel.c 
b/src/backend/access/transam/parallel.c
index 17b1038..f42034d 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -985,6 +985,8 @@ ParallelWorkerMain(Datum main_arg)
        error_queue_space = shm_toc_lookup(toc, PARALLEL_KEY_ERROR_QUEUE, 
false);
        mq = (shm_mq *) (error_queue_space +
                                         ParallelWorkerNumber * 
PARALLEL_ERROR_QUEUE_SIZE);
+       write_stderr("PID %d claiming queue %d (%p)\n",
+                                MyProc->pid, ParallelWorkerNumber, mq);
        shm_mq_set_sender(mq, MyProc);
        mqh = shm_mq_attach(mq, seg, NULL);
        pq_redirect_to_shm_mq(seg, mqh);
diff --git a/src/backend/postmaster/bgworker.c 
b/src/backend/postmaster/bgworker.c
index 28af6f0..e393d31 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -219,14 +219,18 @@ FindRegisteredWorkerBySlotNumber(int slotno)
 {
        slist_iter      siter;
 
+       /* elog(DEBUG1, "looking for slot-%d registration", slotno); */
        slist_foreach(siter, &BackgroundWorkerList)
        {
                RegisteredBgWorker *rw;
 
                rw = slist_container(RegisteredBgWorker, rw_lnode, siter.cur);
+               elog(DEBUG1, "saw slot-%d registration, want %d",
+                        rw->rw_shmem_slot, slotno);
                if (rw->rw_shmem_slot == slotno)
                        return rw;
        }
+       /* elog(DEBUG1, "did not find slot-%d registration", slotno); */
 
        return NULL;
 }
@@ -297,6 +301,7 @@ BackgroundWorkerStateChange(void)
                        }
                        continue;
                }
+               elog(DEBUG1, "slot %d not yet registered", slotno);
 
                /*
                 * If the worker is marked for termination, we don't need to 
add it to
@@ -390,8 +395,8 @@ BackgroundWorkerStateChange(void)
 
                /* Log it! */
                ereport(DEBUG1,
-                               (errmsg("registering background worker \"%s\"",
-                                               rw->rw_worker.bgw_name)));
+                               (errmsg("registering background worker \"%s\" 
(slot %d)",
+                                               rw->rw_worker.bgw_name, 
rw->rw_shmem_slot)));
 
                slist_push_head(&BackgroundWorkerList, &rw->rw_lnode);
        }
@@ -422,8 +427,8 @@ ForgetBackgroundWorker(slist_mutable_iter *cur)
        slot->in_use = false;
 
        ereport(DEBUG1,
-                       (errmsg("unregistering background worker \"%s\"",
-                                       rw->rw_worker.bgw_name)));
+                       (errmsg("unregistering background worker \"%s\" (slot 
%d)",
+                                       rw->rw_worker.bgw_name, 
rw->rw_shmem_slot)));
 
        slist_delete_current(cur);
        free(rw);
diff --git a/src/backend/postmaster/postmaster.c 
b/src/backend/postmaster/postmaster.c
index 83e99b7..5fb5db1 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -561,6 +561,34 @@ int                        postmaster_alive_fds[2] = {-1, 
-1};
 HANDLE         PostmasterHandle;
 #endif
 
+static bool siglevel = false;
+static void sigforbid(const char *location)
+{
+       int save_errno;
+
+       PG_SETMASK(&BlockSig);
+
+       save_errno = errno;
+       elog(LOG, "forbid signals @ %s", location);
+       if (siglevel)
+               elog(WARNING, "signals already forbidden @ %s", location);
+       siglevel = true;
+       errno = save_errno;
+}
+static void sigpermit(const char *location)
+{
+       int save_errno;
+
+       save_errno = errno;
+       elog(LOG, "permit signals @ %s", location);
+       if (!siglevel)
+               elog(WARNING, "signals already permitted @ %s", location);
+       siglevel = false;
+       errno = save_errno;
+
+       PG_SETMASK(&UnBlockSig);
+}
+
 /*
  * Postmaster main entry point
  */
@@ -629,7 +657,7 @@ PostmasterMain(int argc, char *argv[])
         * postmaster/checkpointer.c.
         */
        pqinitmask();
-       PG_SETMASK(&BlockSig);
+       sigforbid(__FUNCTION__);
 
        pqsignal_no_restart(SIGHUP, SIGHUP_handler);    /* reread config file 
and
                                                                                
                         * have children do same */
@@ -1686,12 +1714,12 @@ ServerLoop(void)
 
                if (pmState == PM_WAIT_DEAD_END)
                {
-                       PG_SETMASK(&UnBlockSig);
+                       sigpermit("PM_WAIT_DEAD_END");
 
                        pg_usleep(100000L); /* 100 msec seems reasonable */
                        selres = 0;
 
-                       PG_SETMASK(&BlockSig);
+                       sigforbid("PM_WAIT_DEAD_END");
                }
                else
                {
@@ -1701,11 +1729,11 @@ ServerLoop(void)
                        /* Needs to run with blocked signals! */
                        DetermineSleepTime(&timeout);
 
-                       PG_SETMASK(&UnBlockSig);
+                       sigpermit("select");
 
                        selres = select(nSockets, &rmask, NULL, NULL, &timeout);
 
-                       PG_SETMASK(&BlockSig);
+                       sigforbid("select");
                }
 
                /* Now check the select() result */
@@ -2507,7 +2535,7 @@ SIGHUP_handler(SIGNAL_ARGS)
 {
        int                     save_errno = errno;
 
-       PG_SETMASK(&BlockSig);
+       sigforbid(__FUNCTION__);
 
        if (Shutdown <= SmartShutdown)
        {
@@ -2566,7 +2594,7 @@ SIGHUP_handler(SIGNAL_ARGS)
 #endif
        }
 
-       PG_SETMASK(&UnBlockSig);
+       sigpermit(__FUNCTION__);
 
        errno = save_errno;
 }
@@ -2580,7 +2608,7 @@ pmdie(SIGNAL_ARGS)
 {
        int                     save_errno = errno;
 
-       PG_SETMASK(&BlockSig);
+       sigforbid(__FUNCTION__);
 
        ereport(DEBUG2,
                        (errmsg_internal("postmaster received signal %d",
@@ -2737,7 +2765,7 @@ pmdie(SIGNAL_ARGS)
                        break;
        }
 
-       PG_SETMASK(&UnBlockSig);
+       sigpermit(__FUNCTION__);
 
        errno = save_errno;
 }
@@ -2752,7 +2780,7 @@ reaper(SIGNAL_ARGS)
        int                     pid;                    /* process id of dead 
child process */
        int                     exitstatus;             /* its exit status */
 
-       PG_SETMASK(&BlockSig);
+       sigforbid(__FUNCTION__);
 
        ereport(DEBUG4,
                        (errmsg_internal("reaping dead processes")));
@@ -3051,7 +3079,7 @@ reaper(SIGNAL_ARGS)
        PostmasterStateMachine();
 
        /* Done with signal handler */
-       PG_SETMASK(&UnBlockSig);
+       sigpermit(__FUNCTION__);
 
        errno = save_errno;
 }
@@ -4765,7 +4793,7 @@ SubPostmasterMain(int argc, char *argv[])
 
        /* In EXEC_BACKEND case we will not have inherited these settings */
        pqinitmask();
-       PG_SETMASK(&BlockSig);
+       sigforbid(__FUNCTION__);
 
        /* Read in remaining GUC variables */
        read_nondefault_variables();
@@ -4957,7 +4985,7 @@ sigusr1_handler(SIGNAL_ARGS)
 {
        int                     save_errno = errno;
 
-       PG_SETMASK(&BlockSig);
+       sigforbid(__FUNCTION__);
 
        /* Process background worker state change. */
        if (CheckPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE))
@@ -5091,7 +5119,7 @@ sigusr1_handler(SIGNAL_ARGS)
                signal_child(StartupPID, SIGUSR2);
        }
 
-       PG_SETMASK(&UnBlockSig);
+       sigpermit(__FUNCTION__);
 
        errno = save_errno;
 }
@@ -5511,13 +5539,13 @@ BackgroundWorkerInitializeConnectionByOid(Oid dboid, 
Oid useroid)
 void
 BackgroundWorkerBlockSignals(void)
 {
-       PG_SETMASK(&BlockSig);
+       sigforbid(__FUNCTION__);
 }
 
 void
 BackgroundWorkerUnblockSignals(void)
 {
-       PG_SETMASK(&UnBlockSig);
+       sigpermit(__FUNCTION__);
 }
 
 #ifdef EXEC_BACKEND
diff --git a/src/backend/utils/error/assert.c b/src/backend/utils/error/assert.c
index 2ef7792..9f4e597 100644
--- a/src/backend/utils/error/assert.c
+++ b/src/backend/utils/error/assert.c
@@ -42,6 +42,8 @@ ExceptionalCondition(const char *conditionName,
        /* Usually this shouldn't be needed, but make sure the msg went out */
        fflush(stderr);
 
+       for(;;);
+
 #ifdef SLEEP_ON_ASSERT
 
        /*
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 918db0a..e0ef9ec 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -554,6 +554,7 @@ errfinish(int dummy,...)
                 */
                fflush(stdout);
                fflush(stderr);
+               for(;;);
                abort();
        }
 
diff --git a/src/test/regress/expected/small_parallel.out 
b/src/test/regress/expected/small_parallel.out
new file mode 100644
index 0000000..a17ed33
--- /dev/null
+++ b/src/test/regress/expected/small_parallel.out
@@ -0,0 +1,44 @@
+-- Serializable isolation would disable parallel query, so explicitly use an
+-- arbitrary other level.
+begin isolation level repeatable read;
+-- encourage use of parallel plans
+set parallel_setup_cost=0;
+set parallel_tuple_cost=0;
+set min_parallel_table_scan_size=0;
+set max_parallel_workers_per_gather=4;
+-- test parallel bitmap heap scan.
+set enable_seqscan to off;
+set enable_indexscan to off;
+set enable_hashjoin to off;
+set enable_mergejoin to off;
+set enable_material to off;
+-- test prefetching, if the platform allows it
+DO $$
+BEGIN
+ SET effective_io_concurrency = 50;
+EXCEPTION WHEN invalid_parameter_value THEN
+END $$;
+set work_mem='64kB';  --set small work mem to force lossy pages
+explain (costs off)
+       select count(*) from tenk1, tenk2 where tenk1.hundred > 1 and 
tenk2.thousand=0;
+                         QUERY PLAN                         
+------------------------------------------------------------
+ Aggregate
+   ->  Nested Loop
+         ->  Seq Scan on tenk2
+               Filter: (thousand = 0)
+         ->  Gather
+               Workers Planned: 4
+               ->  Parallel Bitmap Heap Scan on tenk1
+                     Recheck Cond: (hundred > 1)
+                     ->  Bitmap Index Scan on tenk1_hundred
+                           Index Cond: (hundred > 1)
+(10 rows)
+
+select count(*) from tenk1, tenk2 where tenk1.hundred > 1 and tenk2.thousand=0;
+ count 
+-------
+ 98000
+(1 row)
+
+rollback;
diff --git a/src/test/regress/sql/small_parallel.sql 
b/src/test/regress/sql/small_parallel.sql
new file mode 100644
index 0000000..1598d2e
--- /dev/null
+++ b/src/test/regress/sql/small_parallel.sql
@@ -0,0 +1,28 @@
+-- Serializable isolation would disable parallel query, so explicitly use an
+-- arbitrary other level.
+begin isolation level repeatable read;
+
+-- encourage use of parallel plans
+set parallel_setup_cost=0;
+set parallel_tuple_cost=0;
+set min_parallel_table_scan_size=0;
+set max_parallel_workers_per_gather=4;
+
+-- test parallel bitmap heap scan.
+set enable_seqscan to off;
+set enable_indexscan to off;
+set enable_hashjoin to off;
+set enable_mergejoin to off;
+set enable_material to off;
+-- test prefetching, if the platform allows it
+DO $$
+BEGIN
+ SET effective_io_concurrency = 50;
+EXCEPTION WHEN invalid_parameter_value THEN
+END $$;
+set work_mem='64kB';  --set small work mem to force lossy pages
+explain (costs off)
+       select count(*) from tenk1, tenk2 where tenk1.hundred > 1 and 
tenk2.thousand=0;
+select count(*) from tenk1, tenk2 where tenk1.hundred > 1 and tenk2.thousand=0;
+
+rollback;
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to