1/* 2 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24/* @test 25 * @bug 6853696 26 * @summary ReferenceQueue#remove(timeout) should not return null before 27 * timeout is elapsed 28 */ 29 30import java.lang.InterruptedException; 31import java.lang.System; 32import java.lang.ref.Reference; 33import java.lang.ref.ReferenceQueue; 34import java.lang.ref.WeakReference; 35import java.util.concurrent.CountDownLatch; 36import static java.util.concurrent.TimeUnit.NANOSECONDS; 37 38/** 39 * In order to demonstrate the issue we make several threads (two appears to be sufficient) 40 * to block in ReferenceQueue#remove(timeout) at the same time. 41 * Then, we force a reference to be enqueued by setting its referent to null and calling System.gc(). 42 * One of the threads gets the reference returned from the remove(). 43 * The other threads get null: 44 * 1) with bug: this may happen before the specified timeout is elapsed, 45 * 2) without bug: this can only happen after the timeout is fully elapsed. 46 */ 47 48public class EarlyTimeout extends Thread { 49 50 static final int THREADS_COUNT = 2; 51 static final int TIMEOUT = 1000; 52 53 static Object referent = new Object(); 54 static final ReferenceQueue<Object> queue = new ReferenceQueue<Object>(); 55 static final WeakReference<Object> weakReference = new WeakReference<Object>(referent, queue); 56 static final CountDownLatch startedSignal = new CountDownLatch(THREADS_COUNT); 57 58 long actual; 59 Reference<?> reference; 60 61 public static void main(String[] args) throws Exception { 62 EarlyTimeout[] threads = new EarlyTimeout[THREADS_COUNT]; 63 for (int i = 0; i < THREADS_COUNT; ++i) { 64 threads[i] = new EarlyTimeout(); 65 threads[i].start(); 66 } 67 // The main thread waits until the threads has started and give it a chance 68 // for the threads to block on the queue.remove(TIMEOUT) call 69 startedSignal.await(); 70 Thread.sleep(TIMEOUT / 2); 71 referent = null; 72 System.gc(); 73 for (EarlyTimeout thread : threads) { 74 thread.join(); 75 } 76 if (weakReference.get() != null) { 77 throw new RuntimeException("weakReference was not cleared"); 78 } 79 int nonNullRefCount = 0; 80 for (EarlyTimeout thread : threads) { 81 if (thread.reference == null && thread.actual < TIMEOUT) { 82 throw new RuntimeException("elapsed time " + thread.actual 83 + " is less than timeout " + TIMEOUT); 84 } 85 if (thread.reference != null && thread.reference == weakReference) { 86 nonNullRefCount++; 87 } 88 } 89 if (nonNullRefCount > 1) { 90 throw new RuntimeException("more than one references were removed from queue"); 91 } 92 } 93 94 public void run() { 95 try { 96 startedSignal.countDown(); 97 long start = System.nanoTime(); 98 reference = queue.remove(TIMEOUT); 99 actual = NANOSECONDS.toMillis(System.nanoTime() - start); 100 } catch (InterruptedException ex) { 101 throw new RuntimeException(ex); 102 } 103 } 104} 105