GNU bug report logs - #76969
kill-buffer fails silently when a thread exists where it's current

Previous Next

Package: emacs;

Reported by: Dmitry Gutov <dmitry <at> gutov.dev>

Date: Wed, 12 Mar 2025 01:17:01 UTC

Severity: normal

To reply to this bug, email your comments to 76969 AT debbugs.gnu.org.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Wed, 12 Mar 2025 01:17:01 GMT) Full text and rfc822 format available.

Acknowledgement sent to Dmitry Gutov <dmitry <at> gutov.dev>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Wed, 12 Mar 2025 01:17:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Dmitry Gutov <dmitry <at> gutov.dev>
To: bug-gnu-emacs <at> gnu.org
Subject: kill-buffer fails silently when a thread exists where it's current
Date: Wed, 12 Mar 2025 03:16:08 +0200
This stems from a private bug report about diff-hl when it uses a thread 
to update the fringe highlights.

To reproduce:

  1. Install, enable diff-hl-mode.
  2. (setq diff-hl-update-async t)
  3. Visit a code buffer in a (e.g.) Git repo, save it.
  4. Make an edit, don't save.
  5. Evaluate this:

  (progn
    (save-buffer)
    (kill-buffer))

Current:

The result is that the buffer is not killed. And that happens silently, 
no errors or anything. Only further examination and reading the sources 
led to understanding the reason.

Expected:

It probably should be killed. After the thread is signaled some error 
(perhaps) and is aborted. And if the buffer can't be killed, 
'kill-buffer' itself should exit with an error.

As I understand the behavior is old (2013) and comes from the 
'thread_check_current_buffer' call in Fkill_buffer. But it's not 
mentioned in kill-buffer's docstring or the manual.

Alternative repro:

If you don't have diff-hl installed, you could replace 1 and 2 with:

  (defun foo ()
    (make-thread (lambda () (sleep-for 0.1))))

  (add-hook 'after-save-hook #'foo)




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Thu, 13 Mar 2025 09:48:01 GMT) Full text and rfc822 format available.

Message #8 received at 76969 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Dmitry Gutov <dmitry <at> gutov.dev>
Cc: 76969 <at> debbugs.gnu.org
Subject: Re: bug#76969: kill-buffer fails silently when a thread exists where
 it's current
Date: Thu, 13 Mar 2025 11:47:44 +0200
> Date: Wed, 12 Mar 2025 03:16:08 +0200
> From: Dmitry Gutov <dmitry <at> gutov.dev>
> 
> This stems from a private bug report about diff-hl when it uses a thread 
> to update the fringe highlights.
> 
> To reproduce:
> 
>    1. Install, enable diff-hl-mode.
>    2. (setq diff-hl-update-async t)
>    3. Visit a code buffer in a (e.g.) Git repo, save it.
>    4. Make an edit, don't save.
>    5. Evaluate this:
> 
>    (progn
>      (save-buffer)
>      (kill-buffer))
> 
> Current:
> 
> The result is that the buffer is not killed. And that happens silently, 
> no errors or anything. Only further examination and reading the sources 
> led to understanding the reason.
> 
> Expected:
> 
> It probably should be killed. After the thread is signaled some error 
> (perhaps) and is aborted. And if the buffer can't be killed, 
> 'kill-buffer' itself should exit with an error.
> 
> As I understand the behavior is old (2013) and comes from the 
> 'thread_check_current_buffer' call in Fkill_buffer. But it's not 
> mentioned in kill-buffer's docstring or the manual.

