1/*
2 * Copyright (c) 2004, 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 5093922 2120055
27 * @summary Test that NotificationBroadcasterSupport can be subclassed
28 * and used with synchronized(this) without causing deadlock
29 * @author Eamonn McManus
30 *
31 * @run clean BroadcasterSupportDeadlockTest
32 * @run build BroadcasterSupportDeadlockTest
33 * @run main BroadcasterSupportDeadlockTest
34 */
35
36import java.lang.management.*;
37import java.util.concurrent.*;
38import javax.management.*;
39
40public class BroadcasterSupportDeadlockTest {
41    public static void main(String[] args) throws Exception {
42        try {
43            Class.forName(ManagementFactory.class.getName());
44        } catch (Throwable t) {
45            System.out.println("TEST CANNOT RUN: needs JDK 5 at least");
46            return;
47        }
48
49        final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
50        final BroadcasterMBean mbean = new Broadcaster();
51        final ObjectName name = new ObjectName("test:type=Broadcaster");
52        mbs.registerMBean(mbean, name);
53
54        ThreadMXBean threads = ManagementFactory.getThreadMXBean();
55        threads.setThreadContentionMonitoringEnabled(true);
56
57        final Semaphore semaphore = new Semaphore(0);
58
59        // Thread 1 - block the Broadcaster
60        Thread t1 = new Thread() {
61            public void run() {
62                try {
63                    mbs.invoke(name, "block",
64                               new Object[] {semaphore},
65                               new String[] {Semaphore.class.getName()});
66                } catch (Exception e) {
67                    e.printStackTrace(System.out);
68                } finally {
69                    System.out.println("TEST INCORRECT: block returned");
70                    System.exit(1);
71                }
72            }
73        };
74        t1.setDaemon(true);
75        t1.start();
76
77        /* Wait for Thread 1 to be doing Object.wait().  It's very
78           difficult to synchronize properly here so we wait for the
79           semaphore, then wait a little longer for the mbs.invoke to
80           run, then just in case that isn't enough, we wait for the
81           thread to be in WAITING state.  This isn't foolproof,
82           because the machine could be very slow and the
83           Thread.getState() could find the thread in WAITING state
84           due to some operation it does on its way to the one we're
85           interested in.  */
86        semaphore.acquire();
87        Thread.sleep(100);
88        while (t1.getState() != Thread.State.WAITING)
89            Thread.sleep(1);
90
91        // Thread 2 - try to add a listener
92        final NotificationListener listener = new NotificationListener() {
93            public void handleNotification(Notification n, Object h) {}
94        };
95        Thread t2 = new Thread() {
96            public void run() {
97                try {
98                    mbs.addNotificationListener(name, listener, null, null);
99                } catch (Exception e) {
100                    System.out.println("TEST INCORRECT: addNL failed:");
101                    e.printStackTrace(System.out);
102                }
103            }
104        };
105        t2.setDaemon(true);
106        t2.start();
107
108        /* Wait for Thread 2 to be blocked on the monitor or to
109           succeed.  */
110        Thread.sleep(100);
111
112        for (int i = 0; i < 1000/*ms*/; i++) {
113            t2.join(1/*ms*/);
114            switch (t2.getState()) {
115            case TERMINATED:
116                System.out.println("TEST PASSED");
117                return;
118            case BLOCKED:
119                java.util.Map<Thread,StackTraceElement[]> traces =
120                    Thread.getAllStackTraces();
121                showStackTrace("Thread 1", traces.get(t1));
122                showStackTrace("Thread 2", traces.get(t2));
123                System.out.println("TEST FAILED: deadlock");
124                System.exit(1);
125                break;
126            default:
127                break;
128            }
129        }
130
131        System.out.println("TEST FAILED BUT DID NOT NOTICE DEADLOCK");
132        Thread.sleep(10000);
133        System.exit(1);
134    }
135
136    private static void showStackTrace(String title,
137                                       StackTraceElement[] stack) {
138        System.out.println("---" + title + "---");
139        if (stack == null)
140            System.out.println("<no stack trace???>");
141        else {
142            for (StackTraceElement elmt : stack)
143                System.out.println("    " + elmt);
144        }
145        System.out.println();
146    }
147
148    public static interface BroadcasterMBean {
149        public void block(Semaphore semaphore);
150    }
151
152    public static class Broadcaster
153            extends NotificationBroadcasterSupport
154            implements BroadcasterMBean {
155        public synchronized void block(Semaphore semaphore) {
156            Object lock = new Object();
157            synchronized (lock) {
158                try {
159                    // Let the caller know that it can now wait for us to
160                    // hit the WAITING state
161                    semaphore.release();
162                    lock.wait(); // block forever
163                } catch (InterruptedException e) {
164                    System.out.println("TEST INCORRECT: lock interrupted:");
165                    e.printStackTrace(System.out);
166                    System.exit(1);
167                }
168            }
169        }
170    }
171}
172