[PATCH v4] [libpq] Try to avoid manually masking SIGPIPEs on every send() - Mailing list pgsql-hackers

From Jeremy Kerr
Subject [PATCH v4] [libpq] Try to avoid manually masking SIGPIPEs on every send()
Date
Msg-id 1248337278.256484.904895531919.1.gpush@pingu
Whole thread Raw
In response to Re: [PATCH 1/2 v3] [libpq] rework sigpipe-handling macros  (Robert Haas <robertmhaas@gmail.com>)
Responses Re: [PATCH v4] [libpq] Try to avoid manually masking SIGPIPEs on every send()
List pgsql-hackers
Currently, libpq will wrap each send() call on the connection with
two system calls to mask SIGPIPEs. This results in 3 syscalls instead
of one, and (on Linux) can lead to high contention on the signal
mask locks in threaded apps.

We have a couple of other methods to avoid SIGPIPEs:
sockopt(SO_NOSIGPIPE) and the MSG_NOSIGNAL flag to send().

This change attempts to use these if they're available at compile-
and run-time. If not, we drop back to manipulating the signal mask as
before.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>

---
v4: roll into one patch, use macros

---src/interfaces/libpq/fe-connect.c |   42 ++++++++++++src/interfaces/libpq/fe-secure.c  |  131
++++++++++++++++++++++++++------------src/interfaces/libpq/libpq-int.h |    2 3 files changed, 136 insertions(+), 39
deletions(-)

