X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fnetconf%2Fconfig-netconf-connector%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fnetconf%2Fconfignetconfconnector%2Fosgi%2FYangStoreServiceImpl.java;h=958af54e3fbf7c63c5bc6d543a030d8cf9bbae2b;hp=5840c9dbd14d1aa3b3fe050d079e426d8ec21f20;hb=26b333ab642abbdda8ef0e3b570083b47af00255;hpb=35189191ea7d4fc663da81cabacacfa4f7ace313 diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreServiceImpl.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreServiceImpl.java index 5840c9dbd1..958af54e3f 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreServiceImpl.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreServiceImpl.java @@ -9,33 +9,64 @@ package org.opendaylight.controller.netconf.confignetconfconnector.osgi; import java.lang.ref.SoftReference; +import java.util.concurrent.atomic.AtomicReference; import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; - -import javax.annotation.concurrent.GuardedBy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class YangStoreServiceImpl implements YangStoreService { + private static final Logger LOG = LoggerFactory.getLogger(YangStoreServiceImpl.class); + + /** + * This is a rather interesting locking model. We need to guard against both the + * cache expiring from GC and being invalidated by schema context change. The + * context can change while we are doing processing, so we do not want to block + * it, so no synchronization can happen on the methods. + * + * So what we are doing is the following: + * + * We synchronize with GC as usual, using a SoftReference. + * + * The atomic reference is used to synchronize with {@link #refresh()}, e.g. when + * refresh happens, it will push a SoftReference(null), e.g. simulate the GC. Now + * that may happen while the getter is already busy acting on the old schema context, + * so it needs to understand that a refresh has happened and retry. To do that, it + * attempts a CAS operation -- if it fails, in knows that the SoftReference has + * been replaced and thus it needs to retry. + * + * Note that {@link #getYangStoreSnapshot()} will still use synchronize() internally + * to stop multiple threads doing the same work. + */ + private final AtomicReference> ref = new AtomicReference<>(new SoftReference(null)); private final SchemaContextProvider service; - @GuardedBy("this") - private SoftReference cache = new SoftReference<>(null); - public YangStoreServiceImpl(SchemaContextProvider service) { + public YangStoreServiceImpl(final SchemaContextProvider service) { this.service = service; } @Override public synchronized YangStoreSnapshotImpl getYangStoreSnapshot() throws YangStoreException { - YangStoreSnapshotImpl yangStoreSnapshot = cache.get(); - if (yangStoreSnapshot == null) { - yangStoreSnapshot = new YangStoreSnapshotImpl(service.getSchemaContext()); - cache = new SoftReference<>(yangStoreSnapshot); + SoftReference r = ref.get(); + YangStoreSnapshotImpl ret = r.get(); + + while (ret == null) { + // We need to be compute a new value + ret = new YangStoreSnapshotImpl(service.getSchemaContext()); + + if (!ref.compareAndSet(r, new SoftReference<>(ret))) { + LOG.debug("Concurrent refresh detected, recomputing snapshot"); + r = ref.get(); + ret = null; + } } - return yangStoreSnapshot; + + return ret; } /** * Called when schema context changes, invalidates cache. */ - public synchronized void refresh() { - cache.clear(); + public void refresh() { + ref.set(new SoftReference(null)); } }