1-timeout-framework-v19.patch

text/x-patch

Filename: 1-timeout-framework-v19.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 v19
File+
src/backend/port/posix_sema.c 29 0
src/backend/port/sysv_sema.c 34 0
src/backend/port/win32_sema.c 63 0
src/backend/storage/lmgr/proc.c 14 3
src/backend/utils/misc/timeout.c 103 0
src/include/storage/pg_sema.h 8 0
src/include/utils/timeout.h 19 0
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-07-22 21:34:50.475375677 +0200
@@ -24,6 +24,7 @@
 #include "miscadmin.h"
 #include "storage/ipc.h"
 #include "storage/pg_sema.h"
+#include "utils/timeout.h"
 
 
 #ifdef USE_NAMED_POSIX_SEMAPHORES
@@ -313,3 +314,31 @@ PGSemaphoreTryLock(PGSemaphore sema)
 
 	return true;
 }
+
+/*
+ * PGSemaphoreTimedLock
+ *
+ * Lock a semaphore (decrement count), blocking if count would be < 0
+ * Return if lock_timeout expired
+ */
+bool
+PGSemaphoreTimedLock(PGSemaphore sema, bool interruptOK)
+{
+	int			errStatus;
+	bool			timeout;
+
+	do
+	{
+		ImmediateInterruptOK = interruptOK;
+		CHECK_FOR_INTERRUPTS();
+		errStatus = sem_wait(PG_SEM_REF(sema));
+		ImmediateInterruptOK = false;
+		timeout = ExtraTimeoutCondition();
+	} while (errStatus < 0 && errno == EINTR && !timeout);
+
+	if (timeout)
+		return false;
+	if (errStatus < 0)
+		elog(FATAL, "sem_wait failed: %m");
+	return true;
+}
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-07-22 21:34:50.476375683 +0200
@@ -27,6 +27,7 @@
 #include "miscadmin.h"
 #include "storage/ipc.h"
 #include "storage/pg_sema.h"
+#include "utils/timeout.h"
 
 
 #ifndef HAVE_UNION_SEMUN
@@ -492,3 +493,36 @@ PGSemaphoreTryLock(PGSemaphore sema)
 
 	return true;
 }
+
+/*
+ * PGSemaphoreTimedLock
+ *
+ * Lock a semaphore (decrement count), blocking if count would be < 0
+ * Return if lock_timeout expired
+ */
+bool
+PGSemaphoreTimedLock(PGSemaphore sema, bool interruptOK)
+{
+	int			errStatus;
+	bool			timeout;
+	struct sembuf sops;
+
+	sops.sem_op = -1;			/* decrement */
+	sops.sem_flg = 0;
+	sops.sem_num = sema->semNum;
+
+	do
+	{
+		ImmediateInterruptOK = interruptOK;
+		CHECK_FOR_INTERRUPTS();
+		errStatus = semop(sema->semId, &sops, 1);
+		ImmediateInterruptOK = false;
+		timeout = ExtraTimeoutCondition();
+	} while (errStatus < 0 && errno == EINTR && !timeout);
+
+	if (timeout)
+		return false;
+	if (errStatus < 0)
+		elog(FATAL, "semop(id=%d) failed: %m", sema->semId);
+	return true;
+}
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-07-22 21:34:50.476375683 +0200
@@ -16,6 +16,7 @@
 #include "miscadmin.h"
 #include "storage/ipc.h"
 #include "storage/pg_sema.h"
+#include "utils/timeout.h"
 
 static HANDLE *mySemSet;		/* IDs of sema sets acquired so far */
 static int	numSems;			/* number of sema sets acquired so far */
