Re: [HACKERS] libpq and psql not on same page about SIGPIPE - Mailing list pgsql-patches

From Bruce Momjian
Subject Re: [HACKERS] libpq and psql not on same page about SIGPIPE
Date
Msg-id 200412020345.iB23jFO11492@candle.pha.pa.us
Whole thread Raw
Responses Re: [HACKERS] libpq and psql not on same page about SIGPIPE
List pgsql-patches
Tom Lane wrote:
> Bruce Momjian <pgman@candle.pha.pa.us> writes:
> > Yes, that is the logic in my patch, except that I don't check errno, I
> > just call sigpending().
>
> No, that's wrong: if there is a pending SIGPIPE that belongs to the
> outer app, you'd clear it.

True, but I documented that in the patch.

> > There are a few writes and it seemed impossible
> > to check them all.
>
> Hmm?  There is only one place this needs to be done, namely
> pqsecure_write.

Look also in fe-print.c.  Looks like a lot of popen writes in there.
I can do it but it will be harder.

> BTW, have you posted the patch yet or are you still working on it?
> The mail server seems a bit flaky today ...

OK, patch attached.  I already sent it but who knows what happened to
it.

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: configure
===================================================================
RCS file: /cvsroot/pgsql/configure,v
retrieving revision 1.409
diff -c -c -r1.409 configure
*** configure    30 Nov 2004 06:13:03 -0000    1.409
--- configure    1 Dec 2004 22:53:58 -0000
***************
*** 17431,17436 ****
--- 17431,17448 ----
  fi
  HAVE_POSIX_SIGNALS=$pgac_cv_func_posix_signals

