Changeset 15584 for trunk/abcl/src


Ignore:
Timestamp:
05/23/22 06:23:42 (8 months ago)
Author:
Mark Evenson
Message:

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

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

BUT

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.

Meanwhile:

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

BUT

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.

Location:
trunk/abcl/src/org/armedbear/lisp
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/abcl/src/org/armedbear/lisp/Extensions.java

    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/Lisp.java

    r15561 r15584  
    475475
    476476  public static volatile boolean interrupted;
    477 
    478   public static synchronized final void setInterrupted(boolean b)
    479   {
    480     interrupted = b;
    481   }
    482 
    483   public static final void handleInterrupt()
    484   {
    485     setInterrupted(false);
    486     Symbol.BREAK.getSymbolFunction().execute();
    487     setInterrupted(false);
     477  public static volatile LispThread threadToInterrupt;
     478
     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  }
     487
     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  }
    489500
  • trunk/abcl/src/org/armedbear/lisp/LispThread.java

    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.