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

From: Dmitry Gutov <[email-masked]>
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: [email-masked], [email-masked]
>> From: Dmitry Gutov <[email-masked]>
>>
>> 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.