A wrapper object that holds a computation (
getValueProvider()
) and caches the result of the computation.
The recommended way of creation is to use one of
CachedValuesManager.getCachedValue(com.intellij.openapi.util.UserDataHolder, com.intellij.openapi.util.Key<com.intellij.psi.util.CachedValue<T>>, com.intellij.psi.util.CachedValueProvider<T>, boolean)
methods.
When
getValue()
is invoked the first time, the computation is run and its result is returned and remembered internally.
In subsequent invocations, the result will be reused to avoid running the same code again and again.
The computation will be re-run in the following circumstances:
- Garbage collector collects the result cached internally (it's kept via a
SoftReference
).
- The IDE determines that cached value is outdated because some its dependencies are changed. See
CachedValueProvider.Result.getDependencyItems()
The implementation is thread-safe but not atomic, i.e. if several threads request the cached value simultaneously, the computation may
be run concurrently on more than one thread. Due to this and unpredictable garbage collection,
cached value providers shouldn't have side effects.
Result equivalence: CachedValue might return a different result even if the previous one
is still reachable and not garbage-collected, and dependencies haven't changed. Therefore CachedValue results
should be equivalent and interchangeable if they're called multiple times. Examples:
This is enforced at runtime by occasional checks in
com.intellij.util.IdempotenceChecker#checkEquivalence(Object, Object, Class)
.
See that method's documentation for further information and advice, when a failure happens.
Context-independence: if you store the CachedValue in a field or user data of some object
X
, then its
CachedValueProvider
may only depend on X and parts of global system state that don't change while
X
is alive and valid (e.g. application/project components/services).
Otherwise re-invoking the CachedValueProvider after invalidation would use outdated data and produce incorrect results,
possibly causing exceptions in places far, far away. In particular, the provider may not capture:
- Parameters of a method where CachedValue is created, except for
X
itself. Example:
PsiElement resolve(PsiElement e, boolean incompleteCode) {
return CachedValuesManager.getCachedValue(e, () -> doResolve(e, incompleteCode)); // WRONG!!!
}
- "this" object creating the CachedValue, if
X
can outlive it,
or if there can be several non-equivalent instances of "this"-object's class all creating a cached value for the same place
- Thread-locals at the moment of creation. If you use them (either directly or via
RecursionGuard.currentStack()
),
please try not to. If you really have to, also use RecursionGuard.prohibitResultCaching(Object)
to ensure values depending on unstable data won't be cached.
- PSI elements around
X
, when X
is a PsiElement
itself,
as they can change during the lifetime of that PSI element. Example:
PsiMethod[] methods = psiClass.getMethods();
return CachedValuesManager.getCachedValue(psiClass, () -> calculateSomeResult(methods)); // WRONG!!!
This is enforced at runtime by occasional checks in
CachedValueStabilityChecker
.
See that class's documentation for further information and advice, when a failure happens.
Recursion prevention: The same cached value provider can be re-entered recursively on the same thread,
if the computation is inherently cyclic. Note that this is likely to result in
StackOverflowError
,
so avoid such situations at all cost. If there's no other way, use
RecursionManager.doPreventingRecursion(java.lang.Object, boolean, com.intellij.openapi.util.Computable<T>)
instead of custom thread-locals to help get out of the endless loop. Please ensure this call happens inside
the
CachedValueProvider
, not outside
getValue()
call. Otherwise you might get no caching at all, because
CachedValue uses
RecursionGuard.StackStamp#mayCacheNow()
to prevent caching incomplete values, and even the top-level
call would be considered incomplete if it happens inside
doPreventingRecursion
.