Fixed concurrency problems 65/2665/3
authorPatrikBuhr <patrik.buhr@est.tech>
Wed, 4 Mar 2020 08:01:51 +0000 (09:01 +0100)
committerPatrikBuhr <patrik.buhr@est.tech>
Wed, 4 Mar 2020 14:09:13 +0000 (15:09 +0100)
The resource locking is enabled and reactive.

Change-Id: Ibf83dddaa41cde9224ea52dbfda9dcbc9e4361a6
Issue-ID: NONRTRIC-107
Signed-off-by: PatrikBuhr <patrik.buhr@est.tech>
policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyController.java
policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapRequestMessage.java
policy-agent/src/main/java/org/oransc/policyagent/repository/Lock.java
policy-agent/src/main/java/org/oransc/policyagent/repository/Ric.java
policy-agent/src/main/java/org/oransc/policyagent/tasks/RepositorySupervision.java
policy-agent/src/main/java/org/oransc/policyagent/tasks/RicSynchronizationTask.java
policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java
policy-agent/src/test/java/org/oransc/policyagent/repository/LockTest.java [new file with mode: 0644]
policy-agent/src/test/java/org/oransc/policyagent/tasks/RepositorySupervisionTest.java

index d6d65fc..21a0eee 100644 (file)
@@ -36,6 +36,7 @@ import org.oransc.policyagent.clients.A1ClientFactory;
 import org.oransc.policyagent.configuration.ApplicationConfig;
 import org.oransc.policyagent.exceptions.ServiceException;
 import org.oransc.policyagent.repository.ImmutablePolicy;
+import org.oransc.policyagent.repository.Lock.LockType;
 import org.oransc.policyagent.repository.Policies;
 import org.oransc.policyagent.repository.Policy;
 import org.oransc.policyagent.repository.PolicyType;
