1/*-------------------------------------------------------------------------
4 * Microsoft Windows Win32 Signal Emulation Functions
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
9 * src/backend/port/win32/signal.c
11 *-------------------------------------------------------------------------
19 * These are exported for use by the UNBLOCKED_SIGNAL_QUEUE() macro.
20 * pg_signal_queue must be volatile since it is changed by the signal
21 * handling thread and inspected without any lock by the main thread.
22 * pg_signal_mask is only changed by main thread so shouldn't need it.
31 * pg_signal_crit_sec is used to protect only pg_signal_queue. That is the only
32 * variable that can be accessed from the signal sending threads!
36/* Note that array elements 0 are unused since they correspond to signal 0 */
41/* Signal handling thread functions */
47 * pg_usleep --- delay the specified number of microseconds, but
48 * stop waiting if a signal arrives.
50 * This replaces the non-signal-aware version provided by src/port/pgsleep.c.
58 * If we're reached by pgwin32_open_handle() early in startup before
59 * the signal event is set up, just fall back to a regular
60 * non-interruptible sleep.
62 SleepEx((microsec < 500 ? 1 : (microsec + 500) / 1000), FALSE);
67 (microsec < 500 ? 1 : (microsec + 500) / 1000))
82 HANDLE signal_thread_handle;
96 /* Create the global event handle used to flag signals */
100 (
errmsg_internal(
"could not create signal event: error code %lu", GetLastError())));
102 /* Create thread for handling signals */
103 signal_thread_handle = CreateThread(NULL, 0,
pg_signal_thread, NULL, 0, NULL);
104 if (signal_thread_handle == NULL)
108 /* Create console control handle to pick up Ctrl-C etc */
115 * Dispatch all signals currently queued and not blocked
116 * Blocked signals are ignored, and will be fired at the time of
117 * the pqsigprocmask() call.
128 /* One or more unblocked signals queued for execution */
135 /* Execute this signal */
142 if (
sig != SIG_ERR &&
sig != SIG_IGN &&
sig != SIG_DFL)
149 block_mask = act->sa_mask;
150 if ((act->sa_flags & SA_NODEFER) == 0)
153 sigprocmask(SIG_BLOCK, &block_mask, &save_mask);
155 sigprocmask(SIG_SETMASK, &save_mask, NULL);
158 break;
/* Restart outer loop, in case signal mask or
159 * queue has been modified inside signal
169/* signal masking. Only called on main thread, no sync required */
196 * Dispatch any signals queued up right away, in case we have unblocked
197 * one or more signals previously queued
205 * Unix-like signal handler installation
207 * Only called on main thread, no sync required
211 struct sigaction *oldact)
225/* Create the signal listener pipe for specified PID */
232 snprintf(pipename,
sizeof(pipename),
"\\\\.\\pipe\\pgsignal_%u", (
int) pid);
234 pipe = CreateNamedPipe(pipename, PIPE_ACCESS_DUPLEX,
235 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
236 PIPE_UNLIMITED_INSTANCES, 16, 16, 1000, NULL);
238 if (pipe == INVALID_HANDLE_VALUE)
240 (
errmsg(
"could not create signal listener pipe for PID %d: error code %lu",
241 (
int) pid, GetLastError())));
248 * All functions below execute on the signal handler thread
249 * and must be synchronized as such!
250 * NOTE! The only global variable that can be used is
256 * Queue a signal for the main thread, by setting the flag bit and event.
263 return;
/* ignore any bad signal number */
272/* Signal handling thread */
279 /* Set up pipe name, in case we have to re-create the pipe. */
280 snprintf(pipename,
sizeof(pipename),
"\\\\.\\pipe\\pgsignal_%lu", GetCurrentProcessId());
286 /* Create a new pipe instance if we don't have one. */
287 if (pipe == INVALID_HANDLE_VALUE)
289 pipe = CreateNamedPipe(pipename, PIPE_ACCESS_DUPLEX,
290 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
291 PIPE_UNLIMITED_INSTANCES, 16, 16, 1000, NULL);
293 if (pipe == INVALID_HANDLE_VALUE)
295 write_stderr(
"could not create signal listener pipe: error code %lu; retrying\n", GetLastError());
302 * Wait for a client to connect. If something connects before we
303 * reach here, we'll get back a "failure" with ERROR_PIPE_CONNECTED,
304 * which is actually a success (way to go, Microsoft).
306 fConnected = ConnectNamedPipe(pipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
310 * We have a connection from a would-be signal sender. Process it.
315 if (ReadFile(pipe, &sigNum, 1, &bytes, NULL) &&
319 * Queue the signal before responding to the client. In this
320 * way, it's guaranteed that once kill() has returned in the
321 * signal sender, the next CHECK_FOR_INTERRUPTS() in the
322 * signal recipient will see the signal. (This is a stronger
323 * guarantee than POSIX makes; maybe we don't need it? But
324 * without it, we've seen timing bugs on Windows that do not
325 * manifest on any known Unix.)
330 * Write something back to the client, allowing its
331 * CallNamedPipe() call to terminate.
333 WriteFile(pipe, &sigNum, 1, &bytes, NULL);
/* Don't care if it
337 * We must wait for the client to read the data before we can
338 * disconnect, else the data will be lost. (If the WriteFile
339 * call failed, there'll be nothing in the buffer, so this
342 FlushFileBuffers(pipe);
347 * If we fail to read a byte from the client, assume it's the
348 * client's problem and do nothing. Perhaps it'd be better to
349 * force a pipe close and reopen?
353 /* Disconnect from client so that we can re-use the pipe. */
354 DisconnectNamedPipe(pipe);
359 * Connection failed. Cleanup and try again.
361 * This should never happen. If it does, there's a window where
362 * we'll miss signals until we manage to re-create the pipe.
363 * However, just trying to use the same pipe again is probably not
364 * going to work, so we have little choice.
367 pipe = INVALID_HANDLE_VALUE;
374/* Console control handler will execute on a thread created
375 by the OS at the time of invocation */
379 if (dwCtrlType == CTRL_C_EVENT ||
380 dwCtrlType == CTRL_BREAK_EVENT ||
381 dwCtrlType == CTRL_CLOSE_EVENT ||
382 dwCtrlType == CTRL_SHUTDOWN_EVENT)
#define write_stderr(str)
int errmsg_internal(const char *fmt,...)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
Assert(PointerIsAligned(start, uint64))
void(* pqsigfunc)(SIGNAL_ARGS)
HANDLE pgwin32_create_signal_listener(pid_t pid)
static DWORD WINAPI pg_signal_thread(LPVOID param)
int pqsigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
static struct sigaction pg_signal_array[PG_SIGNAL_COUNT]
void pg_queue_signal(int signum)
void pg_usleep(long microsec)
volatile int pg_signal_queue
HANDLE pgwin32_initial_signal_pipe
static CRITICAL_SECTION pg_signal_crit_sec
static BOOL WINAPI pg_console_handler(DWORD dwCtrlType)
static pqsigfunc pg_signal_defaults[PG_SIGNAL_COUNT]
void pgwin32_dispatch_queued_signals(void)
HANDLE pgwin32_signal_event
int pqsigprocmask(int how, const sigset_t *set, sigset_t *oset)
void pgwin32_signal_initialize(void)
#define UNBLOCKED_SIGNAL_QUEUE()