I removed a lot of text from the original email and mangled it horribly=20
in other ways.
On Tuesday 27 June 2006 19:36, rog@... wrote:
> Example 1. for SLIME users:
> at the SLIME REPL, type:
> (sb-thread:make-thread #'(lambda () (format t "hello, world~%")
> Isn't it reasonable to expect similar behaviour to a plain
> (format t "hello, world~%") here (the output actually goes to
> the inferior-lisp buffer)?
Reasonable, yes. But fraught with peril.
> The problem is that SLIME has bound a new value onto
> *standard-output*, but the new thread does not inherit it. Luckily we
> know the dynamic variable used by FORMAT, so can explicitly pass the
> value across via a lexical binding.
> This isn't so easy to do in the LTK example. What special variables
> do the tk functions use? They're not necessarily exported from
> the ltk package. A quick grep through the source reveals 21 special
> variables - if we get the thread in the example to inherit all of
> these, then it works (thread safety of the underlying library aside).
> But this seriously breaks modularity. If the maintainer of the
> package binds a new dynamic variable, then our program breaks.
> Surely this can't be right?
Copying the dynamic bindings does not automatically make a library=20
thread safe. The side effect of the current arrangement is that you (or=20
preferrably the library author) is forced to think about threads a bit=20
> It's not as if others have not already encountered the problem. In
> each case, an ad hoc solution is produced. In  a special function
> call is used (what if that function had not existed?). In  (which
> seems to have set the precedent for this behaviour), dynamic variable
> inheritance is also explicit, but this suffers from exactly the same
> problems as outlined above.
> It seems to me that there are a number of (potentially conflicting)
> requirements for the behaviour of dynamic variables with respect to
> 1) the minimum memory taken by a new thread should not be
> proportional to the number of dynamic variables 
> 2) dynamic bindings in different threads should not conflict.
> 3) it should not be necessary to know all of a package's dynamic
> variables in order to be able to spawn a new thread using one of that
> package's functions. 4) code should be able to communicate by
> changing the value of a dynamic variable.
> The current situation breaks 3), and it also breaks 4) apart from
> the special case of globally defined (and never bound) variables.
> You could try to get around 3) by having all packages export a
> list of variables that need to be inherited, but this seems error
> prone, and one would end up conflicting 1) anyway.
> Why are global variables special anyway?
Huhh? Well, suffice it to say that specials are here to say. While quite=20
problematic with regards to their interactions with threads they are=20
useful in a number of situations.
> There remain the points raised in :
> > 1) dynamic-extent
> > Suppose your package has a non-exported special, binds it, promises
> > it's going to be dynamic extent and proceeds to call user code. The
> > user code spawns a thread and the promise is broken.
> Surely declaring a special to be dynamic extent is fraught with peril
> anyway, as someone might just use the value and break your
> promise, regardless?
The same example in code:
(defun foo (fn)
(let ((*x* (cons 1 2)))
(declare (dynamic-extent *x*))
Now if you do:
(foo (lambda () (make-thread (lambda () (sleep 1) (gc)))))
you might have just crashed the image.
> > 2) gc
> > It's hard to control giving out references to objects. Yeah, it's
> > similar to 1), but colour of the smoke is different.
> If the value isn't copied, merely a reference to the value,
> then you still have control over the reference (unless someone
> explicitly copies the value, which could happen anyway).
(defun foo (fn)
(let ((*x* (make-huge-object))
Now if you do:
(foo (lambda ()
(make-thread (lambda () (loop (print (eval (read)))))))
then the huge object is never unallocated.
These problems cannot be avoided by a sufficiently smart compiler=20
because it is impossible to know in advance which specials are going to=20
be accessed by a thread. And the programmer is out of luck too.
Now with the current behaviour, one at least stands a chance of getting=20