Clean up the lock state properly when aborting because of early deadlock
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 4 Sep 2001 02:26:57 +0000 (02:26 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 4 Sep 2001 02:26:57 +0000 (02:26 +0000)
detection in ProcSleep().  Bug noted by Tomasz Zielonka --- how did this
escape detection for this long??

src/backend/storage/lmgr/proc.c

index 9fc91d06cdbeb524c157bb9a87391b7c2b7fd971..f491bc16f70b5918ef1bd3558c991208c45d807a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.104 2001/07/06 21:04:26 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.105 2001/09/04 02:26:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -506,16 +506,14 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable,
        SPINLOCK        spinlock = lockctl->masterLock;
        PROC_QUEUE *waitQueue = &(lock->waitProcs);
        int                     myHeldLocks = MyProc->heldLocks;
+       bool            early_deadlock = false;
        PROC       *proc;
        int                     i;
-
 #ifndef __BEOS__
        struct itimerval timeval,
                                dummy;
-
 #else
        bigtime_t       time_interval;
-
 #endif
 
        /*
@@ -535,7 +533,6 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable,
         * immediately.  This is the same as the test for immediate grant in
         * LockAcquire, except we are only considering the part of the wait
         * queue before my insertion point.
-        *
         */
        if (myHeldLocks != 0)
        {
@@ -550,9 +547,14 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable,
                                /* Must I wait for him ? */
                                if (lockctl->conflictTab[lockmode] & proc->heldLocks)
                                {
-                                       /* Yes, can report deadlock failure immediately */
-                                       MyProc->errType = STATUS_ERROR;
-                                       return STATUS_ERROR;
+                                       /*
+                                        * Yes, so we have a deadlock.  Easiest way to clean up
+                                        * correctly is to call RemoveFromWaitQueue(), but we
+                                        * can't do that until we are *on* the wait queue.
+                                        * So, set a flag to check below, and break out of loop.
+                                        */
+                                       early_deadlock = true;
+                                       break;
                                }
                                /* I must go before this waiter.  Check special case. */
                                if ((lockctl->conflictTab[lockmode] & aheadRequests) == 0 &&
@@ -600,7 +602,19 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable,
        MyProc->waitHolder = holder;
        MyProc->waitLockMode = lockmode;
 
-       MyProc->errType = STATUS_OK;/* initialize result for success */
+       MyProc->errType = STATUS_OK; /* initialize result for success */
+
+       /*
+        * If we detected deadlock, give up without waiting.  This must agree
+        * with HandleDeadLock's recovery code, except that we shouldn't release
+        * the semaphore since we haven't tried to lock it yet.
+        */
+       if (early_deadlock)
+       {
+               RemoveFromWaitQueue(MyProc);
+               MyProc->errType = STATUS_ERROR;
+               return STATUS_ERROR;
+       }
 
        /* mark that we are waiting for a lock */
        waitingForLock = true;
@@ -693,6 +707,10 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable,
  *
  *      Also remove the process from the wait queue and set its links invalid.
  *      RETURN: the next process in the wait queue.
+ *
+ * XXX: presently, this code is only used for the "success" case, and only
+ * works correctly for that case.  To clean up in failure case, would need
+ * to twiddle the lock's request counts too --- see RemoveFromWaitQueue.
  */
 PROC *
 ProcWakeup(PROC *proc, int errType)