/*
 * Copyright 2000-2015 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 org.jetbrains.plugins.groovy.util;

import com.intellij.openapi.util.NotNullComputable;
import com.intellij.openapi.util.RecursionGuard;
import com.intellij.openapi.util.RecursionManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;

public class NotNullCachedComputableWrapper<T> implements NotNullComputable<T> {

  private static final RecursionGuard ourGuard = RecursionManager.createGuard(NotNullCachedComputableWrapper.class.getName());

  private NotNullComputable<T> myComputable;
  private volatile T myValue;

  public NotNullCachedComputableWrapper(@NotNull NotNullComputable<T> computable) {
    myComputable = computable;
  }

  @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
        }
      }
    }

    return result;
  }

  @TestOnly
  public boolean isComputed() {
    return myValue != null;
  }
}

@@ -21,9 +21,9 @@
 -201
-5
+6
  Jet
@@ -876,16 +876,69 @@
 tOnly;%0A%0A
+import java.util.concurrent.atomic.AtomicReference;%0A%0A
 public c
@@ -1149,16 +1149,25 @@
 private 
+volatile 
 NotNullC
@@ -1207,26 +1207,69 @@
 ate 
-volatile T myValue
+final AtomicReference%3CT%3E myValueRef = new AtomicReference%3C%3E()
 ;%0A%0A 
@@ -1441,36 +1441,67 @@
     
-T result = myValue;%0A
+while (true) %7B%0A      T value = myValueRef.get();%0A  
     if (
 resu
@@ -1496,22 +1496,21 @@
     if (
-result
+value
  != null
@@ -1522,134 +1522,241 @@
 urn 
-result;%0A%0A    //noinspection SynchronizeOnThis%0A    synchronized (this) %7B%0A      result = myValue;%0A      if (result == null) %7B%0A  
+value;                // value already computed and cached%0A%0A      final NotNullComputable%3CT%3E computable = myComputable;%0A      if (computable == null) continue;               // computable is null only after some thread succeeds CAS%0A%0A
     
@@ -1829,22 +1829,17 @@
     
-  result = myC
+value = c
 ompu
@@ -1857,26 +1857,24 @@
 te();%0A      
- 
- 
 if (stamp.ma
@@ -1900,132 +1900,435 @@
     
-  
+if (
 myValue
- = result;%0A          myComputable = null;  // allow gc to clean this up%0A        %7D%0A      %7D%0A    %7D%0A%0A
+Ref.compareAndSet(null, value)) %7B  // try to cache value%0A          myComputable = null;                        // if ok, allow gc to clean computable%0A          return value;%0A        %7D                                             // if not ok then another thread already set cached value%0A      %7D%0A      else %7B%0A   
     
+ 
 return 
-result;
+value;                                 // do not try to cache, just return value%0A      %7D%0A    %7D
 %0A  %7D
@@ -2387,24 +2387,33 @@
 turn myValue
+Ref.get()
  != null;%0A  

implement NotNullCachedComputableWrapper without blocking (IDEA-154705)