]> granicus.if.org Git - postgresql/blobdiff - src/backend/storage/lmgr/lock.c
Restructure LOCKTAG as per discussions of a couple months ago.
[postgresql] / src / backend / storage / lmgr / lock.c
index 76b2ff9b480451ef5ad04ff2d2b456c865734ae5..576fc205299eebe48047cab9f3dcdf5169ab35fe 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.146 2005/02/04 02:04:53 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.150 2005/04/29 22:28:24 tgl Exp $
  *
  * NOTES
  *       Outside modules can create a lock table and acquire/release
@@ -97,7 +97,7 @@ static const char *const lock_mode_names[] =
  * --------
  */
 
-int                    Trace_lock_oidmin = BootstrapObjectIdData;
+int                    Trace_lock_oidmin = FirstNormalObjectId;
 bool           Trace_locks = false;
 bool           Trace_userlocks = false;
 int                    Trace_lock_table = 0;
@@ -108,10 +108,11 @@ inline static bool
 LOCK_DEBUG_ENABLED(const LOCK *lock)
 {
        return
-               (((LOCK_LOCKMETHOD(*lock) == DEFAULT_LOCKMETHOD && Trace_locks)
-          || (LOCK_LOCKMETHOD(*lock) == USER_LOCKMETHOD && Trace_userlocks))
-                && (lock->tag.relId >= (Oid) Trace_lock_oidmin))
-               || (Trace_lock_table && (lock->tag.relId == Trace_lock_table));
+               (((Trace_locks && LOCK_LOCKMETHOD(*lock) == DEFAULT_LOCKMETHOD)
+                 || (Trace_userlocks && LOCK_LOCKMETHOD(*lock) == USER_LOCKMETHOD))
+                && ((Oid) lock->tag.locktag_field2 >= (Oid) Trace_lock_oidmin))
+               || (Trace_lock_table
+                       && (lock->tag.locktag_field2 == Trace_lock_table));
 }
 
 
