Skip to content

Commit

Permalink
Merge branch '2.18'
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed May 10, 2024
2 parents ca04646 + 9ab0c70 commit 3ef1335
Show file tree
Hide file tree
Showing 7 changed files with 24 additions and 184 deletions.
1 change: 1 addition & 0 deletions release-notes/VERSION-2.x
Expand Up @@ -30,6 +30,7 @@ a pure JSON library.
to prevent use by downstream consumers
(requested by @seadbrane)
#1266: Change default recycler pool to `newConcurrentDequePool()` in 2.18
#1271: Deprecate `LockFreePool` implementation in 2.18 (remove from 3.0)
#1277: Add back Java 22 optimisation in FastDoubleParser

2.17.1 (04-May-2024)
Expand Down
61 changes: 2 additions & 59 deletions src/main/java/tools/jackson/core/util/JsonRecyclerPools.java
Expand Up @@ -7,7 +7,6 @@
import tools.jackson.core.json.JsonFactory;
import tools.jackson.core.util.RecyclerPool.BoundedPoolBase;
import tools.jackson.core.util.RecyclerPool.ConcurrentDequePoolBase;
import tools.jackson.core.util.RecyclerPool.LockFreePoolBase;

/**
* Set of {@link RecyclerPool} implementations to be used by the default
Expand All @@ -18,10 +17,8 @@ public final class JsonRecyclerPools
{
/**
* Method to call to get the default recycler pool instance:
* as of Jackson 2.18.x and later (except for 2.17.0) this is same as calling
* {@link #newConcurrentDequePool()}: before this it was calling
* {@link #threadLocalPool()} (except for 2.17.1 that temporarily
* called {@link #newLockFreePool()}, changed back due to reported issues).
* as of Jackson 2.18.x and later this is same as calling
* {@link #newConcurrentDequePool()}.
*
* @return the default {@link RecyclerPool} implementation to use
* if no specific implementation desired.
Expand Down Expand Up @@ -69,24 +66,6 @@ public static RecyclerPool<BufferRecycler> newConcurrentDequePool() {
return ConcurrentDequePool.construct();
}

/**
* Accessor for getting the shared/global {@link LockFreePool} instance.
*
* @return Globally shared instance of {@link LockFreePool}.
*/
public static RecyclerPool<BufferRecycler> sharedLockFreePool() {
return LockFreePool.GLOBAL;
}

/**
* Accessor for constructing a new, non-shared {@link LockFreePool} instance.
*
* @return Globally shared instance of {@link LockFreePool}.
*/
public static RecyclerPool<BufferRecycler> newLockFreePool() {
return LockFreePool.construct();
}

/**
* Accessor for getting the shared/global {@link BoundedPool} instance.
*
Expand Down Expand Up @@ -212,42 +191,6 @@ protected Object readResolve() {
}
}

/**
* {@link RecyclerPool} implementation that uses
* a lock free linked list for recycling instances.
*<p>
* Pool is unbounded: see {@link RecyclerPool} for
* details on what this means.
*/
public static class LockFreePool extends LockFreePoolBase<BufferRecycler>
{
private static final long serialVersionUID = 1L;

protected static final LockFreePool GLOBAL = new LockFreePool(SERIALIZATION_SHARED);

// // // Life-cycle (constructors, factory methods)

protected LockFreePool(int serialization) {
super(serialization);
}

public static LockFreePool construct() {
return new LockFreePool(SERIALIZATION_NON_SHARED);
}

@Override
public BufferRecycler createPooled() {
return new BufferRecycler();
}

// // // JDK serialization support

// Make sure to re-link to global/shared or non-shared.
protected Object readResolve() {
return _resolveToShared(GLOBAL).orElseGet(() -> construct());
}
}

