LCOV - code coverage report
Current view: top level - src/fe_utils - cancel.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 16 30 53.3 %
Date: 2020-06-01 09:07:10 Functions: 3 4 75.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*------------------------------------------------------------------------
       2             :  *
       3             :  * Query cancellation support for frontend code
       4             :  *
       5             :  * Assorted utility functions to control query cancellation with signal
       6             :  * handler for SIGINT.
       7             :  *
       8             :  *
       9             :  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
      10             :  * Portions Copyright (c) 1994, Regents of the University of California
      11             :  *
      12             :  * src/fe-utils/cancel.c
      13             :  *
      14             :  *------------------------------------------------------------------------
      15             :  */
      16             : 
      17             : #include "postgres_fe.h"
      18             : 
      19             : #include <unistd.h>
      20             : 
      21             : #include "fe_utils/cancel.h"
      22             : #include "fe_utils/connect.h"
      23             : #include "fe_utils/string_utils.h"
      24             : 
      25             : 
      26             : /*
      27             :  * Write a simple string to stderr --- must be safe in a signal handler.
      28             :  * We ignore the write() result since there's not much we could do about it.
      29             :  * Certain compilers make that harder than it ought to be.
      30             :  */
      31             : #define write_stderr(str) \
      32             :     do { \
      33             :         const char *str_ = (str); \
      34             :         int     rc_; \
      35             :         rc_ = write(fileno(stderr), str_, strlen(str_)); \
      36             :         (void) rc_; \
      37             :     } while (0)
      38             : 
      39             : /*
      40             :  * Contains all the information needed to cancel a query issued from
      41             :  * a database connection to the backend.
      42             :  */
      43             : static PGcancel *volatile cancelConn = NULL;
      44             : 
      45             : /*
      46             :  * CancelRequested tracks if a cancellation request has completed after
      47             :  * a signal interruption.  Note that if cancelConn is not set, in short
      48             :  * if SetCancelConn() was never called or if ResetCancelConn() freed
      49             :  * the cancellation object, then CancelRequested is switched to true after
      50             :  * all cancellation attempts.
      51             :  */
      52             : volatile sig_atomic_t CancelRequested = false;
      53             : 
      54             : #ifdef WIN32
      55             : static CRITICAL_SECTION cancelConnLock;
      56             : #endif
      57             : 
      58             : /*
      59             :  * Additional callback for cancellations.
      60             :  */
      61             : static void (*cancel_callback) (void) = NULL;
      62             : 
      63             : 
      64             : /*
      65             :  * SetCancelConn
      66             :  *
      67             :  * Set cancelConn to point to the current database connection.
      68             :  */
      69             : void
      70      184458 : SetCancelConn(PGconn *conn)
      71             : {
      72             :     PGcancel   *oldCancelConn;
      73             : 
      74             : #ifdef WIN32
      75             :     EnterCriticalSection(&cancelConnLock);
      76             : #endif
      77             : 
      78             :     /* Free the old one if we have one */
      79      184458 :     oldCancelConn = cancelConn;
      80             : 
      81             :     /* be sure handle_sigint doesn't use pointer while freeing */
      82      184458 :     cancelConn = NULL;
      83             : 
      84      184458 :     if (oldCancelConn != NULL)
      85           0 :         PQfreeCancel(oldCancelConn);
      86             : 
      87      184458 :     cancelConn = PQgetCancel(conn);
      88             : 
      89             : #ifdef WIN32
      90             :     LeaveCriticalSection(&cancelConnLock);
      91             : #endif
      92      184458 : }
      93             : 
      94             : /*
      95             :  * ResetCancelConn
      96             :  *
      97             :  * Free the current cancel connection, if any, and set to NULL.
      98             :  */
      99             : void
     100      184458 : ResetCancelConn(void)
     101             : {
     102             :     PGcancel   *oldCancelConn;
     103             : 
     104             : #ifdef WIN32
     105             :     EnterCriticalSection(&cancelConnLock);
     106             : #endif
     107             : 
     108      184458 :     oldCancelConn = cancelConn;
     109             : 
     110             :     /* be sure handle_sigint doesn't use pointer while freeing */
     111      184458 :     cancelConn = NULL;
     112             : 
     113      184458 :     if (oldCancelConn != NULL)
     114      184430 :         PQfreeCancel(oldCancelConn);
     115             : 
     116             : #ifdef WIN32
     117             :     LeaveCriticalSection(&cancelConnLock);
     118             : #endif
     119      184458 : }
     120             : 
     121             : 
     122             : /*
     123             :  * Code to support query cancellation
     124             :  *
     125             :  * Note that sending the cancel directly from the signal handler is safe
     126             :  * because PQcancel() is written to make it so.  We use write() to report
     127             :  * to stderr because it's better to use simple facilities in a signal
     128             :  * handler.
     129             :  *
     130             :  * On Windows, the signal canceling happens on a separate thread, because
     131             :  * that's how SetConsoleCtrlHandler works.  The PQcancel function is safe
     132             :  * for this (unlike PQrequestCancel).  However, a CRITICAL_SECTION is required
     133             :  * to protect the PGcancel structure against being changed while the signal
     134             :  * thread is using it.
     135             :  */
     136             : 
     137             : #ifndef WIN32
     138             : 
     139             : /*
     140             :  * handle_sigint
     141             :  *
     142             :  * Handle interrupt signals by canceling the current command, if cancelConn
     143             :  * is set.
     144             :  */
     145             : static void
     146           0 : handle_sigint(SIGNAL_ARGS)
     147             : {
     148           0 :     int         save_errno = errno;
     149             :     char        errbuf[256];
     150             : 
     151           0 :     if (cancel_callback != NULL)
     152           0 :         cancel_callback();
     153             : 
     154             :     /* Send QueryCancel if we are processing a database query */
     155           0 :     if (cancelConn != NULL)
     156             :     {
     157           0 :         if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
     158             :         {
     159           0 :             CancelRequested = true;
     160           0 :             write_stderr(_("Cancel request sent\n"));
     161             :         }
     162             :         else
     163             :         {
     164           0 :             write_stderr(_("Could not send cancel request: "));
     165           0 :             write_stderr(errbuf);
     166             :         }
     167             :     }
     168             :     else
     169           0 :         CancelRequested = true;
     170             : 
     171           0 :     errno = save_errno;         /* just in case the write changed it */
     172           0 : }
     173             : 
     174             : /*
     175             :  * setup_cancel_handler
     176             :  *
     177             :  * Register query cancellation callback for SIGINT.
     178             :  */
     179             : void
     180        4814 : setup_cancel_handler(void (*callback) (void))
     181             : {
     182        4814 :     cancel_callback = callback;
     183        4814 :     pqsignal(SIGINT, handle_sigint);
     184        4814 : }
     185             : 
     186             : #else                           /* WIN32 */
     187             : 
     188             : static BOOL WINAPI
     189             : consoleHandler(DWORD dwCtrlType)
     190             : {
     191             :     char        errbuf[256];
     192             : 
     193             :     if (dwCtrlType == CTRL_C_EVENT ||
     194             :         dwCtrlType == CTRL_BREAK_EVENT)
     195             :     {
     196             :         if (cancel_callback != NULL)
     197             :             cancel_callback();
     198             : 
     199             :         /* Send QueryCancel if we are processing a database query */
     200             :         EnterCriticalSection(&cancelConnLock);
     201             :         if (cancelConn != NULL)
     202             :         {
     203             :             if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
     204             :             {
     205             :                 write_stderr(_("Cancel request sent\n"));
     206             :                 CancelRequested = true;
     207             :             }
     208             :             else
     209             :             {
     210             :                 write_stderr(_("Could not send cancel request: %s"));
     211             :                 write_stderr(errbuf);
     212             :             }
     213             :         }
     214             :         else
     215             :             CancelRequested = true;
     216             : 
     217             :         LeaveCriticalSection(&cancelConnLock);
     218             : 
     219             :         return TRUE;
     220             :     }
     221             :     else
     222             :         /* Return FALSE for any signals not being handled */
     223             :         return FALSE;
     224             : }
     225             : 
     226             : void
     227             : setup_cancel_handler(void (*callback) (void))
     228             : {
     229             :     cancel_callback = callback;
     230             : 
     231             :     InitializeCriticalSection(&cancelConnLock);
     232             : 
     233             :     SetConsoleCtrlHandler(consoleHandler, TRUE);
     234             : }
     235             : 
     236             : #endif                          /* WIN32 */

Generated by: LCOV version 1.13