There are other reasons which preclude killing a buffer that aren't
mentioned in the doc string.  For example, this:

  /* 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.  But is this the only request
here?  If not, please elaborate.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Thu, 13 Mar 2025 12:11:02 GMT) Full text and rfc822 format available.

Message #11 received at 76969 <at> debbugs.gnu.org (full text, mbox):

From: Dmitry Gutov <dmitry <at> gutov.dev>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 76969 <at> debbugs.gnu.org
Subject: Re: bug#76969: kill-buffer fails silently when a thread exists where
 it's current
Date: Thu, 13 Mar 2025 14:10:36 +0200
On 13/03/2025 11:47, Eli Zaretskii wrote:

>> Expected:
>>
>> It probably should be killed. After the thread is signaled some error
>> (perhaps) and is aborted. And if the buffer can't be killed,
>> 'kill-buffer' itself should exit with an error.
>>
>> As I understand the behavior is old (2013) and comes from the
>> 'thread_check_current_buffer' call in Fkill_buffer. But it's not
>> mentioned in kill-buffer's docstring or the manual.
> 
> 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.

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

To reiterate, having the buffer killed (and the associated thread with 
it) seems preferable - perhaps after allowing the thread to handle the 
attempt. And/or if the killing does not happen, showing a warning or an 
error would be an improvement.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Thu, 13 Mar 2025 15:00:02 GMT) Full text and rfc822 format available.

Message #14 received at 76969 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Dmitry Gutov <dmitry <at> gutov.dev>
Cc: 76969 <at> debbugs.gnu.org
Subject: Re: bug#76969: kill-buffer fails silently when a thread exists where
 it's current
Date: Thu, 13 Mar 2025 16:58:56 +0200
> Date: Thu, 13 Mar 2025 14:10:36 +0200
> Cc: 76969 <at> debbugs.gnu.org
> From: Dmitry Gutov <dmitry <at> gutov.dev>
> 
> 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.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Thu, 13 Mar 2025 19:31:02 GMT) Full text and rfc822 format available.

Message #17 received at 76969 <at> debbugs.gnu.org (full text, mbox):

From: Dmitry Gutov <dmitry <at> gutov.dev>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: Spencer Baugh <sbaugh <at> janestreet.com>, 76969 <at> debbugs.gnu.org
Subject: Re: bug#76969: kill-buffer fails silently when a thread exists where
 it's current
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).




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Thu, 13 Mar 2025 20:17:02 GMT) Full text and rfc822 format available.

Message #20 received at 76969 <at> debbugs.gnu.org (full text, mbox):

From: Spencer Baugh <sbaugh <at> janestreet.com>
To: Dmitry Gutov <dmitry <at> gutov.dev>
Cc: Eli Zaretskii <eliz <at> gnu.org>, 76969 <at> debbugs.gnu.org
Subject: Re: bug#76969: kill-buffer fails silently when a thread exists
 where it's current
Date: Thu, 13 Mar 2025 16:16:46 -0400
Dmitry Gutov <dmitry <at> gutov.dev> writes:
> On 13/03/2025 16:58, Eli Zaretskii wrote:
>>> 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).

Note that if you try kill a buffer which is the process-buffer of some
process:

- process-kill-buffer-query-function will query the user whether they
  want to proceed.

- If the user decides to proceed, then the process in that buffer will
  simply be killed with SIGHUP.

Perhaps we should do a similar thing for threads?

- Add a new kill-buffer-query-functions which checks if the buffer being
  killed is the current-buffer of any threads, and if so, queries the
  user if they want to proceed.

- If the user decides to proceed, then do something like killing the
  thread with SIGHUP.  Probably call thread-signal on the thread.  We'll
  still need to switch the thread's current-buffer to a new buffer,
  since the thread could choose to handle the signal and continue
  executing.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Thu, 13 Mar 2025 20:31:02 GMT) Full text and rfc822 format available.

Message #23 received at 76969 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Dmitry Gutov <dmitry <at> gutov.dev>
Cc: sbaugh <at> janestreet.com, 76969 <at> debbugs.gnu.org
Subject: Re: bug#76969: kill-buffer fails silently when a thread exists where
 it's current
Date: Thu, 13 Mar 2025 22:29:55 +0200
> Date: Thu, 13 Mar 2025 21:30:15 +0200
> Cc: 76969 <at> debbugs.gnu.org, Spencer Baugh <sbaugh <at> janestreet.com>
> From: Dmitry Gutov <dmitry <at> gutov.dev>
> 
> On 13/03/2025 16:58, Eli Zaretskii wrote:
> 
> > 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.

Suppose a process filter or sentinel, or a timer does that -- is that
any different? should we forbid that?

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

I'd still prefer to kill the buffer, just more safely.  I mean, how is
this different from killing a buffer that is displayed in one or more
windows?  We do handle that.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Thu, 13 Mar 2025 20:39:02 GMT) Full text and rfc822 format available.

Message #26 received at 76969 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Spencer Baugh <sbaugh <at> janestreet.com>
Cc: dmitry <at> gutov.dev, 76969 <at> debbugs.gnu.org
Subject: Re: bug#76969: kill-buffer fails silently when a thread exists
 where it's current
Date: Thu, 13 Mar 2025 22:38:15 +0200
> From: Spencer Baugh <sbaugh <at> janestreet.com>
> Cc: Eli Zaretskii <eliz <at> gnu.org>,  76969 <at> debbugs.gnu.org
> Date: Thu, 13 Mar 2025 16:16:46 -0400
> 
> Note that if you try kill a buffer which is the process-buffer of some
> process:
> 
> - process-kill-buffer-query-function will query the user whether they
>   want to proceed.
> 
> - If the user decides to proceed, then the process in that buffer will
>   simply be killed with SIGHUP.
> 
> Perhaps we should do a similar thing for threads?

That still leaves the issue of what to do if the user says to kill.
The basic problem of being unable to leave a thread without a
current-buffer still stands, and needs to be solved.

> - Add a new kill-buffer-query-functions which checks if the buffer being
>   killed is the current-buffer of any threads, and if so, queries the
>   user if they want to proceed.
> 
> - If the user decides to proceed, then do something like killing the
>   thread with SIGHUP.  Probably call thread-signal on the thread.  We'll
>   still need to switch the thread's current-buffer to a new buffer,
>   since the thread could choose to handle the signal and continue
>   executing.

Same here: these measures don't solve the technical reason why killing
buffers that are current in some other thread was disallowed.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Thu, 13 Mar 2025 21:23:02 GMT) Full text and rfc822 format available.

Message #29 received at 76969 <at> debbugs.gnu.org (full text, mbox):

From: Dmitry Gutov <dmitry <at> gutov.dev>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: sbaugh <at> janestreet.com, 76969 <at> debbugs.gnu.org
Subject: Re: bug#76969: kill-buffer fails silently when a thread exists where
 it's current
Date: Thu, 13 Mar 2025 23:21:59 +0200
On 13/03/2025 22:29, Eli Zaretskii wrote:

>> 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.
> 
> Suppose a process filter or sentinel, or a timer does that -- is that
> any different? should we forbid that?

Yeah, I think a timer or a sentinel killing a non-"private" buffer would 
be a programmer error. Still, in that case the caller can compare the 
kill-buffer argument to current-buffer and see that they're equal - so 
print-debugging would still work (or edebug, etc).

>>>> 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).
> 
> I'd still prefer to kill the buffer, just more safely.  I mean, how is
> this different from killing a buffer that is displayed in one or more
> windows?  We do handle that.

Okay, how about Spencer's suggestion (maybe modulo the 
kill-buffer-query-functions part)? When we try to kill a buffer that is 
current to a thread, we first send a signal to that thread (to be 
handled async), then switch that thread's buffer to another (*). Do that 
check in all threads, then kill the buffer.

This way the Lisp code running in there would be notified about the 
change - and probably crash right away unless it expects this specific 
error. Better than a silent change.

(*) Though we should probably do that after and if all 
kill-buffer-query-functions have run with positive result.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Fri, 14 Mar 2025 07:27:02 GMT) Full text and rfc822 format available.

Message #32 received at 76969 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Dmitry Gutov <dmitry <at> gutov.dev>
Cc: sbaugh <at> janestreet.com, 76969 <at> debbugs.gnu.org
Subject: Re: bug#76969: kill-buffer fails silently when a thread exists where
 it's current
Date: Fri, 14 Mar 2025 09:26:03 +0200
> Date: Thu, 13 Mar 2025 23:21:59 +0200
> Cc: 76969 <at> debbugs.gnu.org, sbaugh <at> janestreet.com
> From: Dmitry Gutov <dmitry <at> gutov.dev>
> 
> On 13/03/2025 22:29, Eli Zaretskii wrote:
> 
> >> 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.
> > 
> > Suppose a process filter or sentinel, or a timer does that -- is that
> > any different? should we forbid that?
> 
> Yeah, I think a timer or a sentinel killing a non-"private" buffer would 
> be a programmer error. Still, in that case the caller can compare the 
> kill-buffer argument to current-buffer and see that they're equal - so 
> print-debugging would still work (or edebug, etc).

If it's important, we could provide a thread-buffer function to return
the current buffer of a thread.  Should be easy to implement.

> >>>> 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).
> > 
> > I'd still prefer to kill the buffer, just more safely.  I mean, how is
> > this different from killing a buffer that is displayed in one or more
> > windows?  We do handle that.
> 
> Okay, how about Spencer's suggestion (maybe modulo the 
> kill-buffer-query-functions part)? When we try to kill a buffer that is 
> current to a thread, we first send a signal to that thread (to be 
> handled async), then switch that thread's buffer to another (*). Do that 
> check in all threads, then kill the buffer.

If you signal a thread that wasn't prepared to catch the signal, the
thread will terminate.  How many thread functions out there are
prepared to catch signals?  This would require every thread function
to be wrapped in condition-case with a non-trivial handler.  And what
do we expect the handler to do? probably what the loop in kill-buffer
that replaces the current buffer will do anyway, or perhaps just quit
in a more orderly fashion?

Maybe this could be an optional feature, though.  That is, the
make-thread call could accept an additional optional argument to
indicate that this thread would like to be signaled if its current
buffer is ever killed.

Patches welcome.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Sat, 15 Mar 2025 01:29:01 GMT) Full text and rfc822 format available.

Message #35 received at 76969 <at> debbugs.gnu.org (full text, mbox):

From: Dmitry Gutov <dmitry <at> gutov.dev>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: sbaugh <at> janestreet.com, 76969 <at> debbugs.gnu.org
Subject: Re: bug#76969: kill-buffer fails silently when a thread exists where
 it's current
Date: Sat, 15 Mar 2025 03:27:52 +0200
On 14/03/2025 09:26, Eli Zaretskii wrote:

>>> I'd still prefer to kill the buffer, just more safely.  I mean, how is
>>> this different from killing a buffer that is displayed in one or more
>>> windows?  We do handle that.
>>
>> Okay, how about Spencer's suggestion (maybe modulo the
>> kill-buffer-query-functions part)? When we try to kill a buffer that is
>> current to a thread, we first send a signal to that thread (to be
>> handled async), then switch that thread's buffer to another (*). Do that
>> check in all threads, then kill the buffer.
> 
> If you signal a thread that wasn't prepared to catch the signal, the
> thread will terminate.

I think that is fine: terminate if the thread is not prepared to handle 
a buffer switch, but also allow it to handle it.

> How many thread functions out there are
> prepared to catch signals?  This would require every thread function
> to be wrapped in condition-case with a non-trivial handler.

IME that is already a requirement - we don't have other straightforward 
ways to make sure the thread's code doesn't error, and or notifying the 
user otherwise. No automatic printing of such errors. thread-join 
doesn't raise a signal if the thread ended with a error either (which 
seems like a standard in other languages that I've worked with).

More importantly, having a thread die seems safer than forcing an 
unexpected buffer change on it - this can lead to visual effects being 
applied to a wrong buffer, or more importantly, to data loss.

> And what
> do we expect the handler to do? probably what the loop in kill-buffer
> that replaces the current buffer will do anyway, or perhaps just quit
> in a more orderly fashion?

Some parts of the function might actually be safe against buffer change 
(if the important context/data had been captured) - e.g. if the function 
created a temp buffer later anyway. Some parts couldn't handle it, but 
an error handled would be the place to perform orderly cleanup.

> Maybe this could be an optional feature, though.  That is, the
> make-thread call could accept an additional optional argument to
> indicate that this thread would like to be signaled if its current
> buffer is ever killed.

And I guess otherwise the buffer wouldn't be allowed to be killed?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Sat, 15 Mar 2025 09:31:01 GMT) Full text and rfc822 format available.

Message #38 received at 76969 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Dmitry Gutov <dmitry <at> gutov.dev>
Cc: sbaugh <at> janestreet.com, 76969 <at> debbugs.gnu.org
Subject: Re: bug#76969: kill-buffer fails silently when a thread exists where
 it's current
Date: Sat, 15 Mar 2025 11:30:31 +0200
> Date: Sat, 15 Mar 2025 03:27:52 +0200
> Cc: 76969 <at> debbugs.gnu.org, sbaugh <at> janestreet.com
> From: Dmitry Gutov <dmitry <at> gutov.dev>
> 
> On 14/03/2025 09:26, Eli Zaretskii wrote:
> 
> > If you signal a thread that wasn't prepared to catch the signal, the
> > thread will terminate.
> 
> I think that is fine: terminate if the thread is not prepared to handle 
> a buffer switch, but also allow it to handle it.

I think you only have in mind threads whose function is processing the
buffer that will be killed.  But threads can do other jobs, including
jobs which are utterly unrelated to the current buffer, in which case
terminating them is too drastic and unjustified.

> > How many thread functions out there are
> > prepared to catch signals?  This would require every thread function
> > to be wrapped in condition-case with a non-trivial handler.
> 
> IME that is already a requirement - we don't have other straightforward 
> ways to make sure the thread's code doesn't error, and or notifying the 
> user otherwise. No automatic printing of such errors. thread-join 
> doesn't raise a signal if the thread ended with a error either (which 
> seems like a standard in other languages that I've worked with).

Having a signal delivered from another thread is unusual, so saying
it's a requirement is IMO far-fetched.  It's like saying that every
function or command should protect itself from quitting by the user.

> More importantly, having a thread die seems safer than forcing an 
> unexpected buffer change on it - this can lead to visual effects being 
> applied to a wrong buffer, or more importantly, to data loss.

You again are thinking about only a specific class of thread
functions.  Compare this with killing a buffer shown in the selected
window and in some other windows, and draw your conclusions from the
similarity of these two situations.

> > Maybe this could be an optional feature, though.  That is, the
> > make-thread call could accept an additional optional argument to
> > indicate that this thread would like to be signaled if its current
> > buffer is ever killed.
> 
> And I guess otherwise the buffer wouldn't be allowed to be killed?

I meant to kill it in any case.  If that ruins the thread's job, it
would be the same programmatic error as when a buffer is killed by a
timer.

We could also have a buffer-local variable which prevents buffer from
being killed.  A thread which cannot allow its current buffer to be
killed could then set this variable non-nil while the processing which
requires that is in progress.





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Sat, 15 Mar 2025 13:56:06 GMT) Full text and rfc822 format available.

Message #41 received at 76969 <at> debbugs.gnu.org (full text, mbox):

From: Dmitry Gutov <dmitry <at> gutov.dev>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: sbaugh <at> janestreet.com, 76969 <at> debbugs.gnu.org
Subject: Re: bug#76969: kill-buffer fails silently when a thread exists where
 it's current
Date: Sat, 15 Mar 2025 15:55:00 +0200
On 15/03/2025 11:30, Eli Zaretskii wrote:
>> Date: Sat, 15 Mar 2025 03:27:52 +0200
>> Cc: 76969 <at> debbugs.gnu.org, sbaugh <at> janestreet.com
>> From: Dmitry Gutov <dmitry <at> gutov.dev>
>>
>> On 14/03/2025 09:26, Eli Zaretskii wrote:
>>
>>> If you signal a thread that wasn't prepared to catch the signal, the
>>> thread will terminate.
>>
>> I think that is fine: terminate if the thread is not prepared to handle
>> a buffer switch, but also allow it to handle it.
> 
> I think you only have in mind threads whose function is processing the
> buffer that will be killed.  But threads can do other jobs, including
> jobs which are utterly unrelated to the current buffer, in which case
> terminating them is too drastic and unjustified.

My view is that there would be a bunch of threads running concurrently, 
most of them "harmless" - but there would be some that modify buffer 
contents. Having the buffer switched under them could lead to data loss 
in another buffer which happened to be next in the list, with the 
changes quite possibly saved to disk.

We can't really distinguish between these kinds of threads from the 
outside, so the general model would need to err on the side of safety.

>>> How many thread functions out there are
>>> prepared to catch signals?  This would require every thread function
>>> to be wrapped in condition-case with a non-trivial handler.
>>
>> IME that is already a requirement - we don't have other straightforward
>> ways to make sure the thread's code doesn't error, and or notifying the
>> user otherwise. No automatic printing of such errors. thread-join
>> doesn't raise a signal if the thread ended with a error either (which
>> seems like a standard in other languages that I've worked with).
> 
> Having a signal delivered from another thread is unusual, so saying
> it's a requirement is IMO far-fetched.  It's like saying that every
> function or command should protect itself from quitting by the user.

Interactive commands, timers and functions in the main thread go through 
the usual error handling - which at least results in the error being 
printed to Messages. More, if the debugger is toggled on.

>> More importantly, having a thread die seems safer than forcing an
>> unexpected buffer change on it - this can lead to visual effects being
>> applied to a wrong buffer, or more importantly, to data loss.
> 
> You again are thinking about only a specific class of thread
> functions.  Compare this with killing a buffer shown in the selected
> window and in some other windows, and draw your conclusions from the
> similarity of these two situations.

Not sure what thread function you're thinking of in this case.

>>> Maybe this could be an optional feature, though.  That is, the
>>> make-thread call could accept an additional optional argument to
>>> indicate that this thread would like to be signaled if its current
>>> buffer is ever killed.
>>
>> And I guess otherwise the buffer wouldn't be allowed to be killed?
> 
> I meant to kill it in any case.  If that ruins the thread's job, it
> would be the same programmatic error as when a buffer is killed by a
> timer.
> 
> We could also have a buffer-local variable which prevents buffer from
> being killed.  A thread which cannot allow its current buffer to be
> killed could then set this variable non-nil while the processing which
> requires that is in progress.

It might slightly alter the thread's job, in an unpredictable way - many 
Lisp functions depend on buffer variables, and switching the current 
buffer from under them would switch those values unpredictably too.

I'm not sure an average Lisp coder dabbling into threads would 
anticipate those kind of problems in advance.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Sat, 15 Mar 2025 14:07:02 GMT) Full text and rfc822 format available.

Message #44 received at 76969 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Dmitry Gutov <dmitry <at> gutov.dev>
Cc: sbaugh <at> janestreet.com, 76969 <at> debbugs.gnu.org
Subject: Re: bug#76969: kill-buffer fails silently when a thread exists where
 it's current
Date: Sat, 15 Mar 2025 16:06:31 +0200
> Date: Sat, 15 Mar 2025 15:55:00 +0200
> Cc: 76969 <at> debbugs.gnu.org, sbaugh <at> janestreet.com
> From: Dmitry Gutov <dmitry <at> gutov.dev>
> 
> On 15/03/2025 11:30, Eli Zaretskii wrote:
> >> Date: Sat, 15 Mar 2025 03:27:52 +0200
> >> Cc: 76969 <at> debbugs.gnu.org, sbaugh <at> janestreet.com
> >> From: Dmitry Gutov <dmitry <at> gutov.dev>
> >>
> >> On 14/03/2025 09:26, Eli Zaretskii wrote:
> >>
> >>> If you signal a thread that wasn't prepared to catch the signal, the
> >>> thread will terminate.
> >>
> >> I think that is fine: terminate if the thread is not prepared to handle
> >> a buffer switch, but also allow it to handle it.
> > 
> > I think you only have in mind threads whose function is processing the
> > buffer that will be killed.  But threads can do other jobs, including
> > jobs which are utterly unrelated to the current buffer, in which case
> > terminating them is too drastic and unjustified.
> 
> My view is that there would be a bunch of threads running concurrently, 
> most of them "harmless" - but there would be some that modify buffer 
> contents. Having the buffer switched under them could lead to data loss 
> in another buffer which happened to be next in the list, with the 
> changes quite possibly saved to disk.

Yes.  My point is that terminating each such thread because its
current buffer was killed might be overkill for threads which don't
care about their current-buffer.

> We can't really distinguish between these kinds of threads from the 
> outside, so the general model would need to err on the side of safety.

The side of safety in my book is not to kill the buffer.  You
suggested instead to signal the thread, which would terminate it, and
I consider that not erring on the side of safety.

> >> More importantly, having a thread die seems safer than forcing an
> >> unexpected buffer change on it - this can lead to visual effects being
> >> applied to a wrong buffer, or more importantly, to data loss.
> > 
> > You again are thinking about only a specific class of thread
> > functions.  Compare this with killing a buffer shown in the selected
> > window and in some other windows, and draw your conclusions from the
> > similarity of these two situations.
> 
> Not sure what thread function you're thinking of in this case.

In this analogy, showing buffers is that "thread function".

> > We could also have a buffer-local variable which prevents buffer from
> > being killed.  A thread which cannot allow its current buffer to be
> > killed could then set this variable non-nil while the processing which
> > requires that is in progress.
> 
> It might slightly alter the thread's job, in an unpredictable way - many 
> Lisp functions depend on buffer variables, and switching the current 
> buffer from under them would switch those values unpredictably too.
> 
> I'm not sure an average Lisp coder dabbling into threads would 
> anticipate those kind of problems in advance.

I cannot connect your response with my suggestion to introduce a new
buffer-local variable which, if non-nil, would prevent the buffer from
being killed if it's the current-buffer of some thread.  Perhaps
there's some kind of misunderstanding.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Sat, 15 Mar 2025 23:30:02 GMT) Full text and rfc822 format available.

Message #47 received at 76969 <at> debbugs.gnu.org (full text, mbox):

From: Dmitry Gutov <dmitry <at> gutov.dev>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: sbaugh <at> janestreet.com, 76969 <at> debbugs.gnu.org
Subject: Re: bug#76969: kill-buffer fails silently when a thread exists where
 it's current
Date: Sun, 16 Mar 2025 01:29:00 +0200
On 15/03/2025 16:06, Eli Zaretskii wrote:

>> My view is that there would be a bunch of threads running concurrently,
>> most of them "harmless" - but there would be some that modify buffer
>> contents. Having the buffer switched under them could lead to data loss
>> in another buffer which happened to be next in the list, with the
>> changes quite possibly saved to disk.
> 
> Yes.  My point is that terminating each such thread because its
> current buffer was killed might be overkill for threads which don't
> care about their current-buffer.

I wonder if the set of functions which behave safely under such 
conditions is large enough for this to be the default.

>> We can't really distinguish between these kinds of threads from the
>> outside, so the general model would need to err on the side of safety.
> 
> The side of safety in my book is not to kill the buffer.  You
> suggested instead to signal the thread, which would terminate it, and
> I consider that not erring on the side of safety.

Okay... then by default we will not kill the buffer, but allow threads 
to opt into allowing the buffer to be killed, preceded by a signal?

That sounds safe enough.

But the downside is we retain the current issue, right? As described in 
the original report.

If only some threads opt into a different behavior, in general things 
stay the same.

>>>> More importantly, having a thread die seems safer than forcing an
>>>> unexpected buffer change on it - this can lead to visual effects being
>>>> applied to a wrong buffer, or more importantly, to data loss.
>>>
>>> You again are thinking about only a specific class of thread
>>> functions.  Compare this with killing a buffer shown in the selected
>>> window and in some other windows, and draw your conclusions from the
>>> similarity of these two situations.
>>
>> Not sure what thread function you're thinking of in this case.
> 
> In this analogy, showing buffers is that "thread function".

Okay. So take just a function that is going to display a buffer. If the 
buffer is killed, wouldn't it be better for the

>>> We could also have a buffer-local variable which prevents buffer from
>>> being killed.  A thread which cannot allow its current buffer to be
>>> killed could then set this variable non-nil while the processing which
>>> requires that is in progress.
>>
>> It might slightly alter the thread's job, in an unpredictable way - many
>> Lisp functions depend on buffer variables, and switching the current
>> buffer from under them would switch those values unpredictably too.
>>
>> I'm not sure an average Lisp coder dabbling into threads would
>> anticipate those kind of problems in advance.
> 
> I cannot connect your response with my suggestion to introduce a new
> buffer-local variable which, if non-nil, would prevent the buffer from
> being killed if it's the current-buffer of some thread.  Perhaps
> there's some kind of misunderstanding.

I was unclear if you think it's better to always kill the buffer, or to 
have that behavior opt-in.

If the buffer is killed, though, I believe it is better to signal its 
threads. Having the buffer substituted can easily get unnoticed, and 
would be hard to debug.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Sun, 16 Mar 2025 06:08:01 GMT) Full text and rfc822 format available.

Message #50 received at 76969 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Dmitry Gutov <dmitry <at> gutov.dev>
Cc: sbaugh <at> janestreet.com, 76969 <at> debbugs.gnu.org
Subject: Re: bug#76969: kill-buffer fails silently when a thread exists where
 it's current
Date: Sun, 16 Mar 2025 08:07:18 +0200
> Date: Sun, 16 Mar 2025 01:29:00 +0200
> Cc: 76969 <at> debbugs.gnu.org, sbaugh <at> janestreet.com
> From: Dmitry Gutov <dmitry <at> gutov.dev>
> 
> On 15/03/2025 16:06, Eli Zaretskii wrote:
> 
> >> My view is that there would be a bunch of threads running concurrently,
> >> most of them "harmless" - but there would be some that modify buffer
> >> contents. Having the buffer switched under them could lead to data loss
> >> in another buffer which happened to be next in the list, with the
> >> changes quite possibly saved to disk.
> > 
> > Yes.  My point is that terminating each such thread because its
> > current buffer was killed might be overkill for threads which don't
> > care about their current-buffer.
> 
> I wonder if the set of functions which behave safely under such 
> conditions is large enough for this to be the default.

If someone would like to make a survey of use patterns of Lisp
threads, I think it will be useful in more than one way.

> >> We can't really distinguish between these kinds of threads from the
> >> outside, so the general model would need to err on the side of safety.
> > 
> > The side of safety in my book is not to kill the buffer.  You
> > suggested instead to signal the thread, which would terminate it, and
> > I consider that not erring on the side of safety.
> 
> Okay... then by default we will not kill the buffer, but allow threads 
> to opt into allowing the buffer to be killed, preceded by a signal?
> 
> That sounds safe enough.

I actually thought the other way around: kill the buffer and deliver
the signal, unless a special buffer-local variable is non-nil.

> But the downside is we retain the current issue, right? As described in 
> the original report.

With your default, yes.  OTOH, the original report didn't explain why
not killing the buffer is a problem.  In general, any Lisp program
that calls kill-buffer should be prepared to deal with the fact that
the buffer might not be killed, due to any of the possible reasons
which prevent that already, even if other threads are no involved.

> 
> If only some threads opt into a different behavior, in general things 
> stay the same.
> 
> >>>> More importantly, having a thread die seems safer than forcing an
> >>>> unexpected buffer change on it - this can lead to visual effects being
> >>>> applied to a wrong buffer, or more importantly, to data loss.
> >>>
> >>> You again are thinking about only a specific class of thread
> >>> functions.  Compare this with killing a buffer shown in the selected
> >>> window and in some other windows, and draw your conclusions from the
> >>> similarity of these two situations.
> >>
> >> Not sure what thread function you're thinking of in this case.
> > 
> > In this analogy, showing buffers is that "thread function".
> 
> Okay. So take just a function that is going to display a buffer. If the 
> buffer is killed, wouldn't it be better for the

This sentence seems to be unfinished.

> I was unclear if you think it's better to always kill the buffer, or to 
> have that behavior opt-in.

That's because I don't yet have a firm opinion.  And to some extent
that depends on the optional features we will implement:

 . is signal always delivered or only to threads which said they want it?
 . do we add a buffer-local dont-ever-kill-my-buffer variable?

> If the buffer is killed, though, I believe it is better to signal its 
> threads. Having the buffer substituted can easily get unnoticed, and 
> would be hard to debug.

And I tend to prefer leaving this to the Lisp program which starts the
thread, via an additional argument to make-thread.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Mon, 17 Mar 2025 19:18:02 GMT) Full text and rfc822 format available.

Message #53 received at 76969 <at> debbugs.gnu.org (full text, mbox):

From: Spencer Baugh <sbaugh <at> janestreet.com>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: Dmitry Gutov <dmitry <at> gutov.dev>, 76969 <at> debbugs.gnu.org
Subject: Re: bug#76969: kill-buffer fails silently when a thread exists
 where it's current
Date: Mon, 17 Mar 2025 15:17:09 -0400
Eli Zaretskii <eliz <at> gnu.org> writes:
>> Date: Sun, 16 Mar 2025 01:29:00 +0200
>> Cc: 76969 <at> debbugs.gnu.org, sbaugh <at> janestreet.com
>> From: Dmitry Gutov <dmitry <at> gutov.dev>
>> 
>> On 15/03/2025 16:06, Eli Zaretskii wrote:
>> 
>> >> My view is that there would be a bunch of threads running concurrently,
>> >> most of them "harmless" - but there would be some that modify buffer
>> >> contents. Having the buffer switched under them could lead to data loss
>> >> in another buffer which happened to be next in the list, with the
>> >> changes quite possibly saved to disk.
>> > 
>> > Yes.  My point is that terminating each such thread because its
>> > current buffer was killed might be overkill for threads which don't
>> > care about their current-buffer.
>> 
>> I wonder if the set of functions which behave safely under such 
>> conditions is large enough for this to be the default.
>
> If someone would like to make a survey of use patterns of Lisp
> threads, I think it will be useful in more than one way.
>
>> >> We can't really distinguish between these kinds of threads from the
>> >> outside, so the general model would need to err on the side of safety.
>> > 
>> > The side of safety in my book is not to kill the buffer.  You
>> > suggested instead to signal the thread, which would terminate it, and
>> > I consider that not erring on the side of safety.
>> 
>> Okay... then by default we will not kill the buffer, but allow threads 
>> to opt into allowing the buffer to be killed, preceded by a signal?
>> 
>> That sounds safe enough.
>
> I actually thought the other way around: kill the buffer and deliver
> the signal, unless a special buffer-local variable is non-nil.

I agree.  I think "kill the buffer and deliver the signal" makes sense
as a default behavior, suppressed when a special buffer-local is
non-nil.

I think then we wouldn't need a new argument for make-thread, because
threads which care about suppressing kill-buffer can simply set this new
buffer-local variable.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Mon, 17 Mar 2025 19:42:01 GMT) Full text and rfc822 format available.

Message #56 received at 76969 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Spencer Baugh <sbaugh <at> janestreet.com>
Cc: dmitry <at> gutov.dev, 76969 <at> debbugs.gnu.org
Subject: Re: bug#76969: kill-buffer fails silently when a thread exists
 where it's current
Date: Mon, 17 Mar 2025 21:41:19 +0200
> From: Spencer Baugh <sbaugh <at> janestreet.com>
> Cc: Dmitry Gutov <dmitry <at> gutov.dev>,  76969 <at> debbugs.gnu.org
> Date: Mon, 17 Mar 2025 15:17:09 -0400
> 
> Eli Zaretskii <eliz <at> gnu.org> writes:
> >> Date: Sun, 16 Mar 2025 01:29:00 +0200
> >> Cc: 76969 <at> debbugs.gnu.org, sbaugh <at> janestreet.com
> >> From: Dmitry Gutov <dmitry <at> gutov.dev>
> >> 
> >> On 15/03/2025 16:06, Eli Zaretskii wrote:
> >> 
> >> >> My view is that there would be a bunch of threads running concurrently,
> >> >> most of them "harmless" - but there would be some that modify buffer
> >> >> contents. Having the buffer switched under them could lead to data loss
> >> >> in another buffer which happened to be next in the list, with the
> >> >> changes quite possibly saved to disk.
> >> > 
> >> > Yes.  My point is that terminating each such thread because its
> >> > current buffer was killed might be overkill for threads which don't
> >> > care about their current-buffer.
> >> 
> >> I wonder if the set of functions which behave safely under such 
> >> conditions is large enough for this to be the default.
> >
> > If someone would like to make a survey of use patterns of Lisp
> > threads, I think it will be useful in more than one way.
> >
> >> >> We can't really distinguish between these kinds of threads from the
> >> >> outside, so the general model would need to err on the side of safety.
> >> > 
> >> > The side of safety in my book is not to kill the buffer.  You
> >> > suggested instead to signal the thread, which would terminate it, and
> >> > I consider that not erring on the side of safety.
> >> 
> >> Okay... then by default we will not kill the buffer, but allow threads 
> >> to opt into allowing the buffer to be killed, preceded by a signal?
> >> 
> >> That sounds safe enough.
> >
> > I actually thought the other way around: kill the buffer and deliver
> > the signal, unless a special buffer-local variable is non-nil.
> 
> I agree.  I think "kill the buffer and deliver the signal" makes sense
> as a default behavior, suppressed when a special buffer-local is
> non-nil.
> 
> I think then we wouldn't need a new argument for make-thread, because
> threads which care about suppressing kill-buffer can simply set this new
> buffer-local variable.

The question still stands whether we should deliver the signal to
threads which didn't indicate they expect a signal.  Don't forget that
the target thread could be the main thread.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Tue, 18 Mar 2025 00:09:03 GMT) Full text and rfc822 format available.

Message #59 received at 76969 <at> debbugs.gnu.org (full text, mbox):

From: Dmitry Gutov <dmitry <at> gutov.dev>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: sbaugh <at> janestreet.com, 76969 <at> debbugs.gnu.org
Subject: Re: bug#76969: kill-buffer fails silently when a thread exists where
 it's current
Date: Tue, 18 Mar 2025 02:07:57 +0200
On 16/03/2025 08:07, Eli Zaretskii wrote:

>> I wonder if the set of functions which behave safely under such
>> conditions is large enough for this to be the default.
> 
> If someone would like to make a survey of use patterns of Lisp
> threads, I think it will be useful in more than one way.

Here's a minor survey: among the packages I have installed there are 2 
(!) which use threads, out of 55: lsp-mode and diff-hl, the latter has 
triggered this discussion.

In GNU ELPA, there are also phpinspect, debbugs and phps-mode.

>>>> We can't really distinguish between these kinds of threads from the
>>>> outside, so the general model would need to err on the side of safety.
>>>
>>> The side of safety in my book is not to kill the buffer.  You
>>> suggested instead to signal the thread, which would terminate it, and
>>> I consider that not erring on the side of safety.
>>
>> Okay... then by default we will not kill the buffer, but allow threads
>> to opt into allowing the buffer to be killed, preceded by a signal?
>>
>> That sounds safe enough.
> 
> I actually thought the other way around: kill the buffer and deliver
> the signal, unless a special buffer-local variable is non-nil.

This sounds like the same description, with the only difference that the 
said variable is nil by default.

If so, sounds good to me.

>> But the downside is we retain the current issue, right? As described in
>> the original report.
> 
> With your default, yes.  OTOH, the original report didn't explain why
> not killing the buffer is a problem.

Unpredictability, lack of observability, poor debugging. Mentioned all 
these in the first few messages.

> In general, any Lisp program
> that calls kill-buffer should be prepared to deal with the fact that
> the buffer might not be killed, due to any of the possible reasons
> which prevent that already, even if other threads are no involved.

Most of which have stayed documented thus far.

In any case, as I said in the beginning, we don't have to kill the 
buffer, but then we should consider other ways of making it clearer why 
it wasn't killed (to the programmers, to the users).

Having the "kill and signal" default would be easier conceptually.

>>>> Not sure what thread function you're thinking of in this case.
>>>
>>> In this analogy, showing buffers is that "thread function".
>>
>> Okay. So take just a function that is going to display a buffer. If the
>> buffer is killed, wouldn't it be better for the
> 
> This sentence seems to be unfinished.

Sorry.

...would it be better for the "show buffer" function to be interrupted, 
rather than have it show a different buffer?

This question might be moot already, if we agree that "kill" and 
"signal" should go together in our scenario (either both happen or neither).

>> I was unclear if you think it's better to always kill the buffer, or to
>> have that behavior opt-in.
> 
> That's because I don't yet have a firm opinion.  And to some extent
> that depends on the optional features we will implement:
> 
>   . is signal always delivered or only to threads which said they want it?
>   . do we add a buffer-local dont-ever-kill-my-buffer variable?

The difference between these option is really whether the behavior is 
off or no by default, right?

As for whether it's a thread property or a local var, either seems 
workable to me.

>> If the buffer is killed, though, I believe it is better to signal its
>> threads. Having the buffer substituted can easily get unnoticed, and
>> would be hard to debug.
> 
> And I tend to prefer leaving this to the Lisp program which starts the
> thread, via an additional argument to make-thread.

I don't think we could make the argument a required one. And as long as 
it isn't, the question of default stays relevant.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#76969; Package emacs. (Tue, 18 Mar 2025 00:22:03 GMT) Full text and rfc822 format available.

Message #62 received at 76969 <at> debbugs.gnu.org (full text, mbox):

From: Dmitry Gutov <dmitry <at> gutov.dev>
To: Eli Zaretskii <eliz <at> gnu.org>, Spencer Baugh <sbaugh <at> janestreet.com>
Cc: 76969 <at> debbugs.gnu.org
Subject: Re: bug#76969: kill-buffer fails silently when a thread exists where
 it's current
Date: Tue, 18 Mar 2025 02:21:23 +0200
On 17/03/2025 21:41, Eli Zaretskii wrote:
> Don't forget that
> the target thread could be the main thread.

Good question what we should do with the main thread in this case - but 
even if it receives the signal, it should be more a lot more visible to 
the user than in background threads, helping fix whatever misbehavior 
that caused it.

But we'll probably use the same method that's being discussed to declare 
a thread an exception, prohibiting its buffer from being killed externally.




This bug report was last modified 93 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.