1/*
2 * Copyright (c) 2006, 2015, 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 * @bug 6475157
27 * @summary Tests deadlock in simultaneous connection and connector-server close
28 * @author Eamonn McManus
29 */
30
31/* This test is somewhat dependent on implementation details.  If it suddenly
32 * starts failing after a rewrite of the RMIConnectorServer code, you should
33 * consider whether it is still relevant.
34 */
35
36import java.io.IOException;
37import java.lang.management.ManagementFactory;
38import java.lang.management.ThreadInfo;
39import java.lang.management.ThreadMXBean;
40import java.util.concurrent.Exchanger;
41import javax.management.MBeanServer;
42import javax.management.remote.JMXServiceURL;
43import javax.management.remote.rmi.RMIConnection;
44import javax.management.remote.rmi.RMIConnectorServer;
45import javax.management.remote.rmi.RMIJRMPServerImpl;
46
47public class ConnectorStopDeadlockTest {
48    private static String failure;
49    private static RMIConnectorServer connectorServer;
50
51    public static void main(String[] args) throws Exception {
52        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
53        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
54        RMIJRMPServerImplSub impl = new RMIJRMPServerImplSub();
55
56        System.out.println("Creating connectorServer");
57        connectorServer = new RMIConnectorServer(url, null, impl, mbs);
58        System.out.println("Starting connectorServer");
59        connectorServer.start();
60        System.out.println("Making client");
61        RMIConnection cc = impl.newClient(null);
62        System.out.println("Closing client");
63        cc.close();
64        if (connectorServer.isActive()) {
65            System.out.println("Stopping connectorServer");
66            connectorServer.stop();
67        }
68        if (failure == null)
69            System.out.println("TEST PASSED, no deadlock");
70        else
71            System.out.println("TEST FAILED");
72    }
73
74    static void fail(Throwable e) {
75        System.out.println("FAILED WITH EXCEPTION: " + e);
76        e.printStackTrace(System.out);
77        failure = e.toString();
78    }
79
80    static void fail(String s) {
81        System.out.println("FAILED: " + s);
82        failure = s;
83    }
84
85//    static MonitorInfo[] threadLocks(Thread t) {
86//        ThreadMXBean tm = ManagementFactory.getThreadMXBean();
87//        ThreadInfo[] tis = tm.getThreadInfo(new long[] {t.getId()}, true, true);
88//        if (tis[0] == null)
89//            return null;
90//        else
91//            return tis[0].getLockedMonitors();
92//    }
93//
94//    static void showLocks(Thread t) {
95//        System.out.println("Locks for " + t.getName() + ":");
96//        MonitorInfo[] mis = threadLocks(t);
97//        if (mis == null)
98//            System.out.println("  (no longer exists)");
99//        else if (mis.length == 0)
100//            System.out.println("  (none)");
101//        else {
102//            for (MonitorInfo mi : mis)
103//                System.out.println("  " + mi);
104//        }
105//    }
106
107    // Wait until thread t blocks waiting for a lock held by the calling thread,
108    // or until it exits.
109    static void waitForBlock(Thread t) {
110        Thread currentThread = Thread.currentThread();
111        System.out.println("waiting for thread " + t.getName() + " to block " +
112                "on a lock held by thread " + currentThread.getName());
113        ThreadMXBean tm = ManagementFactory.getThreadMXBean();
114        while (true) {
115            ThreadInfo ti = tm.getThreadInfo(t.getId());
116            if (ti == null) {
117                System.out.println("  thread has exited");
118                return;
119            }
120            if (ti.getLockOwnerId() == currentThread.getId()) {
121                System.out.println("  thread now blocked");
122                return;
123            }
124            Thread.yield();
125        }
126    }
127
128    public static class RMIJRMPServerImplSub extends RMIJRMPServerImpl {
129        RMIJRMPServerImplSub() throws IOException {
130            super(0, null, null, null);
131        }
132
133        public RMIConnection makeClient() throws IOException {
134            return super.makeClient("connection id", null);
135        }
136
137        @Override
138        protected void clientClosed(RMIConnection conn) throws IOException {
139            System.out.println("clientClosed, will call connectorServer.stop");
140            final Exchanger<Void> x = new Exchanger<Void>();
141            Thread t = new Thread() {
142                public void run() {
143                    try {
144                        connectorServer.stop();
145                    } catch (Exception e) {
146                        fail(e);
147                    }
148                }
149            };
150            t.setName("connectorServer.stop");
151            t.start();
152            waitForBlock(t);
153            /* If this thread is synchronized on RMIServerImpl, then
154             * the thread that does connectorServer.stop will acquire
155             * the clientList lock and then block waiting for the RMIServerImpl
156             * lock.  Our call to super.clientClosed will then deadlock because
157             * it needs to acquire the clientList lock.
158             */
159            System.out.println("calling super.clientClosed");
160            System.out.flush();
161            super.clientClosed(conn);
162        }
163    }
164}
165