]> Sergey Matveev's repositories - public-inbox.git/commitdiff
listener: use EPOLLEXCLUSIVE for listen sockets
authorEric Wong <e@80x24.org>
Fri, 3 May 2019 10:34:09 +0000 (10:34 +0000)
committerEric Wong <e@80x24.org>
Sun, 5 May 2019 00:22:49 +0000 (00:22 +0000)
Since our listen sockets are non-blocking and we may run
multiple httpd|nntpd processes; we need a way to avoid
thundering herds when there are multiple httpd|nntpd worker
processes.

EPOLLEXCLUSIVE was added just for that in Linux 4.5

TODO
lib/PublicInbox/DS.pm
lib/PublicInbox/Listener.pm
lib/PublicInbox/Syscall.pm

diff --git a/TODO b/TODO
index 372f7331e78d345fd44117707bd9cc87618f34b4..ac255b861ca3e21d6598be1493845b79afbaf812 100644 (file)
--- a/TODO
+++ b/TODO
@@ -56,9 +56,6 @@ all need to be considered for everything we introduce)
   ugh... https://rt.cpan.org/Ticket/Display.html?id=116615
   (IO::KQueue is broken with Danga::Socket / PublicInbox::DS)
 
-* EPOLLEXCLUSIVE for listen socket fairness across -httpd/nntpd
-  worker processes.
-
 * improve documentation
 
 * linkify thread skeletons better
index 543d3fdcc755da03c910892b16c6a596a20c34d4..3ccc275d727e6a5245eab59f112ef7759c28ac33 100644 (file)
@@ -78,6 +78,8 @@ our (
      @Timers,                    # timers
      );
 
+# this may be set to zero with old kernels
+our $EPOLLEXCLUSIVE = EPOLLEXCLUSIVE;
 Reset();
 
 #####################################################################
@@ -666,11 +668,9 @@ This is normally (always?) called from your subclass via:
 
 =cut
 sub new {
-    my PublicInbox::DS $self = shift;
+    my ($self, $sock, $exclusive) = @_;
     $self = fields::new($self) unless ref $self;
 
-    my $sock = shift;
-
     $self->{sock}        = $sock;
     my $fd = fileno($sock);
 
@@ -685,13 +685,23 @@ sub new {
     $self->{corked} = 0;
     $self->{read_push_back} = [];
 
-    $self->{event_watch} = POLLERR|POLLHUP|POLLNVAL;
+    my $ev = $self->{event_watch} = POLLERR|POLLHUP|POLLNVAL;
 
     _InitPoller();
 
     if ($HaveEpoll) {
-        epoll_ctl($Epoll, EPOLL_CTL_ADD, $fd, $self->{event_watch})
-            and die "couldn't add epoll watch for $fd\n";
+        if ($exclusive) {
+            $ev = $self->{event_watch} = EPOLLIN|EPOLLERR|EPOLLHUP|$EPOLLEXCLUSIVE;
+        }
+retry:
+        if (epoll_ctl($Epoll, EPOLL_CTL_ADD, $fd, $ev)) {
+            if ($!{EINVAL} && ($ev & $EPOLLEXCLUSIVE)) {
+                $EPOLLEXCLUSIVE = 0; # old kernel
+                $ev = $self->{event_watch} = EPOLLIN|EPOLLERR|EPOLLHUP;
+                goto retry;
+            }
+            die "couldn't add epoll watch for $fd: $!\n";
+        }
     }
     elsif ($HaveKQueue) {
         # Add them to the queue but disabled for now
index d1f0d2e943b03dc860d57246059a842f52b5b727..a75a6fd9964ed1c9c2d2780b6680aa1b39ee76b2 100644 (file)
@@ -17,7 +17,7 @@ sub new ($$$) {
        listen($s, 1024);
        IO::Handle::blocking($s, 0);
        my $self = fields::new($class);
-       $self->SUPER::new($s); # calls epoll_create for the first socket
+       $self->SUPER::new($s, 1); # calls epoll_create for the first socket
        $self->watch_read(1);
        $self->{post_accept} = $cb;
        $self
index cf7004548684c857607b7a46ac8a726d5733a932..919436471b0e4dc98871a3a5658dd99049027c5f 100644 (file)
@@ -23,10 +23,12 @@ $VERSION     = "0.25";
 @ISA         = qw(Exporter);
 @EXPORT_OK   = qw(sendfile epoll_ctl epoll_create epoll_wait
                   EPOLLIN EPOLLOUT EPOLLERR EPOLLHUP EPOLLRDBAND
-                  EPOLL_CTL_ADD EPOLL_CTL_DEL EPOLL_CTL_MOD);
+                  EPOLL_CTL_ADD EPOLL_CTL_DEL EPOLL_CTL_MOD
+                  EPOLLEXCLUSIVE);
 %EXPORT_TAGS = (epoll => [qw(epoll_ctl epoll_create epoll_wait
                              EPOLLIN EPOLLOUT EPOLLERR EPOLLHUP EPOLLRDBAND
-                             EPOLL_CTL_ADD EPOLL_CTL_DEL EPOLL_CTL_MOD)],
+                             EPOLL_CTL_ADD EPOLL_CTL_DEL EPOLL_CTL_MOD
+                             EPOLLEXCLUSIVE)],
                 sendfile => [qw(sendfile)],
                 );
 
@@ -35,6 +37,7 @@ use constant EPOLLOUT      => 4;
 use constant EPOLLERR      => 8;
 use constant EPOLLHUP      => 16;
 use constant EPOLLRDBAND   => 128;
+use constant EPOLLEXCLUSIVE => (1 << 28);
 use constant EPOLL_CTL_ADD => 1;
 use constant EPOLL_CTL_DEL => 2;
 use constant EPOLL_CTL_MOD => 3;