Synchronization of data read by multiple threads

classic Classic list List threaded Threaded
32 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Synchronization of data read by multiple threads

Ryan LeCompte

Hello all,

 

I know that one has two options for “protecting” data that can be potentially read/written by multiple threads. In order for the main thread to always see the most recently written value, one must declare the variable as “volatile” or synchronize all access to it, such as:

 

synchronized void stop() {

   stopped = true;

}

 

synchronized boolean isStopped() {

   return stopped;

}

 

However, if the “synchronized” approach is taken, does it have to be at such a granular level? Or can it suffice that whenever the variable “stopped” is used, that it’s at least protected by SOME lock? For example, if “stopped” is only directly referenced in three methods that perform various operations, can all three methods be declared as “synchronized” and the above two methods (stop() / isStopped()) simply removed? Or do we always need to have “synchronized accessors” for the variable in question? Also, what happens if there are three methods that use the “stopped” variable, but they are using different locks? For example, let’s say method1 uses “stopped” in a synchronized block on LOCK1, and method2 uses “stopped” in a synchronized block on LOCK2, and method3 uses “stopped” in a synchronized block on LOCK3. Will we still have the same effect as simply declaring the variable as “volatile” here?

 

Thanks,

Ryan

 


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Synchronization of data read by multiple threads

Jeremy Manson
Ryan LeCompte wrote:

> However, if the "synchronized" approach is taken, does it have to be
> at such a granular level? Or can it suffice that whenever the
> variable "stopped" is used, that it's at least protected by SOME
> lock? For example, if "stopped" is only directly referenced in three
> methods that perform various operations, can all three methods be
> declared as "synchronized" and the above two methods (stop() /
> isStopped()) simply removed? Or do we always need to have
> "synchronized accessors" for the variable in question?


You can do it this way.  Synchronized accessors are nice because that
way, anyone who uses the variable in future revisions of the code will
be forced to synchronize.  But if it saves a reasonable amount of
locking overhead, there is no reason not to do what you suggest.

Bear in mind the answer to the next question, though - it is crucial
when deciding whether you have done this correctly or not.

> Also, what happens if there are three methods that use the "stopped"
> variable, but they are using different locks? For example, let's say
> method1 uses "stopped" in a synchronized block on LOCK1, and method2
> uses "stopped" in a synchronized block on LOCK2, and method3 uses
> "stopped" in a synchronized block on LOCK3. Will we still have the
> same effect as simply declaring the variable as "volatile" here?

This is no good.  You won't get ordering or visibility guarantees.
Locks are only guaranteed to be ordered with respect to each other if
they are on the same monitor.  There are a whole host of potential
problems.

The first, and most obvious, one is that you won't get mutual exclusion.

The second is that the compiler can remove a lock if it doesn't think
that lock will ever be used to communicate between threads.  The way it
decides this is by determining if there is another thread that accesses
that lock.  So you are introducing the possibility that the lock will be
eliminated.

It can get even more subtle than that.  So it really needs to be the
same lock.

                                        Jeremy
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

RE: Synchronization of data read by multiple threads

Ryan LeCompte
Thanks for the response, Jeremy. I believe I definitely have a handle on
this now. The idiom that I've described would also be necessary for
non-primitives, right? Also, in practice, how "common" is it that one sees
odd behavior in their programs when they don't properly follow the idiom
that I've described here (synchronization or volatile approach)? I believe
Josh Bloch mentions in his book "Effective Java" that you may only see bad
side effects if you're on a multi processor machine.

Ryan

-----Original Message-----
From: Jeremy Manson [mailto:[hidden email]]
Sent: Tuesday, October 25, 2005 10:37 AM
To: Ryan LeCompte; [hidden email]
Subject: Re: [concurrency-interest] Synchronization of data read by multiple
threads

Ryan LeCompte wrote:

> However, if the "synchronized" approach is taken, does it have to be
> at such a granular level? Or can it suffice that whenever the
> variable "stopped" is used, that it's at least protected by SOME
> lock? For example, if "stopped" is only directly referenced in three
> methods that perform various operations, can all three methods be
> declared as "synchronized" and the above two methods (stop() /
> isStopped()) simply removed? Or do we always need to have
> "synchronized accessors" for the variable in question?


You can do it this way.  Synchronized accessors are nice because that
way, anyone who uses the variable in future revisions of the code will
be forced to synchronize.  But if it saves a reasonable amount of
locking overhead, there is no reason not to do what you suggest.

