diff -dcrpN postgresql.orig/doc/src/sgml/config.sgml postgresql/doc/src/sgml/config.sgml *** postgresql.orig/doc/src/sgml/config.sgml 2012-03-28 10:54:25.177368339 +0200 --- postgresql/doc/src/sgml/config.sgml 2012-04-04 10:36:01.982604984 +0200 *************** COPY postgres_log FROM '/full/path/to/lo *** 4944,4950 **** milliseconds, starting from the time the command arrives at the server from the client. If log_min_error_statement is set to ERROR or lower, the statement that timed out will also be ! logged. A value of zero (the default) turns this off. --- 4944,4953 ---- milliseconds, starting from the time the command arrives at the server from the client. If log_min_error_statement is set to ERROR or lower, the statement that timed out will also be ! logged. The timeout may happen any time, i.e. while waiting for locks ! on database objects or in case of a large result set, during data ! retrieval from the server after all locks were successfully acquired. ! A value of zero (the default) turns this off. *************** COPY postgres_log FROM '/full/path/to/lo *** 4955,4960 **** --- 4958,4991 ---- + + lock_timeout (integer) + + lock_timeout configuration parameter + + + + Abort any statement that tries to acquire a heavy-weight lock on rows, + pages, tables, indices or other objects and the lock has to wait more + than the specified number of milliseconds, starting from the time the + command arrives at the server from the client. If the statement involves + more than one such lock, the timeout applies to every one of them. + This makes the statement possibly wait for up to N * lock_timeout + time in the worst case where N is the number of locks attempted to acquire. + As opposed to statement_timeout, this timeout (and the error) + may only occurs while waiting for locks. If log_min_error_statement + is set to ERROR or lower, the statement that timed out will + also be logged. A value of zero (the default) turns off the limitation. + + + + Setting lock_timeout in + postgresql.conf is not recommended because it + affects all sessions. + + + + vacuum_freeze_table_age (integer) diff -dcrpN postgresql.orig/doc/src/sgml/ref/lock.sgml postgresql/doc/src/sgml/ref/lock.sgml *** postgresql.orig/doc/src/sgml/ref/lock.sgml 2011-08-07 11:29:16.004256905 +0200 --- postgresql/doc/src/sgml/ref/lock.sgml 2012-04-03 15:54:26.543224868 +0200 *************** LOCK [ TABLE ] [ ONLY ] NOWAIT is specified, LOCK TABLE does not wait to acquire the desired lock: if it cannot be acquired immediately, the command is aborted and an ! error is emitted. Once obtained, the lock is held for the ! remainder of the current transaction. (There is no UNLOCK TABLE command; locks are always released at transaction end.) --- 39,49 ---- NOWAIT is specified, LOCK TABLE does not wait to acquire the desired lock: if it cannot be acquired immediately, the command is aborted and an ! error is emitted. If lock_timeout is set to a value ! higher than 0, and the lock cannot be acquired under the specified ! timeout value in milliseconds, the command is aborted and an error ! is emitted. Once obtained, the lock is held for the remainder of ! the current transaction. (There is no UNLOCK TABLE command; locks are always released at transaction end.) diff -dcrpN postgresql.orig/doc/src/sgml/ref/select.sgml postgresql/doc/src/sgml/ref/select.sgml *** postgresql.orig/doc/src/sgml/ref/select.sgml 2012-01-29 20:33:53.893650856 +0100 --- postgresql/doc/src/sgml/ref/select.sgml 2012-04-04 10:30:35.035960738 +0200 *************** FOR SHARE [ OF semNum; + + do + { + ImmediateInterruptOK = interruptOK; + CHECK_FOR_INTERRUPTS(); + errStatus = semop(sema->semId, &sops, 1); + ImmediateInterruptOK = false; + } while (errStatus < 0 && errno == EINTR && !get_timeout_indicator(LOCK_TIMEOUT)); + + if (get_timeout_indicator(LOCK_TIMEOUT)) + return; + if (errStatus < 0) + elog(FATAL, "semop(id=%d) failed: %m", sema->semId); + } diff -dcrpN postgresql.orig/src/backend/port/win32_sema.c postgresql/src/backend/port/win32_sema.c *** postgresql.orig/src/backend/port/win32_sema.c 2012-01-02 12:35:11.504186789 +0100 --- postgresql/src/backend/port/win32_sema.c 2012-04-04 15:01:35.197850015 +0200 *************** *** 16,21 **** --- 16,22 ---- #include "miscadmin.h" #include "storage/ipc.h" #include "storage/pg_sema.h" + #include "storage/timeout.h" static HANDLE *mySemSet; /* IDs of sema sets acquired so far */ static int numSems; /* number of sema sets acquired so far */ *************** PGSemaphoreTryLock(PGSemaphore sema) *** 205,207 **** --- 206,263 ---- /* keep compiler quiet */ return false; } + + /* + * PGSemaphoreTimedLock + * + * Lock a semaphore (decrement count), blocking if count would be < 0. + * Serve the interrupt if interruptOK is true. + * Return if lock_timeout expired. + */ + void + PGSemaphoreTimedLock(PGSemaphore sema, bool interruptOK) + { + DWORD ret; + HANDLE wh[2]; + + wh[0] = *sema; + wh[1] = pgwin32_signal_event; + + /* + * As in other implementations of PGSemaphoreLock, we need to check for + * cancel/die interrupts each time through the loop. But here, there is + * no hidden magic about whether the syscall will internally service a + * signal --- we do that ourselves. + */ + do + { + ImmediateInterruptOK = interruptOK; + CHECK_FOR_INTERRUPTS(); + + errno = 0; + ret = WaitForMultipleObjectsEx(2, wh, FALSE, INFINITE, TRUE); + + if (ret == WAIT_OBJECT_0) + { + /* We got it! */ + return; + } + else if (ret == WAIT_OBJECT_0 + 1) + { + /* Signal event is set - we have a signal to deliver */ + pgwin32_dispatch_queued_signals(); + errno = EINTR; + } + else + /* Otherwise we are in trouble */ + errno = EIDRM; + + ImmediateInterruptOK = false; + } while (errno == EINTR && !get_timeout_indicator(LOCK_TIMEOUT)); + + if (get_timeout_indicator(LOCK_TIMEOUT)) + return; + if (errno != 0) + ereport(FATAL, + (errmsg("could not lock semaphore: error code %d", (int) GetLastError()))); + } diff -dcrpN postgresql.orig/src/backend/postmaster/autovacuum.c postgresql/src/backend/postmaster/autovacuum.c *** postgresql.orig/src/backend/postmaster/autovacuum.c 2012-01-27 10:29:06.483649830 +0100 --- postgresql/src/backend/postmaster/autovacuum.c 2012-04-04 15:03:20.447379486 +0200 *************** *** 86,94 **** #include "storage/ipc.h" #include "storage/latch.h" #include "storage/pmsignal.h" - #include "storage/proc.h" #include "storage/procsignal.h" #include "storage/sinvaladt.h" #include "tcop/tcopprot.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" --- 86,94 ---- #include "storage/ipc.h" #include "storage/latch.h" #include "storage/pmsignal.h" #include "storage/procsignal.h" #include "storage/sinvaladt.h" + #include "storage/timeout.h" #include "tcop/tcopprot.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" *************** AutoVacLauncherMain(int argc, char *argv *** 484,490 **** /* Forget any pending QueryCancel request */ QueryCancelPending = false; ! disable_sig_alarm(true); QueryCancelPending = false; /* again in case timeout occurred */ /* Report the error to the server log */ --- 484,490 ---- /* Forget any pending QueryCancel request */ QueryCancelPending = false; ! disable_all_timeouts(false); QueryCancelPending = false; /* again in case timeout occurred */ /* Report the error to the server log */ diff -dcrpN postgresql.orig/src/backend/postmaster/postmaster.c postgresql/src/backend/postmaster/postmaster.c *** postgresql.orig/src/backend/postmaster/postmaster.c 2012-03-29 08:36:59.775468454 +0200 --- postgresql/src/backend/postmaster/postmaster.c 2012-04-04 15:03:54.927552935 +0200 *************** *** 112,118 **** #include "storage/ipc.h" #include "storage/pg_shmem.h" #include "storage/pmsignal.h" ! #include "storage/proc.h" #include "tcop/tcopprot.h" #include "utils/builtins.h" #include "utils/datetime.h" --- 112,118 ---- #include "storage/ipc.h" #include "storage/pg_shmem.h" #include "storage/pmsignal.h" ! #include "storage/timeout.h" #include "tcop/tcopprot.h" #include "utils/builtins.h" #include "utils/datetime.h" *************** BackendInitialize(Port *port) *** 3466,3474 **** * Ready to begin client interaction. We will give up and exit(1) after a * time delay, so that a broken client can't hog a connection * indefinitely. PreAuthDelay and any DNS interactions above don't count ! * against the time limit. */ ! if (!enable_sig_alarm(AuthenticationTimeout * 1000, false)) elog(FATAL, "could not set timer for startup packet timeout"); /* --- 3466,3474 ---- * Ready to begin client interaction. We will give up and exit(1) after a * time delay, so that a broken client can't hog a connection * indefinitely. PreAuthDelay and any DNS interactions above don't count ! * against the time limit. Use the deadlock timeout interface. */ ! if (!enable_timeout(DEADLOCK_TIMEOUT, AuthenticationTimeout * 1000)) elog(FATAL, "could not set timer for startup packet timeout"); /* *************** BackendInitialize(Port *port) *** 3506,3512 **** /* * Disable the timeout, and prevent SIGTERM/SIGQUIT again. */ ! if (!disable_sig_alarm(false)) elog(FATAL, "could not disable timer for startup packet timeout"); PG_SETMASK(&BlockSig); } --- 3506,3512 ---- /* * Disable the timeout, and prevent SIGTERM/SIGQUIT again. */ ! if (!disable_timeout(DEADLOCK_TIMEOUT, false)) elog(FATAL, "could not disable timer for startup packet timeout"); PG_SETMASK(&BlockSig); } diff -dcrpN postgresql.orig/src/backend/postmaster/startup.c postgresql/src/backend/postmaster/startup.c *** postgresql.orig/src/backend/postmaster/startup.c 2012-01-02 12:35:11.508186555 +0100 --- postgresql/src/backend/postmaster/startup.c 2012-04-04 15:04:32.902743966 +0200 *************** *** 27,33 **** #include "storage/ipc.h" #include "storage/latch.h" #include "storage/pmsignal.h" ! #include "storage/proc.h" #include "utils/guc.h" --- 27,33 ---- #include "storage/ipc.h" #include "storage/latch.h" #include "storage/pmsignal.h" ! #include "storage/timeout.h" #include "utils/guc.h" *************** StartupProcessMain(void) *** 195,202 **** pqsignal(SIGTERM, StartupProcShutdownHandler); /* request shutdown */ pqsignal(SIGQUIT, startupproc_quickdie); /* hard crash time */ if (EnableHotStandby) ! pqsignal(SIGALRM, handle_standby_sig_alarm); /* ignored unless ! * InHotStandby */ else pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); --- 195,202 ---- pqsignal(SIGTERM, StartupProcShutdownHandler); /* request shutdown */ pqsignal(SIGQUIT, startupproc_quickdie); /* hard crash time */ if (EnableHotStandby) ! pqsignal(SIGALRM, handle_sig_alarm); /* ignored unless ! * InHotStandby */ else pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); diff -dcrpN postgresql.orig/src/backend/storage/ipc/standby.c postgresql/src/backend/storage/ipc/standby.c *** postgresql.orig/src/backend/storage/ipc/standby.c 2012-02-05 12:28:36.003281960 +0100 --- postgresql/src/backend/storage/ipc/standby.c 2012-04-04 15:05:29.887030606 +0200 *************** *** 23,32 **** #include "miscadmin.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" - #include "storage/proc.h" #include "storage/procarray.h" #include "storage/sinvaladt.h" #include "storage/standby.h" #include "utils/ps_status.h" #include "utils/timestamp.h" --- 23,32 ---- #include "miscadmin.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/procarray.h" #include "storage/sinvaladt.h" #include "storage/standby.h" + #include "storage/timeout.h" #include "utils/ps_status.h" #include "utils/timestamp.h" *************** ResolveRecoveryConflictWithLock(Oid dbOi *** 394,400 **** void ResolveRecoveryConflictWithBufferPin(void) { - bool sig_alarm_enabled = false; TimestampTz ltime; TimestampTz now; --- 394,399 ---- *************** ResolveRecoveryConflictWithBufferPin(voi *** 409,417 **** * We're willing to wait forever for conflicts, so set timeout for * deadlock check (only) */ ! if (enable_standby_sig_alarm(now, now, true)) ! sig_alarm_enabled = true; ! else elog(FATAL, "could not set timer for process wakeup"); } else if (now >= ltime) --- 408,414 ---- * We're willing to wait forever for conflicts, so set timeout for * deadlock check (only) */ ! if (!enable_timeout(STANDBY_DEADLOCK_TIMEOUT, DeadlockTimeout)) elog(FATAL, "could not set timer for process wakeup"); } else if (now >= ltime) *************** ResolveRecoveryConflictWithBufferPin(voi *** 423,446 **** } else { /* * Wake up at ltime, and check for deadlocks as well if we will be * waiting longer than deadlock_timeout */ ! if (enable_standby_sig_alarm(now, ltime, false)) ! sig_alarm_enabled = true; ! else elog(FATAL, "could not set timer for process wakeup"); } /* Wait to be signaled by UnpinBuffer() */ ProcWaitForSignal(); ! if (sig_alarm_enabled) ! { ! if (!disable_standby_sig_alarm()) ! elog(FATAL, "could not disable timer for process wakeup"); ! } } void --- 420,447 ---- } else { + long secs, msecs; + int usecs; + /* * Wake up at ltime, and check for deadlocks as well if we will be * waiting longer than deadlock_timeout */ ! if (!enable_timeout(STANDBY_DEADLOCK_TIMEOUT, DeadlockTimeout)) ! elog(FATAL, "could not set timer for process wakeup"); ! ! TimestampDifference(now, ltime, &secs, &usecs); ! msecs = secs * 1000 + usecs / 1000; ! ! if (!enable_timeout(STANDBY_TIMEOUT, msecs)) elog(FATAL, "could not set timer for process wakeup"); } /* Wait to be signaled by UnpinBuffer() */ ProcWaitForSignal(); ! if (!disable_all_timeouts(false)) ! elog(FATAL, "could not disable timer for process wakeup"); } void diff -dcrpN postgresql.orig/src/backend/storage/lmgr/lmgr.c postgresql/src/backend/storage/lmgr/lmgr.c *** postgresql.orig/src/backend/storage/lmgr/lmgr.c 2012-01-02 12:35:11.517186027 +0100 --- postgresql/src/backend/storage/lmgr/lmgr.c 2012-04-03 15:55:39.981617113 +0200 *************** *** 19,26 **** --- 19,29 ---- #include "access/transam.h" #include "access/xact.h" #include "catalog/catalog.h" + #include "catalog/pg_database.h" #include "miscadmin.h" #include "storage/lmgr.h" + #include "utils/lsyscache.h" + #include "storage/proc.h" #include "storage/procarray.h" #include "utils/inval.h" *************** LockRelationOid(Oid relid, LOCKMODE lock *** 78,83 **** --- 81,101 ---- res = LockAcquire(&tag, lockmode, false, false); + if (res == LOCKACQUIRE_NOT_AVAIL) + { + char *relname = get_rel_name(relid); + if (relname) + ereport(ERROR, + (errcode(ERRCODE_LOCK_NOT_AVAILABLE), + errmsg("could not obtain lock on relation \"%s\"", + relname))); + else + ereport(ERROR, + (errcode(ERRCODE_LOCK_NOT_AVAILABLE), + errmsg("could not obtain lock on relation with OID %u", + relid))); + } + /* * Now that we have the lock, check for invalidation messages, so that we * will update or flush any stale relcache entry before we try to use it. *************** LockRelation(Relation relation, LOCKMODE *** 174,179 **** --- 192,203 ---- res = LockAcquire(&tag, lockmode, false, false); + if (res == LOCKACQUIRE_NOT_AVAIL) + ereport(ERROR, + (errcode(ERRCODE_LOCK_NOT_AVAILABLE), + errmsg("could not obtain lock on relation \"%s\"", + RelationGetRelationName(relation)))); + /* * Now that we have the lock, check for invalidation messages; see notes * in LockRelationOid. *************** LockRelationIdForSession(LockRelId *reli *** 251,257 **** SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId); ! (void) LockAcquire(&tag, lockmode, true, false); } /* --- 275,294 ---- SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId); ! if (LockAcquire(&tag, lockmode, true, false) == LOCKACQUIRE_NOT_AVAIL) ! { ! char *relname = get_rel_name(relid->relId); ! if (relname) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on relation \"%s\"", ! relname))); ! else ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on relation with OID %u", ! relid->relId))); ! } } /* *************** LockRelationForExtension(Relation relati *** 286,292 **** relation->rd_lockInfo.lockRelId.dbId, relation->rd_lockInfo.lockRelId.relId); ! (void) LockAcquire(&tag, lockmode, false, false); } /* --- 323,333 ---- relation->rd_lockInfo.lockRelId.dbId, relation->rd_lockInfo.lockRelId.relId); ! if (LockAcquire(&tag, lockmode, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on index \"%s\"", ! RelationGetRelationName(relation)))); } /* *************** LockPage(Relation relation, BlockNumber *** 320,326 **** relation->rd_lockInfo.lockRelId.relId, blkno); ! (void) LockAcquire(&tag, lockmode, false, false); } /* --- 361,371 ---- relation->rd_lockInfo.lockRelId.relId, blkno); ! if (LockAcquire(&tag, lockmode, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on page %u of relation \"%s\"", ! blkno, RelationGetRelationName(relation)))); } /* *************** LockTuple(Relation relation, ItemPointer *** 376,382 **** ItemPointerGetBlockNumber(tid), ItemPointerGetOffsetNumber(tid)); ! (void) LockAcquire(&tag, lockmode, false, false); } /* --- 421,431 ---- ItemPointerGetBlockNumber(tid), ItemPointerGetOffsetNumber(tid)); ! if (LockAcquire(&tag, lockmode, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on row in relation \"%s\"", ! RelationGetRelationName(relation)))); } /* *************** XactLockTableInsert(TransactionId xid) *** 430,436 **** SET_LOCKTAG_TRANSACTION(tag, xid); ! (void) LockAcquire(&tag, ExclusiveLock, false, false); } /* --- 479,488 ---- SET_LOCKTAG_TRANSACTION(tag, xid); ! if (LockAcquire(&tag, ExclusiveLock, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on transaction with ID %u", xid))); } /* *************** XactLockTableWait(TransactionId xid) *** 474,480 **** SET_LOCKTAG_TRANSACTION(tag, xid); ! (void) LockAcquire(&tag, ShareLock, false, false); LockRelease(&tag, ShareLock, false); --- 526,535 ---- SET_LOCKTAG_TRANSACTION(tag, xid); ! if (LockAcquire(&tag, ShareLock, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on transaction with ID %u", xid))); LockRelease(&tag, ShareLock, false); *************** LockDatabaseObject(Oid classid, Oid obji *** 535,541 **** objid, objsubid); ! (void) LockAcquire(&tag, lockmode, false, false); /* Make sure syscaches are up-to-date with any changes we waited for */ AcceptInvalidationMessages(); --- 590,600 ---- objid, objsubid); ! if (LockAcquire(&tag, lockmode, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on class:object: %u:%u", ! classid, objid))); /* Make sure syscaches are up-to-date with any changes we waited for */ AcceptInvalidationMessages(); *************** LockSharedObject(Oid classid, Oid objid, *** 576,582 **** objid, objsubid); ! (void) LockAcquire(&tag, lockmode, false, false); /* Make sure syscaches are up-to-date with any changes we waited for */ AcceptInvalidationMessages(); --- 635,645 ---- objid, objsubid); ! if (LockAcquire(&tag, lockmode, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on class:object: %u:%u", ! classid, objid))); /* Make sure syscaches are up-to-date with any changes we waited for */ AcceptInvalidationMessages(); *************** LockSharedObjectForSession(Oid classid, *** 618,624 **** objid, objsubid); ! (void) LockAcquire(&tag, lockmode, true, false); } /* --- 681,702 ---- objid, objsubid); ! if (LockAcquire(&tag, lockmode, true, false) == LOCKACQUIRE_NOT_AVAIL) ! switch(classid) ! { ! case DatabaseRelationId: ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on database with ID %u", ! objid))); ! break; ! default: ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on class:object: %u:%u", ! classid, objid))); ! break; ! } } /* diff -dcrpN postgresql.orig/src/backend/storage/lmgr/lock.c postgresql/src/backend/storage/lmgr/lock.c *** postgresql.orig/src/backend/storage/lmgr/lock.c 2012-01-25 06:45:42.322747976 +0100 --- postgresql/src/backend/storage/lmgr/lock.c 2012-04-04 10:47:12.072945214 +0200 *************** static void RemoveLocalLock(LOCALLOCK *l *** 338,344 **** static PROCLOCK *SetupLockInTable(LockMethod lockMethodTable, PGPROC *proc, const LOCKTAG *locktag, uint32 hashcode, LOCKMODE lockmode); static void GrantLockLocal(LOCALLOCK *locallock, ResourceOwner owner); ! static void WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner); static void ReleaseLockForOwner(LOCALLOCK *locallock, ResourceOwner owner); static bool UnGrantLock(LOCK *lock, LOCKMODE lockmode, PROCLOCK *proclock, LockMethod lockMethodTable); --- 338,344 ---- static PROCLOCK *SetupLockInTable(LockMethod lockMethodTable, PGPROC *proc, const LOCKTAG *locktag, uint32 hashcode, LOCKMODE lockmode); static void GrantLockLocal(LOCALLOCK *locallock, ResourceOwner owner); ! static int WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner); static void ReleaseLockForOwner(LOCALLOCK *locallock, ResourceOwner owner); static bool UnGrantLock(LOCK *lock, LOCKMODE lockmode, PROCLOCK *proclock, LockMethod lockMethodTable); *************** ProcLockHashCode(const PROCLOCKTAG *proc *** 544,550 **** * dontWait: if true, don't wait to acquire lock * * Returns one of: ! * LOCKACQUIRE_NOT_AVAIL lock not available, and dontWait=true * LOCKACQUIRE_OK lock successfully acquired * LOCKACQUIRE_ALREADY_HELD incremented count for lock already held * --- 544,550 ---- * dontWait: if true, don't wait to acquire lock * * Returns one of: ! * LOCKACQUIRE_NOT_AVAIL lock not available, either dontWait=true or timeout * LOCKACQUIRE_OK lock successfully acquired * LOCKACQUIRE_ALREADY_HELD incremented count for lock already held * *************** LockAcquireExtended(const LOCKTAG *lockt *** 863,869 **** locktag->locktag_type, lockmode); ! WaitOnLock(locallock, owner); TRACE_POSTGRESQL_LOCK_WAIT_DONE(locktag->locktag_field1, locktag->locktag_field2, --- 863,869 ---- locktag->locktag_type, lockmode); ! status = WaitOnLock(locallock, owner); TRACE_POSTGRESQL_LOCK_WAIT_DONE(locktag->locktag_field1, locktag->locktag_field2, *************** LockAcquireExtended(const LOCKTAG *lockt *** 878,897 **** * done when the lock was granted to us --- see notes in WaitOnLock. */ ! /* ! * Check the proclock entry status, in case something in the ipc ! * communication doesn't work correctly. ! */ ! if (!(proclock->holdMask & LOCKBIT_ON(lockmode))) { ! PROCLOCK_PRINT("LockAcquire: INCONSISTENT", proclock); ! LOCK_PRINT("LockAcquire: INCONSISTENT", lock, lockmode); ! /* Should we retry ? */ ! LWLockRelease(partitionLock); ! elog(ERROR, "LockAcquire failed"); } - PROCLOCK_PRINT("LockAcquire: granted", proclock); - LOCK_PRINT("LockAcquire: granted", lock, lockmode); } LWLockRelease(partitionLock); --- 878,909 ---- * done when the lock was granted to us --- see notes in WaitOnLock. */ ! switch (status) { ! case STATUS_OK: ! /* ! * Check the proclock entry status, in case something in the ipc ! * communication doesn't work correctly. ! */ ! if (!(proclock->holdMask & LOCKBIT_ON(lockmode))) ! { ! PROCLOCK_PRINT("LockAcquire: INCONSISTENT", proclock); ! LOCK_PRINT("LockAcquire: INCONSISTENT", lock, lockmode); ! /* Should we retry ? */ ! LWLockRelease(partitionLock); ! elog(ERROR, "LockAcquire failed"); ! } ! PROCLOCK_PRINT("LockAcquire: granted", proclock); ! LOCK_PRINT("LockAcquire: granted", lock, lockmode); ! break; ! case STATUS_WAITING: ! PROCLOCK_PRINT("LockAcquire: timed out", proclock); ! LOCK_PRINT("LockAcquire: timed out", lock, lockmode); ! break; ! default: ! elog(ERROR, "LockAcquire invalid status"); ! break; } } LWLockRelease(partitionLock); *************** LockAcquireExtended(const LOCKTAG *lockt *** 911,917 **** locktag->locktag_field2); } ! return LOCKACQUIRE_OK; } /* --- 923,929 ---- locktag->locktag_field2); } ! return (status == STATUS_OK ? LOCKACQUIRE_OK : LOCKACQUIRE_NOT_AVAIL); } /* *************** GrantAwaitedLock(void) *** 1371,1384 **** * Caller must have set MyProc->heldLocks to reflect locks already held * on the lockable object by this process. * * The appropriate partition lock must be held at entry. */ ! static void WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner) { LOCKMETHODID lockmethodid = LOCALLOCK_LOCKMETHOD(*locallock); LockMethod lockMethodTable = LockMethods[lockmethodid]; char *volatile new_status = NULL; LOCK_PRINT("WaitOnLock: sleeping on lock", locallock->lock, locallock->tag.mode); --- 1383,1402 ---- * Caller must have set MyProc->heldLocks to reflect locks already held * on the lockable object by this process. * + * Result: returns value of ProcSleep() + * STATUS_OK if we acquired the lock + * STATUS_ERROR if not (deadlock) + * STATUS_WAITING if not (timeout) + * * The appropriate partition lock must be held at entry. */ ! static int WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner) { LOCKMETHODID lockmethodid = LOCALLOCK_LOCKMETHOD(*locallock); LockMethod lockMethodTable = LockMethods[lockmethodid]; char *volatile new_status = NULL; + int wait_status; LOCK_PRINT("WaitOnLock: sleeping on lock", locallock->lock, locallock->tag.mode); *************** WaitOnLock(LOCALLOCK *locallock, Resourc *** 1420,1427 **** */ PG_TRY(); { ! if (ProcSleep(locallock, lockMethodTable) != STATUS_OK) { /* * We failed as a result of a deadlock, see CheckDeadLock(). Quit * now. --- 1438,1450 ---- */ PG_TRY(); { ! wait_status = ProcSleep(locallock, lockMethodTable); ! switch (wait_status) { + case STATUS_OK: + case STATUS_WAITING: + break; + default: /* * We failed as a result of a deadlock, see CheckDeadLock(). Quit * now. *************** WaitOnLock(LOCALLOCK *locallock, Resourc *** 1466,1473 **** pfree(new_status); } ! LOCK_PRINT("WaitOnLock: wakeup on lock", locallock->lock, locallock->tag.mode); } /* --- 1489,1502 ---- pfree(new_status); } ! if (wait_status == STATUS_OK) ! LOCK_PRINT("WaitOnLock: wakeup on lock", ! locallock->lock, locallock->tag.mode); ! else if (wait_status == STATUS_WAITING) ! LOCK_PRINT("WaitOnLock: timeout on lock", locallock->lock, locallock->tag.mode); + + return wait_status; } /* *************** VirtualXactLock(VirtualTransactionId vxi *** 3739,3745 **** LWLockRelease(proc->backendLock); /* Time to wait. */ ! (void) LockAcquire(&tag, ShareLock, false, false); LockRelease(&tag, ShareLock, false); return true; --- 3768,3778 ---- LWLockRelease(proc->backendLock); /* Time to wait. */ ! if (LockAcquire(&tag, ShareLock, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on virtual transaction with ID %u", ! vxid.localTransactionId))); LockRelease(&tag, ShareLock, false); return true; diff -dcrpN postgresql.orig/src/backend/storage/lmgr/Makefile postgresql/src/backend/storage/lmgr/Makefile *** postgresql.orig/src/backend/storage/lmgr/Makefile 2011-02-10 10:36:32.248685484 +0100 --- postgresql/src/backend/storage/lmgr/Makefile 2012-04-04 14:31:15.547760162 +0200 *************** subdir = src/backend/storage/lmgr *** 12,18 **** 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 include $(top_srcdir)/src/backend/common.mk --- 12,18 ---- 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 timeout.o include $(top_srcdir)/src/backend/common.mk diff -dcrpN postgresql.orig/src/backend/storage/lmgr/proc.c postgresql/src/backend/storage/lmgr/proc.c *** postgresql.orig/src/backend/storage/lmgr/proc.c 2012-03-22 11:00:51.723002056 +0100 --- postgresql/src/backend/storage/lmgr/proc.c 2012-04-04 14:57:50.740720758 +0200 *************** *** 48,59 **** #include "storage/procarray.h" #include "storage/procsignal.h" #include "storage/spin.h" #include "utils/timestamp.h" /* GUC variables */ - int DeadlockTimeout = 1000; - int StatementTimeout = 0; bool log_lock_waits = false; /* Pointer to this process's PGPROC struct, if any */ --- 48,58 ---- #include "storage/procarray.h" #include "storage/procsignal.h" #include "storage/spin.h" + #include "storage/timeout.h" #include "utils/timestamp.h" /* GUC variables */ bool log_lock_waits = false; /* Pointer to this process's PGPROC struct, if any */ *************** PGPROC *PreparedXactProcs = NULL; *** 77,103 **** /* If we are waiting for a lock, this points to the associated LOCALLOCK */ static LOCALLOCK *lockAwaited = NULL; ! /* Mark these volatile because they can be changed by signal handler */ ! static volatile bool standby_timeout_active = false; ! static volatile bool statement_timeout_active = false; ! static volatile bool deadlock_timeout_active = false; ! static volatile DeadLockState deadlock_state = DS_NOT_YET_CHECKED; ! volatile bool cancel_from_timeout = false; /* timeout_start_time is set when log_lock_waits is true */ static TimestampTz timeout_start_time; - /* statement_fin_time is valid only if statement_timeout_active is true */ - static TimestampTz statement_fin_time; - static TimestampTz statement_fin_time2; /* valid only in recovery */ - - static void RemoveProcFromArray(int code, Datum arg); static void ProcKill(int code, Datum arg); static void AuxiliaryProcKill(int code, Datum arg); - static bool CheckStatementTimeout(void); - static bool CheckStandbyTimeout(void); - /* * Report shared-memory space needed by InitProcGlobal. --- 76,90 ---- /* If we are waiting for a lock, this points to the associated LOCALLOCK */ static LOCALLOCK *lockAwaited = NULL; ! /* Mark this volatile because they can be changed by signal handler */ ! volatile DeadLockState deadlock_state = DS_NOT_YET_CHECKED; /* timeout_start_time is set when log_lock_waits is true */ static TimestampTz timeout_start_time; static void RemoveProcFromArray(int code, Datum arg); static void ProcKill(int code, Datum arg); static void AuxiliaryProcKill(int code, Datum arg); /* * Report shared-memory space needed by InitProcGlobal. *************** LockWaitCancel(void) *** 651,657 **** return; /* Turn off the deadlock timer, if it's still running (see ProcSleep) */ ! disable_sig_alarm(false); /* Unlink myself from the wait queue, if on it (might not be anymore!) */ partitionLock = LockHashPartitionLock(lockAwaited->hashcode); --- 638,644 ---- return; /* Turn off the deadlock timer, if it's still running (see ProcSleep) */ ! disable_timeout(DEADLOCK_TIMEOUT, false); /* Unlink myself from the wait queue, if on it (might not be anymore!) */ partitionLock = LockHashPartitionLock(lockAwaited->hashcode); *************** ProcQueueInit(PROC_QUEUE *queue) *** 885,891 **** * The lock table's partition lock must be held at entry, and will be held * at exit. * ! * Result: STATUS_OK if we acquired the lock, STATUS_ERROR if not (deadlock). * * ASSUME: that no one will fiddle with the queue until after * we release the partition lock. --- 872,881 ---- * The lock table's partition lock must be held at entry, and will be held * at exit. * ! * Result: ! * STATUS_OK if we acquired the lock ! * STATUS_ERROR if not (deadlock) ! * STATUS_WAITING if not (timeout) * * ASSUME: that no one will fiddle with the queue until after * we release the partition lock. *************** ProcSleep(LOCALLOCK *locallock, LockMeth *** 907,912 **** --- 897,903 ---- LOCKMASK myHeldLocks = MyProc->heldLocks; bool early_deadlock = false; bool allow_autovacuum_cancel = true; + bool timeout_detected; int myWaitStatus; PGPROC *proc; int i; *************** ProcSleep(LOCALLOCK *locallock, LockMeth *** 1044,1055 **** * By delaying the check until we've waited for a bit, we can avoid * running the rather expensive deadlock-check code in most cases. */ ! if (!enable_sig_alarm(DeadlockTimeout, false)) elog(FATAL, "could not set timer for process wakeup"); /* ! * If someone wakes us between LWLockRelease and PGSemaphoreLock, ! * PGSemaphoreLock will not block. The wakeup is "saved" by the semaphore * implementation. While this is normally good, there are cases where a * saved wakeup might be leftover from a previous operation (for example, * we aborted ProcWaitForSignal just before someone did ProcSendSignal). --- 1035,1054 ---- * By delaying the check until we've waited for a bit, we can avoid * running the rather expensive deadlock-check code in most cases. */ ! if (!enable_timeout(DEADLOCK_TIMEOUT, DeadlockTimeout)) elog(FATAL, "could not set timer for process wakeup"); /* ! * Reset timer so we are awaken in case of lock timeout. ! * This doesn't modify the timer for deadlock check in case ! * the deadlock check happens earlier. ! */ ! if (!enable_timeout(LOCK_TIMEOUT, LockTimeout)) ! elog(FATAL, "could not set timer for process wakeup"); ! ! /* ! * If someone wakes us between LWLockRelease and PGSemaphoreTimedLock, ! * PGSemaphoreTimedLock will not block. The wakeup is "saved" by the semaphore * implementation. While this is normally good, there are cases where a * saved wakeup might be leftover from a previous operation (for example, * we aborted ProcWaitForSignal just before someone did ProcSendSignal). *************** ProcSleep(LOCALLOCK *locallock, LockMeth *** 1066,1072 **** */ do { ! PGSemaphoreLock(&MyProc->sem, true); /* * waitStatus could change from STATUS_WAITING to something else --- 1065,1074 ---- */ do { ! PGSemaphoreTimedLock(&MyProc->sem, true); ! ! if (get_timeout_indicator(LOCK_TIMEOUT)) ! break; /* * waitStatus could change from STATUS_WAITING to something else *************** ProcSleep(LOCALLOCK *locallock, LockMeth *** 1194,1202 **** } while (myWaitStatus == STATUS_WAITING); /* ! * Disable the timer, if it's still running */ ! if (!disable_sig_alarm(false)) elog(FATAL, "could not disable timer for process wakeup"); /* --- 1196,1212 ---- } while (myWaitStatus == STATUS_WAITING); /* ! * Disable the deadlock timer, if it's still running */ ! if (!disable_timeout(DEADLOCK_TIMEOUT, false)) ! elog(FATAL, "could not disable timer for process wakeup"); ! ! /* ! * Disable the lock timeout timer, if it's still running ! * but keep the indicator for later checks. ! */ ! timeout_detected = get_timeout_indicator(LOCK_TIMEOUT); ! if (!disable_timeout(LOCK_TIMEOUT, false)) elog(FATAL, "could not disable timer for process wakeup"); /* *************** ProcSleep(LOCALLOCK *locallock, LockMeth *** 1207,1212 **** --- 1217,1231 ---- LWLockAcquire(partitionLock, LW_EXCLUSIVE); /* + * If we're in timeout, so: + * 1. we're not waiting anymore and + * 2. we're not the one that the lock will be granted to, + * remove ourselves from the wait queue. + */ + if (timeout_detected) + RemoveFromWaitQueue(MyProc, hashcode); + + /* * We no longer want LockWaitCancel to do anything. */ lockAwaited = NULL; *************** ProcSleep(LOCALLOCK *locallock, LockMeth *** 1220,1227 **** /* * We don't have to do anything else, because the awaker did all the * necessary update of the lock table and MyProc. */ ! return MyProc->waitStatus; } --- 1239,1248 ---- /* * We don't have to do anything else, because the awaker did all the * necessary update of the lock table and MyProc. + * RemoveFromWaitQueue() have set MyProc->waitStatus = STATUS_ERROR, + * we need to distinguish this case. */ ! return (timeout_detected ? STATUS_WAITING : MyProc->waitStatus); } *************** ProcLockWakeup(LockMethod lockMethodTabl *** 1328,1448 **** } /* - * CheckDeadLock - * - * We only get to this routine if we got SIGALRM after DeadlockTimeout - * while waiting for a lock to be released by some other process. Look - * to see if there's a deadlock; if not, just return and continue waiting. - * (But signal ProcSleep to log a message, if log_lock_waits is true.) - * If we have a real deadlock, remove ourselves from the lock's wait queue - * and signal an error to ProcSleep. - * - * NB: this is run inside a signal handler, so be very wary about what is done - * here or in called routines. - */ - static void - CheckDeadLock(void) - { - int i; - - /* - * Acquire exclusive lock on the entire shared lock data structures. Must - * grab LWLocks in partition-number order to avoid LWLock deadlock. - * - * Note that the deadlock check interrupt had better not be enabled - * anywhere that this process itself holds lock partition locks, else this - * will wait forever. Also note that LWLockAcquire creates a critical - * section, so that this routine cannot be interrupted by cancel/die - * interrupts. - */ - for (i = 0; i < NUM_LOCK_PARTITIONS; i++) - LWLockAcquire(FirstLockMgrLock + i, LW_EXCLUSIVE); - - /* - * Check to see if we've been awoken by anyone in the interim. - * - * If we have, we can return and resume our transaction -- happy day. - * Before we are awoken the process releasing the lock grants it to us so - * we know that we don't have to wait anymore. - * - * We check by looking to see if we've been unlinked from the wait queue. - * This is quicker than checking our semaphore's state, since no kernel - * call is needed, and it is safe because we hold the lock partition lock. - */ - if (MyProc->links.prev == NULL || - MyProc->links.next == NULL) - goto check_done; - - #ifdef LOCK_DEBUG - if (Debug_deadlocks) - DumpAllLocks(); - #endif - - /* Run the deadlock check, and set deadlock_state for use by ProcSleep */ - deadlock_state = DeadLockCheck(MyProc); - - if (deadlock_state == DS_HARD_DEADLOCK) - { - /* - * Oops. We have a deadlock. - * - * Get this process out of wait state. (Note: we could do this more - * efficiently by relying on lockAwaited, but use this coding to - * preserve the flexibility to kill some other transaction than the - * one detecting the deadlock.) - * - * RemoveFromWaitQueue sets MyProc->waitStatus to STATUS_ERROR, so - * ProcSleep will report an error after we return from the signal - * handler. - */ - Assert(MyProc->waitLock != NULL); - RemoveFromWaitQueue(MyProc, LockTagHashCode(&(MyProc->waitLock->tag))); - - /* - * Unlock my semaphore so that the interrupted ProcSleep() call can - * finish. - */ - PGSemaphoreUnlock(&MyProc->sem); - - /* - * We're done here. Transaction abort caused by the error that - * ProcSleep will raise will cause any other locks we hold to be - * released, thus allowing other processes to wake up; we don't need - * to do that here. NOTE: an exception is that releasing locks we - * hold doesn't consider the possibility of waiters that were blocked - * behind us on the lock we just failed to get, and might now be - * wakable because we're not in front of them anymore. However, - * RemoveFromWaitQueue took care of waking up any such processes. - */ - } - else if (log_lock_waits || deadlock_state == DS_BLOCKED_BY_AUTOVACUUM) - { - /* - * Unlock my semaphore so that the interrupted ProcSleep() call can - * print the log message (we daren't do it here because we are inside - * a signal handler). It will then sleep again until someone releases - * the lock. - * - * If blocked by autovacuum, this wakeup will enable ProcSleep to send - * the canceling signal to the autovacuum worker. - */ - PGSemaphoreUnlock(&MyProc->sem); - } - - /* - * And release locks. We do this in reverse order for two reasons: (1) - * Anyone else who needs more than one of the locks will be trying to lock - * them in increasing order; we don't want to release the other process - * until it can get all the locks it needs. (2) This avoids O(N^2) - * behavior inside LWLockRelease. - */ - check_done: - for (i = NUM_LOCK_PARTITIONS; --i >= 0;) - LWLockRelease(FirstLockMgrLock + i); - } - - - /* * ProcWaitForSignal - wait for a signal from another backend. * * This can share the semaphore normally used for waiting for locks, --- 1349,1354 ---- *************** ProcSendSignal(int pid) *** 1494,1894 **** if (proc != NULL) PGSemaphoreUnlock(&proc->sem); } - - - /***************************************************************************** - * SIGALRM interrupt support - * - * Maybe these should be in pqsignal.c? - *****************************************************************************/ - - /* - * Enable the SIGALRM interrupt to fire after the specified delay - * - * Delay is given in milliseconds. Caller should be sure a SIGALRM - * signal handler is installed before this is called. - * - * This code properly handles nesting of deadlock timeout alarms within - * statement timeout alarms. - * - * Returns TRUE if okay, FALSE on failure. - */ - bool - enable_sig_alarm(int delayms, bool is_statement_timeout) - { - TimestampTz fin_time; - struct itimerval timeval; - - if (is_statement_timeout) - { - /* - * Begin statement-level timeout - * - * Note that we compute statement_fin_time with reference to the - * statement_timestamp, but apply the specified delay without any - * correction; that is, we ignore whatever time has elapsed since - * statement_timestamp was set. In the normal case only a small - * interval will have elapsed and so this doesn't matter, but there - * are corner cases (involving multi-statement query strings with - * embedded COMMIT or ROLLBACK) where we might re-initialize the - * statement timeout long after initial receipt of the message. In - * such cases the enforcement of the statement timeout will be a bit - * inconsistent. This annoyance is judged not worth the cost of - * performing an additional gettimeofday() here. - */ - Assert(!deadlock_timeout_active); - fin_time = GetCurrentStatementStartTimestamp(); - fin_time = TimestampTzPlusMilliseconds(fin_time, delayms); - statement_fin_time = fin_time; - cancel_from_timeout = false; - statement_timeout_active = true; - } - else if (statement_timeout_active) - { - /* - * Begin deadlock timeout with statement-level timeout active - * - * Here, we want to interrupt at the closer of the two timeout times. - * If fin_time >= statement_fin_time then we need not touch the - * existing timer setting; else set up to interrupt at the deadlock - * timeout time. - * - * NOTE: in this case it is possible that this routine will be - * interrupted by the previously-set timer alarm. This is okay - * because the signal handler will do only what it should do according - * to the state variables. The deadlock checker may get run earlier - * than normal, but that does no harm. - */ - timeout_start_time = GetCurrentTimestamp(); - fin_time = TimestampTzPlusMilliseconds(timeout_start_time, delayms); - deadlock_timeout_active = true; - if (fin_time >= statement_fin_time) - return true; - } - else - { - /* Begin deadlock timeout with no statement-level timeout */ - deadlock_timeout_active = true; - /* GetCurrentTimestamp can be expensive, so only do it if we must */ - if (log_lock_waits) - timeout_start_time = GetCurrentTimestamp(); - } - - /* If we reach here, okay to set the timer interrupt */ - MemSet(&timeval, 0, sizeof(struct itimerval)); - timeval.it_value.tv_sec = delayms / 1000; - timeval.it_value.tv_usec = (delayms % 1000) * 1000; - if (setitimer(ITIMER_REAL, &timeval, NULL)) - return false; - return true; - } - - /* - * Cancel the SIGALRM timer, either for a deadlock timeout or a statement - * timeout. If a deadlock timeout is canceled, any active statement timeout - * remains in force. - * - * Returns TRUE if okay, FALSE on failure. - */ - bool - disable_sig_alarm(bool is_statement_timeout) - { - /* - * Always disable the interrupt if it is active; this avoids being - * interrupted by the signal handler and thereby possibly getting - * confused. - * - * We will re-enable the interrupt if necessary in CheckStatementTimeout. - */ - if (statement_timeout_active || deadlock_timeout_active) - { - struct itimerval timeval; - - MemSet(&timeval, 0, sizeof(struct itimerval)); - if (setitimer(ITIMER_REAL, &timeval, NULL)) - { - statement_timeout_active = false; - cancel_from_timeout = false; - deadlock_timeout_active = false; - return false; - } - } - - /* Always cancel deadlock timeout, in case this is error cleanup */ - deadlock_timeout_active = false; - - /* Cancel or reschedule statement timeout */ - if (is_statement_timeout) - { - statement_timeout_active = false; - cancel_from_timeout = false; - } - else if (statement_timeout_active) - { - if (!CheckStatementTimeout()) - return false; - } - return true; - } - - - /* - * Check for statement timeout. If the timeout time has come, - * trigger a query-cancel interrupt; if not, reschedule the SIGALRM - * interrupt to occur at the right time. - * - * Returns true if okay, false if failed to set the interrupt. - */ - static bool - CheckStatementTimeout(void) - { - TimestampTz now; - - if (!statement_timeout_active) - return true; /* do nothing if not active */ - - now = GetCurrentTimestamp(); - - if (now >= statement_fin_time) - { - /* Time to die */ - statement_timeout_active = false; - cancel_from_timeout = true; - #ifdef HAVE_SETSID - /* try to signal whole process group */ - kill(-MyProcPid, SIGINT); - #endif - kill(MyProcPid, SIGINT); - } - else - { - /* Not time yet, so (re)schedule the interrupt */ - long secs; - int usecs; - struct itimerval timeval; - - TimestampDifference(now, statement_fin_time, - &secs, &usecs); - - /* - * It's possible that the difference is less than a microsecond; - * ensure we don't cancel, rather than set, the interrupt. - */ - if (secs == 0 && usecs == 0) - usecs = 1; - MemSet(&timeval, 0, sizeof(struct itimerval)); - timeval.it_value.tv_sec = secs; - timeval.it_value.tv_usec = usecs; - if (setitimer(ITIMER_REAL, &timeval, NULL)) - return false; - } - - return true; - } - - - /* - * Signal handler for SIGALRM for normal user backends - * - * Process deadlock check and/or statement timeout check, as needed. - * To avoid various edge cases, we must be careful to do nothing - * when there is nothing to be done. We also need to be able to - * reschedule the timer interrupt if called before end of statement. - */ - void - handle_sig_alarm(SIGNAL_ARGS) - { - int save_errno = errno; - - /* SIGALRM is cause for waking anything waiting on the process latch */ - if (MyProc) - SetLatch(&MyProc->procLatch); - - if (deadlock_timeout_active) - { - deadlock_timeout_active = false; - CheckDeadLock(); - } - - if (statement_timeout_active) - (void) CheckStatementTimeout(); - - errno = save_errno; - } - - /* - * Signal handler for SIGALRM in Startup process - * - * To avoid various edge cases, we must be careful to do nothing - * when there is nothing to be done. We also need to be able to - * reschedule the timer interrupt if called before end of statement. - * - * We set either deadlock_timeout_active or statement_timeout_active - * or both. Interrupts are enabled if standby_timeout_active. - */ - bool - enable_standby_sig_alarm(TimestampTz now, TimestampTz fin_time, bool deadlock_only) - { - TimestampTz deadlock_time = TimestampTzPlusMilliseconds(now, - DeadlockTimeout); - - if (deadlock_only) - { - /* - * Wake up at deadlock_time only, then wait forever - */ - statement_fin_time = deadlock_time; - deadlock_timeout_active = true; - statement_timeout_active = false; - } - else if (fin_time > deadlock_time) - { - /* - * Wake up at deadlock_time, then again at fin_time - */ - statement_fin_time = deadlock_time; - statement_fin_time2 = fin_time; - deadlock_timeout_active = true; - statement_timeout_active = true; - } - else - { - /* - * Wake only at fin_time because its fairly soon - */ - statement_fin_time = fin_time; - deadlock_timeout_active = false; - statement_timeout_active = true; - } - - if (deadlock_timeout_active || statement_timeout_active) - { - long secs; - int usecs; - struct itimerval timeval; - - TimestampDifference(now, statement_fin_time, - &secs, &usecs); - if (secs == 0 && usecs == 0) - usecs = 1; - MemSet(&timeval, 0, sizeof(struct itimerval)); - timeval.it_value.tv_sec = secs; - timeval.it_value.tv_usec = usecs; - if (setitimer(ITIMER_REAL, &timeval, NULL)) - return false; - standby_timeout_active = true; - } - - return true; - } - - bool - disable_standby_sig_alarm(void) - { - /* - * Always disable the interrupt if it is active; this avoids being - * interrupted by the signal handler and thereby possibly getting - * confused. - * - * We will re-enable the interrupt if necessary in CheckStandbyTimeout. - */ - if (standby_timeout_active) - { - struct itimerval timeval; - - MemSet(&timeval, 0, sizeof(struct itimerval)); - if (setitimer(ITIMER_REAL, &timeval, NULL)) - { - standby_timeout_active = false; - return false; - } - } - - standby_timeout_active = false; - - return true; - } - - /* - * CheckStandbyTimeout() runs unconditionally in the Startup process - * SIGALRM handler. Timers will only be set when InHotStandby. - * We simply ignore any signals unless the timer has been set. - */ - static bool - CheckStandbyTimeout(void) - { - TimestampTz now; - bool reschedule = false; - - standby_timeout_active = false; - - now = GetCurrentTimestamp(); - - /* - * Reschedule the timer if its not time to wake yet, or if we have both - * timers set and the first one has just been reached. - */ - if (now >= statement_fin_time) - { - if (deadlock_timeout_active) - { - /* - * We're still waiting when we reach deadlock timeout, so send out - * a request to have other backends check themselves for deadlock. - * Then continue waiting until statement_fin_time, if that's set. - */ - SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK); - deadlock_timeout_active = false; - - /* - * Begin second waiting period if required. - */ - if (statement_timeout_active) - { - reschedule = true; - statement_fin_time = statement_fin_time2; - } - } - else - { - /* - * We've now reached statement_fin_time, so ask all conflicts to - * leave, so we can press ahead with applying changes in recovery. - */ - SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN); - } - } - else - reschedule = true; - - if (reschedule) - { - long secs; - int usecs; - struct itimerval timeval; - - TimestampDifference(now, statement_fin_time, - &secs, &usecs); - if (secs == 0 && usecs == 0) - usecs = 1; - MemSet(&timeval, 0, sizeof(struct itimerval)); - timeval.it_value.tv_sec = secs; - timeval.it_value.tv_usec = usecs; - if (setitimer(ITIMER_REAL, &timeval, NULL)) - return false; - standby_timeout_active = true; - } - - return true; - } - - void - handle_standby_sig_alarm(SIGNAL_ARGS) - { - int save_errno = errno; - - if (standby_timeout_active) - (void) CheckStandbyTimeout(); - - errno = save_errno; - } --- 1400,1402 ---- diff -dcrpN postgresql.orig/src/backend/storage/lmgr/timeout.c postgresql/src/backend/storage/lmgr/timeout.c *** postgresql.orig/src/backend/storage/lmgr/timeout.c 1970-01-01 01:00:00.000000000 +0100 --- postgresql/src/backend/storage/lmgr/timeout.c 2012-04-04 15:12:26.057149045 +0200 *************** *** 0 **** --- 1,723 ---- + /*------------------------------------------------------------------------- + * + * timeout.c + * routines to manage timeout sources handled by SIGALRM + * + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/storage/lmgr/timeout.c + * + *------------------------------------------------------------------------- + */ + /* + * Interface: + * enable_timeout(), + * disable_timeout(), disable_all_timeouts() + * get_timeout_indicator(), get_timeout_start() + * and + * handle_sig_alarm() + */ + #include "postgres.h" + + #include + + #include "access/xact.h" + #include "miscadmin.h" + #include "storage/proc.h" + #include "storage/standby.h" + #include "storage/timeout.h" + #include "utils/timestamp.h" + + /* GUC variables */ + int DeadlockTimeout = 1000; + int StatementTimeout = 0; + int LockTimeout = 0; + + /* + * Infrastructure for timeouts + */ + + static void InitDeadLock(TimestampTz start_time, TimestampTz fin_time); + static void DestroyDeadLock(bool keep_indicator); + static bool CheckDeadLock(void); + + static void InitLockTimeout(TimestampTz start_time, TimestampTz fin_time); + static void DestroyLockTimeout(bool keep_indicator); + static bool CheckLockTimeout(void); + + static void InitStatementTimeout(TimestampTz start_time, TimestampTz fin_time); + static void DestroyStatementTimeout(bool keep_indicator); + static bool CheckStatementTimeout(void); + + static void InitStandbyDeadLock(TimestampTz start_time, TimestampTz fin_time); + static void DestroyStandbyDeadLock(bool keep_indicator); + static bool CheckStandbyDeadLock(void); + + static void InitStandbyTimeout(TimestampTz start_time, TimestampTz fin_time); + static void DestroyStandbyTimeout(bool keep_indicator); + static bool CheckStandbyTimeout(void); + + typedef void (*timeout_init)(TimestampTz, TimestampTz); + typedef void (*timeout_destroy)(bool); + typedef bool (*timeout_check)(void); + typedef TimestampTz (*timeout_start)(void); + + typedef struct { + TimeoutName index; + bool resched_next; + + /* volatile because it may be changed from the signal handler */ + volatile bool indicator; + + timeout_init timeout_init; + timeout_destroy timeout_destroy; + timeout_check timeout_check; + timeout_start timeout_start; + + TimestampTz start_time; + TimestampTz fin_time; + } timeout_params; + + /* + * List of possible timeout reasons in the order of enum TimeoutName. + * The priority of timeouts (in case two of them would trigger at the + * same time) is determined by this order: the earlier one in the list + * has higher priority. + */ + static timeout_params base_timeouts[TIMEOUT_MAX] = { + { + DEADLOCK_TIMEOUT, true, false, + InitDeadLock, DestroyDeadLock, + CheckDeadLock, GetCurrentTimestamp, + 0 + }, + + { + LOCK_TIMEOUT, false, false, + InitLockTimeout, DestroyLockTimeout, + CheckLockTimeout, GetCurrentTimestamp, + 0 + }, + + { + STATEMENT_TIMEOUT, false, false, + InitStatementTimeout, DestroyStatementTimeout, + CheckStatementTimeout, GetCurrentStatementStartTimestamp, + 0 + }, + + { + STANDBY_DEADLOCK_TIMEOUT, true, false, + InitStandbyDeadLock, DestroyStandbyDeadLock, + CheckStandbyDeadLock, GetCurrentTimestamp, + 0 + }, + + { + STANDBY_TIMEOUT, false, false, + InitStandbyTimeout, DestroyStandbyTimeout, + CheckStandbyTimeout, GetCurrentTimestamp, + 0 + } + }; + + /* + * List of active timeouts ordered by their fin_time and priority. + */ + static int n_timeouts = 0; + static timeout_params *timeouts[TIMEOUT_MAX]; + + /***************************************************************************** + * Internal helper functions + *****************************************************************************/ + + /* + * Find the index of a given timeout type in the active array + */ + static int + find_active_timeout(TimeoutName tn) + { + int i; + + for (i = 0; i < n_timeouts; i++) + { + if (timeouts[i]->index == tn) + return i; + } + + return -1; + } + + #define is_timeout_active(tn) (find_active_timeout(tn) >= 0) + + /***************************************************************************** + * Init, Destroy and Check functions for different timeouts. + *****************************************************************************/ + + static void + InitTimeout(TimeoutName tn, TimestampTz start_time, TimestampTz fin_time) + { + base_timeouts[tn].indicator = false; + base_timeouts[tn].start_time = start_time; + base_timeouts[tn].fin_time = fin_time; + } + + static void + DestroyTimeout(TimeoutName tn, bool keep_indicator) + { + if (!keep_indicator) + base_timeouts[tn].indicator = false; + base_timeouts[tn].start_time = 0; + base_timeouts[tn].fin_time = 0; + } + + /* + * DeadLock functions + */ + + static void + InitDeadLock(TimestampTz start_time, TimestampTz fin_time) + { + InitTimeout(DEADLOCK_TIMEOUT, start_time, fin_time); + } + + static void + DestroyDeadLock(bool keep_indicator) + { + DestroyTimeout(DEADLOCK_TIMEOUT, keep_indicator); + } + + /* + * CheckDeadLock + * + * We only get to this routine if we got SIGALRM after DeadlockTimeout + * while waiting for a lock to be released by some other process. Look + * to see if there's a deadlock; if not, just return and continue waiting. + * (But signal ProcSleep to log a message, if log_lock_waits is true.) + * If we have a real deadlock, remove ourselves from the lock's wait queue + * and signal an error to ProcSleep. + * + * NB: this is run inside a signal handler, so be very wary about what is done + * here or in called routines. + */ + static bool + CheckDeadLock(void) + { + TimestampTz now; + int i; + + now = GetCurrentTimestamp(); + + /* If our time has not come yet, do nothing. */ + if (now < base_timeouts[DEADLOCK_TIMEOUT].fin_time) + return false; + + /* + * Acquire exclusive lock on the entire shared lock data structures. Must + * grab LWLocks in partition-number order to avoid LWLock deadlock. + * + * Note that the deadlock check interrupt had better not be enabled + * anywhere that this process itself holds lock partition locks, else this + * will wait forever. Also note that LWLockAcquire creates a critical + * section, so that this routine cannot be interrupted by cancel/die + * interrupts. + */ + for (i = 0; i < NUM_LOCK_PARTITIONS; i++) + LWLockAcquire(FirstLockMgrLock + i, LW_EXCLUSIVE); + + /* + * Check to see if we've been awoken by anyone in the interim. + * + * If we have, we can return and resume our transaction -- happy day. + * Before we are awoken the process releasing the lock grants it to us so + * we know that we don't have to wait anymore. + * + * We check by looking to see if we've been unlinked from the wait queue. + * This is quicker than checking our semaphore's state, since no kernel + * call is needed, and it is safe because we hold the lock partition lock. + */ + if (MyProc->links.prev == NULL || + MyProc->links.next == NULL) + goto check_done; + + #ifdef LOCK_DEBUG + if (Debug_deadlocks) + DumpAllLocks(); + #endif + + /* Run the deadlock check, and set deadlock_state for use by ProcSleep */ + deadlock_state = DeadLockCheck(MyProc); + + if (deadlock_state == DS_HARD_DEADLOCK) + { + /* + * Oops. We have a deadlock. + * + * Get this process out of wait state. (Note: we could do this more + * efficiently by relying on lockAwaited, but use this coding to + * preserve the flexibility to kill some other transaction than the + * one detecting the deadlock.) + * + * RemoveFromWaitQueue sets MyProc->waitStatus to STATUS_ERROR, so + * ProcSleep will report an error after we return from the signal + * handler. + */ + Assert(MyProc->waitLock != NULL); + RemoveFromWaitQueue(MyProc, LockTagHashCode(&(MyProc->waitLock->tag))); + + /* + * Unlock my semaphore so that the interrupted ProcSleep() call can + * finish. + */ + PGSemaphoreUnlock(&MyProc->sem); + + /* + * We're done here. Transaction abort caused by the error that + * ProcSleep will raise will cause any other locks we hold to be + * released, thus allowing other processes to wake up; we don't need + * to do that here. NOTE: an exception is that releasing locks we + * hold doesn't consider the possibility of waiters that were blocked + * behind us on the lock we just failed to get, and might now be + * wakable because we're not in front of them anymore. However, + * RemoveFromWaitQueue took care of waking up any such processes. + */ + } + else if (log_lock_waits || deadlock_state == DS_BLOCKED_BY_AUTOVACUUM) + { + /* + * Unlock my semaphore so that the interrupted ProcSleep() call can + * print the log message (we daren't do it here because we are inside + * a signal handler). It will then sleep again until someone releases + * the lock. + * + * If blocked by autovacuum, this wakeup will enable ProcSleep to send + * the canceling signal to the autovacuum worker. + */ + PGSemaphoreUnlock(&MyProc->sem); + } + + /* + * And release locks. We do this in reverse order for two reasons: (1) + * Anyone else who needs more than one of the locks will be trying to lock + * them in increasing order; we don't want to release the other process + * until it can get all the locks it needs. (2) This avoids O(N^2) + * behavior inside LWLockRelease. + */ + check_done: + for (i = NUM_LOCK_PARTITIONS; --i >= 0;) + LWLockRelease(FirstLockMgrLock + i); + + base_timeouts[DEADLOCK_TIMEOUT].indicator = true; + + return true; + } + + /* + * LockTimeout functions + */ + + static void + InitLockTimeout(TimestampTz start_time, TimestampTz fin_time) + { + InitTimeout(LOCK_TIMEOUT, start_time, fin_time); + } + + static void + DestroyLockTimeout(bool keep_indicator) + { + DestroyTimeout(LOCK_TIMEOUT, keep_indicator); + } + + /* + * Check for lock timeout. If the timeout time has come, + * indicate that our timeout was detected. + */ + static bool + CheckLockTimeout(void) + { + TimestampTz now; + + now = GetCurrentTimestamp(); + + if (now >= base_timeouts[LOCK_TIMEOUT].fin_time) + { + base_timeouts[LOCK_TIMEOUT].indicator = true; + return true; + } + + return false; + } + + /* + * StatementTimeout functions + */ + + static void + InitStatementTimeout(TimestampTz start_time, TimestampTz fin_time) + { + InitTimeout(STATEMENT_TIMEOUT, start_time, fin_time); + } + + static void + DestroyStatementTimeout(bool keep_indicator) + { + DestroyTimeout(STATEMENT_TIMEOUT, keep_indicator); + } + + /* + * Check for statement timeout. If the timeout time has come, + * trigger a query-cancel interrupt. + * + * Returns true if the time has come. + */ + static bool + CheckStatementTimeout(void) + { + TimestampTz now; + + now = GetCurrentTimestamp(); + + if (now >= base_timeouts[STATEMENT_TIMEOUT].fin_time) + { + base_timeouts[STATEMENT_TIMEOUT].indicator = true; + #ifdef HAVE_SETSID + /* try to signal whole process group */ + kill(-MyProcPid, SIGINT); + #endif + kill(MyProcPid, SIGINT); + + return true; + } + + return false; + } + + /* + * StandbyDeadLock functions + */ + + static void + InitStandbyDeadLock(TimestampTz start_time, TimestampTz fin_time) + { + InitTimeout(STANDBY_DEADLOCK_TIMEOUT, start_time, fin_time); + } + + static void + DestroyStandbyDeadLock(bool keep_indicator) + { + DestroyTimeout(STANDBY_DEADLOCK_TIMEOUT, keep_indicator); + } + + static bool + CheckStandbyDeadLock(void) + { + TimestampTz now; + + now = GetCurrentTimestamp(); + + if (now >= base_timeouts[STANDBY_DEADLOCK_TIMEOUT].fin_time) + { + base_timeouts[STANDBY_DEADLOCK_TIMEOUT].indicator = true; + SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK); + return true; + } + + return false; + } + + /* + * StandbyTimeout functions + */ + + static void + InitStandbyTimeout(TimestampTz start_time, TimestampTz fin_time) + { + InitTimeout(STANDBY_TIMEOUT, start_time, fin_time); + } + + static void + DestroyStandbyTimeout(bool keep_indicator) + { + DestroyTimeout(STANDBY_TIMEOUT, keep_indicator); + } + + static bool + CheckStandbyTimeout(void) + { + TimestampTz now; + + now = GetCurrentTimestamp(); + + if (now >= base_timeouts[STANDBY_TIMEOUT].fin_time) + { + base_timeouts[STANDBY_TIMEOUT].indicator = true; + SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN); + return true; + } + + return false; + } + + /* + * Insert tn'th timeout into the list of timeouts at the given index + * if the previous timeout allows rescheduling the next one in the list. + */ + static void + insert_timeout(TimeoutName tn, int index) + { + int i; + + if (index > 0 && !timeouts[index-1]->resched_next) + return; + + for (i = n_timeouts - 1; i >= index; i--) + timeouts[i+1] = timeouts[i]; + + timeouts[index] = &base_timeouts[tn]; + n_timeouts++; + } + + /* + * Remove the index'th element from the timeout list. + */ + static void + remove_timeout_index(int index) + { + int i; + + if (index < 0) + return; + + for (i = index + 1; i < n_timeouts; i++) + timeouts[i-1] = timeouts[i]; + + if (n_timeouts > 0 && index < n_timeouts) + n_timeouts--; + } + + /* + * (Re)schedule the next active timeout + */ + static bool + schedule_timeout(TimestampTz now) + { + long secs; + int usecs; + struct itimerval timeval; + + /* There is no active timeout, do nothing. */ + if (n_timeouts == 0) + return true; + + TimestampDifference(now, timeouts[0]->fin_time, + &secs, &usecs); + + /* + * It's possible that the difference is less than a microsecond; + * ensure we don't cancel, rather than set, the interrupt. + */ + if (secs == 0 && usecs == 0) + usecs = 1; + MemSet(&timeval, 0, sizeof(struct itimerval)); + timeval.it_value.tv_sec = secs; + timeval.it_value.tv_usec = usecs; + if (setitimer(ITIMER_REAL, &timeval, NULL)) + return false; + return true; + } + + /***************************************************************************** + * Public API + *****************************************************************************/ + + + /* + * Enable the SIGALRM interrupt to fire after the specified delay + * + * Delay is given in milliseconds. Caller should be sure a SIGALRM + * signal handler is installed before this is called. + * + * This code properly handles nesting of different timeout alarms. + * + * Returns TRUE if okay, FALSE on failure to set the timer. + */ + bool + enable_timeout(TimeoutName tn, int delayms) + { + TimestampTz start_time; + TimestampTz fin_time; + int i; + + Assert(!is_timeout_active(tn)); + + if (delayms <= 0) + return true; + + start_time = base_timeouts[tn].timeout_start(); + fin_time = TimestampTzPlusMilliseconds(start_time, delayms); + + /* Find out the index where to insert the new timeout. */ + for (i = 0; i < n_timeouts; i++) + { + /* + * The new timeout triggers earlier than + * a previously added one: insert here. + */ + if (fin_time < timeouts[i]->fin_time) + break; + /* + * The new timeout triggers at the same time + * as the previously added one but has greater priority. + */ + if (fin_time == timeouts[i]->fin_time && tn < timeouts[i]->index) + break; + } + + /* + * Initialize the timeout parameters + */ + base_timeouts[tn].timeout_init(start_time, fin_time); + + insert_timeout(tn, i); + + if (i > 0) + return true; + + /* If we reach here, okay to set the timer interrupt */ + if (!schedule_timeout(start_time)) + return false; + return true; + } + + /* + * Cancel the SIGALRM timer for the specific timeout. + * If a timeout is canceled, any other active timeout remains in force. + * + * Returns TRUE if okay, FALSE on failure to set the timer. + */ + bool + disable_timeout(TimeoutName tn, bool keep_indicator) + { + int i; + + /* + * Always disable the interrupt if it is active; this avoids being + * interrupted by the signal handler and thereby possibly getting + * confused. + * + * We will re-enable the interrupt if necessary in ->check_timeout(). + */ + if (n_timeouts > 0) + { + struct itimerval timeval; + + MemSet(&timeval, 0, sizeof(struct itimerval)); + if (setitimer(ITIMER_REAL, &timeval, NULL)) + { + n_timeouts = 0; + for (i = 0; i < TIMEOUT_MAX; i++) + base_timeouts[i].timeout_destroy(false); + + return false; + } + } + + /* Find the timeout and remove from the list. */ + i = find_active_timeout(tn); + remove_timeout_index(i); + + /* Do cleanup. */ + base_timeouts[tn].timeout_destroy(keep_indicator); + + /* + * If the first timeout was removed from the list and there is + * at least one active left, reschedule it. + */ + if (i == 0 && n_timeouts > 0) + if (!schedule_timeout(GetCurrentTimestamp())) + return false; + + return true; + } + + /* + * Disable SIGALRM and remove all timeouts from the list and + * reset the timeout indicators. + */ + bool + disable_all_timeouts(bool keep_indicators) + { + struct itimerval timeval; + int i; + bool ret; + + MemSet(&timeval, 0, sizeof(struct itimerval)); + ret = (setitimer(ITIMER_REAL, &timeval, NULL) == 0); + + n_timeouts = 0; + for (i = 0; i < TIMEOUT_MAX; i++) + base_timeouts[i].timeout_destroy(keep_indicators); + + return ret; + } + + /* + * Return the timeout indicator + */ + bool + get_timeout_indicator(TimeoutName tn) + { + return base_timeouts[tn].indicator; + } + + /* + * Return the start of the timer for this timeout + */ + TimestampTz + get_timeout_start(TimeoutName tn) + { + return base_timeouts[tn].start_time; + } + + /* + * Signal handler for SIGALRM + * + * Process deadlock check and/or statement timeout check, as needed. + * To avoid various edge cases, we must be careful to do nothing + * when there is nothing to be done. We also need to be able to + * reschedule the timer interrupt if called before end of statement. + */ + void + handle_sig_alarm(SIGNAL_ARGS) + { + int save_errno = errno; + + /* SIGALRM is cause for waking anything waiting on the process latch */ + if (MyProc) + SetLatch(&MyProc->procLatch); + + if (n_timeouts > 0) + { + bool ret; + bool reschedule = true; + + ret = timeouts[0]->timeout_check(); + + /* Shift timeouts if the timeout was triggered */ + if (ret) + { + reschedule = timeouts[0]->resched_next; + remove_timeout_index(0); + } + + if (reschedule) + schedule_timeout(GetCurrentTimestamp()); + else + disable_all_timeouts(true); + } + + errno = save_errno; + } diff -dcrpN postgresql.orig/src/backend/tcop/postgres.c postgresql/src/backend/tcop/postgres.c *** postgresql.orig/src/backend/tcop/postgres.c 2012-03-28 10:54:25.195368447 +0200 --- postgresql/src/backend/tcop/postgres.c 2012-04-04 15:07:43.152700939 +0200 *************** *** 64,69 **** --- 64,70 ---- #include "storage/proc.h" #include "storage/procsignal.h" #include "storage/sinval.h" + #include "storage/timeout.h" #include "tcop/fastpath.h" #include "tcop/pquery.h" #include "tcop/tcopprot.h" *************** start_xact_command(void) *** 2368,2376 **** /* Set statement timeout running, if any */ /* NB: this mustn't be enabled until we are within an xact */ if (StatementTimeout > 0) ! enable_sig_alarm(StatementTimeout, true); else ! cancel_from_timeout = false; xact_started = true; } --- 2369,2377 ---- /* Set statement timeout running, if any */ /* NB: this mustn't be enabled until we are within an xact */ if (StatementTimeout > 0) ! enable_timeout(STATEMENT_TIMEOUT, StatementTimeout); else ! disable_timeout(STATEMENT_TIMEOUT, false); xact_started = true; } *************** finish_xact_command(void) *** 2382,2388 **** if (xact_started) { /* Cancel any active statement timeout before committing */ ! disable_sig_alarm(true); /* Now commit the command */ ereport(DEBUG3, --- 2383,2389 ---- if (xact_started) { /* Cancel any active statement timeout before committing */ ! disable_all_timeouts(false); /* Now commit the command */ ereport(DEBUG3, *************** ProcessInterrupts(void) *** 2863,2869 **** (errcode(ERRCODE_QUERY_CANCELED), errmsg("canceling authentication due to timeout"))); } ! if (cancel_from_timeout) { ImmediateInterruptOK = false; /* not idle anymore */ DisableNotifyInterrupt(); --- 2864,2870 ---- (errcode(ERRCODE_QUERY_CANCELED), errmsg("canceling authentication due to timeout"))); } ! if (get_timeout_indicator(STATEMENT_TIMEOUT)) { ImmediateInterruptOK = false; /* not idle anymore */ DisableNotifyInterrupt(); *************** PostgresMain(int argc, char *argv[], con *** 3728,3737 **** /* * Forget any pending QueryCancel request, since we're returning to ! * the idle loop anyway, and cancel the statement timer if running. */ QueryCancelPending = false; ! disable_sig_alarm(true); QueryCancelPending = false; /* again in case timeout occurred */ /* --- 3729,3738 ---- /* * Forget any pending QueryCancel request, since we're returning to ! * the idle loop anyway, and cancel the timer if running. */ QueryCancelPending = false; ! disable_all_timeouts(false); QueryCancelPending = false; /* again in case timeout occurred */ /* diff -dcrpN postgresql.orig/src/backend/utils/adt/lockfuncs.c postgresql/src/backend/utils/adt/lockfuncs.c *** postgresql.orig/src/backend/utils/adt/lockfuncs.c 2012-01-02 12:35:11.533185089 +0100 --- postgresql/src/backend/utils/adt/lockfuncs.c 2012-04-04 11:24:59.796965393 +0200 *************** pg_advisory_lock_int8(PG_FUNCTION_ARGS) *** 421,427 **** SET_LOCKTAG_INT64(tag, key); ! (void) LockAcquire(&tag, ExclusiveLock, true, false); PG_RETURN_VOID(); } --- 421,431 ---- SET_LOCKTAG_INT64(tag, key); ! if (LockAcquire(&tag, ExclusiveLock, true, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain exclusive advisory lock on value %llu", ! (long long)key))); PG_RETURN_VOID(); } *************** pg_advisory_xact_lock_int8(PG_FUNCTION_A *** 438,444 **** SET_LOCKTAG_INT64(tag, key); ! (void) LockAcquire(&tag, ExclusiveLock, false, false); PG_RETURN_VOID(); } --- 442,452 ---- SET_LOCKTAG_INT64(tag, key); ! if (LockAcquire(&tag, ExclusiveLock, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain exclusive advisory lock on value %llu", ! (long long)key))); PG_RETURN_VOID(); } *************** pg_advisory_lock_shared_int8(PG_FUNCTION *** 454,460 **** SET_LOCKTAG_INT64(tag, key); ! (void) LockAcquire(&tag, ShareLock, true, false); PG_RETURN_VOID(); } --- 462,472 ---- SET_LOCKTAG_INT64(tag, key); ! if (LockAcquire(&tag, ShareLock, true, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain shared advisory lock on value %llu", ! (long long)key))); PG_RETURN_VOID(); } *************** pg_advisory_xact_lock_shared_int8(PG_FUN *** 471,477 **** SET_LOCKTAG_INT64(tag, key); ! (void) LockAcquire(&tag, ShareLock, false, false); PG_RETURN_VOID(); } --- 483,493 ---- SET_LOCKTAG_INT64(tag, key); ! if (LockAcquire(&tag, ShareLock, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain shared advisory lock on value %llu", ! (long long)key))); PG_RETURN_VOID(); } *************** pg_advisory_lock_int4(PG_FUNCTION_ARGS) *** 604,610 **** SET_LOCKTAG_INT32(tag, key1, key2); ! (void) LockAcquire(&tag, ExclusiveLock, true, false); PG_RETURN_VOID(); } --- 620,630 ---- SET_LOCKTAG_INT32(tag, key1, key2); ! if (LockAcquire(&tag, ExclusiveLock, true, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain exclusive advisory lock on values %u:%u", ! key1, key2))); PG_RETURN_VOID(); } *************** pg_advisory_xact_lock_int4(PG_FUNCTION_A *** 622,628 **** SET_LOCKTAG_INT32(tag, key1, key2); ! (void) LockAcquire(&tag, ExclusiveLock, false, false); PG_RETURN_VOID(); } --- 642,652 ---- SET_LOCKTAG_INT32(tag, key1, key2); ! if (LockAcquire(&tag, ExclusiveLock, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain exclusive advisory lock on values %u:%u", ! key1, key2))); PG_RETURN_VOID(); } *************** pg_advisory_lock_shared_int4(PG_FUNCTION *** 639,645 **** SET_LOCKTAG_INT32(tag, key1, key2); ! (void) LockAcquire(&tag, ShareLock, true, false); PG_RETURN_VOID(); } --- 663,673 ---- SET_LOCKTAG_INT32(tag, key1, key2); ! if (LockAcquire(&tag, ShareLock, true, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain shared advisory lock on values %u:%u", ! key1, key2))); PG_RETURN_VOID(); } *************** pg_advisory_xact_lock_shared_int4(PG_FUN *** 657,663 **** SET_LOCKTAG_INT32(tag, key1, key2); ! (void) LockAcquire(&tag, ShareLock, false, false); PG_RETURN_VOID(); } --- 685,695 ---- SET_LOCKTAG_INT32(tag, key1, key2); ! if (LockAcquire(&tag, ShareLock, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain shared advisory lock on values %u:%u", ! key1, key2))); PG_RETURN_VOID(); } diff -dcrpN postgresql.orig/src/backend/utils/init/postinit.c postgresql/src/backend/utils/init/postinit.c *** postgresql.orig/src/backend/utils/init/postinit.c 2012-01-20 13:59:24.913240794 +0100 --- postgresql/src/backend/utils/init/postinit.c 2012-04-04 15:08:31.671944981 +0200 *************** *** 41,51 **** #include "storage/fd.h" #include "storage/ipc.h" #include "storage/lmgr.h" - #include "storage/proc.h" #include "storage/procarray.h" #include "storage/procsignal.h" #include "storage/sinvaladt.h" #include "storage/smgr.h" #include "tcop/tcopprot.h" #include "utils/acl.h" #include "utils/fmgroids.h" --- 41,51 ---- #include "storage/fd.h" #include "storage/ipc.h" #include "storage/lmgr.h" #include "storage/procarray.h" #include "storage/procsignal.h" #include "storage/sinvaladt.h" #include "storage/smgr.h" + #include "storage/timeout.h" #include "tcop/tcopprot.h" #include "utils/acl.h" #include "utils/fmgroids.h" *************** PerformAuthentication(Port *port) *** 204,210 **** * during authentication. Since we're inside a transaction and might do * database access, we have to use the statement_timeout infrastructure. */ ! if (!enable_sig_alarm(AuthenticationTimeout * 1000, true)) elog(FATAL, "could not set timer for authorization timeout"); /* --- 204,210 ---- * during authentication. Since we're inside a transaction and might do * database access, we have to use the statement_timeout infrastructure. */ ! if (!enable_timeout(STATEMENT_TIMEOUT, AuthenticationTimeout * 1000)) elog(FATAL, "could not set timer for authorization timeout"); /* *************** PerformAuthentication(Port *port) *** 215,221 **** /* * Done with authentication. Disable the timeout, and log if needed. */ ! if (!disable_sig_alarm(true)) elog(FATAL, "could not disable timer for authorization timeout"); if (Log_connections) --- 215,221 ---- /* * Done with authentication. Disable the timeout, and log if needed. */ ! if (!disable_timeout(STATEMENT_TIMEOUT, false)) elog(FATAL, "could not disable timer for authorization timeout"); if (Log_connections) diff -dcrpN postgresql.orig/src/backend/utils/misc/guc.c postgresql/src/backend/utils/misc/guc.c *** postgresql.orig/src/backend/utils/misc/guc.c 2012-03-28 10:54:25.196368452 +0200 --- postgresql/src/backend/utils/misc/guc.c 2012-04-04 15:09:07.395124656 +0200 *************** *** 63,68 **** --- 63,69 ---- #include "storage/standby.h" #include "storage/fd.h" #include "storage/predicate.h" + #include "storage/timeout.h" #include "tcop/tcopprot.h" #include "tsearch/ts_cache.h" #include "utils/builtins.h" *************** static struct config_int ConfigureNamesI *** 1858,1863 **** --- 1859,1875 ---- 0, 0, INT_MAX, NULL, NULL, NULL }, + + { + {"lock_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT, + gettext_noop("Sets the maximum allowed timeout for any lock taken by a statement."), + gettext_noop("A value of 0 turns off the timeout."), + GUC_UNIT_MS + }, + &LockTimeout, + 0, 0, INT_MAX, + NULL, NULL, NULL + }, { {"vacuum_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT, diff -dcrpN postgresql.orig/src/backend/utils/misc/postgresql.conf.sample postgresql/src/backend/utils/misc/postgresql.conf.sample *** postgresql.orig/src/backend/utils/misc/postgresql.conf.sample 2012-03-28 10:54:25.197368458 +0200 --- postgresql/src/backend/utils/misc/postgresql.conf.sample 2012-04-03 15:29:03.725060295 +0200 *************** *** 527,532 **** --- 527,535 ---- #------------------------------------------------------------------------------ #deadlock_timeout = 1s + #lock_timeout = 0 # timeout value for heavy-weight locks + # taken by statements. 0 disables timeout + # unit in milliseconds, default is 0 #max_locks_per_transaction = 64 # min 10 # (change requires restart) # Note: Each lock table slot uses ~270 bytes of shared memory, and there are diff -dcrpN postgresql.orig/src/include/storage/pg_sema.h postgresql/src/include/storage/pg_sema.h *** postgresql.orig/src/include/storage/pg_sema.h 2012-01-02 12:35:11.640178818 +0100 --- postgresql/src/include/storage/pg_sema.h 2012-04-03 15:30:52.716621135 +0200 *************** extern void PGSemaphoreUnlock(PGSemaphor *** 80,83 **** --- 80,86 ---- /* Lock a semaphore only if able to do so without blocking */ extern bool PGSemaphoreTryLock(PGSemaphore sema); + /* Lock a semaphore (decrement count), blocking if count would be < 0 */ + extern void PGSemaphoreTimedLock(PGSemaphore sema, bool interruptOK); + #endif /* PG_SEMA_H */ diff -dcrpN postgresql.orig/src/include/storage/proc.h postgresql/src/include/storage/proc.h *** postgresql.orig/src/include/storage/proc.h 2012-01-31 09:43:43.936392561 +0100 --- postgresql/src/include/storage/proc.h 2012-04-04 14:58:19.578865852 +0200 *************** extern PGPROC *PreparedXactProcs; *** 215,226 **** /* configurable options */ - extern int DeadlockTimeout; - extern int StatementTimeout; extern bool log_lock_waits; ! extern volatile bool cancel_from_timeout; ! /* * Function Prototypes --- 215,224 ---- /* configurable options */ extern bool log_lock_waits; ! /* */ ! extern volatile DeadLockState deadlock_state; /* * Function Prototypes *************** extern void LockWaitCancel(void); *** 249,261 **** extern void ProcWaitForSignal(void); extern void ProcSendSignal(int pid); - extern bool enable_sig_alarm(int delayms, bool is_statement_timeout); - extern bool disable_sig_alarm(bool is_statement_timeout); - extern void handle_sig_alarm(SIGNAL_ARGS); - - extern bool enable_standby_sig_alarm(TimestampTz now, - TimestampTz fin_time, bool deadlock_only); - extern bool disable_standby_sig_alarm(void); - extern void handle_standby_sig_alarm(SIGNAL_ARGS); - #endif /* PROC_H */ --- 247,250 ---- diff -dcrpN postgresql.orig/src/include/storage/timeout.h postgresql/src/include/storage/timeout.h *** postgresql.orig/src/include/storage/timeout.h 1970-01-01 01:00:00.000000000 +0100 --- postgresql/src/include/storage/timeout.h 2012-04-04 15:02:21.255081715 +0200 *************** *** 0 **** --- 1,40 ---- + /*------------------------------------------------------------------------- + * + * timeout.h + * SIGALRM timeout API + * + * + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/storage/timeout.h + * + *------------------------------------------------------------------------- + */ + #ifndef _TIMEOUT_H_ + #define _TIMEOUT_H_ + + #include "datatype/timestamp.h" + + /* configurable options */ + extern int DeadlockTimeout; + extern int StatementTimeout; + extern int LockTimeout; + + typedef enum TimeoutName { + DEADLOCK_TIMEOUT, + LOCK_TIMEOUT, + STATEMENT_TIMEOUT, + STANDBY_DEADLOCK_TIMEOUT, + STANDBY_TIMEOUT, + TIMEOUT_MAX + } TimeoutName; + + extern bool enable_timeout(TimeoutName tn, int delayms); + extern bool disable_timeout(TimeoutName tn, bool keep_indicator); + extern bool disable_all_timeouts(bool keep_indicators); + extern bool get_timeout_indicator(TimeoutName tn); + extern TimestampTz get_timeout_start(TimeoutName tn); + extern void handle_sig_alarm(SIGNAL_ARGS); + + #endif /* _TIMEOUT_H_ */ diff -dcrpN postgresql.orig/src/test/regress/expected/prepared_xacts.out postgresql/src/test/regress/expected/prepared_xacts.out *** postgresql.orig/src/test/regress/expected/prepared_xacts.out 2011-08-16 09:39:01.117194748 +0200 --- postgresql/src/test/regress/expected/prepared_xacts.out 2012-04-04 12:22:18.719786919 +0200 *************** set statement_timeout to 2000; *** 198,203 **** --- 198,210 ---- SELECT * FROM pxtest3; ERROR: canceling statement due to statement timeout reset statement_timeout; + -- pxtest3 should be locked because of the pending DROP + set lock_timeout to 2000; + SELECT * FROM pxtest3; + ERROR: could not obtain lock on relation "pxtest3" + LINE 1: SELECT * FROM pxtest3; + ^ + reset lock_timeout; -- Disconnect, we will continue testing in a different backend \c - -- There should still be two prepared transactions *************** set statement_timeout to 2000; *** 213,218 **** --- 220,232 ---- SELECT * FROM pxtest3; ERROR: canceling statement due to statement timeout reset statement_timeout; + -- pxtest3 should be locked because of the pending DROP + set lock_timeout to 2000; + SELECT * FROM pxtest3; + ERROR: could not obtain lock on relation "pxtest3" + LINE 1: SELECT * FROM pxtest3; + ^ + reset lock_timeout; -- Commit table creation COMMIT PREPARED 'regress-one'; \d pxtest2 diff -dcrpN postgresql.orig/src/test/regress/sql/prepared_xacts.sql postgresql/src/test/regress/sql/prepared_xacts.sql *** postgresql.orig/src/test/regress/sql/prepared_xacts.sql 2011-07-18 15:42:00.117371861 +0200 --- postgresql/src/test/regress/sql/prepared_xacts.sql 2012-04-04 12:20:31.456241386 +0200 *************** set statement_timeout to 2000; *** 126,131 **** --- 126,136 ---- SELECT * FROM pxtest3; reset statement_timeout; + -- pxtest3 should be locked because of the pending DROP + set lock_timeout to 2000; + SELECT * FROM pxtest3; + reset lock_timeout; + -- Disconnect, we will continue testing in a different backend \c - *************** set statement_timeout to 2000; *** 137,142 **** --- 142,152 ---- SELECT * FROM pxtest3; reset statement_timeout; + -- pxtest3 should be locked because of the pending DROP + set lock_timeout to 2000; + SELECT * FROM pxtest3; + reset lock_timeout; + -- Commit table creation COMMIT PREPARED 'regress-one'; \d pxtest2