Re: Looking at reordering memory operations

2018-03-11 Thread John Hening

>
> That (t1 always see the same value of x when x is modified elsewhere) is 
> possible, e.g. in a tight loop reading x and nothing else
>

And this is why I dislike when people say: "volatile field" ensures you 
*only* about ordering. It is not true. Because it also means: 

Mr javac/java/jit, 

I synchronized access to that object so please be careful during reoderding 
*AND* and optimization like: (Java perspective) I don't load the value from 
the memory because noone can modify it (because it is not 
volatile/synchronized- I have no clue that object is shared!)


Gil, thanks for your explanation. 

W dniu piątek, 9 marca 2018 23:20:37 UTC+1 użytkownik John Hening napisał:
>
>
> executor = Executors.newFixedThreadPool(16);
> while(true) {
> SocketChannel connection = serverSocketChannel.accept();
> connection.configueBlocking(false);
> executor.execute(() -> writeTask(connection)); 
> }
> void writeTask(SocketChannel s){
> s.isBlocking();
> }
>
> public final SelectableChannel configureBlocking(boolean block) throws 
> IOException
> {
> synchronized (regLock) {
> ...
> blocking = block;
> }
> return this;
> }
>
>
>
> We see the following situation: the main thread is setting 
> connection.configueBlocking(false)
>
> and another thread (launched by executor) is reading that. So, it looks 
> like a datarace.
>
> My question is:
>
> 1. Here 
> configureBlocking
>
> is synchronized so it behaves as memory barrier. It means that code is ok- 
> even if reading/writing to 
> blocking
>
> field is not synchronized- reading/writing boolean is atomic.
>
> 2. What if 
> configureBlocking
>
> wouldn't be synchronized? What in a such situation? I think that it would 
> be necessary to emit a memory barrier because it is theoretically possible 
> that setting blocking field could be reordered. 
>
> Am I right?
>

-- 
You received this message because you are subscribed to the Google Groups 
"mechanical-sympathy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to mechanical-sympathy+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: Looking at reordering memory operations

2018-03-10 Thread Gil Tene


On Saturday, March 10, 2018 at 1:18:40 PM UTC-8, John Hening wrote:
>
> Gil, thanks for your response. It is very helpful. 
>
> In your specific example above, there is actually no ordering question, 
> because your writeTask() operations doesn't actually observe the state 
> changed by connection.configueBlocking(false)
>
>
> I agree that my question wasn't correct. There is not 'ordering'. I meant 
> visibility. 
>

Visibility and ordering are related. Questions about the visibility of 
state (like that of the field "blocking") apply only to things that 
interact with that state. And when things interact with some state, the 
ordering in which changes to that state becomes visible (with relation to 
changes to other state, like e.g. the enqueing of an operation via 
Executor.execute(), being visible) has (or doesn't have) certain guarantees.

E.g. in the example discussed, with the synchronized blocks in place on 
both the writer and the reader of the field "blocking", we are guaranteed 
that the change of "blocking = false" is visible to the thread that 
executes writeTask() (if writeTask actually uses the value of "blocking" 
obtained within the synchronized block) before the request to execute 
write() is visible to that same thread...
 

>  Without the use of synchronized in isBlocking(), the use of synchronized 
> in configureBlocking() wouldn't make a difference.
>
> Yes, semi-synchronized doesn't work. So, I conclude that without a 
> synchronization the result of `blocking = false` could be invisible for 
> writeTask, am I right?
>

It is not a matter of being invisible. It's a field in a shared object, so 
all operations on it are eventually visible (to things that access it). 
What you can certainly say here is that without using synchronized blocks 
*on both ends* (both the writer and the reader), and without being replaced 
by some other ordering mechanism *on both ends*, your writeTask could 
observe a value of "blocking" that predates the modification of it in the 
thread that calls connection.configueBlocking().
 

> As your question about the possibility of "skipping" some write operations.
>
>
> By skipping I meant 'being invisible for observers'. For example, if one 
> thread t1 read any not-volatile-integer x then it is possible that t1 see 
> always the same value of x (though there is another thread t2 that modifies 
> x).
>

That (t1 always see the same value of x when x is modified elsewhere) is 
possible, e.g. in a tight loop reading x and nothing else. But that will 
only happen if no other ordering constructs force the visibility of 
modifications to x. E.g if thread t1 read some volatile field y that thread 
t2 modifies after modifying x, then thread t1 will observe the modified 
value of x in reads that occur after observing the modified value of y. In 
such a case, it won't "always see the same value of x".


>
>
> 1. It is interesting for me what about a such situation:
>
>while(true) {
> SocketChannel connection = serverSocketChannel.accept();
> connection.configueBlocking(false);
> Unsafe.storeFence();
> executor.execute(() -> writeTask(connection));
> }
> void writeTask(SocketChannel s){
> (***)
> any_static_global_field = s.isBlocking();
> }
>
> For my eye it should work but I have doubts. What does it mean storeFence? 
> Please flush it to the memory immediately! 
>

Unsafe.storeFence doesn't mean "flush...". It means "Ensures lack of 
reordering of stores before the fence with loads or stores after the 
fence." (that's literally what the Javadoc for it says).
 

> So, it will be visible before starting the executor thread. But, it seems 
> that, here, load fence is not necessary (***). Why? The blocking field must 
> be read from memory (there is no possibility that it is cached, because it 
> is read the first time by the executor thread). When it comes to CPU cache 
> it may be cached but cache is coherent = no problem). Moreover, there is no 
> need to ensure ordering here. So, loadFence is not necessary. Yes? 
>

