]> granicus.if.org Git - postgresql/blob - src/backend/port/win32/signal.c
Move pgkill out into /port so pg_ctl can use it on Win32.
[postgresql] / src / backend / port / win32 / signal.c
1 /*-------------------------------------------------------------------------
2  *
3  * signal.c
4  *        Microsoft Windows Win32 Signal Emulation Functions
5  *
6  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  *        $PostgreSQL: pgsql/src/backend/port/win32/signal.c,v 1.2 2004/05/27 13:08:50 momjian Exp $
10  *
11  *-------------------------------------------------------------------------
12  */
13
14 #include "postgres.h"
15
16 #include <libpq/pqsignal.h>
17
18
19 /* pg_signal_crit_sec is used to protect only pg_signal_queue. That is the only
20  * variable that can be accessed from the signal sending threads! */
21 static CRITICAL_SECTION pg_signal_crit_sec;
22 static int      pg_signal_queue;
23
24 #define PG_SIGNAL_COUNT 32
25 static pqsigfunc pg_signal_array[PG_SIGNAL_COUNT];
26 static pqsigfunc pg_signal_defaults[PG_SIGNAL_COUNT];
27 static int      pg_signal_mask;
28
29 DLLIMPORT HANDLE pgwin32_signal_event;
30
31
32 /* Signal handling thread function */
33 static DWORD WINAPI pg_signal_thread(LPVOID param);
34 static BOOL WINAPI pg_console_handler(DWORD dwCtrlType);
35
36 /* Sleep function that can be interrupted by signals */
37 void pgwin32_backend_usleep(long microsec) {
38         if (WaitForSingleObject(pgwin32_signal_event, (microsec < 500 ? 1 : (microsec + 500) / 1000)) == WAIT_OBJECT_0) {
39                 pgwin32_dispatch_queued_signals();
40                 errno = EINTR;
41                 return;
42         }
43 }
44
45
46 /* Initialization */
47 void
48 pgwin32_signal_initialize(void)
49 {
50         int                     i;
51         HANDLE          signal_thread_handle;
52
53         InitializeCriticalSection(&pg_signal_crit_sec);
54
55         for (i = 0; i < PG_SIGNAL_COUNT; i++)
56         {
57                 pg_signal_array[i] = SIG_DFL;
58                 pg_signal_defaults[i] = SIG_IGN;
59         }
60         pg_signal_mask = 0;
61         pg_signal_queue = 0;
62
63         /* Create the global event handle used to flag signals */
64         pgwin32_signal_event = CreateEvent(NULL, TRUE, FALSE, NULL);
65         if (pgwin32_signal_event == NULL) 
66                 ereport(FATAL,
67                                 (errmsg_internal("Failed to create signal event: %i!",(int)GetLastError())));
68
69         /* Create thread for handling signals */
70         signal_thread_handle = CreateThread(NULL, 0, pg_signal_thread, NULL, 0, NULL);
71         if (signal_thread_handle == NULL)
72                 ereport(FATAL,
73                                 (errmsg_internal("Failed to create signal handler thread!")));
74
75         /* Create console control handle to pick up Ctrl-C etc */
76         if (!SetConsoleCtrlHandler(pg_console_handler, TRUE)) 
77                 ereport(FATAL,
78                                 (errmsg_internal("Failed to set console control handler!")));
79 }
80
81
82 /* Dispatch all signals currently queued and not blocked
83  * Blocked signals are ignored, and will be fired at the time of
84  * the sigsetmask() call. */
85 void
86 pgwin32_dispatch_queued_signals(void)
87 {
88         int                     i;
89
90         EnterCriticalSection(&pg_signal_crit_sec);
91         while (pg_signal_queue & ~pg_signal_mask)
92         {
93                 /* One or more unblocked signals queued for execution */
94
95                 int                     exec_mask = pg_signal_queue & ~pg_signal_mask;
96
97                 for (i = 0; i < PG_SIGNAL_COUNT; i++)
98                 {
99                         if (exec_mask & sigmask(i))
100                         {
101                                 /* Execute this signal */
102                                 pqsigfunc       sig = pg_signal_array[i];
103
104                                 if (sig == SIG_DFL)
105                                         sig = pg_signal_defaults[i];
106                                 pg_signal_queue &= ~sigmask(i);
107                                 if (sig != SIG_ERR && sig != SIG_IGN && sig != SIG_DFL)
108                                 {
109                                         LeaveCriticalSection(&pg_signal_crit_sec);
110                                         sig(i);
111                                         EnterCriticalSection(&pg_signal_crit_sec);
112                                         break;          /* Restart outer loop, in case signal mask
113                                                                  * or queue has been modified inside
114                                                                  * signal handler */
115                                 }
116                         }
117                 }
118         }
119         ResetEvent(pgwin32_signal_event);
120         LeaveCriticalSection(&pg_signal_crit_sec);
121 }
122
123 /* signal masking. Only called on main thread, no sync required */
124 int
125 pqsigsetmask(int mask)
126 {
127         int                     prevmask;
128
129         prevmask = pg_signal_mask;
130         pg_signal_mask = mask;
131
132         /*
133          * Dispatch any signals queued up right away, in case we have
134          * unblocked one or more signals previously queued
135          */
136         pgwin32_dispatch_queued_signals();
137
138         return prevmask;
139 }
140
141
142 /* signal manipulation. Only called on main thread, no sync required */
143 pqsigfunc
144 pqsignal(int signum, pqsigfunc handler)
145 {
146         pqsigfunc       prevfunc;
147
148         if (signum >= PG_SIGNAL_COUNT || signum < 0)
149                 return SIG_ERR;
150         prevfunc = pg_signal_array[signum];
151         pg_signal_array[signum] = handler;
152         return prevfunc;
153 }
154
155 /*
156  * All functions below execute on the signal handler thread
157  * and must be synchronized as such!
158  * NOTE! The only global variable that can be used is
159  * pg_signal_queue!
160  */
161
162
163 void
164 pg_queue_signal(int signum)
165 {
166         if (signum >= PG_SIGNAL_COUNT || signum < 0)
167                 return;
168
169         EnterCriticalSection(&pg_signal_crit_sec);
170         pg_signal_queue |= sigmask(signum);
171         LeaveCriticalSection(&pg_signal_crit_sec);
172
173         SetEvent(pgwin32_signal_event);
174 }
175
176 /* Signal dispatching thread */
177 static DWORD WINAPI
178 pg_signal_dispatch_thread(LPVOID param)
179 {
180         HANDLE          pipe = (HANDLE) param;
181         BYTE            sigNum;
182         DWORD           bytes;
183
184         if (!ReadFile(pipe, &sigNum, 1, &bytes, NULL))
185         {
186                 /* Client died before sending */
187                 CloseHandle(pipe);
188                 return 0;
189         }
190         if (bytes != 1)
191         {
192                 /* Received <bytes> bytes over signal pipe (should be 1) */
193                 CloseHandle(pipe);
194                 return 0;
195         }
196         WriteFile(pipe, &sigNum, 1, &bytes, NULL);      /* Don't care if it works
197                                                                                                  * or not.. */
198         FlushFileBuffers(pipe);
199         DisconnectNamedPipe(pipe);
200         CloseHandle(pipe);
201
202         pg_queue_signal(sigNum);
203         return 0;
204 }
205
206 /* Signal handling thread */
207 static DWORD WINAPI
208 pg_signal_thread(LPVOID param)
209 {
210         char            pipename[128];
211         HANDLE          pipe = INVALID_HANDLE_VALUE;
212
213         wsprintf(pipename, "\\\\.\\pipe\\pgsignal_%i", GetCurrentProcessId());
214
215         for (;;)
216         {
217                 BOOL            fConnected;
218                 HANDLE          hThread;
219
220                 pipe = CreateNamedPipe(pipename, PIPE_ACCESS_DUPLEX,
221                                    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
222                                                    PIPE_UNLIMITED_INSTANCES, 16, 16, 1000, NULL);
223                 if (pipe == INVALID_HANDLE_VALUE)
224                 {
225                         fprintf(stderr, gettext("Failed to create signal listener pipe: %i. Retrying.\n"), (int) GetLastError());
226                         SleepEx(500, FALSE);
227                         continue;
228                 }
229
230                 fConnected = ConnectNamedPipe(pipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
231                 if (fConnected)
232                 {
233                         hThread = CreateThread(NULL, 0,
234                                           (LPTHREAD_START_ROUTINE) pg_signal_dispatch_thread,
235                                                                    (LPVOID) pipe, 0, NULL);
236                         if (hThread == INVALID_HANDLE_VALUE)
237                                 fprintf(stderr, gettext("Failed to create signal dispatch thread: %i\n"), (int) GetLastError());
238                         else
239                                 CloseHandle(hThread);
240                 }
241                 else
242                         /* Connection failed. Cleanup and try again */
243                         CloseHandle(pipe);
244         }
245         return 0;
246 }
247
248
249 /* Console control handler will execute on a thread created 
250    by the OS at the time of invocation */
251 static BOOL WINAPI pg_console_handler(DWORD dwCtrlType) {
252         if (dwCtrlType == CTRL_C_EVENT ||
253                 dwCtrlType == CTRL_BREAK_EVENT ||
254                 dwCtrlType == CTRL_CLOSE_EVENT ||
255                 dwCtrlType == CTRL_SHUTDOWN_EVENT) {
256                 pg_queue_signal(SIGINT);
257                 return TRUE;
258         }
259         return FALSE;
260 }