com.palantir.atlasdb.keyvalue.cassandra.pool.AsyncSupplier Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of atlasdb-cassandra Show documentation
Show all versions of atlasdb-cassandra Show documentation
Palantir open source project
The newest version!
/*
* (c) Copyright 2022 Palantir Technologies Inc. All rights reserved.
*
* 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 com.palantir.atlasdb.keyvalue.cassandra.pool;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.util.concurrent.Futures;
import com.palantir.common.concurrent.PTExecutors;
import com.palantir.logsafe.exceptions.SafeRuntimeException;
import com.palantir.logsafe.logger.SafeLogger;
import com.palantir.logsafe.logger.SafeLoggerFactory;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.function.Supplier;
import javax.annotation.concurrent.ThreadSafe;
/**
* Supplier that will evaluate a delegate supplier asynchronously, returning {@link Optional#empty()} until
* the underlying result is complete, at which point it will be returned. This computation is only initiated after
* the first call to get, but will be memoized infinitely after that. If the delegate throws, it will not be retried
* and {@link Optional#empty()} will be returned for the lifecycle of this supplier. Note that this class is
* responsible for creating and shutting down any executors.
*
* This class is thread safe by synchronising on {@link #get()}.
*/
@ThreadSafe
public final class AsyncSupplier implements Supplier> {
private static final SafeLogger log = SafeLoggerFactory.get(AsyncSupplier.class);
private final Supplier> delegate;
private final ExecutorService executorService;
private Future> result;
@VisibleForTesting
AsyncSupplier(Supplier> delegate, ExecutorService executorService) {
this.delegate = delegate;
this.executorService = executorService;
}
@Override
public synchronized Optional get() {
if (result == null) {
result = executorService.submit(delegate::get);
executorService.shutdown();
} else if (result.isDone()) {
try {
return result.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new SafeRuntimeException("Interrupted while attempting to compute supplier value", e);
} catch (ExecutionException e) {
log.warn("Failed to evaluate delegate supplier asynchronously; returning empty", e);
result = Futures.immediateFuture(Optional.empty());
}
}
return Optional.empty();
}
public static AsyncSupplier create(Supplier> delegate) {
return new AsyncSupplier<>(delegate, PTExecutors.newSingleThreadExecutor());
}
}