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

From: Dmitry Gutov <[email-masked]>
Date: Thu, 13 Mar 2025 21:30:15 +0200

On 13/03/2025 16:58, Eli Zaretskii wrote:

>> 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.

Great - at least being aware of those cases should help.

>> 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;
>      }

Makes sense, but it's probably not a good idea for threads. Same reason: 
unpredictability.

If the direct kill-buffer call swaps the buffer under you, it's somewhat 
odd, but at least it's predictable and can be debugged. Having a 
different thread do that do your execution flow at a random time is 
quite another thing.

>> 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.

Yeah, I was imagining something like that. If it's not possible with the 
current infra, maybe leave a TODO?

>> 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.

Or a warning, or an entry in *Messages*, at least. Anything's better 
than silent noop. And you can't examine the thread's current buffer in 
Lisp, to find the conflicting thread another way from Lisp (e.g. for 
print-debugging).