From cabbafb4f1671e4fdf9cbabaa1362479c98610db Mon Sep 17 00:00:00 2001 From: Lars Vogel Date: Thu, 15 Oct 2026 07:40:21 +0200 Subject: [PATCH] Drop unregistered terminal service in TerminalServiceManager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit removedService was empty, so after the terminal bundle was stopped or updated getCurrentService() kept returning the unregistered service from a separate cache and its reference was never released. getCurrentService() now reads the service from the ServiceTracker, which drops removed services itself, and removedService ungets the reference. Assisted-by: multiple AI agents and layers of automated tooling 🤖 --- .../terminal/api/TerminalServiceManager.java | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/com.microsoft.copilot.eclipse.terminal.api/src/com/microsoft/copilot/eclipse/terminal/api/TerminalServiceManager.java b/com.microsoft.copilot.eclipse.terminal.api/src/com/microsoft/copilot/eclipse/terminal/api/TerminalServiceManager.java index 8d1c39fc4..0997f1664 100644 --- a/com.microsoft.copilot.eclipse.terminal.api/src/com/microsoft/copilot/eclipse/terminal/api/TerminalServiceManager.java +++ b/com.microsoft.copilot.eclipse.terminal.api/src/com/microsoft/copilot/eclipse/terminal/api/TerminalServiceManager.java @@ -74,10 +74,9 @@ private static TerminalServiceManager createInstance() { } private final BundleContext bundleContext; - private ServiceTracker serviceTracker; + private volatile ServiceTracker serviceTracker; private final AtomicBoolean installationAttempted = new AtomicBoolean(false); private final CopyOnWriteArrayList listeners = new CopyOnWriteArrayList<>(); - private volatile IRunInTerminalTool currentService = null; private volatile List cachedMissingDependencies = null; private final boolean isModernTerminal; @@ -121,8 +120,9 @@ public void addListener(TerminalServiceListener listener) { listeners.add(listener); // If service is already available, notify immediately - if (currentService != null) { - listener.onServiceAvailable(currentService); + IRunInTerminalTool service = getCurrentService(); + if (service != null) { + listener.onServiceAvailable(service); } // Notify with cached missing dependencies if available @@ -144,7 +144,8 @@ public void removeListener(TerminalServiceListener listener) { * Get the current terminal service if available. */ public IRunInTerminalTool getCurrentService() { - return currentService; + ServiceTracker tracker = serviceTracker; + return tracker != null ? tracker.getService() : null; } /** @@ -156,7 +157,6 @@ private void start() { public IRunInTerminalTool addingService(ServiceReference reference) { IRunInTerminalTool service = bundleContext.getService(reference); if (service != null) { - currentService = service; notifyServiceAvailable(service); } return service; @@ -165,23 +165,19 @@ public IRunInTerminalTool addingService(ServiceReference ref @Override public void modifiedService(ServiceReference reference, IRunInTerminalTool service) { if (service != null) { - currentService = service; notifyServiceAvailable(service); } } @Override public void removedService(ServiceReference reference, IRunInTerminalTool service) { + bundleContext.ungetService(reference); } }; serviceTracker = new ServiceTracker<>(bundleContext, IRunInTerminalTool.class, customizer); serviceTracker.open(); - IRunInTerminalTool[] services = serviceTracker.getServices(new IRunInTerminalTool[0]); - if (services != null && services.length > 0) { - currentService = services[0]; - } // Check if bundle needs to be installed or updated attemptTerminalBundleInstallation(); } @@ -190,11 +186,11 @@ public void removedService(ServiceReference reference, IRunI * Stop the service manager. */ public void stop() { - if (serviceTracker != null) { - serviceTracker.close(); + ServiceTracker tracker = serviceTracker; + if (tracker != null) { serviceTracker = null; + tracker.close(); } - currentService = null; listeners.clear(); }