1/*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3 *
4 * This code is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 only, as
6 * published by the Free Software Foundation.
7 *
8 * This code is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11 * version 2 for more details (a copy is included in the LICENSE file that
12 * accompanied this code).
13 *
14 * You should have received a copy of the GNU General Public License version
15 * 2 along with this work; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
19 * or visit www.oracle.com if you need additional information or have any
20 * questions.
21 */
22
23/*
24 * This file is available under and governed by the GNU General Public
25 * License version 2 only, as published by the Free Software Foundation.
26 * However, the following notice accompanied the original version of this
27 * file:
28 *
29 * Written by Martin Buchholz with assistance from members of JCP
30 * JSR-166 Expert Group and released to the public domain, as
31 * explained at http://creativecommons.org/publicdomain/zero/1.0/
32 */
33
34/*
35 * @test
36 * @modules java.base/java.util.concurrent:open
37 * @run testng WhiteBox
38 * @summary White box tests of implementation details
39 */
40
41import static org.testng.Assert.*;
42import org.testng.annotations.DataProvider;
43import org.testng.annotations.Test;
44
45import java.lang.ref.WeakReference;
46import java.lang.invoke.MethodHandles;
47import java.lang.invoke.VarHandle;
48import java.util.ArrayDeque;
49import java.util.ArrayList;
50import java.util.Arrays;
51import java.util.Collection;
52import java.util.Collections;
53import java.util.Iterator;
54import java.util.List;
55import java.util.NoSuchElementException;
56import java.util.Queue;
57import java.util.concurrent.ArrayBlockingQueue;
58import java.util.concurrent.CountDownLatch;
59import java.util.concurrent.ThreadLocalRandom;
60import java.util.concurrent.TimeUnit;
61import java.util.function.BooleanSupplier;
62
63@Test
64public class WhiteBox {
65    final ThreadLocalRandom rnd = ThreadLocalRandom.current();
66    final MethodHandles.Lookup lookup = MethodHandles.lookup();
67    final VarHandle ITRS, ITEMS, TAKEINDEX, PUTINDEX, COUNT, HEAD, NEXT, PREVTAKEINDEX;
68
69    WhiteBox() throws ReflectiveOperationException {
70        Class<?> qClass = ArrayBlockingQueue.class;
71        Class<?> itrClass  = Class.forName(qClass.getName() + "$Itr");
72        Class<?> itrsClass = Class.forName(qClass.getName() + "$Itrs");
73        Class<?> nodeClass = Class.forName(itrsClass.getName() + "$Node");
74        ITRS      = findVarHandle(qClass, "itrs", itrsClass);
75        ITEMS     = findVarHandle(qClass, "items", Object[].class);
76        TAKEINDEX = findVarHandle(qClass, "takeIndex", int.class);
77        PUTINDEX  = findVarHandle(qClass, "putIndex", int.class);
78        COUNT     = findVarHandle(qClass, "count", int.class);
79        HEAD      = findVarHandle(itrsClass, "head", nodeClass);
80        NEXT      = findVarHandle(nodeClass, "next", nodeClass);
81        PREVTAKEINDEX = findVarHandle(itrClass, "prevTakeIndex", int.class);
82    }
83
84    VarHandle findVarHandle(Class<?> recv, String name, Class<?> type)
85        throws ReflectiveOperationException {
86        return MethodHandles.privateLookupIn(recv, lookup)
87            .findVarHandle(recv, name, type);
88    }
89
90    Object itrs(ArrayBlockingQueue q) { return ITRS.get(q); }
91    Object[] items(ArrayBlockingQueue q) { return (Object[]) ITEMS.get(q); }
92    int takeIndex(ArrayBlockingQueue q) { return (int) TAKEINDEX.get(q); }
93    int putIndex(ArrayBlockingQueue q) { return (int) PUTINDEX.get(q); }
94    int count(ArrayBlockingQueue q) { return (int) COUNT.get(q); }
95    Object head(Object itrs) { return HEAD.get(itrs); }
96    Object next(Object node) { return NEXT.get(node); }
97    int prevTakeIndex(Iterator itr) { return (int) PREVTAKEINDEX.get(itr); }
98
99    boolean isDetached(Iterator it) {
100        return prevTakeIndex(it) < 0;
101    }
102
103    void assertIteratorExhausted(Iterator it) {
104        if (rnd.nextBoolean()) {
105            assertTrue(!it.hasNext());
106            assertTrue(isDetached(it));
107        }
108        if (rnd.nextBoolean()) {
109            it.forEachRemaining(e -> { throw new AssertionError(); });
110            assertTrue(isDetached(it));
111        }
112        if (rnd.nextBoolean())
113            try { it.next(); fail("should throw"); }
114            catch (NoSuchElementException success) {}
115    }
116
117    List<Iterator> trackedIterators(ArrayBlockingQueue q) {
118        List<Iterator> its = new ArrayList<>();
119        Object itrs = itrs(q);
120        if (itrs != null) {
121            for (Object p = head(itrs); p != null; p = next(p))
122                its.add(((WeakReference<Iterator>)(p)).get());
123            Collections.reverse(its);
124        }
125        return its;
126    }
127
128    List<Iterator> attachedIterators(ArrayBlockingQueue q) {
129        List<Iterator> its = new ArrayList<>();
130        Object itrs = itrs(q);
131        if (itrs != null) {
132            for (Object p = head(itrs); p != null; p = next(p)) {
133                Iterator it = ((WeakReference<Iterator>)(p)).get();
134                if (it != null && !isDetached(it))
135                    its.add(it);
136            }
137            Collections.reverse(its);
138        }
139        return its;
140    }
141
142    void assertRemoveThrowsISE(Iterator it) {
143        if (rnd.nextBoolean())
144            try { it.remove(); fail("should throw"); }
145            catch (IllegalStateException success) {}
146    }
147
148    void assertRemoveHasNoEffect(Iterator it, Collection c) {
149        if (rnd.nextBoolean()) {
150            int size = c.size();
151            it.remove(); // no effect
152            assertEquals(c.size(), size);
153            assertRemoveThrowsISE(it);
154        }
155    }
156
157    void checkIterationSanity(Queue q) {
158        if (rnd.nextBoolean())
159            return;
160        int size = q.size();
161        Object[] a = q.toArray();
162        Object[] b = new Object[size+2];
163        Arrays.fill(b, Boolean.TRUE);
164        Object[] c = q.toArray(b);
165        assertEquals(a.length, size);
166        assertSame(b, c);
167        assertNull(b[size]);
168        assertSame(b[size+1], Boolean.TRUE);
169        assertEquals(q.toString(), Arrays.toString(a));
170        Integer[] xx = null, yy = null;
171        if (size > 0) {
172            xx = new Integer[size - 1];
173            Arrays.fill(xx, 42);
174            yy = ((Queue<Integer>)q).toArray(xx);
175            for (Integer zz : xx)
176                assertEquals(42, (int) zz);
177        }
178        Iterator it = q.iterator();
179        for (int i = 0; i < size; i++) {
180            if (rnd.nextBoolean()) assertTrue(it.hasNext());
181            Object x = it.next();
182            assertSame(x, a[i]);
183            assertSame(x, b[i]);
184            if (xx != null) assertSame(x, yy[i]);
185        }
186        if (rnd.nextBoolean()) assertTrue(!it.hasNext());
187    }
188
189    /**
190     * Instead of having putIndex (and takeIndex) at the initial
191     * default of 0, move them to a random location.
192     */
193    void randomizePutIndex(ArrayBlockingQueue q) {
194        assertTrue(q.isEmpty());
195        int capacity = q.remainingCapacity();
196        int n = rnd.nextInt(capacity + 1);
197        int putIndex = putIndex(q);
198        for (int i = n; i-->0; ) q.add(Boolean.TRUE);
199        for (int i = n; i-->0; ) q.remove();
200        assertEquals(putIndex(q), (putIndex + n) % items(q).length);
201    }
202
203    /** No guarantees, but effective in practice. */
204    static void forceFullGc() {
205        CountDownLatch finalizeDone = new CountDownLatch(1);
206        WeakReference<?> ref = new WeakReference<Object>(new Object() {
207            protected void finalize() { finalizeDone.countDown(); }});
208        try {
209            for (int i = 0; i < 10; i++) {
210                System.gc();
211                if (finalizeDone.await(1L, TimeUnit.SECONDS) && ref.get() == null) {
212                    System.runFinalization(); // try to pick up stragglers
213                    return;
214                }
215            }
216        } catch (InterruptedException unexpected) {
217            throw new AssertionError("unexpected InterruptedException");
218        }
219        throw new AssertionError("failed to do a \"full\" gc");
220    }
221
222    static void gcAwait(BooleanSupplier s) {
223        for (int i = 0; i < 10; i++) {
224            if (s.getAsBoolean())
225                return;
226            forceFullGc();
227        }
228        throw new AssertionError("failed to satisfy condition");
229    }
230
231    public void clear_willClearItrs() {
232        boolean fair = rnd.nextBoolean();
233        int capacity = rnd.nextInt(2, 10);
234        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
235        randomizePutIndex(q);
236        List<Iterator> its = new ArrayList<>();
237        for (int i = 0; i < capacity; i++)
238            assertTrue(q.add(i));
239        assertNull(itrs(q));
240        for (int i = 0; i < capacity; i++) {
241            its.add(q.iterator());
242            assertEquals(trackedIterators(q), its);
243            q.poll();
244            q.add(capacity + i);
245        }
246        q.clear();
247        assertNull(itrs(q));
248        int j = 0;
249        for (Iterator it : its) {
250            assertTrue(isDetached(it));
251            if (rnd.nextBoolean()) assertTrue(it.hasNext());
252            if (rnd.nextBoolean()) {
253                assertEquals(it.next(), j);
254                assertIteratorExhausted(it);
255            }
256            j++;
257        }
258    }
259
260    public void queueEmptyingWillClearItrs() {
261        boolean fair = rnd.nextBoolean();
262        int capacity = rnd.nextInt(2, 10);
263        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
264        randomizePutIndex(q);
265        List<Iterator> its = new ArrayList<>();
266        for (int i = 0; i < capacity; i++)
267            q.add(i);
268        assertNull(itrs(q));
269        for (int i = 0; i < capacity; i++) {
270            its.add(q.iterator());
271            assertEquals(trackedIterators(q), its);
272            q.poll();
273            q.add(capacity+i);
274        }
275        for (int i = 0; i < capacity; i++)
276            q.poll();
277        assertNull(itrs(q));
278        int j = 0;
279        for (Iterator it : its) {
280            assertTrue(isDetached(it));
281            if (rnd.nextBoolean()) assertTrue(it.hasNext());
282            if (rnd.nextBoolean()) {
283                assertEquals(it.next(), j);
284                assertIteratorExhausted(it);
285            }
286            j++;
287        }
288    }
289
290    public void advancing2CyclesWillRemoveIterators() {
291        boolean fair = rnd.nextBoolean();
292        int capacity = rnd.nextInt(2, 10);
293        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
294        List<Iterator> its = new ArrayList<>();
295        for (int i = 0; i < capacity; i++)
296            q.add(i);
297        assertNull(itrs(q));
298        for (int i = capacity; i < 3 * capacity; i++) {
299            its.add(q.iterator());
300            assertEquals(trackedIterators(q), its);
301            q.poll();
302            q.add(i);
303        }
304        for (int i = 3 * capacity; i < 4 * capacity; i++) {
305            assertEquals(trackedIterators(q), its.subList(capacity,2*capacity));
306            q.poll();
307            q.add(i);
308        }
309        assertNull(itrs(q));
310        int j = 0;
311        for (Iterator it : its) {
312            assertTrue(isDetached(it));
313            if (rnd.nextBoolean()) assertTrue(it.hasNext());
314            if (rnd.nextBoolean()) {
315                assertEquals(it.next(), j);
316                assertIteratorExhausted(it);
317            }
318            j++;
319        }
320    }
321
322    /**
323     * Interior removal of elements used by an iterator will cause it
324     * to be untracked.
325     */
326    public void interiorRemovalOfElementsUsedByIterator() {
327        boolean fair = rnd.nextBoolean();
328        int capacity = rnd.nextInt(10, 20);
329        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
330        randomizePutIndex(q);
331        q.add(0);
332        for (int i = 1; i < 2 * capacity; i++) {
333            q.add(i);
334            Integer[] elts = { -1, -2, -3 };
335            for (Integer elt : elts) q.add(elt);
336            assertEquals(q.remove(), i - 1);
337            Iterator it = q.iterator();
338            assertEquals(it.next(), i);
339            assertEquals(it.next(), elts[0]);
340            Collections.shuffle(Arrays.asList(elts));
341            assertTrue(q.remove(elts[0]));
342            assertTrue(q.remove(elts[1]));
343            assertEquals(trackedIterators(q), Collections.singletonList(it));
344            assertTrue(q.remove(elts[2]));
345            assertNull(itrs(q));
346            assertEquals(it.next(), -2);
347            assertIteratorExhausted(it);
348            assertTrue(isDetached(it));
349        }
350    }
351
352    public void iteratorsOnEmptyQueue() {
353        boolean fair = rnd.nextBoolean();
354        int capacity = rnd.nextInt(1, 10);
355        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
356        randomizePutIndex(q);
357        for (int i = 0; i < 4; i++) {
358            Iterator it = q.iterator();
359            assertNull(itrs(q));
360            assertIteratorExhausted(it);
361            assertTrue(isDetached(it));
362            assertRemoveThrowsISE(it);
363        }
364    }
365
366    public void interiorRemovalOfIteratorsLastElement() {
367        boolean fair = rnd.nextBoolean();
368        int capacity = rnd.nextInt(3, 10);
369        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
370        randomizePutIndex(q);
371        List<Iterator> its = new ArrayList<>();
372        for (int i = 0; i < capacity; i++)
373            q.add(i);
374        for (int i = 0; i < capacity; i++) {
375            Iterator it = q.iterator();
376            its.add(it);
377            for (int j = 0; j < i; j++)
378                assertEquals(j, it.next());
379            assertEquals(attachedIterators(q), its);
380        }
381        q.remove(capacity - 1);
382        assertEquals(attachedIterators(q), its);
383        for (int i = 1; i < capacity - 1; i++) {
384            q.remove(capacity - i - 1);
385            Iterator it = its.get(capacity - i);
386            assertTrue(isDetached(it));
387            assertEquals(attachedIterators(q), its.subList(0, capacity - i));
388            if (rnd.nextBoolean()) assertTrue(it.hasNext());
389            assertEquals(it.next(), capacity - i);
390            assertIteratorExhausted(it);
391        }
392        assertEquals(attachedIterators(q), its.subList(0, 2));
393        q.remove(0);
394        assertTrue(q.isEmpty());
395        assertNull(itrs(q));
396        Iterator it = its.get(0);
397        assertEquals(it.next(), 0);
398        assertRemoveHasNoEffect(it, q);
399        assertIteratorExhausted(it);
400        assertTrue(isDetached(it));
401        assertRemoveHasNoEffect(its.get(1), q);
402    }
403
404    /**
405     * Checks "interior" removal of alternating elements, straddling 2 cycles
406     */
407    public void interiorRemovalOfAlternatingElements() {
408        boolean fair = rnd.nextBoolean();
409        int capacity = 2 * rnd.nextInt(2, 10);
410        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
411        List<Iterator> its = new ArrayList<>();
412        // Move takeIndex to middle
413        for (int i = 0; i < capacity/2; i++) {
414            assertTrue(q.add(i));
415            assertEquals(q.poll(), i);
416        }
417        assertEquals(takeIndex(q), capacity/2);
418        for (int i = 0; i < capacity; i++)
419            q.add(i);
420        for (int i = 0; i < capacity; i++) {
421            Iterator it = q.iterator();
422            its.add(it);
423            for (int j = 0; j < i; j++)
424                assertEquals(j, it.next());
425            assertEquals(attachedIterators(q), its);
426        }
427
428        // Remove all even elements, in either direction, using
429        // q.remove(), or iterator.remove()
430        switch (rnd.nextInt(3)) {
431        case 0:
432            for (int i = 0; i < capacity; i+=2) assertTrue(q.remove(i));
433            break;
434        case 1:
435            for (int i = capacity - 2; i >= 0; i-=2) assertTrue(q.remove(i));
436            break;
437        case 2:
438            Iterator it = q.iterator();
439            while (it.hasNext()) {
440                int i = (Integer) it.next();
441                if ((i & 1) == 0)
442                    it.remove();
443            }
444            break;
445        default: throw new AssertionError();
446        }
447        assertEquals(attachedIterators(q), its);
448
449        for (int i = 0; i < capacity; i++) {
450            Iterator it = its.get(i);
451            boolean even = ((i & 1) == 0);
452            if (even) {
453                if (rnd.nextBoolean()) assertTrue(it.hasNext());
454                assertEquals(i, it.next());
455                for (int j = i+1; j < capacity; j += 2)
456                    assertEquals(j, it.next());
457                assertTrue(!isDetached(it));
458                assertTrue(!it.hasNext());
459                assertTrue(isDetached(it));
460            } else { /* odd */
461                if (rnd.nextBoolean()) assertTrue(it.hasNext());
462                assertRemoveHasNoEffect(it, q);
463                assertEquals(i, it.next());
464                for (int j = i+2; j < capacity; j += 2)
465                    assertEquals(j, it.next());
466                assertTrue(!isDetached(it));
467                assertTrue(!it.hasNext());
468                assertTrue(isDetached(it));
469            }
470        }
471        assertEquals(trackedIterators(q), Collections.emptyList());
472        assertNull(itrs(q));
473    }
474
475    public void garbageCollectionOfUnreachableIterators() {
476        boolean fair = rnd.nextBoolean();
477        int capacity = rnd.nextInt(1, 10);
478        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
479        randomizePutIndex(q);
480        List<Iterator> its = new ArrayList<>();
481        for (int i = 0; i < capacity; i++) q.add(i);
482        for (int i = 0; i < capacity; i++) its.add(q.iterator());
483        assertEquals(attachedIterators(q), its);
484        its = null;
485        gcAwait(() -> {
486            List<Iterator> trackedIterators = trackedIterators(q);
487            assertEquals(trackedIterators.size(), capacity);
488            for (Iterator x : trackedIterators)
489                if (x != null) return false;
490            return true;
491        });
492        Iterator it = q.iterator(); //
493        assertEquals(trackedIterators(q), Collections.singletonList(it));
494    }
495
496    public void garbageCollectionOfUnreachableIteratorsWithRandomlyRetainedSubset() {
497        boolean fair = rnd.nextBoolean();
498        int capacity = rnd.nextInt(10, 20);
499        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
500        randomizePutIndex(q);
501        List<Iterator> its = new ArrayList<>();
502        List<Iterator> retained = new ArrayList<>();
503        final int size = 1 + rnd.nextInt(capacity);
504        for (int i = 0; i < size; i++) q.add(i);
505        for (int i = 0; i < size; i++) its.add(q.iterator());
506        assertEquals(attachedIterators(q), its);
507        // Leave sufficient gaps in retained
508        for (int i = 0; i < size; i+= 2+rnd.nextInt(3))
509            retained.add(its.get(i));
510        its = null;
511        gcAwait(() -> {
512            List<Iterator> trackedIterators = trackedIterators(q);
513            assertEquals(trackedIterators.size(), size);
514            for (Iterator it : trackedIterators)
515                if ((it != null) ^ retained.contains(it)) return false;
516            return true;
517        });
518        Iterator it = q.iterator(); // trigger another sweep
519        retained.add(it);
520        assertEquals(trackedIterators(q), retained);
521    }
522
523    /**
524     * Checks incremental sweeping of unreachable iterators.
525     * Excessively white box?!
526     */
527    public void incrementalSweepingOfUnreachableIterators() {
528        boolean fair = rnd.nextBoolean();
529        int capacity = rnd.nextInt(10, 20);
530        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
531        randomizePutIndex(q);
532        final int SHORT_SWEEP_PROBES = 4;
533        final int LONG_SWEEP_PROBES = 16;
534        final int PROBE_HOP = LONG_SWEEP_PROBES + 6 * SHORT_SWEEP_PROBES;
535        final int PROBE_HOP_COUNT = 10;
536        // Expect around 8 sweeps per PROBE_HOP
537        final int SWEEPS_PER_PROBE_HOP = 8;
538        List<Iterator> its = new ArrayList<>();
539        for (int i = 0; i < capacity; i++)
540            q.add(i);
541        for (int i = 0; i < PROBE_HOP_COUNT * PROBE_HOP; i++)
542            its.add(q.iterator());
543        assertEquals(attachedIterators(q), its);
544        // make some garbage, separated by PROBE_HOP
545        for (int i = 0; i < its.size(); i += PROBE_HOP)
546            its.set(i, null);
547        its.removeIf(it -> it == null);
548        forceFullGc();
549        int retries;
550        for (retries = 0;
551             trackedIterators(q).contains(null) && retries < 1000;
552             retries++)
553            // one round of sweeping
554            its.add(q.iterator());
555        assertTrue(retries >= PROBE_HOP_COUNT * (SWEEPS_PER_PROBE_HOP - 2));
556        assertTrue(retries <= PROBE_HOP_COUNT * (SWEEPS_PER_PROBE_HOP + 2));
557        assertEquals(trackedIterators(q), its);
558    }
559
560    public void Iterator_remove_safetyWhileInDetachedMode() {
561        boolean fair = rnd.nextBoolean();
562        int capacity = rnd.nextInt(10, 20);
563        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
564        List<Iterator> its = new ArrayList<>();
565        for (int i = 0; i < capacity/2; i++) {
566            q.add(i);
567            q.remove();
568        }
569        assertEquals(takeIndex(q), capacity/2);
570        for (int i = 0; i < capacity; i++)
571            q.add(i);
572        for (int i = 0; i < capacity; i++) {
573            Iterator it = q.iterator();
574            its.add(it);
575            for (int j = 0; j < i; j++)
576                assertEquals(j, it.next());
577        }
578        assertEquals(attachedIterators(q), its);
579        for (int i = capacity - 1; i >= 0; i--) {
580            Iterator it = its.get(i);
581            assertEquals(i, it.next()); // last element
582            assertTrue(!isDetached(it));
583            assertTrue(!it.hasNext()); // first hasNext failure
584            assertTrue(isDetached(it));
585            int size = q.size();
586            assertTrue(q.contains(i));
587            switch (rnd.nextInt(3)) {
588            case 0:
589                it.remove();
590                assertTrue(!q.contains(i));
591                assertEquals(q.size(), size - 1);
592                break;
593            case 1:
594                // replace i with impostor
595                if (q.remainingCapacity() == 0) {
596                    assertTrue(q.remove(i));
597                    assertTrue(q.add(-1));
598                } else {
599                    assertTrue(q.add(-1));
600                    assertTrue(q.remove(i));
601                }
602                it.remove(); // should have no effect
603                assertEquals(size, q.size());
604                assertTrue(q.contains(-1));
605                assertTrue(q.remove(-1));
606                break;
607            case 2:
608                // replace i with true impostor
609                if (i != 0) {
610                    assertTrue(q.remove(i));
611                    assertTrue(q.add(i));
612                }
613                it.remove();
614                assertTrue(!q.contains(i));
615                assertEquals(q.size(), size - 1);
616                break;
617            default: throw new AssertionError();
618            }
619            assertRemoveThrowsISE(it);
620            assertTrue(isDetached(it));
621            assertTrue(!trackedIterators(q).contains(it));
622        }
623        assertTrue(q.isEmpty());
624        assertNull(itrs(q));
625        for (Iterator it : its)
626            assertIteratorExhausted(it);
627    }
628
629    /**
630     * Checks dequeues bypassing iterators' current positions.
631     */
632    public void dequeuesBypassingIteratorCurrentPositions() {
633        boolean fair = rnd.nextBoolean();
634        int capacity = rnd.nextInt(10, 20);
635        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
636        Queue<Iterator> its0 = new ArrayDeque<>();
637        Queue<Iterator> itsMid = new ArrayDeque<>();
638        List<Iterator> its = new ArrayList<>();
639        for (int i = 0; i < capacity; i++)
640            q.add(i);
641        for (int i = 0; i < 2 * capacity + 1; i++) {
642            Iterator it = q.iterator();
643            its.add(it);
644            its0.add(it);
645        }
646        for (int i = 0; i < 2 * capacity + 1; i++) {
647            Iterator it = q.iterator();
648            for (int j = 0; j < capacity/2; j++)
649                assertEquals(j, it.next());
650            its.add(it);
651            itsMid.add(it);
652        }
653        for (int i = capacity; i < 3 * capacity; i++) {
654            Iterator it;
655
656            it = its0.remove();
657            assertRemoveThrowsISE(it);
658            if (rnd.nextBoolean()) assertTrue(it.hasNext());
659            assertEquals(0, it.next());
660            int victim = i - capacity;
661            for (int j = victim + (victim == 0 ? 1 : 0); j < i; j++) {
662                if (rnd.nextBoolean()) assertTrue(it.hasNext());
663                assertEquals(j, it.next());
664            }
665            assertIteratorExhausted(it);
666
667            it = itsMid.remove();
668            if (victim >= capacity/2)
669                assertRemoveHasNoEffect(it, q);
670            assertEquals(capacity/2, it.next());
671            if (victim > capacity/2)
672                assertRemoveHasNoEffect(it, q);
673            for (int j = Math.max(victim, capacity/2 + 1); j < i; j++) {
674                if (rnd.nextBoolean()) assertTrue(it.hasNext());
675                assertEquals(j, it.next());
676            }
677            assertIteratorExhausted(it);
678
679            if (rnd.nextBoolean()) {
680                assertEquals(victim, q.remove());
681            } else {
682                ArrayList list = new ArrayList(1);
683                q.drainTo(list, 1);
684                assertEquals(list.size(), 1);
685                assertEquals(victim, list.get(0));
686            }
687            assertTrue(q.add(i));
688        }
689        // takeIndex has wrapped twice.
690        Iterator it0 = its0.remove();
691        Iterator itMid = itsMid.remove();
692        assertTrue(isDetached(it0));
693        assertTrue(isDetached(itMid));
694        if (rnd.nextBoolean()) assertTrue(it0.hasNext());
695        if (rnd.nextBoolean()) assertTrue(itMid.hasNext());
696        assertRemoveThrowsISE(it0);
697        assertRemoveHasNoEffect(itMid, q);
698        if (rnd.nextBoolean()) assertEquals(0, it0.next());
699        if (rnd.nextBoolean()) assertEquals(capacity/2, itMid.next());
700        assertTrue(isDetached(it0));
701        assertTrue(isDetached(itMid));
702        assertEquals(capacity, q.size());
703        assertEquals(0, q.remainingCapacity());
704    }
705
706    /**
707     * Checks collective sanity of iteration, toArray() and toString().
708     */
709    public void collectiveSanity() {
710        boolean fair = rnd.nextBoolean();
711        int capacity = rnd.nextInt(10, 20);
712        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
713        randomizePutIndex(q);
714        for (int i = 0; i < capacity; i++) {
715            checkIterationSanity(q);
716            assertEquals(capacity, q.size() + q.remainingCapacity());
717            q.add(i);
718        }
719        for (int i = 0; i < (capacity + (capacity >> 1)); i++) {
720            checkIterationSanity(q);
721            assertEquals(capacity, q.size() + q.remainingCapacity());
722            assertEquals(i, q.peek());
723            assertEquals(i, q.poll());
724            checkIterationSanity(q);
725            assertEquals(capacity, q.size() + q.remainingCapacity());
726            q.add(capacity + i);
727        }
728        for (int i = 0; i < capacity; i++) {
729            checkIterationSanity(q);
730            assertEquals(capacity, q.size() + q.remainingCapacity());
731            int expected = i + capacity + (capacity >> 1);
732            assertEquals(expected, q.peek());
733            assertEquals(expected, q.poll());
734        }
735        checkIterationSanity(q);
736    }
737
738    public void iteratorsDetachedWhenExhaustedAndLastRetRemoved() {
739        boolean fair = rnd.nextBoolean();
740        int capacity = rnd.nextInt(2, 10);
741        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
742        randomizePutIndex(q);
743        int size = rnd.nextInt(1, capacity + 1);
744        for (int i = 0; i < size; i++) q.add(i);
745        Iterator it = q.iterator();
746        for (int i = 0; i < size - 1; i++) assertEquals(i, it.next());
747        assertEquals(trackedIterators(q), Collections.singletonList(it));
748        assertFalse(isDetached(it));
749        switch (rnd.nextInt(2)) {
750        case 0: assertTrue(q.remove(size - 1)); break;
751        case 1: assertTrue(q.removeIf(e -> e.equals(size - 1))); break;
752        default: throw new AssertionError();
753        }
754        assertEquals(size - 1, it.next()); // should trigger detach
755        assertNull(itrs(q));
756        assertTrue(isDetached(it));
757        assertRemoveHasNoEffect(it, q);
758    }
759}
760