RE: JavaMemoryModel: More ordering constraints does not imply mor e reliable software

From: David Holmes (dholmes@dstc.edu.au)
Date: Wed Jul 11 2001 - 19:35:40 EDT


Miles Sabin wrote:

> Here's what such a class might look like,
>
> public final class NotAMonitor
> {
> private volatile boolean force;
>
> public void enter() // reads are equivalent to monitor enters
> {
> boolean temp = force;
> }
>
> public void exit() // writes are equivalent to monitor exits
> {
> force = true;
> }
> }
> Now here's how it would fit in to Bills reworking of the optimistic
> readers example,
>
> volatile int vno = 0;
> NotAMonitor notAMonitor = new NotAMonitor();
> int a,b;
> synchronized void inc(int x)
> {
> int oldVersion = vno;
> vno = oldVersion+1;
> notAMonitor.enter(); /* Don't allow accesses to be hoisted */
> a += x;
> b += x;
> vno = oldVersion+2;
> }
> ....
>
> Does this really help? I think only to a _very_ small extent, and at
> the cost of an object allocation and the loss of the possiblity of
> doing anything more useful with the encapsulated volatile.

Hmmm - I guess I was thinking more of encapsulating the read/write/read
sequence as a total barrier, rather than individual reads and writes. But
you are right, such methods add very little when they are needed simply to
complement the volatile actions that already exist in the code.

An alternative is to provide more complete encapsulated volatile wrapper
classes to replace the use of simple volatiles. For example, something like:

class VolatileInt {
   private volatile int value;
   private volatile boolean force;

   public VolatileInt(int value) {
       this.value = value;
   }

   public int get() {
       force = force; // gives r/w/r sequence - total barrier
       return value;
   }

   public void set(int newVal) {
       boolean temp = force; // again a r/w/r total barrier
       value = newVal;
       temp = force;
   }

   public void increment() {
       value = value + 1; // r/w the r again
       boolean temp = force;
   }

   // decrement ... etc
}

I'm assuming, perhaps incorrectly, that these volatile accesses won't get
reordered with respect to each other.

You could then redo the concurrent readers optimisation as:

  VolatileInt vno = new VolatileInt(0);
  int a,b;
  synchronized void inc(int x)
  {
    int oldVersion = vno.get();
    vno.set(oldVersion+1);
    a += x;
    b += x;
    vno.set(oldVersion+2);
  }

  /* unsynchronized */
  int prod()
  {
    int v1 = vno.get(); /* pre-inspect */
    int t1 = a;
    int t2 = b;
    int v2 = vno.get(); /* post-inspect */
    if (v1 == v2 && v1%2 == 0)
      return t1*t2; /* commit */
    else ... /* abort */
  }

This makes the code much cleaner to me - and a good compiler could get rid
of most of the overhead: inline method calls; remove redundant accesses;
merge adjacent memory barriers.

Cheers,
David Holmes
-------------------------------
JavaMemoryModel mailing list - http://www.cs.umd.edu/~pugh/java/memoryModel



This archive was generated by hypermail 2b29 : Thu Oct 13 2005 - 07:00:33 EDT