--- /dev/null 2015-11-20 14:00:37.000000000 -0800 +++ new/jdk/test/java/lang/StackWalker/AcrossThreads.java 2015-11-20 14:00:37.000000000 -0800 @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8143214 + * @summary Verify that StackWalker works well when one instance of StackWalker + * is used by several threads sequentially or concurrently. + * @run testng AcrossThreads + */ + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import java.lang.StackWalker.StackFrame; +import static java.lang.StackWalker.Option.*; + +import org.testng.annotations.*; +import static org.testng.Assert.*; + +public class AcrossThreads { + static final StackWalker WALKERS[] = new StackWalker[] { + StackWalker.getInstance(RETAIN_CLASS_REFERENCE), + StackWalker.getInstance(EnumSet.of(SHOW_REFLECT_FRAMES, RETAIN_CLASS_REFERENCE)), + StackWalker.getInstance(EnumSet.of(SHOW_HIDDEN_FRAMES, RETAIN_CLASS_REFERENCE)) + }; + + @DataProvider + public StackWalker[][] walkerProvider() { + return new StackWalker[][] { + new StackWalker[] { WALKERS[0] }, + new StackWalker[] { WALKERS[1] }, + new StackWalker[] { WALKERS[2] } + }; + } + + @Test(dataProvider = "walkerProvider") + public void test(StackWalker walker) { + Thread t1 = new T1(walker); + // call methods of one instance of StackWalker sequentially in T1, T2, T3. + t1.start(); + try { + t1.join(); + } catch (InterruptedException e) { } + + List threads = new ArrayList(); + for (int i = 0; i < 100; i++) { + threads.add(new T1(walker)); + threads.add(new T2(walker)); + threads.add(new T3(walker)); + } + // call methods of one instance of StackWalker concurrently in several threads. + threads.parallelStream().forEach(t -> { + t.setDaemon(true); + t.start(); + }); + threads.parallelStream().forEach(t -> { + try { + t.join(); + } catch (InterruptedException e) { } + }); + } + + interface Consumer { + final int LOOPS = 5; + + public void consume(); + + default public void assertWalker(StackWalker walker, int n) { + if (--n == 0) { + Map methods = new HashMap(); + walker.forEach(f -> { + Integer i = methods.putIfAbsent(f.getMethodName(), 1); + if (i != null) { + methods.put(f.getMethodName(), i + 1); + } + }); + + // verify that walker.forEach(...) reaches the specified methods. + assertTrue(methods.get("consume") == 1); + assertTrue(methods.get("run") == 1); + assertTrue(methods.get("assertWalker") == LOOPS); + + // verify that walker.walk(...) reaches the specified methods. + assertTrue(walker.walk(s -> s.map(StackFrame::getMethodName) + .filter(mn -> mn.equals("consume")) + .count()) == 1); + assertTrue(walker.walk(s -> s.map(StackFrame::getMethodName) + .filter(mn -> mn.equals("run")) + .count()) == 1); + assertTrue(walker.walk(s -> s.map(StackFrame::getMethodName) + .filter(mn -> mn.equals("assertWalker")) + .count()) == LOOPS); + } else { + assertWalker(walker, n); + } + } + } + + class T1 extends Thread implements Consumer { + final StackWalker walker; + + public T1(StackWalker walker) { + this.walker = walker; + } + + public void run() { + consume(); + + Thread t2 = new T2(walker); + t2.start(); + try { + t2.join(); + } catch (InterruptedException e) { } + + consume(); + } + + public void consume() { + assertWalker(walker, LOOPS); + + // verify walker.walk() reaches T1 class through methods run() and consume(). + assertTrue(walker.walk(s -> s.filter(f -> T1.class == f.getDeclaringClass()) + .count()) == 2); + + assertCallerClass(walker); + assertEquals(T1.class, walker.getCallerClass()); + } + } + + class T2 extends Thread implements Consumer { + final StackWalker walker; + + public T2(StackWalker walker) { + this.walker = walker; + } + + public void run() { + consume(); + + Thread t3 = new T3(walker); + t3.start(); + try { + t3.join(); + } catch (InterruptedException e) { } + + consume(); + } + + public void consume() { + assertWalker(walker, LOOPS); + + // verify walker.walk() reaches T2 class through methods run() and consume(). + assertTrue(walker.walk(s -> s.filter(f -> T2.class == f.getDeclaringClass()) + .count()) == 2); + // verify T1 is not reached, even if call is invoked + // from test()->T1.start()->T1.run()->T2 + assertTrue(walker.walk(s -> s.filter(f -> T1.class == f.getDeclaringClass()) + .count()) == 0); + + assertCallerClass(walker); + assertEquals(T2.class, walker.getCallerClass()); + } + } + + class T3 extends Thread implements Consumer { + final StackWalker walker; + + public T3(StackWalker walker) { + this.walker = walker; + } + + public void run() { + consume(); + } + + public void consume() { + assertWalker(walker, LOOPS); + + // verify walker.walk() reaches T1 class through methods run() and consume(). + assertTrue(walker.walk(s -> s.filter(f -> T3.class == f.getDeclaringClass()) + .count()) == 2); + // verify T1, T2 is not reached, even if call is invoked + // from test() -> T1.start() -> T1.run() -> T2.start() -> T2.run() -> T3 + assertTrue(walker.walk(s -> s.filter(f -> T2.class == f.getDeclaringClass()) + .count()) == 0); + assertTrue(walker.walk(s -> s.filter(f -> T1.class == f.getDeclaringClass()) + .count()) == 0); + + assertCallerClass(walker); + assertEquals(T3.class, walker.getCallerClass()); + } + } + + static void assertCallerClass(StackWalker walker) { + // verify walker.getCallerClass() get the expected class. + call(walker); + } + + static void call(StackWalker walker) { + Class c = walker.getCallerClass(); + assertEquals(c, AcrossThreads.class); + } +}