io.atlassian.util.concurrent.ConcurrentOperationMapImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of atlassian-util-concurrent Show documentation
Show all versions of atlassian-util-concurrent Show documentation
This project contains utility classes that are used by
various products and projects inside Atlassian and may have some
utility to the world at large.
/**
* Copyright 2008 Atlassian Pty Ltd
*
* 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 io.atlassian.util.concurrent;
import net.jcip.annotations.ThreadSafe;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.function.Function;
import static java.util.Objects.requireNonNull;
@ThreadSafe public class ConcurrentOperationMapImpl implements ConcurrentOperationMap {
private final ConcurrentMap> map = new ConcurrentHashMap>();
private final Function, CallerRunsFuture> futureFactory;
/**
* Constructor for ConcurrentOperationMapImpl.
*/
public ConcurrentOperationMapImpl() {
this(CallerRunsFuture::new);
}
ConcurrentOperationMapImpl(final Function, CallerRunsFuture> futureFactory) {
this.futureFactory = requireNonNull(futureFactory, "futureFactory");
}
/** {@inheritDoc} */
public final R runOperation(final K key, final Callable operation) throws ExecutionException {
CallerRunsFuture future = map.get(key);
while (future == null) {
map.putIfAbsent(key, futureFactory.apply(operation));
future = map.get(key);
}
try {
return future.get();
} finally {
map.remove(key, future);
}
}
static class CallerRunsFuture extends FutureTask {
CallerRunsFuture(final Callable callable) {
super(callable);
}
@Override public T get() throws ExecutionException {
run();
try {
return super.get();
} catch (final InterruptedException e) {
// /CLOVER:OFF
// how to test?
throw new RuntimeInterruptedException(e);
// /CLOVER:ON
} catch (final ExecutionException e) {
final Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
} else {
throw e;
}
}
}
}
}