Bear in mind the answer to the next question, though - it is crucial
when deciding whether you have done this correctly or not.

> Also, what happens if there are three methods that use the "stopped"
> variable, but they are using different locks? For example, let's say
> method1 uses "stopped" in a synchronized block on LOCK1, and method2
> uses "stopped" in a synchronized block on LOCK2, and method3 uses
> "stopped" in a synchronized block on LOCK3. Will we still have the
> same effect as simply declaring the variable as "volatile" here?

This is no good.  You won't get ordering or visibility guarantees.
Locks are only guaranteed to be ordered with respect to each other if
they are on the same monitor.  There are a whole host of potential
problems.

The first, and most obvious, one is that you won't get mutual exclusion.

The second is that the compiler can remove a lock if it doesn't think
that lock will ever be used to communicate between threads.  The way it
decides this is by determining if there is another thread that accesses
that lock.  So you are introducing the possibility that the lock will be
eliminated.

It can get even more subtle than that.  So it really needs to be the
same lock.

                                        Jeremy

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Synchronization of data read by multiple threads

Jeremy Manson
Ryan LeCompte wrote:
> Thanks for the response, Jeremy. I believe I definitely have a handle on
> this now. The idiom that I've described would also be necessary for
> non-primitives, right?

If the field being accessed were part of some invariant that required
mutual exclusion / atomicity guarantees for multiple fields, then yes.

> Also, in practice, how "common" is it that one sees
> odd behavior in their programs when they don't properly follow the idiom
> that I've described here (synchronization or volatile approach)? I believe
> Josh Bloch mentions in his book "Effective Java" that you may only see bad
> side effects if you're on a multi processor machine.

You can see lots of nasty things on a uniprocessor.  The compiler can
perform transformations that cause results on a uniprocessor that are
just as surprising as any results on a multiprocessor.  YMMV, of course,
depending on the program, the compiler, and the architecture.

For example, consider the following:

boolean stopped = false;

Thread 1:
while (!stopped) {
   // do stuff not affecting stopped
}

Thread 2:
stopped = true;

It would be perfectly legal for a compiler to examine the code in Thread
1 and determine that it does not change the value of stopped.  It could
then decide that what you have in Thread 1 is an infinite loop, and
remove the loop guard:

Replacement Thread 1:
if (!stopped) {
   while (true) {
     // do stuff
   }
}

And Thread 1 will never end, regardless of what Thread 2 does.

On the other hand, if you declare stopped to be volatile, you are
telling the compiler it can be modified by another thread.  It then
won't perform this transformation.

                                        Jeremy
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

RE: Synchronization of data read by multiple threads

Ryan LeCompte
I'm a bit unclear with your first answer regarding a non-primitive field.
What exactly do you mean by "invariant" here? I was thinking of a
non-primitive field, such as (String foo;) that could be read/modified by
multiple threads. If the program required that whenever the variable "foo"
is used in method1, method2, or method3 that it's most recently written
value is always returned, then method1/method2/method3 would have to be
synchronized (on the same lock of course) OR the 'foo' variable would have
to be declared volatile.

Ryan

-----Original Message-----
From: Jeremy Manson [mailto:[hidden email]]
Sent: Tuesday, October 25, 2005 11:17 AM
To: Ryan LeCompte
Cc: [hidden email]
Subject: Re: [concurrency-interest] Synchronization of data read by multiple
threads

Ryan LeCompte wrote:
> Thanks for the response, Jeremy. I believe I definitely have a handle on
> this now. The idiom that I've described would also be necessary for
> non-primitives, right?

If the field being accessed were part of some invariant that required
mutual exclusion / atomicity guarantees for multiple fields, then yes.

> Also, in practice, how "common" is it that one sees
> odd behavior in their programs when they don't properly follow the idiom
> that I've described here (synchronization or volatile approach)? I believe
> Josh Bloch mentions in his book "Effective Java" that you may only see bad
> side effects if you're on a multi processor machine.

You can see lots of nasty things on a uniprocessor.  The compiler can
perform transformations that cause results on a uniprocessor that are
just as surprising as any results on a multiprocessor.  YMMV, of course,
depending on the program, the compiler, and the architecture.

For example, consider the following:

boolean stopped = false;

Thread 1:
while (!stopped) {
   // do stuff not affecting stopped
}

