From d3747d9f66d995463c4f890f21c6836462bbe03f Mon Sep 17 00:00:00 2001 From: Neil Richards Date: Wed, 24 Jun 2026 02:44:28 +0100 Subject: [PATCH 01/11] refactor(yoko-core): remove assertion-only finalize() methods - Removed finalize() from ClientManager (assertions for destroyed state and empty collections) - Removed finalize() from InitialServiceManager (assertion for destroy_ flag) - Removed finalize() from ORBInstance (assertion for destroyCalled flag) - Removed finalize() from ServerManager (assertions for destroy_ flag and empty collections) - Removed finalize() from ThreadPool (assertion for destroy_ flag) - Removed finalize() from ValueFactoryManager (assertion for destroy_ flag) - Removed finalize() from POA_impl (assertion for destroyed state) - Removed finalize() from PoaCurrentImpl (empty finalize method) - Lines removed: 40 Co-authored-by-AI: IBM Bob 1.0.4 --- .../java/org/apache/yoko/orb/OB/ClientManager.java | 7 ------- .../org/apache/yoko/orb/OB/InitialServiceManager.java | 4 ---- .../main/java/org/apache/yoko/orb/OB/ORBInstance.java | 10 +++------- .../java/org/apache/yoko/orb/OB/ServerManager.java | 9 +-------- .../main/java/org/apache/yoko/orb/OB/ThreadPool.java | 8 +------- .../org/apache/yoko/orb/OB/ValueFactoryManager.java | 4 ---- .../org/apache/yoko/orb/OBPortableServer/POA_impl.java | 5 ----- .../apache/yoko/orb/PortableServer/PoaCurrentImpl.java | 5 +---- 8 files changed, 6 insertions(+), 46 deletions(-) diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/ClientManager.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/ClientManager.java index 79e4a7530..276c59d9a 100644 --- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/ClientManager.java +++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/ClientManager.java @@ -72,13 +72,6 @@ public final class ClientManager { // ClientManager private and protected member implementations // ---------------------------------------------------------------------- - protected void finalize() throws Throwable { - Assert.ensure(destroyed); - Assert.ensure(allClients.isEmpty()); - Assert.ensure(reusableClients.isEmpty()); - - super.finalize(); - } // ---------------------------------------------------------------------- // ClientManager package member implementations diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/InitialServiceManager.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/InitialServiceManager.java index c2a92edeb..fdfc10f5f 100644 --- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/InitialServiceManager.java +++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/InitialServiceManager.java @@ -60,10 +60,6 @@ private static class Service { // InitialServiceManager private and protected member implementations // ---------------------------------------------------------------------- - protected void finalize() throws Throwable { - Assert.ensure(destroy_); - super.finalize(); - } // ---------------------------------------------------------------------- // InitialServiceManager package member implementations diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/ORBInstance.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/ORBInstance.java index c8b7951ca..3f770c72f 100644 --- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/ORBInstance.java +++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/ORBInstance.java @@ -77,10 +77,6 @@ public final class ORBInstance { private OrbAsyncHandler asyncHandler; private final AtomicBoolean destroyCalled = new AtomicBoolean(); // True if destroy() was called - protected void finalize() throws Throwable { - Assert.ensure(destroyCalled.get()); - super.finalize(); - } public ORBInstance(ORB orb, String orbId, String serverID, String serverInstance, ObjectFactory objectFactory, @@ -201,7 +197,7 @@ public void destroy() { // CoreTraceLevels is not destroyed -- it is indestructible // Client and server executors shut down in the ORBControl - + conFactoryRegistry = null; accFactoryRegistry = null; unknownExceptionStrategy = null; @@ -292,11 +288,11 @@ public ExecutorService getServerExecutor() { public Phaser getServerPhaser() { return serverPhaser; } - + public ExecutorService getClientExecutor() { return clientExecutor; } - + public Phaser getClientPhaser() { return clientPhaser; } diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/ServerManager.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/ServerManager.java index b138de57b..b1d88d9d9 100644 --- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/ServerManager.java +++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/ServerManager.java @@ -28,7 +28,7 @@ public final class ServerManager { static final Logger logger = getLogger(ServerManager.class.getName()); - + private boolean destroy_; // if destroy() was called private CollocatedServer collocatedServer_; // The collocated server @@ -40,13 +40,6 @@ public final class ServerManager { // ServerManager private and protected member implementations // ---------------------------------------------------------------------- - protected void finalize() throws Throwable { - Assert.ensure(destroy_); - Assert.ensure(allServers_.isEmpty()); - Assert.ensure(collocatedServer_ == null); - - super.finalize(); - } // ---------------------------------------------------------------------- // ServerManager public member implementations diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/ThreadPool.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/ThreadPool.java index f7a9adae2..9b214b6b5 100644 --- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/ThreadPool.java +++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/ThreadPool.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 IBM Corporation and others. + * Copyright 2026 IBM Corporation and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -70,12 +70,6 @@ public ThreadPool(int id, int n) { } } - protected void finalize() throws Throwable { - if (!destroy_) - throw new InternalError(); - - super.finalize(); - } void destroy() { synchronized (this) { diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueFactoryManager.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueFactoryManager.java index 18017a943..98821d6bd 100644 --- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueFactoryManager.java +++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueFactoryManager.java @@ -57,10 +57,6 @@ public final class ValueFactoryManager { // ValueFactoryManager private and protected member implementations // ---------------------------------------------------------------------- - protected void finalize() throws Throwable { - Assert.ensure(destroy_); - super.finalize(); - } // ---------------------------------------------------------------------- // ValueFactoryManager package member implementations diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OBPortableServer/POA_impl.java b/yoko-core/src/main/java/org/apache/yoko/orb/OBPortableServer/POA_impl.java index bc240f8dd..4a16ca644 100644 --- a/yoko-core/src/main/java/org/apache/yoko/orb/OBPortableServer/POA_impl.java +++ b/yoko-core/src/main/java/org/apache/yoko/orb/OBPortableServer/POA_impl.java @@ -388,11 +388,6 @@ private static Policy[] getDefaultRawPolicies() { }; } - @SuppressWarnings("deprecation") - protected void finalize() throws Throwable { - Assert.ensure(poaControl_.getDestroyed()); - super.finalize(); - } // ---------------------------------------------------------------------- // Standard IDL to Java mapping diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/PortableServer/PoaCurrentImpl.java b/yoko-core/src/main/java/org/apache/yoko/orb/PortableServer/PoaCurrentImpl.java index 9ce3751a0..387fa363f 100644 --- a/yoko-core/src/main/java/org/apache/yoko/orb/PortableServer/PoaCurrentImpl.java +++ b/yoko-core/src/main/java/org/apache/yoko/orb/PortableServer/PoaCurrentImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 IBM Corporation and others. + * Copyright 2026 IBM Corporation and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,9 +39,6 @@ final public class PoaCurrentImpl extends org.omg.CORBA.LocalObject implements o public PoaCurrentImpl() { } - protected void finalize() throws Throwable { - super.finalize(); - } public org.omg.PortableServer.POA get_POA() throws org.omg.PortableServer.CurrentPackage.NoContext { From e8ab07562984e347ce907797b39226331b26a970 Mon Sep 17 00:00:00 2001 From: Neil Richards Date: Wed, 24 Jun 2026 03:33:09 +0100 Subject: [PATCH 02/11] feat(yoko-util): add YokoCleaner for Java 8-compatible resource cleanup - Created YokoCleaner class using PhantomReference + ReferenceQueue - Returns SimplyCloseable for manual cleanup triggering - Uses LazyReference internally for idempotent, thread-safe cleanup - Use LazyReference to defer thread creation until first register() call - All threads calling close() block until cleanup completes - Exceptions in cleanup actions are caught and logged - Added comprehensive test suite with 10 passing tests - Lines added: 382 Co-authored-by-AI: IBM Bob 1.0.4 --- .../yoko/util/concurrent/YokoCleaner.java | 168 ++++++++++++++ .../yoko/util/concurrent/YokoCleanerTest.java | 214 ++++++++++++++++++ 2 files changed, 382 insertions(+) create mode 100644 yoko-util/src/main/java/org/apache/yoko/util/concurrent/YokoCleaner.java create mode 100644 yoko-util/src/test/java/org/apache/yoko/util/concurrent/YokoCleanerTest.java diff --git a/yoko-util/src/main/java/org/apache/yoko/util/concurrent/YokoCleaner.java b/yoko-util/src/main/java/org/apache/yoko/util/concurrent/YokoCleaner.java new file mode 100644 index 000000000..d1781fbe3 --- /dev/null +++ b/yoko-util/src/main/java/org/apache/yoko/util/concurrent/YokoCleaner.java @@ -0,0 +1,168 @@ +/* + * Copyright 2026 IBM Corporation and others. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.apache.yoko.util.concurrent; + +import org.apache.yoko.io.SimplyCloseable; + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; + +/** + * A Java 8-compatible implementation of automatic resource cleanup similar to Java 9's {@code java.lang.ref.Cleaner}. + * + *

This class uses {@link PhantomReference} and {@link ReferenceQueue} to detect when objects become + * phantom reachable (i.e., eligible for garbage collection) and automatically executes registered cleanup actions. + * + *

All YokoCleaner instances share a single daemon thread for processing cleanup actions, reducing + * thread overhead in applications with multiple cleaner instances. + * + *

Usage Example: + *

{@code
+ * public class ResourceHolder {
+ *     private static final YokoCleaner cleaner = YokoCleaner.create();
+ *     private final SimplyCloseable cleanable;
+ *
+ *     public ResourceHolder() {
+ *         // Register cleanup action that will run when this object is GC'd
+ *         this.cleanable = cleaner.register(this, () -> {
+ *             // Cleanup code here - must not reference 'this'
+ *             closeNativeResource();
+ *         });
+ *     }
+ *
+ *     public void close() {
+ *         // Manually trigger cleanup if needed
+ *         cleanable.close();
+ *     }
+ * }
+ * }
+ * + *

Important Notes: + *

    + *
  • The cleanup action must NOT hold a strong reference to the object being monitored
  • + *
  • Cleanup actions run on a shared daemon thread
  • + *
  • Exceptions thrown by cleanup actions are logged but do not stop the cleaner thread
  • + *
  • The returned {@link SimplyCloseable} is idempotent - multiple calls to {@code close()} are safe
  • + *
  • All threads calling {@code close()} will block until the cleanup action completes
  • + *
+ * + * @see java.lang.ref.Cleaner + * @see java.lang.ref.PhantomReference + */ +public final class YokoCleaner { + private static final Logger logger = Logger.getLogger(YokoCleaner.class.getName()); + + // Shared queue and cleaner thread for all YokoCleaner instances + private static final ReferenceQueue SHARED_QUEUE = new ReferenceQueue<>(); + private static final LazyReference lazyCleanupThread = new LazyReference<>(YokoCleaner::startCleanupThread); + + private static Void startCleanupThread() { + Thread thread = new Thread(YokoCleaner::processQueue, "YokoCleaner-Thread"); + thread.setDaemon(true); + thread.start(); + return null; + } + + private final Set> cleanables = ConcurrentHashMap.newKeySet(); + + /** + * Creates a new cleaner instance. All instances share a single daemon thread for processing cleanup actions. + * + * @return a new cleaner instance + */ + public static YokoCleaner create() { return new YokoCleaner(); } + + private YokoCleaner() { + // Nothing to initialize - uses shared static resources + } + + /** + * Registers an object and a cleanup action to be executed when the object becomes phantom reachable. + * + *

CRITICAL: The cleanup action must NOT hold a strong reference to the object being + * monitored, otherwise the object will never become phantom reachable and the cleanup will never run. + * + *

The returned {@link SimplyCloseable} can be used to manually trigger cleanup before garbage collection. + * Calling {@code close()} is idempotent and thread-safe - all threads will block until cleanup completes. + * + * @param obj the object to monitor for garbage collection + * @param action the cleanup action to execute (must not reference {@code obj}) + * @return a {@link SimplyCloseable} that can be used to manually trigger cleanup + * @throws NullPointerException if obj or action is null + */ + public SimplyCloseable register(Object obj, Runnable action) { + Objects.requireNonNull(obj, "obj must not be null"); + Objects.requireNonNull(action, "action must not be null"); + + // Ensure cleaner thread is started on first registration + lazyCleanupThread.get(); + + return PhantomCleanable.create(obj, action, cleanables); + } + + private static void processQueue() { + logger.info("YokoCleaner thread started"); + for (;;) { + try { + // Block until a reference is available + PhantomCleanable ref = (PhantomCleanable) SHARED_QUEUE.remove(); + ref.close(); + } catch (InterruptedException e) { + // Continue processing - daemon thread runs forever + } catch (Throwable t) { + // Log but don't stop the cleaner thread - catch Throwable to ensure thread continues + logger.warning("Exception in YokoCleaner cleanup action: " + t); + } + } + } + + private static final class PhantomCleanable extends PhantomReference implements SimplyCloseable { + private final LazyReference lazyCleanup; + + static PhantomCleanable create(T referent, Runnable action, Set> cleanables) { + PhantomCleanable cleanable = new PhantomCleanable<>(referent, YokoCleaner.SHARED_QUEUE, action, cleanables); + cleanables.add(cleanable); + return cleanable; + } + + private PhantomCleanable(T referent, ReferenceQueue queue, Runnable action, Set> cleanables) { + super(referent, queue); + // Use LazyReference to ensure cleanup happens exactly once + // and all threads wait for completion + this.lazyCleanup = new LazyReference<>(() -> { + try { + action.run(); + } catch (Throwable t) { + // Log but don't propagate exceptions from cleanup actions + logger.warning("Exception in cleanup action: " + t); + } finally { + cleanables.remove(this); + clear(); + } + return null; + }); + } + + @Override + public void close() { lazyCleanup.get(); } + } +} diff --git a/yoko-util/src/test/java/org/apache/yoko/util/concurrent/YokoCleanerTest.java b/yoko-util/src/test/java/org/apache/yoko/util/concurrent/YokoCleanerTest.java new file mode 100644 index 000000000..8209926f6 --- /dev/null +++ b/yoko-util/src/test/java/org/apache/yoko/util/concurrent/YokoCleanerTest.java @@ -0,0 +1,214 @@ +/* + * Copyright 2026 IBM Corporation and others. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.apache.yoko.util.concurrent; + +import org.apache.yoko.io.SimplyCloseable; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.*; + +public class YokoCleanerTest { + + private YokoCleaner cleaner; + + @BeforeEach + public void setUp() { + cleaner = YokoCleaner.create(); + } + + @AfterEach + public void tearDown() { + // No shutdown needed - singleton cleaner thread runs forever + } + + @Test + public void testManualCleanup() throws Exception { + AtomicInteger cleanupCount = new AtomicInteger(0); + Object obj = new Object(); + + SimplyCloseable cleanable = cleaner.register(obj, cleanupCount::incrementAndGet); + + // Manually trigger cleanup + cleanable.close(); + + assertEquals(1, cleanupCount.get(), "Cleanup should have been called once"); + } + + @Test + public void testIdempotentCleanup() throws Exception { + AtomicInteger cleanupCount = new AtomicInteger(0); + Object obj = new Object(); + + SimplyCloseable cleanable = cleaner.register(obj, cleanupCount::incrementAndGet); + + // Call close multiple times + cleanable.close(); + cleanable.close(); + cleanable.close(); + + assertEquals(1, cleanupCount.get(), "Cleanup should have been called exactly once despite multiple close() calls"); + } + + @Test + public void testConcurrentCleanup() throws Exception { + AtomicInteger cleanupCount = new AtomicInteger(0); + CountDownLatch startLatch = new CountDownLatch(1); + CountDownLatch doneLatch = new CountDownLatch(5); + Object obj = new Object(); + + SimplyCloseable cleanable = cleaner.register(obj, () -> { + cleanupCount.incrementAndGet(); + try { + Thread.sleep(100); // Simulate some work + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }); + + // Start 5 threads that all try to close concurrently + for (int i = 0; i < 5; i++) { + new Thread(() -> { + try { + startLatch.await(); + cleanable.close(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } finally { + doneLatch.countDown(); + } + }).start(); + } + + startLatch.countDown(); // Start all threads + assertTrue(doneLatch.await(5, TimeUnit.SECONDS), "All threads should complete"); + assertEquals(1, cleanupCount.get(), "Cleanup should have been called exactly once despite concurrent close() calls"); + } + + @Test + public void testAutomaticCleanupOnGC() throws Exception { + AtomicInteger cleanupCount = new AtomicInteger(0); + CountDownLatch cleanupLatch = new CountDownLatch(1); + + // Create object in a separate scope so it can be GC'd + createAndRegisterObject(cleanupCount, cleanupLatch); + + // Force garbage collection + for (int i = 0; i < 10; i++) { + System.gc(); + System.runFinalization(); + if (cleanupLatch.await(100, TimeUnit.MILLISECONDS)) { + break; + } + } + + assertTrue(cleanupLatch.await(5, TimeUnit.SECONDS), "Cleanup should have been triggered by GC"); + assertEquals(1, cleanupCount.get(), "Cleanup should have been called once"); + } + + private void createAndRegisterObject(AtomicInteger cleanupCount, CountDownLatch cleanupLatch) { + Object obj = new Object(); + cleaner.register(obj, () -> { + cleanupCount.incrementAndGet(); + cleanupLatch.countDown(); + }); + // obj goes out of scope here and becomes eligible for GC + } + + @Test + public void testCleanupWithException() throws Exception { + AtomicInteger cleanupCount = new AtomicInteger(0); + Object obj = new Object(); + + SimplyCloseable cleanable = cleaner.register(obj, () -> { + cleanupCount.incrementAndGet(); + throw new RuntimeException("Test exception"); + }); + + // Should not throw exception to caller + assertDoesNotThrow(cleanable::close); + assertEquals(1, cleanupCount.get(), "Cleanup should have been called despite exception"); + } + + @Test + public void testNullObjectThrowsException() { + assertThrows(NullPointerException.class, () -> { + cleaner.register(null, () -> {}); + }); + } + + @Test + public void testNullActionThrowsException() { + assertThrows(NullPointerException.class, () -> { + cleaner.register(new Object(), null); + }); + } + + @Test + public void testMultipleRegistrations() throws Exception { + AtomicInteger cleanup1Count = new AtomicInteger(0); + AtomicInteger cleanup2Count = new AtomicInteger(0); + AtomicInteger cleanup3Count = new AtomicInteger(0); + + Object obj1 = new Object(); + Object obj2 = new Object(); + Object obj3 = new Object(); + + SimplyCloseable cleanable1 = cleaner.register(obj1, cleanup1Count::incrementAndGet); + SimplyCloseable cleanable2 = cleaner.register(obj2, cleanup2Count::incrementAndGet); + SimplyCloseable cleanable3 = cleaner.register(obj3, cleanup3Count::incrementAndGet); + + cleanable1.close(); + cleanable2.close(); + cleanable3.close(); + + assertEquals(1, cleanup1Count.get()); + assertEquals(1, cleanup2Count.get()); + assertEquals(1, cleanup3Count.get()); + } + + @Test + public void testCleanupActionDoesNotReferenceObject() throws Exception { + // This test verifies the pattern where cleanup action must not reference the monitored object + AtomicInteger cleanupCount = new AtomicInteger(0); + + class Resource { + private final int id; + Resource(int id) { this.id = id; } + int getId() { return id; } + } + + Resource resource = new Resource(42); + int capturedId = resource.getId(); // Capture value, not reference + + SimplyCloseable cleanable = cleaner.register(resource, () -> { + // This is correct - uses captured primitive value, not object reference + cleanupCount.addAndGet(capturedId); + }); + + cleanable.close(); + assertEquals(42, cleanupCount.get()); + } + + +} From c96683e27c6a47b4d3de10904bcab48270a024af Mon Sep 17 00:00:00 2001 From: Neil Richards Date: Wed, 24 Jun 2026 05:04:47 +0100 Subject: [PATCH 03/11] refactor(yoko-core): replace DowncallStub finalizer with YokoCleaner Phase 3 of finalization refactoring: Refactor DowncallStub to use YokoCleaner for automatic resource cleanup instead of finalize(). Key changes: - Added CleanupState static nested class using LazyReference pattern for thread-safe lazy initialization of clientProfilePairs - Changed clientProfilePairs_ from Vector to LazyReference - Removed destroy() and finalize() methods - Updated all references to use LazyReference.get() - Cleanup action releases clients via ClientManager when object becomes phantom reachable - Substantive lines added: 71, lines removed: 63 Benefits: - Eliminates finalizer overhead and unpredictability - Thread-safe lazy initialization via LazyReference - Explicit cleanup via SimplyCloseable when needed - Automatic cleanup on garbage collection via YokoCleaner Tests: All 91,844 yoko-core tests pass, test-osgi tests pass --- .../org/apache/yoko/orb/OB/DowncallStub.java | 134 ++++++++++-------- 1 file changed, 71 insertions(+), 63 deletions(-) diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/DowncallStub.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/DowncallStub.java index 920177794..4b014a462 100644 --- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/DowncallStub.java +++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/DowncallStub.java @@ -17,25 +17,11 @@ */ package org.apache.yoko.orb.OB; -import static java.util.logging.Level.FINE; -import static org.apache.yoko.io.AlignmentBoundary.EIGHT_BYTE_BOUNDARY; -import static org.apache.yoko.util.Arrays.emptyArray; -import static org.apache.yoko.io.Buffer.createWriteBuffer; -import static org.apache.yoko.logging.VerboseLogging.RETRY_LOG; -import static org.apache.yoko.orb.OCI.GiopVersion.GIOP1_2; -import static org.apache.yoko.orb.exceptions.Transients.NO_USABLE_PROFILE_IN_IOR; -import static org.apache.yoko.util.Assert.ensure; -import static org.apache.yoko.util.MinorCodes.MinorShutdownCalled; -import static org.apache.yoko.util.MinorCodes.describeBadInvOrder; -import static org.omg.CORBA.CompletionStatus.COMPLETED_NO; - -import java.util.Vector; -import java.util.logging.Logger; - import org.apache.yoko.io.ReadBuffer; +import org.apache.yoko.io.SimplyCloseable; import org.apache.yoko.io.WriteBuffer; -import org.apache.yoko.orb.CORBA.YokoInputStream; import org.apache.yoko.orb.CORBA.OutputStreamHolder; +import org.apache.yoko.orb.CORBA.YokoInputStream; import org.apache.yoko.orb.CORBA.YokoOutputStream; import org.apache.yoko.orb.IOP.ServiceContexts; import org.apache.yoko.orb.OCI.ConnectorInfo; @@ -43,6 +29,8 @@ import org.apache.yoko.orb.OCI.ProfileInfoHolder; import org.apache.yoko.orb.OCI.TransportInfo; import org.apache.yoko.util.Assert; +import org.apache.yoko.util.concurrent.LazyReference; +import org.apache.yoko.util.concurrent.YokoCleaner; import org.omg.CORBA.BAD_INV_ORDER; import org.omg.CORBA.BooleanHolder; import org.omg.CORBA.COMM_FAILURE; @@ -84,11 +72,55 @@ import org.omg.Messaging.PolicyValueSeqHolder; import org.omg.Messaging.ReplyHandler; +import java.util.Vector; +import java.util.logging.Logger; + +import static java.util.logging.Level.FINE; +import static org.apache.yoko.io.AlignmentBoundary.EIGHT_BYTE_BOUNDARY; +import static org.apache.yoko.io.Buffer.createWriteBuffer; +import static org.apache.yoko.logging.VerboseLogging.RETRY_LOG; +import static org.apache.yoko.orb.OCI.GiopVersion.GIOP1_2; +import static org.apache.yoko.orb.exceptions.Transients.NO_USABLE_PROFILE_IN_IOR; +import static org.apache.yoko.util.Arrays.emptyArray; +import static org.apache.yoko.util.Assert.ensure; +import static org.apache.yoko.util.MinorCodes.MinorShutdownCalled; +import static org.apache.yoko.util.MinorCodes.describeBadInvOrder; +import static org.omg.CORBA.CompletionStatus.COMPLETED_NO; + // // DowncallStub is equivalent to the C++ class OB::MarshalStubImpl // public final class DowncallStub { static final Logger logger = Logger.getLogger(DowncallStub.class.getName()); + private static final YokoCleaner cleaner = YokoCleaner.create(); + + // + // Shared state for cleanup - must not reference DowncallStub instance + // Uses LazyReference to handle lazy initialization of clientProfilePairs_ + // + private static final class CleanupState { + final ORBInstance orbInstance; + final LazyReference> clientProfilePairsRef; + + CleanupState(ORBInstance orbInstance, LazyReference> clientProfilePairsRef) { + this.orbInstance = orbInstance; + this.clientProfilePairsRef = clientProfilePairsRef; + } + + void cleanup() { + ClientManager clientManager = orbInstance.getClientManager(); + if (clientManager != null && clientProfilePairsRef != null) { + Vector pairs = clientProfilePairsRef.get(); + if (pairs != null) { + for (ClientProfilePair pair : pairs) { + clientManager.releaseClient(pair.client); + } + pairs.removeAllElements(); + } + } + } + } + // // The ORBInstance object // @@ -107,17 +139,16 @@ public final class DowncallStub { private RefCountPolicyList policies_; // - // All client/profile pairs + // All client/profile pairs - uses LazyReference for thread-safe lazy initialization // - private Vector clientProfilePairs_; + private final LazyReference> clientProfilePairsRef; // // We need a class to carry the DowncallStub and Downcall across // a portable stub invocation // - private class InvocationContext { + private static class InvocationContext { DowncallStub downcallStub; - Downcall downcall; } @@ -125,66 +156,42 @@ private class InvocationContext { // Private and protected member implementations // ------------------------------------------------------------------ private synchronized ClientProfilePair getClientProfilePair() throws FailureException { - // Lazy initialization of the client/profile pairs - if (null == clientProfilePairs_) { - // Get all clients that can be used - ClientManager clientManager = orbInstance_.getClientManager(); - clientProfilePairs_ = clientManager.getClientProfilePairs(IOR_, policies_.value); - } + // Get the lazily-initialized client/profile pairs + Vector pairs = clientProfilePairsRef.get(); // If we can't get any client/profile pairs, set and raise the // failure exception, and let the stub handle this. - if (clientProfilePairs_.isEmpty()) { + if (pairs.isEmpty()) { RETRY_LOG.fine(() -> "No profiles available"); throw new FailureException(NO_USABLE_PROFILE_IN_IOR.create()); } - // NB: see handleFailureException() for how clientProfilePairs_ is modified (pruned) in exception + // NB: see handleFailureException() for how clientProfilePairsRef is modified (pruned) in exception // processing (so the first element may change) - return clientProfilePairs_.elementAt(0); + return pairs.elementAt(0); } - private void destroy() { - // - // If the ORB has been destroyed then the clientManager can be nil - // - ClientManager clientManager = orbInstance_.getClientManager(); - if (clientManager != null && clientProfilePairs_ != null) { - for (ClientProfilePair pair: clientProfilePairs_) { - clientManager.releaseClient(pair.client); - } - } - - clientProfilePairs_.removeAllElements(); - } - - protected void finalize() throws Throwable { - destroy(); - - super.finalize(); - } // ------------------------------------------------------------------ // Public member implementations // ------------------------------------------------------------------ public DowncallStub(ORBInstance orbInstance, IOR ior, IOR origIOR, RefCountPolicyList policies) { - clientProfilePairs_ = null; - - // - // Save the ORBInstance object - // + // Initialize fields orbInstance_ = orbInstance; - // - // Save the IOR - // IOR_ = ior; origIOR_ = origIOR; - - // - // Save the policies - // policies_ = policies; + + // Lazy initialization of client/profile pairs + clientProfilePairsRef = new LazyReference<>(() -> { + ClientManager clientManager = orbInstance_.getClientManager(); + return clientManager.getClientProfilePairs(IOR_, policies_.value); + }); + + // Register cleanup action - uses shared state to avoid referencing 'this' + CleanupState state = new CleanupState(orbInstance_, clientProfilePairsRef); + @SuppressWarnings("resource") SimplyCloseable ignored = cleaner.register(this, state::cleanup); } // @@ -339,10 +346,11 @@ public synchronized void handleFailureException(ClientProfilePair cp, FailureExc MinorShutdownCalled, COMPLETED_NO); - clientProfilePairs_.stream() + Vector pairs = clientProfilePairsRef.get(); + pairs.stream() .filter(cp::equals) .findFirst() - .ifPresent(clientProfilePairs_::remove); + .ifPresent(pairs::remove); // We only retry upon COMM_FAILURE, TRANSIENT, and NO_RESPONSE try { @@ -362,7 +370,7 @@ public synchronized void handleFailureException(ClientProfilePair cp, FailureExc } // If no client/profile pairs are left, we cannot retry either - if (clientProfilePairs_.isEmpty()) { + if (pairs.isEmpty()) { logger.log(FINE, ex.exception, () -> "no profiles left to try"); throw ex; } From fdf189d1d8a6b5a787071bb50c22a5d3a576a8b3 Mon Sep 17 00:00:00 2001 From: Neil Richards Date: Wed, 24 Jun 2026 05:19:51 +0100 Subject: [PATCH 04/11] refactor(yoko-core): remove assertion-only finalizers from GIOPServerStarter and GIOPServer Phase 3 of finalization refactoring: Remove assertion-only finalize() methods from GIOPServerStarter and GIOPServer classes. - Removed finalize() from GIOPServerStarter (checked serverState == CLOSED) - Removed finalize() from GIOPServer (checked destroy_ and starter_ == null) - Both finalizers only contained Assert.ensure() calls with no cleanup logic - Lines added: 1, lines removed: 18 Tests: All 91,844 yoko-core tests pass Co-authored-by-AI: IBM Bob 1.0.4 --- .../main/java/org/apache/yoko/orb/OB/GIOPServer.java | 11 ----------- .../org/apache/yoko/orb/OB/GIOPServerStarter.java | 8 +------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPServer.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPServer.java index 0508d720c..ce3c59be9 100644 --- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPServer.java +++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPServer.java @@ -42,17 +42,6 @@ final class GIOPServer extends Server { protected GIOPServerStarter starter_; // The server starter - // ---------------------------------------------------------------------- - // GIOPServer private and protected member implementations - // ---------------------------------------------------------------------- - - protected void finalize() throws Throwable { - ensure(destroy_); - ensure(starter_ == null); - - super.finalize(); - } - // ---------------------------------------------------------------------- // GIOPServer package member implementations // ---------------------------------------------------------------------- diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPServerStarter.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPServerStarter.java index b59120b59..083891149 100644 --- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPServerStarter.java +++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPServerStarter.java @@ -32,7 +32,7 @@ abstract class GIOPServerStarter { static final Logger logger = Logger.getLogger(GIOPServerStarter.class.getName()); - + protected final ORBInstance orbInstance_; // The ORBInstance protected final Acceptor acceptor_; // The acceptor @@ -58,12 +58,6 @@ public boolean cannotTransitionTo(ServerState next) { // GIOPServer private and protected member implementation // ---------------------------------------------------------------------- - protected void finalize() throws Throwable { - Assert.ensure(serverState == CLOSED); - - super.finalize(); - } - // // Emit a trace message when closing the acceptor // From a9efc8c9fc5f66dc7f575b268ad4084b18c77c60 Mon Sep 17 00:00:00 2001 From: Neil Richards Date: Wed, 24 Jun 2026 08:43:46 +0100 Subject: [PATCH 05/11] refactor(yoko-core): remove Request finalizer Phase 3 of finalization refactoring: Remove finalize() method from Request class. - Removed finalizer that called multi.removeDeferredRequest(this) - Finalizer was redundant: Request objects in deferredRequests_ list have strong references and cannot be GC'd until removed from list - Finalizer could only run after request already removed (redundant) or during ORB shutdown (edge case not worth finalizer overhead) - Removed TODO comment about RequestStateSent memory leak - Lines removed: 32 Tests: All yoko-core tests pass Co-authored-by-AI: IBM Bob 1.0.4 --- .../org/apache/yoko/orb/CORBA/Request.java | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/CORBA/Request.java b/yoko-core/src/main/java/org/apache/yoko/orb/CORBA/Request.java index 653ef198b..71c2d2524 100644 --- a/yoko-core/src/main/java/org/apache/yoko/orb/CORBA/Request.java +++ b/yoko-core/src/main/java/org/apache/yoko/orb/CORBA/Request.java @@ -736,38 +736,6 @@ public boolean poll_response() { raiseDIIExceptions_ = orb._OB_raiseDIIExceptions(); } - protected void finalize() throws Throwable { - if (state_ == RequestStateSent) { - // - // TODO: This is a memory leak, as a request has been - // sent, but the response has never been picked - // up. The correct thing would be to tell the Downcall - // object to cancel the request. But we don't have - // this ability yet. - // - } - - // - // Find out whether this was a deferred request for which - // get_response() hasn't been called yet. - // - ORBInstance orbInstance = delegate_ - ._OB_ORBInstance(); - MultiRequestSender multi = orbInstance - .getMultiRequestSender(); - if (multi != null) // It might be possible that the - // MultiRequestSender is already destroyed - { - // - // Remove this request from the list of the outstanding - // deferred requests - // - multi.removeDeferredRequest(this); - } - - super.finalize(); - } - public boolean _OB_completed() { synchronized (stateMutex_) { return state_ == RequestStateReceived; From 86071342cf75d5017d39f62456d5e2d49876fd6a Mon Sep 17 00:00:00 2001 From: Neil Richards Date: Wed, 24 Jun 2026 08:59:36 +0100 Subject: [PATCH 06/11] refactor(yoko-core): remove finalizers from Delegate and DirectServant Phase 3 of finalization refactoring: Remove finalize() methods from Delegate and DirectServant classes. - Removed Delegate.finalize() that called directServant.destroy() - directServant.destroy() is already called explicitly when needed - Finalizer was redundant safety net with no practical benefit - Removed DirectServant.finalize() assertion-only finalizer - Only contained ensure(deactivated_) assertion check - No actual cleanup logic - Lines added: 1, lines removed: 17 Tests: All yoko-core tests pass Co-authored-by-AI: IBM Bob 1.0.4 --- .../java/org/apache/yoko/orb/CORBA/Delegate.java | 9 +-------- .../yoko/orb/OBPortableServer/DirectServant.java | 13 ++----------- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/CORBA/Delegate.java b/yoko-core/src/main/java/org/apache/yoko/orb/CORBA/Delegate.java index 3f6fd9c44..dcdfaf7dd 100644 --- a/yoko-core/src/main/java/org/apache/yoko/orb/CORBA/Delegate.java +++ b/yoko-core/src/main/java/org/apache/yoko/orb/CORBA/Delegate.java @@ -168,14 +168,7 @@ private synchronized void checkRetry(int retry, SystemException ex) { logged(RETRY_LOG, ex, "Allow retry"); } - @SuppressWarnings("deprecation") - protected void finalize() throws Throwable { - // CollocatedServant must be explicitly destroyed in order to make it eligible for garbage collection - if (directServant != null) { - directServant.destroy(); - } - super.finalize(); - } + // ------------------------------------------------------------------ // Standard IDL to Java Mapping diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OBPortableServer/DirectServant.java b/yoko-core/src/main/java/org/apache/yoko/orb/OBPortableServer/DirectServant.java index 34f887955..cb0db598a 100644 --- a/yoko-core/src/main/java/org/apache/yoko/orb/OBPortableServer/DirectServant.java +++ b/yoko-core/src/main/java/org/apache/yoko/orb/OBPortableServer/DirectServant.java @@ -1,5 +1,5 @@ /* - * Copyright 2025 IBM Corporation and others. + * Copyright 2026 IBM Corporation and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,7 +39,7 @@ public class DirectServant extends ServantObject { // This flag is true if the servant has been deactivated // private boolean deactivated_; - + private Object original_servant; public DirectServant(POA_impl poa, @@ -55,15 +55,6 @@ public DirectServant(POA_impl poa, } } - protected void finalize() throws Throwable { - // - // This object *must* have been deactivated already - // - ensure(deactivated_); - - super.finalize(); - } - public void destroy() { // // An explicit destroy method is needed in Java to force the From f401bace2211742f9bfeb141e1a9fb35bf8bec7f Mon Sep 17 00:00:00 2001 From: Neil Richards Date: Wed, 24 Jun 2026 10:44:15 +0100 Subject: [PATCH 07/11] refactor(yoko-core): remove finalizer from ActiveObjectOnlyStrategy Phase 3 of finalization refactoring: Remove finalize() method from DirectSeqEntry inner class in ActiveObjectOnlyStrategy. - Removed finalizer that called deactivate() - deactivate() is already called explicitly in completeDeactivate() - Finalizer was redundant safety net with no practical benefit - Lines removed: 5 Tests: All yoko-core tests pass Co-authored-by-AI: IBM Bob 1.0.4 --- .../orb/OBPortableServer/ActiveObjectOnlyStrategy.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OBPortableServer/ActiveObjectOnlyStrategy.java b/yoko-core/src/main/java/org/apache/yoko/orb/OBPortableServer/ActiveObjectOnlyStrategy.java index 5feeee643..36407e572 100644 --- a/yoko-core/src/main/java/org/apache/yoko/orb/OBPortableServer/ActiveObjectOnlyStrategy.java +++ b/yoko-core/src/main/java/org/apache/yoko/orb/OBPortableServer/ActiveObjectOnlyStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2025 IBM Corporation and others. + * Copyright 2026 IBM Corporation and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -67,11 +67,6 @@ private void traceoid() { oid_ = oid; } - protected void finalize() throws Throwable { - deactivate(); - super.finalize(); - } - void deactivate() { // traceoid(); // System.out.println("deactivate: "); From 5282cfc10dd6ae6dabbae170876c56af23622dc4 Mon Sep 17 00:00:00 2001 From: Neil Richards Date: Wed, 24 Jun 2026 10:55:02 +0100 Subject: [PATCH 08/11] refactor(yoko-core): remove finalizer from Transport_impl Phase 3 of finalization refactoring: Remove finalize() method from Transport_impl class. - Removed finalizer that called close() if socket_ was not null - close() is already called explicitly when transport is destroyed - Finalizer was redundant safety net with no practical benefit - Lines removed: 7 Tests: All yoko-core tests pass Co-authored-by-AI: IBM Bob 1.0.4 --- .../java/org/apache/yoko/orb/OCI/IIOP/Transport_impl.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OCI/IIOP/Transport_impl.java b/yoko-core/src/main/java/org/apache/yoko/orb/OCI/IIOP/Transport_impl.java index e0d44c557..06beaf217 100644 --- a/yoko-core/src/main/java/org/apache/yoko/orb/OCI/IIOP/Transport_impl.java +++ b/yoko-core/src/main/java/org/apache/yoko/orb/OCI/IIOP/Transport_impl.java @@ -319,13 +319,6 @@ public Transport_impl(Acceptor acceptor, Socket socket, ListenerMap lm) { info_ = new TransportInfo_impl(this, acceptor, lm); } - public void finalize() throws Throwable { - if (socket_ != null) - close(); - - super.finalize(); - } - public String toString() { return String.format("Transport to %s with socket %s", info_, socket_); } From 4b938880cbb87b3deee757970cd1c415fc9d15db Mon Sep 17 00:00:00 2001 From: Neil Richards Date: Wed, 24 Jun 2026 11:01:56 +0100 Subject: [PATCH 09/11] refactor(yoko-core): remove finalizer from Connector_impl Phase 3 of finalization refactoring: Remove finalize() method from Connector_impl class. - Removed finalizer that called close() if socket_ was not null - close() is already called explicitly when connector is destroyed - Finalizer was redundant safety net with no practical benefit - Lines removed: 7 Tests: All yoko-core tests pass Co-authored-by-AI: IBM Bob 1.0.4 --- .../java/org/apache/yoko/orb/OCI/IIOP/Connector_impl.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OCI/IIOP/Connector_impl.java b/yoko-core/src/main/java/org/apache/yoko/orb/OCI/IIOP/Connector_impl.java index 4e2441444..bf4acb2a7 100644 --- a/yoko-core/src/main/java/org/apache/yoko/orb/OCI/IIOP/Connector_impl.java +++ b/yoko-core/src/main/java/org/apache/yoko/orb/OCI/IIOP/Connector_impl.java @@ -363,13 +363,6 @@ public org.apache.yoko.orb.OCI.ConnectorInfo get_info() { transportInfo = extractTransportInfo(ior); } - protected void finalize() throws Throwable { - if (socket_ != null) - close(); - - super.finalize(); - } - @Override public String toString() { return "-> " + info_; From 6e703b45f070a37cf1e60016d86e5683a26639a1 Mon Sep 17 00:00:00 2001 From: Neil Richards Date: Wed, 24 Jun 2026 11:05:22 +0100 Subject: [PATCH 10/11] refactor(yoko-core): remove finalizer from ORB_impl Phase 3 of finalization refactoring: Remove finalize() method from ORB_impl class. - Removed finalizer that logged warning if destroy() wasn't called - Finalizer was diagnostic-only with no actual cleanup logic - Warning was unreliable due to unpredictable finalization timing - Lines removed: 7 Tests: All yoko-core tests pass Co-authored-by-AI: IBM Bob 1.0.4 --- .../main/java/org/apache/yoko/orb/OBCORBA/ORB_impl.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OBCORBA/ORB_impl.java b/yoko-core/src/main/java/org/apache/yoko/orb/OBCORBA/ORB_impl.java index 3de1082d5..0cbf2c71d 100644 --- a/yoko-core/src/main/java/org/apache/yoko/orb/OBCORBA/ORB_impl.java +++ b/yoko-core/src/main/java/org/apache/yoko/orb/OBCORBA/ORB_impl.java @@ -493,13 +493,6 @@ private void initialize(StringSeqHolder args, String orbId, } } - protected void finalize() throws Throwable { - if (orbInstance_ != null) { - SHUTDOWN_LOG.fine(() -> "ORB.destroy() was not called. This may result in resource leaks."); - } - super.finalize(); - } - private void initializeDefaultPolicies() { Properties properties = orbInstance_.getProperties(); From 9666eccabb548c87d8ea9d840e2fd32b57ef2722 Mon Sep 17 00:00:00 2001 From: Neil Richards Date: Wed, 24 Jun 2026 11:16:36 +0100 Subject: [PATCH 11/11] refactor(yoko-core): remove finalizer from Acceptor_impl Phase 3 of finalization refactoring: Remove finalize() method from Acceptor_impl class. - Removed finalizer that called close() and removed from listenMap_ - close() is already called explicitly in GIOPServerStarter classes - listenMap_ cleanup was redundant as acceptors are explicitly managed - Removed TODO comment about using phantom refs in AccFactory_impl - Lines removed: 12 Tests: All yoko-core tests pass Co-authored-by-AI: IBM Bob 1.0.4 --- .../org/apache/yoko/orb/OCI/IIOP/Acceptor_impl.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OCI/IIOP/Acceptor_impl.java b/yoko-core/src/main/java/org/apache/yoko/orb/OCI/IIOP/Acceptor_impl.java index 6d7b916a6..27eb9a5d1 100644 --- a/yoko-core/src/main/java/org/apache/yoko/orb/OCI/IIOP/Acceptor_impl.java +++ b/yoko-core/src/main/java/org/apache/yoko/orb/OCI/IIOP/Acceptor_impl.java @@ -311,18 +311,6 @@ public Acceptor_impl(String address, String[] hosts, ProfileCardinality profileC } } - // TODO: get rid of this finalizer, and use phantom refs in AccFactory_impl instead to track Acceptors going away. - protected void finalize() throws Throwable { - if (socket_ != null) { - close(); - } - - // remove this acceptor from the listenMap_ - synchronized (listenMap_) { - for (String s : hosts_) listenMap_.remove(s, (short) port_); - } - } - public String toString() { return "Acceptor listening on " + socket_; }