@@ -157,12 +158,12 @@ public class PolicyController {
         Policy policy = policies.get(id);
         if (policy != null && policy.ric().getState() == Ric.RicState.IDLE) {
             Ric ric = policy.ric();
-            return a1ClientFactory.createA1Client(policy.ric()) //
-                .doOnNext(notUsed -> ric.getLock().lockBlocking()) //
+            return ric.getLock().lock(LockType.SHARED) // //
+                .flatMap(lock -> a1ClientFactory.createA1Client(policy.ric())) //
                 .doOnNext(notUsed -> policies.remove(policy)) //
                 .flatMap(client -> client.deletePolicy(policy)) //
-                .doOnNext(notUsed -> ric.getLock().unlock()) //
-                .doOnError(notUsed -> ric.getLock().unlock()) //
+                .doOnNext(notUsed -> ric.getLock().unlockBlocking()) //
+                .doOnError(notUsed -> ric.getLock().unlockBlocking()) //
                 .flatMap(notUsed -> Mono.just(new ResponseEntity<>(HttpStatus.NO_CONTENT)));
         } else if (policy != null) {
             return Mono.just(new ResponseEntity<>("Busy, recovering", HttpStatus.LOCKED));
@@ -197,14 +198,13 @@ public class PolicyController {
 
             final boolean isCreate = this.policies.get(policy.id()) == null;
 
-            return Mono.just(policy) //
-                .doOnNext(notUsed -> ric.getLock().lockBlocking()) //
+            return ric.getLock().lock(LockType.SHARED) //
                 .flatMap(p -> validateModifiedPolicy(policy)) //
                 .flatMap(notUsed -> a1ClientFactory.createA1Client(ric)) //
                 .flatMap(client -> client.putPolicy(policy)) //
                 .doOnNext(notUsed -> policies.put(policy)) //
-                .doOnNext(notUsed -> ric.getLock().unlock()) //
-                .doOnError(t -> ric.getLock().unlock()) //
+                .doOnNext(notUsed -> ric.getLock().unlockBlocking()) //
+                .doOnError(t -> ric.getLock().unlockBlocking()) //
                 .flatMap(notUsed -> Mono.just(new ResponseEntity<>(isCreate ? HttpStatus.CREATED : HttpStatus.OK))) //
                 .onErrorResume(t -> Mono.just(new ResponseEntity<>(t.getMessage(), HttpStatus.METHOD_NOT_ALLOWED)));
         }
index 7e61b39..354fdaf 100644 (file)
 
 package org.oransc.policyagent.dmaap;
 
-import java.util.Optional;
-
 import com.google.gson.JsonObject;
 
+import java.util.Optional;
+
 import org.immutables.gson.Gson;
 import org.immutables.value.Value;
 
index 68ea5a7..bc5d77a 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.oransc.policyagent.repository;
 
+import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -31,31 +32,71 @@ import reactor.core.publisher.Mono;
 import reactor.core.publisher.MonoSink;
 
 /**
- * A resource lock. The caller thread will be blocked until the lock is granted.
- * Exclusive means that the caller takes exclusive ownership of the resurce. Non
- * exclusive lock means that several users can lock the resource (for shared
- * usage).
+ * A resource lock. Exclusive means that the caller takes exclusive ownership of
+ * the resurce. Non exclusive lock means that several users can lock the
+ * resource (for shared usage).
  */
 public class Lock {
     private static final Logger logger = LoggerFactory.getLogger(Lock.class);
 
     private boolean isExclusive = false;
-    private int cnt = 0;
+    private int lockCounter = 0;
+    private final List<LockRequest> lockRequestQueue = new LinkedList<>();
+
+    private static class AsynchCallbackExecutor implements Runnable {
+        private List<LockRequest> lockRequestQueue = new LinkedList<>();
+
+        public AsynchCallbackExecutor() {
+            Thread thread = new Thread(this);
+            thread.start();
+        }
+
+        public synchronized void addAll(List<LockRequest> requests) {
+            this.lockRequestQueue.addAll(requests);
+            this.notifyAll();
+        }
+
+        @Override
+        public void run() {
+            while (true) {
+                for (LockRequest request : consume()) {
+                    request.callback.success(request.lock);
+                }
+                waitForNewEntries();
+            }
+        }
+
+        private synchronized List<LockRequest> consume() {
+            List<LockRequest> q = this.lockRequestQueue;
+            this.lockRequestQueue = new LinkedList<>();
+            return q;
+        }
+
+        private synchronized void waitForNewEntries() {
+            try {
+                if (this.lockRequestQueue.isEmpty()) {
+                    this.wait();
+                }
+            } catch (InterruptedException e) {
+                logger.warn("waitForUnlock interrupted", e);
+            }
+        }
+    }
+
+    private static AsynchCallbackExecutor callbackProcessor = new AsynchCallbackExecutor();
 
     public static enum LockType {
         EXCLUSIVE, SHARED
     }
 
+    /** The caller thread will be blocked util the lock is granted. */
     public synchronized void lockBlocking(LockType locktype) {
         while (!tryLock(locktype)) {
             this.waitForUnlock();
         }
     }
 
-    public synchronized void lockBlocking() {
-        lockBlocking(LockType.SHARED);
-    }
-
+    /** Reactive version. The Lock will be emitted when the lock is granted */
     public synchronized Mono<Lock> lock(LockType lockType) {
         if (tryLock(lockType)) {
             return Mono.just(this);
@@ -64,46 +105,70 @@ public class Lock {
         }
     }
 
-    public synchronized void unlock() {
-        if (disable()) {
-            return;
-        }
-        if (cnt <= 0) {
-            cnt = -1; // Might as well stop, to make it easier to find the problem
-            throw new RuntimeException("Number of unlocks must match the number of locks");
-        }
-        this.cnt--;
-        if (cnt == 0) {
-            isExclusive = false;
+    public Mono<Lock> unlock() {
+        return Mono.create(monoSink -> {
+            unlockBlocking();
+            monoSink.success(this);
+        });
+    }
+
+    public void unlockBlocking() {
+        synchronized (this) {
+            if (lockCounter <= 0) {
+                lockCounter = -1; // Might as well stop, to make it easier to find the problem
+                throw new RuntimeException("Number of unlocks must match the number of locks");
+            }
+            this.lockCounter--;
+            if (lockCounter == 0) {
+                isExclusive = false;
+            }
+            this.notifyAll();
         }
         this.processQueuedEntries();
-        this.notifyAll();
+    }
+
+    @Override
+    public String toString() {
+        return "Lock cnt: " + this.lockCounter + " exclusive: " + this.isExclusive;
+    }
+
+    /** returns the current number of granted locks */
+    public synchronized int getLockCounter() {
+        return this.lockCounter;
     }
 
     private void processQueuedEntries() {
-        for (Iterator<QueueEntry> i = queue.iterator(); i.hasNext();) {
-            QueueEntry e = i.next();
-            if (tryLock(e.lockType)) {
-                i.remove();
-                e.callback.success(this);
+        List<LockRequest> granted = new ArrayList<>();
+        synchronized (this) {
+            for (Iterator<LockRequest> i = lockRequestQueue.iterator(); i.hasNext();) {
+                LockRequest request = i.next();
+                if (tryLock(request.lockType)) {
+                    i.remove();
+                    granted.add(request);
+                }
             }
         }
+
+        /*
+         * for (LockRequest request : granted) { request.callback.success(this); }
+         */
+        callbackProcessor.addAll(granted);
     }
 
-    static class QueueEntry {
+    private static class LockRequest {
         final MonoSink<Lock> callback;
         final LockType lockType;
+        final Lock lock;
 
-        QueueEntry(MonoSink<Lock> callback, LockType lockType) {
+        LockRequest(MonoSink<Lock> callback, LockType lockType, Lock lock) {
             this.callback = callback;
             this.lockType = lockType;
+            this.lock = lock;
         }
     }
 
-    private final List<QueueEntry> queue = new LinkedList<>();
-
     private synchronized void addToQueue(MonoSink<Lock> callback, LockType lockType) {
-        queue.add(new QueueEntry(callback, lockType));
+        lockRequestQueue.add(new LockRequest(callback, lockType, this));
     }
 
     private void waitForUnlock() {
@@ -114,27 +179,16 @@ public class Lock {
         }
     }
 
-    private boolean disable() {
-        return true;
-    }
-
     private boolean tryLock(LockType lockType) {
-        if (disable()) {
-            return true;
-        }
         if (this.isExclusive) {
             return false;
         }
-        if (lockType == LockType.EXCLUSIVE && cnt > 0) {
+        if (lockType == LockType.EXCLUSIVE && lockCounter > 0) {
             return false;
         }
-        cnt++;
+        lockCounter++;
         this.isExclusive = lockType == LockType.EXCLUSIVE;
         return true;
     }
 
-    public synchronized int getLockCounter() {
-        return this.cnt;
-    }
-
 }
index 4291d6e..ab32065 100644 (file)
@@ -51,7 +51,7 @@ public class Ric {
     private final Lock lock = new Lock();
 
     /**
-     * Creates the Ric. Initial state is {@link RicState.NOT_INITIATED}.
+     * Creates the Ric. Initial state is {@link RicState.UNDEFINED}.
      *
      * @param ricConfig The {@link RicConfig} for this Ric.
      */
index f75db21..8dcb811 100644 (file)
@@ -79,14 +79,20 @@ public class RepositorySupervision {
         synchronized (this.rics) {
             return Flux.fromIterable(rics.getRics()) //
                 .flatMap(this::createRicData) //
-                .flatMap(this::checkRicState) //
-                .doOnNext(ricData -> ricData.ric.getLock().lockBlocking(LockType.EXCLUSIVE)) //
-                .flatMap(this::checkRicPolicies) //
-                .doOnNext(ricData -> ricData.ric.getLock().unlock()) //
-                .flatMap(this::checkRicPolicyTypes); //
+                .flatMap(this::checkOneRic) //
+                .onErrorResume(throwable -> Mono.empty());
         }
     }
 
+    private Mono<RicData> checkOneRic(RicData ricData) {
+        return checkRicState(ricData) //
+            .flatMap(x -> ricData.ric.getLock().lock(LockType.EXCLUSIVE)) //
+            .flatMap(x -> checkRicPolicies(ricData)) //
+            .flatMap(x -> ricData.ric.getLock().unlock()) //
+            .doOnError(throwable -> ricData.ric.getLock().unlockBlocking()) //
+            .flatMap(x -> checkRicPolicyTypes(ricData)); //
+    }
+
     private static class RicData {
         RicData(Ric ric, A1Client a1Client) {
             this.ric = ric;
@@ -105,7 +111,8 @@ public class RepositorySupervision {
 
     private Mono<RicData> checkRicState(RicData ric) {
         if (ric.ric.getState() == RicState.UNDEFINED) {
-            return startSynchronization(ric);
+            return startSynchronization(ric) //
+                .onErrorResume(t -> Mono.empty());
         } else if (ric.ric.getState() == RicState.SYNCHRONIZING) {
             return Mono.empty();
         } else {
@@ -115,23 +122,17 @@ public class RepositorySupervision {
 
     private Mono<RicData> checkRicPolicies(RicData ric) {
         return ric.a1Client.getPolicyIdentities() //
-            .onErrorResume(t -> {
-                ric.ric.getLock().unlock();
-                return Mono.empty();
-            }) //
             .flatMap(ricP -> validateInstances(ricP, ric));
     }
 
     private Mono<RicData> validateInstances(Collection<String> ricPolicies, RicData ric) {
         synchronized (this.policies) {
             if (ricPolicies.size() != policies.getForRic(ric.ric.name()).size()) {
-                ric.ric.getLock().unlock();
                 return startSynchronization(ric);
             }
 
             for (String policyId : ricPolicies) {
                 if (!policies.containsPolicy(policyId)) {
-                    ric.ric.getLock().unlock();
                     return startSynchronization(ric);
                 }
             }
@@ -141,7 +142,6 @@ public class RepositorySupervision {
 
     private Mono<RicData> checkRicPolicyTypes(RicData ric) {
         return ric.a1Client.getPolicyTypeIdentities() //
-            .onErrorResume(notUsed -> Mono.empty()) //
             .flatMap(ricTypes -> validateTypes(ricTypes, ric));
     }
 
@@ -160,7 +160,7 @@ public class RepositorySupervision {
     private Mono<RicData> startSynchronization(RicData ric) {
         RicSynchronizationTask recovery = createSynchronizationTask();
         recovery.run(ric.ric);
-        return Mono.empty();
+        return Mono.error(new Exception("Syncronization started"));
     }
 
     @SuppressWarnings("squid:S2629")
index d759991..9d25fa3 100644 (file)
@@ -82,9 +82,10 @@ public class RicSynchronizationTask {
             }
             ric.setState(RicState.SYNCHRONIZING);
         }
-        ric.getLock().lockBlocking(LockType.EXCLUSIVE); // Make sure no NBI updates are running
-        ric.getLock().unlock();
-        this.a1ClientFactory.createA1Client(ric)//
+
+        ric.getLock().lock(LockType.EXCLUSIVE) // Make sure no NBI updates are running
+            .flatMap(lock -> lock.unlock()) //
+            .flatMap(lock -> this.a1ClientFactory.createA1Client(ric)) //
             .flatMapMany(client -> startSynchronization(ric, client)) //
             .subscribe(x -> logger.debug("Synchronize: {}", x), //
                 throwable -> onSynchronizationError(ric, throwable), //
index 4ea6ff9..917aca0 100644 (file)
@@ -106,8 +106,8 @@ public class ApplicationTest {
     Services services;
 
     private static Gson gson = new GsonBuilder() //
-            .serializeNulls() //
-            .create(); //
+        .serializeNulls() //
+        .create(); //
 
     public static class MockApplicationConfig extends ApplicationConfig {
         @Override
@@ -149,7 +149,7 @@ public class ApplicationTest {
         @Override
         public boolean hasError(ClientHttpResponse httpResponse) throws IOException {
             return (httpResponse.getStatusCode().series() == Series.CLIENT_ERROR
-                    || httpResponse.getStatusCode().series() == Series.SERVER_ERROR);
+                || httpResponse.getStatusCode().series() == Series.SERVER_ERROR);
         }
 
         @Override
@@ -174,7 +174,7 @@ public class ApplicationTest {
     public void verifyNoRicLocks() {
         for (Ric ric : this.rics.getRics()) {
             ric.getLock().lockBlocking(LockType.EXCLUSIVE);
-            ric.getLock().unlock();
+            ric.getLock().unlockBlocking();
             assertThat(ric.getLock().getLockCounter()).isEqualTo(0);
             assertThat(ric.getState()).isEqualTo(Ric.RicState.IDLE);
         }
@@ -216,13 +216,6 @@ public class ApplicationTest {
 
     @Test
     public void testGetRicForManagedElement_thenReturnCorrectRic() throws Exception {
-        addRic("notCorrectRic1");
-        addRic("notCorrectRic2");
-        addRic("notCorrectRic3");
-        addRic("notCorrectRic4");
-        addRic("notCorrectRic5");
-        addRic("notCorrectRic6");
-
         String ricName = "ric1";
         Ric ric = addRic(ricName);
         String managedElementId = "kista_1";
@@ -253,7 +246,7 @@ public class ApplicationTest {
         addPolicyType(policyTypeName, ricName);
 
         String url = baseUrl() + "/policy?type=" + policyTypeName + "&instance=" + policyInstanceId + "&ric=" + ricName
-                + "&service=" + serviceName;
+            + "&service=" + serviceName;
         final String json = jsonString();
         this.rics.getRic(ricName).setState(Ric.RicState.IDLE);
 
@@ -450,11 +443,11 @@ public class ApplicationTest {
     private Policy addPolicy(String id, String typeName, String service, String ric) throws ServiceException {
         addRic(ric);
         Policy p = ImmutablePolicy.builder().id(id) //
-                .json(jsonString()) //
-                .ownerServiceName(service) //
-                .ric(rics.getRic(ric)) //
-                .type(addPolicyType(typeName, ric)) //
-                .lastModified("lastModified").build();
+            .json(jsonString()) //
+            .ownerServiceName(service) //
+            .ric(rics.getRic(ric)) //
+            .type(addPolicyType(typeName, ric)) //
+            .lastModified("lastModified").build();
         policies.put(p);
         return p;
     }
@@ -544,9 +537,9 @@ public class ApplicationTest {
 
     private PolicyType addPolicyType(String policyTypeName, String ricName) {
         PolicyType type = ImmutablePolicyType.builder() //
-                .name(policyTypeName) //
-                .schema("{\"title\":\"" + policyTypeName + "\"}") //
-                .build();
+            .name(policyTypeName) //
+            .schema("{\"title\":\"" + policyTypeName + "\"}") //
+            .build();
 
         policyTypes.put(type);
         addRic(ricName).addSupportedPolicyType(type);
@@ -559,10 +552,10 @@ public class ApplicationTest {
         }
         Vector<String> mes = new Vector<>();
         RicConfig conf = ImmutableRicConfig.builder() //
-                .name(ricName) //
-                .baseUrl(ricName) //
-                .managedElementIds(mes) //
-                .build();
+            .name(ricName) //
+            .baseUrl(ricName) //
+            .managedElementIds(mes) //
+            .build();
         Ric ric = new Ric(conf);
         ric.setState(Ric.RicState.IDLE);
         this.rics.put(ric);
diff --git a/policy-agent/src/test/java/org/oransc/policyagent/repository/LockTest.java b/policy-agent/src/test/java/org/oransc/policyagent/repository/LockTest.java
new file mode 100644 (file)
index 0000000..825010a
--- /dev/null
@@ -0,0 +1,89 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 Nordix Foundation
+ * %%
+ * 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.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.policyagent.repository;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.IOException;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.oransc.policyagent.exceptions.ServiceException;
+import org.oransc.policyagent.repository.Lock.LockType;
+
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+@ExtendWith(MockitoExtension.class)
+public class LockTest {
+
+    private void sleep() {
+        try {
+            Thread.sleep(100);
+        } catch (InterruptedException e) {
+        }
+    }
+
+    private void asynchUnlock(Lock lock) {
+        Thread t = new Thread(() -> {
+            sleep();
+            lock.unlockBlocking();
+        });
+        t.start();
+    }
+
+    @Test
+    public void testLock() throws IOException, ServiceException {
+        Lock lock = new Lock();
+        lock.lockBlocking(LockType.SHARED);
+        lock.unlockBlocking();
+
+        lock.lockBlocking(LockType.EXCLUSIVE);
+        asynchUnlock(lock);
+
+        lock.lockBlocking(LockType.SHARED);
+        lock.unlockBlocking();
+
+        assertThat(lock.getLockCounter()).isEqualTo(0);
+    }
+
+    @Test
+    public void testReactiveLock() {
+        Lock lock = new Lock();
+
+        Mono<Lock> seq = lock.lock(LockType.EXCLUSIVE) //
+            .doOnNext(l -> System.out.println("1 " + l)) //
+            .flatMap(l -> lock.lock(LockType.EXCLUSIVE)) //
+            .flatMap(l -> lock.unlock()) //
+            .doOnNext(l -> System.out.println("2 " + l)); //
+
+        asynchUnlock(lock);
+        StepVerifier.create(seq) //
+            .expectSubscription() //
+            .expectNext(lock) //
+            .verifyComplete();
+
+        assertThat(lock.getLockCounter()).isEqualTo(0);
+
+    }
+
+}
index ea09bb6..d837f78 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.oransc.policyagent.tasks;
 
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
@@ -33,6 +34,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Vector;
 
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -43,6 +45,7 @@ import org.oransc.policyagent.clients.A1ClientFactory;
 import org.oransc.policyagent.configuration.ImmutableRicConfig;
 import org.oransc.policyagent.repository.ImmutablePolicy;
 import org.oransc.policyagent.repository.ImmutablePolicyType;
+import org.oransc.policyagent.repository.Lock.LockType;
 import org.oransc.policyagent.repository.Policies;
 import org.oransc.policyagent.repository.Policy;
 import org.oransc.policyagent.repository.PolicyType;
@@ -61,7 +64,7 @@ public class RepositorySupervisionTest {
         .build();
 
     private static final Ric RIC_1 = new Ric(ImmutableRicConfig.builder() //
-        .name("ric1") //
+        .name("RIC_1") //
         .baseUrl("baseUrl1") //
         .managedElementIds(new Vector<String>(Arrays.asList("kista_1", "kista_2"))) //
         .build());
@@ -94,20 +97,29 @@ public class RepositorySupervisionTest {
     @Mock
     private RicSynchronizationTask recoveryTaskMock;
 
-    private PolicyTypes types;
-    private Policies policies;
-    private Rics rics;
+    private final PolicyTypes types = new PolicyTypes();
+    private Policies policies = new Policies();
+    private Rics rics = new Rics();
 
     @BeforeEach
     public void init() {
         doReturn(Mono.just(a1ClientMock)).when(a1ClientFactory).createA1Client(any(Ric.class));
-        types = new PolicyTypes();
-        policies = new Policies();
-        rics = new Rics();
+        types.clear();
+        policies.clear();
+        rics.clear();
         RIC_1.setState(RicState.UNDEFINED);
         RIC_1.clearSupportedPolicyTypes();
     }
 
+    @AfterEach
+    public void verifyNoRicLocks() {
+        for (Ric ric : this.rics.getRics()) {
+            ric.getLock().lockBlocking(LockType.EXCLUSIVE);
+            ric.getLock().unlockBlocking();
+            assertThat(ric.getLock().getLockCounter()).isEqualTo(0);
+        }
+    }
+
     @Test
     public void whenRicIdleAndNoChangedPoliciesOrPolicyTypes_thenNoRecovery() {
         RIC_1.setState(RicState.IDLE);