Hi, during my studies about concurrency in Java (my sources where the CookBook JSR-133 and related Faq, all the articles inside the blog of mechanical sympathy, the book Java Councurrency in Practice and many others) i've earned a lot of confusion and new doubts over the new knowledge that I have acquired...The last one (and maybe the most important for me) was born when i tried to develop an explicit Reentrant Lock for IPC using a memory mapped file:in the implementation i've aligned the memory inside the mapped file to obtain atomicity even in old x86 architectures and i used a 4 byte region to implement my CAS to simulate the tryLock/Lock and Unlock semantic of the intrinsic lock that we hate/love in java...But i don't want to explain more of my implementation, i prefer to reason toward what happen beetween the (usual) calls:lock();try{...do something..}finally{unlock();}the "...do something.." part specifically.How (not only mine) every (implementation of an) explicit lock could ensure that the JVM do not try to reorder all the instructions inside that region of code?
If i use a CAS over a memory region or over a volatile variable (with Unsafe) i'm ensuring that all the instructions that use it in any form are correctly ordered each other but why this should affect the order of the others?
For me it's (almost) clear the with such an explicit lock every "guarded" region would be executed only by one thread at a time but why i should rely on the visibility of the executed code?
Please heeeeeeeelp....My head is bursting :(
Short answer is fences affect compiler and cpu ordering, not just compiler code motion.
Read this, it's very good: https://www.kernel.org/doc/Documentation/memory-barriers.txt
Sent from my phone
--
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-symp...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-sympathy+unsub...@googlegroups.com.
... in the implementation i've aligned the memory inside the mapped file to obtain atomicity even in old x86 architectures and i used a 4 byte region to implement my CAS to simulate the tryLock/Lock and Unlock semantic of the intrinsic lock that we hate/love in java...
But i don't want to explain more of my implementation, i prefer to reason toward what happen beetween the (usual) calls:lock();try{...do something..}finally{unlock();}
For the unlock part i have used an Unsafe.putOrderedInt on the base that in a normal heap implementation of the explicit lock i would use AtomicXXX.lazySet ...I'm hoping that the garantee of correct ordering remain the same of a putVolatileInt, but maybe it induce more failed CAS in a subsequent lock call because the retrieving of the current state of the lock (a getVolatileInt) in the CAS loop could obtain older values of the lock..i'm not sure if the StoreStore barrier emitted on the unlock it's enough...am i wrong?
Hi Gil,and thanks for the advices :),For the unlock part i have used an Unsafe.putOrderedInt on the base that in a normal heap implementation of the explicit lock i would use AtomicXXX.lazySet ...I'm hoping that the garantee of correct ordering remain the same of a putVolatileInt, but maybe it induce more failed CAS in a subsequent lock call because the retrieving of the current state of the lock (a getVolatileInt) in the CAS loop could obtain older values of the lock..i'm not sure if the StoreStore barrier emitted on the unlock it's enough...am i wrong?
--
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-symp...@googlegroups.com.
To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-sympathy+unsub...@googlegroups.com.
Yeh, but Unsafe.loadFence() is "too strong" for what we need here, as it includes an unneeded LoadLoad fence that not even a volatile store would force. I wish the API had were finer grain ones (LoadLoadFence(), LoadStoreFence(), StoreStoreFence(), StoreLoadFence()). Perhaps we'll get that in Java SE 9...Strictly speaking the combination of Unsafe.loadFence() with Unsafe.poutOrdered() is also not enough for unlock semantics, since it is missing the StoreLoad fence that a monitorExit implies, and that would be "intuitively" implied by any unlock or release semantics. The issue here is subtle: while it is OK to move subsequent regular loads "backwards" into a locked region. It is NOT OK to do the same with volatile loads, as moving volatile loads backwards past unlocks or volatile store can have "surprising" behavior effects. The StoreLoad barrier is there to guard against that move of a volatile load (and only that move). In the case of an unlock, it's not really the moving of the volatile load past the unlocking store that's the problem (if it moved to "just before the unlock store", there wouldn't really be a detectable semantic difference). It's the fact that once in there, it can also move backwards past regular loads and stores in the locked region. And since we tend to think of regular stores and loads as being ordered by the lock (against other lock-protected regular loads and stores, and against outside-the-lock volatile loads and stores), this move can create "surprising" side-effects.Most runtimes choose to satisfy this ordering requirement (volatile loads not crossing backwards into a locked region, or backwards past a volatile store) by placing StoreLoad barriers at the monitorExit and volatile stores operations (rather than ahead of each volatile load). As a result, missing the StoreLoad on a roll-your-own unlock will still make it buggy.Together, this means that your valid choices (even with the Java 8 unsafe fence apis) are:1. put an unsafe.loadFence() *and* and an unsafe.storeFence() before the unlock's store operation.2. put an unsafe.fullFence() before the unlock's store operation (which is basically the same as #1).3. Use an unsafe.putVolatileInt() (or equivalent) for the unlock.Of the these, #3 is the cheapest: #1 and #2 include all the ordering that #3 does, but #3 avoids the unnecessary LoadLoad barrier that would prevent regular loads from floating backward into the locked region for some optimizations.
On Monday, June 16, 2014 7:33:03 AM UTC-4, Martin Thompson wrote:
To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-sympathy+unsub...@googlegroups.com.
Hmm, they seem to say the same thing. What part(s) do you think is reversed?
Sent from my phone
--
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-symp...@googlegroups.com.
The JMM wording is more restrictive (prohibits stores after Store2 from moving before the barrier) but I always interpreted Store1 as including all prior stores as well. So the B1...N stores cannot move after the barrier, and I believe that's actually how this barrier is implemented in the JVM (hotspot, at least). You typically don't care about stores after Store2 because the places where StoreStore is used are to order the target store and preceding stores (e.g. target is publishing some state directly queried by other threads, and you want to ensure that when that target store is visible, prior stores are as well - sort of piggybacking). Technically, if stores after Store2 are also visible at that point you don't really care because they're unrelated to the happens-before edge you're inserting. If those subsequent stores did matter, the placement of the StoreStore would've been different.
Sent from my phone
The quoted statement says less about the subsequent and more about the preceding, the JMM cookbook says more about the subsequent and less about the preceding. Let me illustrate with one example and apply both rules.
If B1,B2.... are all stores before Store1 and the barrier, A1,A2... are all after Store2, and Store1,StoreStore,Store2 are as the cookbook describes: B1, B2 ... Store1 StoreStore Store2 A1, A2...
"will prevent stores done before the operation from flowing past the operation" -> A1..N can float before Store1, the B stores are prohibited from floating after Store1.
"Store1's data are visible to other processors before the data associated with Store2 and all subsequent store instructions" -> B1..N can float after Store2. The A stores are prohibited to float before Store2.
Agreed, the wording could elaborate a bit more. Perhaps send an email to concurrency interest so that Doug & co can opine and put it down on their todo list (maybe they're already aware of this, I don't know)?
Sent from my phone
In the case of StoreStore (not the JMM), I think it actually does cover things clearly, because of program order. When describing the program sequence Store1, StoreStore, Store2, Store1 refers to any store before the StoreStore barrier, and Store2 refers to any store after the barrier. The StoreStore does not discuss any specific store, it's just a line in the sand in the stated program order. It obviously does not guarantee any ordering within the preceding stores, or within the subsequent stores, but it guarantees ordering between the two sets.
--
You received this message because you are subscribed to a topic in the Google Groups "mechanical-sympathy" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/mechanical-sympathy/EHQp7lm5cbM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to mechanical-symp...@googlegroups.com.
On Mon, Jun 16, 2014 at 5:37 PM, Francesco Nigro <nigr...@gmail.com> wrote:
Now i have to "solve" (self-creating the requirements :D) :- Reentrancy (i could inspire me with the Doug Lea's Reentrant Lock implementation)
Not sure if it helps, but a simple solution if you want lock()
to be reentrant, is to keep a ThreadLocal
count of the number of times the lock has been acquired by the current thread, decrement it on unlock()
and release the lock only after it reaches 0
. Or if you want to avoid the ThreadLocal
usage overhead (it does have some overhead), you could keep the currentThread
as a non-volatile variable in addition to a counter also stored as a non-volatile variable and check them before doing the CAS on lock()
. You also need to reset them before the final unlock()
that does the volatile store.
You also may not need to have the lock()
method be reentrant per-se. A simple isAcquiredByCurrentThread
method exposed might do the trick (which would save you from incrementing and decrementing a counter). I could get away with that in a simple implementation of a Lock I have.
In my implementation .lock()
in itself is not reentrant, which is OK, because using it can be done by means of a Scala [macro](https://github.com/monifu/monifu/blob/v0.13.0/monifu-core/src/main/scala/monifu/concurrent/locks/Lock.scala#L249):
lock.enter {
// stuff here
}
The above is translated at compile-time (“enter” is a macro) to approximately something like this, so it doesn’t have overhead and provides the API safety of intrinsic locks:
boolean shouldAcquire = ! lock.isAcquiredByCurrentThread
if (shouldAcquire) lock.lock()
try {
// stuff here
}
finally {
if (shouldAcquire) lock.unlock()
}
Too bad Java doesn’t have macros.