package com.intellij.openapi.util; import com.intellij.util.concurrency.AtomicFieldUpdater; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public abstract class AsyncValueLoaderManager<HOST, VALUE> { private final AtomicFieldUpdater<HOST, AsyncResult<VALUE>> fieldUpdater; @SuppressWarnings("UnusedDeclaration") public AsyncValueLoaderManager(@NotNull AtomicFieldUpdater<HOST, AsyncResult<VALUE>> fieldUpdater) { this.fieldUpdater = fieldUpdater; } public AsyncValueLoaderManager(@NotNull Class<HOST> ownerClass) { //noinspection unchecked fieldUpdater = ((AtomicFieldUpdater)AtomicFieldUpdater.forFieldOfType(ownerClass, AsyncResult.class)); } public boolean isUpToDate(@NotNull HOST host, @NotNull VALUE value) { return true; } public abstract void load(@NotNull HOST host, @NotNull AsyncResult<VALUE> result); public final void reset(HOST host) { fieldUpdater.set(host, null); } public final void set(HOST host, @Nullable VALUE value) { if (value == null) { reset(host); } else { getOrCreateAsyncResult(host, false, false).setDone(value); } } public final boolean has(HOST host) { AsyncResult<VALUE> result = fieldUpdater.get(host); return result != null && result.isDone() && result.getResult() != null; } @NotNull public final AsyncResult<VALUE> get(HOST host) { return get(host, true); } public final AsyncResult<VALUE> get(HOST host, boolean checkFreshness) { return getOrCreateAsyncResult(host, checkFreshness, true); } private AsyncResult<VALUE> getOrCreateAsyncResult(HOST host, boolean checkFreshness, boolean load) { AsyncResult<VALUE> asyncResult = fieldUpdater.get(host); if (asyncResult == null) { if (!fieldUpdater.compareAndSet(host, null, asyncResult = new AsyncResult<>())) { return fieldUpdater.get(host); } } else if (!asyncResult.isProcessed()) { // if current asyncResult is not processed, so, we don't need to check cache state return asyncResult; } else if (asyncResult.isDone()) { if (!checkFreshness || isUpToDate(host, asyncResult.getResult())) { return asyncResult; } if (!fieldUpdater.compareAndSet(host, asyncResult, asyncResult = new AsyncResult<>())) { AsyncResult<VALUE> valueFromAnotherThread = fieldUpdater.get(host); while (valueFromAnotherThread == null) { if (fieldUpdater.compareAndSet(host, null, asyncResult)) { if (load) { load(host, asyncResult); } return asyncResult; } else { valueFromAnotherThread = fieldUpdater.get(host); } } return valueFromAnotherThread; } } if (load) { load(host, asyncResult); } return asyncResult; } }