mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-12-21 05:50:18 +08:00
Fixed concurrent config modification issues
When multiple threads attempt to add nodes and reorder a config section concurrency could cause a NPE in ConfigWriter - Prevented the NPE - Prevented concurrent modification between multiple node modification methods with a semaphore
This commit is contained in:
parent
c7b2050504
commit
202fdbc2e3
@ -23,6 +23,7 @@
|
||||
*/
|
||||
package com.djrapitops.plan.settings.config;
|
||||
|
||||
import com.djrapitops.plan.utilities.UnitSemaphoreAccessLock;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -40,6 +41,8 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
public class ConfigNode {
|
||||
|
||||
protected final UnitSemaphoreAccessLock nodeModificationLock = new UnitSemaphoreAccessLock();
|
||||
|
||||
protected final String key;
|
||||
protected ConfigNode parent;
|
||||
|
||||
@ -128,8 +131,11 @@ public class ConfigNode {
|
||||
if (parent == null) {
|
||||
throw new IllegalStateException("Can not remove root node from a tree.");
|
||||
}
|
||||
parent.childNodes.remove(key);
|
||||
nodeModificationLock.enter();
|
||||
parent.nodeOrder.remove(key);
|
||||
parent.childNodes.remove(key);
|
||||
nodeModificationLock.exit();
|
||||
|
||||
updateParent(null);
|
||||
|
||||
// Remove children recursively to avoid memory leaks
|
||||
@ -149,8 +155,12 @@ public class ConfigNode {
|
||||
*/
|
||||
protected ConfigNode addChild(ConfigNode child) {
|
||||
getNode(child.key).ifPresent(ConfigNode::remove);
|
||||
|
||||
nodeModificationLock.enter();
|
||||
childNodes.put(child.key, child);
|
||||
nodeOrder.add(child.key);
|
||||
nodeModificationLock.exit();
|
||||
|
||||
child.updateParent(this);
|
||||
return child;
|
||||
}
|
||||
@ -197,6 +207,7 @@ public class ConfigNode {
|
||||
}
|
||||
|
||||
public void reorder(List<String> newOrder) {
|
||||
nodeModificationLock.enter();
|
||||
List<String> oldOrder = nodeOrder;
|
||||
nodeOrder = new ArrayList<>();
|
||||
for (String childKey : newOrder) {
|
||||
@ -207,6 +218,7 @@ public class ConfigNode {
|
||||
// Add those that were not in the new order, but are in the old order.
|
||||
oldOrder.removeAll(nodeOrder);
|
||||
nodeOrder.addAll(oldOrder);
|
||||
nodeModificationLock.exit();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -95,7 +95,10 @@ public class ConfigWriter {
|
||||
Map<String, ConfigNode> children = writing.childNodes;
|
||||
for (String key : writing.getNodeOrder()) {
|
||||
ConfigNode node = children.get(key);
|
||||
if (node.value == null && node.nodeOrder.isEmpty()) {
|
||||
// node is null: Inconsistent config node state
|
||||
// value is null: Has no value (empty)
|
||||
// nodeOrder is empty: Has no children
|
||||
if (node == null || node.value == null && node.nodeOrder.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user