Subject: Re: Dynamic function bindings
From: rpw3@rpw3.org (Rob Warnock)
Date: Sat, 21 Jan 2006 21:25:39 -0600
Newsgroups: comp.lang.lisp
Message-ID: <pfSdnTD5Xo-uZk_eRVn-vw@speakeasy.net>
verec  <verec@mac.com> wrote:
+---------------
| I'm wrapping my head around Thomas solution to try and understand
| how his solution is different/solves the threading issue.
| So far, my understanding of his code is that the magic happens
| with the use of progv ...
+---------------

Not really; PROGV per se is a red herring. It has nothing to do
with the core issue, which is this: Even though the CLHS says 
nothing about "threading", the Common Lisp community -- and 
especially the community of implementors (competitors though 
they might be!) -- have generally come to a consensus of how 
dynamic variables should behave in the presence of threading 
[any kind of threading -- green, "native", POSIX, etc], which 
is roughly the following [apologies if I don't use exactly the 
proper terminology]:

In the absence of threading [or with only one thread active], 
things behave exactly as in CLHS 3.1.2.1.1.2 "Dynamic Variables":

    At any given time, all dynamic variables with a given name refer
    to exactly one binding, either in the dynamic environment or in
    the global environment.

where "the global environment", sometimes called the top-level
environment, is simply whatever contains the binding for a special
variable when it is not dynamically bound. Any assignment [with
SETF/SETQ or things that expand to them such as INCF or PUSH] affects 
only the single, current dynamic binding. During the dynamic extent 
of a LET- or LAMBDA-binding, there is no way to change the original 
"global" value or any of the values saved on the "stack" of dynamic 
bindings. Also, (SETF (SYMBOL-VALUE sym) ...) is required to act on 
the current dynamic binding. See the CLHS for many other gory details.

*With* threading, the model changes only slightly, but *VERY* 
significantly, as follows:

1. All of the threads that have *not* LET- or LAMBDA-bound a special
   variable continue to share a single view of a global or top-level
   value. Any assignment (SETF) to that variable is seen by all threads 
   which have *not* LET- or LAMBDA-bound that special variable.

2. Any thread which *does* LET- or LAMBDA-bind a special variable
   creates a *new* conceptual variable and related "stack" of dynamic 
   bindings which is private to that thread [and in some (most?) 
   implementations, its descendents, assuming they don't further bind
   it themselves]. Assignments to or further dynamic rebindings of
   the variable within that thread are seen *only* by that thread [and
   descendents], *NOT* by any of the other threads [which see either
   the top-level binding or their own, private LET- or LAMBDA-bindings].

Thus, threading replaces the CLHS notion that a special variable
always has a *single* binding [which can be dynamically bound in
a *single* conceptual "stack"] with the concept that the first
LET- or LAMBDA-binding of a given special variable by a given
thread creates a *new* per-thread binding [and associated conceptual
"stack" of further dynamic bindings].

So how does this apply to your question about "dynamic functions"? 
Well, since the above "threading model of dynamic variables" is by  
now so firmly established in the CL community as "the right way", 
there is a natural expectation that any implementation of "dynamic 
functions" would behave EXACTLY THE SAME WAY with respect to threading. 
That is, that any DFLET form executed within a thread would create 
a new function binding that would be seen ONLY WITHIN THAT THREAD
(and descendants), just as the above model does for special variables.

And *that* is why various people have been *strongly* suggesting
that you make your dynamic function facility store the current
binding in a per-function-name special variable and use FUNCALL
(or APPLY) indirection through the special variable -- because,
if you do that, the "expected" (by the community) behavior for
things dynamic in the presence of threads will "just happen"
automatically in any implementation that follows the consensus
model of threading and special variables. And if you *don't*
do that, then it won't.

Capische?


-Rob

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607