/** 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); } } } }