+ if test "$pgac_cv_func_posix_signals" != yes -a "$enable_thread_safety" = yes; then
+   { { echo "$as_me:$LINENO: error:
+ *** Thread-safety requires POSIX signals, which are not supported by your
+ *** operating system.
+ " >&5
+ echo "$as_me: error:
+ *** Thread-safety requires POSIX signals, which are not supported by your
+ *** operating system.
+ " >&2;}
+    { (exit 1); exit 1; }; }
+ fi
+
  if test $ac_cv_func_fseeko = yes; then
  # Check whether --enable-largefile or --disable-largefile was given.
  if test "${enable_largefile+set}" = set; then
Index: configure.in
===================================================================
RCS file: /cvsroot/pgsql/configure.in,v
retrieving revision 1.387
diff -c -c -r1.387 configure.in
*** configure.in    30 Nov 2004 06:13:04 -0000    1.387
--- configure.in    1 Dec 2004 22:54:11 -0000
***************
*** 1174,1179 ****
--- 1174,1186 ----


  PGAC_FUNC_POSIX_SIGNALS
+ if test "$pgac_cv_func_posix_signals" != yes -a "$enable_thread_safety" = yes; then
+   AC_MSG_ERROR([
+ *** Thread-safety requires POSIX signals, which are not supported by your
+ *** operating system.
+ ])
+ fi
+
  if test $ac_cv_func_fseeko = yes; then
  AC_SYS_LARGEFILE
  fi
Index: doc/src/sgml/libpq.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v
retrieving revision 1.169
diff -c -c -r1.169 libpq.sgml
*** doc/src/sgml/libpq.sgml    27 Nov 2004 21:56:04 -0000    1.169
--- doc/src/sgml/libpq.sgml    1 Dec 2004 22:54:45 -0000
***************
*** 3955,3975 ****
  </para>

  <para>
! <application>libpq</application> must ignore <literal>SIGPIPE</> signals
! generated internally by <function>send()</> calls to backend processes.
! When <productname>PostgreSQL</> is configured without
! <literal>--enable-thread-safety</>, <application>libpq</> sets
! <literal>SIGPIPE</> to <literal>SIG_IGN</> before each
! <function>send()</> call and restores the original signal handler after
! completion. When <literal>--enable-thread-safety</> is used,
! <application>libpq</> installs its own <literal>SIGPIPE</> handler
! before the first database connection.  This handler uses thread-local
! storage to determine if a <literal>SIGPIPE</> signal has been generated
! by a libpq <function>send()</>. If an application wants to install
! its own <literal>SIGPIPE</> signal handler, it should call
! <function>PQinSend()</> to determine if it should ignore the
! <literal>SIGPIPE</> signal. This function is available in both
! thread-safe and non-thread-safe versions of <application>libpq</>.
  </para>

  <para>
--- 3955,3964 ----
  </para>

  <para>
! <application>libpq</application> blocks and discards <literal>SIGPIPE</>
! signals generated internally by <function>send()</> calls to the backend
! process. Therefore, applications should not expect blocked <literal>SIGPIPE</>
! signals to remain across <application>libpq</application> function calls.
  </para>

  <para>
Index: doc/src/sgml/ref/copy.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/copy.sgml,v
retrieving revision 1.60
diff -c -c -r1.60 copy.sgml
*** doc/src/sgml/ref/copy.sgml    27 Nov 2004 21:56:05 -0000    1.60
--- doc/src/sgml/ref/copy.sgml    1 Dec 2004 22:54:57 -0000
***************
*** 3,8 ****
--- 3,9 ----
  PostgreSQL documentation
  -->

+
  <refentry id="SQL-COPY">
   <refmeta>
    <refentrytitle id="sql-copy-title">COPY</refentrytitle>
Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.289
diff -c -c -r1.289 fe-connect.c
*** src/interfaces/libpq/fe-connect.c    30 Oct 2004 23:11:26 -0000    1.289
--- src/interfaces/libpq/fe-connect.c    1 Dec 2004 22:55:18 -0000
***************
*** 865,879 ****
      const char *node = NULL;
      int            ret;

- #ifdef ENABLE_THREAD_SAFETY
- #ifndef WIN32
-     static pthread_once_t check_sigpipe_once = PTHREAD_ONCE_INIT;
-
-     /* Check only on first connection request */
-     pthread_once(&check_sigpipe_once, pq_check_sigpipe_handler);
- #endif
- #endif
-
      if (!conn)
          return 0;

--- 865,870 ----
Index: src/interfaces/libpq/fe-print.c
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/fe-print.c,v
retrieving revision 1.55
diff -c -c -r1.55 fe-print.c
*** src/interfaces/libpq/fe-print.c    9 Nov 2004 15:57:57 -0000    1.55
--- src/interfaces/libpq/fe-print.c    1 Dec 2004 22:55:25 -0000
***************
*** 91,97 ****
          int            total_line_length = 0;
          int            usePipe = 0;
          char       *pagerenv;
!
  #if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
          pqsigfunc    oldsigpipehandler = NULL;
  #endif
--- 91,100 ----
          int            total_line_length = 0;
          int            usePipe = 0;
          char       *pagerenv;
! #ifdef ENABLE_THREAD_SAFETY
!         sigset_t    osigset;
!         bool        sigpipe_masked = false;
! #endif
  #if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
          pqsigfunc    oldsigpipehandler = NULL;
  #endif
***************
*** 189,195 ****
                  {
                      usePipe = 1;
  #ifdef ENABLE_THREAD_SAFETY
!                     pthread_setspecific(pq_thread_in_send, "t");
  #else
  #ifndef WIN32
                      oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
--- 192,199 ----
                  {
                      usePipe = 1;
  #ifdef ENABLE_THREAD_SAFETY
!                     pq_block_sigpipe(&osigset);
!                     sigpipe_masked = true;
  #else
  #ifndef WIN32
                      oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
***************
*** 311,317 ****
              pclose(fout);
  #endif
  #ifdef ENABLE_THREAD_SAFETY
!             pthread_setspecific(pq_thread_in_send, "f");
  #else
  #ifndef WIN32
              pqsignal(SIGPIPE, oldsigpipehandler);
--- 315,322 ----
              pclose(fout);
  #endif
  #ifdef ENABLE_THREAD_SAFETY
!             if (sigpipe_masked)
!                 pq_reset_sigpipe(&osigset);
  #else
  #ifndef WIN32
              pqsignal(SIGPIPE, oldsigpipehandler);
Index: src/interfaces/libpq/fe-secure.c
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/fe-secure.c,v
retrieving revision 1.57
diff -c -c -r1.57 fe-secure.c
*** src/interfaces/libpq/fe-secure.c    20 Nov 2004 00:35:13 -0000    1.57
--- src/interfaces/libpq/fe-secure.c    1 Dec 2004 22:55:31 -0000
***************
*** 152,163 ****
  static SSL_CTX *SSL_context = NULL;
  #endif

- #ifdef ENABLE_THREAD_SAFETY
- static void sigpipe_handler_ignore_send(int signo);
- pthread_key_t pq_thread_in_send = 0;    /* initializer needed on Darwin */
- static pqsigfunc pq_pipe_handler;
- #endif
-
  /* ------------------------------------------------------------ */
  /*                         Hardcoded values                        */
  /* ------------------------------------------------------------ */
--- 152,157 ----
***************
*** 379,387 ****
  pqsecure_write(PGconn *conn, const void *ptr, size_t len)
  {
      ssize_t        n;
!
  #ifdef ENABLE_THREAD_SAFETY
!     pthread_setspecific(pq_thread_in_send, "t");
  #else
  #ifndef WIN32
      pqsigfunc    oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
--- 373,383 ----
  pqsecure_write(PGconn *conn, const void *ptr, size_t len)
  {
      ssize_t        n;
!
  #ifdef ENABLE_THREAD_SAFETY
!     sigset_t    osigmask;
!
!     pq_block_sigpipe(&osigmask);
  #else
  #ifndef WIN32
      pqsigfunc    oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
***************
*** 454,460 ****
          n = send(conn->sock, ptr, len, 0);

  #ifdef ENABLE_THREAD_SAFETY
!     pthread_setspecific(pq_thread_in_send, "f");
  #else
  #ifndef WIN32
      pqsignal(SIGPIPE, oldsighandler);
--- 450,456 ----
          n = send(conn->sock, ptr, len, 0);

  #ifdef ENABLE_THREAD_SAFETY
!     pq_reset_sigpipe(&osigmask);
  #else
  #ifndef WIN32
      pqsignal(SIGPIPE, oldsighandler);
***************
*** 1216,1280 ****
  }
  #endif   /* USE_SSL */

-
  #ifdef ENABLE_THREAD_SAFETY
- #ifndef WIN32
  /*
!  *    Check SIGPIPE handler and perhaps install our own.
   */
! void
! pq_check_sigpipe_handler(void)
  {
!     pthread_key_create(&pq_thread_in_send, NULL);
!
!     /*
!      * Find current pipe handler and chain on to it.
!      */
!     pq_pipe_handler = pqsignalinquire(SIGPIPE);
!     pqsignal(SIGPIPE, sigpipe_handler_ignore_send);
! }
!
! /*
!  *    Threaded SIGPIPE signal handler
   */
! void
! sigpipe_handler_ignore_send(int signo)
  {
!     /*
!      * If we have gotten a SIGPIPE outside send(), chain or exit if we are
!      * at the end of the chain. Synchronous signals are delivered to the
!      * thread that caused the signal.
!      */
!     if (!PQinSend())
      {
!         if (pq_pipe_handler == SIG_DFL) /* not set by application */
!             exit(128 + SIGPIPE);    /* typical return value for SIG_DFL */
!         else
!             (*pq_pipe_handler) (signo); /* call original handler */
      }
- }
- #endif
- #endif
-
- /*
-  *    Indicates whether the current thread is in send()
-  *    For use by SIGPIPE signal handlers;  they should
-  *    ignore SIGPIPE when libpq is in send().  This means
-  *    that the backend has died unexpectedly.
-  */
- pqbool
- PQinSend(void)
- {
- #ifdef ENABLE_THREAD_SAFETY
-     return (pthread_getspecific(pq_thread_in_send) /* has it been set? */ &&
-             *(char *) pthread_getspecific(pq_thread_in_send) == 't') ? true : false;
- #else

!     /*
!      * No threading: our code ignores SIGPIPE around send(). Therefore, we
!      * can't be in send() if we are checking from a SIGPIPE signal
!      * handler.
!      */
!     return false;
! #endif
  }
--- 1212,1264 ----
  }
  #endif   /* USE_SSL */

  #ifdef ENABLE_THREAD_SAFETY
  /*
!  *    Block SIGPIPE for this thread.  This prevents send()/write() from exiting
!  *    the application.
   */
! int
! pq_block_sigpipe(sigset_t *osigset)
  {
!     sigset_t sigpipe_sigset;
!
!     sigemptyset(&sigpipe_sigset);
!     sigaddset(&sigpipe_sigset, SIGPIPE);
!
!     /* Block SIGPIPE and save previous mask for later reset */
!     return pthread_sigmask(SIG_BLOCK, &sigpipe_sigset, osigset);
! }
!
! /*
!  *    Discard any pending SIGPIPE and reset the signal mask.
!  *    We might be discarding a blocked SIGPIPE that we didn't generate,
!  *    but we document that you can't keep blocked SIGPIPE calls across
!  *    libpq function calls.
   */
! int
! pq_reset_sigpipe(sigset_t *osigset)
  {
!     int    signo;
!     sigset_t sigset;
!
!     /* Is SIGPIPE pending? */
!     if (sigpending(&sigset) != 0)
!         return -1;
!
!     if (sigismember(&sigset, SIGPIPE))
      {
!         sigset_t sigpipe_sigset;
!
!         sigemptyset(&sigpipe_sigset);
!         sigaddset(&sigpipe_sigset, SIGPIPE);
!
!         /* Discard pending and blocked SIGPIPE */
!         sigwait(&sigpipe_sigset, &signo);
!         if (signo != SIGPIPE)
!             return -1;
      }

!     /* Restore saved block mask */
!     return pthread_sigmask(SIG_SETMASK, osigset, NULL);
  }
+ #endif
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.113
diff -c -c -r1.113 libpq-fe.h
*** src/interfaces/libpq/libpq-fe.h    30 Oct 2004 23:11:27 -0000    1.113
--- src/interfaces/libpq/libpq-fe.h    1 Dec 2004 22:55:33 -0000
***************
*** 497,508 ****

  /* === in fe-secure.c === */

- /*
-  *    Indicates whether the libpq thread is in send().
-  *    Used to ignore SIGPIPE if thread is in send().
-  */
- extern pqbool PQinSend(void);
-
  #ifdef __cplusplus
  }
  #endif
--- 497,502 ----
Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.96
diff -c -c -r1.96 libpq-int.h
*** src/interfaces/libpq/libpq-int.h    30 Oct 2004 23:11:27 -0000    1.96
--- src/interfaces/libpq/libpq-int.h    1 Dec 2004 22:55:35 -0000
***************
*** 31,36 ****
--- 31,37 ----

  #ifdef ENABLE_THREAD_SAFETY
  #include <pthread.h>
+ #include <signal.h>
  #endif

  #ifdef WIN32_CLIENT_ONLY
***************
*** 475,489 ****
  extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
  extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);

- #ifdef ENABLE_THREAD_SAFETY
- extern void pq_check_sigpipe_handler(void);
- extern pthread_key_t pq_thread_in_send;
- #endif
-
  #ifdef USE_SSL
  extern bool pq_initssllib;
  #endif

  /*
   * this is so that we can check if a connection is non-blocking internally
   * without the overhead of a function call
--- 476,490 ----
  extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
  extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);

  #ifdef USE_SSL
  extern bool pq_initssllib;
  #endif

+ #ifdef ENABLE_THREAD_SAFETY
+ int pq_block_sigpipe(sigset_t *osigset);
+ int pq_reset_sigpipe(sigset_t *osigset);
+ #endif
+
  /*
   * this is so that we can check if a connection is non-blocking internally
   * without the overhead of a function call

pgsql-patches by date:

Previous
From: Thomas Hallgren
Date:
Subject: Re: SPI function to investigate query semantics
Next
From: Tom Lane
Date:
Subject: Re: [HACKERS] libpq and psql not on same page about SIGPIPE