99 *
1010 *
1111 * IDENTIFICATION
12- * $PostgreSQL: pgsql/src/backend/libpq/pqsignal.c,v 1.28 2003/11/29 19:51:49 pgsql Exp $
12+ * $PostgreSQL: pgsql/src/backend/libpq/pqsignal.c,v 1.29 2004/01/27 00:45:26 momjian Exp $
1313 *
1414 * NOTES
1515 *This shouldn't be in libpq, but the monitor and some other
3838 *is to do signal-handler reinstallation, which doesn't work well
3939 *at all.
4040 * ------------------------------------------------------------------------*/
41+ #ifdef WIN32
42+ #define WIN32_LEAN_AND_MEAN
43+ #define _WIN32_WINNT 0x0400
44+ #endif
45+
4146#include "postgres.h"
4247
48+ #ifndef WIN32
4349#include <signal.h>
50+ #else
51+ #include <windows.h>
52+ #endif
4453
4554#include "libpq/pqsignal.h"
4655
@@ -127,6 +136,7 @@ pqinitmask(void)
127136}
128137
129138
139+ #ifndef WIN32
130140/*
131141 * Set up a signal handler
132142 */
@@ -149,3 +159,234 @@ pqsignal(int signo, pqsigfunc func)
149159return oact .sa_handler ;
150160#endif /* !HAVE_POSIX_SIGNALS */
151161}
162+
163+
164+ #else
165+
166+
167+ /* Win32 specific signals code */
168+
169+ /* pg_signal_crit_sec is used to protect only pg_signal_queue. That is the only
170+ * variable that can be accessed from the signal sending threads! */
171+ static CRITICAL_SECTION pg_signal_crit_sec ;
172+ static int pg_signal_queue ;
173+
174+ #define PG_SIGNAL_COUNT 32
175+ static pqsigfunc pg_signal_array [PG_SIGNAL_COUNT ];
176+ static pqsigfunc pg_signal_defaults [PG_SIGNAL_COUNT ];
177+ static int pg_signal_mask ;
178+
179+ HANDLE pgwin32_main_thread_handle ;
180+
181+ /* Signal handling thread function */
182+ static DWORD WINAPI pg_signal_thread (LPVOID param );
183+
184+ /* Initialization */
185+ void pgwin32_signal_initialize (void ) {
186+ int i ;
187+ HANDLE signal_thread_handle ;
188+ InitializeCriticalSection (& pg_signal_crit_sec );
189+
190+ for (i = 0 ;i < PG_SIGNAL_COUNT ;i ++ ) {
191+ pg_signal_array [i ]= SIG_DFL ;
192+ pg_signal_defaults [i ]= SIG_IGN ;
193+ }
194+ pg_signal_mask = 0 ;
195+ pg_signal_queue = 0 ;
196+
197+ /* Get handle to main thread so we can post calls to it later */
198+ if (!DuplicateHandle (GetCurrentProcess (),GetCurrentThread (),
199+ GetCurrentProcess (),& pgwin32_main_thread_handle ,
200+ 0 ,FALSE,DUPLICATE_SAME_ACCESS )) {
201+ fprintf (stderr ,gettext ("Failed to get main thread handle!\n" ));
202+ exit (1 );
203+ }
204+
205+ /* Create thread for handling signals */
206+ signal_thread_handle = CreateThread (NULL ,0 ,pg_signal_thread ,NULL ,0 ,NULL );
207+ if (signal_thread_handle == NULL ) {
208+ fprintf (stderr ,gettext ("Failed to create signal handler thread!\n" ));
209+ exit (1 );
210+ }
211+ }
212+
213+
214+ /* Dispatch all signals currently queued and not blocked
215+ * Blocked signals are ignored, and will be fired at the time of
216+ * the sigsetmask() call. */
217+ static void dispatch_queued_signals (void ) {
218+ int i ;
219+
220+ EnterCriticalSection (& pg_signal_crit_sec );
221+ while (pg_signal_queue & ~pg_signal_mask ) {
222+ /* One or more unblocked signals queued for execution */
223+
224+ int exec_mask = pg_signal_queue & ~pg_signal_mask ;
225+
226+ for (i = 0 ;i < PG_SIGNAL_COUNT ;i ++ ) {
227+ if (exec_mask & sigmask (i )) {
228+ /* Execute this signal */
229+ pqsigfunc sig = pg_signal_array [i ];
230+ if (sig == SIG_DFL )
231+ sig = pg_signal_defaults [i ];
232+ pg_signal_queue &= ~sigmask (i );
233+ if (sig != SIG_ERR && sig != SIG_IGN && sig != SIG_DFL ) {
234+ LeaveCriticalSection (& pg_signal_crit_sec );
235+ sig (i );
236+ EnterCriticalSection (& pg_signal_crit_sec );
237+ break ;/* Restart outer loop, in case signal mask or queue
238+ has been modified inside signal handler */
239+ }
240+ }
241+ }
242+ }
243+ LeaveCriticalSection (& pg_signal_crit_sec );
244+ }
245+
246+ /* signal masking. Only called on main thread, no sync required */
247+ int pqsigsetmask (int mask ) {
248+ int prevmask ;
249+ prevmask = pg_signal_mask ;
250+ pg_signal_mask = mask ;
251+
252+ /* Dispatch any signals queued up right away, in case we have
253+ unblocked one or more signals previously queued */
254+ dispatch_queued_signals ();
255+
256+ return prevmask ;
257+ }
258+
259+
260+ /* signal manipulation. Only called on main thread, no sync required */
261+ pqsigfunc pqsignal (int signum ,pqsigfunc handler ) {
262+ pqsigfunc prevfunc ;
263+ if (signum >=PG_SIGNAL_COUNT || signum < 0 )
264+ return SIG_ERR ;
265+ prevfunc = pg_signal_array [signum ];
266+ pg_signal_array [signum ]= handler ;
267+ return prevfunc ;
268+ }
269+
270+ /* signal sending */
271+ int pqkill (int pid ,int sig ) {
272+ char pipename [128 ];
273+ BYTE sigData = sig ;
274+ BYTE sigRet = 0 ;
275+ DWORD bytes ;
276+
277+ if (sig >=PG_SIGNAL_COUNT || sig <=0 ) {
278+ errno = EINVAL ;
279+ return -1 ;
280+ }
281+ if (pid <=0 ) {
282+ /* No support for process groups */
283+ errno = EINVAL ;
284+ return -1 ;
285+ }
286+ wsprintf (pipename ,"\\\\.\\pipe\\pgsignal_%i" ,pid );
287+ if (!CallNamedPipe (pipename ,& sigData ,1 ,& sigRet ,1 ,& bytes ,1000 )) {
288+ if (GetLastError ()== ERROR_FILE_NOT_FOUND )
289+ errno = ESRCH ;
290+ else if (GetLastError ()== ERROR_ACCESS_DENIED )
291+ errno = EPERM ;
292+ else
293+ errno = EINVAL ;
294+ return -1 ;
295+ }
296+ if (bytes != 1 || sigRet != sig ) {
297+ errno = ESRCH ;
298+ return -1 ;
299+ }
300+
301+ return 0 ;
302+ }
303+
304+ /* APC callback scheduled on main thread when signals are fired */
305+ static void CALLBACK pg_signal_apc (ULONG_PTR param ) {
306+ dispatch_queued_signals ();
307+ }
308+
309+ /*
310+ * All functions below execute on the signal handler thread
311+ * and must be synchronized as such!
312+ * NOTE! The only global variable that can be used is
313+ * pg_signal_queue!
314+ */
315+
316+
317+ static void pg_queue_signal (int signum ) {
318+ if (signum >=PG_SIGNAL_COUNT || signum < 0 )
319+ return ;
320+
321+ EnterCriticalSection (& pg_signal_crit_sec );
322+ pg_signal_queue |=sigmask (signum );
323+ LeaveCriticalSection (& pg_signal_crit_sec );
324+
325+ QueueUserAPC (pg_signal_apc ,pgwin32_main_thread_handle ,(ULONG_PTR )NULL );
326+ }
327+
328+ /* Signal dispatching thread */
329+ static DWORD WINAPI pg_signal_dispatch_thread (LPVOID param ) {
330+ HANDLE pipe = (HANDLE )param ;
331+ BYTE sigNum ;
332+ DWORD bytes ;
333+
334+ if (!ReadFile (pipe ,& sigNum ,1 ,& bytes ,NULL )) {
335+ /* Client died before sending */
336+ CloseHandle (pipe );
337+ return 0 ;
338+ }
339+ if (bytes != 1 ) {
340+ /* Received <bytes> bytes over signal pipe (should be 1) */
341+ CloseHandle (pipe );
342+ return 0 ;
343+ }
344+ WriteFile (pipe ,& sigNum ,1 ,& bytes ,NULL );/* Don't care if it works or not.. */
345+ FlushFileBuffers (pipe );
346+ DisconnectNamedPipe (pipe );
347+ CloseHandle (pipe );
348+
349+ pg_queue_signal (sigNum );
350+ return 0 ;
351+ }
352+
353+ /* Signal handling thread */
354+ static DWORD WINAPI pg_signal_thread (LPVOID param ) {
355+ char pipename [128 ];
356+ HANDLE pipe = INVALID_HANDLE_VALUE ;
357+
358+ wsprintf (pipename ,"\\\\.\\pipe\\pgsignal_%i" ,GetCurrentProcessId ());
359+
360+ for (;;) {
361+ BOOL fConnected ;
362+ HANDLE hThread ;
363+
364+ pipe = CreateNamedPipe (pipename ,PIPE_ACCESS_DUPLEX ,
365+ PIPE_TYPE_MESSAGE |PIPE_READMODE_MESSAGE |PIPE_WAIT ,
366+ PIPE_UNLIMITED_INSTANCES ,16 ,16 ,1000 ,NULL );
367+ if (pipe == INVALID_HANDLE_VALUE ) {
368+ fprintf (stderr ,gettext ("Failed to create signal listener pipe: %i. Retrying.\n" ),(int )GetLastError ());
369+ SleepEx (500 ,TRUE);
370+ continue ;
371+ }
372+
373+ fConnected = ConnectNamedPipe (pipe ,NULL ) ? TRUE : (GetLastError ()== ERROR_PIPE_CONNECTED );
374+ if (fConnected ) {
375+ hThread = CreateThread (NULL ,0 ,
376+ (LPTHREAD_START_ROUTINE )pg_signal_dispatch_thread ,
377+ (LPVOID )pipe ,0 ,NULL );
378+ if (hThread == INVALID_HANDLE_VALUE ) {
379+ fprintf (stderr ,gettext ("Failed to create signal dispatch thread: %i\n" ),(int )GetLastError ());
380+ }
381+ else
382+ CloseHandle (hThread );
383+ }
384+ else
385+ /* Connection failed. Cleanup and try again */
386+ CloseHandle (pipe );
387+ }
388+ return 0 ;
389+ }
390+
391+
392+ #endif