1 /* 2 * Copyright (c) 2019, 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 /** 25 * @test 26 * @summary enumerate(list,n,recurse) may return 0 if the group is being destroyed, 27 * whereas it should never return a value < n. This lead to inconsistent 28 * results if ThreadGroup::enumerate is called concurrently at the same 29 * time that a child group is being destroyed. This is a race condition, 30 * and this test will not always fail without the fix, but it does fail 31 * often enough. 32 * @bug 8219197 33 * 34 */ 35 import java.util.concurrent.CountDownLatch; 36 import java.util.concurrent.Semaphore; 37 import java.util.concurrent.atomic.AtomicInteger; 38 39 public class Destroy { 40 41 static final class Task implements Runnable { 42 final Semaphore sem; 43 final CountDownLatch count; 44 45 public Task(Semaphore sem, CountDownLatch count) { 46 this.sem = sem; 47 this.count = count; 48 } 49 50 @Override 51 public void run() { 52 try { 53 count.countDown(); 54 sem.acquire(); 55 } catch (Throwable t) { 56 t.printStackTrace(); 57 } finally { 58 System.out.println(Thread.currentThread().getName() 59 + " exiting"); 60 } 61 } 62 } 63 64 public static void main(String[] args) throws Exception { 65 testDestroyChild(); 66 } 67 68 public static void testDestroyChild() throws Exception { 69 ThreadGroup root = new ThreadGroup("root"); 70 ThreadGroup parent = new ThreadGroup(root,"parent"); 71 ThreadGroup child1 = new ThreadGroup(parent, "child1"); 72 CountDownLatch count = new CountDownLatch(2); 73 Semaphore sem1 = new Semaphore(1); 74 Semaphore sem2 = new Semaphore(1); 75 Thread t1 = new Thread(parent, new Task(sem1, count), "PT1"); 76 Thread t2 = new Thread(parent, new Task(sem2, count), "PT2"); 77 sem1.acquire(); 78 sem2.acquire(); 79 try { 80 81 t1.start(); 82 t2.start(); 83 84 System.out.println("\nAwaiting parent threads..."); 85 count.await(); 86 Thread[] threads = new Thread[2]; 87 int nb = root.enumerate(threads, true); 88 if (nb != 2) { 89 throw new AssertionError("wrong number of threads: " + nb); 90 } 91 92 Thread t3 = new Thread(child1::destroy, "destroy"); 93 AtomicInteger nbr = new AtomicInteger(); 94 Thread t4 = new Thread("enumerate") { 95 public void run() { 96 Thread[] threads = new Thread[42]; 97 nbr.addAndGet(root.enumerate(threads, true)); 98 } 99 }; 100 t4.start(); t3.start(); 101 t4.join(); t3.join(); 102 if (nbr.get() != nb) { 103 throw new AssertionError("wrong number of threads: " + nbr.get()); 104 } 105 106 } finally { 107 sem1.release(); 108 sem2.release(); 109 } 110 t1.join(); 111 t2.join(); 112 } 113 }