Retry write on EINTR in signal handler

The signal handler writes the received signal number as a byte value
into the notification pipe. If two signals are received in quick
succession, one of the writes may fail with EINTR without writing the
byte. This commit will check for EINTR and retry the write. If the error
is other than EINTR, a warning will be logged.

Note, that:
- on systems with sigaction libevent uses sigaction with SA_RESTART
- on linux writing to pipe is restartable and firstly it will try to
  write that byte so linux should not be affected in any form [1].

  [1]: https://elixir.bootlin.com/linux/latest/source/fs/pipe.c#L545
This commit is contained in:
Mike Sharov 2021-04-25 09:12:29 -04:00 committed by Azat Khuzhin
parent dff8fd27ed
commit 4f8a61446c

View File

@ -400,9 +400,25 @@ evsig_handler(int sig)
#ifdef _WIN32
send(evsig_base_fd, (char*)&msg, 1, 0);
#else
{
int r = write(evsig_base_fd, (char*)&msg, 1);
(void)r; /* Suppress 'unused return value' and 'unused var' */
for (;;) {
/*
* errno is only set to provide a descriptive message for event_warnx
* if write returns 0. Not setting it will result in "No error" message
* because write does not set errno when returning 0.
*
* EAGAIN will print "Try again" message. Another idea is to use
* ENOSPC, but since we use non blocking sockets EAGAIN is preferable.
*
* Other than setting this text of the logged warning, the value in
* errno has no further effect.
*/
errno = EAGAIN;
if (0 >= write(evsig_base_fd, &msg, 1)) {
if (errno == EINTR)
continue;
event_warnx("%s: write: %s", __func__, strerror(errno));
}
break;
}
#endif
errno = save_errno;