*** a/src/interfaces/libpq/fe-connect.c
--- b/src/interfaces/libpq/fe-connect.c
***************
*** 1089,1094 **** keep_going:                        /* We will come back to here until there is
--- 1089,1097 ----                 while (conn->addr_cur != NULL)                 {                     struct addrinfo
*addr_cur= conn->addr_cur;
 
+ #ifdef SO_NOSIGPIPE
+                     int optval;
+ #endif /* SO_NOSIGPIPE */                      /* Remember current address for possible error msg */
  memcpy(&conn->raddr.addr, addr_cur->ai_addr,
 
***************
*** 1153,1158 **** keep_going:                        /* We will come back to here until there is
--- 1156,1200 ----                     } #endif   /* F_SETFD */ 
+                     /* We have three methods of blocking sigpipe during
+                      * send() calls to this socket:
+                      *
+                      *  - setsockopt(sock, SO_NOSIGPIPE)
+                      *  - send(sock, ..., MSG_NOSIGNAL)
+                      *  - setting the signal mask to SIG_IGN during send()
+                      *
+                      * The first two reduce the number of syscalls (for the
+                      * third, we require three syscalls to implement a send()),
+                      * so use them if they're available. Their availability is
+                      * flagged in the following members of PGconn:
+                      *
+                      * conn->sigpipe_so        - we have set up SO_NOSIGPIPE
+                      * conn->sigpipe_flag    - we're specifying MSG_NOSIGNAL
+                      *
+                      * If we can use SO_NOSIGPIPE, then set sigpipe_so here and
+                      * we don't need to care about anything else. Otherwise,
+                      * try MSG_NOSIGNAL by setting sigpipe_flag. If we get an
+                      * error with MSG_NOSIGNAL, we clear the flag and revert
+                      * to manual masking.
+                      */
+                     conn->sigpipe_so = false;
+ #ifdef MSG_NOSIGNAL
+                     conn->sigpipe_flag = true;
+ #else /* !MSG_NOSIGNAL */
+                     conn->sigpipe_flag = false;
+ #endif /* MSG_NOSIGNAL */
+ 
+ #ifdef SO_NOSIGPIPE
+                     optval = 1;
+                     if (!setsockopt(conn->sock, SOL_SOCKET, SO_NOSIGPIPE,
+                             (char *)&optval, sizeof(optval)))
+                     {
+                         conn->sigpipe_so = true;
+                         conn->sigpipe_flag = false;
+                     }
+ #endif /* SO_NOSIGPIPE */
+ 
+                      /*                      * Start/make connection.  This should not block, since we
     * are in nonblock mode.  If it does, well, too bad.
 
*** a/src/interfaces/libpq/fe-secure.c
--- b/src/interfaces/libpq/fe-secure.c
***************
*** 118,161 **** static long win32_ssl_create_mutex = 0;  /*  * Macros to handle disabling and then restoring the state
ofSIGPIPE handling.
 
-  * Note that DISABLE_SIGPIPE() must appear at the start of a block.  */  #ifndef WIN32 #ifdef ENABLE_THREAD_SAFETY 
! #define DISABLE_SIGPIPE(failaction) \
!     sigset_t    osigmask; \
!     bool        sigpipe_pending; \
!     bool        got_epipe = false; \
! \
!     if (pq_block_sigpipe(&osigmask, &sigpipe_pending) < 0) \
!         failaction 
! #define REMEMBER_EPIPE(cond) \
!     do { \
!         if (cond) \
!             got_epipe = true; \
!     } while (0) 
! #define RESTORE_SIGPIPE() \
!     pq_reset_sigpipe(&osigmask, sigpipe_pending, got_epipe)
! #else                            /* !ENABLE_THREAD_SAFETY */ 
! #define DISABLE_SIGPIPE(failaction) \
!     pqsigfunc    oldsighandler = pqsignal(SIGPIPE, SIG_IGN) 
! #define REMEMBER_EPIPE(cond) 
! #define RESTORE_SIGPIPE() \
!     pqsignal(SIGPIPE, oldsighandler)
! #endif   /* ENABLE_THREAD_SAFETY */
! #else                            /* WIN32 */ 
! #define DISABLE_SIGPIPE(failaction)
! #define REMEMBER_EPIPE(cond)
! #define RESTORE_SIGPIPE()
! #endif   /* WIN32 */  /* ------------------------------------------------------------ */ /*             Procedures
commonto all secure sessions            */
 
--- 118,185 ----  /*  * Macros to handle disabling and then restoring the state of SIGPIPE handling.  */  #ifndef
WIN32
+ 
+ #ifdef USE_SSL
+ #define SIGPIPE_MASKED(c) \
+     ((c)->ssl ? (c)->sigpipe_so : (c)->sigpipe_so || (c)->sigpipe_flag)
+ #else
+ #define SIGPIPE_MASKED(c) \
+     ((c)->sigpipe_so || (c)->sigpipe_flag)
+ #endif
+  #ifdef ENABLE_THREAD_SAFETY 
! struct sigpipe_info {
!     sigset_t    oldsigmask;
!     bool        sigpipe_pending;
!     bool        got_epipe;
! }; 
! #define DECLARE_SIGPIPE_INFO(var) struct sigpipe_info var 
! #define DISABLE_SIGPIPE(conn, info) \
!     ({ int __x = 0; \
!         if (!SIGPIPE_MASKED(conn)) \
!             (info).got_epipe = false; \
!             __x = pq_block_sigpipe(&(info).oldsigmask, \
!                     &(info).sigpipe_pending) < 0; \
!         __x; }) 
! #define REMEMBER_EPIPE(info, cond) \
!     if (cond) (info).got_epipe = true 
! #define RESTORE_SIGPIPE(conn, info) \
!     if (!SIGPIPE_MASKED(conn)) \
!         pq_reset_sigpipe(&(info).oldsigmask, (info).sigpipe_pending, \
!                          (info).got_epipe) 
! #else    /* !ENABLE_THREAD_SAFETY */
! 
! #define DECLARE_SIGPIPE_INFO(var) pqsigfunc var = NULL
! 
! #define DISABLE_SIGPIPE(conn, info) \
!     ({ if (!SIGPIPE_MASKED(conn)) \
!         (info) = pqsignal(SIGPIPE, SIG_IGN); \
!       0; })
! 
! #define REMEMBER_EPIPE(info, cond)
! 
! #define RESTORE_SIGPIPE(conn, info) \
!     if (!SIGPIPE_MASKED(conn)) \
!         pqsignal(SIGPIPE, (info))
! 
! #endif    /* ENABLE_THREAD_SAFETY */
! #else    /* WIN32 */
! 
! #define DECLARE_SIGPIPE_INFO(var)
! #define DISABLE_SIGPIPE(conn, info) 0
! #define REMEMBER_EPIPE(info, cond)
! #define RESTORE_SIGPIPE(conn, info) 
! #endif    /* WIN32 */  /* ------------------------------------------------------------ */ /*             Procedures
commonto all secure sessions            */
 
***************
*** 283,291 **** pqsecure_read(PGconn *conn, void *ptr, size_t len)     if (conn->ssl)     {         int
err;         /* SSL_read can write to the socket, so we need to disable SIGPIPE */
 
!         DISABLE_SIGPIPE(return -1);  rloop:         n = SSL_read(conn->ssl, ptr, len);
--- 307,317 ----     if (conn->ssl)     {         int            err;
+         DECLARE_SIGPIPE_INFO(info);          /* SSL_read can write to the socket, so we need to disable SIGPIPE */
!         if (DISABLE_SIGPIPE(conn, info))
!             return -1;  rloop:         n = SSL_read(conn->ssl, ptr, len);
***************
*** 312,318 **** rloop:                      if (n == -1)                     {
!                         REMEMBER_EPIPE(SOCK_ERRNO == EPIPE);
printfPQExpBuffer(&conn->errorMessage,                                    libpq_gettext("SSL SYSCALL error: %s\n"),
                       SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
 
--- 338,344 ----                      if (n == -1)                     {
!                         REMEMBER_EPIPE(info, SOCK_ERRNO == EPIPE);
printfPQExpBuffer(&conn->errorMessage,                                    libpq_gettext("SSL SYSCALL error: %s\n"),
                       SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
 
***************
*** 348,354 **** rloop:                 break;         } 
!         RESTORE_SIGPIPE();     }     else #endif
--- 374,380 ----                 break;         } 
!         RESTORE_SIGPIPE(conn, info);     }     else #endif
***************
*** 364,377 **** ssize_t pqsecure_write(PGconn *conn, const void *ptr, size_t len) {     ssize_t        n;
! 
!     DISABLE_SIGPIPE(return -1);  #ifdef USE_SSL     if (conn->ssl)     {         int            err;          n =
SSL_write(conn->ssl,ptr, len);         err = SSL_get_error(conn->ssl, n);         switch (err)
 
--- 390,405 ---- pqsecure_write(PGconn *conn, const void *ptr, size_t len) {     ssize_t        n;
!     DECLARE_SIGPIPE_INFO(info);  #ifdef USE_SSL     if (conn->ssl)     {         int            err; 
+         if (DISABLE_SIGPIPE(conn, info))
+             return -1;
+          n = SSL_write(conn->ssl, ptr, len);         err = SSL_get_error(conn->ssl, n);         switch (err)
***************
*** 396,402 **** pqsecure_write(PGconn *conn, const void *ptr, size_t len)                      if (n == -1)
        {
 
!                         REMEMBER_EPIPE(SOCK_ERRNO == EPIPE);
printfPQExpBuffer(&conn->errorMessage,                                    libpq_gettext("SSL SYSCALL error: %s\n"),
                       SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
 
--- 424,430 ----                      if (n == -1)                     {
!                         REMEMBER_EPIPE(&info, SOCK_ERRNO == EPIPE);
printfPQExpBuffer(&conn->errorMessage,                                    libpq_gettext("SSL SYSCALL error: %s\n"),
                       SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
 
***************
*** 434,444 **** pqsecure_write(PGconn *conn, const void *ptr, size_t len)     else #endif     {
!         n = send(conn->sock, ptr, len, 0);
!         REMEMBER_EPIPE(n < 0 && SOCK_ERRNO == EPIPE);     } 
!     RESTORE_SIGPIPE();      return n; }
--- 462,496 ----     else #endif     {
!         int flags = 0;
! 
! #ifdef MSG_NOSIGNAL
!         if (!conn->sigpipe_so && conn->sigpipe_flag)
!             flags |= MSG_NOSIGNAL;
! #endif /* MSG_NOSIGNAL */
! 
! retry_masked:
!         if (DISABLE_SIGPIPE(conn, info))
!             return -1;
! 
!         n = send(conn->sock, ptr, len, flags);
! 
!         if (n < 0) {
!             /* if we see an EINVAL, it may be because MSG_NOSIGNAL isn't
!              * available on this machine. So, clear sigpipe_flag so we don't
!              * try this flag again, and retry the send().
!              */
!             if (flags != 0 && SOCK_ERRNO == EINVAL) {
!                 conn->sigpipe_flag = false;
!                 flags = 0;
!                 goto retry_masked;
!             }
! 
!             REMEMBER_EPIPE(info, SOCK_ERRNO == EPIPE);
!         }     } 
!     RESTORE_SIGPIPE(conn, info);      return n; }
***************
*** 1220,1233 **** close_SSL(PGconn *conn) {     if (conn->ssl)     {
!         DISABLE_SIGPIPE((void) 0);         SSL_shutdown(conn->ssl);         SSL_free(conn->ssl);         conn->ssl =
NULL;        pqsecure_destroy();         /* We have to assume we got EPIPE */
 
!         REMEMBER_EPIPE(true);
!         RESTORE_SIGPIPE();     }      if (conn->peer)
--- 1272,1286 ---- {     if (conn->ssl)     {
!         DECLARE_SIGPIPE_INFO(info);
!         DISABLE_SIGPIPE(conn, info);         SSL_shutdown(conn->ssl);         SSL_free(conn->ssl);         conn->ssl
=NULL;         pqsecure_destroy();         /* We have to assume we got EPIPE */
 
!         REMEMBER_EPIPE(info, true);
!         RESTORE_SIGPIPE(conn, info);     }      if (conn->peer)
*** a/src/interfaces/libpq/libpq-int.h
--- b/src/interfaces/libpq/libpq-int.h
***************
*** 341,346 **** struct pg_conn
--- 341,348 ----     ProtocolVersion pversion;    /* FE/BE protocol version in use */     int            sversion;
 /* server version, e.g. 70401 for 7.4.1 */     bool        password_needed;    /* true if server demanded a password
*/
+     bool        sigpipe_so;        /* have we masked sigpipes via SO_NOSIGPIPE? */
+     bool        sigpipe_flag;    /* can we mask sigpipes via MSG_NOSIGNAL? */      /* Transient state needed while
establishingconnection */     struct addrinfo *addrlist;    /* list of possible backend addresses */
 


pgsql-hackers by date:

Previous
From: Dimitri Fontaine
Date:
Subject: Re: Extension Facility
Next
From: "David E. Wheeler"
Date:
Subject: Re: Extension Facility