Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/javax/management/remote/mandatory/notif/NotificationBufferDeadlockTest.java
38867 views
/*1* Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 623940026* @summary Tests NotificationBuffer doesn't hold locks when adding listeners,27* if test times out then deadlock is suspected.28* @author Eamonn McManus29* @run clean NotificationBufferDeadlockTest30* @run build NotificationBufferDeadlockTest31* @run main NotificationBufferDeadlockTest32*/3334import java.lang.reflect.InvocationHandler;35import java.lang.reflect.Method;36import java.lang.reflect.Proxy;37import java.net.MalformedURLException;38import java.util.List;39import java.util.Set;40import java.util.Vector;41import java.util.concurrent.CountDownLatch;42import javax.management.*;43import javax.management.remote.*;4445/*46* Regression test for a rare but not unheard-of deadlock condition in47* the notification buffer support for connector servers.48* See bug 6239400 for the description of the bug and the example that49* showed it up.50*51* Here we test that, when the connector server adds its listener to an52* MBean, it is not holding a lock that would prevent another thread from53* emitting a notification (from that MBean or another one). This is54* important, because we don't know how user MBeans might implement55* NotificationBroadcaster.addNotificationListener, and in particular we56* can't be sure that the method is well-behaved and can never do a57* blocking operation, such as attempting to acquire a lock that is also58* acquired when notifications are emitted.59*60* The test creates a special MBean whose addNotificationListener method61* does the standard addNotificationListener logic inherited62* from NotificationBroadcasterSupport, then63* creates another thread that emits a notification from the same MBean.64* The addNotificationListener method waits for this thread to complete.65* If the notification buffer logic is incorrect, then emitting the66* notification will attempt to acquire the lock on the buffer, but that67* lock is being held by the thread that called addNotificationListener,68* so there will be deadlock.69*70* We use this DeadlockMBean several times. First, we create one and then71* add a remote listener to it. The first time you add a remote listener72* through a connector server, the connector server adds its own listener73* to all NotificationBroadcaster MBeans. If it holds a lock while doing74* this, we will see deadlock.75*76* Then we create a second DeadlockMBean. When a new MBean is created that77* is a NotificationBroadcaster, the connector server adds its listener to78* that MBean too. Again if it holds a lock while doing this, we will see79* deadlock.80*81* Finally, we do some magic with MBeanServerForwarders so that while82* queryNames is running (to find MBeans to which listeners must be added)83* we will create new MBeans. This tests that this tricky situation is84* handled correctly. It also tests the queryNames that is run when the85* notification buffer is being destroyed (to remove the listeners).86*87* We cause all of our test MBeans to emit exactly one notification and88* check that we have received exactly one notification from each MBean.89* If the logic for adding the notification buffer's listener is incorrect90* we could remove zero or two notifications from an MBean.91*/92public class NotificationBufferDeadlockTest {93public static void main(String[] args) throws Exception {94System.out.println("Check no deadlock if notif sent while initial " +95"remote listeners being added");96final String[] protos = {"rmi", "iiop", "jmxmp"};97for (String p : protos) {98try {99test(p);100} catch (Exception e) {101System.out.println("TEST FAILED: GOT EXCEPTION:");102e.printStackTrace(System.out);103failure = e.toString();104}105}106if (failure == null)107return;108else109throw new Exception("TEST FAILED: " + failure);110}111112private static void test(String proto) throws Exception {113System.out.println("Testing protocol " + proto);114MBeanServer mbs = MBeanServerFactory.newMBeanServer();115ObjectName testName = newName();116DeadlockTest test = new DeadlockTest();117mbs.registerMBean(test, testName);118JMXServiceURL url = new JMXServiceURL("service:jmx:" + proto + ":///");119JMXConnectorServer cs;120try {121cs =122JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);123} catch (MalformedURLException e) {124System.out.println("...protocol not supported, ignoring");125return;126}127128MBeanServerForwarder createDuringQueryForwarder = (MBeanServerForwarder)129Proxy.newProxyInstance(new Object() {}.getClass().getClassLoader(),130new Class[] {MBeanServerForwarder.class},131new CreateDuringQueryInvocationHandler());132cs.setMBeanServerForwarder(createDuringQueryForwarder);133cs.start();134JMXServiceURL addr = cs.getAddress();135JMXConnector cc = JMXConnectorFactory.connect(addr);136MBeanServerConnection mbsc = cc.getMBeanServerConnection();137try {138String fail = test(mbsc, testName);139if (fail != null)140System.out.println("FAILED: " + fail);141failure = fail;142} finally {143cc.close();144cs.stop();145}146}147148private static String test(MBeanServerConnection mbsc,149ObjectName testName) throws Exception {150151NotificationListener dummyListener = new NotificationListener() {152public void handleNotification(Notification n, Object h) {153}154};155thisFailure = null;156mbsc.addNotificationListener(testName, dummyListener, null, null);157if (thisFailure != null)158return thisFailure;159ObjectName newName = newName();160mbsc.createMBean(DeadlockTest.class.getName(), newName);161if (thisFailure != null)162return thisFailure;163Set<ObjectName> names =164mbsc.queryNames(new ObjectName("d:type=DeadlockTest,*"), null);165System.out.printf("...found %d test MBeans\n", names.size());166167sources.clear();168countListener = new MyListener(names.size());169170for (ObjectName name : names)171mbsc.addNotificationListener(name, countListener, null, null);172if (thisFailure != null)173return thisFailure;174for (ObjectName name : names)175mbsc.invoke(name, "send", null, null);176177countListener.waiting();178179if (!sources.containsAll(names))180return "missing names: " + sources;181return thisFailure;182}183184public static interface DeadlockTestMBean {185public void send();186}187188public static class DeadlockTest extends NotificationBroadcasterSupport189implements DeadlockTestMBean {190@Override191public void addNotificationListener(NotificationListener listener,192NotificationFilter filter,193Object handback) {194super.addNotificationListener(listener, filter, handback);195Thread t = new Thread() {196@Override197public void run() {198Notification n =199new Notification("type", DeadlockTest.this, 0L);200DeadlockTest.this.sendNotification(n);201}202};203t.start();204System.out.println("DeadlockTest-addNotificationListener waiting for the sending thread to die...");205try {206t.join(); //if times out here then deadlock is suspected207System.out.println("DeadlockTest-addNotificationListener OK.");208} catch (Exception e) {209thisFailure = "Join exception: " + e;210}211}212213public void send() {214sendNotification(new Notification(TESTING_TYPE, DeadlockTest.this, 1L));215}216}217218private static class CreateDuringQueryInvocationHandler219implements InvocationHandler {220public Object invoke(Object proxy, Method m, Object[] args)221throws Throwable {222if (m.getName().equals("setMBeanServer")) {223mbs = (MBeanServer) args[0];224return null;225}226createMBeanIfQuery(m);227Object ret = m.invoke(mbs, args);228createMBeanIfQuery(m);229return ret;230}231232private void createMBeanIfQuery(Method m) throws InterruptedException {233if (m.getName().equals("queryNames")) {234Thread t = new Thread() {235public void run() {236try {237mbs.createMBean(DeadlockTest.class.getName(),238newName());239} catch (Exception e) {240e.printStackTrace();241thisFailure = e.toString();242}243}244};245t.start();246System.out.println("CreateDuringQueryInvocationHandler-createMBeanIfQuery waiting for the creating thread to die...");247t.join(); // if times out here then deadlock is suspected248System.out.println("CreateDuringQueryInvocationHandler-createMBeanIfQuery OK");249}250}251252private MBeanServer mbs;253}254255private static synchronized ObjectName newName() {256try {257return new ObjectName("d:type=DeadlockTest,instance=" +258++nextNameIndex);259} catch (MalformedObjectNameException e) {260throw new IllegalArgumentException("bad ObjectName", e);261}262}263264private static class MyListener implements NotificationListener {265public MyListener(int waitNB) {266count = new CountDownLatch(waitNB);267}268269public void handleNotification(Notification n, Object h) {270System.out.println("MyListener got: " + n.getSource() + " " + n.getType());271272if (TESTING_TYPE.equals(n.getType())) {273sources.add((ObjectName) n.getSource());274count.countDown();275}276}277278public void waiting() throws InterruptedException {279System.out.println("MyListener-waiting ...");280count.await(); // if times out here then deadlock is suspected281System.out.println("MyListener-waiting done!");282}283284private final CountDownLatch count;285}286287static String thisFailure;288static String failure;289static int nextNameIndex;290291private static MyListener countListener;292private static final List<ObjectName> sources = new Vector();293294private static final String TESTING_TYPE = "testing_type";295}296297298