|
3 | 3 | * timer.c
|
4 | 4 | * Microsoft Windows Win32 Timer Implementation
|
5 | 5 | *
|
| 6 | + * Limitations of this implementation: |
| 7 | + * |
| 8 | + * - Does not support interval timer (value->it_interval) |
| 9 | + * - Only supports ITIMER_REAL |
| 10 | + * |
6 | 11 | * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
7 | 12 | *
|
8 | 13 | * IDENTIFICATION
|
9 |
| - * $PostgreSQL: pgsql/src/backend/port/win32/timer.c,v 1.5 2004/12/31 22:00:37 pgsql Exp $ |
| 14 | + * $PostgreSQL: pgsql/src/backend/port/win32/timer.c,v 1.6 2005/10/25 15:15:16 tgl Exp $ |
10 | 15 | *
|
11 | 16 | *-------------------------------------------------------------------------
|
12 | 17 | */
|
|
16 | 21 | #include "libpq/pqsignal.h"
|
17 | 22 |
|
18 | 23 |
|
19 |
| -static HANDLE timerHandle = INVALID_HANDLE_VALUE; |
| 24 | +/* Communication area for inter-thread communication */ |
| 25 | +typedef struct timerCA { |
| 26 | + struct itimerval value; |
| 27 | + HANDLE event; |
| 28 | + CRITICAL_SECTION crit_sec; |
| 29 | +} timerCA; |
| 30 | + |
| 31 | +static timerCA timerCommArea; |
| 32 | +static HANDLE timerThreadHandle = INVALID_HANDLE_VALUE; |
20 | 33 |
|
21 |
| -static VOID CALLBACK |
22 |
| -timer_completion(LPVOID arg, DWORD timeLow, DWORD timeHigh) |
| 34 | + |
| 35 | +/* Timer management thread */ |
| 36 | +static DWORD WINAPI |
| 37 | +pg_timer_thread(LPVOID param) |
23 | 38 | {
|
24 |
| - pg_queue_signal(SIGALRM); |
25 |
| -} |
| 39 | + DWORD waittime; |
| 40 | + |
| 41 | + Assert(param == NULL); |
26 | 42 |
|
| 43 | + waittime = INFINITE; |
| 44 | + |
| 45 | + for (;;) |
| 46 | + { |
| 47 | + int r; |
| 48 | + |
| 49 | + r = WaitForSingleObjectEx(timerCommArea.event, waittime, FALSE); |
| 50 | + if (r == WAIT_OBJECT_0) |
| 51 | + { |
| 52 | + /* Event signalled from main thread, change the timer */ |
| 53 | + EnterCriticalSection(&timerCommArea.crit_sec); |
| 54 | + if (timerCommArea.value.it_value.tv_sec == 0 && |
| 55 | + timerCommArea.value.it_value.tv_usec == 0) |
| 56 | + waittime = INFINITE; /* Cancel the interrupt */ |
| 57 | + else |
| 58 | + waittime = timerCommArea.value.it_value.tv_usec / 10 + timerCommArea.value.it_value.tv_sec * 1000; |
| 59 | + ResetEvent(timerCommArea.event); |
| 60 | + LeaveCriticalSection(&timerCommArea.crit_sec); |
| 61 | + } |
| 62 | + else if (r == WAIT_TIMEOUT) |
| 63 | + { |
| 64 | + /* Timeout expired, signal SIGALRM and turn it off */ |
| 65 | + pg_queue_signal(SIGALRM); |
| 66 | + waittime = INFINITE; |
| 67 | + } |
| 68 | + else |
| 69 | + { |
| 70 | + /* Should never happen */ |
| 71 | + Assert(false); |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + return 0; |
| 76 | +} |
27 | 77 |
|
28 | 78 | /*
|
29 |
| - * Limitations of this implementation: |
30 |
| - * |
31 |
| - * - Does not support setting ovalue |
32 |
| - * - Does not support interval timer (value->it_interval) |
33 |
| - * - Only supports ITIMER_REAL |
| 79 | + * Win32 setitimer emulation by creating a persistent thread |
| 80 | + * to handle the timer setting and notification upon timeout. |
34 | 81 | */
|
35 | 82 | int
|
36 | 83 | setitimer(int which, const struct itimerval * value, struct itimerval * ovalue)
|
37 | 84 | {
|
38 |
| - LARGE_INTEGER dueTime; |
39 |
| - |
40 |
| - Assert(ovalue == NULL); |
41 | 85 | Assert(value != NULL);
|
42 | 86 | Assert(value->it_interval.tv_sec == 0 && value->it_interval.tv_usec == 0);
|
43 | 87 | Assert(which == ITIMER_REAL);
|
44 | 88 |
|
45 |
| - if (timerHandle == INVALID_HANDLE_VALUE) |
| 89 | + if (timerThreadHandle == INVALID_HANDLE_VALUE) |
46 | 90 | {
|
47 |
| - /* First call in this backend, create new timer object */ |
48 |
| - timerHandle = CreateWaitableTimer(NULL, TRUE, NULL); |
49 |
| - if (timerHandle == NULL) |
| 91 | + /* First call in this backend, create event and the timer thread */ |
| 92 | + timerCommArea.event = CreateEvent(NULL, TRUE, FALSE, NULL); |
| 93 | + if (timerCommArea.event == NULL) |
50 | 94 | ereport(FATAL,
|
51 |
| - (errmsg_internal("failed to create waitable timer: %i", (int) GetLastError()))); |
52 |
| - } |
| 95 | + (errmsg_internal("failed to create timer event: %d", |
| 96 | + (int) GetLastError()))); |
53 | 97 |
|
54 |
| - if (value->it_value.tv_sec == 0 && |
55 |
| - value->it_value.tv_usec == 0) |
56 |
| - { |
57 |
| - /* Turn timer off */ |
58 |
| - CancelWaitableTimer(timerHandle); |
59 |
| - return 0; |
60 |
| - } |
| 98 | + MemSet(&timerCommArea.value, 0, sizeof(struct itimerval)); |
| 99 | + |
| 100 | + InitializeCriticalSection(&timerCommArea.crit_sec); |
61 | 101 |
|
62 |
| - /* Negative time to SetWaitableTimer means relative time */ |
63 |
| - dueTime.QuadPart = -(value->it_value.tv_usec * 10 + value->it_value.tv_sec * 10000000L); |
| 102 | + timerThreadHandle = CreateThread(NULL, 0, pg_timer_thread, NULL, 0, NULL); |
| 103 | + if (timerThreadHandle == INVALID_HANDLE_VALUE) |
| 104 | + ereport(FATAL, |
| 105 | + (errmsg_internal("failed to create timer thread: %d", |
| 106 | + (int) GetLastError()))); |
| 107 | + } |
64 | 108 |
|
65 |
| - /* Turn timer on, or change timer */ |
66 |
| - if (!SetWaitableTimer(timerHandle, &dueTime, 0, timer_completion, NULL, FALSE)) |
67 |
| - ereport(FATAL, |
68 |
| - (errmsg_internal("failed to set waitable timer: %i", (int) GetLastError()))); |
| 109 | + /* Request the timer thread to change settings */ |
| 110 | + EnterCriticalSection(&timerCommArea.crit_sec); |
| 111 | + if (ovalue) |
| 112 | + *ovalue = timerCommArea.value; |
| 113 | + timerCommArea.value = *value; |
| 114 | + LeaveCriticalSection(&timerCommArea.crit_sec); |
| 115 | + SetEvent(timerCommArea.event); |
69 | 116 |
|
70 | 117 | return 0;
|
71 | 118 | }
|
0 commit comments