[ONOS-7334] Using Cache Loader to remove stale entries in p4runtime

Change-Id: Ieead6e199faf23f5fa316516adab659d3e192950
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
index ca9392f..2126dfa 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
@@ -16,9 +16,11 @@
 
 package org.onosproject.drivers.p4runtime;
 
+import com.google.common.cache.LoadingCache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
 import io.grpc.StatusRuntimeException;
 import org.onlab.util.SharedExecutors;
 import org.onosproject.drivers.p4runtime.mirror.P4RuntimeTableMirror;
@@ -43,12 +45,12 @@
 
 import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
 import java.util.Map;
+import java.util.List;
 import java.util.Optional;
 import java.util.Set;
-import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.stream.Collectors;
@@ -90,11 +92,18 @@
     // FIXME: set to true as soon as the feature is implemented in P4Runtime.
     private boolean readAllDirectCounters = false;
 
-    // Needed to synchronize operations over the same table entry.
-    // FIXME: locks should be removed when unused (hint use cache with timeout)
-    private static final ConcurrentMap<PiTableEntryHandle, Lock>
-            ENTRY_LOCKS = Maps.newConcurrentMap();
+    private static final int TABLE_ENTRY_LOCK_EXPIRE_TIME_IN_MIN = 10;
 
+    // Needed to synchronize operations over the same table entry.
+    private static final LoadingCache<PiTableEntryHandle, Lock>
+            ENTRY_LOCKS = CacheBuilder.newBuilder()
+            .expireAfterAccess(TABLE_ENTRY_LOCK_EXPIRE_TIME_IN_MIN, TimeUnit.MINUTES)
+            .build(new CacheLoader<PiTableEntryHandle, Lock>() {
+                @Override
+                public Lock load(PiTableEntryHandle handle) {
+                    return new ReentrantLock();
+                }
+            });
     private PiPipelineModel pipelineModel;
     private PiPipelineInterpreter interpreter;
     private P4RuntimeTableMirror tableMirror;
@@ -275,15 +284,14 @@
                     .of(deviceId, piEntryToApply);
 
             // Serialize operations over the same match key/table/device ID.
-            final Lock lock = ENTRY_LOCKS.computeIfAbsent(handle, k -> new ReentrantLock());
-            lock.lock();
+            ENTRY_LOCKS.getUnchecked(handle).lock();
             try {
                 if (applyEntry(handle, piEntryToApply,
                                ruleToApply, driverOperation)) {
                     result.add(ruleToApply);
                 }
             } finally {
-                lock.unlock();
+                ENTRY_LOCKS.getUnchecked(handle).unlock();
             }
         }