No. At least not quite. For this specific sequence, you already have the 
ordering you want, but not for the reasons you think.

First, please put aside this notion that there is some memory, and some 
cache or store buffer, and some flushing going on. This ordering and 
visibility stuff has nothing to do with any of those potential 
implementation details. and trying to explain things in terms of those 
potential (and incomplete) implementation details mostly serves to confuse 
the issue. A tip I give people for thinking about this stuff is: Always 
think of the compiler as the culprit when it comes to reordering, and in 
that thinking, imagine the compiler being super-smart and super-mean. The 
compiler is allowed to create all sorts of evil, ingenious and 
pre-cognitive reorderings, cachings, and redundant or dead operation 
eliminations (including pre-caching of values it thinks you 

Re: Looking at reordering memory operations

2018-03-10 Thread Gil Tene


On Saturday, March 10, 2018 at 1:18:40 PM UTC-8, John Hening wrote:
>
> Gil, thanks for your response. It is very helpful. 
>
> In your specific example above, there is actually no ordering question, 
> because your writeTask() operations doesn't actually observe the state 
> changed by connection.configueBlocking(false)
>
>
> I agree that my question wasn't correct. There is not 'ordering'. I meant 
> visibility. 
>

No real diference between ordering a
 

>
>
>  Without the use of synchronized in isBlocking(), the use of synchronized 
> in configureBlocking() wouldn't make a difference.
>
> Yes, semi-synchronized doesn't work. So, I conclude that without a 
> synchronization the result of `blocking = false` could be invisible for 
> writeTask, am I right?
>
>
> As your question about the possibility of "skipping" some write operations.
>
>
> By skipping I meant 'being invisible for observers'. For example, if one 
> thread t1 read any not-volatile-integer x then it is possible that t1 see 
> always the same value of x (though there is another thread t2 that modifies 
> x). 
>
>
> 1. It is interesting for me what about a such situation:
>
>while(true) {
> SocketChannel connection = serverSocketChannel.accept();
> connection.configueBlocking(false);
> Unsafe.storeFence();
> executor.execute(() -> writeTask(connection));
> }
> void writeTask(SocketChannel s){
> (***)
> any_static_global_field = s.isBlocking();
> }
>
> For my eye it should work but I have doubts. What does it mean storeFence? 
> Please flush it to the memory immediately! So, it will be visible before 
> starting the executor thread. But, it seems that, here, load fence is not 
> necessary (***). Why? The blocking field must be read from memory (there is 
> no possibility that it is cached, because it is read the first time by the 
> executor thread). When it comes to CPU cache it may be cached but cache is 
> coherent = no problem). Moreover, there is no need to ensure ordering here. 
> So, loadFence is not necessary. Yes? 
>
> 2. 
> volatile int foo;
> ...
> foo = 1;
> foo = 2;
> foo = 3;
>
>
>
> It is very interesting. So, after JITed on x86 it can look like:
>
> mov , 1
> sfence
> mov , 2
> sfence
> mov , 3
> sfence
>
>
>
> Are you sure that CPU can execute that as:
> mov , 3
> sfence
>
>
> ?
>
> I know that: 
>
> mov , 1
> mov , 2
> mov , 3 
>
>
>
> x86-CPU can optimizied it legally. 
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
> W dniu piątek, 9 marca 2018 23:20:37 UTC+1 użytkownik John Hening napisał:
>>
>>
>> executor = Executors.newFixedThreadPool(16);
>> while(true) {
>> SocketChannel connection = serverSocketChannel.accept();
>> connection.configueBlocking(false);
>> executor.execute(() -> writeTask(connection)); 
>> }
>> void writeTask(SocketChannel s){
>> s.isBlocking();
>> }
>>
>> public final SelectableChannel configureBlocking(boolean block) 
>> throws IOException
>> {
>> synchronized (regLock) {
>> ...
>> blocking = block;
>> }
>> return this;
>> }
>>
>>
>>
>> We see the following situation: the main thread is setting 
>> connection.configueBlocking(false)
>>
>> and another thread (launched by executor) is reading that. So, it looks 
>> like a datarace.
>>
>> My question is:
>>
>> 1. Here 
>> configureBlocking
>>
>> is synchronized so it behaves as memory barrier. It means that code is 
>> ok- even if reading/writing to 
>> blocking
>>
>> field is not synchronized- reading/writing boolean is atomic.
>>
>> 2. What if 
>> configureBlocking
>>
>> wouldn't be synchronized? What in a such situation? I think that it would 
>> be necessary to emit a memory barrier because it is theoretically possible 
>> that setting blocking field could be reordered. 
>>
>> Am I right?
>>
>

