/*
- * 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.
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;
+import java.util.concurrent.atomic.AtomicReference;
+
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;
+ private volatile NotNullComputable<T> myComputable;
+ private final AtomicReference<T> myValueRef = new AtomicReference<>();
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
- }
+ while (true) {
+ T value = myValueRef.get();
+ if (value != null) return value; // value already computed and cached
+
+ final NotNullComputable<T> 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;
}
}