Header And Logo

PostgreSQL
| The world's most advanced open source database.

signal.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * signal.c
00004  *    Microsoft Windows Win32 Signal Emulation Functions
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  *
00008  * IDENTIFICATION
00009  *    src/backend/port/win32/signal.c
00010  *
00011  *-------------------------------------------------------------------------
00012  */
00013 
00014 #include "postgres.h"
00015 
00016 #include "libpq/pqsignal.h"
00017 
00018 /*
00019  * These are exported for use by the UNBLOCKED_SIGNAL_QUEUE() macro.
00020  * pg_signal_queue must be volatile since it is changed by the signal
00021  * handling thread and inspected without any lock by the main thread.
00022  * pg_signal_mask is only changed by main thread so shouldn't need it.
00023  */
00024 volatile int pg_signal_queue;
00025 int         pg_signal_mask;
00026 
00027 HANDLE      pgwin32_signal_event;
00028 HANDLE      pgwin32_initial_signal_pipe = INVALID_HANDLE_VALUE;
00029 
00030 /*
00031  * pg_signal_crit_sec is used to protect only pg_signal_queue. That is the only
00032  * variable that can be accessed from the signal sending threads!
00033  */
00034 static CRITICAL_SECTION pg_signal_crit_sec;
00035 
00036 static pqsigfunc pg_signal_array[PG_SIGNAL_COUNT];
00037 static pqsigfunc pg_signal_defaults[PG_SIGNAL_COUNT];
00038 
00039 
00040 /* Signal handling thread function */
00041 static DWORD WINAPI pg_signal_thread(LPVOID param);
00042 static BOOL WINAPI pg_console_handler(DWORD dwCtrlType);
00043 
00044 
00045 /*
00046  * pg_usleep --- delay the specified number of microseconds, but
00047  * stop waiting if a signal arrives.
00048  *
00049  * This replaces the non-signal-aware version provided by src/port/pgsleep.c.
00050  */
00051 void
00052 pg_usleep(long microsec)
00053 {
00054     if (WaitForSingleObject(pgwin32_signal_event,
00055                             (microsec < 500 ? 1 : (microsec + 500) / 1000))
00056         == WAIT_OBJECT_0)
00057     {
00058         pgwin32_dispatch_queued_signals();
00059         errno = EINTR;
00060         return;
00061     }
00062 }
00063 
00064 
00065 /* Initialization */
00066 void
00067 pgwin32_signal_initialize(void)
00068 {
00069     int         i;
00070     HANDLE      signal_thread_handle;
00071 
00072     InitializeCriticalSection(&pg_signal_crit_sec);
00073 
00074     for (i = 0; i < PG_SIGNAL_COUNT; i++)
00075     {
00076         pg_signal_array[i] = SIG_DFL;
00077         pg_signal_defaults[i] = SIG_IGN;
00078     }
00079     pg_signal_mask = 0;
00080     pg_signal_queue = 0;
00081 
00082     /* Create the global event handle used to flag signals */
00083     pgwin32_signal_event = CreateEvent(NULL, TRUE, FALSE, NULL);
00084     if (pgwin32_signal_event == NULL)
00085         ereport(FATAL,
00086                 (errmsg_internal("could not create signal event: error code %lu", GetLastError())));
00087 
00088     /* Create thread for handling signals */
00089     signal_thread_handle = CreateThread(NULL, 0, pg_signal_thread, NULL, 0, NULL);
00090     if (signal_thread_handle == NULL)
00091         ereport(FATAL,
00092                 (errmsg_internal("could not create signal handler thread")));
00093 
00094     /* Create console control handle to pick up Ctrl-C etc */
00095     if (!SetConsoleCtrlHandler(pg_console_handler, TRUE))
00096         ereport(FATAL,
00097                 (errmsg_internal("could not set console control handler")));
00098 }
00099 
00100 /*
00101  * Dispatch all signals currently queued and not blocked
00102  * Blocked signals are ignored, and will be fired at the time of
00103  * the sigsetmask() call.
00104  */
00105 void
00106 pgwin32_dispatch_queued_signals(void)
00107 {
00108     int         i;
00109 
00110     EnterCriticalSection(&pg_signal_crit_sec);
00111     while (UNBLOCKED_SIGNAL_QUEUE())
00112     {
00113         /* One or more unblocked signals queued for execution */
00114         int         exec_mask = UNBLOCKED_SIGNAL_QUEUE();
00115 
00116         for (i = 0; i < PG_SIGNAL_COUNT; i++)
00117         {
00118             if (exec_mask & sigmask(i))
00119             {
00120                 /* Execute this signal */
00121                 pqsigfunc   sig = pg_signal_array[i];
00122 
00123                 if (sig == SIG_DFL)
00124                     sig = pg_signal_defaults[i];
00125                 pg_signal_queue &= ~sigmask(i);
00126                 if (sig != SIG_ERR && sig != SIG_IGN && sig != SIG_DFL)
00127                 {
00128                     LeaveCriticalSection(&pg_signal_crit_sec);
00129                     sig(i);
00130                     EnterCriticalSection(&pg_signal_crit_sec);
00131                     break;      /* Restart outer loop, in case signal mask or
00132                                  * queue has been modified inside signal
00133                                  * handler */
00134                 }
00135             }
00136         }
00137     }
00138     ResetEvent(pgwin32_signal_event);
00139     LeaveCriticalSection(&pg_signal_crit_sec);
00140 }
00141 
00142 /* signal masking. Only called on main thread, no sync required */
00143 int
00144 pqsigsetmask(int mask)
00145 {
00146     int         prevmask;
00147 
00148     prevmask = pg_signal_mask;
00149     pg_signal_mask = mask;
00150 
00151     /*
00152      * Dispatch any signals queued up right away, in case we have unblocked
00153      * one or more signals previously queued
00154      */
00155     pgwin32_dispatch_queued_signals();
00156 
00157     return prevmask;
00158 }
00159 
00160 
00161 /*
00162  * Unix-like signal handler installation
00163  *
00164  * Only called on main thread, no sync required
00165  */
00166 pqsigfunc
00167 pqsignal(int signum, pqsigfunc handler)
00168 {
00169     pqsigfunc   prevfunc;
00170 
00171     if (signum >= PG_SIGNAL_COUNT || signum < 0)
00172         return SIG_ERR;
00173     prevfunc = pg_signal_array[signum];
00174     pg_signal_array[signum] = handler;
00175     return prevfunc;
00176 }
00177 
00178 /* Create the signal listener pipe for specified PID */
00179 HANDLE
00180 pgwin32_create_signal_listener(pid_t pid)
00181 {
00182     char        pipename[128];
00183     HANDLE      pipe;
00184 
00185     snprintf(pipename, sizeof(pipename), "\\\\.\\pipe\\pgsignal_%u", (int) pid);
00186 
00187     pipe = CreateNamedPipe(pipename, PIPE_ACCESS_DUPLEX,
00188                        PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
00189                            PIPE_UNLIMITED_INSTANCES, 16, 16, 1000, NULL);
00190 
00191     if (pipe == INVALID_HANDLE_VALUE)
00192         ereport(ERROR,
00193                 (errmsg("could not create signal listener pipe for PID %d: error code %lu",
00194                         (int) pid, GetLastError())));
00195 
00196     return pipe;
00197 }
00198 
00199 
00200 /*
00201  * All functions below execute on the signal handler thread
00202  * and must be synchronized as such!
00203  * NOTE! The only global variable that can be used is
00204  * pg_signal_queue!
00205  */
00206 
00207 
00208 void
00209 pg_queue_signal(int signum)
00210 {
00211     if (signum >= PG_SIGNAL_COUNT || signum <= 0)
00212         return;
00213 
00214     EnterCriticalSection(&pg_signal_crit_sec);
00215     pg_signal_queue |= sigmask(signum);
00216     LeaveCriticalSection(&pg_signal_crit_sec);
00217 
00218     SetEvent(pgwin32_signal_event);
00219 }
00220 
00221 /* Signal dispatching thread */
00222 static DWORD WINAPI
00223 pg_signal_dispatch_thread(LPVOID param)
00224 {
00225     HANDLE      pipe = (HANDLE) param;
00226     BYTE        sigNum;
00227     DWORD       bytes;
00228 
00229     if (!ReadFile(pipe, &sigNum, 1, &bytes, NULL))
00230     {
00231         /* Client died before sending */
00232         CloseHandle(pipe);
00233         return 0;
00234     }
00235     if (bytes != 1)
00236     {
00237         /* Received <bytes> bytes over signal pipe (should be 1) */
00238         CloseHandle(pipe);
00239         return 0;
00240     }
00241     WriteFile(pipe, &sigNum, 1, &bytes, NULL);  /* Don't care if it works or
00242                                                  * not.. */
00243     FlushFileBuffers(pipe);
00244     DisconnectNamedPipe(pipe);
00245     CloseHandle(pipe);
00246 
00247     pg_queue_signal(sigNum);
00248     return 0;
00249 }
00250 
00251 /* Signal handling thread */
00252 static DWORD WINAPI
00253 pg_signal_thread(LPVOID param)
00254 {
00255     char        pipename[128];
00256     HANDLE      pipe = pgwin32_initial_signal_pipe;
00257 
00258     snprintf(pipename, sizeof(pipename), "\\\\.\\pipe\\pgsignal_%lu", GetCurrentProcessId());
00259 
00260     for (;;)
00261     {
00262         BOOL        fConnected;
00263         HANDLE      hThread;
00264 
00265         if (pipe == INVALID_HANDLE_VALUE)
00266         {
00267             pipe = CreateNamedPipe(pipename, PIPE_ACCESS_DUPLEX,
00268                        PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
00269                                PIPE_UNLIMITED_INSTANCES, 16, 16, 1000, NULL);
00270 
00271             if (pipe == INVALID_HANDLE_VALUE)
00272             {
00273                 write_stderr("could not create signal listener pipe: error code %lu; retrying\n", GetLastError());
00274                 SleepEx(500, FALSE);
00275                 continue;
00276             }
00277         }
00278 
00279         fConnected = ConnectNamedPipe(pipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
00280         if (fConnected)
00281         {
00282             HANDLE      newpipe;
00283 
00284             /*
00285              * We have a connected pipe. Pass this off to a separate thread
00286              * that will do the actual processing of the pipe.
00287              *
00288              * We must also create a new instance of the pipe *before* we
00289              * start running the new thread. If we don't, there is a race
00290              * condition whereby the dispatch thread might run CloseHandle()
00291              * before we have created a new instance, thereby causing a small
00292              * window of time where we will miss incoming requests.
00293              */
00294             newpipe = CreateNamedPipe(pipename, PIPE_ACCESS_DUPLEX,
00295                        PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
00296                                PIPE_UNLIMITED_INSTANCES, 16, 16, 1000, NULL);
00297             if (newpipe == INVALID_HANDLE_VALUE)
00298             {
00299                 /*
00300                  * This really should never fail. Just retry in case it does,
00301                  * even though we have a small race window in that case. There
00302                  * is nothing else we can do other than abort the whole
00303                  * process which will be even worse.
00304                  */
00305                 write_stderr("could not create signal listener pipe: error code %lu; retrying\n", GetLastError());
00306 
00307                 /*
00308                  * Keep going so we at least dispatch this signal. Hopefully,
00309                  * the call will succeed when retried in the loop soon after.
00310                  */
00311             }
00312             hThread = CreateThread(NULL, 0,
00313                           (LPTHREAD_START_ROUTINE) pg_signal_dispatch_thread,
00314                                    (LPVOID) pipe, 0, NULL);
00315             if (hThread == INVALID_HANDLE_VALUE)
00316                 write_stderr("could not create signal dispatch thread: error code %lu\n",
00317                              GetLastError());
00318             else
00319                 CloseHandle(hThread);
00320 
00321             /*
00322              * Background thread is running with our instance of the pipe. So
00323              * replace our reference with the newly created one and loop back
00324              * up for another run.
00325              */
00326             pipe = newpipe;
00327         }
00328         else
00329         {
00330             /*
00331              * Connection failed. Cleanup and try again.
00332              *
00333              * This should never happen. If it does, we have a small race
00334              * condition until we loop up and re-create the pipe.
00335              */
00336             CloseHandle(pipe);
00337             pipe = INVALID_HANDLE_VALUE;
00338         }
00339     }
00340     return 0;
00341 }
00342 
00343 
00344 /* Console control handler will execute on a thread created
00345    by the OS at the time of invocation */
00346 static BOOL WINAPI
00347 pg_console_handler(DWORD dwCtrlType)
00348 {
00349     if (dwCtrlType == CTRL_C_EVENT ||
00350         dwCtrlType == CTRL_BREAK_EVENT ||
00351         dwCtrlType == CTRL_CLOSE_EVENT ||
00352         dwCtrlType == CTRL_SHUTDOWN_EVENT)
00353     {
00354         pg_queue_signal(SIGINT);
00355         return TRUE;
00356     }
00357     return FALSE;
00358 }