2 * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.parser.stmt.reactor;
10 import static com.google.common.base.Verify.verify;
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;
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;
34 * Real "core" reactor statement implementation of {@link Mutable}, supporting basic reactor lifecycle.
36 * @param <A> Argument type
37 * @param <D> Declared Statement representation
38 * @param <E> Effective Statement representation
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);
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}).
51 * Reference count is hierarchical in that parent references also pin down their child statements and do not allow
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}.
59 private int refcount = REFCOUNT_NONE;
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.
64 private static final int REFCOUNT_NONE = 0;
66 * Reference count overflow or some other recoverable logic error. Do not rely on refcounts and do not sweep
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}.
75 private static final int REFCOUNT_DEFUNCT = Integer.MAX_VALUE;
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.
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}.
85 private static final int REFCOUNT_SWEEPING = -Integer.MAX_VALUE;
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.
91 * Note on value assignment:
92 * This is the lowest value observable, making it easier on checking others on equality.
94 private static final int REFCOUNT_SWEPT = Integer.MIN_VALUE;
96 private @Nullable E effectiveInstance;
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.
106 public abstract StatementContextBase<?, ?, ?> getParentContext();
109 public abstract RootStatementContext<?, ?, ?> getRoot();
112 public abstract Collection<? extends StatementContextBase<?, ?, ?>> mutableDeclaredSubstatements();
115 public final @NonNull Registry getBehaviourRegistry() {
116 return getRoot().getBehaviourRegistryImpl();
120 public final YangVersion getRootVersion() {
121 return getRoot().getRootVersionImpl();
125 public final void setRootVersion(final YangVersion version) {
126 getRoot().setRootVersionImpl(version);
130 public final void addMutableStmtToSeal(final MutableStatement mutableStatement) {
131 getRoot().addMutableStmtToSealImpl(mutableStatement);
135 public final void addRequiredSource(final SourceIdentifier dependency) {
136 getRoot().addRequiredSourceImpl(dependency);
140 public final void setRootIdentifier(final SourceIdentifier identifier) {
141 getRoot().setRootIdentifierImpl(identifier);
145 public final boolean isEnabledSemanticVersioning() {
146 return getRoot().isEnabledSemanticVersioningImpl();
150 public final ModelActionBuilder newInferenceAction(final ModelProcessingPhase phase) {
151 return getRoot().getSourceContext().newInferenceAction(phase);
155 public final StatementDefinition publicDefinition() {
156 return definition().getPublicView();
160 public final String toString() {
161 return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
164 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
165 return toStringHelper.add("definition", definition()).add("rawArgument", rawArgument());
169 * Return the context in which this statement was defined.
171 * @return statement definition
173 abstract @NonNull StatementDefinitionContext<A, D, E> definition();
177 // NamespaceStorageSupport/Mutable integration methods. Keep these together.
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);
188 public final <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAllFromNamespace(final Class<N> type) {
189 return getNamespace(type);
193 public final <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAllFromCurrentStmtCtxNamespace(
194 final Class<N> type) {
195 return getLocalNamespace(type);
199 protected final void checkLocalNamespaceAllowed(final Class<? extends IdentifierNamespace<?, ?>> type) {
200 definition().checkNamespaceAllowed(type);
204 protected <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceElementAdded(final Class<N> type, final K key,
206 // definition().onNamespaceElementAdded(this, type, key, value);
211 // Statement build entry points -- both public and package-private.
216 public final E buildEffective() {
218 return (existing = effectiveInstance) != null ? existing : loadEffective();
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:
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
231 if (refcount == REFCOUNT_NONE) {
237 abstract @NonNull E createEffective();
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.
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.
250 * @throws VerifyException if {@link #effectiveSubstatements()} has already been discarded
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;
259 LOG.debug("Disabled refcount increment of {}", this);
264 * Release a reference on this context. This call may result in {@link #effectiveSubstatements()} becoming
267 final void decRef() {
268 final int current = refcount;
269 if (current == REFCOUNT_DEFUNCT) {
271 LOG.debug("Disabled refcount decrement of {}", this);
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;
281 refcount = current - 1;
282 LOG.trace("Refcount {} on {}", refcount, this);
284 // We are no longer guarded by effective instance
290 * Sweep this statement context as a result of {@link #sweepSubstatements()}, i.e. when parent is also being swept.
292 private void sweep() {
294 LOG.trace("Releasing {}", this);
299 static final void sweep(final Collection<? extends ReactorStmtCtx<?, ?, ?>> substatements) {
300 for (ReactorStmtCtx<?, ?, ?> stmt : substatements) {
305 static final int countUnswept(final Collection<? extends ReactorStmtCtx<?, ?, ?>> substatements) {
307 for (ReactorStmtCtx<?, ?, ?> stmt : substatements) {
308 if (stmt.refcount > REFCOUNT_NONE || !stmt.noImplictRef()) {
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
321 * {@link #effectiveSubstatements()} as well as namespaces may become inoperable as a result of this operation.
323 * @return True if the entire tree has been completely swept, false otherwise.
325 abstract int sweepSubstatements();
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.
335 // Propagate towards parent if there is one
336 final ReactorStmtCtx<?, ?, ?> parent = getParentContext();
337 if (parent != null) {
338 parent.sweepOnChildDecrement();
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.
350 // Check parent reference count
351 final int refs = refcount;
352 if (refs > REFCOUNT_NONE || refs <= REFCOUNT_SWEEPING || !noImplictRef()) {
357 // parent is potentially reclaimable
358 if (noParentRefcount()) {
359 LOG.trace("Cleanup {} of parent {}", refcount, this);
361 final ReactorStmtCtx<?, ?, ?> parent = getParentContext();
362 if (parent != null) {
363 parent.sweepOnChildDecrement();
369 private boolean noImplictRef() {
370 return effectiveInstance != null || !isSupportedToBuildEffective();
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
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;
388 private boolean isAwaitingChildren() {
389 return refcount > REFCOUNT_SWEEPING && refcount < REFCOUNT_NONE;
392 private boolean isSweepable() {
393 return refcount == REFCOUNT_NONE && noImplictRef();
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);
404 verify(current != REFCOUNT_SWEPT, "Attempt to sweep a child of swept %s", this);
406 refcount = current + 1;
407 LOG.trace("Child refcount {}", refcount);
408 if (refcount == REFCOUNT_NONE) {
410 final ReactorStmtCtx<?, ?, ?> parent = getParentContext();
411 LOG.trace("Propagating to parent {}", parent);
412 if (parent != null && parent.isAwaitingChildren()) {
413 parent.sweepOnChildDone();
418 private void sweepDone() {
419 LOG.trace("Sweep done for {}", this);
420 refcount = REFCOUNT_SWEPT;
424 private boolean sweepState() {
425 refcount = REFCOUNT_SWEEPING;
426 final int childRefs = sweepSubstatements();
427 if (childRefs == 0) {
431 if (childRefs < 0 || childRefs >= REFCOUNT_DEFUNCT) {
432 LOG.warn("Negative child refcount {} cannot be stored, reference counting disabled for {}", childRefs, this,
434 refcount = REFCOUNT_DEFUNCT;
436 LOG.trace("Still {} outstanding children of {}", childRefs, this);
437 refcount = -childRefs;