Skip to content

Commit c8bafd8

Browse files
committed
Attempt to fix server info storage race-condition
There was a possible race condition where ConfigWriter writes empty contents since file-read operation did not lock childNodes Affects issues: - Possibly fixed #2254
1 parent cd72aef commit c8bafd8

3 files changed

Lines changed: 64 additions & 108 deletions

File tree

Plan/common/src/main/java/com/djrapitops/plan/settings/config/ConfigNode.java

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@
2323
*/
2424
package com.djrapitops.plan.settings.config;
2525

26-
import com.djrapitops.plan.utilities.UnitSemaphoreAccessLock;
2726
import org.apache.commons.lang3.StringUtils;
2827

2928
import java.io.IOException;
3029
import java.util.*;
3130
import java.util.concurrent.CopyOnWriteArrayList;
31+
import java.util.concurrent.locks.ReentrantLock;
3232
import java.util.function.BiConsumer;
3333
import java.util.stream.Collectors;
3434

@@ -39,13 +39,12 @@
3939
*/
4040
public class ConfigNode {
4141

42-
protected final UnitSemaphoreAccessLock nodeModificationLock = new UnitSemaphoreAccessLock();
42+
protected final ReentrantLock nodeModificationLock = new ReentrantLock();
4343

4444
protected final String key;
45+
protected final Map<String, ConfigNode> childNodes;
4546
protected ConfigNode parent;
46-
4747
protected List<String> nodeOrder;
48-
protected final Map<String, ConfigNode> childNodes;
4948
protected List<String> comment;
5049

5150
protected String value;
@@ -129,10 +128,10 @@ public void remove() {
129128
if (parent == null) {
130129
throw new IllegalStateException("Can not remove root node from a tree.");
131130
}
132-
nodeModificationLock.enter();
131+
nodeModificationLock.lock();
133132
parent.nodeOrder.remove(key);
134133
parent.childNodes.remove(key);
135-
nodeModificationLock.exit();
134+
nodeModificationLock.unlock();
136135

137136
updateParent(null);
138137

@@ -154,10 +153,10 @@ public void remove() {
154153
protected ConfigNode addChild(ConfigNode child) {
155154
getNode(child.key).ifPresent(ConfigNode::remove);
156155

157-
nodeModificationLock.enter();
156+
nodeModificationLock.lock();
158157
childNodes.put(child.key, child);
159158
nodeOrder.add(child.key);
160-
nodeModificationLock.exit();
159+
nodeModificationLock.unlock();
161160

162161
child.updateParent(this);
163162
return child;
@@ -213,7 +212,7 @@ public void sort() {
213212
}
214213

215214
public void reorder(List<String> newOrder) {
216-
nodeModificationLock.enter();
215+
nodeModificationLock.lock();
217216
List<String> oldOrder = nodeOrder;
218217
nodeOrder = new ArrayList<>();
219218
for (String childKey : newOrder) {
@@ -224,7 +223,7 @@ public void reorder(List<String> newOrder) {
224223
// Add those that were not in the new order, but are in the old order.
225224
oldOrder.removeAll(nodeOrder);
226225
nodeOrder.addAll(oldOrder);
227-
nodeModificationLock.exit();
226+
nodeModificationLock.unlock();
228227
}
229228

230229
/**
@@ -264,10 +263,7 @@ public void setComment(List<String> comment) {
264263
}
265264

266265
private String getEnvironmentVariable() {
267-
String key = getEnvironmentVariableKey();
268-
String variable = System.getenv(key);
269-
Map<String, String> env = System.getenv();
270-
return variable;
266+
return System.getenv(getEnvironmentVariableKey());
271267
}
272268

273269
public List<String> getStringList() {
@@ -379,40 +375,50 @@ public Double getDouble(String path) {
379375
}
380376

381377
public void copyMissing(ConfigNode from) {
382-
// Override comment conditionally
383-
if (comment.size() < from.comment.size()) {
384-
comment = from.comment;
385-
}
378+
nodeModificationLock.lock();
379+
try {
380+
// Override comment conditionally
381+
if (comment.size() < from.comment.size()) {
382+
comment = from.comment;
383+
}
386384

387-
// Override value conditionally
388-
boolean currentValueIsMissing = value == null || value.isEmpty();
389-
boolean otherNodeHasValue = from.value != null && !from.value.isEmpty();
390-
if (currentValueIsMissing && otherNodeHasValue) {
391-
value = from.value;
392-
}
385+
// Override value conditionally
386+
boolean currentValueIsMissing = value == null || value.isEmpty();
387+
boolean otherNodeHasValue = from.value != null && !from.value.isEmpty();
388+
if (currentValueIsMissing && otherNodeHasValue) {
389+
value = from.value;
390+
}
393391

394-
// Copy all nodes from 'from'
395-
for (String childKey : from.nodeOrder) {
396-
ConfigNode newChild = from.childNodes.get(childKey);
392+
// Copy all nodes from 'from'
393+
for (String childKey : from.nodeOrder) {
394+
ConfigNode newChild = from.childNodes.get(childKey);
397395

398-
// Copy values recursively to children
399-
ConfigNode created = addNode(childKey);
400-
created.copyMissing(newChild);
396+
// Copy values recursively to children
397+
ConfigNode created = addNode(childKey);
398+
created.copyMissing(newChild);
399+
}
400+
} finally {
401+
nodeModificationLock.unlock();
401402
}
402403
}
403404

404405
public void copyAll(ConfigNode from) {
405-
// Override comment and value unconditionally.
406-
comment = from.comment;
407-
value = from.value;
406+
nodeModificationLock.lock();
407+
try {
408+
// Override comment and value unconditionally.
409+
comment = from.comment;
410+
value = from.value;
408411

409-
// Copy all nodes from 'from'
410-
for (String childKey : from.nodeOrder) {
411-
ConfigNode newChild = from.childNodes.get(childKey);
412+
// Copy all nodes from 'from'
413+
for (String childKey : from.nodeOrder) {
414+
ConfigNode newChild = from.childNodes.get(childKey);
412415

413-
// Copy values recursively to children
414-
ConfigNode created = addNode(childKey);
415-
created.copyAll(newChild);
416+
// Copy values recursively to children
417+
ConfigNode created = addNode(childKey);
418+
created.copyAll(newChild);
419+
}
420+
} finally {
421+
nodeModificationLock.unlock();
416422
}
417423
}
418424

Plan/common/src/main/java/com/djrapitops/plan/settings/config/ConfigWriter.java

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -93,21 +93,26 @@ public List<String> createLines(ConfigNode writing) {
9393
}
9494

9595
private void dfsTreeTraverseLineResolve(ConfigNode writing, Collection<String> lines) {
96-
Map<String, ConfigNode> children = writing.childNodes;
97-
for (String key : writing.getNodeOrder()) {
98-
ConfigNode node = children.get(key);
99-
// node is null: Inconsistent config node state
100-
// value is null: Has no value (empty)
101-
// nodeOrder is empty: Has no children
102-
if (node == null || node.value == null && node.nodeOrder.isEmpty()) {
103-
continue;
96+
writing.nodeModificationLock.lock();
97+
try {
98+
Map<String, ConfigNode> children = writing.childNodes;
99+
for (String key : writing.getNodeOrder()) {
100+
ConfigNode node = children.get(key);
101+
// node is null: Inconsistent config node state
102+
// value is null: Has no value (empty)
103+
// nodeOrder is empty: Has no children
104+
if (node == null || node.value == null && node.nodeOrder.isEmpty()) {
105+
continue;
106+
}
107+
108+
indent = node.getNodeDepth() * 4;
109+
addComment(node.comment, lines);
110+
addValue(node, lines);
111+
112+
dfsTreeTraverseLineResolve(node, lines);
104113
}
105-
106-
indent = node.getNodeDepth() * 4;
107-
addComment(node.comment, lines);
108-
addValue(node, lines);
109-
110-
dfsTreeTraverseLineResolve(node, lines);
114+
} finally {
115+
writing.nodeModificationLock.unlock();
111116
}
112117
}
113118

Plan/common/src/main/java/com/djrapitops/plan/utilities/UnitSemaphoreAccessLock.java

Lines changed: 0 additions & 55 deletions
This file was deleted.

0 commit comments

Comments
 (0)