@@ -120,12 +121,14 @@ LOCK_PRINT(const char *where, const LOCK *lock, LOCKMODE type)
 {
        if (LOCK_DEBUG_ENABLED(lock))
                elog(LOG,
-                        "%s: lock(%lx) tbl(%d) rel(%u) db(%u) obj(%u) grantMask(%x) "
+                        "%s: lock(%lx) id(%u,%u,%u,%u,%u,%u) grantMask(%x) "
                         "req(%d,%d,%d,%d,%d,%d,%d)=%d "
                         "grant(%d,%d,%d,%d,%d,%d,%d)=%d wait(%d) type(%s)",
                         where, MAKE_OFFSET(lock),
-                        lock->tag.lockmethodid, lock->tag.relId, lock->tag.dbId,
-                        lock->tag.objId.blkno, lock->grantMask,
+                        lock->tag.locktag_field1, lock->tag.locktag_field2,
+                        lock->tag.locktag_field3, lock->tag.locktag_field4,
+                        lock->tag.locktag_type, lock->tag.locktag_lockmethodid,
+                        lock->grantMask,
                         lock->requested[1], lock->requested[2], lock->requested[3],
                         lock->requested[4], lock->requested[5], lock->requested[6],
                         lock->requested[7], lock->nRequested,
@@ -139,14 +142,9 @@ LOCK_PRINT(const char *where, const LOCK *lock, LOCKMODE type)
 inline static void
 PROCLOCK_PRINT(const char *where, const PROCLOCK *proclockP)
 {
-       if (
-               (((PROCLOCK_LOCKMETHOD(*proclockP) == DEFAULT_LOCKMETHOD && Trace_locks)
-                 || (PROCLOCK_LOCKMETHOD(*proclockP) == USER_LOCKMETHOD && Trace_userlocks))
-                && (((LOCK *) MAKE_PTR(proclockP->tag.lock))->tag.relId >= (Oid) Trace_lock_oidmin))
-               || (Trace_lock_table && (((LOCK *) MAKE_PTR(proclockP->tag.lock))->tag.relId == Trace_lock_table))
-               )
+       if (LOCK_DEBUG_ENABLED((LOCK *) MAKE_PTR(proclockP->tag.lock)))
                elog(LOG,
-               "%s: proclock(%lx) lock(%lx) tbl(%d) proc(%lx) xid(%u) hold(%x)",
+               "%s: proclock(%lx) lock(%lx) method(%u) proc(%lx) xid(%u) hold(%x)",
                         where, MAKE_OFFSET(proclockP), proclockP->tag.lock,
                         PROCLOCK_LOCKMETHOD(*(proclockP)),
                         proclockP->tag.proc, proclockP->tag.xid,
@@ -346,13 +344,9 @@ LockMethodTableInit(const char *tabName,
  * LockMethodTableRename -- allocate another lockmethod ID to the same
  *             lock table.
  *
- * NOTES: Both the lock module and the lock chain (lchain.c)
- *             module use table id's to distinguish between different
- *             kinds of locks.  Short term and long term locks look
- *             the same to the lock table, but are handled differently
- *             by the lock chain manager.      This function allows the
- *             client to use different lockmethods when acquiring/releasing
- *             short term and long term locks, yet store them all in one hashtable.
+ * NOTES: This function makes it possible to have different lockmethodids,
+ *             and hence different locking semantics, while still storing all
+ *             the data in one shared-memory hashtable.
  */
 
 LOCKMETHODID
@@ -404,33 +398,16 @@ LockMethodTableRename(LOCKMETHODID lockmethodid)
  *             the lock.  While the lock is active other clients can still
  *             read and write the tuple but they can be aware that it has
  *             been locked at the application level by someone.
- *             User locks use lock tags made of an uint16 and an uint32, for
- *             example 0 and a tuple oid, or any other arbitrary pair of
- *             numbers following a convention established by the application.
- *             In this sense tags don't refer to tuples or database entities.
+ *
  *             User locks and normal locks are completely orthogonal and
- *             they don't interfere with each other, so it is possible
- *             to acquire a normal lock on an user-locked tuple or user-lock
- *             a tuple for which a normal write lock already exists.
+ *             they don't interfere with each other.
+ *
  *             User locks are always non blocking, therefore they are never
  *             acquired if already held by another process.  They must be
  *             released explicitly by the application but they are released
  *             automatically when a backend terminates.
  *             They are indicated by a lockmethod 2 which is an alias for the
- *             normal lock table, and are distinguished from normal locks
- *             by the following differences:
- *
- *                                                                             normal lock             user lock
- *
- *             lockmethodid                                    1                               2
- *             tag.dbId                                                database oid    database oid
- *             tag.relId                                               rel oid or 0    0
- *             tag.objId                                               block id                lock id2
- *                                                                             or xact id
- *             tag.offnum                                              0                               lock id1
- *             proclock.xid                                    xid or 0                0
- *             persistence                                             transaction             user or backend
- *                                                                             or backend
+ *             normal lock table.
  *
  *             The lockmode parameter can have the same values for normal locks
  *             although probably only WRITE_LOCK can have some practical use.
@@ -456,13 +433,14 @@ LockAcquire(LOCKMETHODID lockmethodid, LOCKTAG *locktag,
        int                     i;
 
 #ifdef LOCK_DEBUG
-       if (lockmethodid == USER_LOCKMETHOD && Trace_userlocks)
-               elog(LOG, "LockAcquire: user lock [%u] %s",
-                        locktag->objId.blkno, lock_mode_names[lockmode]);
+       if (Trace_userlocks && lockmethodid == USER_LOCKMETHOD)
+               elog(LOG, "LockAcquire: user lock [%u,%u] %s",
+                        locktag->locktag_field1, locktag->locktag_field2,
+                        lock_mode_names[lockmode]);
 #endif
 
-       /* ???????? This must be changed when short term locks will be used */
-       locktag->lockmethodid = lockmethodid;
+       /* ugly */
+       locktag->locktag_lockmethodid = lockmethodid;
 
        Assert(lockmethodid < NumLockMethods);
        lockMethodTable = LockMethods[lockmethodid];
@@ -993,9 +971,6 @@ UnGrantLock(LOCK *lock, LOCKMODE lockmode,
        }
 
        LOCK_PRINT("UnGrantLock: updated", lock, lockmode);
-       Assert((lock->nRequested >= 0) && (lock->requested[lockmode] >= 0));
-       Assert((lock->nGranted >= 0) && (lock->granted[lockmode] >= 0));
-       Assert(lock->nGranted <= lock->nRequested);
 
        /*
         * We need only run ProcLockWakeup if the released lock conflicts with
@@ -1079,6 +1054,7 @@ WaitOnLock(LOCKMETHODID lockmethodid, LOCALLOCK *locallock,
        LockMethod      lockMethodTable = LockMethods[lockmethodid];
        char       *new_status,
                           *old_status;
+       size_t          len;
 
        Assert(lockmethodid < NumLockMethods);
 
@@ -1086,9 +1062,10 @@ WaitOnLock(LOCKMETHODID lockmethodid, LOCALLOCK *locallock,
                           locallock->lock, locallock->tag.mode);
 
        old_status = pstrdup(get_ps_display());
-       new_status = (char *) palloc(strlen(old_status) + 10);
-       strcpy(new_status, old_status);
-       strcat(new_status, " waiting");
+       len = strlen(old_status);
+       new_status = (char *) palloc(len + 8 + 1);
+       memcpy(new_status, old_status, len);
+       strcpy(new_status + len, " waiting");
        set_ps_display(new_status);
 
        awaitedLock = locallock;
@@ -1115,8 +1092,7 @@ WaitOnLock(LOCKMETHODID lockmethodid, LOCALLOCK *locallock,
        {
                /*
                 * We failed as a result of a deadlock, see CheckDeadLock(). Quit
-                * now.  Removal of the proclock and lock objects, if no longer
-                * needed, will happen in xact cleanup (see above for motivation).
+                * now.
                 */
                awaitedLock = NULL;
                LOCK_PRINT("WaitOnLock: aborting on lock",
@@ -1148,22 +1124,21 @@ WaitOnLock(LOCKMETHODID lockmethodid, LOCALLOCK *locallock,
  *
  * Locktable lock must be held by caller.
  *
- * NB: this does not remove the process' proclock object, nor the lock object,
- * even though their counts might now have gone to zero.  That will happen
- * during a subsequent LockReleaseAll call, which we expect will happen
- * during transaction cleanup. (Removal of a proc from its wait queue by
- * this routine can only happen if we are aborting the transaction.)
+ * NB: this does not clean up any locallock object that may exist for the lock.
  */
 void
 RemoveFromWaitQueue(PGPROC *proc)
 {
        LOCK       *waitLock = proc->waitLock;
+       PROCLOCK   *proclock = proc->waitProcLock;
        LOCKMODE        lockmode = proc->waitLockMode;
+       LOCKMETHODID lockmethodid = LOCK_LOCKMETHOD(*waitLock);
 
        /* Make sure proc is waiting */
        Assert(proc->links.next != INVALID_OFFSET);
        Assert(waitLock);
        Assert(waitLock->waitProcs.size > 0);
+       Assert(0 < lockmethodid && lockmethodid < NumLockMethods);
 
        /* Remove proc from lock's wait queue */
        SHMQueueDelete(&(proc->links));
@@ -1183,8 +1158,32 @@ RemoveFromWaitQueue(PGPROC *proc)
        proc->waitLock = NULL;
        proc->waitProcLock = NULL;
 
+       /*
+        * Delete the proclock immediately if it represents no already-held locks.
+        * This must happen now because if the owner of the lock decides to release
+        * it, and the requested/granted counts then go to zero, LockRelease
+        * expects there to be no remaining proclocks.
+        */
+       if (proclock->holdMask == 0)
+       {
+               PROCLOCK_PRINT("RemoveFromWaitQueue: deleting proclock", proclock);
+               SHMQueueDelete(&proclock->lockLink);
+               SHMQueueDelete(&proclock->procLink);
+               proclock = (PROCLOCK *) hash_search(LockMethodProcLockHash[lockmethodid],
+                                                                                       (void *) &(proclock->tag),
+                                                                                       HASH_REMOVE, NULL);
+               if (!proclock)
+                       elog(WARNING, "proclock table corrupted");
+       }
+
+       /*
+        * There should still be some requests for the lock ... else what were
+        * we waiting for?  Therefore no need to delete the lock object.
+        */
+       Assert(waitLock->nRequested > 0);
+
        /* See if any other waiters for the lock can be woken up now */
-       ProcLockWakeup(GetLocksMethodTable(waitLock), waitLock);
+       ProcLockWakeup(LockMethods[lockmethodid], waitLock);
 }
 
 /*
@@ -1207,15 +1206,17 @@ LockRelease(LOCKMETHODID lockmethodid, LOCKTAG *locktag,
        PROCLOCK   *proclock;
        LWLockId        masterLock;
        LockMethod      lockMethodTable;
-       bool            wakeupNeeded = false;
+       bool            wakeupNeeded;
 
 #ifdef LOCK_DEBUG
-       if (lockmethodid == USER_LOCKMETHOD && Trace_userlocks)
-               elog(LOG, "LockRelease: user lock tag [%u] %d", locktag->objId.blkno, lockmode);
+       if (Trace_userlocks && lockmethodid == USER_LOCKMETHOD)
+               elog(LOG, "LockRelease: user lock [%u,%u] %s",
+                        locktag->locktag_field1, locktag->locktag_field2,
+                        lock_mode_names[lockmode]);
 #endif
 
-       /* ???????? This must be changed when short term locks will be used */
-       locktag->lockmethodid = lockmethodid;
+       /* ugly */
+       locktag->locktag_lockmethodid = lockmethodid;
 
        Assert(lockmethodid < NumLockMethods);
        lockMethodTable = LockMethods[lockmethodid];
@@ -1335,7 +1336,7 @@ LockRelease(LOCKMETHODID lockmethodid, LOCKTAG *locktag,
         */
        if (proclock->holdMask == 0)
        {
-               PROCLOCK_PRINT("LockRelease: deleting", proclock);
+               PROCLOCK_PRINT("LockRelease: deleting proclock", proclock);
                SHMQueueDelete(&proclock->lockLink);
                SHMQueueDelete(&proclock->procLink);
                proclock = (PROCLOCK *) hash_search(LockMethodProcLockHash[lockmethodid],
@@ -1356,6 +1357,7 @@ LockRelease(LOCKMETHODID lockmethodid, LOCKTAG *locktag,
                 * We've just released the last lock, so garbage-collect the lock
                 * object.
                 */
+               LOCK_PRINT("LockRelease: deleting lock", lock, lockmode);
                Assert(SHMQueueEmpty(&(lock->procLocks)));
                lock = (LOCK *) hash_search(LockMethodLockHash[lockmethodid],
                                                                        (void *) &(lock->tag),