-- 
You received this message because you are subscribed to the Google Groups 
"mechanical-sympathy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to mechanical-sympathy+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: Looking at reordering memory operations

2018-03-10 Thread John Hening
Gil, thanks for your response. It is very helpful. 

In your specific example above, there is actually no ordering question, 
because your writeTask() operations doesn't actually observe the state 
changed by connection.configueBlocking(false)


I agree that my question wasn't correct. There is not 'ordering'. I meant 
visibility. 


 Without the use of synchronized in isBlocking(), the use of synchronized 
in configureBlocking() wouldn't make a difference.

Yes, semi-synchronized doesn't work. So, I conclude that without a 
synchronization the result of `blocking = false` could be invisible for 
writeTask, am I right?


As your question about the possibility of "skipping" some write operations.


By skipping I meant 'being invisible for observers'. For example, if one 
thread t1 read any not-volatile-integer x then it is possible that t1 see 
always the same value of x (though there is another thread t2 that modifies 
x). 


1. It is interesting for me what about a such situation:

   while(true) {
SocketChannel connection = serverSocketChannel.accept();
connection.configueBlocking(false);
Unsafe.storeFence();
executor.execute(() -> writeTask(connection));
}
void writeTask(SocketChannel s){
(***)
any_static_global_field = s.isBlocking();
}

For my eye it should work but I have doubts. What does it mean storeFence? 
Please flush it to the memory immediately! So, it will be visible before 
starting the executor thread. But, it seems that, here, load fence is not 
necessary (***). Why? The blocking field must be read from memory (there is 
no possibility that it is cached, because it is read the first time by the 
executor thread). When it comes to CPU cache it may be cached but cache is 
coherent = no problem). Moreover, there is no need to ensure ordering here. 
So, loadFence is not necessary. Yes? 

2. 
volatile int foo;
...
foo = 1;
foo = 2;
foo = 3;



It is very interesting. So, after JITed on x86 it can look like:

mov , 1
sfence
mov , 2
sfence
mov , 3
sfence



Are you sure that CPU can execute that as:
mov , 3
sfence


?

I know that: 

mov , 1
mov , 2
mov , 3 



x86-CPU can optimizied it legally. 
















W dniu piątek, 9 marca 2018 23:20:37 UTC+1 użytkownik John Hening napisał:
>
>
> executor = Executors.newFixedThreadPool(16);
> while(true) {
> SocketChannel connection = serverSocketChannel.accept();
> connection.configueBlocking(false);
> executor.execute(() -> writeTask(connection)); 
> }
> void writeTask(SocketChannel s){
> s.isBlocking();
> }
>
> public final SelectableChannel configureBlocking(boolean block) throws 
> IOException
> {
> synchronized (regLock) {
> ...
> blocking = block;
> }
> return this;
> }
>
>
>
> We see the following situation: the main thread is setting 
> connection.configueBlocking(false)
>
> and another thread (launched by executor) is reading that. So, it looks 
> like a datarace.
>
> My question is:
>
> 1. Here 
> configureBlocking
>
> is synchronized so it behaves as memory barrier. It means that code is ok- 
> even if reading/writing to 
> blocking
>
> field is not synchronized- reading/writing boolean is atomic.
>
> 2. What if 
> configureBlocking
>
> wouldn't be synchronized? What in a such situation? I think that it would 
> be necessary to emit a memory barrier because it is theoretically possible 
> that setting blocking field could be reordered. 
>
> Am I right?
>

