# now we can close sockets that wanted to close during our event processing.
# (we didn't want to close them during the loop, as we didn't want fd numbers
# being reused and confused during the event loop)
- while (my $sock = shift @ToClose) {
- my $fd = fileno($sock);
-
- # close the socket. (not a PublicInbox::DS close)
- CORE::close($sock);
-
- # and now we can finally remove the fd from the map. see
- # comment above in ->close.
- delete $DescriptorMap{$fd};
- }
-
+ delete($DescriptorMap{fileno($_)}) for @ToClose;
+ @ToClose = (); # let refcounting drop everything all at once
# by default we keep running, unless a postloop callback (either per-object
# or global) cancels it
$written;
}
+sub epbit ($$) { # (sock, default)
+ ref($_[0]) eq 'IO::Socket::SSL' ? PublicInbox::TLS::epollbit() : $_[1];
+}
+
# returns 1 if done, 0 if incomplete
sub flush_write ($) {
my ($self) = @_;
goto next_buf;
}
} elsif ($! == EAGAIN) {
+ epwait($sock, epbit($sock, EPOLLOUT) | EPOLLONESHOT);
$self->{wbuf_off} = $off;
- watch($self, EPOLLOUT|EPOLLONESHOT);
return 0;
} else {
return $self->close;
sub do_read ($$$;$) {
my ($self, $rbuf, $len, $off) = @_;
- my $r = sysread($self->{sock}, $$rbuf, $len, $off // 0);
+ my $r = sysread(my $sock = $self->{sock}, $$rbuf, $len, $off // 0);
return ($r == 0 ? $self->close : $r) if defined $r;
# common for clients to break connections without warning,
# would be too noisy to log here:
- if (ref($self) eq 'IO::Socket::SSL') {
- my $ev = PublicInbox::TLS::epollbit() or return $self->close;
+ if ($! == EAGAIN) {
+ epwait($sock, epbit($sock, EPOLLIN) | EPOLLONESHOT);
rbuf_idle($self, $rbuf);
- watch($self, $ev | EPOLLONESHOT);
- } elsif ($! == EAGAIN) {
- rbuf_idle($self, $rbuf);
- watch($self, EPOLLIN | EPOLLONESHOT);
+ 0;
} else {
$self->close;
}
if (defined $written) {
return 1 if $written == $to_write;
+ requeue($self); # runs: event_step -> flush_write
} elsif ($! == EAGAIN) {
+ epwait($sock, epbit($sock, EPOLLOUT) | EPOLLONESHOT);
$written = 0;
} else {
return $self->close;
}
+
+ # deal with EAGAIN or partial write:
my $tmpio = tmpio($self, $bref, $written) or return 0;
# wbuf may be an empty array if we're being called inside
# ->flush_write via CODE bref:
push @{$self->{wbuf} ||= []}, $tmpio;
- watch($self, EPOLLOUT|EPOLLONESHOT);
return 0;
}
}
# queue up the unwritten substring:
my $tmpio = tmpio($self, \($_[1]), $n) or return 0;
$self->{wbuf} = [ $tmpio ];
- watch($self, EPOLLOUT|EPOLLONESHOT);
+ epwait($sock, EPOLLOUT|EPOLLONESHOT);
return 0;
}
}
$self->write(\($_[1]));
}
-sub watch ($$) {
- my ($self, $ev) = @_;
- my $sock = $self->{sock} or return;
+sub epwait ($$) {
+ my ($sock, $ev) = @_;
epoll_ctl($Epoll, EPOLL_CTL_MOD, fileno($sock), $ev) and
confess("EPOLL_CTL_MOD $!");
- 0;
}
# return true if complete, false if incomplete (or failure)
my $sock = $self->{sock} or return;
return 1 if $sock->accept_SSL;
return $self->close if $! != EAGAIN;
- if (my $ev = PublicInbox::TLS::epollbit()) {
- unshift @{$self->{wbuf} ||= []}, \&accept_tls_step;
- return watch($self, $ev | EPOLLONESHOT);
- }
- drop($self, 'BUG? EAGAIN but '.PublicInbox::TLS::err());
+ epwait($sock, PublicInbox::TLS::epollbit() | EPOLLONESHOT);
+ unshift @{$self->{wbuf} ||= []}, \&accept_tls_step;
+ 0;
}
+# return true if complete, false if incomplete (or failure)
sub shutdn_tls_step ($) {
my ($self) = @_;
my $sock = $self->{sock} or return;
return $self->close if $sock->stop_SSL(SSL_fast_shutdown => 1);
return $self->close if $! != EAGAIN;
- if (my $ev = PublicInbox::TLS::epollbit()) {
- unshift @{$self->{wbuf} ||= []}, \&shutdn_tls_step;
- return watch($self, $ev | EPOLLONESHOT);
- }
- drop($self, 'BUG? EAGAIN but '.PublicInbox::TLS::err());
+ epwait($sock, PublicInbox::TLS::epollbit() | EPOLLONESHOT);
+ unshift @{$self->{wbuf} ||= []}, \&shutdn_tls_step;
+ 0;
}
# don't bother with shutdown($sock, 2), we don't fork+exec w/o CLOEXEC