[slime-devel] Re: Use of with-lock inside without-interrupts

Martin Simmons martin at lispworks.com
Wed Aug 6 11:33:51 UTC 2008

>>>>> On Tue, 05 Aug 2008 22:51:05 +0200, Helmut Eller said:
> * Martin Simmons [2008-08-05 21:26+0200] writes:
> > I see some new changes in swank-lispworks.lisp where mp:with-lock is used
> > inside mp:without-interrupts.  What is the purpose of that?
> My reasoning with this code was:
>     (loop
>      (mp:process-wait "receive-if"
>                       (lambda () (some test (mailbox.queue mbox))))
>      (mp:without-interrupts
>        (mp:with-lock (lock "receive-if/try" 0.1)
>          (let* ((q (mailbox.queue mbox))
>                 (tail (member-if test q)))
>            (when tail
>              (setf (mailbox.queue mbox) (nconc (ldiff q tail) (cdr tail)))
>              (return (car tail)))))))
> there would be no guarantee that the part inside WITH-LOCK isn't
> interrupted, e.g. by PROCESS-INTERRUPT to run the SLIME debugger.  But
> the debugger, sooner or later, calls RECEIVE-IF in the same thread;
> recursively so to say.  It could happen that the debugger removes the
> the element that the suspended call was about to remove, i.e. both
> calls would, after the debugger resumes, return the same element.
> Disabling interrupts inside the WITH-LOCK didn't seem right either,
> because then the interrupt handler would be delayed, but still be run in
> the dynamic extend of the lock and so blocking any sender.
> That's pretty unfortunate mix of threading and interrupts.  The current
> variant with the timeout appeared to me as the least broken.  But I
> would love to learn how to do this properly.

Hmmm, I see the problem now.

I think you could implement it without blocking all interrupts by maintaining
your own interrupt queue, something like this:

(defvar *pending-slime-interrupts* nil)
(defvar *in-without-slime-interrupts* nil)

(defmacro without-slime-interrupts (&body body)
  `(let ((*pending-slime-interrupts* nil)
         (*in-without-slime-interrupts* t))
         (progn , at body)
       (when *pending-slime-interrupts*
         (let ((*in-without-slime-interrupts* nil))
           (mapc 'funcall *pending-slime-interrupts*))))))

(defun invoke-or-queue-interrupt (function)
  (if *in-without-slime-interrupts*
      (push function *pending-slime-interrupts*)
    (funcall function)))

Then the SLIME interrupt function should call INVOKE-OR-QUEUE-INTERRUPT, which
will delay the interrupt if inside the WITHOUT-SLIME-INTERRUPTS form.

Martin Simmons
LispWorks Ltd

More information about the slime-devel mailing list