Interrupting repl in Slime

Alan Ruttenberg alanruttenberg at gmail.com
Thu Apr 7 17:02:22 UTC 2022


ABCL does use the native mechanism to implement interrupt-thread. The
control thread receives save the interrupt-thread function, sets a flag,
and then uses java's thread.interrupt on the targeted thread. The problem
is that the handling of the interrupt in the targeted thread needs to be
done by that thread and when the thread is busy it won't handle the
interrupt. I think that's the intent including the checks for interrupt in
generated code - so that it might be handled sooner. The problem is that
check isn't checking the right variable.

On Thu, Apr 7, 2022 at 3:23 AM Alessio Stalla <alessiostalla at gmail.com>
wrote:

> To me, this looks overly complicated with all these flags. I never
> understood why ABCL does all this. Java threads already natively have an
> interrupt flag. Is there a reason for not using the native Java mechanism?
> https://docs.oracle.com/javase/tutorial/essential/concurrency/interrupt.html
>
> On Thu, 7 Apr 2022 at 00:13, Alan Ruttenberg <alanruttenberg at gmail.com>
> wrote:
>
>> Hi folks,
>>
>> Maybe someone has a feel for how interrupts work in ABCL?
>>
>> Doing an interrupt in ABCL from Slime doesn't interrupt until the end of
>> a function or sleep or wait is called.  This means whenever I accidentally
>> have a too long or infinite loop by mistake the only thing I can do is quit
>> lisp and start over. This is probably THE most irritating problem I have
>> with ABCL. It doesn't happen often but when it does it completely disrupts
>> work.
>>
>> Slime handles an interrupt by calling interrupt-thread. Interrupt-thread
>> saves the function and sets a flag. When the interrupted process next
>> checks the flag it will call the interrupt function. At first I thought
>> lisp wasn't checking for an interrupt often enough, but that's not the
>> problem. If I compile (loop for i = 1)  I get
>>
>>   public final LispObject execute() {
>> ;         do {
>> ;             if (!Lisp.interrupted) {
>> ;                 continue;
>> ;             }
>> ;             Lisp.handleInterrupt();
>> ;         } while (true);
>> ;     }
>>
>> So why doesn't the interrupt work? Well it seems there are two paths to
>> interrupt. One is via interrupt-thread. Here is the code that sets the flag.
>>
>>     final synchronized void interrupt(LispObject function, LispObject
>> args)
>>     {
>>         pending = new Cons(args, pending);
>>         pending = new Cons(function, pending);
>>         threadInterrupted = true;
>>         javaThread.interrupt();
>>     }
>>
>> Note that it is setting thread.threadInterrupted rather than
>> Lisp.interrupted. In fact, the only way to set Lisp.interrupted is via
>> Lisp.setInterrupted() and the only function that calls that is
>> ext:interrupt-lisp. Nothing calls ext:interrupt-lisp. So that nice check
>> within the loop is checking for a signal that currently never arrives.
>>
>> The javathread.interrupt is only periodically detected by an explicit
>> call or via an InterruptedException that Java will throw when the thread
>> yields or via a check of thread.isInterrupted. The latter is checked in one
>> place, inside a catch, which afaik isn't executed unless there's an error
>> in the thread - it's the "Ignoring uncaught exception" path.
>>
>> Now, if I call ext:interrupt-lisp from the *inferior-lisp* a break comes
>> up during the infinite loop and I can kill it! This is the desired
>> behavior.
>>
>> The problem is, I can't figure out where to call (ext:interrupt-lisp)
>> within slime. I tried to put it right after interrupt-thread when that is
>> called in swank::queue-thread-interrupt, which is what is eventually called
>> when you hit C-c in the repl.  But that causes an error and kills the slime
>> session. And emacs, for that matter, which needs to be force quit.
>>
>> I'm thinking that in the case of a control-c we should not use
>> thread-interrupt but rather lisp interrupted. threadInterrupted is
>> thread local and Lisp.interrupted is a global. So technically the control c
>> might not go to the right thread, since all threads are checking the
>> global. But in the situation where it's locked in an infinite loop, there's
>> no other thread doing anything, so the right thing happens. In any case I
>> think that's probably easy to fix by setting another volatile to the thread
>> to be interrupted. In general one would have to worry about a race
>> condition if there were several places that might call interrupt-lisp, but
>> in this case it would only be called with an explicit Control-c and perhaps
>> on a request to kill a thread from the slime threads buffer.
>>
>> It's an open question as to whether interrupt-thread should take
>> advantage of this as well, but that would require more care to avoid a race
>> condition.
>>
>> Any help on sorting this would be much appreciated.
>>
>> Thanks,
>> Alan
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mailman.common-lisp.net/pipermail/armedbear-devel/attachments/20220407/b9d1a19f/attachment.html>


More information about the armedbear-devel mailing list