From 7b916b087ec725af4337a43e209bea8b491ebd9a Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Thu, 14 Apr 2016 19:18:22 +0300 Subject: [PATCH] [groovy] implement NotNullCachedComputableWrapper without blocking (IDEA-154705) --- .../util/NotNullCachedComputableWrapper.java | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/util/NotNullCachedComputableWrapper.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/util/NotNullCachedComputableWrapper.java index adc6069eeda3..86f28f64de89 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/util/NotNullCachedComputableWrapper.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/util/NotNullCachedComputableWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -21,12 +21,14 @@ import com.intellij.openapi.util.RecursionManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.TestOnly; +import java.util.concurrent.atomic.AtomicReference; + public class NotNullCachedComputableWrapper implements NotNullComputable { private static final RecursionGuard ourGuard = RecursionManager.createGuard(NotNullCachedComputableWrapper.class.getName()); - private NotNullComputable myComputable; - private volatile T myValue; + private volatile NotNullComputable myComputable; + private final AtomicReference myValueRef = new AtomicReference<>(); public NotNullCachedComputableWrapper(@NotNull NotNullComputable computable) { myComputable = computable; @@ -35,27 +37,29 @@ public class NotNullCachedComputableWrapper implements NotNullComputable { @NotNull @Override public T compute() { - T result = myValue; - if (result != null) return result; - - //noinspection SynchronizeOnThis - synchronized (this) { - result = myValue; - if (result == null) { - final RecursionGuard.StackStamp stamp = ourGuard.markStack(); - result = myComputable.compute(); - if (stamp.mayCacheNow()) { - myValue = result; - myComputable = null; // allow gc to clean this up - } + while (true) { + T value = myValueRef.get(); + if (value != null) return value; // value already computed and cached + + final NotNullComputable computable = myComputable; + if (computable == null) continue; // computable is null only after some thread succeeds CAS + + final RecursionGuard.StackStamp stamp = ourGuard.markStack(); + value = computable.compute(); + if (stamp.mayCacheNow()) { + if (myValueRef.compareAndSet(null, value)) { // try to cache value + myComputable = null; // if ok, allow gc to clean computable + return value; + } // if not ok then another thread already set cached value + } + else { + return value; // do not try to cache, just return value } } - - return result; } @TestOnly public boolean isComputed() { - return myValue != null; + return myValueRef.get() != null; } } -- 2.32.0