On Tuesday 08 August 2006 18:45, Nikodemus Siivola wrote:
> G=C3=A1bor Melis <mega@...> writes:
> > Here is a scenario in which this approach fails to protect against
> > threads sharing a binding. A library has a special that it declares
> > thread local. Threads started after the library is loaded will
> > correctly have thread local bindings for the special. But a thread
> > that was started before the library was loaded will see the global
> > binding of the special. If such a thread calls into the lib or
> > accesses the value of the special in another way lossage is
> > imminent. How can such a thread depend on that special? Lots of
> > ways: defining an around method on a method which called, calling
> > callbacks, using symbol-value and find-symbol. Any kind of
> > dependency injection has a chance to fall flat.
> I see your point, but I don't think I agree.
> Any sort of post-initializion injection of thread-local bindings
> seems to me to actually create more interactions between threads, not
> less, and hence more grounds for subtle bugs. In comparison, the bugs=20
> related to *default-special-bindings* and already running threads
> seem relatively simple "load first, run then" issues.
Such a simple thing. But when it leads to thread safety issues it can=20
become quite tricksome.
> Rather handwavy, I know. :/
> I would also argue, that if a variable is already declared (locally
> or globally) special, then any threaded code that manipulates it is
> liable to already assume that the binding is either thread-local or
> shared, and toggling this from the outside seems rather rude and
What's toggled is the "sharedness" of the global binding, obviously the=20
thread-local bindings are left alone. How can a thread be confused by=20
having a special declared thread-local? From the point of view of that=20
thread that is no different from another thread setting the global=20
binding. This is more or less the same as what I wrote to Max in my=20
second mail (see "... as if the global value ..."). This argument holds=20
no water if there are dependencies between threads that are broken by=20
thread-localizing the variable. However, I argue that if that is the=20
case then there is a basic contradiction at the level of intentions:=20
one can either make it always thread-local or not.
> In the cases where the variable is not special, but =C3=BAsed via
> SYMBOL-VALUE and friends, then I believe in most cases the intention
> is to explicitly manipulate the global but unknown at compile-time
> variable -- not a local binding.
Already at this point I'm losing it. I resort to question flooding for=20
the rest of the mail.
> In cases where the variable is not special when the thread starts and
> LOAD/EVAL is called after it has been made special/local, it seems to
> me that the dependency order is broken.
Care to expand on what dependency order you mean here?
> I'm not sure if any of this makes sense, but this seems to be the
> closest I can come to explaining my unease:
> Exhibit 1
> (make-thread (lambda () (loop (setf *foo* (eval (foo))))))
> If *FOO* is made local while the loop is running, then it
> drastically alters the dynamic behaviour.
Exactly how is the dynamic behaviour altered? Is it detectable by the=20
thread? By other threads? What does (FOO) do? Something that uses the=20
value of *FOO*?
> Exhibit 2
> (make-thread (lambda () (loop (setf (symbol-value (foo)) (bar)))))
> Ditto, only more so.
> Exhibit 3
> (make-thread (lambda () (sleep 10) (load
> If the loaded code defines *FOO*, then we have a conflict in any
> case. If the loaded code doesn't define *FOO*, then we have a clear
Maybe if I had understood the first one then I'd stand a chance here.
> > 2) thread local specials must have no global binding
> > Makes sense to me. How to implement it is another question.
> I'm not sure about this. This make thread local specials strongly
> distinct from normal special variables.
Well, if you want distinct bindings for each thread then there is no=20
global binding to speak of, or in other words different threads have=20
different "global" bindings. This distinction is the crux of the matter=20
and I can see no way around it, save making thread-local vars even more=20
different from specials.
> > This patch deviates from allegro's *cl-default-special-bindings* in
> > an important way: in acl the value of that special is an "alist
> > associating special variable symbols with forms to evaluate for
> > binding values" which makes to possible to have (*readtable* .
> > (copy-readtable nil)) copy the current readtable into the child
> > thread or (*db* . (open-db-connection)) to give each thread its own
> > db connection which I think is impossible to do here. Truth to tell
> > I dislike even the allegro way, because it needs to do several
> > little evals at the start of each thread even if those bindings are
> > never used.
> One possibility is to keep initialization _functions_, not values or
> forms in the *default-special-bindings* alist. More flexible then
> just values, and fast compared to EVAL.
Good idea. That makes me think that maybe allegro doesn't really eval=20
them one by one but forms a single LET expression with a funcall in its=20
body. That could even be compiled. But that would probably need some=20
kind of protocol for modifying *d-s-b*.
> (No, I'm not dead set against the injection idea, but I am rather
> skeptical of its benefits.)
> -- Nikodemus Schemer: "Buddha is small, clean, and
> serious." Lispnik: "Buddha is big, has hairy armpits, and laughs."