~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to src/backend/port/win32/signal.c

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

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