Thank you Gary.<div><br><div>I've now put into my ccl-init.lisp the line</div><div>   (defun cl-user::yield () (process-interrupt ccl::*initial-process* #'break))</div><div>which will give me an easily remembered workaround.<br><br></div><div>As I now understand it, perhaps wrongly, the Listener does run a REPL but not in the appropriate</div><div>thread. The break loop in *initial-process* responds to :Y by executing code.</div><div>Idea: could the Listener thread not respond to :Y by causing that code to be executed in the</div><div>initial process? Perhaps only when it is not itself in a break loop? (If that's daft, feel free to say so!)</div><div><br></div><div>Arthur</div><div><br></div><div><br></div><div>----- Original Message -----<br>From: Gary Byers <gb@clozure.com><br>Date: Friday, May 8, 2009 12:26 pm<br>Subject: Re: [Openmcl-devel] debugging debugging<br>To: Arthur W Cater <arthur.cater@ucd.ie><br>Cc: openmcl-devel Devel <openmcl-devel@clozure.com><br><br>> <br>> <br>> On Fri, 8 May 2009, Arthur W Cater wrote:<br>> <br>> > As I mentioned in March, I have a similar problem with errors <br>> occurring on other threads,<br>> > in my case often threads other than the Event�thread.<br>> > I'm told to type (:Y 8) � eg � but neither the Listener nor <br>> the AltConsole does anything<br>> > when I do so. A simple example of this is with<br>> ><br>> > (process-run-function "Breaker" #'(lambda nil (break "Talk to me")))<br>> ><br>> > The Event thread still runs, but there is no way afaik of <br>> getting any<br>> > debugging info, not even a simple backtrace.<br>> ><br>> > Arthur<br>> ><br>> <br>> Yuck.  Let me see if I can explain what's going on here <br>> (though I agree that<br>> what's going on and what should/could be going on are different <br>> things.)<br>> When the lisp starts up, it initializes (global) values of the <br>> standard input<br>> and output stream variables.  When run in a TTY environment <br>> or under Emacs,<br>> these streams are (usefully) connected to the process-wide <br>> standard input<br>> and output devices (pipes or sockets or terminal-like <br>> devices).  When a GUI<br>> application is launched on OSX, the devices to which these <br>> streams are connected<br>> aren't appropriate for interactive I/O; when the CCL IDE starts <br>> up, it tries<br>> to reconnect these streams to devices that talk to the <br>> AltConsole application<br>> (which at least provides a primitive way of doing interactive I/O.)<br>> <br>> When the (non-GUI) CCL starts up, a listener thread is created <br>> that uses<br>> these standard streams (and can therefore talk to a Terminal <br>> window, Emacs, etc.)<br>> The listener thread is marked as being the "owner" of the global <br>> standard input<br>> stream (since it's expected to make heavy use of that stream, to <br>> run the REPL,<br>> etc.)  If some background thread needs to do input from <br>> that stream, it announces<br>> that fact and advises us to type (:y <n>); in the listener, <br>> we have to ensure<br>> that we're talking to the REPL (either at top level or in a <br>> break loop) and type<br>> that command in order to (temporarily) transfer the exclusive <br>> right to use that<br>> global input stream to the background thread.<br>> <br>> If the listener thread isn't in the REPL, we generally have to <br>> wait until it<br>> is or interrupt it to get it to that point before typing (:Y <br>> <n>) will have<br>> the desired effect.<br>> <br>> When the IDE starts up, the initial thread is marked as the <br>> owner of the global<br>> input stream (connected to AltConsole).  When listener <br>> windows are created in<br>> the IDE, the listener threads that are created at the same time <br>> arrange to use<br>> interactive streams connected to that listener window (and its <br>> buffer.)  If<br>> a background thread is created, then by default it will try to <br>> use the global<br>> (AltConsole-based) interactive streams.  (See below.)<br>> <br>> If that background thread needs to use the global input stream (owned<br>> by the initial/event thread), it'll print a message to that <br>> effect on<br>> its standard error or output stream (I forget which, but they'd both<br>> be associated with AltConsole) and wait for the input stream's owner<br>> to transfer ownership (via a :y command in a REPL).  Of course,<br>> the input stream's owner is busy processing events isn't really <br>> runninga REPL, so (as you noted) nothing really happens.<br>> <br>> If we were to keep this paradigm (background threads try to do <br>> emergencyI/O using a shared input stream), then we'd presumably <br>> want to have some<br>> other means of doing the equivalent of :Y (a menu command or <br>> something)that wouldn're require the event thread to be at a <br>> REPL prompt.  If you<br>> want to see what's going on here or need to handle this <br>> situation in an<br>> emergency in the short term, you can do:<br>> <br>> - in an IDE listener, do:<br>> <br>> ? (process-run-function "background" #'break)<br>> <br>> This should cause output to the AltConsole window, announcing <br>> that the background<br>> thread wants to enter a break loop and needs someone to type a <br>> :Y command to let<br>> it use the global input stream.  (Of course, it's not the <br>> act of typing that<br>> command that transfers ownership, it's the act of a REPL running <br>> in the owning<br>> thread reading and processing that command that does the <br>> transfer.)  The owning<br>> thread, of course, is busy processing events, so let's change that.<br>> <br>> - in the IDE listener, do:<br>> <br>> ? (process-interrupt ccl::*initial-process* #'break)<br>> <br>> There are several ways to refer to the initial process; sadly, I <br>> don't know<br>> of a way that involves exported names.<br>> <br>> Event processing should stop, and the initial process should <br>> have entered a<br>> break loop in the AltConsole window.  In that break loop <br>> typing the appropriate<br>> :Y command will do the "ownership transfer" and let the <br>> background thread use<br>> the input stream for its break loop; exiting the break loop will <br>> restore control<br>> to the event thread (which will be in the break loop we forced <br>> it into from the<br>> IDE), and exiting from that break loop will allow the event <br>> thread to get back<br>> to processing events.<br>> <br>> That's all pretty horrible; it could be made slightly less <br>> horrible if<br>> there were some GUI object (a menu item or button or something<br>> somewhere) that avoided the need to enter a break loop to do the<br>> ownership transfer of the input stream, but ... well, there's no<br>> reason for a background thread to have to use the global<br>> AltConsole-based stream for incidental or other I/O: it could <br>> create a<br>> listener window ("on demand") if it needed to do I/O, or try to share<br>> an existing listener window, or (maybe) create its own AltConsole<br>> window, or any number or other things.  (It's very hard to <br>> get the<br>> event thread to do event-driven I/O to and from a GUI window, <br>> but shouldn't<br>> be any harder for a random thread to do that than it is for a <br>> listener.)<br>> </div></div>