[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: