/** compareAndSet in one thread enables another waiting for value to succeed */
  public void testCompareAndSetInMultipleThreads() throws Exception {
    x = one;
    final AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> a;
    try {
      a =
          AtomicReferenceFieldUpdater.newUpdater(
              AtomicReferenceFieldUpdaterTest.class, Integer.class, "x");
    } catch (RuntimeException ok) {
      return;
    }

    Thread t =
        new Thread(
            new CheckedRunnable() {
              public void realRun() {
                while (!a.compareAndSet(AtomicReferenceFieldUpdaterTest.this, two, three))
                  Thread.yield();
              }
            });

    t.start();
    assertTrue(a.compareAndSet(this, one, two));
    t.join(LONG_DELAY_MS);
    assertFalse(t.isAlive());
    assertSame(a.get(this), three);
  }
 /** getAndSet returns previous value and sets to given value */
 public void testGetAndSet() {
   AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> a;
   try {
     a =
         AtomicReferenceFieldUpdater.newUpdater(
             AtomicReferenceFieldUpdaterTest.class, Integer.class, "x");
   } catch (RuntimeException ok) {
     return;
   }
   x = one;
   assertSame(one, a.getAndSet(this, zero));
   assertSame(zero, a.getAndSet(this, m10));
   assertSame(m10, a.getAndSet(this, 1));
 }
 boolean casNext(Node cmp, Node val) {
   boolean success = nextUpdater.compareAndSet(this, cmp, val);
   if (!success) {
     System.err.println("setting next failed");
   }
   return success;
 }
 /** Constructor with non-volatile field throws exception */
 public void testConstructor3() {
   try {
     AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> a =
         AtomicReferenceFieldUpdater.newUpdater(
             AtomicReferenceFieldUpdaterTest.class, Integer.class, "w");
     shouldThrow();
   } catch (RuntimeException success) {
   }
 }
 /** compareAndSet succeeds in changing value if equal to expected else fails */
 public void testCompareAndSet() {
   AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> a;
   try {
     a =
         AtomicReferenceFieldUpdater.newUpdater(
             AtomicReferenceFieldUpdaterTest.class, Integer.class, "x");
   } catch (RuntimeException ok) {
     return;
   }
   x = one;
   assertTrue(a.compareAndSet(this, one, two));
   assertTrue(a.compareAndSet(this, two, m4));
   assertSame(m4, a.get(this));
   assertFalse(a.compareAndSet(this, m5, seven));
   assertFalse(seven == a.get(this));
   assertTrue(a.compareAndSet(this, m4, seven));
   assertSame(seven, a.get(this));
 }
 /** get returns the last value lazySet by same thread */
 public void testGetLazySet() {
   AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> a;
   try {
     a =
         AtomicReferenceFieldUpdater.newUpdater(
             AtomicReferenceFieldUpdaterTest.class, Integer.class, "x");
   } catch (RuntimeException ok) {
     return;
   }
   x = one;
   assertSame(one, a.get(this));
   a.lazySet(this, two);
   assertSame(two, a.get(this));
   a.lazySet(this, m3);
   assertSame(m3, a.get(this));
 }
  static class Node {
    static ConcurrentSoftQueue<Node> pool = new ConcurrentSoftQueue<Node>();

    static Node alloc() {
      final int threshold = 2;
      int tryCnt = 0;
      while (true) {
        tryCnt++;
        Node n = pool.poll();
        if (n == null) {
          return new Node();
        } else {
          if (n.getRefCnt() > 0) {
            pool.add(n);
            if (tryCnt <= threshold) continue;
            else return new Node();
          } else {
            if (n.next != null) n.next.decRefCnt();
            n.init();
            return n;
          }
        }
      }
    }

    static void free(Node n) {
      pool.add(n);
    }

    private static void nodeCopy(
        double[] srcKeys,
        Object[] srcVals,
        int srcBegin,
        double[] targetKeys,
        Object[] targetVals,
        int targetBegin,
        int copyLen) {
      System.arraycopy(srcKeys, srcBegin, targetKeys, targetBegin, copyLen);
      System.arraycopy(srcVals, srcBegin, targetVals, targetBegin, copyLen);
    }

    static Node safeNext(Node b) {
      while (true) {
        Node n = b.next;
        if (n == null) return null;
        n.incRefCnt();
        if (n == b.next) return n;
        else release(n);
      }
    }

    static void release(Node n) {
      n.decRefCnt();
    }

    volatile int refcnt;
    volatile int length;
    final double[] keys;
    final Object[] vals;
    volatile Node next;

    Node() {
      this(CHUNK_SIZE);
    }

    Node(int capacity) {
      keys = new double[capacity];
      vals = new Object[capacity];
      refcnt = 1;
      next = null;
    }

    void init() {
      incRefCnt();
      length = 0;
      next = null;
      clearEntry();
    }

    public void clearEntry() {
      Arrays.fill(vals, null);
    }

    public String toString() {
      String str =
          "Node(refcnt:" + refcnt + ", isMarked:" + isMarked() + ", length:" + length + ")[";
      for (int i = 0; i < len(); i++) {
        str += keys[i] + " ";
      }
      str += "]";
      return str;
    }

    void _print() {
      System.out.print("Node(refcnt:" + refcnt + "," + length + "/" + keys.length + ")[");
      for (int i = 0; i < length; i++) {
        // System.out.print(keys[i]+":"+vals[i]+" ");
        System.out.print(keys[i] + " ");
      }
      System.out.println("]");
    }

    static final AtomicReferenceFieldUpdater<Node, Node> nextUpdater =
        AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "next");
    static final AtomicIntegerFieldUpdater<Node> lenUpdater =
        AtomicIntegerFieldUpdater.newUpdater(Node.class, "length");
    static final AtomicIntegerFieldUpdater<Node> refcntUpdater =
        AtomicIntegerFieldUpdater.newUpdater(Node.class, "refcnt");

    boolean casNext(Node cmp, Node val) {
      boolean success = nextUpdater.compareAndSet(this, cmp, val);
      if (!success) {
        System.err.println("setting next failed");
      }
      return success;
    }

    boolean casLen(int cmp, int val) {
      return lenUpdater.compareAndSet(this, cmp, val);
    }

    int incRefCnt() {
      return refcntUpdater.incrementAndGet(this);
    }

    int decRefCnt() {
      int refcnt = refcntUpdater.decrementAndGet(this);
      if (refcnt < 0) {
        throw new RuntimeException("refcnt:" + refcnt);
      }
      return refcnt;
    }

    int getRefCnt() {
      return refcnt;
    }

    boolean isFull() {
      return len() == keys.length;
    }

    int len() {
      int len = length;
      return (len >= 0) ? len : -len;
    }

    boolean mark() {
      int len = length;
      if (len < 0) return false;
      return casLen(len, -len);
    }

    boolean isMarked() {
      return length < 0;
    }

    double first() {
      assert len() != 0;
      return keys[0];
    }

    double last() {
      assert len() != 0;
      return keys[len() - 1];
    }

    boolean contains(double key) {
      return Arrays.binarySearch(keys, 0, len(), key) >= 0;
    }

    int findKeyIndex(double key) {
      return Arrays.binarySearch(keys, 0, len(), key);
    }

    Object get(double key) {
      if (len() == 0) return null;
      int pos;
      if (key == keys[0]) pos = 0;
      else pos = Arrays.binarySearch(keys, 0, len(), key);
      if (pos < 0) return null;
      return vals[pos];
    }

    Object replace(double key, Object expect, Object value) {
      synchronized (this) {
        if (isMarked()) return Retry;

        int len = len();
        int pos = Arrays.binarySearch(keys, 0, len, key);
        if (pos < 0) return null;
        Object old = vals[pos];
        if (expect == null) {
          vals[pos] = value;
          return old;
        } else if (expect.equals(old)) {
          vals[pos] = value;
          return old;
        } else {
          return null;
        }
      }
    }

    // no concurrent read/write access is assumed
    Object put(double key, Object value, ConcurrentDoubleOrderedListMap orderedMap) {
      int len = len();
      int pos = Arrays.binarySearch(keys, 0, len, key);
      if (pos >= 0) {
        Object old = vals[pos];
        vals[pos] = value;
        return old;
      } else {
        pos = -(pos + 1);
        putReally(pos, key, value, orderedMap);
        return null;
      }
    }

    private boolean emptySlotInNextTwo() {
      if (next == null) return false;
      if (!next.isFull()) return true;
      if (next.next == null) return false;
      if (!next.next.isFull()) return true;
      return false;
    }

    // no concurrent read/write access is assumed
    private void putReally(
        int pos, double key, Object value, ConcurrentDoubleOrderedListMap orderedMap) {
      int len = len();
      if (len + 1 <= keys.length) { // inserted in the current node
        nodeCopy(keys, vals, pos, keys, vals, pos + 1, len - pos);
        keys[pos] = key;
        vals[pos] = value;
        length = len + 1;
        if (pos == 0) {
          orderedMap.skipListMap.put(keys[0], this);
          if (len != 0) orderedMap.skipListMap.remove(keys[1], this);
        }
      } else if (emptySlotInNextTwo()) {
        if (pos == len) {
          next.put(key, value, orderedMap);
          return;
        }
        next.put(keys[len - 1], vals[len - 1], orderedMap);
        nodeCopy(keys, vals, pos, keys, vals, pos + 1, len - pos - 1);
        keys[pos] = key;
        vals[pos] = value;
        if (pos == 0) {
          orderedMap.skipListMap.remove(keys[1], this);
          orderedMap.skipListMap.put(keys[0], this);
        }
      } else { // current node is full, so requires a new node
        Node n = Node.alloc();
        double[] nkeys = n.keys;
        Object[] nvals = n.vals;
        int l1 = len / 2, l2 = len - l1;
        if (next == null && pos == len) { // this is the last node, simply add to the new node.
          nkeys[0] = key;
          nvals[0] = value;
          n.length = 1;
          orderedMap.skipListMap.put(nkeys[0], n);
        } else if (pos < l1) { // key,value is stored in the current node
          length = l1 + 1;
          n.length = l2;
          nodeCopy(keys, vals, l1, nkeys, nvals, 0, l2);

          nodeCopy(keys, vals, pos, keys, vals, pos + 1, l1 - pos);
          keys[pos] = key;
          vals[pos] = value;
          if (pos == 0) {
            orderedMap.skipListMap.remove(keys[1]);
            orderedMap.skipListMap.put(keys[0], this);
          }
          orderedMap.skipListMap.put(nkeys[0], n);
        } else { // key,value is stored in the new node
          length = l1;
          n.length = l2 + 1;
          int newpos = pos - l1;

          nodeCopy(keys, vals, l1, nkeys, nvals, 0, newpos);
          nkeys[newpos] = key;
          nvals[newpos] = value;
          nodeCopy(keys, vals, pos, nkeys, nvals, newpos + 1, l2 - newpos);

          orderedMap.skipListMap.put(nkeys[0], n);
        }
        n.next = this.next;
        this.next = n;
      }
    }

    // concurrent read/write access is allowed
    boolean appendNewAtomic(double key, Object value, ConcurrentDoubleOrderedListMap orderedMap) {
      synchronized (this) {
        if (isMarked()) return false;
        if (next != null) return false;

        Node n = Node.alloc();
        n.put(key, value, orderedMap);
        // assert n.len()==1;
        n.next = null;
        boolean success = casNext(null, n);
        assert success;
        return true;
      }
    }
    // concurrent read/write access is allowed
    Object putAtomic(
        double key,
        Object value,
        Node b,
        boolean onlyIfAbsent,
        ConcurrentDoubleOrderedListMap orderedMap) {
      synchronized (b) {
        if (b.isMarked()) return Retry;
        synchronized (this) {
          if (isMarked()) return Retry;

          int len = len();
          int pos = Arrays.binarySearch(keys, 0, len, key);
          if (pos >= 0) {
            Object old = vals[pos];
            if (onlyIfAbsent) {
              if (old == null) {
                vals[pos] = value;
                return null;
              } else {
                return old;
              }
            }
            vals[pos] = value;
            return old;
          }
          pos = -(pos + 1);
          putAtomicReally(b, pos, key, value, orderedMap);
          return null;
        }
      }
    }

    // only used by putAtomic and PutAtomicIfAbsent. Inside synchronized(b) and synchronized(this).
    private void putAtomicReally(
        Node b, int pos, double key, Object value, ConcurrentDoubleOrderedListMap orderedMap) {
      int len = len();
      if (len + 1 <= keys.length) {
        if (pos == len) { // in-place append in the current node
          keys[pos] = key;
          vals[pos] = value;
          length = len + 1;
          if (pos == 0) {
            orderedMap.skipListMap.put(keys[0], this);
          }
        } else { // copied to a new node, replacing the current node
          mark();
          Node n = Node.alloc();
          n.next = this.next;
          if (next != null) next.incRefCnt();
          double[] nkeys = n.keys;
          Object[] nvals = n.vals;
          n.length = len + 1;
          nodeCopy(keys, vals, 0, nkeys, nvals, 0, pos);
          nkeys[pos] = key;
          nvals[pos] = value;
          nodeCopy(keys, vals, pos, nkeys, nvals, pos + 1, len - pos);

          orderedMap.skipListMap.put(nkeys[0], n);
          b.casNext(this, n); // should always succeed.
          if (pos == 0) {
            orderedMap.skipListMap.remove(keys[0], this);
          }
          release(this);
          free(this);
        }
      } else { // requires 2 new nodes, to replace the current node
        mark();
        Node n1 = Node.alloc();
        double[] n1keys = n1.keys;
        Object[] n1vals = n1.vals;
        Node n2 = Node.alloc();
        double[] n2keys = n2.keys;
        Object[] n2vals = n2.vals;
        int l1 = len / 2, l2 = len - l1;
        if (pos < l1) { // key, value stored in n1
          n1.length = l1 + 1;
          n2.length = l2;

          nodeCopy(keys, vals, 0, n1keys, n1vals, 0, pos);
          n1keys[pos] = key;
          n1vals[pos] = value;
          nodeCopy(keys, vals, pos, n1keys, n1vals, pos + 1, l1 - pos);
          nodeCopy(keys, vals, l1, n2keys, n2vals, 0, l2);

          n1.next = n2;
          n2.next = this.next;
          if (next != null) next.incRefCnt();

          orderedMap.skipListMap.put(n1keys[0], n1);
          orderedMap.skipListMap.put(n2keys[0], n2);
          b.casNext(this, n1); // should always succeed.
          if (pos == 0) {
            orderedMap.skipListMap.remove(keys[0], this);
          }
          release(this);
          free(this);
        } else { // key,value is stored in n2
          n1.length = l1;
          n2.length = l2 + 1;
          int newpos = pos - l1;

          nodeCopy(keys, vals, 0, n1keys, n1vals, 0, l1);

          nodeCopy(keys, vals, l1, n2keys, n2vals, 0, newpos);
          n2keys[newpos] = key;
          n2vals[newpos] = value;
          nodeCopy(keys, vals, pos, n2keys, n2vals, newpos + 1, l2 - newpos);

          n1.next = n2;
          n2.next = this.next;
          if (next != null) next.incRefCnt();

          orderedMap.skipListMap.put(n1keys[0], n1);
          orderedMap.skipListMap.put(n2keys[0], n2);
          b.casNext(this, n1); // should always succeed.
          release(this);
          free(this);
        }
      }
    }
  }