-- 
You received this message because you are subscribed to the Google Groups 
"mechanical-sympathy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to mechanical-sympathy+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: Looking at reordering memory operations

2018-03-10 Thread Gil Tene
There are many ways in which desired order may be achieved, and you need to 
examine all code that interacts with the state elements to reason about how 
(and if) ordering is enforced. Removing the "synchronized" alone  (in 
some seemingly working code) is "always a bad idea". But that doesn't make 
it "required". The question is, what scheme would you replace it with...

In your specific example above, there is actually no ordering question, 
because your writeTask() operations doesn't actually observe the state 
changed by connection.configueBlocking(false);. It comes close, but the 
fact that nothing is done with the return value of isBlocking() means that 
you have nothing to ask an ordering question about. [the entire execution 
to isBlocking() is dead code, and will be legitimately eliminated by JIT 
compilers after inlining]. However, if you change the example slightly such 
that writeTask() propagated the value of isBlocking() somewhere (e.g. to 
some static volatile boolean), we'd have a question to deal with., 
So let's assume you did that...

In the specific case of the SocketChannel implementation and the example 
above, the modification of the SocketChannel-internal blocking state in 
connection.configueBlocking(false); is guranteed to be visible to the 
potential observation of the same state by the writeTask() operation run by 
some executor thread because *both* configureBlocking() and isBlocked() use 
a synchronized block around the access to the "blocking" field (e.g. at 
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/nio/channels/spi/AbstractSelectableChannel.java#AbstractSelectableChannel.isBlocking%28%29).
 
Without the use of synchronized in isBlocking(), the use of synchronized in 
configureBlocking() wouldn't make a difference.

There are many other ways that this ordering guarantee could be achieved. 
E.g. (for this specific sequence of T1: blocking = false; enqueue 
writeTask() operation; and T2: start writeTask() operation; writeTask 
return value of blocking;) making the SocketChannel-internal blocking field 
volatile would also ensure the writeTask() operation above would return 
blocking = false. And many other means for ordering these are possible.

As your question about the possibility of "skipping" some write operations: 
Write operations are never actually "skipped" (they will eventually 
happen). But in situations where a write is followed by a subsequent 
over-writing write to the same field, the code can legitimately act like it 
"ran fast enough that no-one was able to observe the intermediate state", 
and simply execute the last write. The CPU can do this. The Compiler can do 
this. And the thread running fast enough can do this. It is important to 
understand that this is true even when synchronization and other 
ordering operations exist. E.g the following sequence:

synchronized(regLock) {
  blocking = false;
}
synchronized(regLock) {
  blocking = true;
}
synchronized(regLock) {
  blocking = false;
}

Can be legitimately executed as:

synchronized(regLock) {
  blocking = false;
}

And the sequence:

volatile int foo;
...
foo = 1;
foo = 2;
foo = 3;

can (and will) be legitimately executed as:
foo = 3; 

Ordering questions only come into play if you put other memory-interacting 
things in the middle, between those writes. Then questions about whether or 
not those other things can be re-ordered with the writes come up. Sometimes 
the rules prevent such re-ordering (forcing the actual intermediate writes 
to be executed), and sometimes the rules allow re-ordering (allowing e..g 
writes in loops to be pushed to happen only once when the loop completes). 
In general, in Java, unless some of the "other thing" players are volatile, 
atomics, or synchronized blocks, any reordering is allowed as long as it 
does not change the eventual meaning of computations the sequence. 


On Saturday, March 10, 2018 at 8:13:53 AM UTC-8, John Hening wrote:
>
> ok, reordering is not a good idea to consider here. But, please note that 
> if conifgureBlocking wans't synchronized then a statement:
>
> blocking = block
>
> could be "skipped" on compilation level because JMM doesn't guarantee you 
> that every access to the memory will be "commit" to the main memory. 
> synchronized method/ putting memory barrier would solve that problem. What 
> do you think?
>
>
> W dniu piątek, 9 marca 2018 23:20:37 UTC+1 użytkownik John Hening napisał:
>>
>>
>> executor = Executors.newFixedThreadPool(16);
>> while(true) {
>> SocketChannel connection = serverSocketChannel.accept();
>> connection.configueBlocking(false);
>> executor.execute(() -> writeTask(connection)); 
>> }
>> void writeTask(SocketChannel s){
>> s.isBlocking();
>> }
>>
>> public final SelectableChannel configureBlocking(boolean block) 
>> throws IOException
>> {
>> synchronized (regLock) {
>> ...
>> blocking = 

Re: Looking at reordering memory operations

2018-03-10 Thread John Hening
ok, reordering is not a good idea to consider here. But, please note that 
if conifgureBlocking wans't synchronized then a statement:

blocking = false 

could be "skipped" on compilation level because JMM doesn't guarantee you 
that every access to the memory will be "commit" to the main memory. 


W dniu piątek, 9 marca 2018 23:20:37 UTC+1 użytkownik John Hening napisał:
>
>
> executor = Executors.newFixedThreadPool(16);
> while(true) {
> SocketChannel connection = serverSocketChannel.accept();
> connection.configueBlocking(false);
> executor.execute(() -> writeTask(connection)); 
> }
> void writeTask(SocketChannel s){
> s.isBlocking();
> }
>
> public final SelectableChannel configureBlocking(boolean block) throws 
> IOException
> {
> synchronized (regLock) {
> ...
> blocking = block;
> }
> return this;
> }
>
>
>
> We see the following situation: the main thread is setting 
> connection.configueBlocking(false)
>
> and another thread (launched by executor) is reading that. So, it looks 
> like a datarace.
>
> My question is:
>
> 1. Here 
> configureBlocking
>
> is synchronized so it behaves as memory barrier. It means that code is ok- 
> even if reading/writing to 
> blocking
>
> field is not synchronized- reading/writing boolean is atomic.
>
> 2. What if 
> configureBlocking
>
> wouldn't be synchronized? What in a such situation? I think that it would 
> be necessary to emit a memory barrier because it is theoretically possible 
> that setting blocking field could be reordered. 
>
> Am I right?
>

-- 
You received this message because you are subscribed to the Google Groups 
"mechanical-sympathy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to mechanical-sympathy+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: Looking at reordering memory operations

2018-03-10 Thread Avi Kivity



On 03/10/2018 12:20 AM, John Hening wrote:

|

    executor =Executors.newFixedThreadPool(16);
while(true){
SocketChannelconnection =serverSocketChannel.accept();
        connection.configueBlocking(false);
        executor.execute(()->writeTask(connection));
}
voidwriteTask(SocketChannels){
        s.isBlocking();
}

publicfinalSelectableChannelconfigureBlocking(booleanblock)throwsIOException
{
synchronized(regLock){
...
            blocking =block;
}
returnthis;
}
|



We see the following situation: the main thread is setting
|
connection.configueBlocking(false)
|

and another thread (launched by executor) is reading that. So, it 
looks like a datarace.


My question is:

1. Here
|
configureBlocking
|

is synchronized so it behaves as memory barrier. It means that code is 
ok- even if reading/writing to

|
blocking
|

field is not synchronized- reading/writing boolean is atomic.

2. What if
|
configureBlocking
|

wouldn't be synchronized? What in a such situation? I think that it 
would be necessary to emit a memory barrier because it is 
theoretically possible that setting blocking field could be reordered.



Reordered with what? Ordering is always between at least two data accesses.

If you figure out what "blocking" is protecting, you'll know whether you 
need a memory barrier or not.


--
You received this message because you are subscribed to the Google Groups 
"mechanical-sympathy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to mechanical-sympathy+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Looking at reordering memory operations

2018-03-09 Thread John Hening

executor = Executors.newFixedThreadPool(16);
while(true) {
SocketChannel connection = serverSocketChannel.accept();
connection.configueBlocking(false);
executor.execute(() -> writeTask(connection)); 
}
void writeTask(SocketChannel s){
s.isBlocking();
}

public final SelectableChannel configureBlocking(boolean block) throws 
IOException
{
synchronized (regLock) {
...
blocking = block;
}
return this;
}



We see the following situation: the main thread is setting 
connection.configueBlocking(false)

and another thread (launched by executor) is reading that. So, it looks 
like a datarace.

My question is:

1. Here 
configureBlocking

is synchronized so it behaves as memory barrier. It means that code is ok- 
even if reading/writing to 
blocking

field is not synchronized- reading/writing boolean is atomic.

2. What if 
configureBlocking

wouldn't be synchronized? What in a such situation? I think that it would 
be necessary to emit a memory barrier because it is theoretically possible 
that setting blocking field could be reordered. 

Am I right?

-- 
You received this message because you are subscribed to the Google Groups 
"mechanical-sympathy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to mechanical-sympathy+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.