Subject: Re: bug#76969: kill-buffer fails silently when a thread exists where it's current

From: Eli Zaretskii <[email-masked]>
Date: Thu, 13 Mar 2025 16:58:56 +0200

> Date: Thu, 13 Mar 2025 14:10:36 +0200
> Cc: [email-masked]
> From: Dmitry Gutov <[email-masked]>
> 
> On 13/03/2025 11:47, Eli Zaretskii wrote:
> 
> > There are other reasons which preclude killing a buffer that aren't
> > mentioned in the doc string.  For example, this:
> 
> That's a good point.
> 
> >    /* Don't kill the minibuffer now current.  */
> >    if (BASE_EQ (buffer, XWINDOW (minibuf_window)->contents))
> >      return Qnil;
> > 
> > or this:
> > 
> >    /* Make this buffer not be current.  Exit if it is the sole visible
> >       buffer.  */
> >    if (b == current_buffer)
> >      {
> >        tem = Fother_buffer (buffer, Qnil, Qnil);
> >        Fset_buffer (tem);
> >        if (b == current_buffer)
> > 	return Qnil;
> > 
> > or this:
> > 
> >    /* If the buffer now current is shown in the minibuffer and our buffer
> >       is the sole other buffer give up.  */
> >    XSETBUFFER (tem, current_buffer);
> >    if (EQ (tem, XWINDOW (minibuf_window)->contents)
> >        && BASE_EQ (buffer, Fother_buffer (buffer, Qnil, Qnil)))
> >      return Qnil;
> > 
> > So if this is a request to spell out these conditions in the
> > documentation, I'm okay with doing so.
> 
> Yes, I think it will be good to note that there are certain rare 
> exceptions (when the buffer is the minibuffer or the sole other buffer), 
> and that kill-buffer will skip killing them and simply return nil 
> without complaint.

OK, we can extend the doc string with these caveats.

> > But is this the only request
> > here?  If not, please elaborate.
> 
> The situation with threads seems different because it can affect, 
> potentially, (almost) any Lisp code and almost any buffer that a Lisp 
> program expects to kill. The more often threads are used, the higher the 
> odds will be.
> 
> And the consequences seem less severe, conceptually, than killing one of 
> the exceptions mentioned above. The exception for threads seems to have 
> been made as a matter of implementation convenience, so it's worth 
> revisiting.

If we want to kill a buffer that is the current buffer of some thread,
we must do the same thing we do when killing the buffer that is
current in the thread which calls kill-buffer: replace it with some
other buffer, if possible:

  /* Make this buffer not be current.  Exit if it is the sole visible
     buffer.  */
  if (b == current_buffer)
    {
      tem = Fother_buffer (buffer, Qnil, Qnil);
      Fset_buffer (tem);
      if (b == current_buffer)
	return Qnil;
    }

> To reiterate, having the buffer killed (and the associated thread with 
> it) seems preferable - perhaps after allowing the thread to handle the 
> attempt.

You forget that the other thread (the one which uses the buffer as the
current) cannot do anything because it is blocked trying to take the
global lock, while this thread, the one which called kill-buffer,
runs.  The only way to allow that thread to do anything would be to
defer to that thread to do the killing, and yield the global lock to
it so it could actually do that.  But this requires infrastructure we
don't have, because we cannot currently yield to a specific thread.

> And/or if the killing does not happen, showing a warning or an error
> would be an improvement.

We could signal an error, yes.  But it sounds too drastic to me.