From 91a3cb7d027e59af7f314c76fcafff8a3534f3cc Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 5 Nov 2005 03:05:05 +0000 Subject: [PATCH] Repair an error introduced by log_line_prefix patch: it is not acceptable to assume that the string pointer passed to set_ps_display is good forever. There's no need to anyway since ps_status.c itself saves the string, and we already had an API (get_ps_display) to return it. I believe this explains Jim Nasby's report of intermittent crashes in elog.c when %i format code is in use in log_line_prefix. While at it, repair a previously unnoticed problem: on some platforms such as Darwin, the string returned by get_ps_display was blank-padded to the maximum length, meaning that lock.c's attempt to append " waiting" to it never worked. --- src/backend/postmaster/postmaster.c | 1 - src/backend/storage/lmgr/lock.c | 176 ++++++++++++++++------------ src/backend/utils/error/elog.c | 14 ++- src/backend/utils/misc/ps_status.c | 24 +++- src/include/libpq/libpq-be.h | 1 - src/include/utils/ps_status.h | 2 +- 6 files changed, 133 insertions(+), 85 deletions(-) diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 0360cc468e..5cfacd173d 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -2638,7 +2638,6 @@ BackendRun(Port *port) /* set these to empty in case they are needed before we set them up */ port->remote_host = ""; port->remote_port = ""; - port->commandTag = ""; /* * Initialize libpq and enable reporting of ereport errors to the diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c index 87aa730f39..37d8312131 100644 --- a/src/backend/storage/lmgr/lock.c +++ b/src/backend/storage/lmgr/lock.c @@ -166,6 +166,8 @@ static int WaitOnLock(LOCKMETHODID lockmethodid, LOCALLOCK *locallock, ResourceOwner owner); static void LockCountMyLocks(SHMEM_OFFSET lockOffset, PGPROC *proc, int *myHolding); +static bool UnGrantLock(LOCK *lock, LOCKMODE lockmode, + PROCLOCK *proclock, LockMethod lockMethodTable); /* @@ -957,6 +959,62 @@ GrantLock(LOCK *lock, PROCLOCK *proclock, LOCKMODE lockmode) Assert(lock->nGranted <= lock->nRequested); } +/* + * UnGrantLock -- opposite of GrantLock. + * + * Updates the lock and proclock data structures to show that the lock + * is no longer held nor requested by the current holder. + * + * Returns true if there were any waiters waiting on the lock that + * should now be woken up with ProcLockWakeup. + */ +static bool +UnGrantLock(LOCK *lock, LOCKMODE lockmode, + PROCLOCK *proclock, LockMethod lockMethodTable) +{ + bool wakeupNeeded = false; + + Assert((lock->nRequested > 0) && (lock->requested[lockmode] > 0)); + Assert((lock->nGranted > 0) && (lock->granted[lockmode] > 0)); + Assert(lock->nGranted <= lock->nRequested); + + /* + * fix the general lock stats + */ + lock->nRequested--; + lock->requested[lockmode]--; + lock->nGranted--; + lock->granted[lockmode]--; + + if (lock->granted[lockmode] == 0) + { + /* change the conflict mask. No more of this lock type. */ + lock->grantMask &= LOCKBIT_OFF(lockmode); + } + + LOCK_PRINT("UnGrantLock: updated", lock, lockmode); + + /* + * We need only run ProcLockWakeup if the released lock conflicts with + * at least one of the lock types requested by waiter(s). Otherwise + * whatever conflict made them wait must still exist. NOTE: before + * MVCC, we could skip wakeup if lock->granted[lockmode] was still + * positive. But that's not true anymore, because the remaining + * granted locks might belong to some waiter, who could now be + * awakened because he doesn't conflict with his own locks. + */ + if (lockMethodTable->conflictTab[lockmode] & lock->waitMask) + wakeupNeeded = true; + + /* + * Now fix the per-proclock state. + */ + proclock->holdMask &= LOCKBIT_OFF(lockmode); + PROCLOCK_PRINT("UnGrantLock: updated", proclock); + + return wakeupNeeded; +} + /* * GrantLockLocal -- update the locallock data structures to show * the lock request has been granted. @@ -1016,19 +1074,21 @@ WaitOnLock(LOCKMETHODID lockmethodid, LOCALLOCK *locallock, ResourceOwner owner) { LockMethod lockMethodTable = LockMethods[lockmethodid]; - char *new_status, - *old_status; + const char *old_status; + char *new_status; + int len; Assert(lockmethodid < NumLockMethods); LOCK_PRINT("WaitOnLock: sleeping on lock", 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"); + old_status = get_ps_display(&len); + new_status = (char *) palloc(len + 8 + 1); + memcpy(new_status, old_status, len); + strcpy(new_status + len, " waiting"); set_ps_display(new_status); + new_status[len] = '\0'; /* truncate off " waiting" */ awaitedLock = locallock; awaitedOwner = owner; @@ -1054,8 +1114,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", @@ -1072,8 +1131,7 @@ WaitOnLock(LOCKMETHODID lockmethodid, LOCALLOCK *locallock, awaitedLock = NULL; - set_ps_display(old_status); - pfree(old_status); + set_ps_display(new_status); pfree(new_status); LOCK_PRINT("WaitOnLock: wakeup on lock", @@ -1087,22 +1145,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)); @@ -1122,8 +1179,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); } /* @@ -1146,7 +1227,7 @@ 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) @@ -1265,46 +1346,8 @@ LockRelease(LOCKMETHODID lockmethodid, LOCKTAG *locktag, RemoveLocalLock(locallock); return FALSE; } - Assert((lock->nRequested > 0) && (lock->requested[lockmode] > 0)); - Assert((lock->nGranted > 0) && (lock->granted[lockmode] > 0)); - Assert(lock->nGranted <= lock->nRequested); - /* - * fix the general lock stats - */ - lock->nRequested--; - lock->requested[lockmode]--; - lock->nGranted--; - lock->granted[lockmode]--; - - if (lock->granted[lockmode] == 0) - { - /* change the conflict mask. No more of this lock type. */ - lock->grantMask &= LOCKBIT_OFF(lockmode); - } - - LOCK_PRINT("LockRelease: 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 - * at least one of the lock types requested by waiter(s). Otherwise - * whatever conflict made them wait must still exist. NOTE: before - * MVCC, we could skip wakeup if lock->granted[lockmode] was still - * positive. But that's not true anymore, because the remaining - * granted locks might belong to some waiter, who could now be - * awakened because he doesn't conflict with his own locks. - */ - if (lockMethodTable->conflictTab[lockmode] & lock->waitMask) - wakeupNeeded = true; - - /* - * Now fix the per-proclock state. - */ - proclock->holdMask &= LOCKBIT_OFF(lockmode); - PROCLOCK_PRINT("LockRelease: updated", proclock); + wakeupNeeded = UnGrantLock(lock, lockmode, proclock, lockMethodTable); /* * If this was my last hold on this lock, delete my entry in the @@ -1312,7 +1355,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], @@ -1333,6 +1376,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), @@ -1483,22 +1527,8 @@ LockReleaseAll(LOCKMETHODID lockmethodid, bool allxids) for (i = 1; i <= numLockModes; i++) { if (proclock->holdMask & LOCKBIT_ON(i)) - { - lock->requested[i]--; - lock->granted[i]--; - Assert(lock->requested[i] >= 0 && lock->granted[i] >= 0); - if (lock->granted[i] == 0) - lock->grantMask &= LOCKBIT_OFF(i); - lock->nRequested--; - lock->nGranted--; - - /* - * Read comments in LockRelease - */ - if (!wakeupNeeded && - lockMethodTable->conflictTab[i] & lock->waitMask) - wakeupNeeded = true; - } + wakeupNeeded |= UnGrantLock(lock, i, proclock, + lockMethodTable); } } Assert((lock->nRequested >= 0) && (lock->nGranted >= 0)); diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index 730bfc35dd..a3c7fa75ba 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -67,6 +67,7 @@ #include "tcop/tcopprot.h" #include "utils/memutils.h" #include "utils/guc.h" +#include "utils/ps_status.h" /* Global variables */ @@ -1461,13 +1462,20 @@ log_line_prefix(StringInfo buf) break; case 'i': if (MyProcPort) - appendStringInfo(buf, "%s", MyProcPort->commandTag); + { + const char *psdisp; + int displen; + + psdisp = get_ps_display(&displen); + appendStringInfo(buf, "%.*s", displen, psdisp); + } break; case 'r': - if (MyProcPort) + if (MyProcPort && MyProcPort->remote_host) { appendStringInfo(buf, "%s", MyProcPort->remote_host); - if (strlen(MyProcPort->remote_port) > 0) + if (MyProcPort->remote_port && + MyProcPort->remote_port[0] != '\0') appendStringInfo(buf, "(%s)", MyProcPort->remote_port); } diff --git a/src/backend/utils/misc/ps_status.c b/src/backend/utils/misc/ps_status.c index 50fe78cc81..e493ddb33e 100644 --- a/src/backend/utils/misc/ps_status.c +++ b/src/backend/utils/misc/ps_status.c @@ -308,10 +308,6 @@ init_ps_display(const char *username, const char *dbname, void set_ps_display(const char *activity) { - /* save tag for possible use by elog.c */ - if (MyProcPort) - MyProcPort->commandTag = activity; - #ifndef PS_USE_NONE /* no ps display for stand-alone backend */ if (!IsUnderPostmaster) @@ -367,15 +363,31 @@ set_ps_display(const char *activity) /* * Returns what's currently in the ps display, in case someone needs - * it. Note that only the activity part is returned. + * it. Note that only the activity part is returned. On some platforms + * the string will not be null-terminated, so return the effective + * length into *displen. */ const char * -get_ps_display(void) +get_ps_display(int *displen) { #ifdef PS_USE_CLOBBER_ARGV + size_t offset; + /* If ps_buffer is a pointer, it might still be null */ if (!ps_buffer) + { + *displen = 0; return ""; + } + + /* Remove any trailing spaces to offset the effect of PS_PADDING */ + offset = ps_buffer_size; + while (offset > ps_buffer_fixed_size && ps_buffer[offset-1] == PS_PADDING) + offset--; + + *displen = offset - ps_buffer_fixed_size; +#else + *displen = strlen(ps_buffer + ps_buffer_fixed_size); #endif return ps_buffer + ps_buffer_fixed_size; diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index 9772f2baf0..578b4a28e3 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -79,7 +79,6 @@ typedef struct Port * database_name and other members of this struct, we may as well keep * it here. */ - const char *commandTag; /* current command tag */ struct timeval session_start; /* for session duration logging */ /* diff --git a/src/include/utils/ps_status.h b/src/include/utils/ps_status.h index 122cd94915..20eb6446be 100644 --- a/src/include/utils/ps_status.h +++ b/src/include/utils/ps_status.h @@ -19,6 +19,6 @@ extern void init_ps_display(const char *username, const char *dbname, extern void set_ps_display(const char *activity); -extern const char *get_ps_display(void); +extern const char *get_ps_display(int *displen); #endif /* PS_STATUS_H */ -- 2.39.5