From: Tom Lane Date: Tue, 25 Oct 2005 15:15:16 +0000 (+0000) Subject: Fix Windows setitimer() emulation to not depend on delivering an APC X-Git-Tag: REL9_0_0~8990 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=25777f6fd312b3f3e32edb921d79e3d8c7b499a6;p=pg-rex%2Fsyncrep.git Fix Windows setitimer() emulation to not depend on delivering an APC to the main thread. This allows removal of WaitForSingleObjectEx() calls from the main thread, thereby allowing us to re-enable Qingqing Zhou's CHECK_FOR_INTERRUPTS performance improvement. Qingqing, Magnus, et al. --- diff --git a/src/backend/port/win32/signal.c b/src/backend/port/win32/signal.c index fa7d8cf6a2..3204c9c20e 100644 --- a/src/backend/port/win32/signal.c +++ b/src/backend/port/win32/signal.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/port/win32/signal.c,v 1.13 2005/10/21 21:43:45 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/port/win32/signal.c,v 1.14 2005/10/25 15:15:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -90,16 +90,6 @@ pgwin32_signal_initialize(void) } /* - * Support routine for CHECK_FOR_INTERRUPTS() macro - */ -void -pgwin32_check_queued_signals(void) -{ - if (WaitForSingleObjectEx(pgwin32_signal_event, 0, TRUE) == WAIT_OBJECT_0) - pgwin32_dispatch_queued_signals(); -} - -/* * Dispatch all signals currently queued and not blocked * Blocked signals are ignored, and will be fired at the time of * the sigsetmask() call. diff --git a/src/backend/port/win32/socket.c b/src/backend/port/win32/socket.c index 808977a237..b4bd121ca9 100644 --- a/src/backend/port/win32/socket.c +++ b/src/backend/port/win32/socket.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/port/win32/socket.c,v 1.9 2005/10/15 02:49:23 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/port/win32/socket.c,v 1.10 2005/10/25 15:15:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -93,7 +93,7 @@ TranslateSocketError(void) static int pgwin32_poll_signals(void) { - if (WaitForSingleObjectEx(pgwin32_signal_event, 0, TRUE) == WAIT_OBJECT_0) + if (UNBLOCKED_SIGNAL_QUEUE()) { pgwin32_dispatch_queued_signals(); errno = EINTR; diff --git a/src/backend/port/win32/timer.c b/src/backend/port/win32/timer.c index c31c7bc114..b6c0b407e6 100644 --- a/src/backend/port/win32/timer.c +++ b/src/backend/port/win32/timer.c @@ -3,10 +3,15 @@ * timer.c * Microsoft Windows Win32 Timer Implementation * + * Limitations of this implementation: + * + * - Does not support interval timer (value->it_interval) + * - Only supports ITIMER_REAL + * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/port/win32/timer.c,v 1.5 2004/12/31 22:00:37 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/port/win32/timer.c,v 1.6 2005/10/25 15:15:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,56 +21,98 @@ #include "libpq/pqsignal.h" -static HANDLE timerHandle = INVALID_HANDLE_VALUE; +/* Communication area for inter-thread communication */ +typedef struct timerCA { + struct itimerval value; + HANDLE event; + CRITICAL_SECTION crit_sec; +} timerCA; + +static timerCA timerCommArea; +static HANDLE timerThreadHandle = INVALID_HANDLE_VALUE; -static VOID CALLBACK -timer_completion(LPVOID arg, DWORD timeLow, DWORD timeHigh) + +/* Timer management thread */ +static DWORD WINAPI +pg_timer_thread(LPVOID param) { - pg_queue_signal(SIGALRM); -} + DWORD waittime; + + Assert(param == NULL); + waittime = INFINITE; + + for (;;) + { + int r; + + r = WaitForSingleObjectEx(timerCommArea.event, waittime, FALSE); + if (r == WAIT_OBJECT_0) + { + /* Event signalled from main thread, change the timer */ + EnterCriticalSection(&timerCommArea.crit_sec); + if (timerCommArea.value.it_value.tv_sec == 0 && + timerCommArea.value.it_value.tv_usec == 0) + waittime = INFINITE; /* Cancel the interrupt */ + else + waittime = timerCommArea.value.it_value.tv_usec / 10 + timerCommArea.value.it_value.tv_sec * 1000; + ResetEvent(timerCommArea.event); + LeaveCriticalSection(&timerCommArea.crit_sec); + } + else if (r == WAIT_TIMEOUT) + { + /* Timeout expired, signal SIGALRM and turn it off */ + pg_queue_signal(SIGALRM); + waittime = INFINITE; + } + else + { + /* Should never happen */ + Assert(false); + } + } + + return 0; +} /* - * Limitations of this implementation: - * - * - Does not support setting ovalue - * - Does not support interval timer (value->it_interval) - * - Only supports ITIMER_REAL + * Win32 setitimer emulation by creating a persistent thread + * to handle the timer setting and notification upon timeout. */ int setitimer(int which, const struct itimerval * value, struct itimerval * ovalue) { - LARGE_INTEGER dueTime; - - Assert(ovalue == NULL); Assert(value != NULL); Assert(value->it_interval.tv_sec == 0 && value->it_interval.tv_usec == 0); Assert(which == ITIMER_REAL); - if (timerHandle == INVALID_HANDLE_VALUE) + if (timerThreadHandle == INVALID_HANDLE_VALUE) { - /* First call in this backend, create new timer object */ - timerHandle = CreateWaitableTimer(NULL, TRUE, NULL); - if (timerHandle == NULL) + /* First call in this backend, create event and the timer thread */ + timerCommArea.event = CreateEvent(NULL, TRUE, FALSE, NULL); + if (timerCommArea.event == NULL) ereport(FATAL, - (errmsg_internal("failed to create waitable timer: %i", (int) GetLastError()))); - } + (errmsg_internal("failed to create timer event: %d", + (int) GetLastError()))); - if (value->it_value.tv_sec == 0 && - value->it_value.tv_usec == 0) - { - /* Turn timer off */ - CancelWaitableTimer(timerHandle); - return 0; - } + MemSet(&timerCommArea.value, 0, sizeof(struct itimerval)); + + InitializeCriticalSection(&timerCommArea.crit_sec); - /* Negative time to SetWaitableTimer means relative time */ - dueTime.QuadPart = -(value->it_value.tv_usec * 10 + value->it_value.tv_sec * 10000000L); + timerThreadHandle = CreateThread(NULL, 0, pg_timer_thread, NULL, 0, NULL); + if (timerThreadHandle == INVALID_HANDLE_VALUE) + ereport(FATAL, + (errmsg_internal("failed to create timer thread: %d", + (int) GetLastError()))); + } - /* Turn timer on, or change timer */ - if (!SetWaitableTimer(timerHandle, &dueTime, 0, timer_completion, NULL, FALSE)) - ereport(FATAL, - (errmsg_internal("failed to set waitable timer: %i", (int) GetLastError()))); + /* Request the timer thread to change settings */ + EnterCriticalSection(&timerCommArea.crit_sec); + if (ovalue) + *ovalue = timerCommArea.value; + timerCommArea.value = *value; + LeaveCriticalSection(&timerCommArea.crit_sec); + SetEvent(timerCommArea.event); return 0; } diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index f82fffed77..fc13891e41 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -13,7 +13,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.182 2005/10/22 17:09:48 tgl Exp $ + * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.183 2005/10/25 15:15:16 tgl Exp $ * * NOTES * some of the information in this file should be moved to other files. @@ -88,7 +88,8 @@ do { \ #define CHECK_FOR_INTERRUPTS() \ do { \ - pgwin32_check_queued_signals(); \ + if (UNBLOCKED_SIGNAL_QUEUE()) \ + pgwin32_dispatch_queued_signals(); \ if (InterruptPending) \ ProcessInterrupts(); \ } while(0) diff --git a/src/include/port/win32.h b/src/include/port/win32.h index 9d1a6da6c6..5bf6ce208f 100644 --- a/src/include/port/win32.h +++ b/src/include/port/win32.h @@ -1,4 +1,4 @@ -/* $PostgreSQL: pgsql/src/include/port/win32.h,v 1.48 2005/10/21 21:43:46 tgl Exp $ */ +/* $PostgreSQL: pgsql/src/include/port/win32.h,v 1.49 2005/10/25 15:15:16 tgl Exp $ */ /* undefine and redefine after #include */ #undef mkdir @@ -224,7 +224,6 @@ extern HANDLE pgwin32_initial_signal_pipe; void pgwin32_signal_initialize(void); HANDLE pgwin32_create_signal_listener(pid_t pid); -void pgwin32_check_queued_signals(void); void pgwin32_dispatch_queued_signals(void); void pg_queue_signal(int signum);