Skip to content

Commit 35f24eb

Browse files
committed
feat: remove char block sections array
- replace with "forced" memory access order to eliminate race conditions between sections and blocks arrays
1 parent 9380555 commit 35f24eb

File tree

11 files changed

+76
-99
lines changed

11 files changed

+76
-99
lines changed

worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1016,7 +1016,7 @@ public synchronized boolean trim(boolean aggressive) {
10161016
} else {
10171017
for (int i = getMinSectionPosition(); i <= getMaxSectionPosition(); i++) {
10181018
int layer = i - getMinSectionPosition();
1019-
if (!hasSection(i) || !super.sections[layer].isFull()) {
1019+
if (!hasSection(i) || super.blocks[layer] == null) {
10201020
continue;
10211021
}
10221022
LevelChunkSection existing = getSections(true)[layer];

worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1017,7 +1017,7 @@ public synchronized boolean trim(boolean aggressive) {
10171017
} else {
10181018
for (int i = getMinSectionPosition(); i <= getMaxSectionPosition(); i++) {
10191019
int layer = i - getMinSectionPosition();
1020-
if (!hasSection(i) || !super.sections[layer].isFull()) {
1020+
if (!hasSection(i) || super.blocks[layer] == null) {
10211021
continue;
10221022
}
10231023
LevelChunkSection existing = getSections(true)[layer];

worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1018,7 +1018,7 @@ public synchronized boolean trim(boolean aggressive) {
10181018
} else {
10191019
for (int i = getMinSectionPosition(); i <= getMaxSectionPosition(); i++) {
10201020
int layer = i - getMinSectionPosition();
1021-
if (!hasSection(i) || !super.sections[layer].isFull()) {
1021+
if (!hasSection(i) || super.blocks[layer] == null) {
10221022
continue;
10231023
}
10241024
LevelChunkSection existing = getSections(true)[layer];

worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1052,7 +1052,7 @@ public synchronized boolean trim(boolean aggressive) {
10521052
} else {
10531053
for (int i = getMinSectionPosition(); i <= getMaxSectionPosition(); i++) {
10541054
int layer = i - getMinSectionPosition();
1055-
if (!hasSection(i) || !super.sections[layer].isFull()) {
1055+
if (!hasSection(i) || super.blocks[layer] == null) {
10561056
continue;
10571057
}
10581058
LevelChunkSection existing = getSections(true)[layer];

worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_3/PaperweightGetBlocks.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1011,7 +1011,7 @@ public synchronized boolean trim(boolean aggressive) {
10111011
} else {
10121012
for (int i = getMinSectionPosition(); i <= getMaxSectionPosition(); i++) {
10131013
int layer = i - getMinSectionPosition();
1014-
if (!hasSection(i) || !super.sections[layer].isFull()) {
1014+
if (!hasSection(i) || super.blocks[layer] == null) {
10151015
continue;
10161016
}
10171017
LevelChunkSection existing = getSections(true)[layer];

worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightGetBlocks.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,7 @@ public synchronized boolean trim(boolean aggressive) {
10131013
} else {
10141014
for (int i = getMinSectionPosition(); i <= getMaxSectionPosition(); i++) {
10151015
int layer = i - getMinSectionPosition();
1016-
if (!hasSection(i) || !super.sections[layer].isFull()) {
1016+
if (!hasSection(i) || super.blocks[layer] == null) {
10171017
continue;
10181018
}
10191019
LevelChunkSection existing = getSections(true)[layer];

worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/CharFilterBlock.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -354,8 +354,7 @@ public final BlockState getBlockBelow() {
354354
}
355355
if (layer > minLayer) {
356356
final int newLayer = layer - 1;
357-
final CharGetBlocks chunk = this.get;
358-
return states[chunk.sections[newLayer].get(chunk, newLayer, index + 3840)];
357+
return states[get.get(newLayer, index + 3840)];
359358
}
360359
return BlockTypes.__RESERVED__.getDefaultState();
361360
}
@@ -367,8 +366,7 @@ public final BlockState getBlockAbove() {
367366
}
368367
if (layer < maxLayer) {
369368
final int newLayer = layer + 1;
370-
final CharGetBlocks chunk = this.get;
371-
return states[chunk.sections[newLayer].get(chunk, newLayer, index - 3840)];
369+
return states[get.get(newLayer, index - 3840)];
372370
}
373371
return BlockTypes.__RESERVED__.getDefaultState();
374372
}
@@ -382,7 +380,7 @@ public final BlockState getBlockRelativeY(int y) {
382380
} else if ((layerAdd > 0 && layerAdd < (maxLayer - layer)) || (layerAdd < 0 && layerAdd < (minLayer - layer))) {
383381
final int newLayer = layer + layerAdd;
384382
final int index = this.index + ((y & 15) << 8);
385-
return states[get.sections[newLayer].get(get, newLayer, index)];
383+
return states[get.get(newLayer, index)];
386384
}
387385
return BlockTypes.__RESERVED__.getDefaultState();
388386
}

worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharBlocks.java

Lines changed: 54 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import org.apache.logging.log4j.Logger;
1010

1111
import javax.annotation.Nullable;
12-
import java.lang.invoke.VarHandle;
1312
import java.util.Arrays;
1413

1514
public abstract class CharBlocks implements IBlocks {
@@ -18,12 +17,11 @@ public abstract class CharBlocks implements IBlocks {
1817

1918
protected static final Section FULL = new Section() {
2019
@Override
21-
public char[] get(CharBlocks blocks, int layer) {
22-
char[] arr = blocks.blocks[layer];
20+
public char[] get(CharBlocks blocks, int layer, char[] arr) {
2321
if (arr == null) {
2422
// Chunk probably trimmed mid-operations, but do nothing about it to avoid other issues
2523
synchronized (blocks.sectionLocks[layer]) {
26-
LOGGER.warn("Unexpected null section, please report this occurence alongside a debugpaste.");
24+
LOGGER.warn("Unexpected null section, please report this occurrence alongside a debugpaste.");
2725
return getSkipFull(blocks, layer, false);
2826
}
2927
}
@@ -32,12 +30,11 @@ public char[] get(CharBlocks blocks, int layer) {
3230

3331
// Ignore aggressive switch here.
3432
@Override
35-
public char[] get(CharBlocks blocks, int layer, boolean aggressive) {
36-
char[] arr = blocks.blocks[layer];
33+
public char[] get(CharBlocks blocks, int layer, char[] arr, boolean aggressive) {
3734
if (arr == null) {
3835
// Chunk probably trimmed mid-operations, but do nothing about it to avoid other issues
3936
synchronized (blocks.sectionLocks[layer]) {
40-
LOGGER.warn("Unexpected null section, please report this occurence alongside a debugpaste.");
37+
LOGGER.warn("Unexpected null section, please report this occurrence alongside a debugpaste.");
4138
return getSkipFull(blocks, layer, aggressive);
4239
}
4340
}
@@ -51,17 +48,14 @@ public boolean isFull() {
5148
};
5249
protected static final Section EMPTY = new Section() {
5350
@Override
54-
public char[] get(CharBlocks blocks, int layer) {
51+
public char[] get(CharBlocks blocks, int layer, char[] arr) {
5552
// Defaults to aggressive as it should only be avoided where we know we've reset a chunk during an edit
56-
return get(blocks, layer, true);
53+
return get(blocks, layer, arr, true);
5754
}
5855

5956
@Override
60-
public char[] get(CharBlocks blocks, int layer, boolean aggressive) {
57+
public char[] get(CharBlocks blocks, int layer, char[] arr, boolean aggressive) {
6158
synchronized (blocks.sectionLocks[layer]) {
62-
if (blocks.sections[layer] == FULL) {
63-
return FULL.get(blocks, layer);
64-
}
6559
return getSkipFull(blocks, layer, aggressive);
6660
}
6761
}
@@ -72,7 +66,6 @@ public boolean isFull() {
7266
}
7367
};
7468
public char[][] blocks;
75-
public Section[] sections;
7669
public Object[] sectionLocks;
7770
protected int minSectionPosition;
7871
protected int maxSectionPosition;
@@ -88,10 +81,8 @@ public CharBlocks(int minSectionPosition, int maxSectionPosition) {
8881
this.maxSectionPosition = maxSectionPosition;
8982
this.sectionCount = maxSectionPosition - minSectionPosition + 1;
9083
blocks = new char[sectionCount][];
91-
sections = new Section[sectionCount];
9284
sectionLocks = new Object[sectionCount];
9385
for (int i = 0; i < sectionCount; i++) {
94-
sections[i] = EMPTY;
9586
sectionLocks[i] = new Object();
9687
}
9788
}
@@ -102,45 +93,38 @@ public void init(int chunkX, int chunkZ) {
10293
}
10394

10495
@Override
105-
public synchronized boolean trim(boolean aggressive) {
106-
boolean result = true;
96+
public boolean trim(boolean aggressive) {
10797
for (int i = 0; i < sectionCount; i++) {
108-
if (!sections[i].isFull() && blocks[i] != null) {
109-
blocks[i] = null;
110-
} else {
111-
result = false;
98+
synchronized (sectionLocks[i]) {
99+
if (blocks[i] != null) {
100+
return false;
101+
}
112102
}
113103
}
114-
return result;
104+
return true;
115105
}
116106

117107
@Override
118108
public boolean trim(boolean aggressive, int layer) {
119-
boolean result = true;
120109
synchronized (sectionLocks[layer]) {
121-
if (!sections[layer].isFull() && blocks[layer] != null) {
122-
blocks[layer] = null;
123-
} else {
124-
result = false;
125-
}
110+
return blocks[layer] == null;
126111
}
127-
return result;
128112
}
129113

130114
@Override
131-
public synchronized IChunkSet reset() {
115+
public IChunkSet reset() {
132116
for (int i = 0; i < sectionCount; i++) {
133-
sections[i] = EMPTY;
134-
VarHandle.storeStoreFence();
135-
blocks[i] = null;
117+
synchronized (sectionLocks[i]) {
118+
blocks[i] = null;
119+
}
136120
}
137121
return null;
138122
}
139123

140124
public void reset(int layer) {
141125
layer -= minSectionPosition;
142126
synchronized (sectionLocks[layer]) {
143-
sections[layer] = EMPTY;
127+
blocks[layer] = null;
144128
}
145129
}
146130

@@ -154,11 +138,10 @@ public char[] update(int layer, char[] data, boolean aggressive) {
154138

155139
protected char[] loadPrivately(int layer) {
156140
layer -= getMinSectionPosition();
157-
if (sections[layer] != null) {
158-
synchronized (sectionLocks[layer]) {
159-
if (sections[layer].isFull() && blocks[layer] != null) {
160-
return blocks[layer];
161-
}
141+
synchronized (sectionLocks[layer]) {
142+
char[] data = blocks[layer];
143+
if (data != null) {
144+
return data;
162145
}
163146
}
164147
return update(layer, null, true);
@@ -168,15 +151,14 @@ protected char[] loadPrivately(int layer) {
168151
@Override
169152
public boolean hasSection(int layer) {
170153
layer -= minSectionPosition;
171-
return layer >= 0 && layer < sections.length && sections[layer].isFull();
154+
return layer >= 0 && layer < blocks.length && blocks[layer] != null;
172155
}
173156

174157
@Override
175158
public char[] load(int layer) {
176159
layer -= minSectionPosition;
177-
synchronized (sectionLocks[layer]) {
178-
return sections[layer].get(this, layer);
179-
}
160+
char[] data = blocks[layer];
161+
return (data == null ? EMPTY : FULL).get(this, layer, data);
180162
}
181163

182164
@Nullable
@@ -186,7 +168,7 @@ public char[] loadIfPresent(int layer) {
186168
return null;
187169
}
188170
layer -= minSectionPosition;
189-
return sections[layer].isFull() ? blocks[layer] : null;
171+
return blocks[layer];
190172
}
191173

192174
@Override
@@ -251,38 +233,17 @@ public void set(int x, int y, int z, char value) {
251233
*/
252234

253235
public final char get(int layer, int index) {
254-
return sections[layer - minSectionPosition].get(this, layer, index);
236+
char[] data = blocks[layer - minSectionPosition];
237+
return (data == null ? EMPTY : FULL).get(this, layer, index, data);
255238
}
256239

257240
public final void set(int layer, int index, char value) throws ArrayIndexOutOfBoundsException {
258-
sections[layer - minSectionPosition].set(this, layer, index, value);
241+
char[] data = blocks[layer - minSectionPosition];
242+
(data == null ? EMPTY : FULL).set(this, layer, index, value, data);
259243
}
260244

261245
public abstract static class Section {
262246

263-
abstract char[] get(CharBlocks blocks, int layer);
264-
265-
abstract char[] get(CharBlocks blocks, int layer, boolean aggressive);
266-
267-
public abstract boolean isFull();
268-
269-
public final char get(CharBlocks blocks, int layer, int index) {
270-
int normalized = layer - blocks.minSectionPosition;
271-
char[] section = get(blocks, normalized);
272-
if (section == null) {
273-
synchronized (blocks.sectionLocks[normalized]) {
274-
blocks.reset(layer);
275-
section = EMPTY.get(blocks, normalized, false);
276-
}
277-
}
278-
return section[index];
279-
}
280-
281-
public final synchronized void set(CharBlocks blocks, int layer, int index, char value) {
282-
layer -= blocks.minSectionPosition;
283-
get(blocks, layer)[index] = value;
284-
}
285-
286247
static char[] getSkipFull(CharBlocks blocks, int layer, boolean aggressive) {
287248
char[] arr = blocks.blocks[layer];
288249
if (arr == null) {
@@ -296,12 +257,32 @@ static char[] getSkipFull(CharBlocks blocks, int layer, boolean aggressive) {
296257
throw new IllegalStateException("Array cannot be null (update): " + blocks.getClass());
297258
}
298259
}
299-
if (blocks.blocks[layer] != null) {
300-
blocks.sections[layer] = FULL;
301-
}
302260
return arr;
303261
}
304262

263+
abstract char[] get(CharBlocks blocks, int layer, char[] data);
264+
265+
abstract char[] get(CharBlocks blocks, int layer, char[] data, boolean aggressive);
266+
267+
public abstract boolean isFull();
268+
269+
public final char get(CharBlocks blocks, int layer, int index, char[] data) {
270+
int normalized = layer - blocks.minSectionPosition;
271+
char[] section = get(blocks, normalized, data);
272+
if (section == null) {
273+
synchronized (blocks.sectionLocks[normalized]) {
274+
blocks.reset(layer);
275+
section = EMPTY.get(blocks, normalized, data, false);
276+
}
277+
}
278+
return section[index];
279+
}
280+
281+
public final synchronized void set(CharBlocks blocks, int layer, int index, char value, char[] data) {
282+
layer -= blocks.minSectionPosition;
283+
get(blocks, layer, data)[index] = value;
284+
}
285+
305286
}
306287

307288
}

worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharGetBlocks.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@ public BaseBlock getFullBlock(int x, int y, int z) {
2525
}
2626

2727
@Override
28-
public synchronized boolean trim(boolean aggressive) {
28+
public boolean trim(boolean aggressive) {
2929
for (int i = 0; i < sectionCount; i++) {
30-
sections[i] = EMPTY;
31-
blocks[i] = null;
30+
synchronized (sectionLocks[i]) {
31+
blocks[i] = null;
32+
}
3233
}
3334
return true;
3435
}
@@ -48,11 +49,12 @@ protected char defaultOrdinal() {
4849
}
4950

5051
@Override
51-
public synchronized boolean trim(boolean aggressive, int layer) {
52+
public boolean trim(boolean aggressive, int layer) {
5253
layer -= minSectionPosition;
53-
sections[layer] = EMPTY;
54-
blocks[layer] = null;
55-
return true;
54+
synchronized (sectionLocks[layer]) {
55+
blocks[layer] = null;
56+
return true;
57+
}
5658
}
5759

5860
@Override

0 commit comments

Comments
 (0)