diff -durpN postgresql/src/backend/port/ipc_test.c postgresql.1/src/backend/port/ipc_test.c --- postgresql/src/backend/port/ipc_test.c 2012-04-16 19:57:22.437915477 +0200 +++ postgresql.1/src/backend/port/ipc_test.c 2012-08-21 15:53:50.059329927 +0200 @@ -240,7 +240,7 @@ main(int argc, char **argv) printf("Testing Lock ... "); fflush(stdout); - PGSemaphoreLock(&storage->sem, false); + PGSemaphoreLock(&storage->sem, false, NULL); printf("OK\n"); @@ -262,8 +262,8 @@ main(int argc, char **argv) PGSemaphoreUnlock(&storage->sem); PGSemaphoreUnlock(&storage->sem); - PGSemaphoreLock(&storage->sem, false); - PGSemaphoreLock(&storage->sem, false); + PGSemaphoreLock(&storage->sem, false, NULL); + PGSemaphoreLock(&storage->sem, false, NULL); printf("OK\n"); @@ -311,7 +311,7 @@ main(int argc, char **argv) printf("Waiting for child (should wait 3 sec here) ... "); fflush(stdout); - PGSemaphoreLock(&storage->sem, false); + PGSemaphoreLock(&storage->sem, false, NULL); printf("OK\n"); diff -durpN postgresql/src/backend/port/posix_sema.c postgresql.1/src/backend/port/posix_sema.c --- postgresql/src/backend/port/posix_sema.c 2012-04-16 19:57:22.438915489 +0200 +++ postgresql.1/src/backend/port/posix_sema.c 2012-08-21 15:49:26.215579665 +0200 @@ -236,9 +236,11 @@ PGSemaphoreReset(PGSemaphore sema) * Lock a semaphore (decrement count), blocking if count would be < 0 */ void -PGSemaphoreLock(PGSemaphore sema, bool interruptOK) +PGSemaphoreLock(PGSemaphore sema, bool interruptOK, + PGSemaphoreCondition condition_checker) { int errStatus; + bool condition = false; /* * See notes in sysv_sema.c's implementation of PGSemaphoreLock. Just as @@ -252,8 +254,12 @@ PGSemaphoreLock(PGSemaphore sema, bool i CHECK_FOR_INTERRUPTS(); errStatus = sem_wait(PG_SEM_REF(sema)); ImmediateInterruptOK = false; - } while (errStatus < 0 && errno == EINTR); + if (condition_checker) + condition = condition_checker(); + } while (errStatus < 0 && errno == EINTR && !condition); + if (condition) + return; if (errStatus < 0) elog(FATAL, "sem_wait failed: %m"); } diff -durpN postgresql/src/backend/port/sysv_sema.c postgresql.1/src/backend/port/sysv_sema.c --- postgresql/src/backend/port/sysv_sema.c 2012-05-14 08:20:56.284830580 +0200 +++ postgresql.1/src/backend/port/sysv_sema.c 2012-08-21 15:49:26.991584804 +0200 @@ -358,9 +358,11 @@ PGSemaphoreReset(PGSemaphore sema) * Lock a semaphore (decrement count), blocking if count would be < 0 */ void -PGSemaphoreLock(PGSemaphore sema, bool interruptOK) +PGSemaphoreLock(PGSemaphore sema, bool interruptOK, + PGSemaphoreCondition condition_checker) { int errStatus; + bool condition = false; struct sembuf sops; sops.sem_op = -1; /* decrement */ @@ -414,8 +416,12 @@ PGSemaphoreLock(PGSemaphore sema, bool i CHECK_FOR_INTERRUPTS(); errStatus = semop(sema->semId, &sops, 1); ImmediateInterruptOK = false; - } while (errStatus < 0 && errno == EINTR); + if (condition_checker) + condition = condition_checker(); + } while (errStatus < 0 && errno == EINTR && !condition); + if (condition) + return; if (errStatus < 0) elog(FATAL, "semop(id=%d) failed: %m", sema->semId); } diff -durpN postgresql/src/backend/port/win32_sema.c postgresql.1/src/backend/port/win32_sema.c --- postgresql/src/backend/port/win32_sema.c 2012-06-11 06:22:48.137921483 +0200 +++ postgresql.1/src/backend/port/win32_sema.c 2012-08-21 15:49:24.921571070 +0200 @@ -116,10 +116,12 @@ PGSemaphoreReset(PGSemaphore sema) * Serve the interrupt if interruptOK is true. */ void -PGSemaphoreLock(PGSemaphore sema, bool interruptOK) +PGSemaphoreLock(PGSemaphore sema, bool interruptOK, + PGSemaphoreCondition condition_checker) { DWORD ret; HANDLE wh[2]; + bool condition = false; /* * Note: pgwin32_signal_event should be first to ensure that it will be @@ -158,8 +160,12 @@ PGSemaphoreLock(PGSemaphore sema, bool i errno = EIDRM; ImmediateInterruptOK = false; - } while (errno == EINTR); + if (condition_checker) + condition = condition_checker(); + } while (errno == EINTR && !condition); + if (condition) + return; if (errno != 0) ereport(FATAL, (errmsg("could not lock semaphore: error code %lu", GetLastError()))); diff -durpN postgresql/src/backend/storage/lmgr/deadlock.c postgresql.1/src/backend/storage/lmgr/deadlock.c --- postgresql/src/backend/storage/lmgr/deadlock.c 2012-07-27 10:19:36.932331508 +0200 +++ postgresql.1/src/backend/storage/lmgr/deadlock.c 2012-08-21 15:08:16.728428766 +0200 @@ -31,6 +31,7 @@ #include "storage/lmgr.h" #include "storage/proc.h" #include "utils/memutils.h" +#include "utils/timeout.h" /* One edge in the waits-for graph */ @@ -897,6 +898,9 @@ DeadLockReport(void) StringInfoData locktagbuf; int i; + if (!DeadLockTimeoutCondition()) + return; + initStringInfo(&clientbuf); initStringInfo(&logbuf); initStringInfo(&locktagbuf); diff -durpN postgresql/src/backend/storage/lmgr/lmgrtimeout.c postgresql.1/src/backend/storage/lmgr/lmgrtimeout.c --- postgresql/src/backend/storage/lmgr/lmgrtimeout.c 1970-01-01 01:00:00.000000000 +0100 +++ postgresql.1/src/backend/storage/lmgr/lmgrtimeout.c 2012-08-21 13:05:19.214992078 +0200 @@ -0,0 +1,121 @@ +/*------------------------------------------------------------------------- + * + * lmgrtimeout.c + * routines to manage lock manager timeout sources + * + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/storage/lmgr/lmgrtimeout.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "storage/lmgrtimeout.h" + +/* + * Linked set of lock manager timeout functions + */ +static LmgrTimeoutFunctions *CurrentLmgrTimeoutFunctions = NULL; + +/* + * Register a set of functions for an lock manager timeout + */ +void +RegisterLmgrTimeout(LmgrTimeoutFunctions *functions) +{ + Assert(functions != NULL); + Assert(functions->enable != NULL); + Assert(functions->disable != NULL); + Assert(functions->condition != NULL); + Assert(functions->reporter != NULL); + + functions->next = CurrentLmgrTimeoutFunctions; + CurrentLmgrTimeoutFunctions = functions; +} + +/* + * Enable registered lock manager timeouts + */ +void +EnableLmgrTimeouts(void) +{ + LmgrTimeoutFunctions *ptr = CurrentLmgrTimeoutFunctions; + + while (ptr) + { + ptr->enable(); + ptr = ptr->next; + } +} + +/* + * Disable registered lock manager timeouts + */ +void +DisableLmgrTimeouts(void) +{ + LmgrTimeoutFunctions *ptr = CurrentLmgrTimeoutFunctions; + + while (ptr) + { + ptr->disable(); + ptr = ptr->next; + } +} + +/* + * Call the timeout indicator functions in the order of registration + */ +static bool +DoLmgrTimeoutCondition(LmgrTimeoutFunctions *ptr) +{ + if (ptr) + { + if (ptr->next) + if (DoLmgrTimeoutCondition(ptr->next)) + return true; + if (ptr->condition()) + return true; + } + + return false; +} + +/* + * Call the timeout indicator and return if any one of them is set + */ +bool +LmgrTimeoutCondition(void) +{ + return DoLmgrTimeoutCondition(CurrentLmgrTimeoutFunctions); +} + +/* + * Call the timeout error reporter functions in the order of registration + */ +static void +DoReportLmgrTimeoutError(LmgrTimeoutFunctions *ptr) +{ + if (ptr) + { + if (ptr->next) + DoReportLmgrTimeoutError(ptr->next); + + /* This may not return because of calling ereport() */ + ptr->reporter(); + } +} + +/* + * Call the timeout error reporter if set + */ +void +ReportLmgrTimeoutError(void) +{ + DoReportLmgrTimeoutError(CurrentLmgrTimeoutFunctions); +} diff -durpN postgresql/src/backend/storage/lmgr/lock.c postgresql.1/src/backend/storage/lmgr/lock.c --- postgresql/src/backend/storage/lmgr/lock.c 2012-06-26 09:10:21.280759421 +0200 +++ postgresql.1/src/backend/storage/lmgr/lock.c 2012-08-21 16:01:33.095488150 +0200 @@ -38,6 +38,7 @@ #include "miscadmin.h" #include "pg_trace.h" #include "pgstat.h" +#include "storage/lmgrtimeout.h" #include "storage/proc.h" #include "storage/sinvaladt.h" #include "storage/spin.h" @@ -1512,9 +1513,9 @@ WaitOnLock(LOCALLOCK *locallock, Resourc /* * Now that we aren't holding the partition lock, we can give an - * error report including details about the detected deadlock. + * error report on the timeout condition that triggered. */ - DeadLockReport(); + ReportLmgrTimeoutError(); /* not reached */ } } diff -durpN postgresql/src/backend/storage/lmgr/lwlock.c postgresql.1/src/backend/storage/lmgr/lwlock.c --- postgresql/src/backend/storage/lmgr/lwlock.c 2012-06-27 07:41:33.603316276 +0200 +++ postgresql.1/src/backend/storage/lmgr/lwlock.c 2012-08-21 15:54:39.410667067 +0200 @@ -477,7 +477,7 @@ LWLockAcquire(LWLockId lockid, LWLockMod for (;;) { /* "false" means cannot accept cancel/die interrupt here. */ - PGSemaphoreLock(&proc->sem, false); + PGSemaphoreLock(&proc->sem, false, NULL); if (!proc->lwWaiting) break; extraWaits++; @@ -682,7 +682,7 @@ LWLockAcquireOrWait(LWLockId lockid, LWL for (;;) { /* "false" means cannot accept cancel/die interrupt here. */ - PGSemaphoreLock(&proc->sem, false); + PGSemaphoreLock(&proc->sem, false, NULL); if (!proc->lwWaiting) break; extraWaits++; diff -durpN postgresql/src/backend/storage/lmgr/Makefile postgresql.1/src/backend/storage/lmgr/Makefile --- postgresql/src/backend/storage/lmgr/Makefile 2012-04-16 19:57:22.458915722 +0200 +++ postgresql.1/src/backend/storage/lmgr/Makefile 2012-08-21 13:05:00.254861812 +0200 @@ -12,7 +12,7 @@ subdir = src/backend/storage/lmgr top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global -OBJS = lmgr.o lock.o proc.o deadlock.o lwlock.o spin.o s_lock.o predicate.o +OBJS = lmgr.o lmgrtimeout.o lock.o proc.o deadlock.o lwlock.o spin.o s_lock.o predicate.o include $(top_srcdir)/src/backend/common.mk diff -durpN postgresql/src/backend/storage/lmgr/proc.c postgresql.1/src/backend/storage/lmgr/proc.c --- postgresql/src/backend/storage/lmgr/proc.c 2012-07-27 10:19:36.933331515 +0200 +++ postgresql.1/src/backend/storage/lmgr/proc.c 2012-08-21 15:56:36.948469452 +0200 @@ -43,6 +43,7 @@ #include "replication/syncrep.h" #include "storage/ipc.h" #include "storage/lmgr.h" +#include "storage/lmgrtimeout.h" #include "storage/pmsignal.h" #include "storage/proc.h" #include "storage/procarray.h" @@ -640,8 +641,11 @@ LockErrorCleanup(void) if (lockAwaited == NULL) return; - /* Turn off the deadlock timer, if it's still running (see ProcSleep) */ - disable_timeout(DEADLOCK_TIMEOUT, false); + /* + * Turn off the lock manager timers if they are still running + * (see ProcSleep) + */ + DisableLmgrTimeouts(); /* Unlink myself from the wait queue, if on it (might not be anymore!) */ partitionLock = LockHashPartitionLock(lockAwaited->hashcode); @@ -1028,15 +1032,9 @@ ProcSleep(LOCALLOCK *locallock, LockMeth deadlock_state = DS_NOT_YET_CHECKED; /* - * Set timer so we can wake up after awhile and check for a deadlock. If a - * deadlock is detected, the handler releases the process's semaphore and - * sets MyProc->waitStatus = STATUS_ERROR, allowing us to know that we - * must report failure rather than success. - * - * By delaying the check until we've waited for a bit, we can avoid - * running the rather expensive deadlock-check code in most cases. + * Set the timer for registered lock manager timeouts */ - enable_timeout_after(DEADLOCK_TIMEOUT, DeadlockTimeout); + EnableLmgrTimeouts(); /* * If someone wakes us between LWLockRelease and PGSemaphoreLock, @@ -1057,7 +1055,7 @@ ProcSleep(LOCALLOCK *locallock, LockMeth */ do { - PGSemaphoreLock(&MyProc->sem, true); + PGSemaphoreLock(&MyProc->sem, true, LmgrTimeoutCondition); /* * waitStatus could change from STATUS_WAITING to something else @@ -1203,9 +1201,9 @@ ProcSleep(LOCALLOCK *locallock, LockMeth } while (myWaitStatus == STATUS_WAITING); /* - * Disable the timer, if it's still running + * Disable the timers, if they are still running */ - disable_timeout(DEADLOCK_TIMEOUT, false); + DisableLmgrTimeouts(); /* * Re-acquire the lock table's partition lock. We have to do this to hold @@ -1451,6 +1449,19 @@ check_done: /* + * DeadLockTimeoutCondition + * Indicate whether deadlock was detected + */ +bool +DeadLockTimeoutCondition(void) +{ + if (deadlock_state == DS_NOT_YET_CHECKED) + return false; + return get_timeout_indicator(DEADLOCK_TIMEOUT) && (deadlock_state != DS_NO_DEADLOCK); +} + + +/* * ProcWaitForSignal - wait for a signal from another backend. * * This can share the semaphore normally used for waiting for locks, @@ -1464,7 +1475,7 @@ check_done: void ProcWaitForSignal(void) { - PGSemaphoreLock(&MyProc->sem, true); + PGSemaphoreLock(&MyProc->sem, true, NULL); } /* diff -durpN postgresql/src/backend/utils/init/postinit.c postgresql.1/src/backend/utils/init/postinit.c --- postgresql/src/backend/utils/init/postinit.c 2012-07-22 16:48:48.525857751 +0200 +++ postgresql.1/src/backend/utils/init/postinit.c 2012-08-21 16:14:31.026731931 +0200 @@ -41,6 +41,7 @@ #include "storage/fd.h" #include "storage/ipc.h" #include "storage/lmgr.h" +#include "storage/lmgrtimeout.h" #include "storage/procarray.h" #include "storage/procsignal.h" #include "storage/proc.h" @@ -65,11 +66,17 @@ static void PerformAuthentication(Port * static void CheckMyDatabase(const char *name, bool am_superuser); static void InitCommunication(void); static void ShutdownPostgres(int code, Datum arg); +static void EnableDeadLockTimeout(void); +static void DisableDeadLockTimeout(void); static void StatementTimeoutHandler(void); static bool ThereIsAtLeastOneRole(void); static void process_startup_options(Port *port, bool am_superuser); static void process_settings(Oid databaseid, Oid roleid); +static LmgrTimeoutFunctions DeadLockTimeoutFunctions = { + EnableDeadLockTimeout, DisableDeadLockTimeout, + DeadLockTimeoutCondition, DeadLockReport +}; /*** InitPostgres support ***/ @@ -501,6 +508,7 @@ InitPostgres(const char *in_dbname, Oid if (!bootstrap) { RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLock); + RegisterLmgrTimeout(&DeadLockTimeoutFunctions); RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler); } @@ -982,6 +990,26 @@ ShutdownPostgres(int code, Datum arg) LockReleaseAll(USER_LOCKMETHOD, true); } +static void +EnableDeadLockTimeout(void) +{ + /* + * Set timer so we can wake up after awhile and check for a deadlock. If a + * deadlock is detected, the handler releases the process's semaphore and + * sets MyProc->waitStatus = STATUS_ERROR, allowing us to know that we + * must report failure rather than success. + * + * By delaying the check until we've waited for a bit, we can avoid + * running the rather expensive deadlock-check code in most cases. + */ + enable_timeout_after(DEADLOCK_TIMEOUT, DeadlockTimeout); +} + +static void +DisableDeadLockTimeout(void) +{ + disable_timeout(DEADLOCK_TIMEOUT, true); +} /* * STATEMENT_TIMEOUT handler: trigger a query-cancel interrupt. diff -durpN postgresql/src/include/storage/lmgrtimeout.h postgresql.1/src/include/storage/lmgrtimeout.h --- postgresql/src/include/storage/lmgrtimeout.h 1970-01-01 01:00:00.000000000 +0100 +++ postgresql.1/src/include/storage/lmgrtimeout.h 2012-08-21 13:30:43.050291053 +0200 @@ -0,0 +1,36 @@ +/*------------------------------------------------------------------------- + * + * lmgrtimeout.h + * POSTGRES lock manager timeout definitions. + * + * + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/storage/lmgrtimeout.h + * + *------------------------------------------------------------------------- + */ +#ifndef LMGRTIMEOUT_H +#define LMGRTIMEOUT_H + +/* callback function signatures for lock manager timeouts */ +typedef void (*LmgrTimeoutCallback)(void); +typedef bool (*LmgrTimeoutConditionCallback)(void); + +typedef struct LmgrTimeoutFunctions { + LmgrTimeoutCallback enable; + LmgrTimeoutCallback disable; + LmgrTimeoutConditionCallback condition; + LmgrTimeoutCallback reporter; + struct LmgrTimeoutFunctions *next; +} LmgrTimeoutFunctions; + +/* register and handle lock manager timeouts */ +extern void RegisterLmgrTimeout(LmgrTimeoutFunctions *functions); +extern void EnableLmgrTimeouts(void); +extern void DisableLmgrTimeouts(void); +extern bool LmgrTimeoutCondition(void); +extern void ReportLmgrTimeoutError(void); + +#endif /* LMGRTIMEOUT_H */ diff -durpN postgresql/src/include/storage/pg_sema.h postgresql.1/src/include/storage/pg_sema.h --- postgresql/src/include/storage/pg_sema.h 2012-04-16 19:57:22.672918205 +0200 +++ postgresql.1/src/include/storage/pg_sema.h 2012-08-21 15:44:06.655459333 +0200 @@ -62,6 +62,8 @@ typedef HANDLE PGSemaphoreData; typedef PGSemaphoreData *PGSemaphore; +typedef bool (*PGSemaphoreCondition)(void); + /* Module initialization (called during postmaster start or shmem reinit) */ extern void PGReserveSemaphores(int maxSemas, int port); @@ -72,7 +74,8 @@ extern void PGSemaphoreCreate(PGSemaphor extern void PGSemaphoreReset(PGSemaphore sema); /* Lock a semaphore (decrement count), blocking if count would be < 0 */ -extern void PGSemaphoreLock(PGSemaphore sema, bool interruptOK); +extern void PGSemaphoreLock(PGSemaphore sema, bool interruptOK, + PGSemaphoreCondition condition); /* Unlock a semaphore (increment count) */ extern void PGSemaphoreUnlock(PGSemaphore sema); diff -durpN postgresql/src/include/storage/proc.h postgresql.1/src/include/storage/proc.h --- postgresql/src/include/storage/proc.h 2012-07-22 16:48:48.548857900 +0200 +++ postgresql.1/src/include/storage/proc.h 2012-08-21 15:10:38.641347523 +0200 @@ -244,6 +244,7 @@ extern int ProcSleep(LOCALLOCK *localloc extern PGPROC *ProcWakeup(PGPROC *proc, int waitStatus); extern void ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock); extern void CheckDeadLock(void); +extern bool DeadLockTimeoutCondition(void); extern bool IsWaitingForLock(void); extern void LockErrorCleanup(void);