@@ -209,3 +210,65 @@ PGSemaphoreTryLock(PGSemaphore sema)
 	/* 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.
+ */
+bool
+PGSemaphoreTimedLock(PGSemaphore sema, bool interruptOK)
+{
+	DWORD		ret;
+	HANDLE		wh[2];
+	bool			timeout;
+
+	/*
+	 * Note: pgwin32_signal_event should be first to ensure that it will be
+	 * reported when multiple events are set.  We want to guarantee that
+	 * pending signals are serviced.
+	 */
+	wh[0] = pgwin32_signal_event;
+	wh[1] = *sema;
+
+	/*
+	 * 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();
+
+		ret = WaitForMultipleObjectsEx(2, wh, FALSE, INFINITE, TRUE);
+
+		if (ret == WAIT_OBJECT_0)
+		{
+			/* Signal event is set - we have a signal to deliver */
+			pgwin32_dispatch_queued_signals();
+			errno = EINTR;
+		}
+		else if (ret == WAIT_OBJECT_0 + 1)
+		{
+			/* We got it! */
+			return;
+		}
+		else
+			/* Otherwise we are in trouble */
+			errno = EIDRM;
+
+		ImmediateInterruptOK = false;
+		timeout = ExtraTimeoutCondition();
+	} while (errno == EINTR && !timeout);
+
+	if (timeout)
+		return false;
+	if (errno != 0)
+		ereport(FATAL,
+				(errmsg("could not lock semaphore: error code %d", (int) GetLastError())));
+	return true;
+}
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-22 16:48:48.520857718 +0200
+++ postgresql.1/src/backend/storage/lmgr/proc.c	2012-07-22 21:34:50.478375695 +0200
@@ -640,8 +640,12 @@ LockErrorCleanup(void)
 	if (lockAwaited == NULL)
 		return;
 
-	/* Turn off the deadlock timer, if it's still running (see ProcSleep) */
+	/*
+	 * Turn off the deadlock timer and any other extra timers,
+	 * if they are still running (see ProcSleep)
+	 */
 	disable_timeout(DEADLOCK_TIMEOUT, false);
+	DisableExtraTimeouts();
 
 	/* Unlink myself from the wait queue, if on it (might not be anymore!) */
 	partitionLock = LockHashPartitionLock(lockAwaited->hashcode);
