Fix EOSClusterSingletonServiceProvider shutdown 79/109879/1
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 20 Jan 2024 09:43:55 +0000 (10:43 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Sat, 20 Jan 2024 09:50:09 +0000 (10:50 +0100)
If we close the listener registration we will not be getting any events.
Prevent that by staggered shutdown, where we first mark the instance as
shutting down and only after it has shut down we release the listeners.

JIRA: MDSAL-853
Change-Id: Ibea92376ca83b2cd9f87de5302047503f609ebca
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
singleton-service/mdsal-singleton-dom-impl/src/main/java/org/opendaylight/mdsal/singleton/dom/impl/EOSClusterSingletonServiceProvider.java

index 0e4464cea4e894275632e7179a1b47075283e324..8332a926057ee5f4ed0c6aad91ae19b2503ce6d5 100644 (file)
@@ -23,6 +23,7 @@ import java.util.concurrent.ExecutionException;
 import javax.annotation.PreDestroy;
 import javax.inject.Inject;
 import javax.inject.Singleton;
+import org.checkerframework.checker.lock.qual.Holding;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.eos.common.api.CandidateAlreadyRegisteredException;
 import org.opendaylight.mdsal.eos.common.api.EntityOwnershipStateChange;
@@ -74,34 +75,42 @@ public final class EOSClusterSingletonServiceProvider
     @PreDestroy
     @Deactivate
     @Override
-    public synchronized void close() throws ExecutionException, InterruptedException {
-        if (serviceEntityListenerReg == null) {
-            // Idempotent
-            return;
-        }
+    public void close() throws ExecutionException, InterruptedException {
+        final Registration reg;
+        final ListenableFuture<?> future;
+        synchronized (this) {
+            if (serviceEntityListenerReg == null) {
+                // Idempotent
+                return;
+            }
 
-        LOG.info("Cluster Singleton Service stopping");
-        serviceEntityListenerReg.close();
-        serviceEntityListenerReg = null;
+            LOG.info("Cluster Singleton Service stopping");
+            reg = serviceEntityListenerReg;
+            serviceEntityListenerReg = null;
+            future = Futures.allAsList(serviceGroupMap.values().stream()
+                .map(ServiceGroup::closeClusterSingletonGroup)
+                .toList());
+        }
 
-        final var future = Futures.allAsList(serviceGroupMap.values().stream()
-            .map(ServiceGroup::closeClusterSingletonGroup)
-            .toList());
         try {
             LOG.debug("Waiting for service groups to stop");
             future.get();
         } finally {
+            reg.close();
             asyncCloseEntityListenerReg.close();
             asyncCloseEntityListenerReg = null;
             serviceGroupMap.clear();
+            LOG.info("Cluster Singleton Service stopped");
         }
-
-        LOG.info("Cluster Singleton Service stopped");
     }
 
     @Override
     public synchronized Registration registerClusterSingletonService(final ClusterSingletonService service) {
         final var serviceIdentifier = requireNonNull(service.getIdentifier());
+        if (serviceEntityListenerReg == null) {
+            throw new IllegalStateException(this + "is closed");
+        }
+
         LOG.debug("Call registrationService {} method for ClusterSingletonService Provider {}", service, this);
 
         final var identifierValue = serviceIdentifier.value();
@@ -140,6 +149,7 @@ public final class EOSClusterSingletonServiceProvider
             services);
     }
 
+    @Holding("this")
     private void initializeOrRemoveGroup(final ServiceGroup group) throws CandidateAlreadyRegisteredException {
         try {
             group.initialize();