jdk/test/javax/management/notification/BroadcasterSupportDeadlockTest.java
changeset 2 90ce3da70b43
child 5506 202f599c92aa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/notification/BroadcasterSupportDeadlockTest.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2004-2005 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 5093922 2120055
+ * @summary Test that NotificationBroadcasterSupport can be subclassed
+ * and used with synchronized(this) without causing deadlock
+ * @author Eamonn McManus
+ * @run clean BroadcasterSupportDeadlockTest
+ * @run build BroadcasterSupportDeadlockTest
+ * @run main BroadcasterSupportDeadlockTest
+ */
+
+import java.lang.management.*;
+import java.util.concurrent.*;
+import javax.management.*;
+
+public class BroadcasterSupportDeadlockTest {
+    public static void main(String[] args) throws Exception {
+        try {
+            Class.forName(ManagementFactory.class.getName());
+        } catch (Throwable t) {
+            System.out.println("TEST CANNOT RUN: needs JDK 5 at least");
+            return;
+        }
+
+        final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+        final BroadcasterMBean mbean = new Broadcaster();
+        final ObjectName name = new ObjectName("test:type=Broadcaster");
+        mbs.registerMBean(mbean, name);
+
+        ThreadMXBean threads = ManagementFactory.getThreadMXBean();
+        threads.setThreadContentionMonitoringEnabled(true);
+
+        final Semaphore semaphore = new Semaphore(0);
+
+        // Thread 1 - block the Broadcaster
+        Thread t1 = new Thread() {
+            public void run() {
+                try {
+                    mbs.invoke(name, "block",
+                               new Object[] {semaphore},
+                               new String[] {Semaphore.class.getName()});
+                } catch (Exception e) {
+                    e.printStackTrace(System.out);
+                } finally {
+                    System.out.println("TEST INCORRECT: block returned");
+                    System.exit(1);
+                }
+            }
+        };
+        t1.setDaemon(true);
+        t1.start();
+
+        /* Wait for Thread 1 to be doing Object.wait().  It's very
+           difficult to synchronize properly here so we wait for the
+           semaphore, then wait a little longer for the mbs.invoke to
+           run, then just in case that isn't enough, we wait for the
+           thread to be in WAITING state.  This isn't foolproof,
+           because the machine could be very slow and the
+           Thread.getState() could find the thread in WAITING state
+           due to some operation it does on its way to the one we're
+           interested in.  */
+        semaphore.acquire();
+        Thread.sleep(100);
+        while (t1.getState() != Thread.State.WAITING)
+            Thread.sleep(1);
+
+        // Thread 2 - try to add a listener
+        final NotificationListener listener = new NotificationListener() {
+            public void handleNotification(Notification n, Object h) {}
+        };
+        Thread t2 = new Thread() {
+            public void run() {
+                try {
+                    mbs.addNotificationListener(name, listener, null, null);
+                } catch (Exception e) {
+                    System.out.println("TEST INCORRECT: addNL failed:");
+                    e.printStackTrace(System.out);
+                }
+            }
+        };
+        t2.setDaemon(true);
+        t2.start();
+
+        /* Wait for Thread 2 to be blocked on the monitor or to
+           succeed.  */
+        Thread.sleep(100);
+
+        for (int i = 0; i < 1000/*ms*/; i++) {
+            t2.join(1/*ms*/);
+            switch (t2.getState()) {
+            case TERMINATED:
+                System.out.println("TEST PASSED");
+                return;
+            case BLOCKED:
+                java.util.Map<Thread,StackTraceElement[]> traces =
+                    Thread.getAllStackTraces();
+                showStackTrace("Thread 1", traces.get(t1));
+                showStackTrace("Thread 2", traces.get(t2));
+                System.out.println("TEST FAILED: deadlock");
+                System.exit(1);
+                break;
+            default:
+                break;
+            }
+        }
+
+        System.out.println("TEST FAILED BUT DID NOT NOTICE DEADLOCK");
+        Thread.sleep(10000);
+        System.exit(1);
+    }
+
+    private static void showStackTrace(String title,
+                                       StackTraceElement[] stack) {
+        System.out.println("---" + title + "---");
+        if (stack == null)
+            System.out.println("<no stack trace???>");
+        else {
+            for (StackTraceElement elmt : stack)
+                System.out.println("    " + elmt);
+        }
+        System.out.println();
+    }
+
+    public static interface BroadcasterMBean {
+        public void block(Semaphore semaphore);
+    }
+
+    public static class Broadcaster
+            extends NotificationBroadcasterSupport
+            implements BroadcasterMBean {
+        public synchronized void block(Semaphore semaphore) {
+            Object lock = new Object();
+            synchronized (lock) {
+                try {
+                    // Let the caller know that it can now wait for us to
+                    // hit the WAITING state
+                    semaphore.release();
+                    lock.wait(); // block forever
+                } catch (InterruptedException e) {
+                    System.out.println("TEST INCORRECT: lock interrupted:");
+                    e.printStackTrace(System.out);
+                    System.exit(1);
+                }
+            }
+        }
+    }
+}