diff --git a/Core/src/main/java/com/plotsquared/core/synchronization/LockKey.java b/Core/src/main/java/com/plotsquared/core/synchronization/LockKey.java new file mode 100644 index 000000000..0062d4da6 --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/synchronization/LockKey.java @@ -0,0 +1,92 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * PlotSquared plot management system for Minecraft + * Copyright (C) 2020 IntellectualSites + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.plotsquared.core.synchronization; + +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; + +import javax.annotation.Nonnull; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Key used to access {@link java.util.concurrent.locks.Lock locks} + * from a {@link LockRepository} + */ +public final class LockKey { + + private static final Map keyMap = new HashMap<>(); + private static final Object keyLock = new Object(); + + private final String key; + + private LockKey(@Nonnull final String key) { + this.key = Preconditions.checkNotNull(key, "Key may not be null"); + } + + /** + * Get a new named lock key + * + * @param key Key name + * @return Lock key instance + */ + @Nonnull public static LockKey of(@Nonnull final String key) { + synchronized (keyLock) { + return keyMap.computeIfAbsent(key, LockKey::new); + } + } + + /** + * Get all currently recognized lock keys + * + * @return Currently recognized lock keys + */ + @Nonnull static Collection recognizedKeys() { + return Collections.unmodifiableCollection(keyMap.values()); + } + + @Override public String toString() { + return "LockKey{" + "key='" + key + '\'' + '}'; + } + + @Override public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final LockKey lockKey = (LockKey) o; + return Objects.equal(this.key, lockKey.key); + } + + @Override public int hashCode() { + return Objects.hashCode(this.key); + } + +} diff --git a/Core/src/main/java/com/plotsquared/core/synchronization/LockRepository.java b/Core/src/main/java/com/plotsquared/core/synchronization/LockRepository.java new file mode 100644 index 000000000..373f3694c --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/synchronization/LockRepository.java @@ -0,0 +1,116 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * PlotSquared plot management system for Minecraft + * Copyright (C) 2020 IntellectualSites + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.plotsquared.core.synchronization; + +import com.google.common.util.concurrent.Striped; + +import javax.annotation.Nonnull; +import java.util.concurrent.locks.Lock; +import java.util.function.Consumer; + +/** + * A repository for keyed {@link java.util.concurrent.locks.Lock locks} + */ +public final class LockRepository { + + private final Striped striped; + + public LockRepository() { + this.striped = Striped.lock(LockKey.recognizedKeys().size()); + } + + /** + * Get the lock corresponding to the given lock key + * + * @param key Lock key + * @return Lock + */ + @Nonnull public Lock getLock(@Nonnull final LockKey key) { + return this.striped.get(key); + } + + /** + * Consume a lock + * + * @param key Lock key + * @param consumer Lock consumer + */ + public void useLock(@Nonnull final LockKey key, @Nonnull final Consumer consumer) { + consumer.accept(this.getLock(key)); + } + + /** + * Wait for the lock to become available, and run + * the given runnable, then unlock the lock. This is + * a blocking method. + * + * @param key Lock key + * @param runnable Action to run when the lock is available + */ + public void useLock(@Nonnull final LockKey key, @Nonnull final Runnable runnable) { + this.useLock(key, lock -> { + lock.lock(); + runnable.run(); + lock.unlock(); + }); + } + + /** + * Wait for a lock to be available, lock it and return + * an {@link AutoCloseable} instance that locks the key. + *

+ * This is meant to be used with try-with-resources, like such: + *

{@code
+     * try (final LockAccess lockAccess = lockRepository.lock(LockKey.of("your.key"))) {
+     *      // use lock
+     * }
+     * }
+ * + * @param key Lock key + * @return Lock access. Must be closed. + */ + @Nonnull public LockAccess lock(@Nonnull final LockKey key) { + final Lock lock = this.getLock(key); + lock.lock(); + return new LockAccess(lock); + } + + + public static class LockAccess implements AutoCloseable { + + private final Lock lock; + + private LockAccess(@Nonnull final Lock lock) { + this.lock = lock; + } + + @Override public void close() { + this.lock.unlock(); + } + + } + +}