1/*
2 * Written by Doug Lea with assistance from members of JCP JSR-166
3 * Expert Group and released to the public domain, as explained at
4 * http://creativecommons.org/publicdomain/zero/1.0/
5 * Other contributors include Andrew Wright, Jeffrey Hayes,
6 * Pat Fisher, Mike Judd.
7 */
8
9package jsr166;
10
11import junit.framework.*;
12import java.util.Arrays;
13import java.util.concurrent.atomic.AtomicIntegerArray;
14
15public class AtomicIntegerArrayTest extends JSR166TestCase {
16
17    /**
18     * constructor creates array of given size with all elements zero
19     */
20    public void testConstructor() {
21        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
22        for (int i = 0; i < SIZE; i++)
23            assertEquals(0, aa.get(i));
24    }
25
26    /**
27     * constructor with null array throws NPE
28     */
29    public void testConstructor2NPE() {
30        try {
31            int[] a = null;
32            AtomicIntegerArray aa = new AtomicIntegerArray(a);
33            shouldThrow();
34        } catch (NullPointerException success) {}
35    }
36
37    /**
38     * constructor with array is of same size and has all elements
39     */
40    public void testConstructor2() {
41        int[] a = { 17, 3, -42, 99, -7 };
42        AtomicIntegerArray aa = new AtomicIntegerArray(a);
43        assertEquals(a.length, aa.length());
44        for (int i = 0; i < a.length; i++)
45            assertEquals(a[i], aa.get(i));
46    }
47
48    /**
49     * get and set for out of bound indices throw IndexOutOfBoundsException
50     */
51    public void testIndexing() {
52        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
53        for (int index : new int[] { -1, SIZE }) {
54            try {
55                aa.get(index);
56                shouldThrow();
57            } catch (IndexOutOfBoundsException success) {}
58            try {
59                aa.set(index, 1);
60                shouldThrow();
61            } catch (IndexOutOfBoundsException success) {}
62            try {
63                aa.lazySet(index, 1);
64                shouldThrow();
65            } catch (IndexOutOfBoundsException success) {}
66            try {
67                aa.compareAndSet(index, 1, 2);
68                shouldThrow();
69            } catch (IndexOutOfBoundsException success) {}
70            try {
71                aa.weakCompareAndSet(index, 1, 2);
72                shouldThrow();
73            } catch (IndexOutOfBoundsException success) {}
74            try {
75                aa.getAndAdd(index, 1);
76                shouldThrow();
77            } catch (IndexOutOfBoundsException success) {}
78            try {
79                aa.addAndGet(index, 1);
80                shouldThrow();
81            } catch (IndexOutOfBoundsException success) {}
82        }
83    }
84
85    /**
86     * get returns the last value set at index
87     */
88    public void testGetSet() {
89        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
90        for (int i = 0; i < SIZE; i++) {
91            aa.set(i, 1);
92            assertEquals(1, aa.get(i));
93            aa.set(i, 2);
94            assertEquals(2, aa.get(i));
95            aa.set(i, -3);
96            assertEquals(-3, aa.get(i));
97        }
98    }
99
100    /**
101     * get returns the last value lazySet at index by same thread
102     */
103    public void testGetLazySet() {
104        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
105        for (int i = 0; i < SIZE; i++) {
106            aa.lazySet(i, 1);
107            assertEquals(1, aa.get(i));
108            aa.lazySet(i, 2);
109            assertEquals(2, aa.get(i));
110            aa.lazySet(i, -3);
111            assertEquals(-3, aa.get(i));
112        }
113    }
114
115    /**
116     * compareAndSet succeeds in changing value if equal to expected else fails
117     */
118    public void testCompareAndSet() {
119        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
120        for (int i = 0; i < SIZE; i++) {
121            aa.set(i, 1);
122            assertTrue(aa.compareAndSet(i, 1, 2));
123            assertTrue(aa.compareAndSet(i, 2, -4));
124            assertEquals(-4, aa.get(i));
125            assertFalse(aa.compareAndSet(i, -5, 7));
126            assertEquals(-4, aa.get(i));
127            assertTrue(aa.compareAndSet(i, -4, 7));
128            assertEquals(7, aa.get(i));
129        }
130    }
131
132    /**
133     * compareAndSet in one thread enables another waiting for value
134     * to succeed
135     */
136    public void testCompareAndSetInMultipleThreads() throws Exception {
137        final AtomicIntegerArray a = new AtomicIntegerArray(1);
138        a.set(0, 1);
139        Thread t = new Thread(new CheckedRunnable() {
140            public void realRun() {
141                while (!a.compareAndSet(0, 2, 3))
142                    Thread.yield();
143            }});
144
145        t.start();
146        assertTrue(a.compareAndSet(0, 1, 2));
147        t.join(LONG_DELAY_MS);
148        assertFalse(t.isAlive());
149        assertEquals(3, a.get(0));
150    }
151
152    /**
153     * repeated weakCompareAndSet succeeds in changing value when equal
154     * to expected
155     */
156    public void testWeakCompareAndSet() {
157        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
158        for (int i = 0; i < SIZE; i++) {
159            aa.set(i, 1);
160            while (!aa.weakCompareAndSet(i, 1, 2));
161            while (!aa.weakCompareAndSet(i, 2, -4));
162            assertEquals(-4, aa.get(i));
163            while (!aa.weakCompareAndSet(i, -4, 7));
164            assertEquals(7, aa.get(i));
165        }
166    }
167
168    /**
169     * getAndSet returns previous value and sets to given value at given index
170     */
171    public void testGetAndSet() {
172        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
173        for (int i = 0; i < SIZE; i++) {
174            aa.set(i, 1);
175            assertEquals(1, aa.getAndSet(i, 0));
176            assertEquals(0, aa.getAndSet(i, -10));
177            assertEquals(-10, aa.getAndSet(i, 1));
178        }
179    }
180
181    /**
182     * getAndAdd returns previous value and adds given value
183     */
184    public void testGetAndAdd() {
185        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
186        for (int i = 0; i < SIZE; i++) {
187            aa.set(i, 1);
188            assertEquals(1, aa.getAndAdd(i, 2));
189            assertEquals(3, aa.get(i));
190            assertEquals(3, aa.getAndAdd(i, -4));
191            assertEquals(-1, aa.get(i));
192        }
193    }
194
195    /**
196     * getAndDecrement returns previous value and decrements
197     */
198    public void testGetAndDecrement() {
199        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
200        for (int i = 0; i < SIZE; i++) {
201            aa.set(i, 1);
202            assertEquals(1, aa.getAndDecrement(i));
203            assertEquals(0, aa.getAndDecrement(i));
204            assertEquals(-1, aa.getAndDecrement(i));
205        }
206    }
207
208    /**
209     * getAndIncrement returns previous value and increments
210     */
211    public void testGetAndIncrement() {
212        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
213        for (int i = 0; i < SIZE; i++) {
214            aa.set(i, 1);
215            assertEquals(1, aa.getAndIncrement(i));
216            assertEquals(2, aa.get(i));
217            aa.set(i, -2);
218            assertEquals(-2, aa.getAndIncrement(i));
219            assertEquals(-1, aa.getAndIncrement(i));
220            assertEquals(0, aa.getAndIncrement(i));
221            assertEquals(1, aa.get(i));
222        }
223    }
224
225    /**
226     * addAndGet adds given value to current, and returns current value
227     */
228    public void testAddAndGet() {
229        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
230        for (int i = 0; i < SIZE; i++) {
231            aa.set(i, 1);
232            assertEquals(3, aa.addAndGet(i, 2));
233            assertEquals(3, aa.get(i));
234            assertEquals(-1, aa.addAndGet(i, -4));
235            assertEquals(-1, aa.get(i));
236        }
237    }
238
239    /**
240     * decrementAndGet decrements and returns current value
241     */
242    public void testDecrementAndGet() {
243        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
244        for (int i = 0; i < SIZE; i++) {
245            aa.set(i, 1);
246            assertEquals(0, aa.decrementAndGet(i));
247            assertEquals(-1, aa.decrementAndGet(i));
248            assertEquals(-2, aa.decrementAndGet(i));
249            assertEquals(-2, aa.get(i));
250        }
251    }
252
253    /**
254     * incrementAndGet increments and returns current value
255     */
256    public void testIncrementAndGet() {
257        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
258        for (int i = 0; i < SIZE; i++) {
259            aa.set(i, 1);
260            assertEquals(2, aa.incrementAndGet(i));
261            assertEquals(2, aa.get(i));
262            aa.set(i, -2);
263            assertEquals(-1, aa.incrementAndGet(i));
264            assertEquals(0, aa.incrementAndGet(i));
265            assertEquals(1, aa.incrementAndGet(i));
266            assertEquals(1, aa.get(i));
267        }
268    }
269
270    static final int COUNTDOWN = 100000;
271
272    class Counter extends CheckedRunnable {
273        final AtomicIntegerArray aa;
274        volatile int counts;
275        Counter(AtomicIntegerArray a) { aa = a; }
276        public void realRun() {
277            for (;;) {
278                boolean done = true;
279                for (int i = 0; i < aa.length(); i++) {
280                    int v = aa.get(i);
281                    assertTrue(v >= 0);
282                    if (v != 0) {
283                        done = false;
284                        if (aa.compareAndSet(i, v, v-1))
285                            ++counts;
286                    }
287                }
288                if (done)
289                    break;
290            }
291        }
292    }
293
294    /**
295     * Multiple threads using same array of counters successfully
296     * update a number of times equal to total count
297     */
298    public void testCountingInMultipleThreads() throws InterruptedException {
299        final AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
300        for (int i = 0; i < SIZE; i++)
301            aa.set(i, COUNTDOWN);
302        Counter c1 = new Counter(aa);
303        Counter c2 = new Counter(aa);
304        Thread t1 = new Thread(c1);
305        Thread t2 = new Thread(c2);
306        t1.start();
307        t2.start();
308        t1.join();
309        t2.join();
310        assertEquals(c1.counts+c2.counts, SIZE * COUNTDOWN);
311    }
312
313    /**
314     * a deserialized serialized array holds same values
315     */
316    public void testSerialization() throws Exception {
317        AtomicIntegerArray x = new AtomicIntegerArray(SIZE);
318        for (int i = 0; i < SIZE; i++)
319            x.set(i, -i);
320        AtomicIntegerArray y = serialClone(x);
321        assertNotSame(x, y);
322        assertEquals(x.length(), y.length());
323        for (int i = 0; i < SIZE; i++) {
324            assertEquals(x.get(i), y.get(i));
325        }
326    }
327
328    /**
329     * toString returns current value.
330     */
331    public void testToString() {
332        int[] a = { 17, 3, -42, 99, -7 };
333        AtomicIntegerArray aa = new AtomicIntegerArray(a);
334        assertEquals(Arrays.toString(a), aa.toString());
335    }
336
337}
338