]> granicus.if.org Git - postgresql/blob - src/backend/port/win32/signal.c
280f10da23d32740b4492f7e1e9280b3d08f5443
[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.1 2004/04/12 16:19:18 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 /* signal sending */
156 int
157 pqkill(int pid, int sig)
158 {
159         char            pipename[128];
160         BYTE            sigData = sig;
161         BYTE            sigRet = 0;
162         DWORD           bytes;
163
164         if (sig >= PG_SIGNAL_COUNT || sig <= 0)
165         {
166                 errno = EINVAL;
167                 return -1;
168         }
169         if (pid <= 0)
170         {
171                 /* No support for process groups */
172                 errno = EINVAL;
173                 return -1;
174         }
175         wsprintf(pipename, "\\\\.\\pipe\\pgsignal_%i", pid);
176         if (!CallNamedPipe(pipename, &sigData, 1, &sigRet, 1, &bytes, 1000))
177         {
178                 if (GetLastError() == ERROR_FILE_NOT_FOUND)
179                         errno = ESRCH;
180                 else if (GetLastError() == ERROR_ACCESS_DENIED)
181                         errno = EPERM;
182                 else
183                         errno = EINVAL;
184                 return -1;
185         }
186         if (bytes != 1 || sigRet != sig)
187         {
188                 errno = ESRCH;
189                 return -1;
190         }
191
192         return 0;
193 }
194
195 /*
196  * All functions below execute on the signal handler thread
197  * and must be synchronized as such!
198  * NOTE! The only global variable that can be used is
199  * pg_signal_queue!
200  */
201
202
203 void
204 pg_queue_signal(int signum)
205 {
206         if (signum >= PG_SIGNAL_COUNT || signum < 0)
207                 return;
208
209         EnterCriticalSection(&pg_signal_crit_sec);
210         pg_signal_queue |= sigmask(signum);
211         LeaveCriticalSection(&pg_signal_crit_sec);
212
213         SetEvent(pgwin32_signal_event);
214 }
215
216 /* Signal dispatching thread */
217 static DWORD WINAPI
218 pg_signal_dispatch_thread(LPVOID param)
219 {
220         HANDLE          pipe = (HANDLE) param;
221         BYTE            sigNum;
222         DWORD           bytes;
223
224         if (!ReadFile(pipe, &sigNum, 1, &bytes, NULL))
225         {
226                 /* Client died before sending */
227                 CloseHandle(pipe);
228                 return 0;
229         }
230         if (bytes != 1)
231         {
232                 /* Received <bytes> bytes over signal pipe (should be 1) */
233                 CloseHandle(pipe);
234                 return 0;
235         }
236         WriteFile(pipe, &sigNum, 1, &bytes, NULL);      /* Don't care if it works
237                                                                                                  * or not.. */
238         FlushFileBuffers(pipe);
239         DisconnectNamedPipe(pipe);
240         CloseHandle(pipe);
241
242         pg_queue_signal(sigNum);
243         return 0;
244 }
245
246 /* Signal handling thread */
247 static DWORD WINAPI
248 pg_signal_thread(LPVOID param)
249 {
250         char            pipename[128];
251         HANDLE          pipe = INVALID_HANDLE_VALUE;
252
253         wsprintf(pipename, "\\\\.\\pipe\\pgsignal_%i", GetCurrentProcessId());
254
255         for (;;)
256         {
257                 BOOL            fConnected;
258                 HANDLE          hThread;
259
260                 pipe = CreateNamedPipe(pipename, PIPE_ACCESS_DUPLEX,
261                                    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
262                                                    PIPE_UNLIMITED_INSTANCES, 16, 16, 1000, NULL);
263                 if (pipe == INVALID_HANDLE_VALUE)
264                 {
265                         fprintf(stderr, gettext("Failed to create signal listener pipe: %i. Retrying.\n"), (int) GetLastError());
266                         SleepEx(500, FALSE);
267                         continue;
268                 }
269
270                 fConnected = ConnectNamedPipe(pipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
271                 if (fConnected)
272                 {
273                         hThread = CreateThread(NULL, 0,
274                                           (LPTHREAD_START_ROUTINE) pg_signal_dispatch_thread,
275                                                                    (LPVOID) pipe, 0, NULL);
276                         if (hThread == INVALID_HANDLE_VALUE)
277                                 fprintf(stderr, gettext("Failed to create signal dispatch thread: %i\n"), (int) GetLastError());
278                         else
279                                 CloseHandle(hThread);
280                 }
281                 else
282                         /* Connection failed. Cleanup and try again */
283                         CloseHandle(pipe);
284         }
285         return 0;
286 }
287
288
289 /* Console control handler will execute on a thread created 
290    by the OS at the time of invocation */
291 static BOOL WINAPI pg_console_handler(DWORD dwCtrlType) {
292         if (dwCtrlType == CTRL_C_EVENT ||
293                 dwCtrlType == CTRL_BREAK_EVENT ||
294                 dwCtrlType == CTRL_CLOSE_EVENT ||
295                 dwCtrlType == CTRL_SHUTDOWN_EVENT) {
296                 pg_queue_signal(SIGINT);
297                 return TRUE;
298         }
299         return FALSE;
300 }