[slime-devel] Re: OpenMCL

Peter Seibel peter at javamonkey.com
Sat Dec 13 08:16:57 UTC 2003


Luke Gorrie <luke at bluetail.com> writes:

> Peter Seibel <peter at javamonkey.com> writes:
> 
> > Here's how I was envisioning it: when I pick a thread to debug from
> > the list of debuggable therads SLIME creates a new buffer for
> > interacting with that thread. I switch between threads by switching
> > between buffers.  Thus I can C-x 2 to split my window and put one
> > thread-buffer in each window and switch back and forth with C-x
> > o.
> 
> The main issue is for people writing SLIME-based Elisp code. Anyone
> not interested in how that works can safely skip this explanation :-)
> 
> The tricky part is when Emacs has made RPCs to Lisp, and the Lisp
> threads being debugged are actually computing results to return to
> Emacs functions.
> 
> Initially the Emacs stack looks something like:
> 
>   ((emacs-event-loop))
> 
> Then we call an Emacs function that wants to RPC to Lisp to evaluate
> the expression FOO in thread T1 and do-something-with the result (the
> top of the stack comes first):
> 
>   ((receive-reply-from T1)
>    (do-something-with (eval-in-thread FOO T1))
>    (emacs-event-loop))
> 
> But FOO causes an error and invokes the debugger, so Emacs enters a
> recursive edit (i.e. calls the event loop recursively) to do the
> debugging, hoping to debug the problem and then get its result:

Just to make sure I understand this, it can do that because it
actually got back a response from the Common Lisp which it grokked is
an event that told it to invoke the debugger. Otherwise it would still
be blocked, right. Does that mean if I call (eval-in-thread (sleep
300) t1), my emacs is going to hang for 5 minutes?

>   ((emacs-event-loop)
>    (debug-thread T1)
>    (receive-reply-from T1)
>    (do-something-with (eval-in-thread FOO T1))
>    (emacs-event-loop))
> 
> Now we have one thread available for debugging. But suppose we let it
> wait a while, and now ask another thread T2 to evaluate an expression
> BAR and do-something-else with that result, and T2 also enters the
> debugger:
> 
>   ((emacs-event-loop)
>    (debug-thread T2)
>    (do-something-else-with (eval-in-thread BAR T2))
>    (emacs-event-loop)
>    (debug-thread T1)
>    (receive-reply-from T1)
>    (do-something-with (eval-in-thread FOO T1))
>    (emacs-event-loop))
> 
> Now we have two Emacs functions on the stack that are expecting a
> value - `do-something-with' and `do-something-else-with'.
> 
> See the problem?

Yeah. But what I don't understand (because, I think, I don't know
enough about what a recursive edit really implies in emacs, let alone
SLIME) is what other options there are. If there's only one emacs
stack, regardless of how many buffers you have--which is what I'm
gathering--then I'd say eventually the way to go is to make all
interactions with Common Lisp be asynchronous--when you pass an
expression to Lisp to be evaluated you have four choices what happens
with the result--it gets stuck into a buffer, it gets passed to
MESSAGE, it goes to the bit bucket in the sky, or it gets passed to a
user-supplied function.

> To deliver the result of FOO to do-something-with, we need to have
> popped everything below off the stack, including
> do-something-else-with. So either we have to debug BAR first, so
> that it can deliver its result and unwind the stack for us, or we
> get the result of FOO and 'throw' up the stack, so that
> do-something-else-with is discarded and there won't be anything left
> to use the result of BAR.
> 
> Okay, so maybe this is not so exciting to you guys doing pure CL, but
> it's a big deal for those of us using Emacs as a UI toolkit, and
> indirectly for people using the UIs we write :-)
>
> There are some solutions available. One is to just do the throw -- we
> have to be able to handle that anyway (and we can) because the same
> thing happens if the user calls `abort-recursive-edit'
> (`C-]').

That seems bad because that means you lose all that state that was on
the stack. That's okay for aborts but seems an impovrished environment
for writing more sophisticated features.

> Alternatively, we could ditch our synchronous-eval elisp funtion and
> use a purely asynchronous interface based on
> continuation-passing-style -- then we don't need the Elisp stack or
> recursive edits, and can deliver results in any order.

That seems like the way to go. 

> But I don't think the time is ripe yet :-)

Well, you're the one hacking on it, not me. But I'd be worried that if
you build an synchronous API and everybody builds on it, you'll soon
have a code base that prevents you from switching to an asynch API.
And then you'll never be able to provide the good
debug-two-threads-in-interleaved-fashion UI. 

> BTW, Distel has exactly the debugging interface you describe. All
> its Elisp code is totally asynchronous, based on (macro-assisted)
> CPS with a trampolined-style scheduler providing a multiprocessing
> abstraction in Elisp. Make me happy, read all about it :-)

Will do, it just finished printing out.

>   Distel: Distributed Emacs Lisp (for Erlang)
>   http://www.bluetail.com/~luke/distel/distel-euc.pdf

-Peter

-- 
Peter Seibel                                      peter at javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp





More information about the slime-devel mailing list