Subject: Re: bug#64819: 30.0.50; condition-wait not interruptible

From: Eli Zaretskii <[email-masked]>
Date: Mon, 24 Jul 2023 19:23:31 +0300

> From: Helmut Eller <[email-masked]>
> Cc: [email-masked]
> Date: Mon, 24 Jul 2023 16:57:05 +0200
> 
> On Mon, Jul 24 2023, Eli Zaretskii wrote:
> 
> >> From: Helmut Eller <[email-masked]>
> >> We could say that C-g sets quit-flag and causes all blocking calls to
> >> condition-wait to return nil (spurious wakeup).  At that point all
> >> threads are conceptually running.  Then the first thread (unspecified
> >> which one) who calls maybe_quit() finishes handling C-g and clears
> >> quit-flag afterwards.  Those threads who don't feel prepared to handle
> >> C-g can bind inhibit-quit.
> >
> > I don't think we can allow more than one thread at a time to run the
> > parts of the Lisp interpreter that lead to maybe_quit.
> 
> I didn't suggest that.  Nor did I suggest that the thread scheduler
> should switch away from the currently running thread.

You did say "At that point all threads are conceptually running."
Perhaps I misunderstood what you meant.

> What I did suggest is that the thread blocked in condition-wait is
> considered runnable.  So that the thread scheduler is allowed to pick
> this thread the next time when somebody calls thread-yield or
> condition-wait.

We don't have a scheduler.  The threads "schedule themselves", in the
sense that the first thread that succeeds to grab the global lock gets
to run, and the others wait for the lock to be released.

So for this to work, the C-g handler will have to release some thread.

> Maybe we can agree on this: when only one thread exists and it is
> blocked in condition-wait, then condition-wait should be interruptible
> by C-g.

Yes, this is simpler.

> For the situation where some threads are blocked in condition-wait and
> one other thread is running, I think that running thread would call
> maybe_quit and clear quite-flag before calling thread-yield.  The other
> threads would observe spurious wakeups as soon as they are allowed to
> run.

I'm not sure I follow here.  First, no one guarantees that a running
thread will call thread-yield; it could call sit-for or somesuch
instead.  And second if C-g only sets quit-flag, then it will not be
able to release a stuck thread; and if it does release a stuck thread,
it will somehow need to stop the running one, or we will have more
than one thread running.

> > So I think to do anything smarter in the deadlock situation you
> > describe we'd need to detect the deadlock first.  Once we do that
> > (which isn't easy: perhaps do that in the signal handler?), we'd need
> > to decide which of the deadlocked threads to free, which is also not
> > trivial.  Hmmm...
> 
> I doubt that deadlock detection is possible in the general case.
> E.g. how could we possibly know that a timer is or isn't going to call
> condition-notify in 5 seconds?

We are talking about a situation where the user typed C-g, so we have
some indication that the situation is not normal.

> > Btw, did you try your recipe in a Unix TTY?  There, C-g actually
> > delivers SIGINT to Emacs, so you might see a different behavior (or a
> > crash ;-).
> 
> When I run the recipe with: "emacs -nw -l deadlock.el -f deadlock" then
> I see the emergency escape feature kick in.  Only after the second C-g
> (of course).  A single C-g doesn't seem to do anything.

As expected, thanks.