From efa16e9e5fb07088ef2e0f2509e40fd97e4141d1 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Tue, 3 Feb 2026 09:35:21 +0000 Subject: [PATCH] 8170896: TEST_BUG: java/rmi/server/Unreferenced/leaseCheckInterval/LeaseCheckInterval.java failed with unreferenced() not invoked after 20.0 seconds Reviewed-by: smarks, msheppar, dfuchs --- .../LeaseCheckInterval.java | 99 ++++++++++--------- .../leaseCheckInterval/SelfTerminator.java | 22 +++-- 2 files changed, 69 insertions(+), 52 deletions(-) diff --git a/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/LeaseCheckInterval.java b/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/LeaseCheckInterval.java index 3fa349fd671..e7c990ebdbd 100644 --- a/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/LeaseCheckInterval.java +++ b/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/LeaseCheckInterval.java @@ -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; + } } diff --git a/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/SelfTerminator.java b/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/SelfTerminator.java index 4875634dbb3..36ebbf05bf1 100644 --- a/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/SelfTerminator.java +++ b/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/SelfTerminator.java @@ -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); + } }