From 1d1b5f0ff97a30df3199d24c91f4e7b05722ebbd Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Wed, 5 Dec 2018 16:25:14 +0300 Subject: [PATCH] add RecursionPreventingSafePublicationLazy --- .../src/com/intellij/util/KtUtils.kt | 2 + .../RecursionPreventingSafePublicationLazy.kt | 51 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 platform/lang-impl/src/com/intellij/util/RecursionPreventingSafePublicationLazy.kt diff --git a/platform/lang-impl/src/com/intellij/util/KtUtils.kt b/platform/lang-impl/src/com/intellij/util/KtUtils.kt index bef5379ad7ec..ea48fd690f1f 100644 --- a/platform/lang-impl/src/com/intellij/util/KtUtils.kt +++ b/platform/lang-impl/src/com/intellij/util/KtUtils.kt @@ -22,3 +22,5 @@ fun Collection.toArray(empty: Array): Array { } fun lazyPub(initializer: () -> T): Lazy = lazy(LazyThreadSafetyMode.PUBLICATION, initializer) + +fun lazyPreventingRecursion(initializer: () -> T): Lazy = RecursionPreventingSafePublicationLazy(initializer) diff --git a/platform/lang-impl/src/com/intellij/util/RecursionPreventingSafePublicationLazy.kt b/platform/lang-impl/src/com/intellij/util/RecursionPreventingSafePublicationLazy.kt new file mode 100644 index 000000000000..763d1631f6fc --- /dev/null +++ b/platform/lang-impl/src/com/intellij/util/RecursionPreventingSafePublicationLazy.kt @@ -0,0 +1,51 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.util + +import com.intellij.openapi.util.RecursionManager +import java.util.concurrent.atomic.AtomicReference + +/** + * Same as [SafePublicationLazyImpl], but returns `null` in case of computation recursion occurred. + */ +class RecursionPreventingSafePublicationLazy(initializer: () -> T) : Lazy { + + @Volatile + private var initializer: (() -> T)? = initializer + private val valueRef: AtomicReference = AtomicReference() + + override val value: T? + get() { + val computedValue = valueRef.get() + if (computedValue !== null) { + return computedValue + } + + val initializerValue = initializer + if (initializerValue === null) { + // Some thread managed to clear the initializer => it managed to set the value. + return valueRef.get() + } + + val newValue = ourRecursionGuard.doPreventingRecursion(this, false, initializerValue) + if (newValue === null) { + // In case of recursion don't update [_value] and don't clear [initializer]. + return null + } + + if (!valueRef.compareAndSet(null, newValue)) { + // Some thread managed to set the value. + return valueRef.get() + } + + initializer = null + return newValue + } + + override fun isInitialized(): Boolean = valueRef.get() !== null + + override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet." + + companion object { + private val ourRecursionGuard = RecursionManager.createGuard("RecursionPreventingSafePublicationLazy") + } +}