Subject: Re: SYMBOL-FUNCTION and thread implementations
From: rpw3@rpw3.org (Rob Warnock)
Date: Tue, 24 Jul 2007 23:04:30 -0500
Newsgroups: comp.lang.lisp
Message-ID: <k7udnfYfnIhTVjvbnZ2dnUVZ_j2dnZ2d@speakeasy.net>
Mark H. <mark.hoemmen@gmail.com> wrote:
+---------------
| rpw3@rpw3.org (Rob Warnock) wrote:
| > Capische?
| 
| Yup, that makes sense, though I'm still not 100% clear on how those
| two binding mechanisms are implemented in a multithreaded CL.  SBCL
| doesn't let threads inherit dynamic bindings:
| 
| http://www.sbcl.org/manual/Special-Variables.html#Special-Variables
| 
| which, after reading your explanation, makes more sense.  Otherwise,
| SBCL would have to keep the dynamic binding context around for the
| thread, no matter how long the thread takes to complete (_if_ it
| completes -- it could be killed before it even runs).
+---------------

Yes, SBCL's ancestor, CMUCL, didn't permit inheriting dynamic bindings
either, and for the same reasons.

Hmmm... But SBCL's MAKE-THREAD appears to have lost something useful
from CMUCL's MP:MAKE-PROCESS, namely, the ability to pre-establish a
set of dynamic bindings for a new thread:

    cmu> (describe 'mp:make-process)

    MAKE-PROCESS is an external symbol in the MULTIPROCESSING package.
    Function: #<Function MULTIPROCESSING:MAKE-PROCESS {102C0BD1}>
    Function arguments:
      (function &key (name "Anonymous") (run-reasons (list :enable))
	             (arrest-reasons nil) (initial-bindings nil))
    Function documentation:
    ...
    :INITIAL-BINDINGS
	  An alist of initial special bindings for the process.  At
	  startup the new process has a fresh set of special bindings
	  with a default binding of *package* setup to the CL-USER
	  package.  INITIAL-BINDINGS specifies additional bindings for
	  the process.  The cdr of each alist element is evaluated in
	  the fresh dynamic environment and then bound to the car of the
	  element.

which you can use to effectively "inherit" from a select set of the
parent thread's current dynamic bindings this way:

    (let ((vars     '(*foo* *bar* *baz*))
	  (vals (list *foo* *bar* *baz*)))
      (mp:make-process #'new-thread-func
		       :initial-bindings (pairlis vars vals)))

Though I suppose you could always simulate it in SBCL by using PROGV
[which is exactly how CMUCL's MP:MAKE-PROCESS did it under the hood!]:

    (let ((vars     '(*foo* *bar* *baz*))
	  (vals (list *foo* *bar* *baz*)))
      (mp:make-process (lambda ()
			 (progv vars vals
			   (new-thread-func)))))

But of course, these are only inherited *values* of the parent's
current dynamic bindings, not the dynamic values themselves.
[That is, if after the child is running either the parent or
the child SETF's one, the other thread won't see the change.]


-Rob

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