8170896: TEST_BUG: java/rmi/server/Unreferenced/leaseCheckInterval/LeaseCheckInterval.java failed with unreferenced() not invoked after 20.0 seconds

Reviewed-by: smarks, msheppar, dfuchs
This commit is contained in:
Jaikiran Pai 2026-02-03 09:35:21 +00:00
parent f43fbf0823
commit efa16e9e5f
2 changed files with 69 additions and 52 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2026, Oracle and/or its affiliates. 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
@ -31,10 +31,9 @@
* may be delayed longer than expected. While this is not a spec violation
* (because there are no timeliness guarantees for any of these garbage
* collection-related events), the user might expect that an unreferenced()
* invocation for an object whose last client has terminated abnorally
* invocation for an object whose last client has terminated abnormally
* should occur on relatively the same time order as the lease value
* granted.
* @author Peter Jones
*
* @library ../../../testlibrary
* @modules java.rmi/sun.rmi.registry
@ -47,40 +46,39 @@
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.server.Unreferenced;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.CountDownLatch;
public class LeaseCheckInterval implements Remote, Unreferenced {
public static final String BINDING = "LeaseCheckInterval";
// lease expiry time (milliseconds)
private static final long LEASE_VALUE = 10000;
private static final long TIMEOUT = 20000;
// the maximum allowed duration between the lease expiration and
// the Unreferenced.unreferenced() callback method to be invoked
private static final Duration EXPECTED_MAX_DURATION = Duration.ofMinutes(1);
private Object lock = new Object();
private boolean unreferencedInvoked = false;
// will be counted down when Unreferenced.unreferenced() is invoked
private static final CountDownLatch callbackInvocationLatch = new CountDownLatch(1);
@Override
public void unreferenced() {
System.err.println("unreferenced() method invoked");
synchronized (lock) {
unreferencedInvoked = true;
lock.notify();
}
System.err.println("[" + Instant.now() + "] unreferenced() method invoked");
callbackInvocationLatch.countDown();
}
public static void main(String[] args) throws Exception {
System.err.println("\nRegression test for bug 4285878\n");
/*
* Set the duration of leases granted to a very small value, so that
* we can test if expirations are detected in a roughly comparable
* time.
*/
System.setProperty("java.rmi.dgc.leaseValue",
String.valueOf(LEASE_VALUE));
System.setProperty("java.rmi.dgc.leaseValue", String.valueOf(LEASE_VALUE));
System.err.println("running test with java.rmi.dgc.leaseValue set to " + LEASE_VALUE);
LeaseCheckInterval obj = new LeaseCheckInterval();
JavaVM jvm = null;
@ -90,37 +88,32 @@ public class LeaseCheckInterval implements Remote, Unreferenced {
Registry localRegistry = TestLibrary.createRegistryOnEphemeralPort();
int registryPort = TestLibrary.getRegistryPort(localRegistry);
System.err.println("created local registry");
System.err.println("created local registry on port " + registryPort);
localRegistry.bind(BINDING, obj);
System.err.println("bound remote object in local registry");
synchronized (obj.lock) {
System.err.println("starting remote client VM...");
jvm = new JavaVM("SelfTerminator", "-Drmi.registry.port=" +
registryPort, "");
jvm.start();
System.err.println("waiting for unreferenced() callback...");
obj.lock.wait(TIMEOUT);
if (obj.unreferencedInvoked) {
System.err.println("TEST PASSED: " +
"unreferenced() invoked in timely fashion");
} else {
throw new RuntimeException(
"TEST FAILED: unreferenced() not invoked after " +
((double) TIMEOUT / 1000.0) + " seconds");
}
}
} catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
throw new RuntimeException(
"TEST FAILED: unexpected exception: " + e.toString());
System.err.println("starting remote client VM...");
jvm = new JavaVM("SelfTerminator", "-Drmi.registry.port=" + registryPort, "");
// launch the self terminating java application which will lookup
// the bound object (thus creating a lease) and then terminate itself (thus
// creating the condition for a lease expiry).
jvm.start();
final Instant startTime = Instant.now();
System.err.println("waiting for SelfTerminator process to complete");
final int exitCode = jvm.waitFor();
if (exitCode != 0) {
throw new AssertionError("SelfTerminator process exited with" +
" a non-zero exit code: " + exitCode);
}
System.err.println("SelfTerminator process completed in "
+ Duration.between(startTime, Instant.now())
+ ", now waiting for Unreferenced.unreferenced() callback to be invoked");
callbackInvocationLatch.await();
final Instant waitEndedAt = Instant.now();
final Duration waitDuration = assertWithinExpectedTimeLimit(waitEndedAt, startTime);
System.err.println("TEST PASSED: unreferenced() invoked in timely" +
" fashion (duration=" + waitDuration + ")");
} finally {
if (jvm != null) {
jvm.destroy();
@ -135,4 +128,22 @@ public class LeaseCheckInterval implements Remote, Unreferenced {
}
}
}
/*
* Verifies that the duration between the lease expiration and the callback
* invocation is within the expected limit. Throws an exception if the wait
* duration is larger than expected limit, else returns the actual wait duration.
*/
private static Duration assertWithinExpectedTimeLimit(final Instant waitEndedAt,
final Instant terminationStartedAt) {
final Duration waitDuration = Duration.between(terminationStartedAt, waitEndedAt);
System.out.println("wait completed in " + waitDuration);
if (waitDuration.compareTo(EXPECTED_MAX_DURATION) > 0) {
throw new RuntimeException("Took unexpectedly long (duration=" +
waitDuration + ") to invoke Unreferenced.unreferenced()," +
" expected max duration=" + EXPECTED_MAX_DURATION);
}
return waitDuration;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2026, Oracle and/or its affiliates. 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
@ -21,26 +21,32 @@
* questions.
*/
/*
*
*/
import java.rmi.Remote;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.time.Instant;
public class SelfTerminator {
public static void main(String[] args) {
public static void main(String[] args) throws Exception {
try {
log("main() invoked");
int registryPort =
Integer.parseInt(System.getProperty("rmi.registry.port"));
Registry registry =
LocateRegistry.getRegistry("", registryPort);
Remote stub = registry.lookup(LeaseCheckInterval.BINDING);
log("looked up binding, now terminating the process");
Runtime.getRuntime().halt(0);
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable t) {
log("failure: " + t);
t.printStackTrace();
throw t; // propagate any failures and fail the process
}
}
private static void log(final String message) {
final Instant now = Instant.now();
System.err.println("[" + now + "] " + SelfTerminator.class.getName() + " - " + message);
}
}