mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2024-11-28 13:45:20 +01:00
Maybe fix blocking issue with #getPermissions
This commit is contained in:
parent
42882ebe11
commit
ebab79c4ca
@ -46,7 +46,6 @@ import java.util.*;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,10 +71,85 @@ public abstract class PermissionHolder {
|
|||||||
private final Set<Node> nodes = new HashSet<>();
|
private final Set<Node> nodes = new HashSet<>();
|
||||||
private final Set<Node> transientNodes = new HashSet<>();
|
private final Set<Node> transientNodes = new HashSet<>();
|
||||||
|
|
||||||
private Cache<ImmutableSortedSet<LocalizedNode>> cache = new Cache<>();
|
private Cache<ImmutableSet<Node>> enduringCache = new Cache<>(() -> {
|
||||||
private Cache<ImmutableSortedSet<LocalizedNode>> mergedCache = new Cache<>();
|
synchronized (nodes) {
|
||||||
private Cache<ImmutableSet<Node>> enduringCache = new Cache<>();
|
return ImmutableSet.copyOf(nodes);
|
||||||
private Cache<ImmutableSet<Node>> transientCache = new Cache<>();
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
private Cache<ImmutableSet<Node>> transientCache = new Cache<>(() -> {
|
||||||
|
synchronized (transientNodes) {
|
||||||
|
return ImmutableSet.copyOf(transientNodes);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
private Cache<ImmutableSortedSet<LocalizedNode>> cache = new Cache<>(() -> {
|
||||||
|
TreeSet<LocalizedNode> combined = new TreeSet<>(PriorityComparator.reverse());
|
||||||
|
Set<Node> enduring = getNodes();
|
||||||
|
if (!enduring.isEmpty()) {
|
||||||
|
combined.addAll(getNodes().stream()
|
||||||
|
.map(n -> makeLocal(n, getObjectName()))
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Set<Node> tran = getTransientNodes();
|
||||||
|
if (!tran.isEmpty()) {
|
||||||
|
combined.addAll(getTransientNodes().stream()
|
||||||
|
.map(n -> makeLocal(n, getObjectName()))
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<LocalizedNode> it = combined.iterator();
|
||||||
|
Set<LocalizedNode> higherPriority = new HashSet<>();
|
||||||
|
|
||||||
|
iterate:
|
||||||
|
while (it.hasNext()) {
|
||||||
|
LocalizedNode entry = it.next();
|
||||||
|
for (LocalizedNode h : higherPriority) {
|
||||||
|
if (entry.getNode().almostEquals(h.getNode())) {
|
||||||
|
it.remove();
|
||||||
|
continue iterate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
higherPriority.add(entry);
|
||||||
|
}
|
||||||
|
return ImmutableSortedSet.copyOfSorted(combined);
|
||||||
|
});
|
||||||
|
|
||||||
|
private Cache<ImmutableSortedSet<LocalizedNode>> mergedCache = new Cache<>(() -> {
|
||||||
|
TreeSet<LocalizedNode> combined = new TreeSet<>(PriorityComparator.reverse());
|
||||||
|
Set<Node> enduring = getNodes();
|
||||||
|
if (!enduring.isEmpty()) {
|
||||||
|
combined.addAll(getNodes().stream()
|
||||||
|
.map(n -> makeLocal(n, getObjectName()))
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Set<Node> tran = getTransientNodes();
|
||||||
|
if (!tran.isEmpty()) {
|
||||||
|
combined.addAll(getTransientNodes().stream()
|
||||||
|
.map(n -> makeLocal(n, getObjectName()))
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<LocalizedNode> it = combined.iterator();
|
||||||
|
Set<LocalizedNode> higherPriority = new HashSet<>();
|
||||||
|
|
||||||
|
iterate:
|
||||||
|
while (it.hasNext()) {
|
||||||
|
LocalizedNode entry = it.next();
|
||||||
|
for (LocalizedNode h : higherPriority) {
|
||||||
|
if (entry.getNode().equalsIgnoringValueOrTemp(h.getNode())) {
|
||||||
|
it.remove();
|
||||||
|
continue iterate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
higherPriority.add(entry);
|
||||||
|
}
|
||||||
|
return ImmutableSortedSet.copyOfSorted(combined);
|
||||||
|
});
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final Lock ioLock = new ReentrantLock();
|
private final Lock ioLock = new ReentrantLock();
|
||||||
@ -83,25 +157,11 @@ public abstract class PermissionHolder {
|
|||||||
public abstract String getFriendlyName();
|
public abstract String getFriendlyName();
|
||||||
|
|
||||||
public Set<Node> getNodes() {
|
public Set<Node> getNodes() {
|
||||||
Optional<ImmutableSet<Node>> opt = enduringCache.getIfPresent();
|
return enduringCache.get();
|
||||||
if (opt.isPresent()) {
|
|
||||||
return opt.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized (nodes) {
|
|
||||||
return enduringCache.get(() -> ImmutableSet.copyOf(nodes));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Node> getTransientNodes() {
|
public Set<Node> getTransientNodes() {
|
||||||
Optional<ImmutableSet<Node>> opt = transientCache.getIfPresent();
|
return transientCache.get();
|
||||||
if (opt.isPresent()) {
|
|
||||||
return opt.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized (transientNodes) {
|
|
||||||
return transientCache.get(() -> ImmutableSet.copyOf(transientNodes));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void invalidateCache(boolean enduring) {
|
private void invalidateCache(boolean enduring) {
|
||||||
@ -119,61 +179,7 @@ public abstract class PermissionHolder {
|
|||||||
* @return the holders transient and permanent nodes
|
* @return the holders transient and permanent nodes
|
||||||
*/
|
*/
|
||||||
public SortedSet<LocalizedNode> getPermissions(boolean mergeTemp) {
|
public SortedSet<LocalizedNode> getPermissions(boolean mergeTemp) {
|
||||||
Optional<ImmutableSortedSet<LocalizedNode>> opt = mergeTemp ? mergedCache.getIfPresent() : cache.getIfPresent();
|
return mergeTemp ? mergedCache.get() : cache.get();
|
||||||
if (opt.isPresent()) {
|
|
||||||
return opt.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
Supplier<ImmutableSortedSet<LocalizedNode>> supplier = () -> {
|
|
||||||
// Create sorted set
|
|
||||||
TreeSet<LocalizedNode> combined = new TreeSet<>(PriorityComparator.reverse());
|
|
||||||
|
|
||||||
// Flatten enduring and transient nodes
|
|
||||||
Set<Node> enduring = getNodes();
|
|
||||||
if (!enduring.isEmpty()) {
|
|
||||||
combined.addAll(getNodes().stream()
|
|
||||||
.map(n -> makeLocal(n, getObjectName()))
|
|
||||||
.collect(Collectors.toList())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<Node> tran = getTransientNodes();
|
|
||||||
if (!tran.isEmpty()) {
|
|
||||||
combined.addAll(getTransientNodes().stream()
|
|
||||||
.map(n -> makeLocal(n, getObjectName()))
|
|
||||||
.collect(Collectors.toList())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create an iterator over all permissions being considered
|
|
||||||
Iterator<LocalizedNode> it = combined.iterator();
|
|
||||||
|
|
||||||
// Temporary set to store high priority values
|
|
||||||
Set<LocalizedNode> higherPriority = new HashSet<>();
|
|
||||||
|
|
||||||
// Iterate through each node being considered
|
|
||||||
iterate:
|
|
||||||
while (it.hasNext()) {
|
|
||||||
LocalizedNode entry = it.next();
|
|
||||||
|
|
||||||
// Check through all of the higher priority nodes
|
|
||||||
for (LocalizedNode h : higherPriority) {
|
|
||||||
|
|
||||||
// Check to see if the entry being considered was already processed at a higher priority
|
|
||||||
if (mergeTemp ? entry.getNode().equalsIgnoringValueOrTemp(h.getNode()) : entry.getNode().almostEquals(h.getNode())) {
|
|
||||||
it.remove();
|
|
||||||
continue iterate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This entry will be kept.
|
|
||||||
higherPriority.add(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ImmutableSortedSet.copyOfSorted(combined);
|
|
||||||
};
|
|
||||||
|
|
||||||
return mergeTemp ? mergedCache.get(supplier) : cache.get(supplier);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -239,7 +245,7 @@ public abstract class PermissionHolder {
|
|||||||
|
|
||||||
excludedGroups.add(getObjectName().toLowerCase());
|
excludedGroups.add(getObjectName().toLowerCase());
|
||||||
|
|
||||||
Set<Node> parents = getPermissions(true).stream()
|
Set<Node> parents = all.stream()
|
||||||
.map(LocalizedNode::getNode)
|
.map(LocalizedNode::getNode)
|
||||||
.filter(Node::getValue)
|
.filter(Node::getValue)
|
||||||
.filter(Node::isGroupNode)
|
.filter(Node::isGroupNode)
|
||||||
|
@ -22,30 +22,62 @@
|
|||||||
|
|
||||||
package me.lucko.luckperms.common.utils;
|
package me.lucko.luckperms.common.utils;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread-safe caching utility
|
||||||
|
* @param <T> the type being stored
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class Cache<T> {
|
public class Cache<T> {
|
||||||
private T t = null;
|
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||||
|
private final Supplier<T> supplier;
|
||||||
|
|
||||||
public T get(Supplier<T> supplier) {
|
private T cached = null;
|
||||||
synchronized (this) {
|
|
||||||
if (t == null) {
|
public T get() {
|
||||||
t = supplier.get();
|
lock.readLock().lock();
|
||||||
|
try {
|
||||||
|
if (cached != null) {
|
||||||
|
return cached;
|
||||||
}
|
}
|
||||||
return t;
|
} finally {
|
||||||
|
lock.readLock().unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
lock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
// Check again
|
||||||
|
if (cached != null) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
cached = supplier.get();
|
||||||
|
return cached;
|
||||||
|
} finally {
|
||||||
|
lock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<T> getIfPresent() {
|
public Optional<T> getIfPresent() {
|
||||||
synchronized (this) {
|
lock.readLock().lock();
|
||||||
return Optional.ofNullable(t);
|
try {
|
||||||
|
return Optional.ofNullable(cached);
|
||||||
|
} finally {
|
||||||
|
lock.readLock().unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void invalidate() {
|
public void invalidate() {
|
||||||
synchronized (this) {
|
lock.writeLock().lock();
|
||||||
t = null;
|
try {
|
||||||
|
cached = null;
|
||||||
|
} finally {
|
||||||
|
lock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user