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 Doug Lea with assistance from members of JCP JSR-166
  30  * Expert Group and released to the public domain, as explained at
  31  * http://creativecommons.org/publicdomain/zero/1.0/
  32  */
  33 
  34 /*
  35  * @test
  36  * @bug 6805775 6815766
  37  * @library /test/lib
  38  * @run main OfferDrainToLoops 100
  39  * @summary Test concurrent offer vs. drainTo
  40  */
  41 
  42 import java.util.ArrayList;
  43 import java.util.List;
  44 import java.util.SplittableRandom;
  45 import java.util.concurrent.ArrayBlockingQueue;
  46 import java.util.concurrent.BlockingQueue;
  47 import java.util.concurrent.LinkedBlockingDeque;
  48 import java.util.concurrent.LinkedBlockingQueue;
  49 import java.util.concurrent.LinkedTransferQueue;
  50 import java.util.concurrent.atomic.AtomicLong;
  51 import jdk.test.lib.Utils;
  52 
  53 @SuppressWarnings({"unchecked", "rawtypes", "deprecation"})
  54 public class OfferDrainToLoops {
  55     static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
  56     final long testDurationMillisDefault = 10_000L;
  57     final long testDurationMillis;
  58 
  59     OfferDrainToLoops(String[] args) {
  60         testDurationMillis = (args.length > 0) ?
  61             Long.valueOf(args[0]) : testDurationMillisDefault;
  62     }
  63 
  64     void checkNotContainsNull(Iterable it) {
  65         for (Object x : it)
  66             check(x != null);
  67     }
  68 
  69     void test(String[] args) throws Throwable {
  70         test(new LinkedBlockingQueue());
  71         test(new LinkedBlockingQueue(2000));
  72         test(new LinkedBlockingDeque());
  73         test(new LinkedBlockingDeque(2000));
  74         test(new ArrayBlockingQueue(2000));
  75         test(new LinkedTransferQueue());
  76     }
  77 
  78     void test(final BlockingQueue q) throws Throwable {
  79         System.out.println(q.getClass().getSimpleName());
  80         final long testDurationNanos = testDurationMillis * 1000L * 1000L;
  81         final long quittingTimeNanos = System.nanoTime() + testDurationNanos;
  82         final SplittableRandom rnd = new SplittableRandom();
  83 
  84         // Poor man's bounded buffer.
  85         final AtomicLong approximateCount = new AtomicLong(0L);
  86 
  87         abstract class CheckedThread extends Thread {
  88             final SplittableRandom rnd;
  89 
  90             CheckedThread(String name, SplittableRandom rnd) {
  91                 super(name);
  92                 this.rnd = rnd;
  93                 setDaemon(true);
  94                 start();
  95             }
  96             /** Polls for quitting time. */
  97             protected boolean quittingTime() {
  98                 return System.nanoTime() - quittingTimeNanos > 0;
  99             }
 100             /** Polls occasionally for quitting time. */
 101             protected boolean quittingTime(long i) {
 102                 return (i % 1024) == 0 && quittingTime();
 103             }
 104             protected abstract void realRun();
 105             public void run() {
 106                 try { realRun(); } catch (Throwable t) { unexpected(t); }
 107             }
 108         }
 109 
 110         Thread offerer = new CheckedThread("offerer", rnd.split()) {
 111             protected void realRun() {
 112                 long c = 0;
 113                 for (long i = 0; ! quittingTime(i); i++) {
 114                     if (q.offer(c)) {
 115                         if ((++c % 1024) == 0) {
 116                             approximateCount.getAndAdd(1024);
 117                             while (approximateCount.get() > 10000)
 118                                 Thread.yield();
 119                         }
 120                     } else {
 121                         Thread.yield();
 122                     }}}};
 123 
 124         Thread drainer = new CheckedThread("drainer", rnd.split()) {
 125             protected void realRun() {
 126                 while (! quittingTime()) {
 127                     List list = new ArrayList();
 128                     int n = rnd.nextBoolean() ?
 129                         q.drainTo(list) :
 130                         q.drainTo(list, 100);
 131                     approximateCount.getAndAdd(-n);
 132                     equal(list.size(), n);
 133                     for (int j = 0; j < n - 1; j++)
 134                         equal((Long) list.get(j) + 1L, list.get(j + 1));
 135                     Thread.yield();
 136                 }
 137                 q.clear();
 138                 approximateCount.set(0); // Releases waiting offerer thread
 139             }};
 140 
 141         Thread scanner = new CheckedThread("scanner", rnd.split()) {
 142             protected void realRun() {
 143                 while (! quittingTime()) {
 144                     switch (rnd.nextInt(3)) {
 145                     case 0: checkNotContainsNull(q); break;
 146                     case 1: q.size(); break;
 147                     case 2:
 148                         Long[] a = (Long[]) q.toArray(new Long[0]);
 149                         int n = a.length;
 150                         for (int j = 0; j < n - 1; j++) {
 151                             check(a[j] < a[j+1]);
 152                             check(a[j] != null);
 153                         }
 154                         break;
 155                     }
 156                     Thread.yield();
 157                 }}};
 158 
 159         for (Thread thread : new Thread[] { offerer, drainer, scanner }) {
 160             thread.join(LONG_DELAY_MS + testDurationMillis);
 161             if (thread.isAlive()) {
 162                 System.err.printf("Hung thread: %s%n", thread.getName());
 163                 failed++;
 164                 for (StackTraceElement e : thread.getStackTrace())
 165                     System.err.println(e);
 166                 thread.join(LONG_DELAY_MS);
 167             }
 168         }
 169     }
 170 
 171     //--------------------- Infrastructure ---------------------------
 172     volatile int passed = 0, failed = 0;
 173     void pass() {passed++;}
 174     void fail() {failed++; Thread.dumpStack();}
 175     void fail(String msg) {System.err.println(msg); fail();}
 176     void unexpected(Throwable t) {failed++; t.printStackTrace();}
 177     void check(boolean cond) {if (cond) pass(); else fail();}
 178     void equal(Object x, Object y) {
 179         if (x == null ? y == null : x.equals(y)) pass();
 180         else fail(x + " not equal to " + y);}
 181     public static void main(String[] args) throws Throwable {
 182         new OfferDrainToLoops(args).instanceMain(args);}
 183     public void instanceMain(String[] args) throws Throwable {
 184         try {test(args);} catch (Throwable t) {unexpected(t);}
 185         System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
 186         if (failed > 0) throw new AssertionError("Some tests failed");}
 187 }