What follows is my current (mostly implemented) plan for doing away
with sessions. Before putting in the finishing touches I wanted to
check if this rubs anyone the wrong way. I'd really like to remove
sessions entirely and put this in before 1.0.
Interface inspired by the OpenMCL take on this, but extended to be,
1. Sessions are modeled after unix process handling in terminal, but
threads are not shell spawned processes, even though they
sometimes may act like them. Instead of mediating foreground /
background threads we really want to mediate access to the user,
but there may be several conceptual users, and if we can open
multiple listeners, then there is no reason why multiple threads
cannot talk to the same user simultaneously.
2. Sessions rely heavily on INTERRUPT-THREAD, including asynch uwinding,
which is fundamentally bad. (See earlier discussions on asynch unwinds.)
Terminal is an abstraction based on an exlusive access to an
interactive two way stream: a terminal is owned by the thread that
currently has exclusive access to it.
By default SBCL is assumed to be running in a text-based terminal, in
which case obtaining a terminal simply refers to gaining access to
that sole terminal. This behaviour can however be customized, so that
a thread that requires interaction with the user can have, for
example, a new terminal window opened for it for as long as it needs
(All symbols in package SB-EXT or SB-THREAD -- probably SB-THREAD.)
In SBCL itself this interface is used to obtain an interactive
stream for the debugger, etc.
Base class of all terminal objects. Can be subclassed.
Required. A two way stream used by the terminal. Accessible via
the reader function TERMINAL-IO.
Optional. An object used to name the terminal. Accesible via the
reader function TERMINAL-NAME.
A new terminal is initially unowned by any thread.
Global special variable bound to the current terminal. Initially
bound to an instance of TERMINAL whose TERMINAL-IO is *TERMINAL-IO*.
function TERMINAL-IO &optional terminal => two-way-stream
Returns the two way stream for the TERMINAL. The terminal defaults
function TERMINAL-OWNER &optional terminal => thread
Returns the thread that currently owns TERMINAL, or NIL if the
terminal is unowned by any thread. The terminal defaults to
*TERMINAL*. There is no guarantee that the thread is still alive or
has access to the terminal by the time the function returns.
function TERMINAL-NAME &optional terminal => object
Returns the name of the TERMINAL. The terminal defaults to
generic function ACTIVATE-TERMINAL terminal
Called after a thread is made the owner of the terminal. May be
extended, but not overridden for subclasses of TERMINAL.
generic function DEACTIVATE-TERMINAL terminal
Called after a thread has released a terminal. May be extended, but
not overridden for subclasses of TERMINAL.
function GET-TERMINAL &optional terminal => terminal
Waits until TERMINAL is available, makes the current thread its
owner, calls ACTIVATE-TERMINAL with the terminal, and finally
returns the terminal. The terminal defaults to *TERMINAL*. Has no
effect aside from returning the terminal if the current thread is
already the owner of the terminal.
generic function RELEASE-TERMINAL &optional terminal => terminal
Calls DEACTIVATE-TERMINAL with the TERMINAL, then makes the
terminal unowned by the current thread, making it available for
threads using GET-TERMINAL, and finally returns the terminal. The
terminal defaults to *TERMINAL*. An error is signalled if the
current thread is not the owner of the terminal.
Global special variable. Must be NIL, or a designator for a
function callable with no arguments, used to find an interactive
terminal. Defaults to NIL. See FIND-TERMINAL for details.
function FIND-TERMINAL => terminal
Attempts to find an interactive terminal. If *FIND-TERMINAL-HOOK*
is not NIL, it should be a function designator. The designated
function is called with no arguments and *FIND-TERMINAL-HOOK* bound
to NIL. If *FIND-TERMINAL-HOOK* is NIL, or the function returns
with a value that is not a terminal, *TERMINAL* is returned.
macro WITH-TERMINAL (terminal) &body forms => value*
Waits to get become owner of TERMINAL, then executes FORMS with
*TERMINAL* bound to the terminal, and *TERMINAL-IO* bound to the
TERMINAL-IO of the terminal. Finally releases the terminal unless
the current thread had exclusive access to it prior to executing
WITH-TERMINAL. Returns the values returned by the last of FORMS.
macro WITHOUT-TERMINAL (terminal) &body forms => value*
If current thread owns TERMINAL, releases it for execution of
forms, and then waits to regain ownership. If current thread does
not have own the terminal, executed forms without any special
processing. Returns the values returned by the last of FORMS.
-- Nikodemus Schemer: "Buddha is small, clean, and serious."
Lispnik: "Buddha is big, has hairy armpits, and laughs."