[Openmcl-devel] The AltConsole

Gary Byers gb at clozure.com
Wed Apr 24 04:02:22 PDT 2013

On Wed, 24 Apr 2013, Ron Garret wrote:

> On Apr 24, 2013, at 12:21 AM, peter wrote:
>> At 10:40 PM -0700 13/4/23, Ron Garret wrote:
>>> Why does CCL use the AltConsole instead of the OSX system logging facilities?
>>> Just wondering.  I'm doing some development where I have to stop and restart CCL often and I end up with dozens of altconsoles in my dock.
>> It'd be rather useful if there was a flag that allowed background output to be sent to the Altconsole or the OSX console log. But I have a sense that this traffic redirection might need to be handled explicitly in our code.
>> But presumably we've the altconsole so some human interaction is possible with the CCL state which would not be possible in the console.
> Yes, the AltConsole is very useful when a thread that isn't attached to a listener throws an exception, but routine logging (i.e. output to *terminal-io*) from such threads could still go to syslog.  But I guess the answer to my question is: the AltConsole has to be there anyway, so we might as well use it for logging.

[I think that the following is generally correct, but it's from memory.  If
there are any nits to be picked, I'd be surprised if they're significant in
any way.)

When running a Cocoa application, file descriptor 0 is associated with /dev/null
(a pseudodevice that always returns EOF on input) and one or both of fds 1 and
2 are associated with the system logging service.  (I don't remember the exact
details, and those details have changed from OSX release to release.)

When running the Cocoa IDE (or something derived from it), some code that runs
at startup looks in the application bundle's resources for an "AltConsole.app"
sub-bundle; if that sub-bundle and its executable are found, a bidirectional
pipe is created and the executable is run with its fds 0 and 1 attached to
one side of the pipe and the main applications fds 1 and 0 are redirected to
the other the other end of that pipe.  (The altconsole executable is supposed
to just listen for input on its fd 0 - output from the main application's fd 1 -
and only start a simple Cocoa UI when input arrives; since Lion, "starting a
simple UI" gets as far a having its icon appear in the dock, and the user has
to notice that and click on the icon in order to see the altconsole window in
all its ... um ... glory.)

If the "AltConsole.app" sub-bundle isn't present, none of this I/O
redirection happens.  (The intent was that the AltConsole window could
be useful when debugging but would be removed from a shipping application;
AFAIK, some people with shipping applications prefer to leave it in.)

If the I/O redirection happens, it happens:
  - in the initial thread, before any other threads are created and before the
    initial thread starts running the event loop
  - after the standard streams (the values of *TERMINAL-IO* and friends) have
    been initialized to streams associated with fds 0 and 1
  - in a way that affects how errors that occur in the event thread are handled.
    (If the AltConsole is available, the event thread will enter a break loop
    that effectively interacts with it, however crudely; if not, IIRC some
    error information (possibly including a backtrace) is written and the thread
    crosses its fingers and tries to reset itself to the point where the next
    event would be processed.)

It's a little awkward, but it's generally possible to do some debugging in
a break loop in the AltConsole window.  You don't have much in the way of
lisp-aware editing support and you don't have a GUI (the GUI is sitting in
a break loop ...), but you can poke around a bit and may be able to understand
the problem more easily than if all you could do is look at printed backtraces
in a logfile somewhere.  It's not an ideal debugging environment (but neither
is staring at logfiles), and I think the answer to your basic question (why
is AltConsole used ?) is that it's better than nothing, and many people find
staring at logfiles to be not much better than nothing.

> Now that I think of it, I can probably fix my current annoyance by simply rebinding Hunchentoot's *terminal-io*.
> That reminds me of another thing that's been puzzling me for a long time: occasionally I'll get a message in the AltConsole that looks something like this:
> "Thread something-or-other needs to tell you something terribly important, but it can't for some obscure reason.  Type (:y 23) to yield control to this thread."
> (Obviously I'm paraphrasing -- I haven't actually seen this in quite a while.)

If two threads try to write to the same stream, that's generally possible because
a lock guards the internal state of the stream.  Even if that internal state is
kept consistent, the output is generally going to be jumbled and possibly unreadable,
and you'd need to use some higher-level locking protocol to decide which thread
writes to the stream when.

If two threads try to read from the same stream at the same time, it's generally
necessary to use a higher-level protocol in order to have any idea of which thread
reads what (parts of the) input, and that's more often more than a cosmetic issue.
The higher-level protocol that CCL uses affect some ways of requesting input from
a shared input stream (it affects things like break loops and Y-OR-N-P; it -doesn't-
affect things like READ-LINE or READ-CHAR) and involves the concept of some thread
being designated as the (current) "owner" of that shared resource and a means of
(at least temporarily) transferring or yielding ownership to some other thread.
If you run CCL in the shell, the initial listener thread "owns" the input side

If you run CCL in the shell and do:

? (process-run-function "example" (lambda () (sleep 1) (break)))

you'll get the same sort of message and the same sort of advice to type a
command which will transfer/yield ownership of the stream to the specified
thread.  That advice will make sense if the current owner (the listener
thread) is back at a REPL prompt and the :Y command will have the intended
effect there.

If you did:

   (process-run-function "example" (lambda () (sleep 1) (break)))
   (sleep 60))

then the message and advice would come at a time when the advice is harder to act
on; you'd have to interrupt the SLEEP call in the listener or wait for it to
finish before you could act on the advice.

In the GUI environment, the same "ownership" protocol is used to control access
to the input side of the (global, static) *TERMINAL-IO* and the event thread
is noted as the owner, but (as you've noted) the advice makes less sense: the
owner of the shared resource is usually off processing events instead of reading
REPL commands from that stream.  There are likely a few solutions to this
problem. but everything that I've considered can be as confusing as the problem

A workaround is to use (in the Cocoa environment) BACKGROUND-PROCESS-RUN-FUNCTION
instead of PROCESS-RUN-FUNCTION.  (I'm fairly sure that the former symbol is
exported and FIND-SYMBOL or APROPOS or ... would tell you what package it's
exported from.)  A thread created with BACKGROUND-PROCESS-RUN-FUNCTION will
behave much like a listener thread in the Cocoa GUI: its standard stream variables
will be bound to a stream/streams that use a Listener window and its underlying
Hemlock buffer for I/O, but in the case of a "background" thread the window
will only be created and made visible if the thread does I/O to that stream.

> Invariably in such situations, typing (:y 23) has no discernible effect.  What am I doing wrong?  I've tried lots of variations on the theme, typing it with and without parens, typing it into a listener, typing it in to the AltConsole.  It's never been a serious problem, which is why I've never bothered to ask about it before, but since I've got the AC on the brain and it's late I figured I'd ask.
> rg
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel

More information about the Openmcl-devel mailing list