From 4f8a61446c1b76ae6e6f9cb0a1e231b669412082 Mon Sep 17 00:00:00 2001 From: Mike Sharov Date: Sun, 25 Apr 2021 09:12:29 -0400 Subject: [PATCH] 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 --- signal.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/signal.c b/signal.c index 89f5fc17..ad7ab643 100644 --- a/signal.c +++ b/signal.c @@ -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;