On 05/06/2014 02:44 PM, Andres Freund wrote:
On 2014-05-05 13:41:00 +0300, Heikki Linnakangas wrote:
+/*
+ * Exit hook to unlock the global transaction entry we're working on.
+ */
+static void
+AtProcExit_Twophase(int code, Datum arg)
+{
+       /* same logic as abort */
+       AtAbort_Twophase();
+}
+
+/*
+ * Abort hook to unlock the global transaction entry we're working on.
+ */
+void
+AtAbort_Twophase(void)
+{
+       if (MyLockedGxact == NULL)
+               return;
+
+       /*
+        * If we were in process of preparing the transaction, but haven't
+        * written the WAL record yet, remove the global transaction entry.
+        * Same if we are in the process of finishing an already-prepared
+        * transaction, and fail after having already written the WAL 2nd
+        * phase commit or rollback record.
+        *
+        * After that it's too late to abort, so just unlock the 
GlobalTransaction
+        * entry.  We might not have transfered all locks and other state to the
+        * prepared transaction yet, so this is a bit bogus, but it's the best 
we
+        * can do.
+        */
+       if (!MyLockedGxact->valid)
+       {
+               RemoveGXact(MyLockedGxact);
+       }
+       else
+       {
+               LWLockAcquire(TwoPhaseStateLock, LW_EXCLUSIVE);
+
+               MyLockedGxact->locking_backend = InvalidBackendId;
+
+               LWLockRelease(TwoPhaseStateLock);
+       }
+       MyLockedGxact = NULL;
+}

Is it guaranteed that all paths have called LWLockReleaseAll()
before calling the proc exit hooks? Otherwise we might end up waiting
for ourselves...

Hmm. AbortTransaction() will release locks before we get here, but the before_shmem_exit() callpath will not. So an elog(FATAL), while holding TwoPhaseStateLock would cause us to deadlock with ourself. But there are no such elogs.

I copied this design from async.c, which is quite similar, so if there's a problem that ought to be fixed too. And there are other more complicated before_shmem callbacks that worry me more, like createdb_failure_callback(). But I think they're all all right.

  /*
   * MarkAsPreparing
@@ -261,29 +329,15 @@ MarkAsPreparing(TransactionId xid, const char *gid,
                                 errmsg("prepared transactions are disabled"),
                          errhint("Set max_prepared_transactions to a nonzero 
value.")));

-       LWLockAcquire(TwoPhaseStateLock, LW_EXCLUSIVE);
-
-       /*
-        * First, find and recycle any gxacts that failed during prepare. We do
-        * this partly to ensure we don't mistakenly say their GIDs are still
-        * reserved, and partly so we don't fail on out-of-slots unnecessarily.
-        */
-       for (i = 0; i < TwoPhaseState->numPrepXacts; i++)
+       /* on first call, register the exit hook */
+       if (!twophaseExitRegistered)
        {
-               gxact = TwoPhaseState->prepXacts[i];
-               if (!gxact->valid && !TransactionIdIsActive(gxact->locking_xid))
-               {
-                       /* It's dead Jim ... remove from the active array */
-                       TwoPhaseState->numPrepXacts--;
-                       TwoPhaseState->prepXacts[i] = 
TwoPhaseState->prepXacts[TwoPhaseState->numPrepXacts];
-                       /* and put it back in the freelist */
-                       gxact->next = TwoPhaseState->freeGXacts;
-                       TwoPhaseState->freeGXacts = gxact;
-                       /* Back up index count too, so we don't miss scanning 
one */
-                       i--;
-               }
+               before_shmem_exit(AtProcExit_Twophase, 0);
+               twophaseExitRegistered = true;
        }

It's not particularly nice to register shmem exit hooks in the middle of
normal processing because it makes it impossible to use
cancel_before_shmem_exit() previously registered hooks. I think this
should be registered at startup, if max_prepared_xacts > 0.

<shrug>. async.c and namespace.c does the same, and it hasn't been a problem.

I committed this now, but please let me know if you see a concrete problem with the locks.

- Heikki


--
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