/**
* {@link RecyclerPool} implementation that uses
* a bounded queue ({@link ArrayBlockingQueue} for recycling instances.
Expand Down
79 changes: 0 additions & 79 deletions src/main/java/tools/jackson/core/util/RecyclerPool.java
Expand Up @@ -301,85 +301,6 @@ public boolean clear() {
}
}

/**
* {@link RecyclerPool} implementation that uses
* a lock free linked list for recycling instances.
* Pool is unbounded: see {@link RecyclerPool} for
* details on what this means.
*/
abstract class LockFreePoolBase<P extends WithPool<P>>
extends StatefulImplBase<P>
{
private static final long serialVersionUID = 1L;

// Needs to be transient to avoid JDK serialization from writing it out
private final transient AtomicReference<Node<P>> head;

// // // Life-cycle (constructors, factory methods)

protected LockFreePoolBase(int serialization) {
super(serialization);
head = new AtomicReference<>();
}

// // // Actual API implementation

@Override
public P acquirePooled() {
// This simple lock free algorithm uses an optimistic compareAndSet strategy to
// populate the underlying linked list in a thread-safe way. However, under very
// heavy contention, the compareAndSet could fail multiple times, so it seems a
// reasonable heuristic to limit the number of retries in this situation.
for (int i = 0; i < 3; i++) {
Node<P> currentHead = head.get();
if (currentHead == null) {
return createPooled();
}
if (head.compareAndSet(currentHead, currentHead.next)) {
currentHead.next = null;
return currentHead.value;
}
}
return createPooled();
}

@Override
public void releasePooled(P pooled) {
Node<P> newHead = new Node<>(pooled);
for (int i = 0; i < 3; i++) {
newHead.next = head.get();
if (head.compareAndSet(newHead.next, newHead)) {
return;
}
}
}

@Override
public int pooledCount() {
int count = 0;
for (Node<P> curr = head.get(); curr != null; curr = curr.next) {
++count;
}
return count;
}

// Yes, we can clear it
@Override
public boolean clear() {
head.set(null);
return true;
}

protected static class Node<P> {
final P value;
Node<P> next;

Node(P value) {
this.value = value;
}
}
}

