001/* 002 * PlotSquared, a land and world management plugin for Minecraft. 003 * Copyright (C) IntellectualSites <https://intellectualsites.com> 004 * Copyright (C) IntellectualSites team and contributors 005 * 006 * This program is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program. If not, see <https://www.gnu.org/licenses/>. 018 */ 019package com.plotsquared.core.synchronization; 020 021import com.google.common.util.concurrent.Striped; 022import org.checkerframework.checker.nullness.qual.NonNull; 023 024import java.util.concurrent.locks.Lock; 025import java.util.function.Consumer; 026 027/** 028 * A repository for keyed {@link java.util.concurrent.locks.Lock locks} 029 */ 030@SuppressWarnings("UnstableApiUsage") 031public final class LockRepository { 032 033 private final Striped<Lock> striped; 034 035 public LockRepository() { 036 this.striped = Striped.lock(LockKey.recognizedKeys().size()); 037 } 038 039 /** 040 * Get the lock corresponding to the given lock key 041 * 042 * @param key Lock key 043 * @return Lock 044 */ 045 public @NonNull Lock getLock(final @NonNull LockKey key) { 046 return this.striped.get(key); 047 } 048 049 /** 050 * Consume a lock 051 * 052 * @param key Lock key 053 * @param consumer Lock consumer 054 */ 055 public void useLock(final @NonNull LockKey key, final @NonNull Consumer<Lock> consumer) { 056 consumer.accept(this.getLock(key)); 057 } 058 059 /** 060 * Wait for the lock to become available, and run 061 * the given runnable, then unlock the lock. This is 062 * a blocking method. 063 * 064 * @param key Lock key 065 * @param runnable Action to run when the lock is available 066 */ 067 public void useLock(final @NonNull LockKey key, final @NonNull Runnable runnable) { 068 try (LockAccess ignored = lock(key)) { 069 runnable.run(); 070 } 071 } 072 073 /** 074 * Wait for a lock to be available, lock it and return 075 * an {@link AutoCloseable} instance that locks the key. 076 * <p> 077 * This is meant to be used with try-with-resources, like such: 078 * <pre>{@code 079 * try (final LockAccess lockAccess = lockRepository.lock(LockKey.of("your.key"))) { 080 * // use lock 081 * } 082 * }</pre> 083 * 084 * @param key Lock key 085 * @return Lock access. Must be closed. 086 */ 087 public @NonNull LockAccess lock(final @NonNull LockKey key) { 088 final Lock lock = this.getLock(key); 089 lock.lock(); 090 return new LockAccess(lock); 091 } 092 093 094 public static class LockAccess implements AutoCloseable { 095 096 private final Lock lock; 097 098 private LockAccess(final @NonNull Lock lock) { 099 this.lock = lock; 100 } 101 102 @Override 103 public void close() { 104 this.lock.unlock(); 105 } 106 107 } 108 109}