}
 
    /*
-    * Emit a WAL record if acquisition of this lock needs to be replayed in a
-    * standby server. Only AccessExclusiveLocks can conflict with lock types
-    * that read-only transactions can acquire in a standby server.
+    * Prepare to emit a WAL record if acquisition of this lock needs to be
+    * replayed in a standby server.
     *
-    * Make sure this definition matches the one in
-    * GetRunningTransactionLocks().
+    * Here we prepare to log; after lock is acquired we'll issue log record.
+    * This arrangement simplifies error recovery in case the preparation step
+    * fails.
     *
-    * First we prepare to log, then after lock acquired we issue log record.
+    * Only AccessExclusiveLocks can conflict with lock types that read-only
+    * transactions can acquire in a standby server. Make sure this definition
+    * matches the one in GetRunningTransactionLocks().
     */
    if (lockmode >= AccessExclusiveLock &&
        locktag->locktag_type == LOCKTAG_RELATION &&
     * lock type on a relation we have already locked using the fast-path, but
     * for now we don't worry about that case either.
     */
-   if (EligibleForRelationFastPath(locktag, lockmode)
-       && FastPathLocalUseCount < FP_LOCK_SLOTS_PER_BACKEND)
+   if (EligibleForRelationFastPath(locktag, lockmode) &&
+       FastPathLocalUseCount < FP_LOCK_SLOTS_PER_BACKEND)
    {
        uint32      fasthashcode = FastPathStrongLockHashPartition(hashcode);
        bool        acquired;
        LWLockRelease(MyProc->backendLock);
        if (acquired)
        {
+           /*
+            * The locallock might contain stale pointers to some old shared
+            * objects; we MUST reset these to null before considering the
+            * lock to be acquired via fast-path.
+            */
+           locallock->lock = NULL;
+           locallock->proclock = NULL;
            GrantLockLocal(locallock, owner);
            return LOCKACQUIRE_OK;
        }
    LWLockAcquire(partitionLock, LW_EXCLUSIVE);
 
    /*
-    * Find or create a proclock entry with this tag
+    * Find or create lock and proclock entries with this tag
+    *
+    * Note: if the locallock object already existed, it might have a pointer
+    * to the lock already ... but we should not assume that that pointer is
+    * valid, since a lock object with zero hold and request counts can go
+    * away anytime.  So we have to use SetupLockInTable() to recompute the
+    * lock and proclock pointers, even if they're already set.
     */
    proclock = SetupLockInTable(lockMethodTable, MyProc, locktag,
                                hashcode, lockmode);
    LWLockRelease(partitionLock);
 
    /*
-    * Emit a WAL record if acquisition of this lock need to be replayed in a
+    * Emit a WAL record if acquisition of this lock needs to be replayed in a
     * standby server.
     */
    if (log_lock)
 
    /*
     * Find or create a lock with this tag.
-    *
-    * Note: if the locallock object already existed, it might have a pointer
-    * to the lock already ... but we probably should not assume that that
-    * pointer is valid, since a lock object with no locks can go away
-    * anytime.
     */
    lock = (LOCK *) hash_search_with_hash_value(LockMethodLockHash,
                                                (const void *) locktag,
        return TRUE;
 
    /* Attempt fast release of any lock eligible for the fast path. */
-   if (EligibleForRelationFastPath(locktag, lockmode)
-       && FastPathLocalUseCount > 0)
+   if (EligibleForRelationFastPath(locktag, lockmode) &&
+       FastPathLocalUseCount > 0)
    {
        bool        released;
 
     * Normally, we don't need to re-find the lock or proclock, since we kept
     * their addresses in the locallock table, and they couldn't have been
     * removed while we were holding a lock on them.  But it's possible that
-    * the locks have been moved to the main hash table by another backend, in
-    * which case we might need to go look them up after all.
+    * the lock was taken fast-path and has since been moved to the main hash
+    * table by another backend, in which case we will need to look up the
+    * objects here.  We assume the lock field is NULL if so.
     */
    lock = locallock->lock;
    if (!lock)
    {
        PROCLOCKTAG proclocktag;
-       bool        found;
 
        Assert(EligibleForRelationFastPath(locktag, lockmode));
        lock = (LOCK *) hash_search_with_hash_value(LockMethodLockHash,
                                                    (const void *) locktag,
                                                    locallock->hashcode,
                                                    HASH_FIND,
-                                                   &found);
-       Assert(found && lock != NULL);
+                                                   NULL);
+       if (!lock)
+           elog(ERROR, "failed to re-find shared lock object");
        locallock->lock = lock;
 
        proclocktag.myLock = lock;
        proclocktag.myProc = MyProc;
        locallock->proclock = (PROCLOCK *) hash_search(LockMethodProcLockHash,
                                                       (void *) &proclocktag,
-                                                      HASH_FIND, &found);
-       Assert(found);
+                                                      HASH_FIND,
+                                                      NULL);
+       if (!locallock->proclock)
+           elog(ERROR, "failed to re-find shared proclock object");
    }
    LOCK_PRINT("LockRelease: found", lock, lockmode);
    proclock = locallock->proclock;
     * entries, then we scan the process's proclocks and get rid of those. We
     * do this separately because we may have multiple locallock entries
     * pointing to the same proclock, and we daren't end up with any dangling
-    * pointers.
+    * pointers.  Fast-path locks are cleaned up during the locallock table
+    * scan, though.
     */
    hash_seq_init(&status, LockMethodLocalHash);
 
 
        /*
         * If the lock or proclock pointers are NULL, this lock was taken via
-        * the relation fast-path.
+        * the relation fast-path (and is not known to have been transferred).
         */
        if (locallock->proclock == NULL || locallock->lock == NULL)
        {
            /*
             * If we don't currently hold the LWLock that protects our
             * fast-path data structures, we must acquire it before attempting
-            * to release the lock via the fast-path.
+            * to release the lock via the fast-path.  We will continue to
+            * hold the LWLock until we're done scanning the locallock table,
+            * unless we hit a transferred fast-path lock.  (XXX is this
+            * really such a good idea?  There could be a lot of entries ...)
             */
            if (!have_fast_path_lwlock)
            {
        RemoveLocalLock(locallock);
    }
 
+   /* Done with the fast-path data structures */
    if (have_fast_path_lwlock)
        LWLockRelease(MyProc->backendLock);
 
            Assert(!result);
            FAST_PATH_CLEAR_LOCKMODE(MyProc, f, lockmode);
            result = true;
+           /* we continue iterating so as to update FastPathLocalUseCount */
        }
        if (FAST_PATH_GET_BITS(MyProc, f) != 0)
            ++FastPathLocalUseCount;
                FAST_PATH_CLEAR_LOCKMODE(proc, f, lockmode);
            }
            LWLockRelease(partitionLock);
+
+           /* No need to examine remaining slots. */
+           break;
        }
        LWLockRelease(proc->backendLock);
    }
  * FastPathGetLockEntry
  *     Return the PROCLOCK for a lock originally taken via the fast-path,
  *     transferring it to the primary lock table if necessary.
