Skip to content

Commit 65001fb

Browse files
committed
Add KEYS synchronization
Also makes methods easier to profile, and they're still inlined.
1 parent 91611df commit 65001fb

File tree

2 files changed

+31
-16
lines changed

2 files changed

+31
-16
lines changed

context/src/main/java/software/amazon/smithy/java/context/ChunkedArrayStorageContext.java

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,28 +34,37 @@ public <T> Context put(Key<T> key, T value) {
3434
int id = key.id;
3535
int chunkIdx = id >> CHUNK_SHIFT;
3636

37-
// Grow chunks array if needed
3837
if (chunkIdx >= chunks.length) {
39-
int newSize = Math.max(chunks.length * 2, chunkIdx + 1);
40-
Object[][] newChunks = new Object[newSize][];
41-
System.arraycopy(chunks, 0, newChunks, 0, chunks.length);
42-
chunks = newChunks;
38+
growChunksArray(chunkIdx);
4339
}
4440

45-
// Allocate chunk if needed
41+
Object[] chunk = getChunk(chunkIdx);
42+
chunk[id & CHUNK_MASK] = value;
43+
return this;
44+
}
45+
46+
private void growChunksArray(int chunkIdx) {
47+
int newSize = Math.max(chunks.length * 2, chunkIdx + 1);
48+
Object[][] newChunks = new Object[newSize][];
49+
System.arraycopy(chunks, 0, newChunks, 0, chunks.length);
50+
chunks = newChunks;
51+
}
52+
53+
private Object[] getChunk(int chunkIdx) {
4654
Object[] chunk = chunks[chunkIdx];
47-
if (chunk == null) {
48-
chunk = new Object[CHUNK_SIZE];
49-
chunks[chunkIdx] = chunk;
50-
}
55+
return chunk != null ? chunk : createChunk(chunkIdx);
56+
}
57+
58+
private Object[] createChunk(int chunkIdx) {
59+
Object[] chunk = new Object[CHUNK_SIZE];
60+
chunks[chunkIdx] = chunk;
5161

5262
// Update numChunks to track highest allocated chunk
5363
if (chunkIdx >= numChunks) {
5464
numChunks = chunkIdx + 1;
5565
}
5666

57-
chunk[id & CHUNK_MASK] = value;
58-
return this;
67+
return chunk;
5968
}
6069

6170
@Override

context/src/main/java/software/amazon/smithy/java/context/Context.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55

66
package software.amazon.smithy.java.context;
77

8-
import java.util.ArrayList;
98
import java.util.List;
109
import java.util.Objects;
10+
import java.util.concurrent.CopyOnWriteArrayList;
1111
import java.util.function.Function;
1212

1313
/**
@@ -26,9 +26,13 @@ public sealed interface Context permits ChunkedArrayStorageContext, Unmodifiable
2626
*/
2727
final class Key<T> {
2828

29-
// Global registry of all keys for copyTo operations
29+
// Global registry of all keys for copyTo operations (CopyOnWriteArrayList for thread safety).
3030
@SuppressWarnings("rawtypes")
31-
static final List<Key> KEYS = new ArrayList<>();
31+
static final List<Key> KEYS = new CopyOnWriteArrayList<>();
32+
33+
// We still need a lock to atomically insert an ID into KEYS and get the ID.
34+
// Spotbugs warns against Synchronizing on KEYS, so using a dedicated lock.
35+
private static final Object KEYS_LOCK = new Object();
3236

3337
private final String name;
3438
final int id;
@@ -40,7 +44,9 @@ final class Key<T> {
4044
private Key(String name, Function<T, T> copyFunction) {
4145
this.name = Objects.requireNonNull(name);
4246
this.copyFunction = Objects.requireNonNull(copyFunction);
43-
synchronized (KEYS) {
47+
48+
// Atomically assign our slot in the KEYS array.
49+
synchronized (KEYS_LOCK) {
4450
this.id = KEYS.size();
4551
KEYS.add(this);
4652
}

0 commit comments

Comments
 (0)