cacheLock and awaitLock

This commit is contained in:
Ivan Pekov 2021-11-03 16:25:48 +02:00
parent 6e5d323213
commit ab11e36206
No known key found for this signature in database
GPG Key ID: E44CE4557A5E12E0
2 changed files with 89 additions and 26 deletions

View File

@ -46,6 +46,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function; import java.util.function.Function;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collector; import java.util.stream.Collector;
@ -76,8 +77,10 @@ public final class CloudExpansionManager {
@NotNull @NotNull
private final Map<String, CloudExpansion> cache = new HashMap<>(); private final Map<String, CloudExpansion> cache = new HashMap<>();
private final ReentrantLock cacheLock = new ReentrantLock();
@NotNull @NotNull
private final Map<String, CompletableFuture<File>> await = new ConcurrentHashMap<>(); private final Map<String, CompletableFuture<File>> await = new ConcurrentHashMap<>();
private final ReentrantLock awaitLock = new ReentrantLock();
static final ExecutorService ASYNC_EXECUTOR = static final ExecutorService ASYNC_EXECUTOR =
Executors.newCachedThreadPool( Executors.newCachedThreadPool(
@ -113,12 +116,19 @@ public final class CloudExpansionManager {
@NotNull @NotNull
@Unmodifiable @Unmodifiable
public Map<String, CloudExpansion> getCloudExpansions() { public Map<String, CloudExpansion> getCloudExpansions() {
cacheLock.lock();
try {
return ImmutableMap.copyOf(cache); return ImmutableMap.copyOf(cache);
} finally {
cacheLock.unlock();
}
} }
@NotNull @NotNull
@Unmodifiable @Unmodifiable
public Map<String, CloudExpansion> getCloudExpansionsInstalled() { public Map<String, CloudExpansion> getCloudExpansionsInstalled() {
cacheLock.lock();
try {
if (cache.isEmpty()) { if (cache.isEmpty()) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
@ -127,11 +137,16 @@ public final class CloudExpansionManager {
.stream() .stream()
.filter(CloudExpansion::hasExpansion) .filter(CloudExpansion::hasExpansion)
.collect(INDEXED_NAME_COLLECTOR); .collect(INDEXED_NAME_COLLECTOR);
} finally {
cacheLock.unlock();
}
} }
@NotNull @NotNull
@Unmodifiable @Unmodifiable
public Map<String, CloudExpansion> getCloudExpansionsByAuthor(@NotNull final String author) { public Map<String, CloudExpansion> getCloudExpansionsByAuthor(@NotNull final String author) {
cacheLock.lock();
try {
if (cache.isEmpty()) { if (cache.isEmpty()) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
@ -140,12 +155,20 @@ public final class CloudExpansionManager {
.stream() .stream()
.filter(expansion -> author.equalsIgnoreCase(expansion.getAuthor())) .filter(expansion -> author.equalsIgnoreCase(expansion.getAuthor()))
.collect(INDEXED_NAME_COLLECTOR); .collect(INDEXED_NAME_COLLECTOR);
} finally {
cacheLock.unlock();
}
} }
@NotNull @NotNull
@Unmodifiable @Unmodifiable
public Set<String> getCloudExpansionAuthors() { public Set<String> getCloudExpansionAuthors() {
cacheLock.lock();
try {
return cache.values().stream().map(CloudExpansion::getAuthor).collect(Collectors.toSet()); return cache.values().stream().map(CloudExpansion::getAuthor).collect(Collectors.toSet());
} finally {
cacheLock.unlock();
}
} }
public int getCloudExpansionAuthorCount() { public int getCloudExpansionAuthorCount() {
@ -163,14 +186,29 @@ public final class CloudExpansionManager {
@NotNull @NotNull
public Optional<CloudExpansion> findCloudExpansionByName(@NotNull final String name) { public Optional<CloudExpansion> findCloudExpansionByName(@NotNull final String name) {
cacheLock.lock();
try {
return Optional.ofNullable(cache.get(toIndexName(name))); return Optional.ofNullable(cache.get(toIndexName(name)));
} finally {
cacheLock.unlock();
}
} }
public void clean() { public void clean() {
cacheLock.lock();
try {
cache.clear(); cache.clear();
} finally {
cacheLock.unlock();
}
awaitLock.lock();
try {
await.values().forEach(future -> future.cancel(true)); await.values().forEach(future -> future.cancel(true));
await.clear(); await.clear();
} finally {
awaitLock.unlock();
}
} }
public void fetch(final boolean allowUnverified) { public void fetch(final boolean allowUnverified) {
@ -231,7 +269,12 @@ public final class CloudExpansionManager {
} }
} }
cacheLock.lock();
try {
cache.put(toIndexName(expansion), expansion); cache.put(toIndexName(expansion), expansion);
} finally {
cacheLock.unlock();
}
} }
} catch (Throwable e) { } catch (Throwable e) {
// ugly swallowing of every throwable, but we have to be defensive // ugly swallowing of every throwable, but we have to be defensive
@ -244,13 +287,24 @@ public final class CloudExpansionManager {
} }
public boolean isDownloading(@NotNull final CloudExpansion expansion) { public boolean isDownloading(@NotNull final CloudExpansion expansion) {
awaitLock.lock();
try {
return await.containsKey(toIndexName(expansion)); return await.containsKey(toIndexName(expansion));
} finally {
awaitLock.unlock();
}
} }
@NotNull @NotNull
public CompletableFuture<File> downloadExpansion(@NotNull final CloudExpansion expansion, public CompletableFuture<File> downloadExpansion(@NotNull final CloudExpansion expansion,
@NotNull final CloudExpansion.Version version) { @NotNull final CloudExpansion.Version version) {
final CompletableFuture<File> previous = await.get(toIndexName(expansion)); CompletableFuture<File> previous;
awaitLock.lock();
try {
previous = await.get(toIndexName(expansion));
} finally {
awaitLock.unlock();
}
if (previous != null) { if (previous != null) {
return previous; return previous;
} }
@ -269,7 +323,12 @@ public final class CloudExpansionManager {
}, ASYNC_EXECUTOR); }, ASYNC_EXECUTOR);
download.whenCompleteAsync((value, exception) -> { download.whenCompleteAsync((value, exception) -> {
awaitLock.lock();
try {
await.remove(toIndexName(expansion)); await.remove(toIndexName(expansion));
} finally {
awaitLock.unlock();
}
if (exception != null) { if (exception != null) {
plugin.getLogger().log(Level.SEVERE, plugin.getLogger().log(Level.SEVERE,
@ -277,7 +336,12 @@ public final class CloudExpansionManager {
} }
}, ASYNC_EXECUTOR); }, ASYNC_EXECUTOR);
awaitLock.lock();
try {
await.put(toIndexName(expansion), download); await.put(toIndexName(expansion), download);
} finally {
awaitLock.unlock();
}
return download; return download;
} }

View File

@ -71,7 +71,6 @@ public final class Futures {
@NotNull @NotNull
private static <T> List<T> awaitCompletion( private static <T> List<T> awaitCompletion(
@NotNull final Collection<CompletableFuture<T>> futures) { @NotNull final Collection<CompletableFuture<T>> futures) {
// TODO: Calling CompletableFuture#join is bad. Find a way to optimise this whole class
return futures.stream().map(CompletableFuture::join).collect(Collectors.toList()); return futures.stream().map(CompletableFuture::join).collect(Collectors.toList());
} }