Changeset 15584

05/23/22 06:23:42 (10 months ago)
Mark Evenson

This commit makes interrupt-thread react quickly.

What happens on interrupt

I'm assuming there's both a worker thread(w) and a control thread(c) running.

Running in thread(c)

Call (interrupt-thread thread(w) function args)
thread(c).interruptThread unpacks the arguments into a list
It then calls thread(w).interrupt(function, args)
thread(w).interrupt pushes args and fun on thread(w) thread local variable pending
it then call java's interrupt() method.

If things are left alone, the java interrupt mechanism will at some
point throw an InterruptedException? when running inside thread(w) various
lispThread functions catch the InterruptedException? and call

processThreadInterrupts() then runs the functions in the thread local
variable pending.

There's also one check that uses the java interrupt mechanism, a call to
thread.isInterrupted(). So if thread(w) notices that it's been
interrupted in this way it will also call processThreadInterrupts().


Java will only throw the exception when running functions like sleep or
wait, so the exception won't be handled until one of those methods is
called within thread(w). The call to isInterrupted() only happens once
per function call. So if a function is in a loop, it could be a long
time until the interrupt is handled.


Thread(w) is running. The compiler has thoughtfully inserted a check on
a static variable Lisp.interrupted inside each iteration of a loop. If a
global Lisp.interrupted get sets to true the compiler inserted code
calls Lisp.handleInterupts().


Lisp.interrupted is never set in the normal course of events. There's a
function to set it, interruptLisp() but no one calls it. interruptLisp
calls Lisp.setInterrupted() which sets Lisp.interrupted to true.

The change:

We add another static Lisp.threadToInterrupt.

We change setInterrupted() take the boolean but also a thread that you
want interrupted. We change handleInterrupts(), which used to call
break() to check whether the current thread is equal to threadTointerrupt.
When the current thread is thread(w), it call thread(w).processThreadInterrupts().

Then, we change interruptLisp() to call Lisp.setInterrupted() with the
thread to be interrupted.

We don't want to have to call interruptLisp separately. So we
call setInterrupted() directly in thread(c).interruptLisp().

And we win.

In slime, when you hit Control-c, it calls interrupt-thread with the
function invoking the debugger. With the change, interrupt-thread gets
handled promptly and the debugger is called, even if we are in an
infinite loop.

3 edited


  • trunk/abcl/src/org/armedbear/lisp/

    r15391 r15584  
    364364    }
    365365    @Override
    366     public LispObject execute()
    367     {
    368       setInterrupted(true);
     366    public LispObject execute(LispObject[] args)
     367    {
     368      if (args.length < 1)
     369        return error(new WrongNumberOfArgumentsException(this, 1, -1));
     370      final LispThread thread;
     371      if (args[0] instanceof LispThread) {
     372        thread = (LispThread) args[0];
     373      }
     374      else {
     375        return type_error(args[0], Symbol.THREAD);
     376      }
     377      setInterrupted(thread,true); // engage the compiler-insert check Lisp.interrupted/Lisp.handleInterrupts mechanism
    369378      return T;
    370379    }
  • trunk/abcl/src/org/armedbear/lisp/

    r15561 r15584  
    476476  public static volatile boolean interrupted;
    478   public static synchronized final void setInterrupted(boolean b)
    479   {
    480     interrupted = b;
    481   }
    483   public static final void handleInterrupt()
    484   {
    485     setInterrupted(false);
    486     Symbol.BREAK.getSymbolFunction().execute();
    487     setInterrupted(false);
     477  public static volatile LispThread threadToInterrupt;
     479  public static synchronized final void setInterrupted(LispThread thread, boolean b)
     480  {
     481    if (b)
     482      { threadToInterrupt = thread; }
     483    else
     484      { threadToInterrupt = null; }
     485    interrupted = b;
     486  }
     488public static synchronized final void handleInterrupt()
     489  {
     490    LispThread currentThread = LispThread.currentThread();
     491    LispThread checkThread = threadToInterrupt;
     492    setInterrupted(null, false);
     493    if ((currentThread == threadToInterrupt) || (threadToInterrupt == null))
     494      {
     495        //        Symbol.BREAK.getSymbolFunction().execute();
     496        currentThread.processThreadInterrupts();
     497      }
     498    setInterrupted(null, false);
    488499  }
  • trunk/abcl/src/org/armedbear/lisp/

    r15552 r15584  
    14761476                funArgs = new Cons(args[i], funArgs);
    14771477            thread.interrupt(fun, funArgs);
     1478            setInterrupted(thread,true);
    14781479            return T;
    14791480        }
Note: See TracChangeset for help on using the changeset viewer.