Improve RESTCONF thread safety when reconfiguring 41/9341/2
authorRobert Varga <rovarga@cisco.com>
Sat, 26 Jul 2014 04:18:40 +0000 (06:18 +0200)
committerRobert Varga <rovarga@cisco.com>
Sat, 26 Jul 2014 15:26:41 +0000 (17:26 +0200)
There is a race which could see us without a populated mapping while a
schemacontext change is underway. Use a immutablemap guarded by an
atomicreference to allow proper switching. Note that the fix is
incomplete, as the schema context needs to be replaced at the same time.

Change-Id: I7d64fae99a0bc0f7d1ec19f08450c1954cb7af97
Signed-off-by: Robert Varga <rovarga@cisco.com>
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java

index ebf0fd896a4089220e0125fa66753ca229ec9576..671bd6da8a1dbbb8a6d51cc33c2ac99d52f03101 100644 (file)
@@ -17,6 +17,7 @@ import com.google.common.base.Strings;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 
 import java.io.UnsupportedEncodingException;
@@ -25,13 +26,14 @@ import java.net.URLDecoder;
 import java.net.URLEncoder;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
 
 import javax.ws.rs.core.Response.Status;
 
@@ -90,7 +92,8 @@ public class ControllerContext implements SchemaContextListener {
 
     private final Map<String, URI> moduleNameToUri = uriToModuleName.inverse();
 
-    private final Map<QName, RpcDefinition> qnameToRpc = new ConcurrentHashMap<>();
+    private final AtomicReference<Map<QName, RpcDefinition>> qnameToRpc =
+            new AtomicReference<>(Collections.<QName, RpcDefinition>emptyMap());
 
     private volatile SchemaContext globalSchema;
     private volatile MountService mountService;
@@ -852,20 +855,22 @@ public class ControllerContext implements SchemaContextListener {
 
     public RpcDefinition getRpcDefinition(final String name) {
         final QName validName = this.toQName(name);
-        return validName == null ? null : this.qnameToRpc.get(validName);
+        return validName == null ? null : this.qnameToRpc.get().get(validName);
     }
 
     @Override
     public void onGlobalContextUpdated(final SchemaContext context) {
         if (context != null) {
-            this.qnameToRpc.clear();
-            this.setGlobalSchema(context);
-            Set<RpcDefinition> _operations = context.getOperations();
-            for (final RpcDefinition operation : _operations) {
-                {
-                    this.qnameToRpc.put(operation.getQName(), operation);
-                }
+            final Collection<RpcDefinition> defs = context.getOperations();
+            final Map<QName, RpcDefinition> newMap = new HashMap<>(defs.size());
+
+            for (final RpcDefinition operation : defs) {
+                newMap.put(operation.getQName(), operation);
             }
+
+            // FIXME: still not completely atomic
+            this.qnameToRpc.set(ImmutableMap.copyOf(newMap));
+            this.setGlobalSchema(context);
         }
     }