develooper Front page | perl.perl5.porters | Postings from March 2017

Re: [perl #41043] perlio forget about buffering settings on EAGAIN

Thread Next
March 27, 2017 00:01
Re: [perl #41043] perlio forget about buffering settings on EAGAIN
Message ID:
This can be tested more cleanly by creating a pipe and never reading
from it, rather than writing to a tty and hoping that it can't keep up.

The behaviour seen arises from the sticky error status of an I/O handle.
In itself, the sticky error status is an intentional feature, which
can avoid the need to put high-level error-handling code on every I/O
operation.  The failed output of the "+" sets the sticky error flag.
The effect of the error flag is then that, even though outputting "*"
succeeds as far as adding it to the buffer, that output operation is
considered failed.  Consequently the print op aborts: it doesn't attempt
the flush operation that would normally result from the autoflush flag
(which remains set), and it returns false.  (If it were ignoring the
autoflush flag and thought it had succeeded in outputting "*" just by
adding it to the buffer then it would return true.)

Further enlightenment can be gained by changing the print operations to
each print two things, from separate operands, as in 'print "*", "*"'.
The operands are written to PerlIO sequentially, and the autoflush
happens after all of them.  That sequence initially manifests by "--"
being passed to the write syscall in one go.  The effect of the sticky
error then becomes clearer at the "*" line.  Writing the first "*" to
the buffer succeeds, but the operation is considered failed because of
the sticky error flag.  The print op therefore aborts before outputting
the second operand, and so when a flush eventually happens (e.g., on
program end) only the one "*" will be passed to the syscall.

You can clear the sticky error flag by using the ->clearerr method
of IO::Handle.  This is the appropriate thing to do with errors that
you expect to be transitory, such as EAGAIN.  This will restore the
autoflushing behaviour that you're expecting: the next print op after
->clearerr will automatically flush.  If you've already buffered some
output with the error flag set then ->clearerr won't flush it, but
you can do an explicit ->flush at the same time.  However, when you're
expecting to handle an error from a specific operation, it's best to
complete the error handling and clear the error flag *before* you try
to output anything that you want to succeed independently of the error
you've handled.

This behaviour isn't a bug in perl.  However, it might be less strange
(though still a bit confusing) if the sticky error flag made subsequent
I/O operations fail *before* they were attempted.  In this ticket's test
case, that would cause the "*" to be lost entirely, not even reappearing
at the end-of-program implicit flush.  ->clearerr would still be needed
to handle EAGAIN, but we wouldn't get half-done print ops when the error
is not cleared.

We could also probably document this better.


Thread Next Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About