Fix a rare race condition when commit_siblings > 0 and a transaction commits
authorHeikki Linnakangas <[email protected]>
Tue, 31 Mar 2009 05:18:47 +0000 (05:18 +0000)
committerHeikki Linnakangas <[email protected]>
Tue, 31 Mar 2009 05:18:47 +0000 (05:18 +0000)
at the same instant as a new backend is spawned. Since CountActiveBackends()
doesn't hold ProcArrayLock, it needs to be prepared for the case that a
pointer at the end of the proc array is still NULL even though numProcs says
it should be valid, since it doesn't hold ProcArrayLock. Backpatch to 8.1.
8.0 and earlier had this right, but it was broken in the split of PGPROC and
sinval shared memory arrays.

Per report and proposal by Marko Kreen.

src/backend/storage/ipc/procarray.c

index b59d8e5da1c693584172e3a670c3ccad44bb637a..a51fa065d3f470d5b355596823a016c89e931f32 100644 (file)
@@ -164,6 +164,7 @@ ProcArrayRemove(PGPROC *proc)
                if (arrayP->procs[index] == proc)
                {
                        arrayP->procs[index] = arrayP->procs[arrayP->numProcs - 1];
+                       arrayP->procs[arrayP->numProcs - 1] = NULL; /* for debugging */
                        arrayP->numProcs--;
                        LWLockRelease(ProcArrayLock);
                        return;
@@ -756,6 +757,20 @@ CountActiveBackends(void)
        {
                PGPROC     *proc = arrayP->procs[index];
 
+               /*
+                * Since we're not holding a lock, need to check that the pointer is
+                * valid. Someone holding the lock could have incremented numProcs
+                * already, but not yet inserted a valid pointer to the array.
+                *
+                * If someone just decremented numProcs, 'proc' could also point to a
+                * PGPROC entry that's no longer in the array. It still points to a
+                * PGPROC struct, though, because freed PGPPROC entries just go to
+                * the free list and are recycled. Its contents are nonsense in that
+                * case, but that's acceptable for this function.
+                */
+               if (proc != NULL)
+                       continue;
+
                if (proc == MyProc)
                        continue;                       /* do not count myself */
                if (proc->pid == 0)