/**
* {@link RecyclerPool} implementation that uses
* a bounded queue ({@link ArrayBlockingQueue} for recycling instances.
Expand Down
10 changes: 6 additions & 4 deletions src/test/java/perf/RecyclerPoolTest.java
Expand Up @@ -146,20 +146,22 @@ private void _testRead(JsonFactory jsonF, byte[] input) throws Exception
private void _testWrite(JsonFactory jsonF) throws Exception
{
StringWriter w = new StringWriter(16);
JsonGenerator g = jsonF.createGenerator(ObjectWriteContext.empty(), w);
g.writeStartArray();
g.writeEndArray();
g.close();
try (JsonGenerator g = jsonF.createGenerator(ObjectWriteContext.empty(), w)) {
g.writeStartArray();
g.writeEndArray();
}
}

public static void main(String[] args) throws Exception
{
RecyclerPoolTest test = new RecyclerPoolTest(THREAD_COUNT);
List<String> results = Arrays.asList(
/*
test.testPool(JsonFactory.builder()
.recyclerPool(JsonRecyclerPools.newLockFreePool())
.build(),
RUNTIME_SECS * 1000),
*/
test.testPool(JsonFactory.builder()
.recyclerPool(JsonRecyclerPools.newConcurrentDequePool())
.build(),
Expand Down
20 changes: 9 additions & 11 deletions src/test/java/tools/jackson/core/JDKSerializabilityTest.java
Expand Up @@ -136,13 +136,11 @@ void recyclerPools() throws Exception
_testRecyclerPoolGlobal(JsonRecyclerPools.threadLocalPool());

_testRecyclerPoolGlobal(JsonRecyclerPools.sharedConcurrentDequePool());
_testRecyclerPoolGlobal(JsonRecyclerPools.sharedLockFreePool());
JsonRecyclerPools.BoundedPool bounded = (JsonRecyclerPools.BoundedPool)
_testRecyclerPoolGlobal(JsonRecyclerPools.sharedBoundedPool());
assertEquals(RecyclerPool.BoundedPoolBase.DEFAULT_CAPACITY, bounded.capacity());

_testRecyclerPoolNonShared(JsonRecyclerPools.newConcurrentDequePool());
_testRecyclerPoolNonShared(JsonRecyclerPools.newLockFreePool());
bounded = (JsonRecyclerPools.BoundedPool)
_testRecyclerPoolNonShared(JsonRecyclerPools.newBoundedPool(250));
assertEquals(250, bounded.capacity());
Expand Down Expand Up @@ -193,17 +191,17 @@ void parseException() throws Exception
@Test
void generationException() throws Exception
{
JsonFactory jf = new JsonFactory();
JsonGenerator g = jf.createGenerator(ObjectWriteContext.empty(), new ByteArrayOutputStream());
StreamWriteException exc = null;
g.writeStartObject();
try {
g.writeNumber(4);
fail("Should not get here");
} catch (StreamWriteException e) {
exc = e;
JsonFactory f = new JsonFactory();
try (JsonGenerator g = f.createGenerator(ObjectWriteContext.empty(), new ByteArrayOutputStream())) {
g.writeStartObject();
try {
g.writeNumber(4);
fail("Should not get here");
} catch (StreamWriteException e) {
exc = e;
}
}
g.close();
byte[] stuff = jdkSerialize(exc);
StreamWriteException result = jdkDeserialize(stuff);
assertNotNull(result);
Expand Down
Expand Up @@ -28,11 +28,6 @@ void threadLocal() throws Exception {
checkBufferRecyclerPoolImpl(JsonRecyclerPools.threadLocalPool(), true, false);
}

@Test
void lockFree() throws Exception {
checkBufferRecyclerPoolImpl(JsonRecyclerPools.newLockFreePool(), true, true);
}

@Test
void concurrentDequeue() throws Exception {
checkBufferRecyclerPoolImpl(JsonRecyclerPools.newConcurrentDequePool(), true, true);
Expand Down
32 changes: 6 additions & 26 deletions src/test/java/tools/jackson/core/util/JsonBufferRecyclersTest.java
Expand Up @@ -31,12 +31,6 @@ void parserWithDequeuPool() throws Exception {
_testParser(JsonRecyclerPools.sharedConcurrentDequePool(), null, null);
}

@Test
void parserWithLockFreePool() throws Exception {
_testParser(JsonRecyclerPools.newLockFreePool(), 0, 1);
_testParser(JsonRecyclerPools.sharedLockFreePool(), null, null);
}

@Test
void parserWithBoundedPool() throws Exception {
_testParser(JsonRecyclerPools.newBoundedPool(5), 0, 1);
Expand Down Expand Up @@ -92,12 +86,6 @@ void generatorWithDequeuPool() throws Exception {
_testGenerator(JsonRecyclerPools.sharedConcurrentDequePool(), null, null);
}

@Test
void generatorWithLockFreePool() throws Exception {
_testGenerator(JsonRecyclerPools.newLockFreePool(), 0, 1);
_testGenerator(JsonRecyclerPools.sharedLockFreePool(), null, null);
}

@Test
void generatorWithBoundedPool() throws Exception {
_testGenerator(JsonRecyclerPools.newBoundedPool(5), 0, 1);
Expand All @@ -116,14 +104,12 @@ private void _testGenerator(RecyclerPool<BufferRecycler> pool,
}

StringWriter w = new StringWriter();
JsonGenerator g = jsonF.createGenerator(ObjectWriteContext.empty(), w);

g.writeStartObject();
g.writeNumberProperty("a", -42);
g.writeStringProperty("b", "barfoo");
g.writeEndObject();

g.close();
try (JsonGenerator g = jsonF.createGenerator(ObjectWriteContext.empty(), w)) {
g.writeStartObject();
g.writeNumberProperty("a", -42);
g.writeStringProperty("b", "barfoo");
g.writeEndObject();
}

if (expSizeAfter != null) {
assertEquals(expSizeAfter, pool.pooledCount());
Expand All @@ -150,12 +136,6 @@ void copyWithDequeuPool() throws Exception {
_testCopy(JsonRecyclerPools.sharedConcurrentDequePool());
}

@Test
void copyWithLockFreePool() throws Exception {
_testCopy(JsonRecyclerPools.newLockFreePool());
_testCopy(JsonRecyclerPools.sharedLockFreePool());
}

@Test
void copyWithBoundedPool() throws Exception {
_testCopy(JsonRecyclerPools.newBoundedPool(5));
Expand Down

0 comments on commit 3ef1335

Please sign in to comment.