/* * Copyright 2000-2011 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.openapi.util; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; /** * A helper object for {@link RecursionManager}. Is obtained from {@link RecursionManager#createGuard(String)}. * * @author peter */ public abstract class RecursionGuard { /** * See {@link #doPreventingRecursion(Object, boolean, Computable)} with memoization disabled */ @SuppressWarnings("JavaDoc") @Deprecated @Nullable public <T> T doPreventingRecursion(@NotNull Object key, @NotNull Computable<T> computation) { return doPreventingRecursion(key, false, computation); } /** * @param key an id of the computation. Is stored internally to ensure that a recursive calls with the same key won't lead to endless recursion. * @param memoize whether the result of the computation may me cached thread-locally until the last currently active doPreventingRecursion call * completes. May be used to speedup things when recursion re-entrance happens: otherwise nothing would be cached at all and * in some cases exponential performance may be observed. * @param computation a piece of code to compute. * @return the result of the computation or null if we're entering a computation with this key on this thread recursively, */ @Nullable public abstract <T> T doPreventingRecursion(@NotNull Object key, boolean memoize, @NotNull Computable<T> computation); /** * Used in pair with {@link com.intellij.openapi.util.RecursionGuard.StackStamp#mayCacheNow()} to ensure that cached are only the reliable values, * not depending on anything incomplete due to recursive prevention policies. * A typical usage is this: * <code> * RecursionGuard.StackStamp stamp = RecursionManager.createGuard("id").markStack(); * * Result result = doComputation(); * * if (stamp.mayCacheNow()) { * cache(result); * } * return result; * </code> * @return an object representing the current stack state, managed by {@link RecursionManager} */ @NotNull public abstract StackStamp markStack(); /** * @return the current thread-local stack of keys passed to {@link #doPreventingRecursion(Object, Computable)} */ @NotNull public abstract List<Object> currentStack(); /** * Makes {@link com.intellij.openapi.util.RecursionGuard.StackStamp#mayCacheNow()} return false for all stamps created since a computation with * key <code>since</code> began. * * Used to prevent caching of results that are non-reliable NOT due to recursion prevention: for example, too deep recursion * ({@link #currentStack()} may help in determining the recursion depth) * * Also disables thread-local memoization (see the second parameter of {@link #doPreventingRecursion(Object, boolean, Computable)}. * * @param since the id of a computation whose result is safe to cache whilst for more nested ones it's not. */ public abstract void prohibitResultCaching(Object since); public interface StackStamp { /** * @return whether a computation that started at the moment of this {@link StackStamp} instance creation does not depend on any re-entrant recursive * results. When such non-reliable results exist in the thread's call stack, returns false, otherwise true. * If you use this with {@link RecursionGuard#doPreventingRecursion(Object, Computable)}, then the * {@link com.intellij.openapi.util.RecursionGuard#markStack()}+{@link #mayCacheNow()} should be outside of recursion prevention call. Otherwise * even the outer recursive computation result won't be cached. * */ boolean mayCacheNow(); } }