lei could get a TTY in noncanonical mode for stdin, so rely on VMIN+VTIME to get the desired non-blocking semantics we'd expect from a pipe or socket. This ought to prevent read(2) (Perl sysread) from returning zero when we really want to hit EAGAIN. --- lib/PublicInbox/InputPipe.pm | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/lib/PublicInbox/InputPipe.pm b/lib/PublicInbox/InputPipe.pm index 39aefab2..8358ddd6 100644 --- a/lib/PublicInbox/InputPipe.pm +++ b/lib/PublicInbox/InputPipe.pm @@ -6,6 +6,31 @@ package PublicInbox::InputPipe; use v5.12; use parent qw(PublicInbox::DS); use PublicInbox::Syscall qw(EPOLLIN); +use POSIX (); +use Carp qw(croak carp); + +# I'm not sure what I'm doing w.r.t terminals. +# FIXME needs non-interactive tests +sub unblock_tty ($) { + my ($self) = @_; + my $fd = fileno($self->{sock}); + my $t = POSIX::Termios->new; + $t->getattr($fd) or croak("tcgetattr($fd): $!"); + return if $t->getlflag & POSIX::ICANON; # line-oriented, good + + # make noncanonical mode TTYs behave like a O_NONBLOCK pipe. + # O_NONBLOCK itself isn't well-defined, here, so rely on VMIN + VTIME + my ($vmin, $vtime) = ($t->getcc(POSIX::VMIN), $t->getcc(POSIX::VTIME)); + return if $vmin == 1 && $vtime == 0; + + $t->setcc(POSIX::VMIN, 1); # 1 byte minimum + $t->setcc(POSIX::VTIME, 0); # no timeout + $t->setattr($fd, POSIX::TCSANOW) or croak("tcsetattr($fd): $!"); + + $t->setcc(POSIX::VMIN, $vmin); + $t->setcc(POSIX::VTIME, $vtime); + $self->{restore_termios} = $t; +} sub consume { my ($in, $cb, @args) = @_; @@ -16,11 +41,17 @@ sub consume { $self->requeue; } elsif (-p $in || -s _) { # O_NONBLOCK for sockets and pipes $in->blocking(0); - } # TODO: tty + } elsif (-t $in) { # isatty(3) can't use `_' stat cache + unblock_tty($self); + } } sub close { my ($self) = @_; + if (my $t = delete($self->{restore_termios})) { + my $fd = fileno($self->{sock} // return); + $t->setattr($fd, POSIX::TCSANOW) or carp("tcsetattr($fd): $!") + } $self->{-need_rq} ? delete($self->{sock}) : $self->SUPER::close }