From: Sacha
Subject: multiprocessing vs. special variables
Date: 
Message-ID: <Hk2Ri.158822$Ox2.8444161@phobos.telenet-ops.be>
Hello all,

Using lispworks 5, am I understanding this correctly ?

if i have some parameter described like this in code :

(defparameter *yobo-count* 1)

Then spawn 2 threads

if I go like this

(let ((*yobo-count* some-value))
;;more code)

with different some-value in different threads i'm all set and no need 
for synchronization.


if I go like this without a previous binding :

(setf *yobo-count* some-value)

The change will be visible from both threads but i need to do 
synchronization.


Is that right ?

Thanks in advance,
Sacha

From: Pascal Costanza
Subject: Re: multiprocessing vs. special variables
Date: 
Message-ID: <5njt9iFirs8iU1@mid.individual.net>
Sacha wrote:
> Hello all,
> 
> Using lispworks 5, am I understanding this correctly ?
> 
> if i have some parameter described like this in code :
> 
> (defparameter *yobo-count* 1)
> 
> Then spawn 2 threads
> 
> if I go like this
> 
> (let ((*yobo-count* some-value))
> ;;more code)
> 
> with different some-value in different threads i'm all set and no need 
> for synchronization.

That's correct. It's not specified in ANSI Common Lisp, but all 
implementations I am aware of do it like that.

> if I go like this without a previous binding :
> 
> (setf *yobo-count* some-value)
> 
> The change will be visible from both threads but i need to do 
> synchronization.

That's also not specified in ANSI Common Lisp, and different 
implementations handle this differently. To be on the safe side, do 
something like that at the "top" of the thread first:

(let ((*yobo-count* *yobo-count*))
   ...)

...or check the respective implementation's documentation to see what 
other options you have to influence how special bindings are inherited 
across threads.


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Sacha
Subject: Re: multiprocessing vs. special variables
Date: 
Message-ID: <vn3Ri.158899$ZK7.8701040@phobos.telenet-ops.be>
Pascal Costanza wrote:
> Sacha wrote:
>> if I go like this without a previous binding :
>>
>> (setf *yobo-count* some-value)
>>
>> The change will be visible from both threads but i need to do 
>> synchronization.
> 
> That's also not specified in ANSI Common Lisp, and different 
> implementations handle this differently. To be on the safe side, do 
> something like that at the "top" of the thread first:
> 
> (let ((*yobo-count* *yobo-count*))
>   ...)
> 
> ....or check the respective implementation's documentation to see what 
> other options you have to influence how special bindings are inherited 
> across threads.
> 


Thank you Pascal.

My goal is to have a value that is not shadowed for different threads.
As a mean to make sure it does what i want, I eventually settle for 
something like this  :

