1-timeout-framework-v19.patch
text/x-patch
Filename: 1-timeout-framework-v19.patch
Type: text/x-patch
Part: 0
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 */