signal_init: Always unblock SIGCHLD. (#5243)

Inherited signal mask may block SIGCHLD, which causes libuv to hang at
epoll_wait.

Closes #5230

Helped-by: Nicolas Hillegeer <nicolas@hillegeer.com>
Helped-by: John Szakmeister <john@szakmeister.net>

Note: the #pragma gymnastics are a workaround for broken system headers on
macOS.

  signal.h:
      int  sigaddset(sigset_t *, int);
      #define     sigaddset(set, signo)   (*(set) |= __sigbits(signo), 0)
  sys/_types/_sigset.h:
      typedef __darwin_sigset_t            sigset_t;
  sys/_types.h:
      typedef __uint32_t   __darwin_sigset_t;      /* [???] signal set */

sigset_t is defined as unsigned int, but the sigaddset() ORs it with an int,
mixing the types.  So GCC generates a sign-conversion warning:

  sig.c:9:13: warning: implicit conversion changes signedness: 'int' to 'unsigned int' [-Wsign-conversion]
    (*(&s) |= __sigbits((sigset_t) 20), 0);
           ~~ ^~~~~~~~~~~~~~~~~~~~~~~~
  1 warning generated.

System headers are normally ignored when the compiler generates warnings:
https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html

  > GCC gives code found in system headers special treatment. All warnings,
  > other than those generated by ‘#warning’ (see Diagnostics), are suppressed
  > while GCC is processing a system header. Macros defined in a system header
  > are immune to a few warnings wherever they are expanded. This immunity is
  > granted on an ad-hoc basis, when we find that a warning generates lots of
  > false positives because of code in macros defined in system headers.

Instead of the #pragma workaround, we could cast the sigset_t pointer:

    # if defined(__APPLE__)
      sigaddset((int *)&mask, SIGCHLD);
    # else
      sigaddset(&mask, SIGCHLD);
    # endif

but that could break if the headers are later fixed.
This commit is contained in:
Justin M. Keyes 2016-08-29 13:39:32 -04:00 committed by GitHub
parent 0b5a7e4ad5
commit 10a54ad12e
2 changed files with 31 additions and 2 deletions

View File

@ -17,8 +17,7 @@
# include "event/process.c.generated.h" # include "event/process.c.generated.h"
#endif #endif
// {SIGNAL}_TIMEOUT is the time (in nanoseconds) that a process has to cleanly // Time (ns) for a process to exit cleanly before we send TERM/KILL.
// exit before we send SIGNAL to it
#define TERM_TIMEOUT 1000000000 #define TERM_TIMEOUT 1000000000
#define KILL_TIMEOUT (TERM_TIMEOUT * 2) #define KILL_TIMEOUT (TERM_TIMEOUT * 2)

View File

@ -2,6 +2,9 @@
#include <stdbool.h> #include <stdbool.h>
#include <uv.h> #include <uv.h>
#ifndef WIN32
# include <signal.h> // for sigset_t
#endif
#include "nvim/ascii.h" #include "nvim/ascii.h"
#include "nvim/vim.h" #include "nvim/vim.h"
@ -29,6 +32,9 @@ static bool rejecting_deadly;
void signal_init(void) void signal_init(void)
{ {
// Ensure that SIGCHLD is unblocked, else libuv (epoll_wait) may hang.
signal_unblock_SIGCHLD();
signal_watcher_init(&main_loop, &spipe, NULL); signal_watcher_init(&main_loop, &spipe, NULL);
signal_watcher_init(&main_loop, &shup, NULL); signal_watcher_init(&main_loop, &shup, NULL);
signal_watcher_init(&main_loop, &squit, NULL); signal_watcher_init(&main_loop, &squit, NULL);
@ -151,3 +157,27 @@ static void on_signal(SignalWatcher *handle, int signum, void *data)
break; break;
} }
} }
static void signal_unblock_SIGCHLD(void)
{
#ifndef WIN32
sigset_t mask;
sigemptyset(&mask);
// Work around broken macOS headers. #5243
# if defined(__APPLE__) && !defined(__clang__) && !defined(__INTEL_COMPILER) \
&& (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wsign-conversion"
# endif
sigaddset(&mask, SIGCHLD);
# if defined(__APPLE__) && !defined(__clang__) && !defined(__INTEL_COMPILER) \
&& (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
# pragma GCC diagnostic pop
# endif
pthread_sigmask(SIG_UNBLOCK, &mask, NULL);
#endif
}