/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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 6239400
* @summary Tests NotificationBuffer doesn't hold locks when adding listeners.
* @author Eamonn McManus
* @run clean NotificationBufferDeadlockTest
* @run build NotificationBufferDeadlockTest
* @run main NotificationBufferDeadlockTest
*/
import javax.management.*;
/*
* Regression test for a rare but not unheard-of deadlock condition in
* the notification buffer support for connector servers.
* See bug 6239400 for the description of the bug and the example that
* showed it up.
*
* Here we test that, when the connector server adds its listener to an
* MBean, it is not holding a lock that would prevent another thread from
* emitting a notification (from that MBean or another one). This is
* important, because we don't know how user MBeans might implement
* NotificationBroadcaster.addNotificationListener, and in particular we
* can't be sure that the method is well-behaved and can never do a
* blocking operation, such as attempting to acquire a lock that is also
* acquired when notifications are emitted.
*
* The test creates a special MBean whose addNotificationListener method
* does the standard addNotificationListener logic inherited
* from NotificationBroadcasterSupport, then
* creates another thread that emits a notification from the same MBean.
* The addNotificationListener method waits for this thread to complete.
* If the notification buffer logic is incorrect, then emitting the
* notification will attempt to acquire the lock on the buffer, but that
* lock is being held by the thread that called addNotificationListener,
* so there will be deadlock.
*
* We use this DeadlockMBean several times. First, we create one and then
* add a remote listener to it. The first time you add a remote listener
* through a connector server, the connector server adds its own listener
* to all NotificationBroadcaster MBeans. If it holds a lock while doing
* this, we will see deadlock.
*
* Then we create a second DeadlockMBean. When a new MBean is created that
* is a NotificationBroadcaster, the connector server adds its listener to
* that MBean too. Again if it holds a lock while doing this, we will see
* deadlock.
*
* Finally, we do some magic with MBeanServerForwarders so that while
* queryNames is running (to find MBeans to which listeners must be added)
* we will create new MBeans. This tests that this tricky situation is
* handled correctly. It also tests the queryNames that is run when the
* notification buffer is being destroyed (to remove the listeners).
*
* We cause all of our test MBeans to emit exactly one notification and
* check that we have received exactly one notification from each MBean.
* If the logic for adding the notification buffer's listener is incorrect
* we could remove zero or two notifications from an MBean.
*/
public class NotificationBufferDeadlockTest {
"remote listeners being added");
try {
test(p);
} catch (Exception e) {
}
}
return;
else
}
try {
cs =
} catch (MalformedURLException e) {
return;
}
new Class[] {MBeanServerForwarder.class},
try {
} finally {
}
}
}
};
thisFailure = null;
if (thisFailure != null)
return thisFailure;
if (thisFailure != null)
return thisFailure;
if (thisFailure != null)
return thisFailure;
}
return "missing names: " + sources;
return thisFailure;
}
public static interface DeadlockTestMBean {
public void send();
}
implements DeadlockTestMBean {
public void run() {
Notification n =
DeadlockTest.this.sendNotification(n);
}
};
t.start();
try {
t.join(5000L);
} catch (Exception e) {
thisFailure = "Join exception: " + e;
}
if (t.isAlive())
thisFailure = "Deadlock detected";
}
public void send() {
}
}
private static class CreateDuringQueryInvocationHandler
implements InvocationHandler {
throws Throwable {
return null;
}
return ret;
}
public void run() {
try {
newName());
} catch (Exception e) {
e.printStackTrace();
thisFailure = e.toString();
}
}
};
t.start();
t.join(5000);
if (t.isAlive())
failure = "Query deadlock detected";
}
}
}
try {
return new ObjectName("d:type=DeadlockTest,instance=" +
++nextNameIndex);
} catch (MalformedObjectNameException e) {
throw new IllegalArgumentException("bad ObjectName", e);
}
}
}
synchronized(this) {
this.notifyAll();
}
}
}
}
synchronized(this) {
try {
} catch (InterruptedException ire) {
break;
}
}
}
}
private final int waitNB;
}
static int nextNameIndex;
}