Move more StatementContextBase methods
[yangtools.git] / yang / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / ReactorStmtCtx.java
1 /*
2  * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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 package org.opendaylight.yangtools.yang.parser.stmt.reactor;
9
10 import static com.google.common.base.Verify.verify;
11
12 import com.google.common.base.MoreObjects;
13 import com.google.common.base.MoreObjects.ToStringHelper;
14 import com.google.common.base.VerifyException;
15 import java.util.Collection;
16 import java.util.Map;
17 import org.eclipse.jdt.annotation.NonNull;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.opendaylight.yangtools.yang.common.YangVersion;
20 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
21 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
22 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
23 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
24 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
25 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
26 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
27 import org.opendaylight.yangtools.yang.parser.spi.meta.MutableStatement;
28 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.Registry;
29 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 /**
34  * Real "core" reactor statement implementation of {@link Mutable}, supporting basic reactor lifecycle.
35  *
36  * @param <A> Argument type
37  * @param <D> Declared Statement representation
38  * @param <E> Effective Statement representation
39  */
40 abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
41         extends NamespaceStorageSupport implements Mutable<A, D, E> {
42     private static final Logger LOG = LoggerFactory.getLogger(ReactorStmtCtx.class);
43
44     /**
45      * Substatement refcount tracking. This mechanics deals with retaining substatements for the purposes of
46      * instantiating their lazy copies in InferredStatementContext. It works in concert with {@link #buildEffective()}
47      * and {@link #buildDeclared()}: declared/effective statement views hold an implicit reference and refcount-based
48      * sweep is not activated until they are done (or this statement is not {@link #isSupportedToBuildEffective}).
49      *
50      * <p>
51      * Reference count is hierarchical in that parent references also pin down their child statements and do not allow
52      * them to be swept.
53      *
54      * <p>
55      * The counter's positive values are tracking incoming references via {@link #incRef()}/{@link #decRef()} methods.
56      * Once we transition to sweeping, this value becomes negative counting upwards to {@link #REFCOUNT_NONE} based on
57      * {@link #sweepOnChildDone()}. Once we reach that, we transition to {@link #REFCOUNT_SWEPT}.
58      */
59     private int refcount = REFCOUNT_NONE;
60     /**
61      * No outstanding references, this statement is a potential candidate for sweeping, provided it has populated its
62      * declared and effective views and {@link #parentRef} is known to be absent.
63      */
64     private static final int REFCOUNT_NONE = 0;
65     /**
66      * Reference count overflow or some other recoverable logic error. Do not rely on refcounts and do not sweep
67      * anything.
68      *
69      * <p>
70      * Note on value assignment:
71      * This allow our incRef() to naturally progress to being saturated. Others jump there directly.
72      * It also makes it  it impossible to observe {@code Interger.MAX_VALUE} children, which we take advantage of for
73      * {@link #REFCOUNT_SWEEPING}.
74      */
75     private static final int REFCOUNT_DEFUNCT = Integer.MAX_VALUE;
76     /**
77      * This statement is being actively swept. This is a transient value set when we are sweeping our children, so that
78      * we prevent re-entering this statement.
79      *
80      * <p>
81      * Note on value assignment:
82      * The value is lower than any legal child refcount due to {@link #REFCOUNT_DEFUNCT} while still being higher than
83      * {@link #REFCOUNT_SWEPT}.
84      */
85     private static final int REFCOUNT_SWEEPING = -Integer.MAX_VALUE;
86     /**
87      * This statement, along with its entire subtree has been swept and we positively know all our children have reached
88      * this state. We {@link #sweepNamespaces()} upon reaching this state.
89      *
90      * <p>
91      * Note on value assignment:
92      * This is the lowest value observable, making it easier on checking others on equality.
93      */
94     private static final int REFCOUNT_SWEPT = Integer.MIN_VALUE;
95
96     private @Nullable E effectiveInstance;
97
98     //
99     //
100     // Common public interface contracts with simple mechanics. Please keep this in one logical block, so we do not end
101     // up mixing concerns and simple details with more complex logic.
102     //
103     //
104
105     @Override
106     public abstract StatementContextBase<?, ?, ?> getParentContext();
107
108     @Override
109     public abstract RootStatementContext<?, ?, ?> getRoot();
110
111     @Override
112     public abstract Collection<? extends StatementContextBase<?, ?, ?>> mutableDeclaredSubstatements();
113
114     @Override
115     public final @NonNull Registry getBehaviourRegistry() {
116         return getRoot().getBehaviourRegistryImpl();
117     }
118
119     @Override
120     public final YangVersion getRootVersion() {
121         return getRoot().getRootVersionImpl();
122     }
123
124     @Override
125     public final void setRootVersion(final YangVersion version) {
126         getRoot().setRootVersionImpl(version);
127     }
128
129     @Override
130     public final void addMutableStmtToSeal(final MutableStatement mutableStatement) {
131         getRoot().addMutableStmtToSealImpl(mutableStatement);
132     }
133
134     @Override
135     public final void addRequiredSource(final SourceIdentifier dependency) {
136         getRoot().addRequiredSourceImpl(dependency);
137     }
138
139     @Override
140     public final void setRootIdentifier(final SourceIdentifier identifier) {
141         getRoot().setRootIdentifierImpl(identifier);
142     }
143
144     @Override
145     public final boolean isEnabledSemanticVersioning() {
146         return getRoot().isEnabledSemanticVersioningImpl();
147     }
148
149     @Override
150     public final ModelActionBuilder newInferenceAction(final ModelProcessingPhase phase) {
151         return getRoot().getSourceContext().newInferenceAction(phase);
152     }
153
154     @Override
155     public final StatementDefinition publicDefinition() {
156         return definition().getPublicView();
157     }
158
159     @Override
160     public final String toString() {
161         return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
162     }
163
164     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
165         return toStringHelper.add("definition", definition()).add("rawArgument", rawArgument());
166     }
167
168     /**
169      * Return the context in which this statement was defined.
170      *
171      * @return statement definition
172      */
173     abstract @NonNull StatementDefinitionContext<A, D, E> definition();
174
175     //
176     //
177     // NamespaceStorageSupport/Mutable integration methods. Keep these together.
178     //
179     //
180
181     @Override
182     public final <K, V, T extends K, N extends IdentifierNamespace<K, V>> V getFromNamespace(
183             final Class<@NonNull N> type, final T key) {
184         return getBehaviourRegistry().getNamespaceBehaviour(type).getFrom(this, key);
185     }
186
187     @Override
188     public final <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAllFromNamespace(final Class<N> type) {
189         return getNamespace(type);
190     }
191
192     @Override
193     public final <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAllFromCurrentStmtCtxNamespace(
194             final Class<N> type) {
195         return getLocalNamespace(type);
196     }
197
198     @Override
199     protected final void checkLocalNamespaceAllowed(final Class<? extends IdentifierNamespace<?, ?>> type) {
200         definition().checkNamespaceAllowed(type);
201     }
202
203     @Override
204     protected <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceElementAdded(final Class<N> type, final K key,
205             final V value) {
206         // definition().onNamespaceElementAdded(this, type, key, value);
207     }
208
209     //
210     //
211     // Statement build entry points -- both public and package-private.
212     //
213     //
214
215     @Override
216     public final E buildEffective() {
217         final E existing;
218         return (existing = effectiveInstance) != null ? existing : loadEffective();
219     }
220
221     private E loadEffective() {
222         // Creating an effective statement does not strictly require a declared instance -- there are statements like
223         // 'input', which are implicitly defined.
224         // Our implementation design makes an invariant assumption that buildDeclared() has been called by the time
225         // we attempt to create effective statement:
226         buildDeclared();
227
228         final E ret = effectiveInstance = createEffective();
229         // we have called createEffective(), substatements are no longer guarded by us. Let's see if we can clear up
230         // some residue.
231         if (refcount == REFCOUNT_NONE) {
232             sweepOnDecrement();
233         }
234         return ret;
235     }
236
237     abstract @NonNull E createEffective();
238
239     //
240     //
241     // Reference counting mechanics start. Please keep these methods in one block for clarity. Note this does not
242     // contribute to state visible outside of this package.
243     //
244     //
245
246     /**
247      * Acquire a reference on this context. As long as there is at least one reference outstanding,
248      * {@link #buildEffective()} will not result in {@link #effectiveSubstatements()} being discarded.
249      *
250      * @throws VerifyException if {@link #effectiveSubstatements()} has already been discarded
251      */
252     final void incRef() {
253         final int current = refcount;
254         verify(current >= REFCOUNT_NONE, "Attempted to access reference count of %s", this);
255         if (current != REFCOUNT_DEFUNCT) {
256             // Note: can end up becoming REFCOUNT_DEFUNCT on overflow
257             refcount = current + 1;
258         } else {
259             LOG.debug("Disabled refcount increment of {}", this);
260         }
261     }
262
263     /**
264      * Release a reference on this context. This call may result in {@link #effectiveSubstatements()} becoming
265      * unavailable.
266      */
267     final void decRef() {
268         final int current = refcount;
269         if (current == REFCOUNT_DEFUNCT) {
270             // no-op
271             LOG.debug("Disabled refcount decrement of {}", this);
272             return;
273         }
274         if (current <= REFCOUNT_NONE) {
275             // Underflow, become defunct
276             LOG.warn("Statement refcount underflow, reference counting disabled for {}", this, new Throwable());
277             refcount = REFCOUNT_DEFUNCT;
278             return;
279         }
280
281         refcount = current - 1;
282         LOG.trace("Refcount {} on {}", refcount, this);
283         if (isSweepable()) {
284             // We are no longer guarded by effective instance
285             sweepOnDecrement();
286         }
287     }
288
289     /**
290      * Sweep this statement context as a result of {@link #sweepSubstatements()}, i.e. when parent is also being swept.
291      */
292     private void sweep() {
293         if (isSweepable()) {
294             LOG.trace("Releasing {}", this);
295             sweepState();
296         }
297     }
298
299     static final void sweep(final Collection<? extends ReactorStmtCtx<?, ?, ?>> substatements) {
300         for (ReactorStmtCtx<?, ?, ?> stmt : substatements) {
301             stmt.sweep();
302         }
303     }
304
305     static final int countUnswept(final Collection<? extends ReactorStmtCtx<?, ?, ?>> substatements) {
306         int result = 0;
307         for (ReactorStmtCtx<?, ?, ?> stmt : substatements) {
308             if (stmt.refcount > REFCOUNT_NONE || !stmt.noImplictRef()) {
309                 result++;
310             }
311         }
312         return result;
313     }
314
315     /**
316      * Implementation-specific sweep action. This is expected to perform a recursive {@link #sweep(Collection)} on all
317      * {@link #declaredSubstatements()} and {@link #effectiveSubstatements()} and report the result of the sweep
318      * operation.
319      *
320      * <p>
321      * {@link #effectiveSubstatements()} as well as namespaces may become inoperable as a result of this operation.
322      *
323      * @return True if the entire tree has been completely swept, false otherwise.
324      */
325     abstract int sweepSubstatements();
326
327     // Called when this statement does not have an implicit reference and have reached REFCOUNT_NONE
328     private void sweepOnDecrement() {
329         LOG.trace("Sweeping on decrement {}", this);
330         if (noParentRefcount()) {
331             // No further parent references, sweep our state.
332             sweepState();
333         }
334
335         // Propagate towards parent if there is one
336         final ReactorStmtCtx<?, ?, ?> parent = getParentContext();
337         if (parent != null) {
338             parent.sweepOnChildDecrement();
339         }
340     }
341
342     // Called from child when it has lost its final reference
343     private void sweepOnChildDecrement() {
344         if (isAwaitingChildren()) {
345             // We are a child for which our parent is waiting. Notify it and we are done.
346             sweepOnChildDone();
347             return;
348         }
349
350         // Check parent reference count
351         final int refs = refcount;
352         if (refs > REFCOUNT_NONE || refs <= REFCOUNT_SWEEPING || !noImplictRef()) {
353             // No-op
354             return;
355         }
356
357         // parent is potentially reclaimable
358         if (noParentRefcount()) {
359             LOG.trace("Cleanup {} of parent {}", refcount, this);
360             if (sweepState()) {
361                 final ReactorStmtCtx<?, ?, ?> parent = getParentContext();
362                 if (parent != null) {
363                     parent.sweepOnChildDecrement();
364                 }
365             }
366         }
367     }
368
369     private boolean noImplictRef() {
370         return effectiveInstance != null || !isSupportedToBuildEffective();
371     }
372
373     // FIXME: cache the resolution of this
374     private boolean noParentRefcount() {
375         final ReactorStmtCtx<?, ?, ?> parent = getParentContext();
376         if (parent != null) {
377             // There are three possibilities:
378             // - REFCOUNT_NONE, in which case we need to search next parent
379             // - negative (< REFCOUNT_NONE), meaning parent is in some stage of sweeping, hence it does not have
380             //   a reference to us
381             // - positive (> REFCOUNT_NONE), meaning parent has an explicit refcount which is holding us down
382             final int refs = parent.refcount;
383             return refs == REFCOUNT_NONE ? parent.noParentRefcount() : refs < REFCOUNT_NONE;
384         }
385         return true;
386     }
387
388     private boolean isAwaitingChildren() {
389         return refcount > REFCOUNT_SWEEPING && refcount < REFCOUNT_NONE;
390     }
391
392     private boolean isSweepable() {
393         return refcount == REFCOUNT_NONE && noImplictRef();
394     }
395
396     private void sweepOnChildDone() {
397         LOG.trace("Sweeping on child done {}", this);
398         final int current = refcount;
399         if (current >= REFCOUNT_NONE) {
400             // no-op, perhaps we want to handle some cases differently?
401             LOG.trace("Ignoring child sweep of {} for {}", this, current);
402             return;
403         }
404         verify(current != REFCOUNT_SWEPT, "Attempt to sweep a child of swept %s", this);
405
406         refcount = current + 1;
407         LOG.trace("Child refcount {}", refcount);
408         if (refcount == REFCOUNT_NONE) {
409             sweepDone();
410             final ReactorStmtCtx<?, ?, ?> parent = getParentContext();
411             LOG.trace("Propagating to parent {}", parent);
412             if (parent != null && parent.isAwaitingChildren()) {
413                 parent.sweepOnChildDone();
414             }
415         }
416     }
417
418     private void sweepDone() {
419         LOG.trace("Sweep done for {}", this);
420         refcount = REFCOUNT_SWEPT;
421         sweepNamespaces();
422     }
423
424     private boolean sweepState() {
425         refcount = REFCOUNT_SWEEPING;
426         final int childRefs = sweepSubstatements();
427         if (childRefs == 0) {
428             sweepDone();
429             return true;
430         }
431         if (childRefs < 0 || childRefs >= REFCOUNT_DEFUNCT) {
432             LOG.warn("Negative child refcount {} cannot be stored, reference counting disabled for {}", childRefs, this,
433                 new Throwable());
434             refcount = REFCOUNT_DEFUNCT;
435         } else {
436             LOG.trace("Still {} outstanding children of {}", childRefs, this);
437             refcount = -childRefs;
438         }
439         return false;
440     }
441 }