1 /*
2  * Written by Doug Lea and Martin Buchholz with assistance from
3  * members of JCP JSR-166 Expert Group and released to the public
4  * domain, as explained at
5  * http://creativecommons.org/publicdomain/zero/1.0/
6  */
7 
8 /*
9  * Source:
10  * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/test/tck-jsr166e/AtomicDoubleArrayTest.java?revision=1.13
11  * (Modified to adapt to guava coding conventions)
12  */
13 
14 package com.google.common.util.concurrent;
15 
16 import java.util.Arrays;
17 
18 /** Unit test for {@link AtomicDoubleArray}. */
19 public class AtomicDoubleArrayTest extends JSR166TestCase {
20 
21   private static final double[] VALUES = {
22     Double.NEGATIVE_INFINITY,
23     -Double.MAX_VALUE,
24     (double) Long.MIN_VALUE,
25     (double) Integer.MIN_VALUE,
26     -Math.PI,
27     -1.0,
28     -Double.MIN_VALUE,
29     -0.0,
30     +0.0,
31     Double.MIN_VALUE,
32     1.0,
33     Math.PI,
34     (double) Integer.MAX_VALUE,
35     (double) Long.MAX_VALUE,
36     Double.MAX_VALUE,
37     Double.POSITIVE_INFINITY,
38     Double.NaN,
39     Float.MAX_VALUE,
40   };
41 
42   /** The notion of equality used by AtomicDoubleArray */
bitEquals(double x, double y)43   static boolean bitEquals(double x, double y) {
44     return Double.doubleToRawLongBits(x) == Double.doubleToRawLongBits(y);
45   }
46 
assertBitEquals(double x, double y)47   static void assertBitEquals(double x, double y) {
48     assertEquals(Double.doubleToRawLongBits(x), Double.doubleToRawLongBits(y));
49   }
50 
51   /** constructor creates array of given size with all elements zero */
testConstructor()52   public void testConstructor() {
53     AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
54     for (int i = 0; i < SIZE; i++) {
55       assertBitEquals(0.0, aa.get(i));
56     }
57   }
58 
59   /** constructor with null array throws NPE */
testConstructor2NPE()60   public void testConstructor2NPE() {
61     double[] a = null;
62     try {
63       new AtomicDoubleArray(a);
64       fail();
65     } catch (NullPointerException success) {
66     }
67   }
68 
69   /** constructor with array is of same size and has all elements */
testConstructor2()70   public void testConstructor2() {
71     AtomicDoubleArray aa = new AtomicDoubleArray(VALUES);
72     assertEquals(VALUES.length, aa.length());
73     for (int i = 0; i < VALUES.length; i++) {
74       assertBitEquals(VALUES[i], aa.get(i));
75     }
76   }
77 
78   /** constructor with empty array has size 0 and contains no elements */
testConstructorEmptyArray()79   public void testConstructorEmptyArray() {
80     AtomicDoubleArray aa = new AtomicDoubleArray(new double[0]);
81     assertEquals(0, aa.length());
82     try {
83       aa.get(0);
84       fail();
85     } catch (IndexOutOfBoundsException success) {
86     }
87   }
88 
89   /** constructor with length zero has size 0 and contains no elements */
testConstructorZeroLength()90   public void testConstructorZeroLength() {
91     AtomicDoubleArray aa = new AtomicDoubleArray(0);
92     assertEquals(0, aa.length());
93     try {
94       aa.get(0);
95       fail();
96     } catch (IndexOutOfBoundsException success) {
97     }
98   }
99 
100   /** get and set for out of bound indices throw IndexOutOfBoundsException */
testIndexing()101   public void testIndexing() {
102     AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
103     for (int index : new int[] {-1, SIZE}) {
104       try {
105         aa.get(index);
106         fail();
107       } catch (IndexOutOfBoundsException success) {
108       }
109       try {
110         aa.set(index, 1.0);
111         fail();
112       } catch (IndexOutOfBoundsException success) {
113       }
114       try {
115         aa.lazySet(index, 1.0);
116         fail();
117       } catch (IndexOutOfBoundsException success) {
118       }
119       try {
120         aa.compareAndSet(index, 1.0, 2.0);
121         fail();
122       } catch (IndexOutOfBoundsException success) {
123       }
124       try {
125         aa.weakCompareAndSet(index, 1.0, 2.0);
126         fail();
127       } catch (IndexOutOfBoundsException success) {
128       }
129       try {
130         aa.getAndAdd(index, 1.0);
131         fail();
132       } catch (IndexOutOfBoundsException success) {
133       }
134       try {
135         aa.addAndGet(index, 1.0);
136         fail();
137       } catch (IndexOutOfBoundsException success) {
138       }
139     }
140   }
141 
142   /** get returns the last value set at index */
testGetSet()143   public void testGetSet() {
144     AtomicDoubleArray aa = new AtomicDoubleArray(VALUES.length);
145     for (int i = 0; i < VALUES.length; i++) {
146       assertBitEquals(0.0, aa.get(i));
147       aa.set(i, VALUES[i]);
148       assertBitEquals(VALUES[i], aa.get(i));
149       aa.set(i, -3.0);
150       assertBitEquals(-3.0, aa.get(i));
151     }
152   }
153 
154   /** get returns the last value lazySet at index by same thread */
testGetLazySet()155   public void testGetLazySet() {
156     AtomicDoubleArray aa = new AtomicDoubleArray(VALUES.length);
157     for (int i = 0; i < VALUES.length; i++) {
158       assertBitEquals(0.0, aa.get(i));
159       aa.lazySet(i, VALUES[i]);
160       assertBitEquals(VALUES[i], aa.get(i));
161       aa.lazySet(i, -3.0);
162       assertBitEquals(-3.0, aa.get(i));
163     }
164   }
165 
166   /** compareAndSet succeeds in changing value if equal to expected else fails */
testCompareAndSet()167   public void testCompareAndSet() {
168     AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
169     for (int i : new int[] {0, SIZE - 1}) {
170       double prev = 0.0;
171       double unused = Math.E + Math.PI;
172       for (double x : VALUES) {
173         assertBitEquals(prev, aa.get(i));
174         assertFalse(aa.compareAndSet(i, unused, x));
175         assertBitEquals(prev, aa.get(i));
176         assertTrue(aa.compareAndSet(i, prev, x));
177         assertBitEquals(x, aa.get(i));
178         prev = x;
179       }
180     }
181   }
182 
183   /** compareAndSet in one thread enables another waiting for value to succeed */
184 
testCompareAndSetInMultipleThreads()185   public void testCompareAndSetInMultipleThreads() throws InterruptedException {
186     final AtomicDoubleArray a = new AtomicDoubleArray(1);
187     a.set(0, 1.0);
188     Thread t =
189         newStartedThread(
190             new CheckedRunnable() {
191               @Override
192               public void realRun() {
193                 while (!a.compareAndSet(0, 2.0, 3.0)) {
194                   Thread.yield();
195                 }
196               }
197             });
198 
199     assertTrue(a.compareAndSet(0, 1.0, 2.0));
200     awaitTermination(t);
201     assertBitEquals(3.0, a.get(0));
202   }
203 
204   /** repeated weakCompareAndSet succeeds in changing value when equal to expected */
testWeakCompareAndSet()205   public void testWeakCompareAndSet() {
206     AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
207     for (int i : new int[] {0, SIZE - 1}) {
208       double prev = 0.0;
209       double unused = Math.E + Math.PI;
210       for (double x : VALUES) {
211         assertBitEquals(prev, aa.get(i));
212         assertFalse(aa.weakCompareAndSet(i, unused, x));
213         assertBitEquals(prev, aa.get(i));
214         while (!aa.weakCompareAndSet(i, prev, x)) {;
215         }
216         assertBitEquals(x, aa.get(i));
217         prev = x;
218       }
219     }
220   }
221 
222   /** getAndSet returns previous value and sets to given value at given index */
testGetAndSet()223   public void testGetAndSet() {
224     AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
225     for (int i : new int[] {0, SIZE - 1}) {
226       double prev = 0.0;
227       for (double x : VALUES) {
228         assertBitEquals(prev, aa.getAndSet(i, x));
229         prev = x;
230       }
231     }
232   }
233 
234   /** getAndAdd returns previous value and adds given value */
testGetAndAdd()235   public void testGetAndAdd() {
236     AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
237     for (int i : new int[] {0, SIZE - 1}) {
238       for (double x : VALUES) {
239         for (double y : VALUES) {
240           aa.set(i, x);
241           double z = aa.getAndAdd(i, y);
242           assertBitEquals(x, z);
243           assertBitEquals(x + y, aa.get(i));
244         }
245       }
246     }
247   }
248 
249   /** addAndGet adds given value to current, and returns current value */
testAddAndGet()250   public void testAddAndGet() {
251     AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
252     for (int i : new int[] {0, SIZE - 1}) {
253       for (double x : VALUES) {
254         for (double y : VALUES) {
255           aa.set(i, x);
256           double z = aa.addAndGet(i, y);
257           assertBitEquals(x + y, z);
258           assertBitEquals(x + y, aa.get(i));
259         }
260       }
261     }
262   }
263 
264   static final long COUNTDOWN = 100000;
265 
266   class Counter extends CheckedRunnable {
267     final AtomicDoubleArray aa;
268     volatile long counts;
269 
Counter(AtomicDoubleArray a)270     Counter(AtomicDoubleArray a) {
271       aa = a;
272     }
273 
274     @Override
realRun()275     public void realRun() {
276       for (; ; ) {
277         boolean done = true;
278         for (int i = 0; i < aa.length(); i++) {
279           double v = aa.get(i);
280           assertTrue(v >= 0);
281           if (v != 0) {
282             done = false;
283             if (aa.compareAndSet(i, v, v - 1.0)) {
284               ++counts;
285             }
286           }
287         }
288         if (done) {
289           break;
290         }
291       }
292     }
293   }
294 
295   /**
296    * Multiple threads using same array of counters successfully update a number of times equal to
297    * total count
298    */
299 
testCountingInMultipleThreads()300   public void testCountingInMultipleThreads() throws InterruptedException {
301     final AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
302     for (int i = 0; i < SIZE; i++) {
303       aa.set(i, (double) COUNTDOWN);
304     }
305     Counter c1 = new Counter(aa);
306     Counter c2 = new Counter(aa);
307     Thread t1 = newStartedThread(c1);
308     Thread t2 = newStartedThread(c2);
309     awaitTermination(t1);
310     awaitTermination(t2);
311     assertEquals(SIZE * COUNTDOWN, c1.counts + c2.counts);
312   }
313 
314   /** a deserialized serialized array holds same values */
testSerialization()315   public void testSerialization() throws Exception {
316     AtomicDoubleArray x = new AtomicDoubleArray(SIZE);
317     for (int i = 0; i < SIZE; i++) {
318       x.set(i, (double) -i);
319     }
320     AtomicDoubleArray y = serialClone(x);
321     assertTrue(x != y);
322     assertEquals(x.length(), y.length());
323     for (int i = 0; i < SIZE; i++) {
324       assertBitEquals(x.get(i), y.get(i));
325     }
326 
327     AtomicDoubleArray a = new AtomicDoubleArray(VALUES);
328     AtomicDoubleArray b = serialClone(a);
329     assertFalse(a.equals(b));
330     assertFalse(b.equals(a));
331     assertEquals(a.length(), b.length());
332     for (int i = 0; i < VALUES.length; i++) {
333       assertBitEquals(a.get(i), b.get(i));
334     }
335   }
336 
337   /** toString returns current value */
testToString()338   public void testToString() {
339     AtomicDoubleArray aa = new AtomicDoubleArray(VALUES);
340     assertEquals(Arrays.toString(VALUES), aa.toString());
341     assertEquals("[]", new AtomicDoubleArray(0).toString());
342     assertEquals("[]", new AtomicDoubleArray(new double[0]).toString());
343   }
344 
345   /** compareAndSet treats +0.0 and -0.0 as distinct values */
testDistinctZeros()346   public void testDistinctZeros() {
347     AtomicDoubleArray aa = new AtomicDoubleArray(SIZE);
348     for (int i : new int[] {0, SIZE - 1}) {
349       assertFalse(aa.compareAndSet(i, -0.0, 7.0));
350       assertFalse(aa.weakCompareAndSet(i, -0.0, 7.0));
351       assertBitEquals(+0.0, aa.get(i));
352       assertTrue(aa.compareAndSet(i, +0.0, -0.0));
353       assertBitEquals(-0.0, aa.get(i));
354       assertFalse(aa.compareAndSet(i, +0.0, 7.0));
355       assertFalse(aa.weakCompareAndSet(i, +0.0, 7.0));
356       assertBitEquals(-0.0, aa.get(i));
357     }
358   }
359 }
360