1-timeout-framework-v21.patch

text/x-patch

Filename: 1-timeout-framework-v21.patch
Type: text/x-patch
Part: 0
Message: Re: [PATCH] lock_timeout and common SIGALRM framework

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
Series: patch v21
File+
src/backend/port/ipc_test.c 4 4
src/backend/port/posix_sema.c 8 2
src/backend/port/sysv_sema.c 8 2
src/backend/port/win32_sema.c 8 2
src/backend/storage/lmgr/deadlock.c 4 0
src/backend/storage/lmgr/lmgrtimeout.c 121 0
src/backend/storage/lmgr/lock.c 3 2
src/backend/storage/lmgr/lwlock.c 2 2
src/backend/storage/lmgr/Makefile 1 1
src/backend/storage/lmgr/proc.c 25 14
src/backend/utils/init/postinit.c 28 0
src/include/storage/lmgrtimeout.h 36 0
src/include/storage/pg_sema.h 4 1
src/include/storage/proc.h 1 0
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);