]> granicus.if.org Git - libevent/commitdiff
Retry write on EINTR in signal handler
authorMike Sharov <msharov@users.sourceforge.net>
Sun, 25 Apr 2021 13:12:29 +0000 (09:12 -0400)
committerAzat Khuzhin <azat@libevent.org>
Tue, 27 Apr 2021 05:06:53 +0000 (08:06 +0300)
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

index 89f5fc1717638f58d0608fd82588f6a17dad0559..ad7ab64399bef0a429c8d4984ed52069e04f66b8 100644 (file)
--- 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;