(defclass container ()
   ((shared-value :accessor shared-value :initform nil)
    (lock :accessor lock :initform (mp:make-lock)))

(defparameter *shared-container* (make-instance 'container))

(defun set-shared-value (value)
   (mp:with-lock ((lock *shared-container*))
      (setf (shared-value *shared-container*) value)))

; and the same thing for get-shared-value


Though that's not simple assignments going on in the real thing.

Thanks again
Sacha
From: Duane Rettig
Subject: Re: multiprocessing vs. special variables
Date: 
Message-ID: <o07ilnhxfq.fsf@gemini.franz.com>
Sacha <····@address.spam> writes:

> Pascal Costanza wrote:
>> Sacha wrote:
>>> if I go like this without a previous binding :
>>>
>>> (setf *yobo-count* some-value)
>>>
>>> The change will be visible from both threads but i need to do
>>> synchronization.
>> That's also not specified in ANSI Common Lisp, and different
>> implementations handle this differently. To be on the safe side, do
>> something like that at the "top" of the thread first:
>> (let ((*yobo-count* *yobo-count*))
>>   ...)
>> ....or check the respective implementation's documentation to see
>> what other options you have to influence how special bindings are
>> inherited across threads.
>>
>
>
> Thank you Pascal.
>
> My goal is to have a value that is not shadowed for different threads.
> As a mean to make sure it does what i want, I eventually settle for
> something like this  :
>
> (defclass container ()
>   ((shared-value :accessor shared-value :initform nil)
>    (lock :accessor lock :initform (mp:make-lock)))
>
> (defparameter *shared-container* (make-instance 'container))
>
> (defun set-shared-value (value)
>   (mp:with-lock ((lock *shared-container*))
>      (setf (shared-value *shared-container*) value)))
>
> ; and the same thing for get-shared-value
>
>
> Though that's not simple assignments going on in the real thing.

In Allegro CL, at least, A globally-special variable has a single
global value, and a value that might be bound per-thread.  If there
are no "lambda-bindings" [e.g. (let ((*shared-container* ...)) ...)]
currently in effect, the "value" of the symbol is the shared global
value, otherwise it is the per-thread value.

Thus, if you want to share *shared-container* between two threads,
don't lambda-bind the variable in those two threads and ot will be
shared (regardless of what you do in other threads).  If the symbol is
exported, and you thus can't guarantee that your users won't bind the
variable, then we provide a function called sys:global-symbol-value,
which is setf-able, to ensure access to the global value only.

-- 
Duane Rettig    ·····@franz.com    Franz Inc.  http://www.franz.com/
555 12th St., Suite 1450               http://www.555citycenter.com/
Oakland, Ca. 94607        Phone: (510) 452-2000; Fax: (510) 452-0182   
From: Harald Hanche-Olsen
Subject: Re: multiprocessing vs. special variables
Date: 
Message-ID: <pco8x637zcb.fsf@shuttle.math.ntnu.no>
+ Duane Rettig <·····@franz.com>:

> In Allegro CL, at least, A globally-special variable has a single
> global value, and a value that might be bound per-thread.  If there
> are no "lambda-bindings" [e.g. (let ((*shared-container* ...)) ...)]
> currently in effect, the "value" of the symbol is the shared global
> value, otherwise it is the per-thread value.
>
> Thus, if you want to share *shared-container* between two threads,
> don't lambda-bind the variable in those two threads and ot will be
> shared (regardless of what you do in other threads).

I'm a bit confused by this.  If you do

(defparameter *foo* :global)

(let ((*foo* :bar))
  ... create and run two threads - call them A and B
  ... do some more computations)

What is the value of *foo* in thread A?  Will it perhaps be :bar until
the outer let is done, and will it then become :global?  Will the two
threads A and B share a binding for *foo* (provided they don't rebind
it of course)?  It is your phrase 'If there are no "lambda bindings"
in effect' that makes me uncertain about the answer.

-- 
* Harald Hanche-Olsen     <URL:http://www.math.ntnu.no/~hanche/>
- It is undesirable to believe a proposition
  when there is no ground whatsoever for supposing it is true.
  -- Bertrand Russell
From: Duane Rettig
Subject: Re: multiprocessing vs. special variables
Date: 
Message-ID: <o0y7e2hmih.fsf@gemini.franz.com>
Harald Hanche-Olsen <······@math.ntnu.no> writes:

> + Duane Rettig <·····@franz.com>:
>
>> In Allegro CL, at least, A globally-special variable has a single
>> global value, and a value that might be bound per-thread.  If there
>> are no "lambda-bindings" [e.g. (let ((*shared-container* ...)) ...)]
>> currently in effect, the "value" of the symbol is the shared global
>> value, otherwise it is the per-thread value.
>>
>> Thus, if you want to share *shared-container* between two threads,
>> don't lambda-bind the variable in those two threads and ot will be
>> shared (regardless of what you do in other threads).
>
> I'm a bit confused by this.  If you do
>
> (defparameter *foo* :global)
>
> (let ((*foo* :bar))
>   ... create and run two threads - call them A and B
>   ... do some more computations)

I assume you are evaluating this form in a third thread, say, Z.

> What is the value of *foo* in thread A?  Will it perhaps be :bar until
> the outer let is done, and will it then become :global?  Will the two
> threads A and B share a binding for *foo* (provided they don't rebind
> it of course)?  It is your phrase 'If there are no "lambda bindings"
> in effect' that makes me uncertain about the answer.

I think you misunderstand the effect of running a thread.  There is no
"inheritance" of special bindings - *foo* is being bound in thread Z,
but is not so bound in threads A or B, and so its global value :global
is exposed, unless forms are run _in_ A or B that bind *foo* there.
The intention is to preserve the semantics of global specials per
thread, so that a single lisp can run two different applications that
use the same names in a portable and still-conformant manner, each in
its own thread and each not being affected by the other.  The trick
with the global slots is a way to maintain communications between
threads that do know about each other.  But as for your intuition
about an inheritance of bindings; we did not choose such a model (and
I believe other CL implementations may have done things similarly)
because if there is an inheritance of bindings, there must then
necessarily be a heirarchy of threads and their callers, and that
would tend to require even more overhead to know when to break the
heirarchy and to make the two threads coequal - it just seems to me to
be a much more heavyweight thread style, similar to the Unix fork/exec
style, which we didn't want - we wanted lighter weight threads, and so
any ties between threads needed to be the exception rather than the
rule.

There is a way to initialize variables on a per-thread or a
per-top-level basis, but most of these two mechanisms are unexported
and undocumented.  The one part that is exposed is the variable called
excl:*cl-default-special-bindings*, which contains an alist of
variables and their initialization forms, to be run whenever a thread
establishes a top-level.  Other than that, and a similar but
undocumented variable that establishes bindings for every thread
(which is also, understandably, kept small to limit overhead) symbols
must explicitly be bound on each thread in order for access to be
shielded from their global values, and to thus use their per-thead
value locations.

-- 
Duane Rettig    ·····@franz.com    Franz Inc.  http://www.franz.com/
555 12th St., Suite 1450               http://www.555citycenter.com/
Oakland, Ca. 94607        Phone: (510) 452-2000; Fax: (510) 452-0182   
From: Harald Hanche-Olsen
Subject: Re: multiprocessing vs. special variables
Date: 
Message-ID: <pcoir568dp9.fsf@shuttle.math.ntnu.no>
+ Duane Rettig <·····@franz.com>:

> I think you misunderstand the effect of running a thread.

I was afraid I did, but your explanation makes it clear that threads
and special bindings work the way I had /hoped/ that they would, and
indeed the way that makes the most sense to me.  But this is the first
time I have seen it explained clearly enough to remove all doubt from
my mind.  Thank you.

-- 
* Harald Hanche-Olsen     <URL:http://www.math.ntnu.no/~hanche/>
- It is undesirable to believe a proposition
  when there is no ground whatsoever for supposing it is true.
  -- Bertrand Russell
From: Sacha
Subject: Re: multiprocessing vs. special variables
Date: 
Message-ID: <e2lRi.160242$G27.8653888@phobos.telenet-ops.be>
Duane Rettig wrote:
> [...]

Thank you very much.

Sacha
From: Pascal Costanza
Subject: Re: multiprocessing vs. special variables
Date: 
Message-ID: <5njuscFipmnoU1@mid.individual.net>
Sacha wrote:

> My goal is to have a value that is not shadowed for different threads.
> As a mean to make sure it does what i want, I eventually settle for 
> something like this  :
> 
> (defclass container ()
>   ((shared-value :accessor shared-value :initform nil)
>    (lock :accessor lock :initform (mp:make-lock)))
> 
> (defparameter *shared-container* (make-instance 'container))
> 
> (defun set-shared-value (value)
>   (mp:with-lock ((lock *shared-container*))
>      (setf (shared-value *shared-container*) value)))
> 
> ; and the same thing for get-shared-value
> 
> 
> Though that's not simple assignments going on in the real thing.

If you're mainly worried about the syntax overhead, that can be removed:

(defparameter +shared-container+ (make-instance 'container))
(define-symbol-macro *shared-container*
   (locked-shared-value +shared-container+))

I don't think, though, that it's a good idea to lock single variables....


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Alex Mizrahi
Subject: Re: multiprocessing vs. special variables
Date: 
Message-ID: <4714d08a$0$90267$14726298@news.sunsite.dk>
(message (Hello 'Sacha)
(you :wrote  :on '(Tue, 16 Oct 2007 12:42:15 GMT))
(

 S> (setf *yobo-count* some-value)

 S> The change will be visible from both threads but i need to do
 S> synchronization.

why do you need synchronization? single variable assignments are (typically) 
atomic.

you'll need syncrhonization if assignment is a part of some other 
operation -- for example, if you do INCF, you (typically) need 
synchronization.
but it has no sense to guard single variable itself -- this should be 
handled by implementation.

)
(With-best-regards '(Alex Mizrahi) :aka 'killer_storm)
"Hanging In The Balance Of Deceit And Blasphemy") 
From: Sacha
Subject: Re: multiprocessing vs. special variables
Date: 
Message-ID: <pA4Ri.158987$n77.8662108@phobos.telenet-ops.be>
Alex Mizrahi wrote:
> (message (Hello 'Sacha)
> (you :wrote  :on '(Tue, 16 Oct 2007 12:42:15 GMT))
> (
> 
>  S> (setf *yobo-count* some-value)
> 
>  S> The change will be visible from both threads but i need to do
>  S> synchronization.
> 
> why do you need synchronization? single variable assignments are (typically) 
> atomic.
> 
> you'll need syncrhonization if assignment is a part of some other 
> operation -- for example, if you do INCF, you (typically) need 
> synchronization.
> but it has no sense to guard single variable itself -- this should be 
> handled by implementation.
> 
> )
> (With-best-regards '(Alex Mizrahi) :aka 'killer_storm)
> "Hanging In The Balance Of Deceit And Blasphemy") 
> 
> 

Yep, sorry the example was pretty bad.

The real thing is a cache which should be visible from all threads while 
still being trade-safe. I had started by defining a bunch of parameters 
as a quick start, and somehow didn't think to move all these into a 
single object.

So I have a solution now, thank you.

Sacha
From: Mark H.
Subject: Re: multiprocessing vs. special variables
Date: 
Message-ID: <1192736161.812826.274280@y27g2000pre.googlegroups.com>
On Oct 16, 7:53 am, "Alex Mizrahi" <········@users.sourceforge.net>
wrote:
> why do you need synchronization? single variable assignments are (typically)
> atomic.

Not necessarily, at least not in C, unless you flag the variable as
"volatile."  Suppose you want to do "x = y", and the compiler has
optimized x into a register in thread A's function, but not in thread
B's function.

Thread A:

load x into register r1
...
load y into register r2
...
r1 = r2
...
store register r1 into x

Thread B:

...
store some other value into x
...

Thread A's r1->x could clobber Thread B's write into x, depending
on the execution interleaving.

mfh
From: Barry Margolin
Subject: Re: multiprocessing vs. special variables
Date: 
Message-ID: <barmar-665173.21444818102007@comcast.dca.giganews.com>
In article <························@y27g2000pre.googlegroups.com>,
 "Mark H." <············@gmail.com> wrote:

> On Oct 16, 7:53 am, "Alex Mizrahi" <········@users.sourceforge.net>
> wrote:
> > why do you need synchronization? single variable assignments are (typically)
> > atomic.
> 
> Not necessarily, at least not in C, unless you flag the variable as
> "volatile."  Suppose you want to do "x = y", and the compiler has
> optimized x into a register in thread A's function, but not in thread
> B's function.

Even volatile variables don't necessarily get atomic assignments.  If a 
variable consists of multiple words (e.g. it's a large structure) it 
will usually require several load/store operations to perform the 
assignment, and the thread may be interrupted in the middle of this.  
POSIX has a typedef sig_atomic_t for variables that can be assigned in 
one instruction, which I expect is "int" on most systems.

> 
> Thread A:
> 
> load x into register r1
> ...
> load y into register r2
> ...
> r1 = r2
> ...
> store register r1 into x
> 
> Thread B:
> 
> ...
> store some other value into x
> ...
> 
> Thread A's r1->x could clobber Thread B's write into x, depending
> on the execution interleaving.
> 
> mfh

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
From: George Neuner
Subject: Re: multiprocessing vs. special variables
Date: 
Message-ID: <p0thh3d3633nsvqeudcasnp11p4sca7kas@4ax.com>
On Thu, 18 Oct 2007 21:44:48 -0400, Barry Margolin
<······@alum.mit.edu> wrote:

>In article <························@y27g2000pre.googlegroups.com>,
> "Mark H." <············@gmail.com> wrote:
>
>> On Oct 16, 7:53 am, "Alex Mizrahi" <········@users.sourceforge.net>
>> wrote:
>> > why do you need synchronization? single variable assignments are (typically)
>> > atomic.
>> 
>> Not necessarily, at least not in C, unless you flag the variable as
>> "volatile."  Suppose you want to do "x = y", and the compiler has
>> optimized x into a register in thread A's function, but not in thread
>> B's function.
>
>Even volatile variables don't necessarily get atomic assignments.  If a 
>variable consists of multiple words (e.g. it's a large structure) it 
>will usually require several load/store operations to perform the 
>assignment, and the thread may be interrupted in the middle of this.  
>POSIX has a typedef sig_atomic_t for variables that can be assigned in 
>one instruction, which I expect is "int" on most systems.

Cache line fills/spills are executed atomically - they lock the bus
during execution.  It is theoretically possible to stage a multiword
update in a locked cache line and then burst it to memory, but in
general programs don't have that kind of control over hardware and
data placement in memory.

And it doesn't work on close coupled multiprocessors with cache
snooping.  It did work on some older shared memory NUMA designs that
had completely independent processors.

George
--
for email reply remove "/" from address
From: Alex Mizrahi
Subject: Re: multiprocessing vs. special variables
Date: 
Message-ID: <471b30c0$0$90266$14726298@news.sunsite.dk>
(message (Hello 'Mark)
(you :wrote  :on '(Thu, 18 Oct 2007 19:36:01 -0000))
(

 ??>> why do you need synchronization? single variable assignments are
 ??>> (typically) atomic.

 MH> Not necessarily, at least not in C, unless you flag the variable as
 MH> "volatile."

by atomic i mean that either destination is always seen either as whole 
value being written, or previous value, but never some garbage that is 
half-current half-previous. (*)

so there's no reason to use syncrhonization like mutex (**).

 MH>  Suppose you want to do "x = y", and the compiler has optimized x into
 MH> a register in thread A's function, but not in thread B's function.

thread visibility is completely another question. the other thread might not 
see the result of assingment. even if it would be value in memory, different 
processors (executing different threads) might see different stuff in memory 
(due to caches) unless they are synchronized.

but i've inserted note "typically" for reason there -- probably we are 
speaking about Common Lisp implementations supporting multiprocessing.
and common sense says us that probably if implementations has mature support 
for multiprocessing, just doing SETQ from different threads shouldn't 
produce any disastrous effects. certainly, if you do SETQ in two threads 
"simultaneosly", you won't be sure what result would be -- but just 
inserting mutex will not help you either.
if implementation doesn't follow this rule and just doing SETQ can result in 
segfault or stuff like this, then probably it's some untypical 
implementation and this issue should be covered in it's documentation in 
bold letters. haven't heard of any Common Lisp doing like this.

(*): i'm also assuming that CL implementation uses some sane constructions 
for assingment -- i.e. assignment of 32-bit quantity on 4-byte aligned 
address on 32-bit x86 processor is atomic. certainly insane implementation 
can skrew this, but are there such?

(**): logically mutex shouldn't affect single assignment, but typically 
synchronization primitives issue a memory barrier that will synchronize 
caches of different CPUs, so _indirectly_ it might have effect: value will 
be visible to other CPUs "sooner". but if we just do assignment and do no 
synchronization, probably we don't care much about when other CPU will see 
effects.

my point was that one should guard some logical operations rather than 
single assignments, do you agree with this? :)

)
(With-best-regards '(Alex Mizrahi) :aka 'killer_storm)
"Hanging In The Balance Of Deceit And Blasphemy") 
From: Mark H.
Subject: Re: multiprocessing vs. special variables
Date: 
Message-ID: <1193420323.082972.13890@z24g2000prh.googlegroups.com>
On Oct 21, 3:58 am, "Alex Mizrahi" <········@users.sourceforge.net>
wrote:
> but i've inserted note "typically" for reason there -- probably we are
> speaking about Common Lisp implementations supporting multiprocessing.
> and common sense says us that probably if implementations has mature support
> for multiprocessing, just doing SETQ from different threads shouldn't
> produce any disastrous effects. certainly, if you do SETQ in two threads
> "simultaneosly", you won't be sure what result would be -- but just
> inserting mutex will not help you either.
> if implementation doesn't follow this rule and just doing SETQ can result in
> segfault or stuff like this, then probably it's some untypical
> implementation and this issue should be covered in it's documentation in
> bold letters. haven't heard of any Common Lisp doing like this.

Oh, I see, that's sort of like Python's interpreter needing a great
big lock because it can't handle executing multiple statements at the
same time?

> (**): logically mutex shouldn't affect single assignment, but typically
> synchronization primitives issue a memory barrier that will synchronize
> caches of different CPUs, so _indirectly_ it might have effect: value will
> be visible to other CPUs "sooner". but if we just do assignment and do no
> synchronization, probably we don't care much about when other CPU will see
> effects.
>
> my point was that one should guard some logical operations rather than
> single assignments, do you agree with this? :)

It depends if you're speaking about thread-safety in the interpreter/
runtime, or thread-safety in your code ;-)

Best,
mfh