Condition raised when stderr is missing

Bug #1887913 reported by Pierre Neidhardt
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
SBCL
Incomplete
Undecided
Unassigned

Bug Description

If I start a Common Lisp program that outputs to stderr and then remove the stderr file descriptor, the program will raise a condition when emitting warnings for instance.

Example using Bash:

$ ./foo-program &
$ exit

I suppose this is normal that SBCL raises a condition when stderr is missing and it tries to output a warning. (Is it specified by the standard?) Is there a way to handle these conditions so that the program does not hang on warnings?

See https://github.com/atlas-engineer/nyxt/issues/849 for a discussion.

Revision history for this message
Douglas Katzman (dougk) wrote :

There's a lot of missing information in this report.
I don't know what "foo-program" is, nor what "nyxt" is so that's not really an example of SBCL behavior at any rate.

As to the problem statement: "It outputs to stderr and then ... remove" - is this to imply that "It outputs to stderr" with success, i.e. visibly? Then I'm not sure what the point is.
Or is it meant to suggest that some output was placed in *ERROR-OUTPUT* which was buffered prior to closing stderr?
The solution could be to call (FORCE-OUTPUT) if you're asking how to make missing output appear.
SBCL's *error-output* is line-buffered, the same as stdio's stderr.
(I'm assuming that "removed" means "closed" - "removed" is not a concept that pertains to a file descriptor, though it may pertain to an on-disk object)

If you're asking to have no condition be raised when a file descriptor is not writable, then I don't know why you would want that.

Revision history for this message
Pierre Neidhardt (ambrevar) wrote :

Nyxt is a Common Lisp web browser (see https://nyxt.atlas.engineer).
foo-program is an example long running Common Lisp application.

My problem is the last one:

> to have no condition be raised when a file descriptor is not writable,

The programmers cannot control from their application when libraries write to stderr (this includes SBCL itself).
If I want to deliver to the end user (not necessarily a programmer) a program that does not hang when stderr is removed, I'd like to tell all the libraries (and SBCL) to not raise a condition when stderr is missing.

Revision history for this message
Douglas Katzman (dougk) wrote :

I literally have no idea what "stderr is missing" means.

This is 'stderr':
* sb-sys:*stderr*
#<SB-SYS:FD-STREAM for "standard error" {1001598163}>
It can't become 'missing'.

For lack of anything better to say about this, try putting a handler-bind around forms that might generate lisp conditions which you don't want to propagate outside of that form.

Changed in sbcl:
status: New → Incomplete
Revision history for this message
Pierre Neidhardt (ambrevar) wrote :

stderr (or any stream for that matter) can be bound to a file descriptor which is can be deleted externally.

To reproduce, see the original post recipe: by exiting the shell, we delete the standard error file descriptor. This raises a condition in the Common Lisp process when it tries to write there.

Ideally, I'd like to know if there is a way to prevent such conditions being raised. A handler-bind is not always an option, especially when libraries start other threads themselves, which may in turn raise this condition.

Revision history for this message
Tomas Hlavaty (tomas-hlavaty) wrote : Re: [Bug 1887913] Re: Condition raised when stderr is missing

On Tue 21 Jul 2020 at 14:43, Pierre Neidhardt <email address hidden> wrote:
> Ideally, I'd like to know if there is a way to prevent such conditions
> being raised. A handler-bind is not always an option, especially when
> libraries start other threads themselves, which may in turn raise this
> condition.

you can set *error-output* etc to *standard-output* for example
http://www.lispworks.com/documentation/HyperSpec/Body/v_debug_.htm
(or open "/dev/null" etc)

Revision history for this message
Pierre Neidhardt (ambrevar) wrote :

This would not work: I don't want to redirect the error output, I want it to behave as the user would expect, I just don't want the application to hang just because the parent shell was quit.

Revision history for this message
Douglas Katzman (dougk) wrote :

There is no such thing as deleting a file descriptor externally. That is not a thing that can happen; the set of file descriptors (small positive integers) that a Unix process knows about is not directly impacted by a behavior external to the process. Specifically, in your comment "we delete the standard error file descriptor," no, we don't delete it, but we might observe that it has no reader.

So in order to talk about behaviors on write() failures we need to use proper terminology. I think what you're trying to say is that a writable object to which a file descriptor referred has ceased being writable. The nuance here is whether you're asking to detect EBADF (which the _descriptor_ itself is not valid - either it was closed or was never valid), versus EPIPE, where the target object (tty, pipe, socket), can not be written. If the latter, I suspect that the situation has changed now that SBCL does not intercept the SIGPIPE handler. (https://sourceforge.net/p/sbcl/sbcl/ci/1300c870)
So you might want to investigate installing your own SIGPIPE handler.
But overall I'm still unsure whether you're observing "hanging" (which would imply, say, doing a blocking write operation), versus seeing a Lisp condition which implies *not* hanging.

So there's really nothing actionable here; but in the future if you wish to report a bug against SBCL you should provide some lisp code, the expected behavior, and the observed behavior.

Revision history for this message
Pierre Neidhardt (ambrevar) wrote :

Sorry for the bad terminology and thank you for clarifying it.

My qestion then boils down to this: how do I install a SIGPIPE / EBADF handler
for the whole application?

About your last comment: I haven't provided any Lisp code because I don't have
any. The recipe is as I posted in the original post with the execution of an
SBCL binary in a shell that is then closed (while the binary keeps running in the background).

- Expected behaviour: With the proper handler in place, the application would be
  able to handle the bad file descriptor that *standard-output* and
  *standard-error* are point to.

- Current behaviour: Without any handler, the execution of the application stops
  (proparly due to a raised condition) when anything is written to
  *standard-output* or *standard-error*.

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.