/* * Written by Doug Lea and Martin Buchholz with assistance from * members of JCP JSR-166 Expert Group and released to the public * domain, as explained at * http://creativecommons.org/publicdomain/zero/1.0/ */ /* * Source: * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/test/tck-jsr166e/AtomicDoubleArrayTest.java?revision=1.13 * (Modified to adapt to guava coding conventions) */ package com.google.common.util.concurrent; import junit.framework.*; import java.util.Arrays; /** * Unit test for {@link AtomicDoubleArray}. */ public class AtomicDoubleArrayTest extends JSR166TestCase { private static final double[] VALUES = { Double.NEGATIVE_INFINITY, -Double.MAX_VALUE, (double) Long.MIN_VALUE, (double) Integer.MIN_VALUE, -Math.PI, -1.0, -Double.MIN_VALUE, -0.0, +0.0, Double.MIN_VALUE, 1.0, Math.PI, (double) Integer.MAX_VALUE, (double) Long.MAX_VALUE, Double.MAX_VALUE, Double.POSITIVE_INFINITY, Double.NaN, Float.MAX_VALUE, }; /** The notion of equality used by AtomicDoubleArray */ static boolean bitEquals(double x, double y) { return Double.doubleToRawLongBits(x) == Double.doubleToRawLongBits(y); } static void assertBitEquals(double x, double y) { assertEquals(Double.doubleToRawLongBits(x), Double.doubleToRawLongBits(y)); } /** * constructor creates array of given size with all elements zero */ public void testConstructor() { AtomicDoubleArray aa = new AtomicDoubleArray(SIZE); for (int i = 0; i < SIZE; i++) { assertBitEquals(0.0, aa.get(i)); } } /** * constructor with null array throws NPE */ public void testConstructor2NPE() { try { double[] a = null; AtomicDoubleArray aa = new AtomicDoubleArray(a); shouldThrow(); } catch (NullPointerException success) {} } /** * constructor with array is of same size and has all elements */ public void testConstructor2() { AtomicDoubleArray aa = new AtomicDoubleArray(VALUES); assertEquals(VALUES.length, aa.length()); for (int i = 0; i < VALUES.length; i++) { assertBitEquals(VALUES[i], aa.get(i)); } } /** * constructor with empty array has size 0 and contains no elements */ public void testConstructorEmptyArray() { AtomicDoubleArray aa = new AtomicDoubleArray(new double[0]); assertEquals(0, aa.length()); try { aa.get(0); shouldThrow(); } catch (IndexOutOfBoundsException success) {} } /** * constructor with length zero has size 0 and contains no elements */ public void testConstructorZeroLength() { AtomicDoubleArray aa = new AtomicDoubleArray(0); assertEquals(0, aa.length()); try { aa.get(0); shouldThrow(); } catch (IndexOutOfBoundsException success) {} } /** * get and set for out of bound indices throw IndexOutOfBoundsException */ public void testIndexing() { AtomicDoubleArray aa = new AtomicDoubleArray(SIZE); for (int index : new int[] { -1, SIZE }) { try { aa.get(index); shouldThrow(); } catch (IndexOutOfBoundsException success) {} try { aa.set(index, 1.0); shouldThrow(); } catch (IndexOutOfBoundsException success) {} try { aa.lazySet(index, 1.0); shouldThrow(); } catch (IndexOutOfBoundsException success) {} try { aa.compareAndSet(index, 1.0, 2.0); shouldThrow(); } catch (IndexOutOfBoundsException success) {} try { aa.weakCompareAndSet(index, 1.0, 2.0); shouldThrow(); } catch (IndexOutOfBoundsException success) {} try { aa.getAndAdd(index, 1.0); shouldThrow(); } catch (IndexOutOfBoundsException success) {} try { aa.addAndGet(index, 1.0); shouldThrow(); } catch (IndexOutOfBoundsException success) {} } } /** * get returns the last value set at index */ public void testGetSet() { AtomicDoubleArray aa = new AtomicDoubleArray(VALUES.length); for (int i = 0; i < VALUES.length; i++) { assertBitEquals(0.0, aa.get(i)); aa.set(i, VALUES[i]); assertBitEquals(VALUES[i], aa.get(i)); aa.set(i, -3.0); assertBitEquals(-3.0, aa.get(i)); } } /** * get returns the last value lazySet at index by same thread */ public void testGetLazySet() { AtomicDoubleArray aa = new AtomicDoubleArray(VALUES.length); for (int i = 0; i < VALUES.length; i++) { assertBitEquals(0.0, aa.get(i)); aa.lazySet(i, VALUES[i]); assertBitEquals(VALUES[i], aa.get(i)); aa.lazySet(i, -3.0); assertBitEquals(-3.0, aa.get(i)); } } /** * compareAndSet succeeds in changing value if equal to expected else fails */ public void testCompareAndSet() { AtomicDoubleArray aa = new AtomicDoubleArray(SIZE); for (int i : new int[] { 0, SIZE - 1}) { double prev = 0.0; double unused = Math.E + Math.PI; for (double x : VALUES) { assertBitEquals(prev, aa.get(i)); assertFalse(aa.compareAndSet(i, unused, x)); assertBitEquals(prev, aa.get(i)); assertTrue(aa.compareAndSet(i, prev, x)); assertBitEquals(x, aa.get(i)); prev = x; } } } /** * compareAndSet in one thread enables another waiting for value * to succeed */ public void testCompareAndSetInMultipleThreads() throws InterruptedException { final AtomicDoubleArray a = new AtomicDoubleArray(1); a.set(0, 1.0); Thread t = newStartedThread(new CheckedRunnable() { public void realRun() { while (!a.compareAndSet(0, 2.0, 3.0)) { Thread.yield(); } }}); assertTrue(a.compareAndSet(0, 1.0, 2.0)); awaitTermination(t); assertBitEquals(3.0, a.get(0)); } /** * repeated weakCompareAndSet succeeds in changing value when equal * to expected */ public void testWeakCompareAndSet() { AtomicDoubleArray aa = new AtomicDoubleArray(SIZE); for (int i : new int[] { 0, SIZE - 1}) { double prev = 0.0; double unused = Math.E + Math.PI; for (double x : VALUES) { assertBitEquals(prev, aa.get(i)); assertFalse(aa.weakCompareAndSet(i, unused, x)); assertBitEquals(prev, aa.get(i)); while (!aa.weakCompareAndSet(i, prev, x)) { ; } assertBitEquals(x, aa.get(i)); prev = x; } } } /** * getAndSet returns previous value and sets to given value at given index */ public void testGetAndSet() { AtomicDoubleArray aa = new AtomicDoubleArray(SIZE); for (int i : new int[] { 0, SIZE - 1}) { double prev = 0.0; for (double x : VALUES) { assertBitEquals(prev, aa.getAndSet(i, x)); prev = x; } } } /** * getAndAdd returns previous value and adds given value */ public void testGetAndAdd() { AtomicDoubleArray aa = new AtomicDoubleArray(SIZE); for (int i : new int[] { 0, SIZE - 1}) { for (double x : VALUES) { for (double y : VALUES) { aa.set(i, x); double z = aa.getAndAdd(i, y); assertBitEquals(x, z); assertBitEquals(x + y, aa.get(i)); } } } } /** * addAndGet adds given value to current, and returns current value */ public void testAddAndGet() { AtomicDoubleArray aa = new AtomicDoubleArray(SIZE); for (int i : new int[] { 0, SIZE - 1}) { for (double x : VALUES) { for (double y : VALUES) { aa.set(i, x); double z = aa.addAndGet(i, y); assertBitEquals(x + y, z); assertBitEquals(x + y, aa.get(i)); } } } } static final long COUNTDOWN = 100000; class Counter extends CheckedRunnable { final AtomicDoubleArray aa; volatile long counts; Counter(AtomicDoubleArray a) { aa = a; } public void realRun() { for (;;) { boolean done = true; for (int i = 0; i < aa.length(); i++) { double v = aa.get(i); assertTrue(v >= 0); if (v != 0) { done = false; if (aa.compareAndSet(i, v, v - 1.0)) { ++counts; } } } if (done) { break; } } } } /** * Multiple threads using same array of counters successfully * update a number of times equal to total count */ public void testCountingInMultipleThreads() throws InterruptedException { final AtomicDoubleArray aa = new AtomicDoubleArray(SIZE); for (int i = 0; i < SIZE; i++) { aa.set(i, (double) COUNTDOWN); } Counter c1 = new Counter(aa); Counter c2 = new Counter(aa); Thread t1 = newStartedThread(c1); Thread t2 = newStartedThread(c2); awaitTermination(t1); awaitTermination(t2); assertEquals(c1.counts + c2.counts, SIZE * COUNTDOWN); } /** * a deserialized serialized array holds same values */ public void testSerialization() throws Exception { AtomicDoubleArray x = new AtomicDoubleArray(SIZE); for (int i = 0; i < SIZE; i++) { x.set(i, (double) -i); } AtomicDoubleArray y = serialClone(x); assertTrue(x != y); assertEquals(x.length(), y.length()); for (int i = 0; i < SIZE; i++) { assertBitEquals(x.get(i), y.get(i)); } AtomicDoubleArray a = new AtomicDoubleArray(VALUES); AtomicDoubleArray b = serialClone(a); assertFalse(a.equals(b)); assertFalse(b.equals(a)); assertEquals(a.length(), b.length()); for (int i = 0; i < VALUES.length; i++) { assertBitEquals(a.get(i), b.get(i)); } } /** * toString returns current value */ public void testToString() { AtomicDoubleArray aa = new AtomicDoubleArray(VALUES); assertEquals(Arrays.toString(VALUES), aa.toString()); assertEquals("[]", new AtomicDoubleArray(0).toString()); assertEquals("[]", new AtomicDoubleArray(new double[0]).toString()); } /** * compareAndSet treats +0.0 and -0.0 as distinct values */ public void testDistinctZeros() { AtomicDoubleArray aa = new AtomicDoubleArray(SIZE); for (int i : new int[] { 0, SIZE - 1}) { assertFalse(aa.compareAndSet(i, -0.0, 7.0)); assertFalse(aa.weakCompareAndSet(i, -0.0, 7.0)); assertBitEquals(+0.0, aa.get(i)); assertTrue(aa.compareAndSet(i, +0.0, -0.0)); assertBitEquals(-0.0, aa.get(i)); assertFalse(aa.compareAndSet(i, +0.0, 7.0)); assertFalse(aa.weakCompareAndSet(i, +0.0, 7.0)); assertBitEquals(-0.0, aa.get(i)); } } }