Thread 2:
stopped = true;

It would be perfectly legal for a compiler to examine the code in Thread
1 and determine that it does not change the value of stopped.  It could
then decide that what you have in Thread 1 is an infinite loop, and
remove the loop guard:

Replacement Thread 1:
if (!stopped) {
   while (true) {
     // do stuff
   }
}

And Thread 1 will never end, regardless of what Thread 2 does.

On the other hand, if you declare stopped to be volatile, you are
telling the compiler it can be modified by another thread.  It then
won't perform this transformation.

                                        Jeremy

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Synchronization of data read by multiple threads

Brian Goetz
In reply to this post by Ryan LeCompte
> I believe
> Josh Bloch mentions in his book "Effective Java" that you may only see bad
> side effects if you're on a multi processor machine.

I don't believe he said that, but if he did, he was wrong.  You can see
bad side effects as the result of compiler optimizations (instruction
reordering, hoisting values into registers, etc) as well.

> It can get even more subtle than that.  So it really needs to be the
> same lock.

I can't stress enough the importance of this point.  Even if all
accesses are "with synchronization", if you are using different locks,
you might as well be using no synchronization at all.

The Simple Way: For each shared mutable variable in your program, make
sure that _every_ access (read or write) to that variable is done with
the _same_ lock held.

The Complicated Way: Learn the Java Memory Model.  Prove that there is a
happens-before edge between every write of a shared variable and
subsequent reads of that variable.

There really isn't much of a middle ground.


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

RE: Synchronization of data read by multiple threads

Ryan LeCompte
Page 190 of Effective Java:

"Unless you are running on a multiprocessor, you are unlikely to observe the
problematic behavior in practice, but there are no guarantees."

Thanks for the extra information!

Ryan

-----Original Message-----
From: Brian Goetz [mailto:[hidden email]]
Sent: Tuesday, October 25, 2005 11:55 AM
To: Ryan LeCompte
Cc: 'Jeremy Manson'; [hidden email]
Subject: Re: [concurrency-interest] Synchronization of data read by multiple
threads

> I believe
> Josh Bloch mentions in his book "Effective Java" that you may only see bad
> side effects if you're on a multi processor machine.

I don't believe he said that, but if he did, he was wrong.  You can see
bad side effects as the result of compiler optimizations (instruction
reordering, hoisting values into registers, etc) as well.

> It can get even more subtle than that.  So it really needs to be the
> same lock.

I can't stress enough the importance of this point.  Even if all
accesses are "with synchronization", if you are using different locks,
you might as well be using no synchronization at all.

The Simple Way: For each shared mutable variable in your program, make
sure that _every_ access (read or write) to that variable is done with
the _same_ lock held.

The Complicated Way: Learn the Java Memory Model.  Prove that there is a
happens-before edge between every write of a shared variable and
subsequent reads of that variable.

There really isn't much of a middle ground.


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Synchronization of data read by multiplethreads

Dawid Kurzyniec
Ryan LeCompte wrote:

>Page 190 of Effective Java:
>
>"Unless you are running on a multiprocessor, you are unlikely to observe the
>problematic behavior in practice, but there are no guarantees."
>
>  
>
"Unlikely" <> "Never" :)

Not using synchronization properly is like driving in the night without
lights - maybe, if the traffic is low, nothing bad will happen, but why
on Earth would you do that? :)

Also, if you drive like this often and in high traffic, it's pretty sure
that eventually a bad thing will happen. Similarly with lack of
synchronization - it will hit you or somebody sooner or later, and it be
usually in the worst possible moment. And you really gain nothing by not
using synchronization properly.

This is not some artificial academic problem; this is a debugging
nightmare to be taken seriously.

Regards,
Dawid

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Synchronization of data read by multiple threads

Jeremy Manson
In reply to this post by Ryan LeCompte
Ryan LeCompte wrote:

> I'm a bit unclear with your first answer regarding a non-primitive field.
> What exactly do you mean by "invariant" here? I was thinking of a
> non-primitive field, such as (String foo;) that could be read/modified by
> multiple threads. If the program required that whenever the variable "foo"
> is used in method1, method2, or method3 that it's most recently written
> value is always returned, then method1/method2/method3 would have to be
> synchronized (on the same lock of course) OR the 'foo' variable would have
> to be declared volatile.
>
> Ryan

I think I may have misinterpreted your question.  This is correct.