@@ -1039,6 +1043,11 @@ ProcSleep(LOCALLOCK *locallock, LockMeth
 	enable_timeout_after(DEADLOCK_TIMEOUT, DeadlockTimeout);
 
 	/*
+	 * Set the timer for other registered timeouts
+	 */
+	EnableExtraTimeouts();
+
+	/*
 	 * 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
@@ -1057,7 +1066,8 @@ ProcSleep(LOCALLOCK *locallock, LockMeth
 	 */
 	do
 	{
-		PGSemaphoreLock(&MyProc->sem, true);
+		if (!PGSemaphoreTimedLock(&MyProc->sem, true))
+			ReportExtraTimeoutError();
 
 		/*
 		 * waitStatus could change from STATUS_WAITING to something else
@@ -1186,9 +1196,10 @@ 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);
+	DisableExtraTimeouts();
 
 	/*
 	 * Re-acquire the lock table's partition lock.  We have to do this to hold
diff -durpN postgresql/src/backend/utils/misc/timeout.c postgresql.1/src/backend/utils/misc/timeout.c
--- postgresql/src/backend/utils/misc/timeout.c	2012-07-22 16:48:48.535857816 +0200
+++ postgresql.1/src/backend/utils/misc/timeout.c	2012-07-22 21:34:50.479375701 +0200
@@ -44,6 +44,11 @@ static timeout_params all_timeouts[MAX_T
 static bool all_timeouts_initialized = false;
 
 /*
+ * Linked set of timeout functions
+ */
+static ExtraTimeoutFunctions *CurrentExtraTimeoutFunctions = NULL;
+
+/*
  * List of active timeouts ordered by their fin_time and priority.
  * This list is subject to change by the interrupt handler, so it's volatile.
  */
@@ -284,6 +289,104 @@ RegisterTimeout(TimeoutId id, timeout_ha
 }
 
 /*
+ * Register a set of functions for an extra timeout
+ */
+void
+RegisterExtraTimeout(ExtraTimeoutFunctions *functions)
+{
+	Assert(functions != NULL);
+	Assert(functions->enable != NULL);
+	Assert(functions->disable != NULL);
+	Assert(functions->condition != NULL);
+	Assert(functions->reporter != NULL);
+
+	functions->next = CurrentExtraTimeoutFunctions;
+	CurrentExtraTimeoutFunctions = functions;
+}
+
+/*
+ * Enable registered extra timeouts
+ */
+void
+EnableExtraTimeouts(void)
+{
+	ExtraTimeoutFunctions *ptr = CurrentExtraTimeoutFunctions;
+
+	while (ptr)
+	{
+		ptr->enable();
+		ptr = ptr->next;
+	}
+}
+
+/*
+ * Disable registered extra timeouts
+ */
+void
+DisableExtraTimeouts(void)
+{
+	ExtraTimeoutFunctions *ptr = CurrentExtraTimeoutFunctions;
+
+	while (ptr)
+	{
+		ptr->disable();
+		ptr = ptr->next;
+	}
+}
+
+/*
+ * Call the timeout indicator functions in the order of registration
+ */
+static bool
+DoExtraTimeoutCondition(ExtraTimeoutFunctions *ptr)
+{
+	if (ptr)
+	{
+		if (ptr->next)
+			if (DoExtraTimeoutCondition(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
+ExtraTimeoutCondition(void)
+{
+	return DoExtraTimeoutCondition(CurrentExtraTimeoutFunctions);
+}
+
+/*
+ * Call the timeout error reporter functions in the order of registration
+ */
+static void
+DoReportExtraTimeoutError(ExtraTimeoutFunctions *ptr)
+{
+	if (ptr)
+	{
+		if (ptr->next)
+			DoReportExtraTimeoutError(ptr->next);
+
+		/* This may not return because of calling ereport() */
+		ptr->reporter();
+	}
+}
+
+/*
+ * Call the timeout error reporter if set
+ */
+void
+ReportExtraTimeoutError(void)
+{
+	DoReportExtraTimeoutError(CurrentExtraTimeoutFunctions);
+}
+
+/*
  * Enable the specified timeout reason
  */
 static void
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-07-22 21:34:50.479375701 +0200
@@ -20,6 +20,8 @@
 #ifndef PG_SEMA_H
 #define PG_SEMA_H
 
+#include "utils/timeout.h"
+
 /*
  * PGSemaphoreData and pointer type PGSemaphore are the data structure
  * representing an individual semaphore.  The contents of PGSemaphoreData
@@ -80,4 +82,10 @@ extern void PGSemaphoreUnlock(PGSemaphor
 /* Lock a semaphore only if able to do so without blocking */
 extern bool PGSemaphoreTryLock(PGSemaphore sema);
 
+/*
+ * Lock a semaphore (decrement count), blocking for at most
+ * "lock_timeout" milliseconds if count would be < 0
+ */
+extern bool PGSemaphoreTimedLock(PGSemaphore sema, bool interruptOK);
+
 #endif   /* PG_SEMA_H */
diff -durpN postgresql/src/include/utils/timeout.h postgresql.1/src/include/utils/timeout.h
--- postgresql/src/include/utils/timeout.h	2012-07-22 16:48:48.549857907 +0200
+++ postgresql.1/src/include/utils/timeout.h	2012-07-22 21:34:50.480375707 +0200
@@ -37,6 +37,18 @@ typedef enum TimeoutId
 /* callback function signature */
 typedef void (*timeout_handler) (void);
 
+/* callback function signatures for extra timeouts */
+typedef void (*TimeoutCallback)(void);
+typedef bool (*TimeoutCondition)(void);
+
+typedef struct ExtraTimeoutFunctions {
+	struct ExtraTimeoutFunctions *next;
+	TimeoutCallback		enable;
+	TimeoutCallback		disable;
+	TimeoutCondition	condition;
+	TimeoutCallback		reporter;
+} ExtraTimeoutFunctions;
+
 /* timeout setup */
 extern void InitializeTimeouts(void);
 extern TimeoutId RegisterTimeout(TimeoutId id, timeout_handler handler);
@@ -51,4 +63,11 @@ extern void disable_all_timeouts(bool ke
 extern bool get_timeout_indicator(TimeoutId id);
 extern TimestampTz get_timeout_start_time(TimeoutId id);
 
+/* register and handle extra timeouts */
+extern void RegisterExtraTimeout(ExtraTimeoutFunctions *functions);
+extern void EnableExtraTimeouts(void);
+extern void DisableExtraTimeouts(void);
+extern bool ExtraTimeoutCondition(void);
+extern void ReportExtraTimeoutError(void);
+
 #endif   /* TIMEOUT_H */