Search code examples
javacpu-architectureatomicmemory-barrierscompare-and-swap

Memory Barrier Vs CAS


I find that CAS will flush all CPU write cache to main memory。 Is this similar to memory barrier?

If this is true, does this mean CAS can make java Happens-Before work?

For answer:

The CAS is CPU instruction. The barrier is a StoreLoad barrier because what I care about is will the data are written before CAS can be read after CAS.

More Detail: I have this question because I am writing a fork-join built in Java. The implementation is like this

{
    //initialize result container
    Objcet[] result = new Object[];
    //worker finish state count
    AtomicInteger state = new AtomicInteger(result.size);
}

//worker thread i
{
  result[i] = new Object();
  //this is a CAS operation
  state.getAndDecrement(); 

  if(state.get() == 0){
     //do something useing result array
  }
}

I want to know can (do something using result array) part see all result element which is written by other worker thread.


Solution

  • I find that CAS will flush all cpu write cache to main memory。 Is this similar to memory barrier?

    1. It depends on what you mean by CAS. (A specific hardware instruction? An implementation strategy used in the implementation of some Java class?)

    2. It depends on what kind of memory barrier you are talking about. There are a number of different kinds ...

    3. It is not necessarily true that a CAS instruction flushes all dirty cache lines. It depends on how a particular instruction set / hardware implements the CAS instruction.

    It is unclear what you mean by "make happens-before work". Certainly, under some circumstance a CAS instruction would provide the necessary memory coherency properties for a specific happens-before relationship. But not necessarily all relationships. It would depend on how the CAS instruction is implemented by the hardware, and how it was used in the (native) code.

    To be honest, unless you are actually writing a Java native code compiler, you would do better to not try to understanding the intricacies of what a JIT compiler does to implement the Java Memory Model; e.g. whether and how it uses CAS instructions. Instead, just do the happens before analysis to figure out if visibility of updates is guaranteed (by the JLS) for the accesses you are concerned about. IMO, you can safely assume1 that the JIT compilers emit native code that behaves according to the requirements of the Java Memory Model specification.

    1 - If not, the chances are high that other Java developers will already have noticed the problem and submitted bug reports. Assuming that the necessary supporting evidence is included, such reports would be addressed urgently.


    UPDATE

    It turns out from your recent updates and comments that your actual question is about the behavior of AtomicInteger operations.

    The memory semantics of the atomic types are specified in the package javadoc for java.util.concurrent.atomic as follows:

    The memory effects for accesses and updates of atomics generally follow the rules for volatiles, as stated in The Java Language Specification (17.4 Memory Model):

    • get has the memory effects of reading a volatile variable.
    • set has the memory effects of writing (assigning) a volatile variable.
    • lazySet has the memory effects of writing (assigning) a volatile variable except that it permits reorderings with subsequent (but not previous) memory actions that do not themselves impose reordering constraints with ordinary non-volatile writes. Among other usage contexts, lazySet may apply when nulling out, for the sake of garbage collection, a reference that is never accessed again.
    • weakCompareAndSet atomically reads and conditionally writes a variable but does not create any happens-before orderings, so provides no guarantees with respect to previous or subsequent reads and writes of any variables other than the target of the weakCompareAndSet.
    • compareAndSet and all other read-and-update operations such as getAndIncrement have the memory effects of both reading and writing volatile variables.

    As you can see, operations on Atomic types are specified to have memory semantics that are equivalent volatile variables. This should be sufficient to reason about your use of Java atomic types ... without resorting to dubious analogies with CAS instructions and memory barriers.


    Your example is incomplete and it is difficult to understand what it is trying to do. Therefore, I can't comment on its correctness. However, you should be able to analyze it yourself using happens-before logic, etc.