Having said that, the recent discussion on volatile should be a good
indication that you shouldn't use it unless you are absolutely sure that
it will do what you expect it to do.

                                        Jeremy

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Synchronization of data read by multiple threads

Gregg Wonderly-2
In reply to this post by Jeremy Manson


Jeremy Manson wrote:

> It would be perfectly legal for a compiler to examine the code in Thread
> 1 and determine that it does not change the value of stopped.  It could
> then decide that what you have in Thread 1 is an infinite loop, and
> remove the loop guard:
>
> Replacement Thread 1:
> if (!stopped) {
>   while (true) {
>     // do stuff
>   }
> }
>
> And Thread 1 will never end, regardless of what Thread 2 does.

I don't believe this is a valid optimization.  If the visibility of stopped is
such that it can be referenced outside of the context the compiler is
optimizing, then it can't possibly make this change to the code.  In that code,
it would be perfectly valid for someone to code

synchronized( something ) {
        stopped = true;
}

and that would make the "stopped" change visible to the Thread 1 context.  This
is java and late bindings can wreak havoc on such optimizations that are
possible in other languages.

If Jeremy is suggesting that the compiler has done complete visibility analysis
and can absolutely determine that the value is not altered, perhaps it might
make that change.

I'm not convinced that such an optimization strategy would be a safe thing to
do.  It will create such amazingly ugly bugs.  In code that has failed to
utilize volatile (it didn't really work before) correctly, there are problems
that will be very difficult to find with such changes to the code.  Now that
volatile does actually work, it will be very unhelpful for the language to have
compilers and JITs being this agressive, out of the box.

It would be very nice to have some tools that would look for variables that are
read and written in different methods, in unsynchronized (from the method level
down) blocks.  If they could do more global analysis too, that would be great.
Then, you could scan your code and look for disparate paths of reading and
alteration and then go study them to decide whether there were problems there or
not.

Gregg Wonderly

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Synchronization of data read by multiple threads

Joe Bowbeer
Gregg Wonderly <[hidden email]> wrote:
> I'm not convinced that such an optimization strategy would be a safe thing to
> do.

It's not a new thing, btw.  Symantec's JIT was doing this in 1997.

Note that the "compiler" in these memory model discussions refers to
the bytecode to native compiler as well as the source to bytecode
compiler.

I believe it is perfectly valid for the 2native compiler to hoist the
"stopped" access out of the loop -- until such time as additional code
starts to execute that might change the value of the flag.

I think hotspot is already equipped to "uncompile" code on the fly,
that is, to undo optimizations when preconditions have changed.

In any event, the JMM now officially sanctions this optimization, so be warned.


On 10/25/05, Gregg Wonderly <[hidden email]> wrote:

>
>
> Jeremy Manson wrote:
> > It would be perfectly legal for a compiler to examine the code in Thread
> > 1 and determine that it does not change the value of stopped.  It could
> > then decide that what you have in Thread 1 is an infinite loop, and
> > remove the loop guard:
> >
> > Replacement Thread 1:
> > if (!stopped) {
> >   while (true) {
> >     // do stuff
> >   }
> > }
> >
> > And Thread 1 will never end, regardless of what Thread 2 does.
>
> I don't believe this is a valid optimization.  If the visibility of stopped is
> such that it can be referenced outside of the context the compiler is
> optimizing, then it can't possibly make this change to the code.  In that code,
> it would be perfectly valid for someone to code
>
> synchronized( something ) {
>         stopped = true;
> }
>
> and that would make the "stopped" change visible to the Thread 1 context.  This
> is java and late bindings can wreak havoc on such optimizations that are
> possible in other languages.
>
> If Jeremy is suggesting that the compiler has done complete visibility analysis
> and can absolutely determine that the value is not altered, perhaps it might
> make that change.
>
> I'm not convinced that such an optimization strategy would be a safe thing to
> do.  It will create such amazingly ugly bugs.  In code that has failed to
> utilize volatile (it didn't really work before) correctly, there are problems
> that will be very difficult to find with such changes to the code.  Now that
> volatile does actually work, it will be very unhelpful for the language to have
> compilers and JITs being this agressive, out of the box.
>
> It would be very nice to have some tools that would look for variables that are
> read and written in different methods, in unsynchronized (from the method level
> down) blocks.  If they could do more global analysis too, that would be great.
> Then, you could scan your code and look for disparate paths of reading and
> alteration and then go study them to decide whether there were problems there or
> not.
>
> Gregg Wonderly
>

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Synchronization of data read by multiple threads

Jeremy Manson
In reply to this post by Gregg Wonderly-2
Gregg Wonderly wrote:
>
>
> Jeremy Manson wrote:
>

[reminder: stopped is NOT volatile]

>> It would be perfectly legal for a compiler to examine the code in
>> Thread 1 and determine that it does not change the value of stopped.  
>> It could then decide that what you have in Thread 1 is an infinite
>> loop, and remove the loop guard:
>>
>> Replacement Thread 1:
>> if (!stopped) {
>>   while (true) {
>>     // do stuff
>>   }
>> }
>>
>> And Thread 1 will never end, regardless of what Thread 2 does.
>
>
> I don't believe this is a valid optimization.  If the visibility of
> stopped is such that it can be referenced outside of the context the
> compiler is optimizing, then it can't possibly make this change to the
> code.  In that code, it would be perfectly valid for someone to code
>
> synchronized( something ) {
>     stopped = true;
> }
>
> and that would make the "stopped" change visible to the Thread 1
> context.  

If you want to use a variable to communicate between threads, you need
to synchronize both sides.  You could do:

Thread 1:
while (true) {
   synchronized (something) {
      if (stopped) break;
   }
   // stuff
}

Thread 2:
synchronized (something) {
   stopped = true;
}

And that would work fine.  But if Thread 1 does not have the
synchronization, then it is not incumbent on that thread to be able to
tell that Thread 2's update has occurred.

If you have to rely on visibility analysis to do compiler optimizations,
then you aren't going to be able to perform any optimizations at all.
Or, rather, few enough that you are going to pay a substantial
performance hit.

 > This is java and late bindings can wreak havoc on such optimizations
 > that are possible in other languages.

Late bindings have nothing to do with this; you can imagine, if you
like, that there are no method calls inside the loop.

It is true that a compiler is unlikely to perform this transformation if
there are unknown method calls in the loop.  On the other hand, with
aggressive inlining, what constitutes an unknown method call may be a
little unpredictable.

Bear in mind as well that when I say "compiler", I actually mean "JIT
compiler".

> If Jeremy is suggesting that the compiler has done complete visibility
> analysis and can absolutely determine that the value is not altered,
> perhaps it might make that change.

A compiler can make that transformation.  If you want visibility outside
the thread, then you must use synchronization or make stopped volatile.

> I'm not convinced that such an optimization strategy would be a safe
> thing to do.  It will create such amazingly ugly bugs.  In code that has
> failed to utilize volatile (it didn't really work before) correctly,
> there are problems that will be very difficult to find with such changes
> to the code.  Now that volatile does actually work, it will be very
> unhelpful for the language to have compilers and JITs being this
> agressive, out of the box.

What makes you think that compilers weren't doing it before?  All it is
is loop invariant code motion.

Now that volatile does work, programmers should be using it.  Or better
still, if they don't understand the issues well enough to use it
safely, they should use a JSR-166 ExecutorService for this pattern.

                                                Jeremy
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Synchronization of data read by multiple threads

Elias Ross-5
In reply to this post by Gregg Wonderly-2
On Tue, 2005-10-25 at 13:31 -0500, Gregg Wonderly wrote:

> It would be very nice to have some tools that would look for variables that are
> read and written in different methods, in unsynchronized (from the method level
> down) blocks.  If they could do more global analysis too, that would be great.
> Then, you could scan your code and look for disparate paths of reading and
> alteration and then go study them to decide whether there were problems there or
> not.

The "findbugs" analysis tool does an okay job looking for internal class
inconsistencies.

http://findbugs.sourceforge.net/bugDescriptions.html#IS2_INCONSISTENT_SYNC

Is this what you're talking about?

It will catch most unintentional synchronization mistakes.

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Synchronization of data read by multiple threads

Brian Goetz
In reply to this post by Gregg Wonderly-2
 > I don't believe this is a valid optimization.  If the visibility of
 > stopped is such that it can be referenced outside of the context the
 > compiler is optimizing, then it can't possibly make this change to the
 > code.  In that code, it would be perfectly valid for someone to code

You would be incorrect, then.

To put it in terms of the new JMM: the compiler can prove that the only
writes to a non-volatile 'stopped' that can happen-before the next read
to stopped occur in the body of the loop.  If it can prove that there
are no such writes in the body of the loop, it becomes loop-invariant
and can be hoisted.

The fact that another thread may want to write to stopped does not
matter -- it is only writes that happen-before the next read in this
thread that the compiler need pay attention to.

All access to shared mutable state must be done with appropriate
synchronization, otherwise bad things happen.

>> It would be perfectly legal for a compiler to examine the code in
>> Thread 1 and determine that it does not change the value of stopped.  
>> It could then decide that what you have in Thread 1 is an infinite
>> loop, and remove the loop guard:
>>
>> Replacement Thread 1:
>> if (!stopped) {
>>   while (true) {
>>     // do stuff
>>   }
>> }
>>
>> And Thread 1 will never end, regardless of what Thread 2 does.
>
>

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Synchronization of data read by multiple threads

Gregg Wonderly-3
Brian Goetz wrote:

>  > I don't believe this is a valid optimization.  If the visibility of
>  > stopped is such that it can be referenced outside of the context the
>  > compiler is optimizing, then it can't possibly make this change to the
>  > code.  In that code, it would be perfectly valid for someone to code
>
> You would be incorrect, then.
>
> To put it in terms of the new JMM: the compiler can prove that the only
> writes to a non-volatile 'stopped' that can happen-before the next read
> to stopped occur in the body of the loop.  If it can prove that there
> are no such writes in the body of the loop, it becomes loop-invariant
> and can be hoisted.

I won't dispute that it can do this.  My argument is that hoisting the loop
control as a loop invariant, is almost never what a user wants, unless they are
specifying a constant for control.  Typically there is a mistake in the
software, as we are discussing here, which needs to be fixed.  People make
mistakes.  They are imperfect.  It's fun and challenging to do the work to make
your optimizer this smart.  I just think JIT design should take a safe approach
to considering the intent of a user, or allow the user to turn it off (which we
can still in Sun's JVM).

If I decide that I want my software loop to be controlled by a lazy read on an
unsynchornized/non-volatile value so that eventually, it changes its behavior
based on that value, but I don't care when, there is no means to do that given
what the JMM says.

I have to undergo the potential system impact of using volatile/synchronized
access.  For highspeed, active application logic, where large areas of memory
are touched, a user today, casually familar with typical caching will guess that
they'll eventually see changes.  The JMM says that this can't be guarenteed.

I am not sure that we are actually adding value to the Java proposition by
creating the expectation from the JITs perspective that all out optimizations
are possible now that volatile has a meaning.  I understand, potentially, how
important that can be to performance.  I'm just frightened by some of the
thoughts that go through my head based on some of the Java software that I've
seen over the years.

I'm lamenting more about the state of things, then trying to argue that my
thinking should somehow be right.

Thanks for putting down words and examples for others to read here to help them
understand all the ins and outs that they need to consider.

Gregg Wonderly
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Synchronization of data read by multiple threads

Doug Lea
Gregg Wonderly wrote:
>
> If I decide that I want my software loop to be controlled by a lazy read
> on an unsynchornized/non-volatile value so that eventually, it changes
> its behavior based on that value, but I don't care when, there is no
> means to do that given what the JMM says.

Most of the posts so far focussed on correctness, which is of
course primary. Here are a couple of notes on performance side:

You should find it comforting that on nearly all processors,
a volatile read costs no more than any other kind of read,
except that compiler/VM/processors cannot retain/cache values in
registers etc. Since you seem to want exactly this effect in
these cases, volatile is just want you want conceptually as well.

Volatile writes on the other hand cost almost as much in terms of
underlying barriers as synchronized blocks. Similarly for compareAndSet
on atomics. Although both are still are normally cheaper than locks
because they never block on locks when contended. Except that if
you have a lot of memory contention retrying compareAndSets
and the like, you are usually better off using locks or related
synchronizations to cause some of those threads to block rather than
fight for cache lines etc.

So, basically, the story on the performance side meshes pretty
well with that on the correctness side. So don't worry, just be
happy and obey the rules :-)

-Doug

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Synchronization of data read by multiple threads

Chris Purcell
> You should find it comforting that on nearly all processors, a
> volatile read costs no more than any other kind of read

I was under the impression that more recent Intels added a read-read
barrier to the instruction set for this purpose: only older chips don't
reorder reads.

On the other hand, x86 guarantees write ordering, making write-write
barriers (and hence volatile writes) cheap-as-free.

Am I missing something?

Cheers,
Chris

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Synchronization of data read by multiple threads

Doug Lea
Chris Purcell wrote:
>>
>
> Am I missing something?


I believe that the most common technique for supporting for volatiles
etc in JVMs is basically the one laid out in the "recipes" section of:
   http://gee.cs.oswego.edu/dl/jmm/cookbook.html

-Doug

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Synchronization of data read by multiple threads

Chris Purcell
> I believe that the most common technique for supporting for volatiles
> etc in JVMs is basically the one laid out in the "recipes" section of:
>   http://gee.cs.oswego.edu/dl/jmm/cookbook.html

Ah! More restrictions on a volatile write than I'd thought.

That processor barrier table will come in handy for me. Many thanks.

Chris

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

RE: Synchronization of data read by multiplethreads

Boehm, Hans
In reply to this post by Dawid Kurzyniec
I think there is another related issue here, which came up in the JSR133
discussions.

Assume you have two threads (stopped initially true):

Thread 1:

while (stopped) {}

Thread 2:

...
stopped = false;

Even if the compiler doesn't "optimize" thread 1 into an infinite loop,
there is no guarantee that a uniprocessor scheduler would ever schedule
thread 2 after thread 1 gets a chance to run.  Indeed, a number of older
JVMs wouldn't ever run thread 2 again, and I think some current more
specialized JVMs still won't.  These two scenarios are not quite
indistinguishable in general, but they're close.  Thus, compiler
optimizations aside, code like this is a bit brittle, even if stopped is
declared volatile.

If you wanted to prevent the compiler optimization, things like this
make that tricky to specify (or to take advantage of).

Hans

> -----Original Message-----
> From: [hidden email]
> [mailto:[hidden email]] On Behalf
> Of Gregg Wonderly
> Sent: Wednesday, October 26, 2005 7:56 AM
> To: [hidden email]
> Subject: Re: [concurrency-interest] Synchronization of data
> read by multiplethreads
>
>
> Brian Goetz wrote:
> >  > I don't believe this is a valid optimization.  If the
> visibility of  
> > > stopped is such that it can be referenced outside of the
> context the  
> > > compiler is optimizing, then it can't possibly make this
> change to
> > the  > code.  In that code, it would be perfectly valid for
> someone to
> > code
> >
> > You would be incorrect, then.
> >
> > To put it in terms of the new JMM: the compiler can prove that the
> > only
> > writes to a non-volatile 'stopped' that can happen-before
> the next read
> > to stopped occur in the body of the loop.  If it can prove
> that there
> > are no such writes in the body of the loop, it becomes
> loop-invariant
> > and can be hoisted.
>
> I won't dispute that it can do this.  My argument is that
> hoisting the loop
> control as a loop invariant, is almost never what a user
> wants, unless they are
> specifying a constant for control.  Typically there is a
> mistake in the
> software, as we are discussing here, which needs to be fixed.
>  People make
> mistakes.  They are imperfect.  It's fun and challenging to
> do the work to make
> your optimizer this smart.  I just think JIT design should
> take a safe approach
> to considering the intent of a user, or allow the user to
> turn it off (which we
> can still in Sun's JVM).
>
> If I decide that I want my software loop to be controlled by
> a lazy read on an
> unsynchornized/non-volatile value so that eventually, it
> changes its behavior
> based on that value, but I don't care when, there is no means
> to do that given
> what the JMM says.
>
> I have to undergo the potential system impact of using
> volatile/synchronized
> access.  For highspeed, active application logic, where large
> areas of memory
> are touched, a user today, casually familar with typical
> caching will guess that
> they'll eventually see changes.  The JMM says that this can't
> be guarenteed.
>
> I am not sure that we are actually adding value to the Java
> proposition by
> creating the expectation from the JITs perspective that all
> out optimizations
> are possible now that volatile has a meaning.  I understand,
> potentially, how
> important that can be to performance.  I'm just frightened by
> some of the
> thoughts that go through my head based on some of the Java
> software that I've
> seen over the years.
>
> I'm lamenting more about the state of things, then trying to
> argue that my
> thinking should somehow be right.
>
> Thanks for putting down words and examples for others to read
> here to help them
> understand all the ins and outs that they need to consider.
>
> Gregg Wonderly
> _______________________________________________
> Concurrency-interest mailing list
> [hidden email]
> http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
>

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
12