+ *
+ * Note: caller takes care of updating the locallock object.
  */
 static PROCLOCK *
 FastPathGetRelationLockEntry(LOCALLOCK *locallock)
        FAST_PATH_CLEAR_LOCKMODE(MyProc, f, lockmode);
 
        LWLockRelease(partitionLock);
+
+       /* No need to examine remaining slots. */
+       break;
    }
 
    LWLockRelease(MyProc->backendLock);
                 */
                if (VirtualTransactionIdIsValid(vxid))
                    vxids[count++] = vxid;
+
+               /* No need to examine remaining slots. */
                break;
            }
 
 
  * shared memory.  We also track the number of lock acquisitions per
  * ResourceOwner, so that we can release just those locks belonging to a
  * particular ResourceOwner.
+ *
+ * When holding a lock taken "normally", the lock and proclock fields always
+ * point to the associated objects in shared memory.  However, if we acquired
+ * the lock via the fast-path mechanism, the lock and proclock fields are set
+ * to NULL, since there probably aren't any such objects in shared memory.
+ * (If the lock later gets promoted to normal representation, we may eventually
+ * update our locallock's lock/proclock fields after finding the shared
+ * objects.)
+ *
+ * Caution: a locallock object can be left over from a failed lock acquisition
+ * attempt.  In this case its lock/proclock fields are untrustworthy, since
+ * the shared lock object is neither held nor awaited, and hence is available
+ * to be reclaimed.  If nLocks > 0 then these pointers must either be valid or
+ * NULL, but when nLocks == 0 they should be considered garbage.
  */
 typedef struct LOCALLOCKTAG
 {
    LOCALLOCKTAG tag;           /* unique identifier of locallock entry */
 
    /* data */
-   LOCK       *lock;           /* associated LOCK object in shared mem */
-   PROCLOCK   *proclock;       /* associated PROCLOCK object in shmem */
+   LOCK       *lock;           /* associated LOCK object, if any */
+   PROCLOCK   *proclock;       /* associated PROCLOCK object, if any */
    uint32      hashcode;       /* copy of LOCKTAG's hash value */
    int64       nLocks;         /* total number of times lock is held */
    int         numLockOwners;  /* # of relevant ResourceOwners */
    int         maxLockOwners;  /* allocated size of array */
-   bool        holdsStrongLockCount;   /* bumped FastPathStrongRelatonLocks? */
+   bool        holdsStrongLockCount;   /* bumped FastPathStrongRelationLocks */
    LOCALLOCKOWNER *lockOwners; /* dynamically resizable array */
 } LOCALLOCK;