Merge "Fixed for bug 1197"
[controller.git] / opendaylight / netconf / config-netconf-connector / src / main / java / org / opendaylight / controller / netconf / confignetconfconnector / osgi / YangStoreServiceImpl.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.controller.netconf.confignetconfconnector.osgi;
10
11 import java.lang.ref.SoftReference;
12 import java.util.concurrent.atomic.AtomicReference;
13
14 import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
15 import org.slf4j.Logger;
16 import org.slf4j.LoggerFactory;
17
18 public class YangStoreServiceImpl implements YangStoreService {
19     private static final Logger LOG = LoggerFactory.getLogger(YangStoreServiceImpl.class);
20
21     /**
22      * This is a rather interesting locking model. We need to guard against both the
23      * cache expiring from GC and being invalidated by schema context change. The
24      * context can change while we are doing processing, so we do not want to block
25      * it, so no synchronization can happen on the methods.
26      *
27      * So what we are doing is the following:
28      *
29      * We synchronize with GC as usual, using a SoftReference.
30      *
31      * The atomic reference is used to synchronize with {@link #refresh()}, e.g. when
32      * refresh happens, it will push a SoftReference(null), e.g. simulate the GC. Now
33      * that may happen while the getter is already busy acting on the old schema context,
34      * so it needs to understand that a refresh has happened and retry. To do that, it
35      * attempts a CAS operation -- if it fails, in knows that the SoftReference has
36      * been replaced and thus it needs to retry.
37      *
38      * Note that {@link #getYangStoreSnapshot()} will still use synchronize() internally
39      * to stop multiple threads doing the same work.
40      */
41     private final AtomicReference<SoftReference<YangStoreSnapshotImpl>> ref = new AtomicReference<>(new SoftReference<YangStoreSnapshotImpl>(null));
42     private final SchemaContextProvider service;
43
44     public YangStoreServiceImpl(final SchemaContextProvider service) {
45         this.service = service;
46     }
47
48     @Override
49     public synchronized YangStoreSnapshotImpl getYangStoreSnapshot() throws YangStoreException {
50         SoftReference<YangStoreSnapshotImpl> r = ref.get();
51         YangStoreSnapshotImpl ret = r.get();
52
53         while (ret == null) {
54             // We need to be compute a new value
55             ret = new YangStoreSnapshotImpl(service.getSchemaContext());
56
57             if (!ref.compareAndSet(r, new SoftReference<>(ret))) {
58                 LOG.debug("Concurrent refresh detected, recomputing snapshot");
59                 r = ref.get();
60                 ret = null;
61             }
62         }
63
64         return ret;
65     }
66
67     /**
68      * Called when schema context changes, invalidates cache.
69      */
70     public void refresh() {
71         ref.set(new SoftReference<YangStoreSnapshotImpl>(null));
72     }
73 }